From 3e89d2998588f8e35c524e523ba2ee853f0f6475 Mon Sep 17 00:00:00 2001 From: Maze Winther Date: Mon, 20 Apr 2026 11:31:17 +0200 Subject: refactor: restructure files to their domains, new preview overlay system, and dep graph --- .../editor/dialogs/delete-project-dialog.tsx | 84 -------- .../components/editor/dialogs/migration-dialog.tsx | 44 ---- .../editor/dialogs/project-info-dialog.tsx | 81 -------- .../editor/dialogs/rename-project-dialog.tsx | 72 ------- .../components/editor/dialogs/shortcuts-dialog.tsx | 229 --------------------- .../editor/dialogs/storage-persistence-dialog.tsx | 41 ---- 6 files changed, 551 deletions(-) delete mode 100644 apps/web/src/components/editor/dialogs/delete-project-dialog.tsx delete mode 100644 apps/web/src/components/editor/dialogs/migration-dialog.tsx delete mode 100644 apps/web/src/components/editor/dialogs/project-info-dialog.tsx delete mode 100644 apps/web/src/components/editor/dialogs/rename-project-dialog.tsx delete mode 100644 apps/web/src/components/editor/dialogs/shortcuts-dialog.tsx delete mode 100644 apps/web/src/components/editor/dialogs/storage-persistence-dialog.tsx (limited to 'apps/web/src/components/editor/dialogs') diff --git a/apps/web/src/components/editor/dialogs/delete-project-dialog.tsx b/apps/web/src/components/editor/dialogs/delete-project-dialog.tsx deleted file mode 100644 index 0086384e..00000000 --- a/apps/web/src/components/editor/dialogs/delete-project-dialog.tsx +++ /dev/null @@ -1,84 +0,0 @@ -import { Button } from "@/components/ui/button"; -import { - Dialog, - DialogBody, - DialogContent, - DialogFooter, - DialogHeader, - DialogTitle, -} from "@/components/ui/dialog"; -import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert"; -import { Label } from "@/components/ui/label"; -import { Input } from "@/components/ui/input"; - -export function DeleteProjectDialog({ - isOpen, - onOpenChange, - onConfirm, - projectNames, -}: { - isOpen: boolean; - onOpenChange: (open: boolean) => void; - onConfirm: () => void; - projectNames: string[]; -}) { - const count = projectNames.length; - const isSingle = count === 1; - const singleName = isSingle ? projectNames[0] : null; - - return ( - - { - event.preventDefault(); - event.stopPropagation(); - }} - > - - - {singleName ? ( - <> - {"Delete '"} - - {singleName} - - {"'?"} - - ) : ( - `Delete ${count} projects?` - )} - - - - - Warning - - This will permanently delete{" "} - {singleName ? `"${singleName}"` : `${count} projects`} and all - associated files. - - -
- - -
-
- - - - -
-
- ); -} diff --git a/apps/web/src/components/editor/dialogs/migration-dialog.tsx b/apps/web/src/components/editor/dialogs/migration-dialog.tsx deleted file mode 100644 index 4c9550c2..00000000 --- a/apps/web/src/components/editor/dialogs/migration-dialog.tsx +++ /dev/null @@ -1,44 +0,0 @@ -"use client"; - -import { - Dialog, - DialogContent, - DialogDescription, - DialogHeader, - DialogTitle, -} from "@/components/ui/dialog"; -import { useEditor } from "@/hooks/use-editor"; -import { Loader2 } from "lucide-react"; - -export function MigrationDialog() { - const editor = useEditor(); - const migrationState = editor.project.getMigrationState(); - - if (!migrationState.isMigrating) return null; - - const title = migrationState.projectName - ? "Updating project" - : "Updating projects"; - const description = migrationState.projectName - ? `Upgrading "${migrationState.projectName}" from v${migrationState.fromVersion} to v${migrationState.toVersion}` - : `Upgrading projects from v${migrationState.fromVersion} to v${migrationState.toVersion}`; - - return ( - - event.preventDefault()} - onEscapeKeyDown={(event) => event.preventDefault()} - > - - {title} - {description} - - -
- -
-
-
- ); -} diff --git a/apps/web/src/components/editor/dialogs/project-info-dialog.tsx b/apps/web/src/components/editor/dialogs/project-info-dialog.tsx deleted file mode 100644 index ed4eb990..00000000 --- a/apps/web/src/components/editor/dialogs/project-info-dialog.tsx +++ /dev/null @@ -1,81 +0,0 @@ -import { - Dialog, - DialogBody, - DialogContent, - DialogFooter, - DialogHeader, - DialogTitle, -} from "@/components/ui/dialog"; -import type { TProjectMetadata } from "@/lib/project/types"; -import { formatDate } from "@/utils/date"; -import { formatTimecode, mediaTimeToSeconds } from "opencut-wasm"; -import { Button } from "@/components/ui/button"; - -function InfoRow({ - label, - value, -}: { - label: string; - value: string | React.ReactNode; -}) { - return ( -
- {label} - {value} -
- ); -} - -export function ProjectInfoDialog({ - isOpen, - onOpenChange, - project, -}: { - isOpen: boolean; - onOpenChange: (open: boolean) => void; - project: TProjectMetadata; -}) { - const durationSeconds = mediaTimeToSeconds({ time: project.duration }); - const durationFormatted = - project.duration > 0 - ? (formatTimecode({ time: project.duration, format: durationSeconds >= 3600 ? "HH:MM:SS" : "MM:SS" }) ?? "") - : "0:00"; - - return ( - - event.preventDefault()}> - - - {project.name} - - - - - - - - - {project.id.slice(0, 8)} - - } - /> - - - - - - - - ); -} diff --git a/apps/web/src/components/editor/dialogs/rename-project-dialog.tsx b/apps/web/src/components/editor/dialogs/rename-project-dialog.tsx deleted file mode 100644 index 73024d71..00000000 --- a/apps/web/src/components/editor/dialogs/rename-project-dialog.tsx +++ /dev/null @@ -1,72 +0,0 @@ -import { Button } from "@/components/ui/button"; -import { - Dialog, - DialogBody, - DialogContent, - DialogFooter, - DialogHeader, - DialogTitle, -} from "@/components/ui/dialog"; -import { Input } from "@/components/ui/input"; -import { useState } from "react"; -import { Label } from "@/components/ui/label"; - -export function RenameProjectDialog({ - isOpen, - onOpenChange, - onConfirm, - projectName, -}: { - isOpen: boolean; - onOpenChange: (open: boolean) => void; - onConfirm: (newName: string) => void; - projectName: string; -}) { - const [name, setName] = useState(projectName); - - const handleOpenChange = (open: boolean) => { - if (open) { - setName(projectName); - } - onOpenChange(open); - }; - - return ( - - - - Rename project - - - - - setName(e.target.value)} - onKeyDown={(e) => { - if (e.key === "Enter") { - e.preventDefault(); - onConfirm(name); - } - }} - placeholder="Enter a new name" - /> - - - - - - - - - ); -} diff --git a/apps/web/src/components/editor/dialogs/shortcuts-dialog.tsx b/apps/web/src/components/editor/dialogs/shortcuts-dialog.tsx deleted file mode 100644 index 70802b5b..00000000 --- a/apps/web/src/components/editor/dialogs/shortcuts-dialog.tsx +++ /dev/null @@ -1,229 +0,0 @@ -"use client"; - -import { useEffect, useState } from "react"; -import { toast } from "sonner"; -import { - type KeyboardShortcut, - useKeyboardShortcutsHelp, -} from "@/hooks/use-keyboard-shortcuts-help"; -import { useKeybindingsStore } from "@/stores/keybindings-store"; -import { Button } from "@/components/ui/button"; -import { - Dialog, - DialogBody, - DialogContent, - DialogFooter, - DialogHeader, - DialogTitle, -} from "@/components/ui/dialog"; - -export function ShortcutsDialog({ - isOpen, - onOpenChange, -}: { - isOpen: boolean; - onOpenChange: (open: boolean) => void; -}) { - const [recordingShortcut, setRecordingShortcut] = - useState(null); - - const { - updateKeybinding, - removeKeybinding, - getKeybindingString, - validateKeybinding, - getKeybindingsForAction, - setIsRecording, - resetToDefaults, - isRecording, - } = useKeybindingsStore(); - - const { shortcuts } = useKeyboardShortcutsHelp(); - - const categories = Array.from(new Set(shortcuts.map((s) => s.category))); - - useEffect(() => { - if (!isRecording || !recordingShortcut) return; - - const handleKeyDown = (e: KeyboardEvent) => { - e.preventDefault(); - e.stopPropagation(); - - const keyString = getKeybindingString(e); - if (keyString) { - const conflict = validateKeybinding( - keyString, - recordingShortcut.action, - ); - if (conflict) { - toast.error( - `Key "${keyString}" is already bound to "${conflict.existingAction}"`, - ); - setRecordingShortcut(null); - return; - } - - const oldKeys = getKeybindingsForAction(recordingShortcut.action); - for (const key of oldKeys) { - removeKeybinding(key); - } - - updateKeybinding(keyString, recordingShortcut.action); - - setIsRecording(false); - setRecordingShortcut(null); - } - }; - - const handleClickOutside = () => { - setRecordingShortcut(null); - setIsRecording(false); - }; - - document.addEventListener("keydown", handleKeyDown); - document.addEventListener("click", handleClickOutside); - - return () => { - document.removeEventListener("keydown", handleKeyDown); - document.removeEventListener("click", handleClickOutside); - }; - }, [ - recordingShortcut, - getKeybindingString, - updateKeybinding, - removeKeybinding, - validateKeybinding, - getKeybindingsForAction, - setIsRecording, - isRecording, - ]); - - const handleStartRecording = (shortcut: KeyboardShortcut) => { - setRecordingShortcut(shortcut); - setIsRecording(true); - }; - - return ( - - - - Keyboard shortcuts - - - -
- {categories.map((category) => ( -
-

- {category} -

-
- {shortcuts - .filter((shortcut) => shortcut.category === category) - .map((shortcut) => ( - handleStartRecording(shortcut)} - /> - ))} -
-
- ))} -
-
- - - -
-
- ); -} - -function ShortcutItem({ - shortcut, - isRecording, - onStartRecording, -}: { - shortcut: KeyboardShortcut; - isRecording: boolean; - onStartRecording: (params: { shortcut: KeyboardShortcut }) => void; -}) { - const displayKeys = shortcut.keys.filter((key: string) => { - if ( - key.includes("Cmd") && - shortcut.keys.includes(key.replace("Cmd", "Ctrl")) - ) - return false; - - return true; - }); - - return ( -
-
- {shortcut.icon && ( -
{shortcut.icon}
- )} - {shortcut.description} -
-
- {displayKeys.map((key: string, index: number) => ( -
-
- {key.split("+").map((keyPart: string, partIndex: number) => { - const keyId = `${shortcut.id}-${index}-${partIndex}`; - return ( - onStartRecording({ shortcut })} - > - {keyPart} - - ); - })} -
- {index < displayKeys.length - 1 && ( - or - )} -
- ))} -
-
- ); -} - -function EditableShortcutKey({ - children, - isRecording, - onStartRecording, -}: { - children: React.ReactNode; - isRecording: boolean; - onStartRecording: () => void; -}) { - const handleClick = (e: React.MouseEvent) => { - e.preventDefault(); - e.stopPropagation(); - onStartRecording(); - }; - - return ( - - ); -} diff --git a/apps/web/src/components/editor/dialogs/storage-persistence-dialog.tsx b/apps/web/src/components/editor/dialogs/storage-persistence-dialog.tsx deleted file mode 100644 index 0e854a93..00000000 --- a/apps/web/src/components/editor/dialogs/storage-persistence-dialog.tsx +++ /dev/null @@ -1,41 +0,0 @@ -"use client"; - -import { Button } from "@/components/ui/button"; -import { - Dialog, - DialogBody, - DialogContent, - DialogFooter, - DialogHeader, - DialogTitle, -} from "@/components/ui/dialog"; -import { useStoragePersistence } from "@/hooks/use-storage-persistence"; - -export function StoragePersistenceDialog() { - const { showDialog, onConfirm, onDismiss } = useStoragePersistence(); - - return ( - !open && onDismiss()}> - - - Don't lose your projects - - -

- Your browser can automatically delete your projects when storage - runs low. -

-

- Allow OpenCut to protect them? -

-
- - - - -
-
- ); -} -- cgit v1.3.1