Merge 8d81b78a04 into 8bdc894690
This commit is contained in:
commit
226355c88b
|
|
@ -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 (
|
||||
<Button
|
||||
variant="text"
|
||||
size="icon"
|
||||
onClick={() => invokeAction("toggle-play")}
|
||||
>
|
||||
<HugeiconsIcon icon={isPlaying ? PauseIcon : PlayIcon} />
|
||||
</Button>
|
||||
<TooltipProvider duration={500}>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<Button
|
||||
variant="text"
|
||||
size="icon"
|
||||
onClick={() => invokeAction("toggle-play")}
|
||||
>
|
||||
<HugeiconsIcon icon={isPlaying ? PauseIcon : PlayIcon} />
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
|
||||
<TooltipContent className="flex items-center gap-2">
|
||||
<span> {togglePlay?.description} </span>
|
||||
<div className="flex gap-1">
|
||||
<Kbd>
|
||||
{togglePlay?.keys?.join(" / ")}
|
||||
</Kbd>
|
||||
</div>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
</TooltipProvider>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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: <HugeiconsIcon icon={ScissorIcon} />,
|
||||
tooltip: (
|
||||
<div className="flex items-center gap-2">
|
||||
<span>Split element</span>
|
||||
<Kbd>{shortcutMap["split"]?.keys?.[0]}</Kbd>
|
||||
</div>
|
||||
),
|
||||
disabled: false,
|
||||
onClick: ({ event }) =>
|
||||
handleAction({ action: "split", event }),
|
||||
},
|
||||
|
||||
"split-left": {
|
||||
icon: <HugeiconsIcon icon={AlignLeftIcon} />,
|
||||
tooltip: (
|
||||
<div className="flex items-center gap-2">
|
||||
<span>Split left</span>
|
||||
<Kbd>{shortcutMap["split-left"]?.keys?.[0]}</Kbd>
|
||||
</div>
|
||||
),
|
||||
disabled: false,
|
||||
onClick: ({ event }) =>
|
||||
handleAction({ action: "split-left", event }),
|
||||
},
|
||||
|
||||
"split-right": {
|
||||
icon: <HugeiconsIcon icon={AlignRightIcon} />,
|
||||
tooltip: (
|
||||
<div className="flex items-center gap-2">
|
||||
<span>Split right</span>
|
||||
<Kbd>{shortcutMap["split-right"]?.keys?.[0]}</Kbd>
|
||||
</div>
|
||||
),
|
||||
disabled: false,
|
||||
onClick: ({ event }) =>
|
||||
handleAction({ action: "split-right", event }),
|
||||
},
|
||||
|
||||
"toggle-source-audio": {
|
||||
icon: (
|
||||
<HugeiconsIcon
|
||||
icon={
|
||||
isSelectedSourceAudioSeparated
|
||||
? Unlink02Icon
|
||||
: Link02Icon
|
||||
}
|
||||
/>
|
||||
),
|
||||
tooltip: sourceAudioLabel,
|
||||
disabled: !canToggleSelectedSourceAudio,
|
||||
onClick: ({ event }) =>
|
||||
handleAction({
|
||||
action: "toggle-source-audio",
|
||||
event,
|
||||
}),
|
||||
},
|
||||
|
||||
"duplicate-selected": {
|
||||
icon: <HugeiconsIcon icon={Copy01Icon} />,
|
||||
tooltip: (
|
||||
<div className="flex items-center gap-2">
|
||||
<span>Duplicate element</span>
|
||||
<Kbd>
|
||||
{shortcutMap["duplicate-selected"]?.keys?.[0]}
|
||||
</Kbd>
|
||||
</div>
|
||||
),
|
||||
disabled: false,
|
||||
onClick: ({ event }) =>
|
||||
handleAction({
|
||||
action: "duplicate-selected",
|
||||
event,
|
||||
}),
|
||||
},
|
||||
|
||||
"freeze-frame": {
|
||||
icon: <HugeiconsIcon icon={SnowIcon} />,
|
||||
tooltip: "Freeze frame (coming soon)",
|
||||
disabled: true,
|
||||
onClick: () => {},
|
||||
},
|
||||
|
||||
"delete-selected": {
|
||||
icon: <HugeiconsIcon icon={Delete02Icon} />,
|
||||
tooltip: (
|
||||
<div className="flex items-center gap-2">
|
||||
<span>Delete element</span>
|
||||
<Kbd>
|
||||
{shortcutMap["delete-selected"]?.keys?.join(" / ")}
|
||||
</Kbd>
|
||||
</div>
|
||||
),
|
||||
disabled: false,
|
||||
onClick: ({ event }) =>
|
||||
handleAction({
|
||||
action: "delete-selected",
|
||||
event,
|
||||
}),
|
||||
},
|
||||
};
|
||||
|
||||
const handleAction = ({
|
||||
action,
|
||||
event,
|
||||
|
|
@ -142,61 +252,14 @@ function ToolbarLeftSection() {
|
|||
return (
|
||||
<div className="flex items-center gap-1">
|
||||
<TooltipProvider delayDuration={500}>
|
||||
<ToolbarButton
|
||||
icon={<HugeiconsIcon icon={ScissorIcon} />}
|
||||
tooltip="Split element"
|
||||
onClick={({ event }) => handleAction({ action: "split", event })}
|
||||
/>
|
||||
|
||||
<ToolbarButton
|
||||
icon={<HugeiconsIcon icon={AlignLeftIcon} />}
|
||||
tooltip="Split left"
|
||||
onClick={({ event }) => handleAction({ action: "split-left", event })}
|
||||
/>
|
||||
|
||||
<ToolbarButton
|
||||
icon={<HugeiconsIcon icon={AlignRightIcon} />}
|
||||
tooltip="Split right"
|
||||
onClick={({ event }) =>
|
||||
handleAction({ action: "split-right", event })
|
||||
}
|
||||
/>
|
||||
|
||||
<ToolbarButton
|
||||
icon={
|
||||
<HugeiconsIcon
|
||||
icon={isSelectedSourceAudioSeparated ? Unlink02Icon : Link02Icon}
|
||||
{Object.values(ToolTipShortcuts).map((item)=> (
|
||||
<ToolbarButton
|
||||
icon={item.icon}
|
||||
disabled={item.disabled}
|
||||
tooltip={item.tooltip}
|
||||
onClick={item.onClick}
|
||||
/>
|
||||
}
|
||||
tooltip={sourceAudioLabel}
|
||||
disabled={!canToggleSelectedSourceAudio}
|
||||
onClick={({ event }) =>
|
||||
handleAction({ action: "toggle-source-audio", event })
|
||||
}
|
||||
/>
|
||||
|
||||
<ToolbarButton
|
||||
icon={<HugeiconsIcon icon={Copy01Icon} />}
|
||||
tooltip="Duplicate element"
|
||||
onClick={({ event }) =>
|
||||
handleAction({ action: "duplicate-selected", event })
|
||||
}
|
||||
/>
|
||||
|
||||
<ToolbarButton
|
||||
icon={<HugeiconsIcon icon={SnowIcon} />}
|
||||
tooltip="Freeze frame (coming soon)"
|
||||
disabled={true}
|
||||
onClick={({ event: _event }) => {}}
|
||||
/>
|
||||
|
||||
<ToolbarButton
|
||||
icon={<HugeiconsIcon icon={Delete02Icon} />}
|
||||
tooltip="Delete element"
|
||||
onClick={({ event }) =>
|
||||
handleAction({ action: "delete-selected", event })
|
||||
}
|
||||
/>
|
||||
))}
|
||||
|
||||
<div className="bg-border mx-1 h-6 w-px" />
|
||||
|
||||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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 (
|
||||
<kbd className={cn("rounded bg-muted px-1.5 py-0.5 text-xs font-mono border", className)}>
|
||||
{children}
|
||||
</kbd>
|
||||
);
|
||||
}
|
||||
11
bun.lock
11
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=="],
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
Loading…
Reference in New Issue