From 8bc26539ebe44d6ff0d74d86ef316934c7f18faf Mon Sep 17 00:00:00 2001 From: Maze Winther Date: Wed, 16 Jul 2025 14:37:08 +0200 Subject: cleanup (this doesn't work) --- apps/web/src/components/editor-provider.tsx | 6 + apps/web/src/components/editor/timeline.tsx | 266 +-------------------- .../web/src/components/keyboard-shortcuts-help.tsx | 137 ++--------- 3 files changed, 25 insertions(+), 384 deletions(-) (limited to 'apps/web/src/components') diff --git a/apps/web/src/components/editor-provider.tsx b/apps/web/src/components/editor-provider.tsx index 38255956..d970f86d 100644 --- a/apps/web/src/components/editor-provider.tsx +++ b/apps/web/src/components/editor-provider.tsx @@ -3,6 +3,7 @@ import { useEffect } from "react"; import { Loader2 } from "lucide-react"; import { useEditorStore } from "@/stores/editor-store"; +import { useKeyboardShortcuts } from "@/hooks/use-keyboard-shortcuts"; interface EditorProviderProps { children: React.ReactNode; @@ -11,6 +12,11 @@ interface EditorProviderProps { export function EditorProvider({ children }: EditorProviderProps) { const { isInitializing, isPanelsReady, initializeApp } = useEditorStore(); + useKeyboardShortcuts({ + enabled: !isInitializing && isPanelsReady, + context: "editor", + }); + useEffect(() => { initializeApp(); }, [initializeApp]); diff --git a/apps/web/src/components/editor/timeline.tsx b/apps/web/src/components/editor/timeline.tsx index 6775e845..539846b3 100644 --- a/apps/web/src/components/editor/timeline.tsx +++ b/apps/web/src/components/editor/timeline.tsx @@ -1,6 +1,5 @@ "use client"; -// @ts-ignore - IDE TypeScript configuration issue (React modules compile fine in Docker) import { ScrollArea } from "../ui/scroll-area"; import { Button } from "../ui/button"; import { @@ -16,7 +15,6 @@ import { Video, Music, TypeIcon, - Magnet, Lock, LockOpen, } from "lucide-react"; @@ -39,9 +37,6 @@ import { useProjectStore } from "@/stores/project-store"; import { useTimelineZoom } from "@/hooks/use-timeline-zoom"; import { processMediaFiles } from "@/lib/media-processing"; import { toast } from "sonner"; -// @ts-ignore - React type definitions -import * as React from "react"; -// @ts-ignore - React type definitions import { useState, useRef, useEffect, useCallback } from "react"; import { TimelineTrackContent } from "./timeline-track"; import { @@ -65,9 +60,7 @@ export function Timeline() { // Timeline shows all tracks (video, audio, effects) and their elements. // You can drag media here to add it to your project. // elements can be trimmed, deleted, and moved. - - // Note: Some parameter types are inferred as 'any' due to store interface definitions - // This doesn't affect runtime safety as the stores provide proper type checking + const { tracks, addTrack, @@ -265,259 +258,6 @@ export function Timeline() { setDuration(Math.max(totalDuration, 10)); // Minimum 10 seconds for empty timeline }, [tracks, setDuration, getTotalDuration]); - // Keyboard event for deleting selected elements - useEffect(() => { - const handleKeyDown = (e: KeyboardEvent) => { - // Don't trigger when typing in input fields or textareas - if ( - e.target instanceof HTMLInputElement || - e.target instanceof HTMLTextAreaElement - ) { - return; - } - - // Only trigger when timeline is focused or mouse is over timeline - if ( - !isInTimeline && - !timelineRef.current?.contains(document.activeElement) - ) { - return; - } - - if ( - (e.key === "Delete" || e.key === "Backspace") && - selectedElements.length > 0 - ) { - selectedElements.forEach(({ trackId, elementId }: { trackId: string; elementId: string }) => { - removeElementFromTrack(trackId, elementId); - }); - clearSelectedElements(); - } - }; - window.addEventListener("keydown", handleKeyDown); - return () => window.removeEventListener("keydown", handleKeyDown); - }, [ - selectedElements, - removeElementFromTrack, - clearSelectedElements, - isInTimeline, - ]); - - // Keyboard event for undo (Cmd+Z) - useEffect(() => { - const handleKeyDown = (e: KeyboardEvent) => { - if ((e.metaKey || e.ctrlKey) && e.key === "z" && !e.shiftKey) { - e.preventDefault(); - undo(); - } - }; - window.addEventListener("keydown", handleKeyDown); - return () => window.removeEventListener("keydown", handleKeyDown); - }, [undo]); - - // Keyboard event for redo (Cmd+Shift+Z or Cmd+Y) - useEffect(() => { - const handleKeyDown = (e: KeyboardEvent) => { - if ((e.metaKey || e.ctrlKey) && e.key === "z" && e.shiftKey) { - e.preventDefault(); - redo(); - } else if ((e.metaKey || e.ctrlKey) && e.key === "y") { - e.preventDefault(); - redo(); - } - }; - window.addEventListener("keydown", handleKeyDown); - return () => window.removeEventListener("keydown", handleKeyDown); - }, [redo]); - - // Core video editor keyboard shortcuts - useEffect(() => { - const handleKeyDown = (e: KeyboardEvent) => { - // Don't interfere with typing in inputs - const activeElement = document.activeElement as HTMLElement; - const isInputFocused = activeElement && ( - activeElement.tagName === 'INPUT' || - activeElement.tagName === 'TEXTAREA' || - activeElement.contentEditable === 'true' - ); - - if (isInputFocused) return; - - const { key, metaKey, ctrlKey, shiftKey } = e; - const isModified = metaKey || ctrlKey || shiftKey; - - switch (key) { - case ' ': // Spacebar - Play/Pause - if (!isModified) { - e.preventDefault(); - toggle(); - toast.info(isPlaying ? 'Paused' : 'Playing', { duration: 1000 }); - } - break; - - case 'j': - case 'J': - if (!isModified) { - e.preventDefault(); - // J - Rewind 1 second - seek(Math.max(0, currentTime - 1)); - toast.info('Rewind 1s', { duration: 1000 }); - } - break; - - case 'k': - case 'K': - if (!isModified) { - e.preventDefault(); - // K - Pause/Play toggle - toggle(); - toast.info(isPlaying ? 'Paused' : 'Playing', { duration: 1000 }); - } - break; - - case 'l': - case 'L': - if (!isModified) { - e.preventDefault(); - // L - Fast forward 1 second - seek(Math.min(duration, currentTime + 1)); - toast.info('Forward 1s', { duration: 1000 }); - } - break; - - case 'ArrowLeft': - if (shiftKey) { - e.preventDefault(); - // Shift+Left - Jump back 5 seconds - seek(Math.max(0, currentTime - 5)); - } else if (!isModified) { - e.preventDefault(); - // Left - Frame step backward (1 frame) - const projectFps = activeProject?.fps || 30; - seek(Math.max(0, currentTime - (1/projectFps))); - } - break; - - case 'ArrowRight': - if (shiftKey) { - e.preventDefault(); - // Shift+Right - Jump forward 5 seconds - seek(Math.min(duration, currentTime + 5)); - } else if (!isModified) { - e.preventDefault(); - // Right - Frame step forward (1 frame) - const projectFps = activeProject?.fps || 30; - seek(Math.min(duration, currentTime + (1/projectFps))); - } - break; - - case 'Home': - if (!isModified) { - e.preventDefault(); - seek(0); - toast.info('Start of timeline', { duration: 1000 }); - } - break; - - case 'End': - if (!isModified) { - e.preventDefault(); - seek(duration); - toast.info('End of timeline', { duration: 1000 }); - } - break; - - case 's': - case 'S': - if (!isModified && selectedElements.length === 1) { - e.preventDefault(); - // S - Split element at playhead - const { trackId, elementId } = selectedElements[0]; - const track = tracks.find((t: any) => t.id === trackId); - const element = track?.elements.find((el: any) => el.id === elementId); - - if (element) { - const effectiveStart = element.startTime; - const effectiveEnd = element.startTime + (element.duration - element.trimStart - element.trimEnd); - - if (currentTime > effectiveStart && currentTime < effectiveEnd) { - splitElement(trackId, elementId, currentTime); - toast.success('Element split at playhead'); - } else { - toast.error('Playhead must be within selected element'); - } - } - } - break; - - case 'n': - case 'N': - if (!isModified) { - e.preventDefault(); - toggleSnapping(); - toast.info(`Snapping ${snappingEnabled ? 'disabled' : 'enabled'}`, { duration: 1000 }); - } - break; - } - - // Multi-key shortcuts - if ((metaKey || ctrlKey) && !shiftKey) { - switch (key.toLowerCase()) { - case 'a': - e.preventDefault(); - // Cmd/Ctrl+A - Select all elements - const allElements = tracks.flatMap((track: any) => - track.elements.map((element: any) => ({ - trackId: track.id, - elementId: element.id - })) - ); - setSelectedElements(allElements); - toast.info(`Selected ${allElements.length} elements`, { duration: 1000 }); - break; - - case 'd': - if (selectedElements.length === 1) { - e.preventDefault(); - // Cmd/Ctrl+D - Duplicate selected element - const { trackId, elementId } = selectedElements[0]; - const track = tracks.find((t: any) => t.id === trackId); - const element = track?.elements.find((el: any) => el.id === elementId); - - if (element) { - const newStartTime = element.startTime + (element.duration - element.trimStart - element.trimEnd) + 0.1; - const { id, ...elementWithoutId } = element; - - addElementToTrack(trackId, { - ...elementWithoutId, - startTime: newStartTime, - }); - - toast.success('Element duplicated'); - } - } - break; - } - } - }; - - window.addEventListener("keydown", handleKeyDown); - return () => window.removeEventListener("keydown", handleKeyDown); - }, [ - isPlaying, - currentTime, - duration, - selectedElements, - tracks, - toggle, - seek, - splitElement, - snappingEnabled, - toggleSnapping, - setSelectedElements, - addElementToTrack - ]); - // Old marquee system removed - using new SelectionBox component instead const handleDragEnter = (e: React.DragEvent) => { @@ -575,7 +315,9 @@ export function Timeline() { useTimelineStore.getState().addTextToNewTrack(dragData); } else { // Handle media items - const mediaItem = mediaItems.find((item: any) => item.id === dragData.id); + const mediaItem = mediaItems.find( + (item: any) => item.id === dragData.id + ); if (!mediaItem) { toast.error("Media item not found"); return; diff --git a/apps/web/src/components/keyboard-shortcuts-help.tsx b/apps/web/src/components/keyboard-shortcuts-help.tsx index 129cb438..9f0d1e57 100644 --- a/apps/web/src/components/keyboard-shortcuts-help.tsx +++ b/apps/web/src/components/keyboard-shortcuts-help.tsx @@ -11,120 +11,8 @@ import { DialogTrigger, } from "./ui/dialog"; import { Badge } from "./ui/badge"; -import { Keyboard, Play, Pause, SkipBack, SkipForward } from "lucide-react"; - -interface Shortcut { - keys: string[]; - description: string; - category: string; - icon?: React.ReactNode; -} - -const shortcuts: Shortcut[] = [ - // Playback Controls - { - keys: ["Space"], - description: "Play/Pause", - category: "Playback", - icon: - }, - { - keys: ["J"], - description: "Rewind 1 second", - category: "Playback", - icon: - }, - { - keys: ["K"], - description: "Play/Pause (alternative)", - category: "Playback", - icon: - }, - { - keys: ["L"], - description: "Fast forward 1 second", - category: "Playback", - icon: - }, - - // Navigation - { - keys: ["←"], - description: "Frame step backward", - category: "Navigation" - }, - { - keys: ["→"], - description: "Frame step forward", - category: "Navigation" - }, - { - keys: ["Shift", "←"], - description: "Jump back 5 seconds", - category: "Navigation" - }, - { - keys: ["Shift", "→"], - description: "Jump forward 5 seconds", - category: "Navigation" - }, - { - keys: ["Home"], - description: "Go to timeline start", - category: "Navigation" - }, - { - keys: ["End"], - description: "Go to timeline end", - category: "Navigation" - }, - - // Editing - { - keys: ["S"], - description: "Split element at playhead", - category: "Editing" - }, - { - keys: ["Delete", "Backspace"], - description: "Delete selected elements", - category: "Editing" - }, - { - keys: ["N"], - description: "Toggle snapping", - category: "Editing" - }, - - // Selection & Organization - { - keys: ["Cmd", "A"], - description: "Select all elements", - category: "Selection" - }, - { - keys: ["Cmd", "D"], - description: "Duplicate selected element", - category: "Selection" - }, - - // History - { - keys: ["Cmd", "Z"], - description: "Undo", - category: "History" - }, - { - keys: ["Cmd", "Shift", "Z"], - description: "Redo", - category: "History" - }, - { - keys: ["Cmd", "Y"], - description: "Redo (alternative)", - category: "History" - } -]; +import { Keyboard } from "lucide-react"; +import { useKeyboardShortcuts } from "@/hooks/use-keyboard-shortcuts"; const KeyBadge = ({ keyName }: { keyName: string }) => { // Replace common key names with symbols @@ -142,7 +30,7 @@ const KeyBadge = ({ keyName }: { keyName: string }) => { ); }; -const ShortcutItem = ({ shortcut }: { shortcut: Shortcut }) => ( +const ShortcutItem = ({ shortcut }: { shortcut: any }) => (
{shortcut.icon && ( @@ -151,7 +39,7 @@ const ShortcutItem = ({ shortcut }: { shortcut: Shortcut }) => ( {shortcut.description}
- {shortcut.keys.map((key, index) => ( + {shortcut.keys.map((key: string, index: number) => (
{index < shortcut.keys.length - 1 && ( @@ -166,7 +54,10 @@ const ShortcutItem = ({ shortcut }: { shortcut: Shortcut }) => ( export const KeyboardShortcutsHelp = () => { const [open, setOpen] = useState(false); - const categories = Array.from(new Set(shortcuts.map(s => s.category))); + // Get shortcuts from centralized hook (disabled so it doesn't add event listeners) + const { shortcuts } = useKeyboardShortcuts({ enabled: false }); + + const categories = Array.from(new Set(shortcuts.map((s) => s.category))); return ( @@ -187,16 +78,16 @@ export const KeyboardShortcutsHelp = () => { Most shortcuts work when the timeline is focused. - +
- {categories.map(category => ( + {categories.map((category) => (

{category}

{shortcuts - .filter(shortcut => shortcut.category === category) + .filter((shortcut) => shortcut.category === category) .map((shortcut, index) => ( ))} @@ -208,7 +99,9 @@ export const KeyboardShortcutsHelp = () => {

Tips:

    -
  • • Shortcuts work when the editor is focused (not typing in inputs)
  • +
  • + • Shortcuts work when the editor is focused (not typing in inputs) +
  • • J/K/L are industry-standard video editing shortcuts
  • • Use arrow keys for frame-perfect positioning
  • • Hold Shift with arrow keys for larger jumps
  • @@ -217,4 +110,4 @@ export const KeyboardShortcutsHelp = () => {
); -}; \ No newline at end of file +}; -- cgit v1.3.1