Skip to content

fix(editor): guard timeline speed overlap#585

Merged
meiiie merged 2 commits into
mainfrom
fix/timeline-speed-layout
May 24, 2026
Merged

fix(editor): guard timeline speed overlap#585
meiiie merged 2 commits into
mainfrom
fix/timeline-speed-layout

Conversation

@meiiie
Copy link
Copy Markdown
Collaborator

@meiiie meiiie commented May 24, 2026

Summary

  • Adds a small speed-change planner for clip speed edits.
  • Blocks speed edits that would overlap the next clip or cause scaled zoom regions to overlap.
  • Shows a compact speed badge on clip timeline items for non-1x speeds.

Root Cause

Changing a clip to a slower speed extends the clip duration, but the current timeline model does not separate source-time anchors from timeline-time anchors. Auto-shifting downstream clips would be a broader architecture change, so this hotfix prevents creating an overlapping timeline state.

Scope

This PR only addresses issue #581 item 3/4: speed-edited timeline overlap and visible speed information. It does not change the Windows HUD/input blocker or Lightning export routing.

Local Checks

  • npm test -- src/components/video-editor/clipSpeedChange.test.ts src/components/video-editor/timeline/model/timelineModel.test.ts
  • npx tsc --noEmit --pretty false
  • npx biome check src/components/video-editor/clipSpeedChange.ts src/components/video-editor/clipSpeedChange.test.ts src/components/video-editor/VideoEditor.tsx src/components/video-editor/timeline/Item.tsx src/components/video-editor/timeline/components/viewport/TimelineCanvas.tsx src/components/video-editor/timeline/model/timelineModel.ts src/components/video-editor/timeline/model/timelineModel.test.ts --formatter-enabled=false
  • git diff --check

Closes part of #581.

Summary by CodeRabbit

  • New Features

    • Per-clip playback speed adjustment with automatic validation
    • Speed indicators shown on timeline clips
  • Bug Fixes / Reliability

    • Prevents speed changes that would overlap adjacent clips or zoom regions; shows localized warnings
  • Tests

    • Added unit tests covering speed-change planning and speed-label formatting

Review Change Stack

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 24, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 4d7b5ab6-76e3-466b-a961-f127c753a042

📥 Commits

Reviewing files that changed from the base of the PR and between fbc6ec9 and b52bded.

📒 Files selected for processing (1)
  • src/components/video-editor/clipSpeedChange.test.ts

📝 Walkthrough

Walkthrough

This PR introduces clip speed change planning logic to validate and apply per-clip playback speed adjustments while maintaining clip and zoom region timing consistency. The planning module is integrated into VideoEditor's speed-change handler and the timeline is updated to expose and display clip-specific speeds throughout the UI.

Changes

Clip Speed Change Feature

Layer / File(s) Summary
Clip speed change planning module
src/components/video-editor/clipSpeedChange.ts
Defines ClipSpeedChangePlan, BlockedClipSpeedChange, and ClipSpeedChangeBlockReason types. Exports formatClipSpeedLabel to produce "Nx" labels (e.g., "0.5x", "2x") for non-default speeds, and planClipSpeedChange to compute updated clip/zoom timings, validate against clip-overlap and zoom-overlap constraints, and return either a valid plan or a blocked outcome with a specific reason.
Planning module unit tests
src/components/video-editor/clipSpeedChange.test.ts
Comprehensive Vitest coverage: formatClipSpeedLabel returns null for default/invalid speeds and "Nx" format for positive non-1 speeds; planClipSpeedChange extends isolated clips when slowing, blocks slowdowns that overlap the next clip with blockedReason: "clip-overlap", scales zoom region timings proportionally within the affected clip, and blocks changes that would cause scaled zooms to overlap unchanged zooms with blockedReason: "zoom-overlap".
VideoEditor speed-change handler integration
src/components/video-editor/VideoEditor.tsx
Imports planClipSpeedChange and replaces manual clip-speed scaling logic with delegated planning: calls planner with current clip/zoom state and requested speed, shows localized toast warnings for blocked changes (distinguishing clip-overlap vs. zoom-overlap messages), and applies the plan to update both clipRegions and zoomRegions.
Timeline model speed exposure
src/components/video-editor/timeline/model/timelineModel.ts, src/components/video-editor/timeline/model/timelineModel.test.ts
Updates buildTimelineItems to compute formatClipSpeedLabel for each clip, conditionally appending the speed label to the clip label (e.g., "Clip 1 (0.5x)") and exposing a numeric speedValue field on timeline items for downstream rendering. Adds test verifying speed label formatting and speedValue population.
Timeline UI speed display
src/components/video-editor/timeline/Item.tsx, src/components/video-editor/timeline/components/viewport/TimelineCanvas.tsx
Item.tsx imports formatClipSpeedLabel and renders a formatted speed badge when speedValue is present; TimelineCanvas.tsx passes the speedValue prop from the timeline item to the rendered clip Item component, enabling speed display in the UI.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • webadderallorg/Recordly#454: Both PRs update VideoEditor.tsx clip speed change logic; this PR plans/validates speed changes while the retrieved PR derives timelineDuration from the updated clipRegions to keep slowed clips visible.

Suggested labels

Checked

Poem

🐰 I hop through clips that speed or slow,
I scale the zooms where they should go.
If overlaps loom, I'll give a shout,
Plan it safe, then show the badge throughout. ✨

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title 'fix(editor): guard timeline speed overlap' clearly and concisely describes the main change: preventing timeline overlap when editing clip speeds.
Description check ✅ Passed The PR description includes Summary, Root Cause, Scope, and Local Checks sections. Most required template sections are addressed with clear, substantial content explaining the purpose, motivation, and testing approach.
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.

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

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/timeline-speed-layout

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

ESLint skipped: no ESLint configuration detected in root package.json. To enable, add eslint to devDependencies.


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.

@meiiie
Copy link
Copy Markdown
Collaborator Author

meiiie commented May 24, 2026

@coderabbitai review

@meiiie meiiie mentioned this pull request May 24, 2026
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 24, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@meiiie meiiie marked this pull request as ready for review May 24, 2026 14:29
Copy link
Copy Markdown
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.

🧹 Nitpick comments (2)
src/components/video-editor/clipSpeedChange.test.ts (2)

18-78: ⚡ Quick win

Consider expanding test coverage for additional scenarios.

The current test suite covers the core behaviors well. To further strengthen the tests, consider adding scenarios for:

  1. Invalid inputs:

    • selectedClipId that doesn't exist → should return null
    • Invalid speed values (0, negative, NaN, Infinity) → should return null
  2. Speeding up clips:

    • Test with speed: 2 to verify clip shortens correctly
  3. Default speed handling:

    • Clip with speed undefined or invalid → should be treated as 1x
  4. Zoom boundary cases:

    • Zoom region that starts outside the clip → should not scale
    • Multiple zooms inside clip that both scale and overlap each other

These additions would provide more comprehensive coverage and document edge-case behavior.

🤖 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 `@src/components/video-editor/clipSpeedChange.test.ts` around lines 18 - 78,
Add unit tests for planClipSpeedChange to cover missing/invalid inputs and edge
cases: add a test where selectedClipId doesn't exist (expect null), tests for
invalid speeds (0, negative, NaN, Infinity) returning null, a test for speeding
up (speed: 2) asserting the selected clip shortens accordingly via clipRegions,
a test where a clip has undefined/invalid speed and is treated as 1x, a test
with a zoom region that starts outside the selected clip (ensure it is not
scaled), and a test with multiple zooms inside the clip that when scaled would
overlap (assert blockedReason "zoom-overlap"); reference planClipSpeedChange and
the BlockedClipSpeedChange shape for expected outputs and use existing patterns
from clipSpeedChange.test.ts to structure assertions.

9-16: ⚡ Quick win

Consider adding edge case tests for completeness.

The current tests cover the main behaviors, but could be strengthened by adding test cases for:

  • Negative speeds: formatClipSpeedLabel(-1)null
  • Non-finite values: formatClipSpeedLabel(Infinity)null, formatClipSpeedLabel(NaN)null

These edge cases are handled by the implementation (line 15), and adding explicit tests would improve coverage and prevent regressions.

📋 Suggested additional test cases
 	it("returns labels only for non-default positive speeds", () => {
 		expect(formatClipSpeedLabel(1)).toBeNull();
 		expect(formatClipSpeedLabel(0)).toBeNull();
+		expect(formatClipSpeedLabel(-1)).toBeNull();
+		expect(formatClipSpeedLabel(Number.POSITIVE_INFINITY)).toBeNull();
+		expect(formatClipSpeedLabel(Number.NaN)).toBeNull();
 		expect(formatClipSpeedLabel(0.5)).toBe("0.5x");
 		expect(formatClipSpeedLabel(2)).toBe("2x");
 	});
🤖 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 `@src/components/video-editor/clipSpeedChange.test.ts` around lines 9 - 16, Add
edge-case assertions to the formatClipSpeedLabel test: verify that negative
speeds (e.g., formatClipSpeedLabel(-1)) return null and that non-finite values
(e.g., formatClipSpeedLabel(Infinity) and formatClipSpeedLabel(NaN)) also return
null; update the "returns labels only for non-default positive speeds" spec in
clipSpeedChange.test.ts to include these three expectations so the tests
explicitly cover the negative and non-finite branches in formatClipSpeedLabel.
🤖 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.

Nitpick comments:
In `@src/components/video-editor/clipSpeedChange.test.ts`:
- Around line 18-78: Add unit tests for planClipSpeedChange to cover
missing/invalid inputs and edge cases: add a test where selectedClipId doesn't
exist (expect null), tests for invalid speeds (0, negative, NaN, Infinity)
returning null, a test for speeding up (speed: 2) asserting the selected clip
shortens accordingly via clipRegions, a test where a clip has undefined/invalid
speed and is treated as 1x, a test with a zoom region that starts outside the
selected clip (ensure it is not scaled), and a test with multiple zooms inside
the clip that when scaled would overlap (assert blockedReason "zoom-overlap");
reference planClipSpeedChange and the BlockedClipSpeedChange shape for expected
outputs and use existing patterns from clipSpeedChange.test.ts to structure
assertions.
- Around line 9-16: Add edge-case assertions to the formatClipSpeedLabel test:
verify that negative speeds (e.g., formatClipSpeedLabel(-1)) return null and
that non-finite values (e.g., formatClipSpeedLabel(Infinity) and
formatClipSpeedLabel(NaN)) also return null; update the "returns labels only for
non-default positive speeds" spec in clipSpeedChange.test.ts to include these
three expectations so the tests explicitly cover the negative and non-finite
branches in formatClipSpeedLabel.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: c061a103-5b09-4f5b-a51b-527de9fe22df

📥 Commits

Reviewing files that changed from the base of the PR and between 9f1c697 and fbc6ec9.

📒 Files selected for processing (7)
  • src/components/video-editor/VideoEditor.tsx
  • src/components/video-editor/clipSpeedChange.test.ts
  • src/components/video-editor/clipSpeedChange.ts
  • src/components/video-editor/timeline/Item.tsx
  • src/components/video-editor/timeline/components/viewport/TimelineCanvas.tsx
  • src/components/video-editor/timeline/model/timelineModel.test.ts
  • src/components/video-editor/timeline/model/timelineModel.ts

@meiiie meiiie merged commit 33fc855 into main May 24, 2026
3 checks passed
@meiiie meiiie deleted the fix/timeline-speed-layout branch May 24, 2026 14:38
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.

1 participant