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 =