Skip to content

Conversation

@enkei64
Copy link
Contributor

@enkei64 enkei64 commented Aug 4, 2025

Description

This PR adds panel presets to the editor, so you can have different panel layouts optimised for specific workflows. The feature includes 4 built-in presets (Default, Media, Inspector, Vertical Preview) with a dropdown selector in the editor header.

Fixes # (issue)
Linter formatted some other files. They did not break anything before.

Type of change

  • New feature (non-breaking change which adds functionality)

How Has This Been Tested?

Manually tested all 4 panel presets (Default, Media, Inspector, Vertical Preview). Tested panel resizing within each preset and verified persistence across preset switches.

Test Configuration:

  • Node version: v22.16.0
  • Browser (if applicable): Arc Browser
  • Operating System: MacOS 26 Tahoe

Screenshots (if applicable)

Demo.mp4

Checklist:

  • My code follows the style guidelines of this project
  • I have performed a self-review of my code
  • I have added screenshots if ui has been changed
  • I have commented my code, particularly in hard-to-understand areas
  • I have made corresponding changes to the documentation
  • My changes generate no new warnings
  • I have added tests that prove my fix is effective or that my feature works
  • New and existing unit tests pass locally with my changes
  • Any dependent changes have been merged and published in downstream modules

Additional context

None.

Summary by CodeRabbit

  • New Features

    • Introduced selectable panel layout presets in the editor, allowing users to switch between multiple workspace arrangements.
    • Added a dropdown menu in the editor header for choosing and resetting panel layout presets.
  • Improvements

    • Editor panel layouts now dynamically adjust based on the selected preset for a more tailored editing experience.
  • Style

    • Minor formatting and whitespace cleanups in various components for improved code consistency.
  • Bug Fixes

    • Corrected the calculation of text element positions in the preview panel for more accurate placement.

@vercel
Copy link

vercel bot commented Aug 4, 2025

@enkeii64 is attempting to deploy a commit to the OpenCut OSS Team on Vercel.

A member of the Team first needs to authorize it.

@netlify
Copy link

netlify bot commented Aug 4, 2025

👷 Deploy request for appcut pending review.

Visit the deploys page to approve it

Name Link
🔨 Latest commit bb36b2e

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Aug 4, 2025

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Walkthrough

This update introduces a panel layout preset system to the web editor. The panel store is refactored to support multiple named presets, each with its own size configurations and reset logic. The editor UI now conditionally renders layouts based on the active preset. A new UI component allows users to select and reset panel presets. Additional changes include minor formatting improvements and the integration of the preset selector into the editor header.

Changes

Cohort / File(s) Change Summary
Panel Preset System & Store Refactor
apps/web/src/stores/panel-store.ts
Refactored panel store to support multiple named presets with size configurations, custom overrides, active preset tracking, and reset logic. Added exported types and constants for presets.
Editor Layout Conditional Rendering
apps/web/src/app/editor/[project_id]/page.tsx
Refactored editor layout logic to conditionally render different panel arrangements based on the active preset from the panel store.
Panel Preset Selector UI
apps/web/src/components/panel-preset-selector.tsx
Added a new exported React component for selecting and resetting panel layout presets, interfacing with the panel store.
Editor Header Integration
apps/web/src/components/editor-header.tsx
Imported and inserted the PanelPresetSelector into the editor header's right-side content.
Preview Panel Positioning Fix
apps/web/src/components/editor/preview-panel.tsx
Adjusted order of operations in coordinate calculations for element positioning to clarify and ensure correct percentage computation.
Formatting & Stylistic Improvements
apps/web/src/components/editor/audio-waveform.tsx, apps/web/src/components/editor/media-panel/tabbar.tsx, apps/web/src/components/keyboard-shortcuts-help.tsx, apps/web/src/components/ui/editable-timecode.tsx, apps/web/src/data/colors/syntax-ui.tsx
Various whitespace, formatting, and code style cleanups with no impact on logic or behavior.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant PanelPresetSelector
    participant PanelStore
    participant Editor

    User->>PanelPresetSelector: Selects a panel preset or resets it
    PanelPresetSelector->>PanelStore: setActivePreset(preset) or resetPreset(preset)
    PanelStore-->>Editor: Updates activePreset and panel sizes
    Editor->>Editor: Rerenders layout based on activePreset
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~40 minutes

Suggested reviewers

  • mazeincoding

Poem

In the editor’s warren, new layouts appear,
Presets for panels—now crystal clear!
With a click or reset, the workspace will shift,
As bunnies arrange things, both nimble and swift.
Hop, hop, hooray, for the panels anew—
A carrot for code, and a fresh point of view!
🥕✨

✨ Finishing Touches
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai generate unit tests to generate unit tests for this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (2)
apps/web/src/components/panel-preset-selector.tsx (1)

77-86: Consider adding keyboard support for the reset button.

While the button is properly configured with hover states and title attribute, consider adding keyboard event handlers for better accessibility.

 <Button
   variant="secondary"
   size="icon"
   className="h-6 w-6 shrink-0 opacity-60 hover:opacity-100"
   onClick={(e) => handleResetPreset(preset, e)}
+  onKeyDown={(e) => {
+    if (e.key === 'Enter' || e.key === ' ') {
+      e.preventDefault();
+      handleResetPreset(preset, e);
+    }
+  }}
   title={`Reset ${PRESET_LABELS[preset]} preset`}
 >
apps/web/src/app/editor/[project_id]/page.tsx (1)

164-164: Minor inconsistency in gap values.

The gap values vary slightly between layouts (0.18rem vs 0.19rem). Consider standardizing these values for consistency.

- className="h-full w-full gap-[0.18rem] px-3 pb-3"
+ className="h-full w-full gap-[0.19rem] px-3 pb-3"

Also applies to: 185-185, 238-238, 247-247, 258-258, 313-313, 322-322, 333-333, 388-388, 400-400

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d022a78 and 3a6bc6e.

📒 Files selected for processing (10)
  • apps/web/src/app/editor/[project_id]/page.tsx (2 hunks)
  • apps/web/src/components/editor-header.tsx (2 hunks)
  • apps/web/src/components/editor/audio-waveform.tsx (3 hunks)
  • apps/web/src/components/editor/media-panel/tabbar.tsx (1 hunks)
  • apps/web/src/components/editor/preview-panel.tsx (1 hunks)
  • apps/web/src/components/keyboard-shortcuts-help.tsx (1 hunks)
  • apps/web/src/components/panel-preset-selector.tsx (1 hunks)
  • apps/web/src/components/ui/editable-timecode.tsx (1 hunks)
  • apps/web/src/data/colors/syntax-ui.tsx (1 hunks)
  • apps/web/src/stores/panel-store.ts (2 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
**/*.{jsx,tsx}

📄 CodeRabbit Inference Engine (.github/copilot-instructions.md)

**/*.{jsx,tsx}: Don't use accessKey attribute on any HTML element.
Don't set aria-hidden="true" on focusable elements.
Don't add ARIA roles, states, and properties to elements that don't support them.
Don't use distracting elements like <marquee> or <blink>.
Only use the scope prop on <th> elements.
Don't assign non-interactive ARIA roles to interactive HTML elements.
Make sure label elements have text content and are associated with an input.
Don't assign interactive ARIA roles to non-interactive HTML elements.
Don't assign tabIndex to non-interactive HTML elements.
Don't use positive integers for tabIndex property.
Don't include "image", "picture", or "photo" in img alt prop.
Don't use explicit role property that's the same as the implicit/default role.
Make static elements with click handlers use a valid role attribute.
Always include a title element for SVG elements.
Give all elements requiring alt text meaningful information for screen readers.
Make sure anchors have content that's accessible to screen readers.
Assign tabIndex to non-interactive HTML elements with aria-activedescendant.
Include all required ARIA attributes for elements with ARIA roles.
Make sure ARIA properties are valid for the element's supported roles.
Always include a type attribute for button elements.
Make elements with interactive roles and handlers focusable.
Give heading elements content that's accessible to screen readers (not hidden with aria-hidden).
Always include a lang attribute on the html element.
Always include a title attribute for iframe elements.
Accompany onClick with at least one of: onKeyUp, onKeyDown, or onKeyPress.
Accompany onMouseOver/onMouseOut with onFocus/onBlur.
Include caption tracks for audio and video elements.
Use semantic elements instead of role attributes in JSX.
Make sure all anchors are valid and navigable.
Ensure all ARIA properties (aria-*) are valid.
Use valid, non-abstract ARIA roles for elements with...

Files:

  • apps/web/src/data/colors/syntax-ui.tsx
  • apps/web/src/components/editor/audio-waveform.tsx
  • apps/web/src/components/editor-header.tsx
  • apps/web/src/components/panel-preset-selector.tsx
  • apps/web/src/components/ui/editable-timecode.tsx
  • apps/web/src/components/editor/preview-panel.tsx
  • apps/web/src/components/editor/media-panel/tabbar.tsx
  • apps/web/src/components/keyboard-shortcuts-help.tsx
  • apps/web/src/app/editor/[project_id]/page.tsx
**/*.{js,jsx,ts,tsx}

📄 CodeRabbit Inference Engine (.github/copilot-instructions.md)

**/*.{js,jsx,ts,tsx}: Don't use consecutive spaces in regular expression literals.
Don't use the arguments object.
Don't use the comma operator.
Don't write functions that exceed a given Cognitive Complexity score.
Don't use unnecessary boolean casts.
Don't use unnecessary callbacks with flatMap.
Use for...of statements instead of Array.forEach.
Don't create classes that only have static members (like a static namespace).
Don't use this and super in static contexts.
Don't use unnecessary catch clauses.
Don't use unnecessary constructors.
Don't use unnecessary continue statements.
Don't export empty modules that don't change anything.
Don't use unnecessary escape sequences in regular expression literals.
Don't use unnecessary labels.
Don't use unnecessary nested block statements.
Don't rename imports, exports, and destructured assignments to the same name.
Don't use unnecessary string or template literal concatenation.
Don't use String.raw in template literals when there are no escape sequences.
Don't use useless case statements in switch statements.
Don't use ternary operators when simpler alternatives exist.
Don't use useless this aliasing.
Don't initialize variables to undefined.
Don't use the void operators (they're not familiar).
Use arrow functions instead of function expressions.
Use Date.now() to get milliseconds since the Unix Epoch.
Use .flatMap() instead of map().flat() when possible.
Use literal property access instead of computed property access.
Don't use parseInt() or Number.parseInt() when binary, octal, or hexadecimal literals work.
Use concise optional chaining instead of chained logical expressions.
Use regular expression literals instead of the RegExp constructor when possible.
Don't use number literal object member names that aren't base 10 or use underscore separators.
Remove redundant terms from logical expressions.
Use while loops instead of for loops when you don't need initializer and update expressions.
Don't reassign const variables....

Files:

  • apps/web/src/data/colors/syntax-ui.tsx
  • apps/web/src/components/editor/audio-waveform.tsx
  • apps/web/src/components/editor-header.tsx
  • apps/web/src/components/panel-preset-selector.tsx
  • apps/web/src/components/ui/editable-timecode.tsx
  • apps/web/src/components/editor/preview-panel.tsx
  • apps/web/src/components/editor/media-panel/tabbar.tsx
  • apps/web/src/components/keyboard-shortcuts-help.tsx
  • apps/web/src/stores/panel-store.ts
  • apps/web/src/app/editor/[project_id]/page.tsx
**/*.{ts,tsx}

📄 CodeRabbit Inference Engine (.github/copilot-instructions.md)

**/*.{ts,tsx}: Don't use primitive type aliases or misleading types.
Don't use empty type parameters in type aliases and interfaces.
Don't use any or unknown as type constraints.
Don't return a value from a function with the return type 'void'.
Don't use the TypeScript directive @ts-ignore.
Don't use TypeScript enums.
Don't export imported variables.
Don't add type annotations to variables, parameters, and class properties that are initialized with literal expressions.
Don't use TypeScript namespaces.
Don't use non-null assertions with the ! postfix operator.
Don't use parameter properties in class constructors.
Don't use user-defined types.
Use as const instead of literal types and type annotations.
Use either T[] or Array<T> consistently.
Initialize each enum member value explicitly.
Use export type for types.
Use import type for types.
Make sure all enum members are literal values.
Don't use TypeScript const enum.
Don't declare empty interfaces.
Don't let variables evolve into any type through reassignments.
Don't use the any type.
Don't misuse the non-null assertion operator (!) in TypeScript files.
Don't use implicit any type on variable declarations.
Don't merge interfaces and classes unsafely.
Don't use overload signatures that aren't next to each other.
Use the namespace keyword instead of the module keyword to declare TypeScript namespaces.
Use consistent accessibility modifiers on class properties and methods.
Use function types instead of object types with call signatures.
Don't use void type outside of generic or return types.

**/*.{ts,tsx}: Don't use TypeScript enums.
Don't export imported variables.
Don't add type annotations to variables, parameters, and class properties that are initialized with literal expressions.
Don't use TypeScript namespaces.
Don't use non-null assertions with the ! postfix operator.
Don't use parameter properties in class constructors.
Don't use user-defined types.
Use as const instead of literal types and...

Files:

  • apps/web/src/data/colors/syntax-ui.tsx
  • apps/web/src/components/editor/audio-waveform.tsx
  • apps/web/src/components/editor-header.tsx
  • apps/web/src/components/panel-preset-selector.tsx
  • apps/web/src/components/ui/editable-timecode.tsx
  • apps/web/src/components/editor/preview-panel.tsx
  • apps/web/src/components/editor/media-panel/tabbar.tsx
  • apps/web/src/components/keyboard-shortcuts-help.tsx
  • apps/web/src/stores/panel-store.ts
  • apps/web/src/app/editor/[project_id]/page.tsx
🧠 Learnings (44)
📚 Learning: applies to **/*.{js,jsx,ts,tsx} : don't use irregular whitespace characters....
Learnt from: CR
PR: OpenCut-app/OpenCut#0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-07-27T22:14:46.402Z
Learning: Applies to **/*.{js,jsx,ts,tsx} : Don't use irregular whitespace characters.

Applied to files:

  • apps/web/src/data/colors/syntax-ui.tsx
  • apps/web/src/components/editor/audio-waveform.tsx
📚 Learning: applies to **/*.{js,jsx,ts,tsx} : don't export empty modules that don't change anything....
Learnt from: CR
PR: OpenCut-app/OpenCut#0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-07-27T22:14:46.402Z
Learning: Applies to **/*.{js,jsx,ts,tsx} : Don't export empty modules that don't change anything.

Applied to files:

  • apps/web/src/data/colors/syntax-ui.tsx
  • apps/web/src/components/editor/audio-waveform.tsx
📚 Learning: applies to **/*.{js,jsx,ts,tsx} : use standard constants instead of approximated literals....
Learnt from: CR
PR: OpenCut-app/OpenCut#0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-07-27T22:14:46.402Z
Learning: Applies to **/*.{js,jsx,ts,tsx} : Use standard constants instead of approximated literals.

Applied to files:

  • apps/web/src/data/colors/syntax-ui.tsx
  • apps/web/src/components/ui/editable-timecode.tsx
  • apps/web/src/components/editor/preview-panel.tsx
📚 Learning: applies to **/*.{js,jsx,ts,tsx} : don't use unnecessary escapes in string literals....
Learnt from: CR
PR: OpenCut-app/OpenCut#0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-07-27T22:14:46.402Z
Learning: Applies to **/*.{js,jsx,ts,tsx} : Don't use unnecessary escapes in string literals.

Applied to files:

  • apps/web/src/data/colors/syntax-ui.tsx
📚 Learning: applies to **/*.{js,jsx,ts,tsx} : don't compare things where both sides are exactly the same....
Learnt from: CR
PR: OpenCut-app/OpenCut#0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-07-27T22:14:46.403Z
Learning: Applies to **/*.{js,jsx,ts,tsx} : Don't compare things where both sides are exactly the same.

Applied to files:

  • apps/web/src/data/colors/syntax-ui.tsx
📚 Learning: applies to **/*.{js,jsx,ts,tsx} : don't use unnecessary labels....
Learnt from: CR
PR: OpenCut-app/OpenCut#0
File: .cursor/rules/ultracite.mdc:0-0
Timestamp: 2025-07-27T22:15:27.748Z
Learning: Applies to **/*.{js,jsx,ts,tsx} : Don't use unnecessary labels.

Applied to files:

  • apps/web/src/data/colors/syntax-ui.tsx
  • apps/web/src/components/editor/audio-waveform.tsx
📚 Learning: applies to **/*.{js,jsx,ts,tsx} : don't use unnecessary escape sequences in regular expression liter...
Learnt from: CR
PR: OpenCut-app/OpenCut#0
File: .cursor/rules/ultracite.mdc:0-0
Timestamp: 2025-07-27T22:15:27.748Z
Learning: Applies to **/*.{js,jsx,ts,tsx} : Don't use unnecessary escape sequences in regular expression literals.

Applied to files:

  • apps/web/src/data/colors/syntax-ui.tsx
📚 Learning: in tailwind css v4, gradient utilities have been completely redesigned. the new system includes `bg-...
Learnt from: brandonmcconnell
PR: OpenCut-app/OpenCut#498
File: apps/web/src/components/editor/media-panel/views/media.tsx:170-173
Timestamp: 2025-07-30T23:20:57.999Z
Learning: In Tailwind CSS v4, gradient utilities have been completely redesigned. The new system includes `bg-linear-*` for linear gradients, `bg-radial-*` for radial gradients, and `bg-conic-*` for conic gradients, with support for color interpolation modifiers like `/oklch` and `/srgb`.

Applied to files:

  • apps/web/src/data/colors/syntax-ui.tsx
📚 Learning: tailwind css v4 introduces new `bg-linear-to-*` gradient utilities including `bg-linear-to-br`, `bg-...
Learnt from: brandonmcconnell
PR: OpenCut-app/OpenCut#498
File: apps/web/src/app/blog/page.tsx:31-32
Timestamp: 2025-07-30T23:20:54.822Z
Learning: Tailwind CSS v4 introduces new `bg-linear-to-*` gradient utilities including `bg-linear-to-br`, `bg-linear-to-tr`, `bg-linear-to-r`, `bg-linear-to-b`, `bg-linear-to-bl`, `bg-linear-to-l`, and `bg-linear-to-t`. These replace or complement the previous `bg-gradient-to-*` utilities from v3 and are part of v4's expanded gradient API that also includes angle-based gradients, gradient interpolation modifiers, and new conic/radial gradient utilities.

Applied to files:

  • apps/web/src/data/colors/syntax-ui.tsx
📚 Learning: tailwind css v4 introduces new gradient utilities including `bg-linear-to-br`, `bg-linear-to-tr`, `b...
Learnt from: brandonmcconnell
PR: OpenCut-app/OpenCut#498
File: apps/web/src/components/editor/media-panel/views/media.tsx:170-173
Timestamp: 2025-07-30T23:20:57.999Z
Learning: Tailwind CSS v4 introduces new gradient utilities including `bg-linear-to-br`, `bg-linear-to-tr`, `bg-linear-to-r`, etc., replacing the previous `bg-gradient-to-*` syntax from v3. These new utilities provide the same gradient functionality with updated naming conventions.

Applied to files:

  • apps/web/src/data/colors/syntax-ui.tsx
📚 Learning: tailwind css v4 introduces new gradient utilities including `bg-linear-to-br`, `bg-linear-to-tr`, `b...
Learnt from: brandonmcconnell
PR: OpenCut-app/OpenCut#498
File: apps/web/src/app/blog/page.tsx:31-32
Timestamp: 2025-07-30T23:20:54.822Z
Learning: Tailwind CSS v4 introduces new gradient utilities including `bg-linear-to-br`, `bg-linear-to-tr`, `bg-linear-to-r`, etc. as part of their updated gradient API, replacing or complementing the previous `bg-gradient-to-*` utilities from v3.

Applied to files:

  • apps/web/src/data/colors/syntax-ui.tsx
📚 Learning: tailwind css v4 replaces the `bg-gradient-to-*` utilities from v3 with new `bg-linear-to-*` utilitie...
Learnt from: brandonmcconnell
PR: OpenCut-app/OpenCut#498
File: apps/web/src/components/editor/media-panel/views/media.tsx:170-173
Timestamp: 2025-07-30T23:20:57.999Z
Learning: Tailwind CSS v4 replaces the `bg-gradient-to-*` utilities from v3 with new `bg-linear-to-*` utilities (e.g., `bg-linear-to-br`, `bg-linear-to-tr`, `bg-linear-to-r`). These new utilities provide enhanced gradient control including explicit angles, color interpolation modes, and better fallback support.

Applied to files:

  • apps/web/src/data/colors/syntax-ui.tsx
📚 Learning: applies to **/*.{js,jsx,ts,tsx} : use `string.trimstart()` and `string.trimend()` over `string.triml...
Learnt from: CR
PR: OpenCut-app/OpenCut#0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-07-27T22:14:46.402Z
Learning: Applies to **/*.{js,jsx,ts,tsx} : Use `String.trimStart()` and `String.trimEnd()` over `String.trimLeft()` and `String.trimRight()`.

Applied to files:

  • apps/web/src/components/editor/audio-waveform.tsx
  • apps/web/src/components/editor/preview-panel.tsx
📚 Learning: applies to **/*.{jsx,tsx} : don't use unnecessary fragments....
Learnt from: CR
PR: OpenCut-app/OpenCut#0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-07-27T22:14:46.402Z
Learning: Applies to **/*.{jsx,tsx} : Don't use unnecessary fragments.

Applied to files:

  • apps/web/src/components/editor/audio-waveform.tsx
📚 Learning: applies to **/*.{js,jsx,ts,tsx} : don't use consecutive spaces in regular expression literals....
Learnt from: CR
PR: OpenCut-app/OpenCut#0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-07-27T22:14:46.402Z
Learning: Applies to **/*.{js,jsx,ts,tsx} : Don't use consecutive spaces in regular expression literals.

Applied to files:

  • apps/web/src/components/editor/audio-waveform.tsx
📚 Learning: applies to **/*.{js,jsx,ts,tsx} : don't use useless undefined....
Learnt from: CR
PR: OpenCut-app/OpenCut#0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-07-27T22:14:46.402Z
Learning: Applies to **/*.{js,jsx,ts,tsx} : Don't use useless undefined.

Applied to files:

  • apps/web/src/components/editor/audio-waveform.tsx
📚 Learning: the file apps/web/src/components/editor/media-panel/views/media.tsx uses "use client" directive, mak...
Learnt from: khanguyen74
PR: OpenCut-app/OpenCut#466
File: apps/web/src/components/editor/media-panel/views/media.tsx:47-52
Timestamp: 2025-07-26T21:07:57.582Z
Learning: The file apps/web/src/components/editor/media-panel/views/media.tsx uses "use client" directive, making it client-only code where window object is always available, so SSR safety checks are not needed.

Applied to files:

  • apps/web/src/components/editor-header.tsx
  • apps/web/src/components/panel-preset-selector.tsx
  • apps/web/src/components/ui/editable-timecode.tsx
  • apps/web/src/components/editor/preview-panel.tsx
  • apps/web/src/components/editor/media-panel/tabbar.tsx
  • apps/web/src/app/editor/[project_id]/page.tsx
📚 Learning: applies to **/*.{js,jsx,ts,tsx} : don't have unused imports....
Learnt from: CR
PR: OpenCut-app/OpenCut#0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-07-27T22:14:46.402Z
Learning: Applies to **/*.{js,jsx,ts,tsx} : Don't have unused imports.

Applied to files:

  • apps/web/src/components/editor-header.tsx
📚 Learning: applies to **/*.{jsx,tsx} : use `<>...` instead of `...`....
Learnt from: CR
PR: OpenCut-app/OpenCut#0
File: .cursor/rules/ultracite.mdc:0-0
Timestamp: 2025-07-27T22:15:27.748Z
Learning: Applies to **/*.{jsx,tsx} : Use `<>...</>` instead of `<Fragment>...</Fragment>`.

Applied to files:

  • apps/web/src/components/editor-header.tsx
  • apps/web/src/components/ui/editable-timecode.tsx
📚 Learning: applies to **/*.{jsx,tsx} : make sure anchors have content that's accessible to screen readers....
Learnt from: CR
PR: OpenCut-app/OpenCut#0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-07-27T22:14:46.402Z
Learning: Applies to **/*.{jsx,tsx} : Make sure anchors have content that's accessible to screen readers.

Applied to files:

  • apps/web/src/components/editor-header.tsx
📚 Learning: applies to **/*.{jsx,tsx} : use semantic elements instead of role attributes in jsx....
Learnt from: CR
PR: OpenCut-app/OpenCut#0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-07-27T22:14:46.402Z
Learning: Applies to **/*.{jsx,tsx} : Use semantic elements instead of role attributes in JSX.

Applied to files:

  • apps/web/src/components/panel-preset-selector.tsx
  • apps/web/src/components/ui/editable-timecode.tsx
📚 Learning: applies to **/*.{ts,tsx} : don't use primitive type aliases or misleading types....
Learnt from: CR
PR: OpenCut-app/OpenCut#0
File: .cursor/rules/ultracite.mdc:0-0
Timestamp: 2025-07-27T22:15:27.748Z
Learning: Applies to **/*.{ts,tsx} : Don't use primitive type aliases or misleading types.

Applied to files:

  • apps/web/src/components/ui/editable-timecode.tsx
📚 Learning: applies to **/*.{ts,tsx} : use `as const` instead of literal types and type annotations....
Learnt from: CR
PR: OpenCut-app/OpenCut#0
File: .cursor/rules/ultracite.mdc:0-0
Timestamp: 2025-07-27T22:15:27.748Z
Learning: Applies to **/*.{ts,tsx} : Use `as const` instead of literal types and type annotations.

Applied to files:

  • apps/web/src/components/ui/editable-timecode.tsx
📚 Learning: applies to **/*.{jsx,tsx} : don't assign to react component props....
Learnt from: CR
PR: OpenCut-app/OpenCut#0
File: .cursor/rules/ultracite.mdc:0-0
Timestamp: 2025-07-27T22:15:27.748Z
Learning: Applies to **/*.{jsx,tsx} : Don't assign to React component props.

Applied to files:

  • apps/web/src/components/ui/editable-timecode.tsx
📚 Learning: applies to **/*.{jsx,tsx} : don't use event handlers on non-interactive elements....
Learnt from: CR
PR: OpenCut-app/OpenCut#0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-07-27T22:14:46.402Z
Learning: Applies to **/*.{jsx,tsx} : Don't use event handlers on non-interactive elements.

Applied to files:

  • apps/web/src/components/ui/editable-timecode.tsx
📚 Learning: applies to **/*.{jsx,tsx} : make elements with interactive roles and handlers focusable....
Learnt from: CR
PR: OpenCut-app/OpenCut#0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-07-27T22:14:46.402Z
Learning: Applies to **/*.{jsx,tsx} : Make elements with interactive roles and handlers focusable.

Applied to files:

  • apps/web/src/components/ui/editable-timecode.tsx
  • apps/web/src/components/editor/media-panel/tabbar.tsx
📚 Learning: applies to **/*.{jsx,tsx} : accompany `onmouseover`/`onmouseout` with `onfocus`/`onblur`....
Learnt from: CR
PR: OpenCut-app/OpenCut#0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-07-27T22:14:46.402Z
Learning: Applies to **/*.{jsx,tsx} : Accompany `onMouseOver`/`onMouseOut` with `onFocus`/`onBlur`.

Applied to files:

  • apps/web/src/components/ui/editable-timecode.tsx
  • apps/web/src/components/editor/media-panel/tabbar.tsx
  • apps/web/src/components/keyboard-shortcuts-help.tsx
📚 Learning: applies to **/*.{jsx,tsx} : don't use dangerous jsx props....
Learnt from: CR
PR: OpenCut-app/OpenCut#0
File: .cursor/rules/ultracite.mdc:0-0
Timestamp: 2025-07-27T22:15:27.748Z
Learning: Applies to **/*.{jsx,tsx} : Don't use dangerous JSX props.

Applied to files:

  • apps/web/src/components/ui/editable-timecode.tsx
📚 Learning: applies to **/*.{js,jsx,ts,tsx} : don't use expressions where the operation doesn't change the value...
Learnt from: CR
PR: OpenCut-app/OpenCut#0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-07-27T22:14:46.402Z
Learning: Applies to **/*.{js,jsx,ts,tsx} : Don't use expressions where the operation doesn't change the value.

Applied to files:

  • apps/web/src/components/editor/preview-panel.tsx
📚 Learning: applies to **/*.{js,jsx,ts,tsx} : don't use literal numbers that lose precision....
Learnt from: CR
PR: OpenCut-app/OpenCut#0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-07-27T22:14:46.402Z
Learning: Applies to **/*.{js,jsx,ts,tsx} : Don't use literal numbers that lose precision.

Applied to files:

  • apps/web/src/components/editor/preview-panel.tsx
📚 Learning: applies to **/*.{jsx,tsx} : make sure all anchors are valid and navigable....
Learnt from: CR
PR: OpenCut-app/OpenCut#0
File: .cursor/rules/ultracite.mdc:0-0
Timestamp: 2025-07-27T22:15:27.748Z
Learning: Applies to **/*.{jsx,tsx} : Make sure all anchors are valid and navigable.

Applied to files:

  • apps/web/src/components/editor/preview-panel.tsx
📚 Learning: applies to **/*.{js,jsx,ts,tsx} : don't use `math.min` and `math.max` to clamp values when the resul...
Learnt from: CR
PR: OpenCut-app/OpenCut#0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-07-27T22:14:46.402Z
Learning: Applies to **/*.{js,jsx,ts,tsx} : Don't use `Math.min` and `Math.max` to clamp values when the result is constant.

Applied to files:

  • apps/web/src/components/editor/preview-panel.tsx
📚 Learning: applies to **/*.{jsx,tsx} : assign `tabindex` to non-interactive html elements with `aria-activedesc...
Learnt from: CR
PR: OpenCut-app/OpenCut#0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-07-27T22:14:46.402Z
Learning: Applies to **/*.{jsx,tsx} : Assign `tabIndex` to non-interactive HTML elements with `aria-activedescendant`.

Applied to files:

  • apps/web/src/components/editor/media-panel/tabbar.tsx
📚 Learning: applies to **/*.{js,jsx,ts,tsx} : don't use nested ternary expressions....
Learnt from: CR
PR: OpenCut-app/OpenCut#0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-07-27T22:14:46.402Z
Learning: Applies to **/*.{js,jsx,ts,tsx} : Don't use nested ternary expressions.

Applied to files:

  • apps/web/src/components/editor/media-panel/tabbar.tsx
  • apps/web/src/components/keyboard-shortcuts-help.tsx
📚 Learning: applies to **/*.{jsx,tsx} : don't assign `tabindex` to non-interactive html elements....
Learnt from: CR
PR: OpenCut-app/OpenCut#0
File: .cursor/rules/ultracite.mdc:0-0
Timestamp: 2025-07-27T22:15:27.748Z
Learning: Applies to **/*.{jsx,tsx} : Don't assign `tabIndex` to non-interactive HTML elements.

Applied to files:

  • apps/web/src/components/editor/media-panel/tabbar.tsx
📚 Learning: applies to **/*.{js,jsx,ts,tsx} : don't use ternary operators when simpler alternatives exist....
Learnt from: CR
PR: OpenCut-app/OpenCut#0
File: .cursor/rules/ultracite.mdc:0-0
Timestamp: 2025-07-27T22:15:27.748Z
Learning: Applies to **/*.{js,jsx,ts,tsx} : Don't use ternary operators when simpler alternatives exist.

Applied to files:

  • apps/web/src/components/editor/media-panel/tabbar.tsx
  • apps/web/src/components/keyboard-shortcuts-help.tsx
📚 Learning: applies to **/*.{jsx,tsx} : don't use positive integers for `tabindex` property....
Learnt from: CR
PR: OpenCut-app/OpenCut#0
File: .cursor/rules/ultracite.mdc:0-0
Timestamp: 2025-07-27T22:15:27.748Z
Learning: Applies to **/*.{jsx,tsx} : Don't use positive integers for `tabIndex` property.

Applied to files:

  • apps/web/src/components/editor/media-panel/tabbar.tsx
📚 Learning: applies to **/*.{js,jsx,ts,tsx} : use assignment operator shorthand where possible....
Learnt from: CR
PR: OpenCut-app/OpenCut#0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-07-27T22:14:46.402Z
Learning: Applies to **/*.{js,jsx,ts,tsx} : Use assignment operator shorthand where possible.

Applied to files:

  • apps/web/src/components/keyboard-shortcuts-help.tsx
📚 Learning: applies to **/*.{js,jsx,ts,tsx} : use concise optional chaining instead of chained logical expressio...
Learnt from: CR
PR: OpenCut-app/OpenCut#0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-07-27T22:14:46.402Z
Learning: Applies to **/*.{js,jsx,ts,tsx} : Use concise optional chaining instead of chained logical expressions.

Applied to files:

  • apps/web/src/components/keyboard-shortcuts-help.tsx
📚 Learning: applies to **/*.{js,jsx,ts,tsx} : remove redundant terms from logical expressions....
Learnt from: CR
PR: OpenCut-app/OpenCut#0
File: .cursor/rules/ultracite.mdc:0-0
Timestamp: 2025-07-27T22:15:27.748Z
Learning: Applies to **/*.{js,jsx,ts,tsx} : Remove redundant terms from logical expressions.

Applied to files:

  • apps/web/src/components/keyboard-shortcuts-help.tsx
📚 Learning: applies to **/*.{js,jsx,ts,tsx} : don't use duplicate conditions in if-else-if chains....
Learnt from: CR
PR: OpenCut-app/OpenCut#0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-07-27T22:14:46.402Z
Learning: Applies to **/*.{js,jsx,ts,tsx} : Don't use duplicate conditions in if-else-if chains.

Applied to files:

  • apps/web/src/components/keyboard-shortcuts-help.tsx
📚 Learning: applies to **/*.{js,jsx,ts,tsx} : don't use unnecessary boolean casts....
Learnt from: CR
PR: OpenCut-app/OpenCut#0
File: .cursor/rules/ultracite.mdc:0-0
Timestamp: 2025-07-27T22:15:27.748Z
Learning: Applies to **/*.{js,jsx,ts,tsx} : Don't use unnecessary boolean casts.

Applied to files:

  • apps/web/src/components/keyboard-shortcuts-help.tsx
📚 Learning: applies to **/*.{jsx,tsx} : make static elements with click handlers use a valid role attribute....
Learnt from: CR
PR: OpenCut-app/OpenCut#0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-07-27T22:14:46.402Z
Learning: Applies to **/*.{jsx,tsx} : Make static elements with click handlers use a valid role attribute.

Applied to files:

  • apps/web/src/components/keyboard-shortcuts-help.tsx
📚 Learning: applies to **/*.{jsx,tsx} : accompany `onclick` with at least one of: `onkeyup`, `onkeydown`, or `on...
Learnt from: CR
PR: OpenCut-app/OpenCut#0
File: .cursor/rules/ultracite.mdc:0-0
Timestamp: 2025-07-27T22:15:27.748Z
Learning: Applies to **/*.{jsx,tsx} : Accompany `onClick` with at least one of: `onKeyUp`, `onKeyDown`, or `onKeyPress`.

Applied to files:

  • apps/web/src/components/keyboard-shortcuts-help.tsx
🔇 Additional comments (15)
apps/web/src/components/editor/audio-waveform.tsx (1)

23-23: LGTM! Clean whitespace removal.

The removal of trailing whitespace-only lines improves code consistency without affecting functionality.

Also applies to: 93-93, 114-114, 117-117, 120-120

apps/web/src/data/colors/syntax-ui.tsx (1)

1-32: LGTM! Improved readability with logical grouping.

The added blank lines between gradient color groups enhance code organization and readability.

apps/web/src/components/ui/editable-timecode.tsx (1)

1-137: LGTM! Consistent formatting applied.

The file has been reformatted for consistency without any functional changes.

apps/web/src/stores/panel-store.ts (3)

4-43: Well-structured preset configuration system!

The type definitions and preset configurations provide a clean foundation for the panel layout feature. The default size values appear reasonable for each preset's intended use case.


81-145: Consistent state management pattern for panel resizing.

The setter methods properly maintain both current sizes and per-preset custom sizes, ensuring user adjustments persist when switching between presets.


148-205: Robust preset switching and reset logic.

The implementation correctly:

  • Saves current customizations before switching presets
  • Applies saved customizations when loading a preset
  • Uses resetCounter to force UI updates when resetting
  • Provides a clean way to retrieve current sizes
apps/web/src/components/editor/media-panel/tabbar.tsx (1)

80-82: LGTM! Improved readability of conditional styling.

The multi-line format makes the ternary expression more readable while maintaining the same logic.

apps/web/src/components/editor-header.tsx (2)

32-32: LGTM!

The import statement correctly references the new panel preset selector component.


151-151: Good placement of the panel preset selector.

The component is appropriately positioned as the first item in the navigation container, making it easily accessible to users.

apps/web/src/components/keyboard-shortcuts-help.tsx (1)

96-98: LGTM!

The ternary expression formatting is cleaner and more concise while maintaining the same logic.

apps/web/src/components/editor/preview-panel.tsx (1)

351-368: Good clarification of order of operations.

The added parentheses make the calculation more explicit by ensuring the conditional expression is evaluated before the division operation. While JavaScript's operator precedence would have handled this correctly anyway, the explicit grouping improves code readability.

apps/web/src/components/panel-preset-selector.tsx (2)

1-27: Well-structured component setup.

Good organization with clear constant definitions for preset labels and descriptions. The use of Record types ensures type safety for all preset values.


35-38: Correct event propagation handling.

Good practice to stop propagation on the reset button click to prevent triggering the parent dropdown item's onClick handler.

apps/web/src/app/editor/[project_id]/page.tsx (2)

33-35: Good state management integration.

Properly destructuring the new activePreset and resetCounter states from the panel store to support the preset functionality.


161-161: Good use of key prop for proper remounting.

The key prop correctly includes both activePreset and resetCounter to ensure panels are properly remounted when switching presets or resetting, which preserves the expected behavior.

Also applies to: 236-236, 311-311, 386-386

Comment on lines 159 to 452
{activePreset === "media" ? (
<ResizablePanelGroup
key={`media-${activePreset}-${resetCounter}`}
direction="horizontal"
className="h-full w-full gap-[0.18rem] px-3 pb-3"
>
{/* Main content area */}
<ResizablePanelGroup
direction="horizontal"
className="h-full w-full gap-[0.19rem] px-3"
<ResizablePanel
defaultSize={toolsPanel}
minSize={15}
maxSize={40}
onResize={setToolsPanel}
className="min-w-0 rounded-sm"
>
<MediaPanel />
</ResizablePanel>

<ResizableHandle withHandle />

<ResizablePanel
defaultSize={100 - toolsPanel}
minSize={60}
className="min-w-0 min-h-0"
>
{/* Tools Panel */}
<ResizablePanel
defaultSize={toolsPanel}
minSize={15}
maxSize={40}
onResize={setToolsPanel}
className="min-w-0 rounded-sm"
<ResizablePanelGroup
direction="vertical"
className="h-full w-full gap-[0.18rem]"
>
<MediaPanel />
</ResizablePanel>
<ResizablePanel
defaultSize={mainContent}
minSize={30}
maxSize={85}
onResize={setMainContent}
className="min-h-0"
>
<ResizablePanelGroup
direction="horizontal"
className="h-full w-full gap-[0.19rem]"
>
<ResizablePanel
defaultSize={previewPanel}
minSize={30}
onResize={setPreviewPanel}
className="min-w-0 min-h-0 flex-1"
>
<PreviewPanel />
</ResizablePanel>

<ResizableHandle withHandle />
<ResizableHandle withHandle />

{/* Preview Area */}
<ResizablePanel
defaultSize={previewPanel}
minSize={30}
onResize={setPreviewPanel}
className="min-w-0 min-h-0 flex-1"
<ResizablePanel
defaultSize={propertiesPanel}
minSize={15}
maxSize={40}
onResize={setPropertiesPanel}
className="min-w-0"
>
<PropertiesPanel />
</ResizablePanel>
</ResizablePanelGroup>
</ResizablePanel>

<ResizableHandle withHandle />

<ResizablePanel
defaultSize={timeline}
minSize={15}
maxSize={70}
onResize={setTimeline}
className="min-h-0"
>
<Timeline />
</ResizablePanel>
</ResizablePanelGroup>
</ResizablePanel>
</ResizablePanelGroup>
) : activePreset === "inspector" ? (
<ResizablePanelGroup
key={`inspector-${activePreset}-${resetCounter}`}
direction="horizontal"
className="h-full w-full gap-[0.18rem] px-3 pb-3"
>
<ResizablePanel
defaultSize={100 - propertiesPanel}
minSize={60}
className="min-w-0 min-h-0"
>
<ResizablePanelGroup
direction="vertical"
className="h-full w-full gap-[0.18rem]"
>
<PreviewPanel />
</ResizablePanel>
<ResizablePanel
defaultSize={mainContent}
minSize={30}
maxSize={85}
onResize={setMainContent}
className="min-h-0"
>
<ResizablePanelGroup
direction="horizontal"
className="h-full w-full gap-[0.19rem]"
>
<ResizablePanel
defaultSize={toolsPanel}
minSize={15}
maxSize={40}
onResize={setToolsPanel}
className="min-w-0 rounded-sm"
>
<MediaPanel />
</ResizablePanel>

<ResizableHandle withHandle />
<ResizableHandle withHandle />

<ResizablePanel
defaultSize={propertiesPanel}
minSize={15}
maxSize={40}
onResize={setPropertiesPanel}
className="min-w-0"
<ResizablePanel
defaultSize={previewPanel}
minSize={30}
onResize={setPreviewPanel}
className="min-w-0 min-h-0 flex-1"
>
<PreviewPanel />
</ResizablePanel>
</ResizablePanelGroup>
</ResizablePanel>

<ResizableHandle withHandle />

<ResizablePanel
defaultSize={timeline}
minSize={15}
maxSize={70}
onResize={setTimeline}
className="min-h-0"
>
<Timeline />
</ResizablePanel>
</ResizablePanelGroup>
</ResizablePanel>

<ResizableHandle withHandle />

<ResizablePanel
defaultSize={propertiesPanel}
minSize={15}
maxSize={40}
onResize={setPropertiesPanel}
className="min-w-0"
>
<PropertiesPanel />
</ResizablePanel>
</ResizablePanelGroup>
) : activePreset === "vertical-preview" ? (
<ResizablePanelGroup
key={`vertical-preview-${activePreset}-${resetCounter}`}
direction="horizontal"
className="h-full w-full gap-[0.18rem] px-3 pb-3"
>
<ResizablePanel
defaultSize={100 - previewPanel}
minSize={60}
className="min-w-0 min-h-0"
>
<ResizablePanelGroup
direction="vertical"
className="h-full w-full gap-[0.18rem]"
>
<PropertiesPanel />
</ResizablePanel>
</ResizablePanelGroup>
</ResizablePanel>

<ResizableHandle withHandle />

{/* Timeline */}
<ResizablePanel
defaultSize={timeline}
minSize={15}
maxSize={70}
onResize={setTimeline}
className="min-h-0 px-3 pb-3"
<ResizablePanel
defaultSize={mainContent}
minSize={30}
maxSize={85}
onResize={setMainContent}
className="min-h-0"
>
<ResizablePanelGroup
direction="horizontal"
className="h-full w-full gap-[0.19rem]"
>
<ResizablePanel
defaultSize={toolsPanel}
minSize={15}
maxSize={40}
onResize={setToolsPanel}
className="min-w-0 rounded-sm"
>
<MediaPanel />
</ResizablePanel>

<ResizableHandle withHandle />

<ResizablePanel
defaultSize={propertiesPanel}
minSize={15}
maxSize={40}
onResize={setPropertiesPanel}
className="min-w-0"
>
<PropertiesPanel />
</ResizablePanel>
</ResizablePanelGroup>
</ResizablePanel>

<ResizableHandle withHandle />

<ResizablePanel
defaultSize={timeline}
minSize={15}
maxSize={70}
onResize={setTimeline}
className="min-h-0"
>
<Timeline />
</ResizablePanel>
</ResizablePanelGroup>
</ResizablePanel>

<ResizableHandle withHandle />

<ResizablePanel
defaultSize={previewPanel}
minSize={30}
onResize={setPreviewPanel}
className="min-w-0 min-h-0"
>
<PreviewPanel />
</ResizablePanel>
</ResizablePanelGroup>
) : (
<ResizablePanelGroup
key={`default-${activePreset}-${resetCounter}`}
direction="vertical"
className="h-full w-full gap-[0.18rem]"
>
<Timeline />
</ResizablePanel>
</ResizablePanelGroup>
<ResizablePanel
defaultSize={mainContent}
minSize={30}
maxSize={85}
onResize={setMainContent}
className="min-h-0"
>
{/* Main content area */}
<ResizablePanelGroup
direction="horizontal"
className="h-full w-full gap-[0.19rem] px-3"
>
{/* Tools Panel */}
<ResizablePanel
defaultSize={toolsPanel}
minSize={15}
maxSize={40}
onResize={setToolsPanel}
className="min-w-0 rounded-sm"
>
<MediaPanel />
</ResizablePanel>

<ResizableHandle withHandle />

{/* Preview Area */}
<ResizablePanel
defaultSize={previewPanel}
minSize={30}
onResize={setPreviewPanel}
className="min-w-0 min-h-0 flex-1"
>
<PreviewPanel />
</ResizablePanel>

<ResizableHandle withHandle />

<ResizablePanel
defaultSize={propertiesPanel}
minSize={15}
maxSize={40}
onResize={setPropertiesPanel}
className="min-w-0"
>
<PropertiesPanel />
</ResizablePanel>
</ResizablePanelGroup>
</ResizablePanel>

<ResizableHandle withHandle />

{/* Timeline */}
<ResizablePanel
defaultSize={timeline}
minSize={15}
maxSize={70}
onResize={setTimeline}
className="min-h-0 px-3 pb-3"
>
<Timeline />
</ResizablePanel>
</ResizablePanelGroup>
)}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Consider refactoring to reduce code duplication.

The current implementation has significant code duplication across the four layout configurations. Each layout repeats similar ResizablePanel and ResizablePanelGroup structures with only arrangement differences. This makes the code harder to maintain and increases the risk of inconsistencies.

Consider extracting common panel components or using a configuration-driven approach to reduce duplication.

Would you like me to help refactor this into a more maintainable structure? We could create reusable panel components or use a configuration object to define each layout's structure.

🤖 Prompt for AI Agents
In apps/web/src/app/editor/[project_id]/page.tsx between lines 159 and 452, the
code has repetitive JSX structures for different activePreset layouts with
similar ResizablePanel and ResizablePanelGroup components. To fix this, refactor
by extracting common panel groups and panels into reusable components or create
a configuration-driven rendering approach that defines layout structures and
panel sizes. This will reduce duplication, improve maintainability, and make it
easier to update layouts consistently.

@enkei64 enkei64 changed the base branch from main to staging August 4, 2025 08:01
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

♻️ Duplicate comments (1)
apps/web/src/app/editor/[project_id]/page.tsx (1)

158-452: Code duplication already flagged - focus on correctness verification.

The significant code duplication in the layout configurations has already been identified in previous reviews. Beyond the duplication concern, verify that each preset layout is correctly implemented and the panel size calculations are accurate.

#!/bin/bash
# Description: Verify that panel size calculations are consistent across all preset layouts
# Expected: All layouts should have consistent min/max sizes and proper default size calculations

# Extract all defaultSize calculations from the layouts
rg -A 1 -B 1 "defaultSize.*-" apps/web/src/app/editor/

# Check for any hardcoded calculations that might be inconsistent
rg "100.*-.*Panel|defaultSize.*[0-9]" apps/web/src/app/editor/\[project_id\]/page.tsx

# Verify that all presets use the same min/max constraints
rg -A 5 -B 5 "minSize|maxSize" apps/web/src/app/editor/\[project_id\]/page.tsx | grep -E "minSize|maxSize" | sort | uniq -c
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3a6bc6e and bb36b2e.

📒 Files selected for processing (2)
  • apps/web/src/app/editor/[project_id]/page.tsx (2 hunks)
  • apps/web/src/stores/panel-store.ts (2 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
**/*.{js,jsx,ts,tsx}

📄 CodeRabbit Inference Engine (.github/copilot-instructions.md)

**/*.{js,jsx,ts,tsx}: Don't use consecutive spaces in regular expression literals.
Don't use the arguments object.
Don't use the comma operator.
Don't write functions that exceed a given Cognitive Complexity score.
Don't use unnecessary boolean casts.
Don't use unnecessary callbacks with flatMap.
Use for...of statements instead of Array.forEach.
Don't create classes that only have static members (like a static namespace).
Don't use this and super in static contexts.
Don't use unnecessary catch clauses.
Don't use unnecessary constructors.
Don't use unnecessary continue statements.
Don't export empty modules that don't change anything.
Don't use unnecessary escape sequences in regular expression literals.
Don't use unnecessary labels.
Don't use unnecessary nested block statements.
Don't rename imports, exports, and destructured assignments to the same name.
Don't use unnecessary string or template literal concatenation.
Don't use String.raw in template literals when there are no escape sequences.
Don't use useless case statements in switch statements.
Don't use ternary operators when simpler alternatives exist.
Don't use useless this aliasing.
Don't initialize variables to undefined.
Don't use the void operators (they're not familiar).
Use arrow functions instead of function expressions.
Use Date.now() to get milliseconds since the Unix Epoch.
Use .flatMap() instead of map().flat() when possible.
Use literal property access instead of computed property access.
Don't use parseInt() or Number.parseInt() when binary, octal, or hexadecimal literals work.
Use concise optional chaining instead of chained logical expressions.
Use regular expression literals instead of the RegExp constructor when possible.
Don't use number literal object member names that aren't base 10 or use underscore separators.
Remove redundant terms from logical expressions.
Use while loops instead of for loops when you don't need initializer and update expressions.
Don't reassign const variables....

Files:

  • apps/web/src/stores/panel-store.ts
  • apps/web/src/app/editor/[project_id]/page.tsx
**/*.{ts,tsx}

📄 CodeRabbit Inference Engine (.github/copilot-instructions.md)

**/*.{ts,tsx}: Don't use primitive type aliases or misleading types.
Don't use empty type parameters in type aliases and interfaces.
Don't use any or unknown as type constraints.
Don't return a value from a function with the return type 'void'.
Don't use the TypeScript directive @ts-ignore.
Don't use TypeScript enums.
Don't export imported variables.
Don't add type annotations to variables, parameters, and class properties that are initialized with literal expressions.
Don't use TypeScript namespaces.
Don't use non-null assertions with the ! postfix operator.
Don't use parameter properties in class constructors.
Don't use user-defined types.
Use as const instead of literal types and type annotations.
Use either T[] or Array<T> consistently.
Initialize each enum member value explicitly.
Use export type for types.
Use import type for types.
Make sure all enum members are literal values.
Don't use TypeScript const enum.
Don't declare empty interfaces.
Don't let variables evolve into any type through reassignments.
Don't use the any type.
Don't misuse the non-null assertion operator (!) in TypeScript files.
Don't use implicit any type on variable declarations.
Don't merge interfaces and classes unsafely.
Don't use overload signatures that aren't next to each other.
Use the namespace keyword instead of the module keyword to declare TypeScript namespaces.
Use consistent accessibility modifiers on class properties and methods.
Use function types instead of object types with call signatures.
Don't use void type outside of generic or return types.

**/*.{ts,tsx}: Don't use TypeScript enums.
Don't export imported variables.
Don't add type annotations to variables, parameters, and class properties that are initialized with literal expressions.
Don't use TypeScript namespaces.
Don't use non-null assertions with the ! postfix operator.
Don't use parameter properties in class constructors.
Don't use user-defined types.
Use as const instead of literal types and...

Files:

  • apps/web/src/stores/panel-store.ts
  • apps/web/src/app/editor/[project_id]/page.tsx
**/*.{jsx,tsx}

📄 CodeRabbit Inference Engine (.github/copilot-instructions.md)

**/*.{jsx,tsx}: Don't use accessKey attribute on any HTML element.
Don't set aria-hidden="true" on focusable elements.
Don't add ARIA roles, states, and properties to elements that don't support them.
Don't use distracting elements like <marquee> or <blink>.
Only use the scope prop on <th> elements.
Don't assign non-interactive ARIA roles to interactive HTML elements.
Make sure label elements have text content and are associated with an input.
Don't assign interactive ARIA roles to non-interactive HTML elements.
Don't assign tabIndex to non-interactive HTML elements.
Don't use positive integers for tabIndex property.
Don't include "image", "picture", or "photo" in img alt prop.
Don't use explicit role property that's the same as the implicit/default role.
Make static elements with click handlers use a valid role attribute.
Always include a title element for SVG elements.
Give all elements requiring alt text meaningful information for screen readers.
Make sure anchors have content that's accessible to screen readers.
Assign tabIndex to non-interactive HTML elements with aria-activedescendant.
Include all required ARIA attributes for elements with ARIA roles.
Make sure ARIA properties are valid for the element's supported roles.
Always include a type attribute for button elements.
Make elements with interactive roles and handlers focusable.
Give heading elements content that's accessible to screen readers (not hidden with aria-hidden).
Always include a lang attribute on the html element.
Always include a title attribute for iframe elements.
Accompany onClick with at least one of: onKeyUp, onKeyDown, or onKeyPress.
Accompany onMouseOver/onMouseOut with onFocus/onBlur.
Include caption tracks for audio and video elements.
Use semantic elements instead of role attributes in JSX.
Make sure all anchors are valid and navigable.
Ensure all ARIA properties (aria-*) are valid.
Use valid, non-abstract ARIA roles for elements with...

Files:

  • apps/web/src/app/editor/[project_id]/page.tsx
🧠 Learnings (4)
📚 Learning: the file apps/web/src/components/editor/media-panel/views/media.tsx uses "use client" directive, mak...
Learnt from: khanguyen74
PR: OpenCut-app/OpenCut#466
File: apps/web/src/components/editor/media-panel/views/media.tsx:47-52
Timestamp: 2025-07-26T21:07:57.582Z
Learning: The file apps/web/src/components/editor/media-panel/views/media.tsx uses "use client" directive, making it client-only code where window object is always available, so SSR safety checks are not needed.

Applied to files:

  • apps/web/src/app/editor/[project_id]/page.tsx
📚 Learning: applies to **/*.{jsx,tsx} : use `<>...` instead of `...`....
Learnt from: CR
PR: OpenCut-app/OpenCut#0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-07-27T22:14:46.402Z
Learning: Applies to **/*.{jsx,tsx} : Use `<>...</>` instead of `<Fragment>...</Fragment>`.

Applied to files:

  • apps/web/src/app/editor/[project_id]/page.tsx
📚 Learning: applies to **/*.{jsx,tsx} : don't use unnecessary fragments....
Learnt from: CR
PR: OpenCut-app/OpenCut#0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-07-27T22:14:46.402Z
Learning: Applies to **/*.{jsx,tsx} : Don't use unnecessary fragments.

Applied to files:

  • apps/web/src/app/editor/[project_id]/page.tsx
📚 Learning: user simonorzel26 prefers incremental refactoring approach: moving code first to reduce pr complexit...
Learnt from: simonorzel26
PR: OpenCut-app/OpenCut#324
File: apps/web/src/components/editor/timeline/timeline-content.tsx:9-34
Timestamp: 2025-07-17T08:22:10.492Z
Learning: User simonorzel26 prefers incremental refactoring approach: moving code first to reduce PR complexity, then cleaning up in separate single-responsibility PRs. This strategy prioritizes safety and manageable PR sizes over immediate perfect code structure.

Applied to files:

  • apps/web/src/app/editor/[project_id]/page.tsx
🔇 Additional comments (9)
apps/web/src/stores/panel-store.ts (7)

4-8: LGTM! Clean type definition for panel presets.

The union type clearly defines the available presets and uses kebab-case consistently for multi-word preset names.


10-16: Well-structured interface for panel sizes.

The PanelSizes interface provides a clean contract for panel size configurations, covering all the necessary panels in the editor.


49-68: LGTM! Well-designed state interface.

The interface properly extends PanelSizes and adds preset-specific state management with clear action definitions.


152-183: LGTM! Well-implemented preset switching logic.

The setActivePreset function correctly:

  • Saves current sizes to the outgoing preset
  • Applies default + custom sizes for the incoming preset
  • Updates the active preset atomically

185-204: LGTM! Proper reset implementation.

The resetPreset function correctly:

  • Clears custom sizes for the target preset
  • Increments reset counter to trigger UI remounts
  • Only updates current panel sizes if resetting the active preset

206-221: LGTM! Clean getter implementation.

The getCurrentPresetSizes function provides a clean way to retrieve current panel sizes.


229-229: LGTM! Proper export of preset configurations.

Exporting PRESET_CONFIGS allows other components to access the default configurations when needed.

apps/web/src/app/editor/[project_id]/page.tsx (2)

33-34: LGTM! Proper integration with panel store.

The addition of activePreset and resetCounter from the panel store enables dynamic layout switching and proper remounting on resets.


160-160: LGTM! Proper key usage for component remounting.

The unique keys combining activePreset and resetCounter ensure proper React component remounting when presets change or are reset, which is essential for the resizable panels to reinitialize with correct sizes.

Also applies to: 235-235, 310-310, 385-385

Comment on lines 18 to 47
const PRESET_CONFIGS: Record<PanelPreset, PanelSizes> = {
default: {
toolsPanel: 25,
previewPanel: 50,
propertiesPanel: 25,
mainContent: 70,
timeline: 30,
},
media: {
toolsPanel: 30,
previewPanel: 45,
propertiesPanel: 25,
mainContent: 100,
timeline: 25,
},
inspector: {
toolsPanel: 25,
previewPanel: 50,
propertiesPanel: 25,
mainContent: 100,
timeline: 25,
},
"vertical-preview": {
toolsPanel: 25,
previewPanel: 40,
propertiesPanel: 35,
mainContent: 100,
timeline: 25,
},
};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

Verify preset configurations align with UI requirements.

The preset configurations define different panel arrangements, but ensure these values work well with the actual UI layouts and provide good user experience across different screen sizes.


🏁 Script executed:

#!/bin/bash
# Description: Check if there are any UI tests or documentation that validate these preset configurations
# Expected: Find test files or documentation that verify these values work well in practice

# Search for test files related to panel presets
fd -e test.ts -e test.tsx -e spec.ts -e spec.tsx | xargs rg -l "preset|panel.*size" 2>/dev/null || echo "No test files found"

# Search for documentation about panel configurations
fd README.md DESIGN.md | xargs rg -l -i "panel|preset" 2>/dev/null || echo "No relevant documentation found"

# Look for any configuration validation or constraints
rg -A 5 -B 5 "minSize|maxSize.*panel" apps/web/src/

Length of output: 22131


Fix panel presets to respect UI min/max constraints

The PRESET_CONFIGS values must fall within the ResizablePanel minSize/maxSize bounds defined in page.tsx:

  • toolsPanel: 15–40
  • previewPanel: ≥30 (no max)
  • propertiesPanel: 15–40
  • mainContent: 30–85
  • timeline: 15–70

Currently, three presets exceed the mainContent maxSize of 85:

• apps/web/src/stores/panel-store.ts (lines 18–47)
media.mainContent = 100
inspector.mainContent = 100
vertical-preview.mainContent = 100

Please adjust these to ≤85 (for example 85) and verify all other values adhere to their respective ranges. Consider adding a test or documentation to enforce these constraints.

🤖 Prompt for AI Agents
In apps/web/src/stores/panel-store.ts between lines 18 and 47, the mainContent
values in the media, inspector, and vertical-preview presets exceed the maxSize
limit of 85. Adjust these mainContent values from 100 to 85 to comply with the
ResizablePanel constraints. Also, review all other panel size values to ensure
they fall within their specified min/max ranges: toolsPanel and propertiesPanel
between 15 and 40, previewPanel at least 30, mainContent between 30 and 85, and
timeline between 15 and 70. Optionally, add a test or documentation to enforce
these size constraints going forward.

Comment on lines +85 to +149
setToolsPanel: (size) => {
const { activePreset, presetCustomSizes } = get();
set({
toolsPanel: size,
presetCustomSizes: {
...presetCustomSizes,
[activePreset]: {
...presetCustomSizes[activePreset],
toolsPanel: size,
},
},
});
},
setPreviewPanel: (size) => {
const { activePreset, presetCustomSizes } = get();
set({
previewPanel: size,
presetCustomSizes: {
...presetCustomSizes,
[activePreset]: {
...presetCustomSizes[activePreset],
previewPanel: size,
},
},
});
},
setPropertiesPanel: (size) => {
const { activePreset, presetCustomSizes } = get();
set({
propertiesPanel: size,
presetCustomSizes: {
...presetCustomSizes,
[activePreset]: {
...presetCustomSizes[activePreset],
propertiesPanel: size,
},
},
});
},
setMainContent: (size) => {
const { activePreset, presetCustomSizes } = get();
set({
mainContent: size,
presetCustomSizes: {
...presetCustomSizes,
[activePreset]: {
...presetCustomSizes[activePreset],
mainContent: size,
},
},
});
},
setTimeline: (size) => {
const { activePreset, presetCustomSizes } = get();
set({
timeline: size,
presetCustomSizes: {
...presetCustomSizes,
[activePreset]: {
...presetCustomSizes[activePreset],
timeline: size,
},
},
});
},
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Reduce code duplication in panel setters.

All five panel setter functions follow identical patterns with only the property name changing. This violates DRY principles and makes maintenance more difficult.

Consider refactoring to a generic setter function:

+      setPanelSize: (panelType: keyof PanelSizes, size: number) => {
+        const { activePreset, presetCustomSizes } = get();
+        set({
+          [panelType]: size,
+          presetCustomSizes: {
+            ...presetCustomSizes,
+            [activePreset]: {
+              ...presetCustomSizes[activePreset],
+              [panelType]: size,
+            },
+          },
+        });
+      },

-      setToolsPanel: (size) => {
-        const { activePreset, presetCustomSizes } = get();
-        set({
-          toolsPanel: size,
-          presetCustomSizes: {
-            ...presetCustomSizes,
-            [activePreset]: {
-              ...presetCustomSizes[activePreset],
-              toolsPanel: size,
-            },
-          },
-        });
-      },
+      setToolsPanel: (size) => get().setPanelSize('toolsPanel', size),

Apply similar changes to the other four setters.

🤖 Prompt for AI Agents
In apps/web/src/stores/panel-store.ts between lines 85 and 149, the five panel
setter functions are nearly identical except for the property they update,
causing code duplication. Refactor by creating a single generic setter function
that takes the panel name and size as parameters, then updates the corresponding
state and presetCustomSizes dynamically. Replace the existing five setters with
calls to this generic function to adhere to DRY principles and simplify
maintenance.

@mazeincoding
Copy link
Collaborator

oh wait the ui is beautiful here, let me merge this, one sec!

mazeincoding added a commit that referenced this pull request Aug 9, 2025
@mazeincoding mazeincoding merged commit 88cb750 into OpenCut-app:staging Aug 9, 2025
1 of 2 checks passed
@mazeincoding
Copy link
Collaborator

mazeincoding commented Aug 9, 2025

broke the github tracking with that merge, will fix in a bit

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants