Skip to content

Comments

Promote project settings to a special layer at the top of the layers list#22

Closed
epavanello wants to merge 4 commits intomainfrom
claude/project-settings-layer-Fl8HI
Closed

Promote project settings to a special layer at the top of the layers list#22
epavanello wants to merge 4 commits intomainfrom
claude/project-settings-layer-Fl8HI

Conversation

@epavanello
Copy link
Owner

@epavanello epavanello commented Feb 15, 2026

Replace the project settings modal with a virtual "project-settings" layer
that sits fixed at the top of the layers panel. When selected, the properties
panel shows project-level fields (dimensions, duration, background, font)
using the same dynamic property system as regular layers, but without
transform, style, time range, keyframe, or animation preset sections.

Key changes:

  • New ProjectSettingsLayer.svelte component registered in the layer system
  • New project-layer.ts with constants, virtual layer synthesis, and helpers
  • ProjectStore auto-selects the project layer on init and loadProject
  • Layers panel renders project layer as a fixed, non-draggable, non-deletable item
  • Properties panel routes project layer property updates to updateProject()
  • Toolbar and project-settings-dialog modal references removed

https://claude.ai/code/session_01REigfXtKvZKtz2JF8sF5HL

Summary by CodeRabbit

  • New Features

    • A Project Settings layer is shown fixed at the top of the layers panel—selectable, non-draggable, and protected from deletion.
    • Edit project properties (width, height, duration, background, font family) directly from the properties panel when the project layer is selected.
    • Empty-state text updated to guide adding animation layers; the add-layer dropdown now hides the project settings entry.
  • Refactor

    • Project Settings button removed from the toolbar; access moved into the layers panel.

…list

Replace the project settings modal with a virtual "project-settings" layer
that sits fixed at the top of the layers panel. When selected, the properties
panel shows project-level fields (dimensions, duration, background, font)
using the same dynamic property system as regular layers, but without
transform, style, time range, keyframe, or animation preset sections.

Key changes:
- New ProjectSettingsLayer.svelte component registered in the layer system
- New project-layer.ts with constants, virtual layer synthesis, and helpers
- ProjectStore auto-selects the project layer on init and loadProject
- Layers panel renders project layer as a fixed, non-draggable, non-deletable item
- Properties panel routes project layer property updates to updateProject()
- Toolbar and project-settings-dialog modal references removed

https://claude.ai/code/session_01REigfXtKvZKtz2JF8sF5HL
@coderabbitai
Copy link

coderabbitai bot commented Feb 15, 2026

Warning

Rate limit exceeded

@epavanello has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 7 minutes and 24 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📝 Walkthrough

Walkthrough

Adds a virtual "project settings" layer type to the editor UI, exposing project properties (width, height, duration, background, fontFamily) as a non-draggable layer; routes edits to the project store, removes the toolbar project-settings dialog, and hides project-settings from the add-layer menu.

Changes

Cohort / File(s) Summary
Project Settings Layer & Utilities
src/lib/layers/components/ProjectSettingsLayer.svelte, src/lib/layers/project-layer.ts
Add a virtual project-settings layer component and utilities/constants (PROJECT_LAYER_ID, PROJECT_LAYER_TYPE, createVirtualProjectLayer, mapProjectLayerPropsToProject, isProjectLayer) plus Zod schema for project props.
Typed Registry
src/lib/layers/typed-registry.ts
Register project-settings in LayerPropsMap using the new layer schema for type-safe props mapping.
Layers Panel & Store Integration
src/lib/components/editor/panels/layers-panel.svelte, src/lib/stores/project.svelte.ts
Insert a fixed, non-draggable project-settings entry at top of layers panel; auto-select project layer by default; prevent deletion and synthesize virtual layer via createVirtualProjectLayer in selectedLayer.
Properties Panel Routing
src/lib/components/editor/panels/properties-panel.svelte
Detect project-settings selection, relabel UI to "Project" terms, route name/props updates through projectStore.updateProject(...) using mapProjectLayerPropsToProject, and conditionally hide/disable layer-specific time/keyframe controls.
Add Layer & Toolbar
src/lib/components/editor/panels/add-layer.svelte, src/lib/components/editor/toolbar.svelte
Hide project-settings from add-layer dropdown; remove toolbar project-settings button, state, and dialog usage.
Misc / Manifests
manifest-file-name, package.json
Minor manifest/package metadata changes referenced in diff.

Sequence Diagram

sequenceDiagram
    participant User
    participant LayersPanel as Layers Panel
    participant ProjectStore
    participant PropertiesPanel as Properties Panel

    User->>LayersPanel: click/select project settings entry
    LayersPanel->>ProjectStore: set selectedLayerId = "__project__"
    ProjectStore->>ProjectStore: selectedLayer getter -> createVirtualProjectLayer(project)
    ProjectStore-->>PropertiesPanel: reactively provide virtual project layer
    PropertiesPanel->>User: render "Project Properties" UI

    User->>PropertiesPanel: edit property (e.g., width)
    PropertiesPanel->>PropertiesPanel: mapProjectLayerPropsToProject(props)
    PropertiesPanel->>ProjectStore: updateProject(partialProject)
    ProjectStore->>ProjectStore: apply project update
    ProjectStore-->>LayersPanel: reactive update (virtual layer reflects new props)
    LayersPanel->>User: re-render project layer display
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

  • Feat/store #17: Modifies ProjectStore and selection logic; touches the same store APIs and virtual-layer handling.
  • file uploads timeline layers #16: Changes removeLayer/selectedLayer behavior and overlaps layers/properties panel updates.

Poem

🐰
I nibble keys and hop in code so bright,
A project-layer stitched into editor light.
Width and height and fonts all snug in place,
No dialog — just a gentle layer embrace.
Hooray for settings, snug in rabbit space!

🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main change: promoting project settings from a modal dialog to a virtual layer displayed at the top of the layers list.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Merge Conflict Detection ✅ Passed ✅ No merge conflicts detected when merging into main

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch claude/project-settings-layer-Fl8HI

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

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@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.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/lib/components/editor/panels/layers-panel.svelte (1)

487-492: ⚠️ Potential issue | 🟡 Minor

"No layers yet" empty state still shows when the project settings layer is visible above.

The empty state message appears when layers.length === 0, but the virtual project settings layer is always visible at the top. Users might find it slightly confusing to see "No layers yet — Add layers from the toolbar" directly below the project settings entry. Consider adjusting the message or adding a subtle visual separator.

🧹 Nitpick comments (4)
src/lib/layers/project-layer.ts (2)

27-55: Consider returning TypedLayer<'project-settings'> for stronger typing.

The return type TypedLayer (unparameterized) resolves to Layer, losing the type-level association with 'project-settings'. Using the parameterized form would give consumers typed props.

♻️ Proposed fix
-export function createVirtualProjectLayer(project: Project): TypedLayer {
+export function createVirtualProjectLayer(project: Project): TypedLayer<'project-settings'> {

60-69: Use Zod schema validation instead of unsafe type casting.

mapProjectLayerPropsToProject casts unknown values without validation (e.g., props.width as number). The ProjectSchema already exists in src/lib/schemas/animation.ts with proper validation rules (.positive() for numeric fields, enum for fontFamily). Validate the props object against ProjectSchema.partial() before returning updates, per the coding guideline: "Validate all external data with Zod schemas before using in the application."

src/lib/stores/project.svelte.ts (1)

143-146: After removing the selected layer, selection falls back to null instead of the project layer.

removeLayer and ungroupLayers (line 267) reset selectedLayerId to null when the deleted/dissolved layer was selected. With the new paradigm where the project layer is always available and auto-selected on init/load, falling back to PROJECT_LAYER_ID would be more consistent and avoid showing the "No layer selected" empty state.

♻️ Proposed fix (removeLayer)
     if (this.selectedLayerId === layerId) {
-      this.selectedLayerId = null;
+      this.selectedLayerId = PROJECT_LAYER_ID;
     }

Apply the same change in ungroupLayers at line 267.

src/lib/components/editor/panels/properties-panel.svelte (1)

309-314: Guard only covers target === 'props' — non-props updates for the project layer would silently fail.

If updateProperty is called with target === 'transform' or 'style' while the project layer is selected, it falls through to projectStore.updateLayer(PROJECT_LAYER_ID, ...), which won't find a matching layer and silently does nothing. Currently safe because the Transform/Style UI is hidden, but a broader early return would be more defensive.

🛡️ Proposed defensive guard
     if (!selectedLayer) return;

     // For the project settings layer, route props directly to updateProject
-    if (isProjectSettings && target === 'props') {
+    if (isProjectSettings) {
+      if (target !== 'props') return; // Only props are editable on the project layer
       const projectUpdates = mapProjectLayerPropsToProject({ [propertyName]: value });
       projectStore.updateProject(projectUpdates);
       return;
     }

Clarify that the empty state refers to animation layers, not the project
settings layer which is always visible. Add a visual separator and update
the message to be more specific and helpful.

Changes:
- Message: "No layers yet" → "No animation layers yet"
- Action: "Add layers from the toolbar" → "Click the + button above to add your first layer"
- Added border-top separator to visually distinguish from project settings layer

https://claude.ai/code/session_01REigfXtKvZKtz2JF8sF5HL
Copy link

@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

🤖 Fix all issues with AI agents
In `@src/lib/components/editor/panels/layers-panel.svelte`:
- Around line 204-222: The project-settings row (ProjectIcon / selectLayer /
PROJECT_LAYER_ID block) lacks the intended visual separator when animation
layers exist; add the border-top separator so it always appears by applying the
CSS border class (e.g., "border-t" or equivalent utility) to the correct
element—either add the border class to the project-settings div that contains
ProjectIcon and projectStore.state.name or insert a small separator element
immediately after that div and before the {`#each`} animation layers list—so the
separator is consistently shown regardless of the empty-state branch.

Comment on lines 204 to 222
<!-- Project Settings Layer (always fixed at top, non-draggable, non-deletable) -->
{@const ProjectIcon = getLayerDefinition(PROJECT_LAYER_TYPE).icon}
<div
class={cn(
'flex cursor-pointer items-center gap-2 rounded-md px-3 py-2 transition-colors hover:bg-muted/50',
{
'bg-muted': isProjectLayer(projectStore.selectedLayerId)
}
)}
onclick={() => selectLayer(PROJECT_LAYER_ID)}
onkeydown={(e) => handleKeyDown(e, PROJECT_LAYER_ID)}
role="button"
tabindex="0"
>
<ProjectIcon class="size-4 shrink-0" />
<div class="flex-1 truncate text-sm font-medium">
{projectStore.state.name}
</div>
</div>
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Visual separator between project layer and animation layers may be missing.

The commit message mentions adding "a border-top separator to visually distinguish the project settings layer from animation layers," but the border-t is only applied inside the empty-state block (Line 488). When animation layers do exist, there's no visual separator between this project-settings row and the first animation layer.

If the separator is intended to always be present, consider adding it around the {#each} block or after this project-settings div:

Proposed fix
   </div>
+
+  <!-- Separator between project settings and animation layers -->
+  <div class="border-t"></div>

   {`#each` layerTree as { layer, children, index } (layer.id)}
🤖 Prompt for AI Agents
In `@src/lib/components/editor/panels/layers-panel.svelte` around lines 204 - 222,
The project-settings row (ProjectIcon / selectLayer / PROJECT_LAYER_ID block)
lacks the intended visual separator when animation layers exist; add the
border-top separator so it always appears by applying the CSS border class
(e.g., "border-t" or equivalent utility) to the correct element—either add the
border class to the project-settings div that contains ProjectIcon and
projectStore.state.name or insert a small separator element immediately after
that div and before the {`#each`} animation layers list—so the separator is
consistently shown regardless of the empty-state branch.

Copy link

@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.

🧹 Nitpick comments (2)
src/lib/components/editor/panels/properties-panel.svelte (2)

306-311: Guard is only for target === 'props' — confirm transform/style paths are unreachable.

The early return only triggers when target === 'props'. If a code path somehow calls updateProperty(...) with target === 'transform' or 'style' while the project layer is selected, it would fall through to updateAnimatableValue and attempt keyframe/layer mutation on the virtual layer. This is currently safe because the UI hides those sections (line 432), but the guard would be more robust if it also covered those targets.

Suggested defensive guard
     // For the project settings layer, route props directly to updateProject
-    if (isProjectSettings && target === 'props') {
+    if (isProjectSettings) {
+      if (target !== 'props') return; // transform/style not applicable
       const projectUpdates = mapProjectLayerPropsToProject({ [propertyName]: value });
       projectStore.updateProject(projectUpdates);
       return;
     }

811-817: Custom property components still rendered and receive addKeyframe for the project settings layer.

layerCustomPropertyComponents is rendered unconditionally. If the project settings layer definition ever defines customPropertyComponents, they would receive the addKeyframe callback which would attempt to mutate the virtual layer's keyframes. Consider gating this block or at least passing undefined for addKeyframe when isProjectSettings is true, consistent with how you handle it for the schema-driven properties above (lines 764, 800).

Suggested fix
           {`#each` layerCustomPropertyComponents as [name, { component: CustomPropertyComponent }] (name)}
             <CustomPropertyComponent
               layer={selectedLayer}
               onUpdateProp={(name, v) => updateProperty(name, v, 'props')}
-              {addKeyframe}
+              addKeyframe={isProjectSettings ? undefined : addKeyframe}
             />
           {/each}

@epavanello epavanello closed this Feb 18, 2026
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