From f8ccb673bfd327e9001d5b7ba1ff77f303c2ec49 Mon Sep 17 00:00:00 2001 From: Maze Winther Date: Fri, 18 Jul 2025 11:51:47 +0200 Subject: style: match editing experience to match capcut --- .../web/src/components/keyboard-shortcuts-help.tsx | 187 +++++++++++++++------ 1 file changed, 131 insertions(+), 56 deletions(-) (limited to 'apps/web/src/components/keyboard-shortcuts-help.tsx') diff --git a/apps/web/src/components/keyboard-shortcuts-help.tsx b/apps/web/src/components/keyboard-shortcuts-help.tsx index 0481b1dd..91cfb01a 100644 --- a/apps/web/src/components/keyboard-shortcuts-help.tsx +++ b/apps/web/src/components/keyboard-shortcuts-help.tsx @@ -1,7 +1,7 @@ "use client"; import { useKeyboardShortcuts } from "@/hooks/use-keyboard-shortcuts"; -import { useState } from "react"; +import { useState, useEffect } from "react"; import { Button } from "./ui/button"; import { Dialog, @@ -12,13 +12,13 @@ import { DialogTrigger, } from "./ui/dialog"; import { getPlatformSpecialKey } from "@/lib/utils"; -import { Badge } from "./ui/badge"; -import { Keyboard, Settings } from "lucide-react"; +import { Keyboard } from "lucide-react"; import { useKeyboardShortcutsHelp, KeyboardShortcut, } from "@/hooks/use-keyboard-shortcuts-help"; -import { KeybindingEditor } from "./keybinding-editor"; +import { useKeybindingsStore } from "@/stores/keybindings-store"; +import { toast } from "sonner"; const modifier: { [key: string]: string; @@ -37,7 +37,15 @@ function getKeyWithModifier(key: string) { return modifier[key] || key; } -const ShortcutItem = ({ shortcut }: { shortcut: KeyboardShortcut }) => { +const ShortcutItem = ({ + shortcut, + recordingKey, + onStartRecording +}: { + shortcut: KeyboardShortcut; + recordingKey: string | null; + onStartRecording: (keyId: string, shortcut: KeyboardShortcut) => void; +}) => { // Filter out lowercase duplicates for display - if both "j" and "J" exist, only show "J" const displayKeys = shortcut.keys.filter((key: string) => { if ( @@ -61,11 +69,21 @@ const ShortcutItem = ({ shortcut }: { shortcut: KeyboardShortcut }) => { {displayKeys.map((key: string, index: number) => (
- {key.split("+").map((keyPart: string, partIndex: number) => ( - - {getKeyWithModifier(keyPart)} - - ))} + {key.split("+").map((keyPart: string, partIndex: number) => { + const keyId = `${shortcut.id}-${index}-${partIndex}`; + return ( + onStartRecording(keyId, shortcut)} + > + {getKeyWithModifier(keyPart)} + + ); + })}
{index < displayKeys.length - 1 && ( or @@ -77,33 +95,110 @@ const ShortcutItem = ({ shortcut }: { shortcut: KeyboardShortcut }) => { ); }; +const EditableShortcutKey = ({ + children, + keyId, + originalKey, + shortcut, + isRecording, + onStartRecording +}: { + children: React.ReactNode; + keyId: string; + originalKey: string; + shortcut: KeyboardShortcut; + isRecording: boolean; + onStartRecording: () => void; +}) => { + const handleClick = (e: React.MouseEvent) => { + e.preventDefault(); + e.stopPropagation(); + onStartRecording(); + }; + + return ( + + {children} + + ); +}; + export const KeyboardShortcutsHelp = () => { const [open, setOpen] = useState(false); - const [showEditor, setShowEditor] = useState(false); + const [recordingKey, setRecordingKey] = useState(null); + const [recordingShortcut, setRecordingShortcut] = useState(null); + + const { + updateKeybinding, + removeKeybinding, + getKeybindingString, + validateKeybinding, + getKeybindingsForAction, + } = useKeybindingsStore(); // Get shortcuts from centralized hook const { shortcuts } = useKeyboardShortcutsHelp(); const categories = Array.from(new Set(shortcuts.map((s) => s.category))); - if (showEditor) { - return ( - - - - - - setShowEditor(false)} - /> - - - ); - } + useEffect(() => { + if (!recordingKey || !recordingShortcut) return; + + const handleKeyDown = (e: KeyboardEvent) => { + e.preventDefault(); + e.stopPropagation(); + + const keyString = getKeybindingString(e); + if (keyString) { + // Auto-save the new keybinding + const conflict = validateKeybinding(keyString, recordingShortcut.action); + if (conflict) { + toast.error( + `Key "${keyString}" is already bound to "${conflict.existingAction}"` + ); + setRecordingKey(null); + setRecordingShortcut(null); + return; + } + + // Remove old keybindings for this action + const oldKeys = getKeybindingsForAction(recordingShortcut.action); + oldKeys.forEach((key) => removeKeybinding(key)); + + // Add new keybinding + updateKeybinding(keyString, recordingShortcut.action); + + setRecordingKey(null); + setRecordingShortcut(null); + } + }; + + const handleClickOutside = (e: MouseEvent) => { + setRecordingKey(null); + setRecordingShortcut(null); + }; + + document.addEventListener("keydown", handleKeyDown); + document.addEventListener("click", handleClickOutside); + + return () => { + document.removeEventListener("keydown", handleKeyDown); + document.removeEventListener("click", handleClickOutside); + }; + }, [recordingKey, recordingShortcut, getKeybindingString, updateKeybinding, removeKeybinding, validateKeybinding, getKeybindingsForAction]); + + const handleStartRecording = (keyId: string, shortcut: KeyboardShortcut) => { + setRecordingKey(keyId); + setRecordingShortcut(shortcut); + }; return ( @@ -121,21 +216,10 @@ export const KeyboardShortcutsHelp = () => { Speed up your video editing workflow with these keyboard shortcuts. - Most shortcuts work when the timeline is focused. + Click any shortcut key to edit it. -
- -
-
{categories.map((category) => (
@@ -146,7 +230,12 @@ export const KeyboardShortcutsHelp = () => { {shortcuts .filter((shortcut) => shortcut.category === category) .map((shortcut, index) => ( - + ))}
@@ -156,17 +245,3 @@ export const KeyboardShortcutsHelp = () => {
); }; - -function ShortcutKey({ children }: { children: React.ReactNode }) { - return ( - - {children} - - ); -} -- cgit v1.3.1