Skip to content

feat(preview): add loop toggle for timeline playback#795

Open
avillagran wants to merge 1 commit into
OpenCut-app:mainfrom
avillagran:feat/loop-toggle
Open

feat(preview): add loop toggle for timeline playback#795
avillagran wants to merge 1 commit into
OpenCut-app:mainfrom
avillagran:feat/loop-toggle

Conversation

@avillagran
Copy link
Copy Markdown

@avillagran avillagran commented May 16, 2026

Summary

Adds a loop toggle button next to the play/pause control in the preview toolbar. When enabled, preview playback wraps back to the start of the timeline instead of pausing once the playhead reaches the end. The state is persisted as a per-project setting (`TProjectSettings.loop`) so it survives reloads and is remembered per project. Defaults to off.

What changed

  • `TProjectSettings.loop?: boolean` — optional field, undefined/false means current behaviour (pause at end).
  • `LoopToggleButton` — new icon button in the preview toolbar, placed next to the play/pause control. Toggles through `UpdateProjectSettingsCommand`, so the action is undoable / redoable.
  • `PlaybackManager.updateTime` — when the playhead reaches the end and `settings.loop` is true, the manager resets its internal start times to zero, fires a seek event, and queues another animation frame instead of pausing. The audio manager reacts to the seek through its existing `onSeek` listener, so audio restarts in sync.

Test plan

  • Manual: timeline > 0 duration, toggle loop on, play → playhead returns to start and continues looping until manually paused; audio restarts in sync each cycle.
  • Manual: with loop off (default), playback still pauses at the end as before.
  • Manual: toggle state survives a reload (persisted in the saved project).
  • TypeScript: no new errors introduced.

Notes

Summary by CodeRabbit

  • New Features
    • Added a loop toggle button in the preview toolbar for quick playback control.
    • Preview playback can now loop: when enabled, playback wraps to the timeline start instead of pausing at the end.
    • Introduced a project-level loop setting (off by default) to enable/disable preview looping.

Review Change Stack

@vercel
Copy link
Copy Markdown

vercel Bot commented May 16, 2026

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

A member of the Team first needs to authorize it.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 16, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: a2d7d5b6-8929-4169-b660-c27f196c1176

📥 Commits

Reviewing files that changed from the base of the PR and between 5f0f5e5 and 2cdf007.

📒 Files selected for processing (3)
  • apps/web/src/core/managers/playback-manager.ts
  • apps/web/src/preview/components/toolbar.tsx
  • apps/web/src/project/types.ts
🚧 Files skipped from review as they are similar to previous changes (3)
  • apps/web/src/preview/components/toolbar.tsx
  • apps/web/src/core/managers/playback-manager.ts
  • apps/web/src/project/types.ts

📝 Walkthrough

Walkthrough

Adds an optional project loop setting, a preview toolbar toggle, and playback-manager logic that wraps playback to timeline start when enabled; otherwise playback pauses at timeline end.

Changes

Playback Looping: Schema, Logic, and UI

Layer / File(s) Summary
Project loop setting schema
apps/web/src/project/types.ts
TProjectSettings gains an optional loop?: boolean field with documentation describing its effect on preview playback behavior and default semantics.
Playback manager looping logic
apps/web/src/core/managers/playback-manager.ts
PlaybackManager.updateTime detects timeline end and either resets playback to ZERO_MEDIA_TIME with seek notifications and schedules another frame (if loop enabled and maxTime > 0) or pauses/clamps at maxTime.
Preview toolbar loop toggle control
apps/web/src/preview/components/toolbar.tsx
Toolbar imports loop/repeat icons, groups PlayPauseButton and new LoopToggleButton in the center area. Button reads project.settings.loop, shows appropriate icon/labels, and toggles the setting via UpdateProjectSettingsCommand.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

  • OpenCut-app/OpenCut#542: Both PRs implement end-of-timeline playback behavior by resetting/seeking back to the start of the timeline.

Poem

🐰 Loop-de-loop, the timeline spins around,
At the end it hops back to the start, unbound.
A tiny toggle, click—rekindle the play,
Frames tumble on in a merry display. 🎬✨

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Description check ⚠️ Warning The description is comprehensive and well-structured, covering summary, changes, test plan, and notes; however, it does not address the repository's PR template requirement to open an issue first for features. Add a note acknowledging that an issue was opened for discussion per the repository's feature submission policy, or document approval from a maintainer as required by the template.
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely describes the main feature being added—a loop toggle for timeline playback—which is the primary change across all modified files.
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 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 and usage tips.

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.

Actionable comments posted: 1

🤖 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 `@apps/web/src/preview/components/toolbar.tsx`:
- Around line 158-160: The Button used as the loop toggle in the toolbar
component (the JSX element with props variant={loop ? "secondary" : "text"} and
size="icon") lacks an explicit type, which can cause unintended form
submissions; update that Button element to include type="button" so it won't act
as a submit button when rendered inside a form (locate the loop toggle JSX in
the toolbar component and add the type prop).
🪄 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: fa52ff3c-a220-4d44-b2cb-33f0dba3e95e

📥 Commits

Reviewing files that changed from the base of the PR and between fbe3db7 and 5f0f5e5.

📒 Files selected for processing (3)
  • apps/web/src/core/managers/playback-manager.ts
  • apps/web/src/preview/components/toolbar.tsx
  • apps/web/src/project/types.ts

Comment thread apps/web/src/preview/components/toolbar.tsx
Adds a loop button next to the play/pause control in the preview
toolbar. When enabled, preview playback wraps back to the start of
the timeline instead of pausing on reaching the end. The state is
persisted as a per-project setting and defaults to off.

- Add optional \`loop: boolean\` field to \`TProjectSettings\`
- Add \`LoopToggleButton\` in the preview toolbar, wired through
  \`UpdateProjectSettingsCommand\` so toggling is undoable
- In \`PlaybackManager.updateTime\`, when the playhead reaches the
  end and the project's \`loop\` setting is on, reset to time zero
  and continue scheduling animation frames instead of pausing.
  The audio manager picks up the seek-to-zero through the existing
  \`onSeek\` listener, so audio restarts in sync with video.
@avillagran
Copy link
Copy Markdown
Author

Thanks @coderabbitai. Pushed a force-update that adds type=\"button\" to the loop toggle so the button can't accidentally trigger a form submission when nested in a form.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 17, 2026

Tip

For best results, initiate chat on the files or code changes.

@avillagran: Great fix! Adding type="button" is the correct defensive practice — without it, buttons inside a <form> default to type="submit" and can inadvertently trigger form submission. The loop toggle is now safe regardless of where it's rendered in the component tree. 🐇✨

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