From 02d258ebc2326c75eaa9cc7166cad5f681d56f36 Mon Sep 17 00:00:00 2001 From: Etienne Lescot Date: Sun, 15 Mar 2026 10:46:37 +0100 Subject: [PATCH 1/4] 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 | 147 +++- src/components/video-editor/VideoEditor.tsx | 187 ++--- src/components/video-editor/VideoPlayback.tsx | 124 ++- .../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, 1515 insertions(+), 162 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 00000000..30bdbe50 --- /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 00000000..b404553d --- /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 00000000..f76f31fd --- /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 00000000..08278675 --- /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 00000000..4ceafb0f --- /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 00000000..19a70a67 --- /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 00000000..3489257b --- /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 00000000..50e56b76 --- /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 00000000..fba36729 --- /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 00000000..6e21fb77 --- /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 00000000..7021d229 --- /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 00000000..1ce801ce --- /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 00000000..9c4ac0f0 --- /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 00000000..b01a40e3 --- /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 00000000..1185c1ff --- /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 00000000..57eaa056 --- /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 00000000..f00fc879 --- /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 00000000..ef1929fb --- /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 00000000..1bfd0809 --- /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 00000000..8ec9b3ce --- /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 00000000..810878ba --- /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 f5afe35a..f98b77d4 100644 --- a/src/components/video-editor/SettingsPanel.tsx +++ b/src/components/video-editor/SettingsPanel.tsx @@ -140,9 +140,18 @@ interface SettingsPanelProps { selectedSpeedValue?: PlaybackSpeed | null; onSpeedChange?: (speed: PlaybackSpeed) => void; onSpeedDelete?: (id: string) => void; - hasWebcam?: boolean; - webcamLayoutPreset?: WebcamLayoutPreset; - onWebcamLayoutPresetChange?: (preset: WebcamLayoutPreset) => 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; @@ -208,9 +217,17 @@ export function SettingsPanel({ selectedSpeedValue, onSpeedChange, onSpeedDelete, - hasWebcam = false, - webcamLayoutPreset = "picture-in-picture", - onWebcamLayoutPresetChange, + 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"); const [wallpaperPaths, setWallpaperPaths] = useState([]); @@ -252,6 +269,7 @@ export function SettingsPanel({ const [selectedColor, setSelectedColor] = useState("#ADADAD"); const [gradient, setGradient] = useState(GRADIENTS[0]); +<<<<<<< HEAD const [showCropModal, setShowCropModal] = useState(false); const cropSnapshotRef = useRef(null); const [cropAspectLocked, setCropAspectLocked] = useState(false); @@ -353,6 +371,7 @@ export function SettingsPanel({ }, [cropRegion, videoWidth, videoHeight], ); + const [showCropDropdown, setShowCropDropdown] = useState(false); const zoomEnabled = Boolean(selectedZoomDepth); const trimEnabled = Boolean(selectedTrimId); @@ -416,20 +435,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) @@ -729,7 +734,7 @@ export function SettingsPanel({