diff --git a/src/app/globals.css b/src/app/globals.css
index 408e2d8e..b3d91236 100644
--- a/src/app/globals.css
+++ b/src/app/globals.css
@@ -140,6 +140,26 @@ textarea:focus {
}
:focus-visible {
+ outline: 2px solid var(--accent);
+ outline-offset: 2px;
+ border-radius: 4px;
+}
+
+@keyframes scale-in {
+ from {
+ opacity: 0;
+ transform: scale(0.96);
+ }
+
+ to {
+ opacity: 1;
+ transform: scale(1);
+ }
+}
+
+.animate-scale-in {
+ animation: scale-in 0.2s ease-out;
+}
outline: 0;
box-shadow: 0 0 0 3px var(--accent-muted);
border-radius: var(--radius);
diff --git a/src/components/KeyboardShortcutsModal.tsx b/src/components/KeyboardShortcutsModal.tsx
new file mode 100644
index 00000000..b095dd13
--- /dev/null
+++ b/src/components/KeyboardShortcutsModal.tsx
@@ -0,0 +1,107 @@
+"use client";
+
+import { Keyboard, X } from "lucide-react";
+
+interface KeyboardShortcutsModalProps {
+ open: boolean;
+ onClose: () => void;
+}
+
+function Kbd({ children }: { children: React.ReactNode }) {
+ return (
+
+ {children}
+
+ );
+}
+
+export default function KeyboardShortcutsModal({
+ open,
+ onClose,
+}: KeyboardShortcutsModalProps) {
+ if (!open) return null;
+
+ const shortcuts = [
+ {
+ keys: ["Ctrl", "Shift", "E"],
+ label: "Export video",
+ },
+ {
+ keys: ["M"],
+ label: "Toggle audio mute",
+ },
+ {
+ keys: ["R"],
+ label: "Reset all settings",
+ },
+ {
+ keys: ["Esc"],
+ label: "Cancel export / Close modal",
+ },
+ {
+ keys: ["1 - 9"],
+ label: "Switch presets",
+ },
+ {
+ keys: ["?"],
+ label: "Open keyboard shortcuts",
+ },
+ ];
+
+ return (
+
+
+
+
+
+
+
+
+
+
+
+ Keyboard Shortcuts
+
+
+
+ Speed up your workflow with shortcuts
+
+
+
+
+
+ {shortcuts.map((shortcut) => (
+
+
+ {shortcut.label}
+
+
+
+ {shortcut.keys.map((key) => (
+ {key}
+ ))}
+
+
+ ))}
+
+
+
+ );
+}
\ No newline at end of file
diff --git a/src/components/VideoEditor.tsx b/src/components/VideoEditor.tsx
index 1e4e9f0d..2da41574 100644
--- a/src/components/VideoEditor.tsx
+++ b/src/components/VideoEditor.tsx
@@ -21,10 +21,20 @@ import { getPresetById } from "@/lib/presets";
import { cn } from "@/lib/utils";
import {
- Layers, Crop, Scissors, RotateCw, Volume2, Type,
- SlidersHorizontal, Zap, AlertTriangle, Github, Copy
+ Layers,
+ Crop,
+ Scissors,
+ RotateCw,
+ Volume2,
+ SlidersHorizontal,
+ Zap,
+ AlertTriangle,
+ Github,
+ Copy,
+ Keyboard,
} from "lucide-react";
import OnboardingTour from "./OnboardingTour";
+import KeyboardShortcutsModal from "./KeyboardShortcutsModal";
import { useKeyboardShortcuts } from "@/hooks/useKeyboardShortcuts";
interface SectionProps {
@@ -52,6 +62,7 @@ function Section({ icon, title, children, delay = 0 }: SectionProps) {
);
}
+
/** Accordion section with collapsible content. */
function AccordionSection({
id,
@@ -117,84 +128,8 @@ function Kbd({ children }: { children: React.ReactNode }) {
);
}
-/** Collapsible panel that lists all keyboard shortcuts. */
-function KeyboardShortcutsPanel() {
- const [open, setOpen] = useState(false);
-
- const shortcuts: { keys: React.ReactNode[]; label: string }[] = [
- {
- keys: [
- Ctrl,
- +,
- Shift,
- +,
- E
- ],
- label: "Export video",
- },
- {
- keys: [M],
- label: "Toggle audio mute",
- },
- {
- keys: [R],
- label: "Reset all settings",
- },
- {
- keys: [Esc],
- label: "Cancel export",
- },
- {
- keys: [1, –, 9],
- label: "Switch preset by index",
- },
- {
- keys: [?],
- label: "Toggle this panel",
- },
-];
- return (
-
-
-
- {open && (
-
- {shortcuts.map(({ keys, label }) => (
- -
- {label}
- {keys}
-
- ))}
-
- )}
-
- );
-}
+/** Collapsible panel that lists all keyboard shortcuts. */
export default function VideoEditor() {
const {
@@ -220,10 +155,12 @@ export default function VideoEditor() {
handleExport,
status,
cancelExport,
- onToggleShortcutsModal: () => {},
+ onToggleShortcutsModal: () =>
+ setShortcutsOpen((prev) => !prev),
});
const [copied, setCopied] = useState(false);
+ const [shortcutsOpen, setShortcutsOpen] = useState(false);
const [shareCopied, setShareCopied] = useState(false);
const [selectedTextId, setSelectedTextId] = useState(null);
const [openSections, setOpenSections] = useState({
@@ -308,6 +245,20 @@ export default function VideoEditor() {
};
}, [videoSrc]);
+ useEffect(() => {
+ const handleEsc = (e: KeyboardEvent) => {
+ if (e.key === "Escape" && shortcutsOpen) {
+ setShortcutsOpen(false);
+ }
+ };
+
+ window.addEventListener("keydown", handleEsc);
+
+ return () => {
+ window.removeEventListener("keydown", handleEsc);
+ };
+}, [shortcutsOpen]);
+
return (
+
setShortcutsOpen(false)}
+ />
{status === "exporting" && `Exporting video: ${progress}%`}
@@ -325,6 +280,35 @@ export default function VideoEditor() {