Skip to content

feat(editor): add Full Camera timeline effect#66

Open
rodrvc wants to merge 2 commits into
getopenscreen:mainfrom
rodrvc:feature/camera-animation
Open

feat(editor): add Full Camera timeline effect#66
rodrvc wants to merge 2 commits into
getopenscreen:mainfrom
rodrvc:feature/camera-animation

Conversation

@rodrvc

@rodrvc rodrvc commented Jul 3, 2026

Copy link
Copy Markdown

Summary

Adds a Full Camera timeline effect: a new timeline row where you can add segments (button or C shortcut) during which the webcam overlay smoothly grows to cover the canvas — a "cut to camera" moment — and then eases back to its normal position.

Design decisions, all following the existing ZoomRegion pattern 1:1 (no new abstractions):

  • Simple segments, not keyframes: CameraFullscreenRegion { id, startMs, endMs }, edited on the timeline exactly like zoom segments (drag to move, drag edges to resize, same overlap guards).
  • Transition fully contained in the segment: progress is exactly 0 at startMs and endMs; ease-out is intentionally slower (1.5x) than ease-in for a gentler return.
  • No distortion: the webcam keeps its aspect ratio the whole way (contain-fit, centered), with a small 2.5% margin so rounded corners stay visible instead of hitting the canvas edges.
  • Preview/export parity: interpolation and target-rect math are shared pure functions (cameraFullscreenUtils.ts, computeCameraFullscreenTargetRect) consumed by both the DOM preview and the frame renderer used for MP4/GIF export.
  • Plays nice with reactive zoom: while a Full Camera segment is active, the reactive webcam shrink is suppressed for that frame; zoom behavior itself is untouched.
  • Persistence with normalization (older projects load cleanly), undo/redo support, configurable shortcut (C), and i18n keys for all 13 locales.

Related issue

None — happy to open one first if you prefer tracking features via issues.

Type of change

  • Feature

Release impact

  • Minor

Desktop impact

  • Not platform-specific

Screenshots / video

The webcam grows from its PiP position to an aspect-ratio-preserving, near-fullscreen rect with a small margin, then eases back. (Can attach a short clip on request.)

Testing

  • npx tsc --noEmit, npm run lint, npm run i18n:check clean (i18n failures pre-existing at HEAD are untouched).
  • npm run test: 258 tests passing, including new unit tests for the easing envelope (containment, monotonicity, short-segment clamping, overlapping regions) and the target-rect math (aspect preservation, margin, portrait/landscape).
  • Manually tested on macOS (dev build): adding/moving/resizing/deleting segments, undo/redo, playback across segments, interaction with zoom + reactive shrink, project save/reload, and older projects without the new field.

Summary by CodeRabbit

  • New Features
    • Added a new full-camera timeline segment with editing, selection, dedicated timeline row, and a maximize-style control.
    • Enabled keyboard shortcuts to add the segment, plus new fullscreen playback behavior.
    • Full-camera regions now carry through preview and GIF/MP4 export for consistent output.
  • Bug Fixes
    • Improved project load/save and snapshots to keep full-camera regions normalized and selection state synced.
    • Enhanced transition and placement handling with safer clamping and overlap resolution.
  • Documentation
    • Updated timeline and shortcut text across supported languages.
  • Tests
    • Added automated coverage for full-camera transition progress and target-rectangle geometry.

Adds a new timeline row for Full Camera segments: during a segment the
webcam overlay grows to (almost) fill the canvas — keeping its aspect
ratio and a small margin so rounded corners stay visible — then eases
back to its normal layout, with a slower ease-out than ease-in. The
transition is fully contained within the segment bounds.

Follows the existing ZoomRegion pattern end to end: shared pure
interpolation math for preview and export parity, persistence with
normalization for older projects, undo/redo support, a configurable
keyboard shortcut (C), and i18n keys for all 13 locales. While a Full
Camera segment is active, the reactive webcam shrink is suppressed to
avoid conflicting transforms; zoom behavior itself is untouched.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@rodrvc rodrvc requested a review from EtienneLescot as a code owner July 3, 2026 21:11
@coderabbitai

coderabbitai Bot commented Jul 3, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 9ffc816e-5dca-4cf1-873f-2bb171460a79

📥 Commits

Reviewing files that changed from the base of the PR and between 165668a and 1c697fd.

📒 Files selected for processing (1)
  • src/components/video-editor/VideoEditor.tsx
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/components/video-editor/VideoEditor.tsx

📝 Walkthrough

Walkthrough

This PR adds a new camera fullscreen timeline region across editor state, timeline controls, playback rendering, export handling, shortcut registration, and localized UI strings.

Changes

Camera Fullscreen Feature

Layer / File(s) Summary
Types, geometry helpers, and progress utility
src/components/video-editor/types.ts, src/lib/compositeLayout.ts, src/lib/compositeLayout.test.ts, src/components/video-editor/videoPlayback/cameraFullscreenUtils.ts, src/components/video-editor/videoPlayback/cameraFullscreenUtils.test.ts
Adds CameraFullscreenRegion, fullscreen target rect geometry, rect interpolation, and fullscreen progress computation with tests.
Persistence and editor history state
src/components/video-editor/projectPersistence.ts, src/hooks/useEditorHistory.ts
Normalizes and stores cameraFullscreenRegions in project/editor state.
Timeline item styling and editor integration
src/components/video-editor/timeline/Item.tsx, src/components/video-editor/timeline/ItemGlass.module.css, src/components/video-editor/timeline/TimelineEditor.tsx
Adds camera-fullscreen item rendering, styling, keyboard actions, span change, delete, and selection wiring.
VideoEditor state and selection wiring
src/components/video-editor/VideoEditor.tsx
Threads camera fullscreen regions through load/save, duration inference, selection, handlers, preview, timeline, and export props.
VideoPlayback fullscreen rendering
src/components/video-editor/VideoPlayback.tsx
Computes fullscreen progress and transforms the webcam PiP toward a fullscreen target rect during playback.
Export pipeline support
src/lib/exporter/frameRenderer.ts, src/lib/exporter/gifExporter.ts, src/lib/exporter/videoExporter.ts
Passes camera fullscreen regions into rendering and export, and disables the video fast path when they are present.
Keyboard shortcut registration
src/lib/shortcuts.ts
Adds addCameraFullscreen to shortcut actions, defaults, and labels.

Localization Strings for Camera Fullscreen

Layer / File(s) Summary
Locale additions across all languages
src/i18n/locales/*/shortcuts.json, src/i18n/locales/*/timeline.json
Adds camera fullscreen shortcut labels, timeline labels, button text, hints, and error strings across all supported locales.

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

Sequence Diagram(s)

sequenceDiagram
  participant VideoEditor
  participant VideoPlayback
  participant computeCameraFullscreenProgress
  participant computeCameraFullscreenTargetRect

  VideoEditor->>VideoPlayback: cameraFullscreenRegions
  VideoPlayback->>computeCameraFullscreenProgress: timeMs, regions
  computeCameraFullscreenProgress-->>VideoPlayback: progress (0..1)
  alt progress > 0
    VideoPlayback->>computeCameraFullscreenTargetRect: canvasSize, webcam aspect
    computeCameraFullscreenTargetRect-->>VideoPlayback: fullscreen rect
    VideoPlayback->>VideoPlayback: lerpRect(baseRect, fullscreenRect, progress)
  else progress == 0
    VideoPlayback->>VideoPlayback: apply reactive PiP scaling
  end
Loading
sequenceDiagram
  participant VideoExporter
  participant GifExporter
  participant FrameRenderer
  participant computeCameraFullscreenProgress
  participant computeCameraFullscreenTargetRect

  VideoExporter->>FrameRenderer: cameraFullscreenRegions config
  GifExporter->>FrameRenderer: cameraFullscreenRegions config
  FrameRenderer->>computeCameraFullscreenProgress: timeMs, regions
  computeCameraFullscreenProgress-->>FrameRenderer: cameraFullscreenProgress
  alt cameraFullscreenProgress > 0
    FrameRenderer->>computeCameraFullscreenTargetRect: canvasSize, webcam rect
    computeCameraFullscreenTargetRect-->>FrameRenderer: fullscreen rect
    FrameRenderer->>FrameRenderer: lerpRect(webcamRect, fullscreenRect, progress)
  else
    FrameRenderer->>FrameRenderer: apply reactive PiP shrink logic
  end
Loading

Suggested reviewers: EtienneLescot

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly matches the main change: adding a Full Camera timeline effect to the editor.
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

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.

Caution

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

⚠️ Outside diff range comments (1)
src/components/video-editor/VideoEditor.tsx (1)

862-884: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

doNewProject misses the camera-fullscreen resets.

This block resets every other selection and ID counter but omits the two new camera-fullscreen refs/setters added by this PR. selectedCameraFullscreenId does self-heal via the effect at Lines 1807-1814 after resetState(), and nextCameraFullscreenIdRef staying above 1 only affects ID numbering (IDs remain unique), but this is inconsistent with the sibling counters/selections reset here.

Proposed fix
 		setSelectedZoomId(null);
+		setSelectedCameraFullscreenId(null);
 		setSelectedTrimId(null);
 		setSelectedSpeedId(null);
 		setSelectedAnnotationId(null);
 		setSelectedBlurId(null);
 		nextZoomIdRef.current = 1;
+		nextCameraFullscreenIdRef.current = 1;
 		nextTrimIdRef.current = 1;
 		nextSpeedIdRef.current = 1;
🤖 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/VideoEditor.tsx` around lines 862 - 884, The
doNewProject reset block in VideoEditor.tsx is missing the new camera-fullscreen
state cleanup. Update the same reset sequence that clears selection state and
reinitializes counters to also clear selectedCameraFullscreenId and reset
nextCameraFullscreenIdRef to its initial value, matching the pattern used for
the other selected IDs and next*Id refs in doNewProject.
🧹 Nitpick comments (1)
src/i18n/locales/tr/timeline.json (1)

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

Inconsistent capitalization in addBlur string.

"addBlur": "Bulanık ekle (B)" uses lowercase "ekle" while every other sibling button capitalizes it, e.g. "addZoom": "Yakınlaştırma Ekle (Z)", "addCameraFullscreen": "Tam Ekran Kamera Ekle (C)". Worth aligning for consistency.

✏️ Suggested fix
-		"addBlur": "Bulanık ekle (B)",
+		"addBlur": "Bulanık Ekle (B)",
🤖 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/i18n/locales/tr/timeline.json` at line 8, The addBlur localization string
in the timeline translations uses inconsistent capitalization compared with
sibling actions; update the value in the addBlur entry to match the title-style
capitalization used by addZoom and addCameraFullscreen. Keep the existing
meaning and hotkey, but make the action phrase consistent with the other button
labels in this locale file.
🤖 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.

Outside diff comments:
In `@src/components/video-editor/VideoEditor.tsx`:
- Around line 862-884: The doNewProject reset block in VideoEditor.tsx is
missing the new camera-fullscreen state cleanup. Update the same reset sequence
that clears selection state and reinitializes counters to also clear
selectedCameraFullscreenId and reset nextCameraFullscreenIdRef to its initial
value, matching the pattern used for the other selected IDs and next*Id refs in
doNewProject.

---

Nitpick comments:
In `@src/i18n/locales/tr/timeline.json`:
- Line 8: The addBlur localization string in the timeline translations uses
inconsistent capitalization compared with sibling actions; update the value in
the addBlur entry to match the title-style capitalization used by addZoom and
addCameraFullscreen. Keep the existing meaning and hotkey, but make the action
phrase consistent with the other button labels in this locale file.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 64d5b82b-4b63-4bdf-8a26-810303d67efd

📥 Commits

Reviewing files that changed from the base of the PR and between 2fc116b and 165668a.

📒 Files selected for processing (42)
  • src/components/video-editor/VideoEditor.tsx
  • src/components/video-editor/VideoPlayback.tsx
  • src/components/video-editor/projectPersistence.ts
  • src/components/video-editor/timeline/Item.tsx
  • src/components/video-editor/timeline/ItemGlass.module.css
  • src/components/video-editor/timeline/TimelineEditor.tsx
  • src/components/video-editor/types.ts
  • src/components/video-editor/videoPlayback/cameraFullscreenUtils.test.ts
  • src/components/video-editor/videoPlayback/cameraFullscreenUtils.ts
  • src/hooks/useEditorHistory.ts
  • src/i18n/locales/ar/shortcuts.json
  • src/i18n/locales/ar/timeline.json
  • src/i18n/locales/en/shortcuts.json
  • src/i18n/locales/en/timeline.json
  • src/i18n/locales/es/shortcuts.json
  • src/i18n/locales/es/timeline.json
  • src/i18n/locales/fr/shortcuts.json
  • src/i18n/locales/fr/timeline.json
  • src/i18n/locales/it/shortcuts.json
  • src/i18n/locales/it/timeline.json
  • src/i18n/locales/ja-JP/shortcuts.json
  • src/i18n/locales/ja-JP/timeline.json
  • src/i18n/locales/ko-KR/shortcuts.json
  • src/i18n/locales/ko-KR/timeline.json
  • src/i18n/locales/pt-BR/shortcuts.json
  • src/i18n/locales/pt-BR/timeline.json
  • src/i18n/locales/ru/shortcuts.json
  • src/i18n/locales/ru/timeline.json
  • src/i18n/locales/tr/shortcuts.json
  • src/i18n/locales/tr/timeline.json
  • src/i18n/locales/vi/shortcuts.json
  • src/i18n/locales/vi/timeline.json
  • src/i18n/locales/zh-CN/shortcuts.json
  • src/i18n/locales/zh-CN/timeline.json
  • src/i18n/locales/zh-TW/shortcuts.json
  • src/i18n/locales/zh-TW/timeline.json
  • src/lib/compositeLayout.test.ts
  • src/lib/compositeLayout.ts
  • src/lib/exporter/frameRenderer.ts
  • src/lib/exporter/gifExporter.ts
  • src/lib/exporter/videoExporter.ts
  • src/lib/shortcuts.ts

…project

Address review feedback: doNewProject reset every other selection and
region id counter but missed the two camera-fullscreen ones.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@rodrvc

rodrvc commented Jul 3, 2026

Copy link
Copy Markdown
Author

Addressed the review: added the missing camera-fullscreen selection/id-counter resets in doNewProject (1c697fd). Skipped the Turkish addBlur capitalization nit — that line predates this PR, so I left it out to keep the diff focused; happy to fix it in a follow-up.

@EtienneLescot

Copy link
Copy Markdown
Collaborator

@rodrvc thanks for you PR, I think this is a good idea.
did you test on all platforms?

@EtienneLescot EtienneLescot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Thank you for the contribution! The implementation of the Full Camera effect is clean, structured, and consistent with other regions. I ran the typechecker and all unit tests locally, and they passed successfully.

I have left one inline comment regarding the transition easing calculation for the ease-out effect to avoid a visual pop/slam at the end of the transition.

Comment thread src/components/video-editor/videoPlayback/cameraFullscreenUtils.ts
@rodrvc

rodrvc commented Jul 6, 2026

Copy link
Copy Markdown
Author

Thanks for the review! Fixed the easing issue on the inline comment exactly as you suggested (1 - easeOutScreenStudio(progress) with progress going 0→1), and updated the corresponding unit test assertions accordingly — all passing.

Re: platforms — I only tested this manually on macOS. Full Camera itself is renderer-only (canvas/PIXI math for the easing envelope and target-rect, consumed by both the DOM preview and the frame renderer for MP4/GIF export); it doesn't touch any of the native platform helpers (WGC/WASAPI on Windows, ScreenCaptureKit on macOS), which are only involved in live capture, not in this edit/export pipeline. build.yml does compile/package on Windows, macOS, and Linux, so I'm fairly confident it works there too, but I haven't done a manual visual check on Windows/Linux yet. Happy to try to get access to a Windows/Linux box to confirm before merge if you'd like extra assurance.

@EtienneLescot

Copy link
Copy Markdown
Collaborator

@rodrvc Thank you.
In my experience, even when native code isn't affected, you can always bump onto something unexpected on one or another platform.
Currently the main bottleneck for me is e2e testing :-) So if you get a chance to test on windows and linux that would be highly appreciated!

Thank you for your contribution!

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