feat(editor): add Full Camera timeline effect#66
Conversation
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>
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (1)
🚧 Files skipped from review as they are similar to previous changes (1)
📝 WalkthroughWalkthroughThis PR adds a new camera fullscreen timeline region across editor state, timeline controls, playback rendering, export handling, shortcut registration, and localized UI strings. ChangesCamera Fullscreen Feature
Localization Strings for Camera Fullscreen
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
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
Suggested reviewers: 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
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
doNewProjectmisses 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.
selectedCameraFullscreenIddoes self-heal via the effect at Lines 1807-1814 afterresetState(), andnextCameraFullscreenIdRefstaying above1only 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 winInconsistent capitalization in
addBlurstring.
"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
📒 Files selected for processing (42)
src/components/video-editor/VideoEditor.tsxsrc/components/video-editor/VideoPlayback.tsxsrc/components/video-editor/projectPersistence.tssrc/components/video-editor/timeline/Item.tsxsrc/components/video-editor/timeline/ItemGlass.module.csssrc/components/video-editor/timeline/TimelineEditor.tsxsrc/components/video-editor/types.tssrc/components/video-editor/videoPlayback/cameraFullscreenUtils.test.tssrc/components/video-editor/videoPlayback/cameraFullscreenUtils.tssrc/hooks/useEditorHistory.tssrc/i18n/locales/ar/shortcuts.jsonsrc/i18n/locales/ar/timeline.jsonsrc/i18n/locales/en/shortcuts.jsonsrc/i18n/locales/en/timeline.jsonsrc/i18n/locales/es/shortcuts.jsonsrc/i18n/locales/es/timeline.jsonsrc/i18n/locales/fr/shortcuts.jsonsrc/i18n/locales/fr/timeline.jsonsrc/i18n/locales/it/shortcuts.jsonsrc/i18n/locales/it/timeline.jsonsrc/i18n/locales/ja-JP/shortcuts.jsonsrc/i18n/locales/ja-JP/timeline.jsonsrc/i18n/locales/ko-KR/shortcuts.jsonsrc/i18n/locales/ko-KR/timeline.jsonsrc/i18n/locales/pt-BR/shortcuts.jsonsrc/i18n/locales/pt-BR/timeline.jsonsrc/i18n/locales/ru/shortcuts.jsonsrc/i18n/locales/ru/timeline.jsonsrc/i18n/locales/tr/shortcuts.jsonsrc/i18n/locales/tr/timeline.jsonsrc/i18n/locales/vi/shortcuts.jsonsrc/i18n/locales/vi/timeline.jsonsrc/i18n/locales/zh-CN/shortcuts.jsonsrc/i18n/locales/zh-CN/timeline.jsonsrc/i18n/locales/zh-TW/shortcuts.jsonsrc/i18n/locales/zh-TW/timeline.jsonsrc/lib/compositeLayout.test.tssrc/lib/compositeLayout.tssrc/lib/exporter/frameRenderer.tssrc/lib/exporter/gifExporter.tssrc/lib/exporter/videoExporter.tssrc/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>
|
Addressed the review: added the missing camera-fullscreen selection/id-counter resets in |
|
@rodrvc thanks for you PR, I think this is a good idea. |
EtienneLescot
left a comment
There was a problem hiding this comment.
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.
|
Thanks for the review! Fixed the easing issue on the inline comment exactly as you suggested ( 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. |
|
@rodrvc Thank you. Thank you for your contribution! |
Summary
Adds a Full Camera timeline effect: a new timeline row where you can add segments (button or
Cshortcut) 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
ZoomRegionpattern 1:1 (no new abstractions):CameraFullscreenRegion { id, startMs, endMs }, edited on the timeline exactly like zoom segments (drag to move, drag edges to resize, same overlap guards).startMsandendMs; ease-out is intentionally slower (1.5x) than ease-in for a gentler return.cameraFullscreenUtils.ts,computeCameraFullscreenTargetRect) consumed by both the DOM preview and the frame renderer used for MP4/GIF export.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
Release impact
Desktop impact
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:checkclean (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).Summary by CodeRabbit