Skip to content

Layout switcher#15

Merged
ronak-create merged 3 commits into
ronak-create:mainfrom
PlkMarudny:layout-switcher
Jul 13, 2026
Merged

Layout switcher#15
ronak-create merged 3 commits into
ronak-create:mainfrom
PlkMarudny:layout-switcher

Conversation

@PlkMarudny

@PlkMarudny PlkMarudny commented Jul 13, 2026

Copy link
Copy Markdown

What does this PR do?

Top toolbar has a track height switcher that allows to switch track height between three predefined values:

  1. Large (original)
  2. Medium - reduced track height
  3. Small - tracks without thumbnails with outline size set to 1px

Choosing the track height resizes the monitor/timeline panels to show all the tracks without overflowing

Closes #

Type of change

  • Bug fix
  • New feature (GUI - resizable tracks)
  • Docs
  • Refactor / internal

How was it verified?

  • node --check server.js && node --check app.js && node --check mcp-server.js passes
  • Opened the editor and confirmed the change in preview
  • Confirmed the change in an export (fast or realtime), if it affects rendering
  • Updated CLAUDE.md / README.md if the schema, props, or API changed

Checklist

  • No new runtime dependencies added
  • Preview and export render identically (single compositor)
  • Commits are focused and messages are descriptive

Summary by CodeRabbit

  • New Features

    • Added Small, Medium, and Large timeline track density options.
    • Added controls to resize the upper workspace and timeline panels.
    • Added a Layout reset option.
    • Timeline panel sizing now persists and automatically adjusts to fit tracks.
    • Video thumbnails adapt to the selected track density.
  • UI Improvements

    • Updated the workspace layout and resizing interactions for improved flexibility.

@coderabbitai

coderabbitai Bot commented Jul 13, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Added timeline density presets with persistent state, conditional video thumbnails, resizable upper/timeline panels, layout reset controls, pane-height persistence, automatic sizing, and corresponding HTML/CSS updates.

Changes

Timeline layout

Layer / File(s) Summary
Track density and thumbnail rendering
app.js
Adds S/M/L track presets, persists the selected density, applies track heights, and renders video thumbnails only for supported presets.
Resizable upper and timeline split
app.js, index.html, style.css
Adds layout controls and a draggable split handle with pointer resizing, reset behavior, height persistence, clamping, and boot-time initialization.
Timeline and interface styling
style.css
Updates flex-based layout sizing, split-panel interaction styles, density-specific clip styling, and expanded UI styling rules.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant LayoutTools
  participant initPanelSplit
  participant timelinePanel
  participant localStorage
  User->>LayoutTools: drag split handle or choose Layout reset
  LayoutTools->>initPanelSplit: update or restore panel height
  initPanelSplit->>timelinePanel: clamp and apply height
  initPanelSplit->>localStorage: persist layout height
Loading
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 inconclusive)

Check name Status Explanation Resolution
Title check ❓ Inconclusive The title is very generic and does not clearly describe the new track-height and resizable timeline layout feature. Rename it to something specific like "Add track height presets and resizable timeline layout".
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🧹 Nitpick comments (3)
style.css (2)

106-117: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Deprecated clip property in .sr-only.

Stylelint flags clip: rect(0, 0, 0, 0); as deprecated; the modern visually-hidden pattern uses clip-path: inset(50%); instead.

🎨 Proposed fix
   overflow: hidden;
-  clip: rect(0, 0, 0, 0);
+  clip-path: inset(50%);
   white-space: nowrap;
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@style.css` around lines 106 - 117, Update the `.sr-only` visually hidden
style by removing the deprecated `clip` declaration and using the modern
`clip-path: inset(50%)` declaration instead, while preserving the existing
accessibility and layout-hiding behavior.

Source: Linters/SAST tools


566-566: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Unquoted font name for consistency with Stylelint and the rest of the file.

Stylelint's font-family-name-quotes flags the quoted "Consolas" here; lines 638 and 1058 already use it unquoted.

🎨 Proposed fix
-  font: 600 14px/1 "Consolas", monospace;
+  font: 600 14px/1 Consolas, monospace;
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@style.css` at line 566, Update the font shorthand declaration to use the
unquoted Consolas font name, matching the existing style at nearby declarations
and satisfying Stylelint’s font-family-name-quotes rule; preserve the monospace
fallback and other font properties.

Source: Linters/SAST tools

app.js (1)

2839-2839: 🚀 Performance & Scalability | 🔵 Trivial | 💤 Low value

Consider debouncing the resize handler.

Every native resize event forces a synchronous getBoundingClientRect() read plus a style mutation via clampTimelineHeight()/setTimelineHeight(). Harmless at current scale, but worth debouncing if this becomes a hot path.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app.js` at line 2839, Debounce the resize listener around clampTimelineHeight
so rapid native resize events do not repeatedly trigger synchronous layout reads
and style updates. Preserve setting state.dirtyTimeline on resize and ensure the
debounced handler still invokes clampTimelineHeight after resizing settles.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@app.js`:
- Around line 2948-2951: Update the resize handle’s "dblclick" event handler to
call restoreDefaultLayout() instead of resetTimelineHeight(), so double-clicking
restores both the default track size and pane height consistently with the "⊡
Layout" button.
- Around line 2845-2852: Update availableTimelineMax() to subtract the .app
container’s 12px total vertical padding before calculating the timeline maximum,
while preserving the existing topbarH, UPPER_MIN, split, and gaps deductions.

In `@index.html`:
- Around line 103-104: Make the upper timeline separator keyboard-operable and
accessible: in index.html (103-104), update `#splitUpperTimeline` with tabindex
and appropriate separator value attributes or an accessible label; in app.js
(2927-2947), add keyboard handling on handle for vertical arrow resizing through
setTimelineHeight() and persist the result using TL_H_KEY; in style.css (60-83),
add a visible :focus-visible style for .v-split.

In `@style.css`:
- Around line 320-325: Merge the later grid-layout declarations into the
existing `.upper` rule instead of defining a duplicate selector, preserving the
earlier `min-height: 140px` floor while retaining the grid columns, gap, and
other layout properties.

---

Nitpick comments:
In `@app.js`:
- Line 2839: Debounce the resize listener around clampTimelineHeight so rapid
native resize events do not repeatedly trigger synchronous layout reads and
style updates. Preserve setting state.dirtyTimeline on resize and ensure the
debounced handler still invokes clampTimelineHeight after resizing settles.

In `@style.css`:
- Around line 106-117: Update the `.sr-only` visually hidden style by removing
the deprecated `clip` declaration and using the modern `clip-path: inset(50%)`
declaration instead, while preserving the existing accessibility and
layout-hiding behavior.
- Line 566: Update the font shorthand declaration to use the unquoted Consolas
font name, matching the existing style at nearby declarations and satisfying
Stylelint’s font-family-name-quotes rule; preserve the monospace fallback and
other font properties.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: d2801d9c-49b0-404c-8127-1140322138ed

📥 Commits

Reviewing files that changed from the base of the PR and between e9286c2 and 503eb38.

📒 Files selected for processing (3)
  • app.js
  • index.html
  • style.css

Comment thread app.js
Comment on lines +2845 to +2852
function availableTimelineMax() {
const app = $("app").getBoundingClientRect();
const topbar = document.querySelector(".topbar")?.getBoundingClientRect();
const topbarH = topbar ? topbar.height : 46;
const split = 6;
const gaps = 18; // three 6px flex gaps between four children
return Math.floor(app.height - topbarH - UPPER_MIN - split - gaps);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

availableTimelineMax() omits .app's own padding, over-allocating space for the timeline pane.

style.css sets .app { padding: 6px; }, so the flex children's content box is app.height - 12px, not app.height. This function only subtracts topbarH, UPPER_MIN, split, and gaps, so the computed max is 12px too large. Combined with the .upper min-height override flagged in style.css (no CSS floor left to catch the overshoot), dragging/fitting the timeline to max will silently squeeze .upper below its intended 140px minimum instead of being clamped.

🐛 Proposed fix
 function availableTimelineMax() {
   const app = $("app").getBoundingClientRect();
+  const appStyle = getComputedStyle($("app"));
+  const appPad = parseFloat(appStyle.paddingTop) + parseFloat(appStyle.paddingBottom);
   const topbar = document.querySelector(".topbar")?.getBoundingClientRect();
   const topbarH = topbar ? topbar.height : 46;
   const split = 6;
   const gaps = 18; // three 6px flex gaps between four children
-  return Math.floor(app.height - topbarH - UPPER_MIN - split - gaps);
+  return Math.floor(app.height - appPad - topbarH - UPPER_MIN - split - gaps);
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
function availableTimelineMax() {
const app = $("app").getBoundingClientRect();
const topbar = document.querySelector(".topbar")?.getBoundingClientRect();
const topbarH = topbar ? topbar.height : 46;
const split = 6;
const gaps = 18; // three 6px flex gaps between four children
return Math.floor(app.height - topbarH - UPPER_MIN - split - gaps);
}
function availableTimelineMax() {
const app = $("app").getBoundingClientRect();
const appStyle = getComputedStyle($("app"));
const appPad = parseFloat(appStyle.paddingTop) + parseFloat(appStyle.paddingBottom);
const topbar = document.querySelector(".topbar")?.getBoundingClientRect();
const topbarH = topbar ? topbar.height : 46;
const split = 6;
const gaps = 18; // three 6px flex gaps between four children
return Math.floor(app.height - appPad - topbarH - UPPER_MIN - split - gaps);
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app.js` around lines 2845 - 2852, Update availableTimelineMax() to subtract
the .app container’s 12px total vertical padding before calculating the timeline
maximum, while preserving the existing topbarH, UPPER_MIN, split, and gaps
deductions.

Comment thread app.js
Comment on lines +2948 to +2951
handle.addEventListener("dblclick", (e) => {
e.preventDefault();
resetTimelineHeight();
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Double-click only resets the pane height, not the "default layout".

The PR's commit message describes double-click as "Restore the default layout when the resize handle is double-clicked," and restoreDefaultLayout() (used by the "⊡ Layout" button) already implements that semantic — resetting both track size and pane height. This handler calls resetTimelineHeight() alone, so if the user is on the "S"/"M" preset, double-clicking the splitter won't restore the "L" default despite the stated intent.

🐛 Proposed fix
   handle.addEventListener("dblclick", (e) => {
     e.preventDefault();
-    resetTimelineHeight();
+    restoreDefaultLayout();
   });
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
handle.addEventListener("dblclick", (e) => {
e.preventDefault();
resetTimelineHeight();
});
handle.addEventListener("dblclick", (e) => {
e.preventDefault();
restoreDefaultLayout();
});
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app.js` around lines 2948 - 2951, Update the resize handle’s "dblclick" event
handler to call restoreDefaultLayout() instead of resetTimelineHeight(), so
double-clicking restores both the default track size and pane height
consistently with the "⊡ Layout" button.

Comment thread index.html
Comment on lines +103 to +104
<div class="v-split" id="splitUpperTimeline" role="separator" aria-orientation="horizontal" title="Drag to resize · double-click to reset"></div>

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Timeline/upper split handle is not keyboard-operable. role="separator" on #splitUpperTimeline implies it should support focus and keyboard resizing per ARIA authoring practices, but the markup, JS, and CSS only implement pointer/mouse interaction.

  • index.html#L103-L104: add tabindex="0", aria-valuemin/aria-valuemax/aria-valuenow (or aria-label) to #splitUpperTimeline so it's focusable and its current size is exposed to assistive tech.
  • app.js#L2927-L2947: add a keydown listener on handle (e.g. ArrowUp/ArrowDown calling setTimelineHeight() and persisting to TL_H_KEY, mirroring the existing pointer-drag logic).
  • style.css#L60-L83: add a :focus-visible style for .v-split so keyboard focus is visible once it becomes focusable.
📍 Affects 3 files
  • index.html#L103-L104 (this comment)
  • app.js#L2927-L2947
  • style.css#L60-L83
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@index.html` around lines 103 - 104, Make the upper timeline separator
keyboard-operable and accessible: in index.html (103-104), update
`#splitUpperTimeline` with tabindex and appropriate separator value attributes or
an accessible label; in app.js (2927-2947), add keyboard handling on handle for
vertical arrow resizing through setTimelineHeight() and persist the result using
TL_H_KEY; in style.css (60-83), add a visible :focus-visible style for .v-split.

Comment thread style.css
Comment on lines +320 to +325
.upper {
display: grid;
grid-template-columns: 280px minmax(0, 1fr) 280px;
gap: 6px;
min-height: 0;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Duplicate .upper selector overrides the flex-layout min-height: 140px with min-height: 0.

This grid-layout .upper rule and the earlier flex-item .upper rule (lines 48-52) both set min-height on the same selector/specificity; since this block appears later in the file, min-height: 0 wins, silently discarding the 140px floor that's meant to match app.js's UPPER_MIN constant. That removes the CSS safety net that would otherwise stop .upper from shrinking below 140px if the timeline pane is over-allocated (see the related availableTimelineMax() padding bug in app.js).

🐛 Proposed fix (merge into a single rule)
-.upper {
-  flex: 1 1 0;
-  min-height: 140px;
-  min-width: 0;
-}
-...
 .upper {
   display: grid;
   grid-template-columns: 280px minmax(0, 1fr) 280px;
   gap: 6px;
-  min-height: 0;
+  flex: 1 1 0;
+  min-height: 140px;
+  min-width: 0;
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
.upper {
display: grid;
grid-template-columns: 280px minmax(0, 1fr) 280px;
gap: 6px;
min-height: 0;
}
.upper {
display: grid;
grid-template-columns: 280px minmax(0, 1fr) 280px;
gap: 6px;
flex: 1 1 0;
min-height: 140px;
min-width: 0;
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@style.css` around lines 320 - 325, Merge the later grid-layout declarations
into the existing `.upper` rule instead of defining a duplicate selector,
preserving the earlier `min-height: 140px` floor while retaining the grid
columns, gap, and other layout properties.

@ronak-create ronak-create merged commit bf7e707 into ronak-create:main Jul 13, 2026
2 checks passed
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