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",