diff --git a/src/components/video-editor/KeyboardShortcutsHelp.tsx b/src/components/video-editor/KeyboardShortcutsHelp.tsx index b3798e1..2911b21 100644 --- a/src/components/video-editor/KeyboardShortcutsHelp.tsx +++ b/src/components/video-editor/KeyboardShortcutsHelp.tsx @@ -1,28 +1,10 @@ import { HelpCircle, Settings2 } from "lucide-react"; -import { useState, useEffect } from "react"; -import { formatShortcut } from "@/utils/platformUtils"; import { useShortcuts } from "@/contexts/ShortcutsContext"; -import { formatBinding, SHORTCUT_LABELS, SHORTCUT_ACTIONS } from "@/lib/shortcuts"; +import { formatBinding, SHORTCUT_LABELS, SHORTCUT_ACTIONS, FIXED_SHORTCUTS } from "@/lib/shortcuts"; export function KeyboardShortcutsHelp() { const { shortcuts, isMac, openConfig } = useShortcuts(); - const [scrollLabels, setScrollLabels] = useState({ pan: 'Shift + Ctrl + Scroll', zoom: 'Ctrl + Scroll' }); - const [undoRedoLabels, setUndoRedoLabels] = useState({ undo: 'Ctrl + Z', redo: 'Ctrl + Shift + Z', redoAlt: 'Ctrl + Y' }); - - useEffect(() => { - Promise.all([ - formatShortcut(['shift', 'mod', 'Scroll']), - formatShortcut(['mod', 'Scroll']), - formatShortcut(['mod', 'Z']), - formatShortcut(['shift', 'mod', 'Z']), - formatShortcut(['mod', 'Y']), - ]).then(([pan, zoom, undo, redo, redoAlt]) => { - setScrollLabels({ pan, zoom }); - setUndoRedoLabels({ undo, redo, redoAlt }); - }); - }, []); - return (
@@ -51,31 +33,15 @@ export function KeyboardShortcutsHelp() {
))} -
-
- Pan Timeline - {scrollLabels.pan} -
-
- Zoom Timeline - {scrollLabels.zoom} -
-
- Cycle Annotations - Tab -
-
-
- Undo - {undoRedoLabels.undo} -
-
- Redo -
- {undoRedoLabels.redo} - or - {undoRedoLabels.redoAlt} -
+
+ {FIXED_SHORTCUTS.map((fixed) => ( +
+ {fixed.label} + + {isMac ? fixed.display.replace(/Ctrl/g, '⌘').replace(/Shift/g, '⇧').replace(/Alt/g, '⌥') : fixed.display} + +
+ ))}
diff --git a/src/components/video-editor/VideoEditor.tsx b/src/components/video-editor/VideoEditor.tsx index 791650a..49ac398 100644 --- a/src/components/video-editor/VideoEditor.tsx +++ b/src/components/video-editor/VideoEditor.tsx @@ -45,7 +45,7 @@ export default function VideoEditor() { useEditorHistory(INITIAL_EDITOR_STATE); const { - zoomRegions, trimRegions, annotationRegions, + zoomRegions, trimRegions, speedRegions, annotationRegions, cropRegion, wallpaper, shadowIntensity, showBlur, motionBlurEnabled, borderRadius, padding, aspectRatio, } = editorState; @@ -60,7 +60,6 @@ export default function VideoEditor() { const [cursorTelemetry, setCursorTelemetry] = useState([]); const [selectedZoomId, setSelectedZoomId] = useState(null); const [selectedTrimId, setSelectedTrimId] = useState(null); - const [speedRegions, setSpeedRegions] = useState([]); const [selectedSpeedId, setSelectedSpeedId] = useState(null); const [selectedAnnotationId, setSelectedAnnotationId] = useState(null); const [isExporting, setIsExporting] = useState(false); @@ -316,16 +315,16 @@ export default function VideoEditor() { endMs: Math.round(span.end), speed: DEFAULT_PLAYBACK_SPEED, }; - setSpeedRegions((prev) => [...prev, newRegion]); + pushState((prev) => ({ speedRegions: [...prev.speedRegions, newRegion] })); setSelectedSpeedId(id); setSelectedZoomId(null); setSelectedTrimId(null); setSelectedAnnotationId(null); - }, []); + }, [pushState]); const handleSpeedSpanChange = useCallback((id: string, span: Span) => { - setSpeedRegions((prev) => - prev.map((region) => + pushState((prev) => ({ + speedRegions: prev.speedRegions.map((region) => region.id === id ? { ...region, @@ -334,24 +333,24 @@ export default function VideoEditor() { } : region, ), - ); - }, []); + })); + }, [pushState]); const handleSpeedDelete = useCallback((id: string) => { - setSpeedRegions((prev) => prev.filter((region) => region.id !== id)); + pushState((prev) => ({ speedRegions: prev.speedRegions.filter((region) => region.id !== id) })); if (selectedSpeedId === id) { setSelectedSpeedId(null); } - }, [selectedSpeedId]); + }, [selectedSpeedId, pushState]); const handleSpeedChange = useCallback((speed: PlaybackSpeed) => { if (!selectedSpeedId) return; - setSpeedRegions((prev) => - prev.map((region) => + pushState((prev) => ({ + speedRegions: prev.speedRegions.map((region) => region.id === selectedSpeedId ? { ...region, speed } : region, ), - ); - }, [selectedSpeedId]); + })); + }, [selectedSpeedId, pushState]); const handleAnnotationAdded = useCallback((span: Span) => { const id = `annotation-${nextAnnotationIdRef.current++}`; diff --git a/src/hooks/useEditorHistory.ts b/src/hooks/useEditorHistory.ts index d3f686d..b7db51c 100644 --- a/src/hooks/useEditorHistory.ts +++ b/src/hooks/useEditorHistory.ts @@ -1,5 +1,5 @@ import { useCallback, useRef, useState } from "react"; -import type { ZoomRegion, TrimRegion, AnnotationRegion, CropRegion } from "@/components/video-editor/types"; +import type { ZoomRegion, TrimRegion, AnnotationRegion, SpeedRegion, CropRegion } from "@/components/video-editor/types"; import { DEFAULT_CROP_REGION } from "@/components/video-editor/types"; import type { AspectRatio } from "@/utils/aspectRatioUtils"; @@ -8,6 +8,7 @@ import type { AspectRatio } from "@/utils/aspectRatioUtils"; export interface EditorState { zoomRegions: ZoomRegion[]; trimRegions: TrimRegion[]; + speedRegions: SpeedRegion[]; annotationRegions: AnnotationRegion[]; cropRegion: CropRegion; wallpaper: string; @@ -22,6 +23,7 @@ export interface EditorState { export const INITIAL_EDITOR_STATE: EditorState = { zoomRegions: [], trimRegions: [], + speedRegions: [], annotationRegions: [], cropRegion: DEFAULT_CROP_REGION, wallpaper: "/wallpapers/wallpaper1.jpg", diff --git a/src/lib/shortcuts.ts b/src/lib/shortcuts.ts index 847c753..66a1b7e 100644 --- a/src/lib/shortcuts.ts +++ b/src/lib/shortcuts.ts @@ -27,11 +27,13 @@ export interface FixedShortcut { } export const FIXED_SHORTCUTS: FixedShortcut[] = [ - { label: 'Cycle Annotations Forward', display: 'Tab', bindings: [{ key: 'tab' }] }, - { label: 'Cycle Annotations Backward', display: 'Shift + Tab', bindings: [{ key: 'tab', shift: true }] }, - { label: 'Delete Selected (alt)', display: 'Del / ⌫', bindings: [{ key: 'delete' }, { key: 'backspace' }] }, - { label: 'Pan Timeline', display: 'Shift + Ctrl + Scroll', bindings: [] }, - { label: 'Zoom Timeline', display: 'Ctrl + Scroll', bindings: [] }, + { label: 'Undo', display: 'Ctrl + Z', bindings: [{ key: 'z', ctrl: true }] }, + { label: 'Redo', display: 'Ctrl + Shift + Z / Ctrl + Y', bindings: [{ key: 'z', ctrl: true, shift: true }, { key: 'y', ctrl: true }] }, + { label: 'Cycle Annotations Forward', display: 'Tab', bindings: [{ key: 'tab' }] }, + { label: 'Cycle Annotations Backward', display: 'Shift + Tab', bindings: [{ key: 'tab', shift: true }] }, + { label: 'Delete Selected (alt)', display: 'Del / ⌫', bindings: [{ key: 'delete' }, { key: 'backspace' }] }, + { label: 'Pan Timeline', display: 'Shift + Ctrl + Scroll', bindings: [] }, + { label: 'Zoom Timeline', display: 'Ctrl + Scroll', bindings: [] }, ]; export type ShortcutConflict =