From 093ec762abc4d46fbd4d68fd9872db16dc10e357 Mon Sep 17 00:00:00 2001 From: Etienne Lescot Date: Sun, 15 Mar 2026 10:46:37 +0100 Subject: [PATCH 01/38] feat: add cursor overlay pipeline --- src/assets/cursors/Cursor=Beachball.svg | 46 ++ src/assets/cursors/Cursor=Cross.svg | 5 + src/assets/cursors/Cursor=Default.svg | 5 + src/assets/cursors/Cursor=Hand-(Grabbing).svg | 5 + src/assets/cursors/Cursor=Hand-(Open).svg | 5 + src/assets/cursors/Cursor=Hand-(Pointing).svg | 5 + src/assets/cursors/Cursor=Menu.svg | 18 + src/assets/cursors/Cursor=Move.svg | 5 + src/assets/cursors/Cursor=Resize-(Down).svg | 5 + src/assets/cursors/Cursor=Resize-(Left).svg | 5 + .../cursors/Cursor=Resize-(Left-Right).svg | 5 + src/assets/cursors/Cursor=Resize-(Right).svg | 5 + src/assets/cursors/Cursor=Resize-(Up).svg | 5 + .../cursors/Cursor=Resize-(Up-Down).svg | 5 + .../Cursor=Resize-North-East-South-West.svg | 5 + .../cursors/Cursor=Resize-North-South.svg | 5 + .../Cursor=Resize-North-West-South-East.svg | 5 + .../cursors/Cursor=Resize-West-East.svg | 5 + src/assets/cursors/Cursor=Text-Cursor.svg | 5 + src/assets/cursors/Cursor=Zoom-In.svg | 8 + src/assets/cursors/Cursor=Zoom-Out.svg | 6 + src/components/video-editor/SettingsPanel.tsx | 140 +++- src/components/video-editor/VideoEditor.tsx | 44 +- src/components/video-editor/VideoPlayback.tsx | 114 ++- .../video-editor/projectPersistence.ts | 47 +- src/components/video-editor/types.ts | 24 + .../videoPlayback/cursorRenderer.ts | 766 ++++++++++++++++++ .../videoPlayback/motionSmoothing.ts | 149 ++++ .../videoPlayback/uploadedCursorAssets.ts | 70 ++ 29 files changed, 1440 insertions(+), 77 deletions(-) create mode 100644 src/assets/cursors/Cursor=Beachball.svg create mode 100644 src/assets/cursors/Cursor=Cross.svg create mode 100644 src/assets/cursors/Cursor=Default.svg create mode 100644 src/assets/cursors/Cursor=Hand-(Grabbing).svg create mode 100644 src/assets/cursors/Cursor=Hand-(Open).svg create mode 100644 src/assets/cursors/Cursor=Hand-(Pointing).svg create mode 100644 src/assets/cursors/Cursor=Menu.svg create mode 100644 src/assets/cursors/Cursor=Move.svg create mode 100644 src/assets/cursors/Cursor=Resize-(Down).svg create mode 100644 src/assets/cursors/Cursor=Resize-(Left).svg create mode 100644 src/assets/cursors/Cursor=Resize-(Left-Right).svg create mode 100644 src/assets/cursors/Cursor=Resize-(Right).svg create mode 100644 src/assets/cursors/Cursor=Resize-(Up).svg create mode 100644 src/assets/cursors/Cursor=Resize-(Up-Down).svg create mode 100644 src/assets/cursors/Cursor=Resize-North-East-South-West.svg create mode 100644 src/assets/cursors/Cursor=Resize-North-South.svg create mode 100644 src/assets/cursors/Cursor=Resize-North-West-South-East.svg create mode 100644 src/assets/cursors/Cursor=Resize-West-East.svg create mode 100644 src/assets/cursors/Cursor=Text-Cursor.svg create mode 100644 src/assets/cursors/Cursor=Zoom-In.svg create mode 100644 src/assets/cursors/Cursor=Zoom-Out.svg create mode 100644 src/components/video-editor/videoPlayback/cursorRenderer.ts create mode 100644 src/components/video-editor/videoPlayback/motionSmoothing.ts create mode 100644 src/components/video-editor/videoPlayback/uploadedCursorAssets.ts diff --git a/src/assets/cursors/Cursor=Beachball.svg b/src/assets/cursors/Cursor=Beachball.svg new file mode 100644 index 000000000..30bdbe502 --- /dev/null +++ b/src/assets/cursors/Cursor=Beachball.svg @@ -0,0 +1,46 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/assets/cursors/Cursor=Cross.svg b/src/assets/cursors/Cursor=Cross.svg new file mode 100644 index 000000000..b404553da --- /dev/null +++ b/src/assets/cursors/Cursor=Cross.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/assets/cursors/Cursor=Default.svg b/src/assets/cursors/Cursor=Default.svg new file mode 100644 index 000000000..f76f31fd7 --- /dev/null +++ b/src/assets/cursors/Cursor=Default.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/assets/cursors/Cursor=Hand-(Grabbing).svg b/src/assets/cursors/Cursor=Hand-(Grabbing).svg new file mode 100644 index 000000000..082786750 --- /dev/null +++ b/src/assets/cursors/Cursor=Hand-(Grabbing).svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/assets/cursors/Cursor=Hand-(Open).svg b/src/assets/cursors/Cursor=Hand-(Open).svg new file mode 100644 index 000000000..4ceafb0f0 --- /dev/null +++ b/src/assets/cursors/Cursor=Hand-(Open).svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/assets/cursors/Cursor=Hand-(Pointing).svg b/src/assets/cursors/Cursor=Hand-(Pointing).svg new file mode 100644 index 000000000..19a70a673 --- /dev/null +++ b/src/assets/cursors/Cursor=Hand-(Pointing).svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/assets/cursors/Cursor=Menu.svg b/src/assets/cursors/Cursor=Menu.svg new file mode 100644 index 000000000..3489257b1 --- /dev/null +++ b/src/assets/cursors/Cursor=Menu.svg @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/src/assets/cursors/Cursor=Move.svg b/src/assets/cursors/Cursor=Move.svg new file mode 100644 index 000000000..50e56b767 --- /dev/null +++ b/src/assets/cursors/Cursor=Move.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/assets/cursors/Cursor=Resize-(Down).svg b/src/assets/cursors/Cursor=Resize-(Down).svg new file mode 100644 index 000000000..fba367294 --- /dev/null +++ b/src/assets/cursors/Cursor=Resize-(Down).svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/assets/cursors/Cursor=Resize-(Left).svg b/src/assets/cursors/Cursor=Resize-(Left).svg new file mode 100644 index 000000000..6e21fb77d --- /dev/null +++ b/src/assets/cursors/Cursor=Resize-(Left).svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/assets/cursors/Cursor=Resize-(Left-Right).svg b/src/assets/cursors/Cursor=Resize-(Left-Right).svg new file mode 100644 index 000000000..7021d2297 --- /dev/null +++ b/src/assets/cursors/Cursor=Resize-(Left-Right).svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/assets/cursors/Cursor=Resize-(Right).svg b/src/assets/cursors/Cursor=Resize-(Right).svg new file mode 100644 index 000000000..1ce801ce1 --- /dev/null +++ b/src/assets/cursors/Cursor=Resize-(Right).svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/assets/cursors/Cursor=Resize-(Up).svg b/src/assets/cursors/Cursor=Resize-(Up).svg new file mode 100644 index 000000000..9c4ac0f00 --- /dev/null +++ b/src/assets/cursors/Cursor=Resize-(Up).svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/assets/cursors/Cursor=Resize-(Up-Down).svg b/src/assets/cursors/Cursor=Resize-(Up-Down).svg new file mode 100644 index 000000000..b01a40e3a --- /dev/null +++ b/src/assets/cursors/Cursor=Resize-(Up-Down).svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/assets/cursors/Cursor=Resize-North-East-South-West.svg b/src/assets/cursors/Cursor=Resize-North-East-South-West.svg new file mode 100644 index 000000000..1185c1fff --- /dev/null +++ b/src/assets/cursors/Cursor=Resize-North-East-South-West.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/assets/cursors/Cursor=Resize-North-South.svg b/src/assets/cursors/Cursor=Resize-North-South.svg new file mode 100644 index 000000000..57eaa0563 --- /dev/null +++ b/src/assets/cursors/Cursor=Resize-North-South.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/assets/cursors/Cursor=Resize-North-West-South-East.svg b/src/assets/cursors/Cursor=Resize-North-West-South-East.svg new file mode 100644 index 000000000..f00fc8797 --- /dev/null +++ b/src/assets/cursors/Cursor=Resize-North-West-South-East.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/assets/cursors/Cursor=Resize-West-East.svg b/src/assets/cursors/Cursor=Resize-West-East.svg new file mode 100644 index 000000000..ef1929fbe --- /dev/null +++ b/src/assets/cursors/Cursor=Resize-West-East.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/assets/cursors/Cursor=Text-Cursor.svg b/src/assets/cursors/Cursor=Text-Cursor.svg new file mode 100644 index 000000000..1bfd0809f --- /dev/null +++ b/src/assets/cursors/Cursor=Text-Cursor.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/assets/cursors/Cursor=Zoom-In.svg b/src/assets/cursors/Cursor=Zoom-In.svg new file mode 100644 index 000000000..8ec9b3ce5 --- /dev/null +++ b/src/assets/cursors/Cursor=Zoom-In.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/src/assets/cursors/Cursor=Zoom-Out.svg b/src/assets/cursors/Cursor=Zoom-Out.svg new file mode 100644 index 000000000..810878bad --- /dev/null +++ b/src/assets/cursors/Cursor=Zoom-Out.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/components/video-editor/SettingsPanel.tsx b/src/components/video-editor/SettingsPanel.tsx index 1ffa0f48d..c373bf9a9 100644 --- a/src/components/video-editor/SettingsPanel.tsx +++ b/src/components/video-editor/SettingsPanel.tsx @@ -240,6 +240,18 @@ interface SettingsPanelProps { webcamSizePreset?: WebcamSizePreset; onWebcamSizePresetChange?: (size: WebcamSizePreset) => void; onWebcamSizePresetCommit?: () => void; + // Cursor settings + showCursor?: boolean; + onShowCursorChange?: (show: boolean) => void; + cursorSize?: number; + onCursorSizeChange?: (size: number) => void; + cursorSmoothing?: number; + onCursorSmoothingChange?: (smoothing: number) => void; + cursorMotionBlur?: number; + onCursorMotionBlurChange?: (blur: number) => void; + cursorClickBounce?: number; + onCursorClickBounceChange?: (bounce: number) => void; + hasCursorData?: boolean; } export default SettingsPanel; @@ -327,6 +339,17 @@ export function SettingsPanel({ webcamSizePreset = DEFAULT_WEBCAM_SIZE_PRESET, onWebcamSizePresetChange, onWebcamSizePresetCommit, + showCursor = true, + onShowCursorChange, + cursorSize = 3.0, + onCursorSizeChange, + cursorSmoothing = 0.67, + onCursorSmoothingChange, + cursorMotionBlur = 0.35, + onCursorMotionBlurChange, + cursorClickBounce = 2.5, + onCursorClickBounceChange, + hasCursorData = false, }: SettingsPanelProps) { const t = useScopedT("settings"); // Resolved URLs are for DOM rendering only (backgroundImage). The canonical @@ -458,6 +481,7 @@ export function SettingsPanel({ }, [cropRegion, videoWidth, videoHeight], ); + const [showCropDropdown, setShowCropDropdown] = useState(false); const zoomEnabled = Boolean(selectedZoomDepth); const trimEnabled = Boolean(selectedTrimId); @@ -521,20 +545,6 @@ export function SettingsPanel({ } }; - const handleCropToggle = () => { - if (!showCropModal && cropRegion) { - cropSnapshotRef.current = { ...cropRegion }; - } - setShowCropModal(!showCropModal); - }; - - const handleCropCancel = () => { - if (cropSnapshotRef.current && onCropChange) { - onCropChange(cropSnapshotRef.current); - } - setShowCropModal(false); - }; - // Find selected annotation const selectedAnnotation = selectedAnnotationId ? annotationRegions.find((a) => a.id === selectedAnnotationId) @@ -1218,7 +1228,7 @@ export function SettingsPanel({ )}