diff --git a/apps/web/src/components/editor/panels/preview/toolbar.tsx b/apps/web/src/components/editor/panels/preview/toolbar.tsx index 9ee6c822a..b3bc649b9 100644 --- a/apps/web/src/components/editor/panels/preview/toolbar.tsx +++ b/apps/web/src/components/editor/panels/preview/toolbar.tsx @@ -26,7 +26,9 @@ import { PREVIEW_ZOOM_PRESETS } from "@/lib/preview/zoom"; import { usePreviewViewport } from "./preview-viewport"; import { GridPopover } from "./guide-popover"; import { usePreviewStore } from "@/stores/preview-store"; - +import { useKeyboardShortcutsHelp } from "@/hooks/use-keyboard-shortcuts-help"; +import { Tooltip, TooltipProvider, TooltipTrigger, TooltipContent } from "@/components/ui/tooltip"; +import { Kbd } from "@/components/ui/kbd"; export function PreviewToolbar({ onToggleFullscreen, }: { @@ -135,14 +137,32 @@ function ZoomSelect() { function PlayPauseButton() { const isPlaying = useEditor((e) => e.playback.getIsPlaying()); - + const { shortcuts } = useKeyboardShortcutsHelp(); + const togglePlay = shortcuts.find( + item => item.action === "toggle-play" + ); return ( - + + + + + + + + {togglePlay?.description} +
+ + {togglePlay?.keys?.join(" / ")} + +
+
+
+
); } diff --git a/apps/web/src/components/editor/panels/timeline/timeline-toolbar.tsx b/apps/web/src/components/editor/panels/timeline/timeline-toolbar.tsx index 80ea10d0a..6db57bbee 100644 --- a/apps/web/src/components/editor/panels/timeline/timeline-toolbar.tsx +++ b/apps/web/src/components/editor/panels/timeline/timeline-toolbar.tsx @@ -49,7 +49,8 @@ import { OcRippleIcon } from "@/components/icons"; import { GraphEditorPopover } from "./graph-editor/popover"; import { PopoverTrigger } from "@/components/ui/popover"; import { useGraphEditorController } from "./graph-editor/use-controller"; - +import { useKeyboardShortcutsHelp } from "@/hooks/use-keyboard-shortcuts-help"; +import { Kbd } from "@/components/ui/kbd"; export function TimelineToolbar({ zoomLevel, minZoom, @@ -95,6 +96,12 @@ function ToolbarLeftSection() { const isCurrentlyBookmarked = useEditor((e) => e.scenes.isBookmarked({ time: e.playback.getCurrentTime() }), ); + const { shortcuts } = useKeyboardShortcutsHelp(); + const shortcutMap = Object.fromEntries( + shortcuts.map(s => [s.action, s]) + ); + + const selectedElement = selectedElements.length === 1 ? (editor.timeline.getElementsWithTracks({ @@ -128,6 +135,109 @@ function ToolbarLeftSection() { element: selectedElement.element, }); + const ToolTipShortcuts = { + split: { + icon: , + tooltip: ( +
+ Split element + {shortcutMap["split"]?.keys?.[0]} +
+ ), + disabled: false, + onClick: ({ event }) => + handleAction({ action: "split", event }), + }, + + "split-left": { + icon: , + tooltip: ( +
+ Split left + {shortcutMap["split-left"]?.keys?.[0]} +
+ ), + disabled: false, + onClick: ({ event }) => + handleAction({ action: "split-left", event }), + }, + + "split-right": { + icon: , + tooltip: ( +
+ Split right + {shortcutMap["split-right"]?.keys?.[0]} +
+ ), + disabled: false, + onClick: ({ event }) => + handleAction({ action: "split-right", event }), + }, + + "toggle-source-audio": { + icon: ( + + ), + tooltip: sourceAudioLabel, + disabled: !canToggleSelectedSourceAudio, + onClick: ({ event }) => + handleAction({ + action: "toggle-source-audio", + event, + }), + }, + + "duplicate-selected": { + icon: , + tooltip: ( +
+ Duplicate element + + {shortcutMap["duplicate-selected"]?.keys?.[0]} + +
+ ), + disabled: false, + onClick: ({ event }) => + handleAction({ + action: "duplicate-selected", + event, + }), + }, + + "freeze-frame": { + icon: , + tooltip: "Freeze frame (coming soon)", + disabled: true, + onClick: () => {}, + }, + + "delete-selected": { + icon: , + tooltip: ( +
+ Delete element + + {shortcutMap["delete-selected"]?.keys?.join(" / ")} + +
+ ), + disabled: false, + onClick: ({ event }) => + handleAction({ + action: "delete-selected", + event, + }), + }, +}; + const handleAction = ({ action, event, @@ -142,61 +252,14 @@ function ToolbarLeftSection() { return (
- } - tooltip="Split element" - onClick={({ event }) => handleAction({ action: "split", event })} - /> - - } - tooltip="Split left" - onClick={({ event }) => handleAction({ action: "split-left", event })} - /> - - } - tooltip="Split right" - onClick={({ event }) => - handleAction({ action: "split-right", event }) - } - /> - - ( + - } - tooltip={sourceAudioLabel} - disabled={!canToggleSelectedSourceAudio} - onClick={({ event }) => - handleAction({ action: "toggle-source-audio", event }) - } - /> - - } - tooltip="Duplicate element" - onClick={({ event }) => - handleAction({ action: "duplicate-selected", event }) - } - /> - - } - tooltip="Freeze frame (coming soon)" - disabled={true} - onClick={({ event: _event }) => {}} - /> - - } - tooltip="Delete element" - onClick={({ event }) => - handleAction({ action: "delete-selected", event }) - } - /> + ))}
@@ -339,7 +402,7 @@ function ToolbarButton({ buttonWrapper, }: { icon: React.ReactNode; - tooltip: string; + tooltip: React.ReactNode | string; onClick?: ({ event }: { event: React.MouseEvent }) => void; disabled?: boolean; isActive?: boolean; diff --git a/apps/web/src/components/ui/kbd.tsx b/apps/web/src/components/ui/kbd.tsx new file mode 100644 index 000000000..77a4052d8 --- /dev/null +++ b/apps/web/src/components/ui/kbd.tsx @@ -0,0 +1,19 @@ +"use client" +import * as React from "react"; +import { cn } from "@/utils/ui"; +type KbdProps = { + children: React.ReactNode; + className?: string; +} +export const Kbd = ( + { + children, + className + + }: KbdProps) => { + return ( + + {children} + + ); +} \ No newline at end of file diff --git a/bun.lock b/bun.lock index acb2f6e81..666822033 100644 --- a/bun.lock +++ b/bun.lock @@ -10,6 +10,7 @@ "@types/react-dom": "^19.2.3", "better-auth": "^1.4.15", "next": "^16.1.3", + "opencut-wasm": "^0.2.5", }, "devDependencies": { "turbo": "^2.8.20", @@ -54,7 +55,7 @@ "nanoid": "^5.1.5", "next": "16.1.3", "next-themes": "^0.4.4", - "opencut-wasm": "file:../../rust/wasm/pkg", + "opencut-wasm": "^0.2.5", "pg": "^8.16.2", "postgres": "^3.4.5", "radix-ui": "^1.4.3", @@ -785,7 +786,7 @@ "@turbo/windows-arm64": ["@turbo/windows-arm64@2.8.20", "", { "os": "win32", "cpu": "arm64" }, "sha512-voicVULvUV5yaGXo0Iue13BcHGYW3u0VgqSbfQwBaHbpj1zLjYV4KIe+7fYIo6DO8FVUJzxFps3ODCQG/Wy2Qw=="], - "@types/bun": ["@types/bun@1.3.11", "", { "dependencies": { "bun-types": "1.3.11" } }, "sha512-5vPne5QvtpjGpsGYXiFyycfpDF2ECyPcTSsFBMa0fraoxiQyMJ3SmuQIGhzPg2WJuWxVBoxWJ2kClYTcw/4fAg=="], + "@types/bun": ["@types/bun@1.3.12", "", { "dependencies": { "bun-types": "1.3.12" } }, "sha512-DBv81elK+/VSwXHDlnH3Qduw+KxkTIWi7TXkAeh24zpi5l0B2kUg9Ga3tb4nJaPcOFswflgi/yAvMVBPrxMB+A=="], "@types/culori": ["@types/culori@4.0.1", "", {}, "sha512-43M51r/22CjhbOXyGT361GZ9vncSVQ39u62x5eJdBQFviI8zWp2X5jzqg7k4M6PVgDQAClpy2bUe2dtwEgEDVQ=="], @@ -875,7 +876,7 @@ "buffer-from": ["buffer-from@1.1.2", "", {}, "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ=="], - "bun-types": ["bun-types@1.3.11", "", { "dependencies": { "@types/node": "*" } }, "sha512-1KGPpoxQWl9f6wcZh57LvrPIInQMn2TQ7jsgxqpRzg+l0QPOFvJVH7HmvHo/AiPgwXy+/Thf6Ov3EdVn1vOabg=="], + "bun-types": ["bun-types@1.3.12", "", { "dependencies": { "@types/node": "*" } }, "sha512-HqOLj5PoFajAQciOMRiIZGNoKxDJSr6qigAttOX40vJuSp6DN/CxWp9s3C1Xwm4oH7ybueITwiaOcWXoYVoRkA=="], "bytes": ["bytes@3.1.2", "", {}, "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg=="], @@ -1359,6 +1360,8 @@ "onnxruntime-web": ["onnxruntime-web@1.22.0-dev.20250409-89f8206ba4", "", { "dependencies": { "flatbuffers": "^25.1.24", "guid-typescript": "^1.0.9", "long": "^5.2.3", "onnxruntime-common": "1.22.0-dev.20250409-89f8206ba4", "platform": "^1.3.6", "protobufjs": "^7.2.4" } }, "sha512-0uS76OPgH0hWCPrFKlL8kYVV7ckM7t/36HfbgoFw6Nd0CZVVbQC4PkrR8mBX8LtNUFZO25IQBqV2Hx2ho3FlbQ=="], + "opencut-wasm": ["opencut-wasm@0.2.5", "", {}, "sha512-tgUeCesdt6DcB2ILzDkAJR09/FL8XeXntWVioudg1PYO0zopsBkMj9yq8boxAMQPrljlVZwSL8dTF2Px0Dn/NA=="], + "p-limit": ["p-limit@6.2.0", "", { "dependencies": { "yocto-queue": "^1.1.1" } }, "sha512-kuUqqHNUqoIWp/c467RI4X6mmyuojY5jGutNU0wVTmEOOfcuwLqyMVoAi9MKi2Ak+5i9+nhmrK4ufZE8069kHA=="], "package-json-from-dist": ["package-json-from-dist@1.0.1", "", {}, "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw=="], @@ -1721,8 +1724,6 @@ "@node-minify/core/mkdirp": ["mkdirp@1.0.4", "", { "bin": { "mkdirp": "bin/cmd.js" } }, "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw=="], - "@opencut/web/opencut-wasm": ["opencut-wasm@file:rust/wasm/pkg", {}], - "@opencut/web/typescript": ["typescript@5.8.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ=="], "@opennextjs/aws/esbuild": ["esbuild@0.25.4", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.25.4", "@esbuild/android-arm": "0.25.4", "@esbuild/android-arm64": "0.25.4", "@esbuild/android-x64": "0.25.4", "@esbuild/darwin-arm64": "0.25.4", "@esbuild/darwin-x64": "0.25.4", "@esbuild/freebsd-arm64": "0.25.4", "@esbuild/freebsd-x64": "0.25.4", "@esbuild/linux-arm": "0.25.4", "@esbuild/linux-arm64": "0.25.4", "@esbuild/linux-ia32": "0.25.4", "@esbuild/linux-loong64": "0.25.4", "@esbuild/linux-mips64el": "0.25.4", "@esbuild/linux-ppc64": "0.25.4", "@esbuild/linux-riscv64": "0.25.4", "@esbuild/linux-s390x": "0.25.4", "@esbuild/linux-x64": "0.25.4", "@esbuild/netbsd-arm64": "0.25.4", "@esbuild/netbsd-x64": "0.25.4", "@esbuild/openbsd-arm64": "0.25.4", "@esbuild/openbsd-x64": "0.25.4", "@esbuild/sunos-x64": "0.25.4", "@esbuild/win32-arm64": "0.25.4", "@esbuild/win32-ia32": "0.25.4", "@esbuild/win32-x64": "0.25.4" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-8pgjLUcUjcgDg+2Q4NYXnPbo/vncAY4UmyaCm0jZevERqCHZIaWwdJHkf8XQtu4AxSKCdvrUbT0XUr1IdZzI8Q=="], diff --git a/package.json b/package.json index 22504650f..4c40532ea 100644 --- a/package.json +++ b/package.json @@ -27,7 +27,8 @@ "@types/react": "^19.2.10", "@types/react-dom": "^19.2.3", "better-auth": "^1.4.15", - "next": "^16.1.3" + "next": "^16.1.3", + "opencut-wasm": "^0.2.5" }, "devDependencies": { "turbo": "^2.8.20",