feat: display keyboard shortcuts on toolbar buttons (#788)

- Add shortcut hints to toolbar buttons via tooltip text
- Integrate useKeyboardShortcutsHelp hook for consistent shortcut retrieval
- Timeline toolbar: split, duplicate, delete, snapping, bookmark buttons
- Preview toolbar: play/pause button
- Format: tooltip text appended with '(Key)' or '(Key1 or Key2)'
- Gracefully handle actions without defined shortcuts
This commit is contained in:
Senthil Raja R 2026-05-17 07:57:16 +05:30 committed by GitHub
parent fbe3db74d7
commit 6fdb1559aa
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 53 additions and 18 deletions

View File

@ -4,6 +4,7 @@ import { useState, useEffect } from "react";
import { useEditor } from "@/editor/use-editor"; import { useEditor } from "@/editor/use-editor";
import { formatTimecode } from "opencut-wasm"; import { formatTimecode } from "opencut-wasm";
import { invokeAction } from "@/actions"; import { invokeAction } from "@/actions";
import { useKeyboardShortcutsHelp } from "@/actions/use-keyboard-shortcuts-help";
import { EditableTimecode } from "@/components/editable-timecode"; import { EditableTimecode } from "@/components/editable-timecode";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { import {
@ -24,6 +25,11 @@ import { PREVIEW_ZOOM_PRESETS } from "@/preview/zoom";
import { usePreviewViewport } from "./preview-viewport"; import { usePreviewViewport } from "./preview-viewport";
import { GridPopover } from "./guide-popover"; import { GridPopover } from "./guide-popover";
import { usePreviewStore } from "@/preview/preview-store"; import { usePreviewStore } from "@/preview/preview-store";
import {
Tooltip,
TooltipTrigger,
TooltipContent,
} from "@/components/ui/tooltip";
import type { MediaTime } from "@/wasm"; import type { MediaTime } from "@/wasm";
export function PreviewToolbar({ export function PreviewToolbar({
@ -133,14 +139,24 @@ function ZoomSelect() {
function PlayPauseButton() { function PlayPauseButton() {
const isPlaying = useEditor((e) => e.playback.getIsPlaying()); const isPlaying = useEditor((e) => e.playback.getIsPlaying());
const { shortcuts } = useKeyboardShortcutsHelp();
const shortcut = shortcuts.find((s) => s.action === "toggle-play");
const tooltipText = shortcut
? `Play/Pause (${shortcut.keys.join(" or ")})`
: "Play/Pause";
return ( return (
<Button <Tooltip delayDuration={200}>
variant="text" <TooltipTrigger asChild>
size="icon" <Button
onClick={() => invokeAction("toggle-play")} variant="text"
> size="icon"
<HugeiconsIcon icon={isPlaying ? PauseIcon : PlayIcon} /> onClick={() => invokeAction("toggle-play")}
</Button> >
<HugeiconsIcon icon={isPlaying ? PauseIcon : PlayIcon} />
</Button>
</TooltipTrigger>
<TooltipContent>{tooltipText}</TooltipContent>
</Tooltip>
); );
} }

View File

@ -19,6 +19,7 @@ import { TIMELINE_ZOOM_MAX } from "@/timeline/scale";
import { sliderToZoom, zoomToSlider } from "@/timeline/zoom-utils"; import { sliderToZoom, zoomToSlider } from "@/timeline/zoom-utils";
import { ScenesView } from "@/components/editor/scenes-view"; import { ScenesView } from "@/components/editor/scenes-view";
import { type TActionWithOptionalArgs, invokeAction } from "@/actions"; import { type TActionWithOptionalArgs, invokeAction } from "@/actions";
import { useKeyboardShortcutsHelp } from "@/actions/use-keyboard-shortcuts-help";
import { import {
canToggleSourceAudio, canToggleSourceAudio,
getSourceAudioActionLabel, getSourceAudioActionLabel,
@ -143,18 +144,21 @@ function ToolbarLeftSection() {
<div className="flex items-center gap-1"> <div className="flex items-center gap-1">
<TooltipProvider delayDuration={500}> <TooltipProvider delayDuration={500}>
<ToolbarButton <ToolbarButton
action="split"
icon={<HugeiconsIcon icon={ScissorIcon} />} icon={<HugeiconsIcon icon={ScissorIcon} />}
tooltip="Split element" tooltip="Split element"
onClick={({ event }) => handleAction({ action: "split", event })} onClick={({ event }) => handleAction({ action: "split", event })}
/> />
<ToolbarButton <ToolbarButton
action="split-left"
icon={<HugeiconsIcon icon={AlignLeftIcon} />} icon={<HugeiconsIcon icon={AlignLeftIcon} />}
tooltip="Split left" tooltip="Split left"
onClick={({ event }) => handleAction({ action: "split-left", event })} onClick={({ event }) => handleAction({ action: "split-left", event })}
/> />
<ToolbarButton <ToolbarButton
action="split-right"
icon={<HugeiconsIcon icon={AlignRightIcon} />} icon={<HugeiconsIcon icon={AlignRightIcon} />}
tooltip="Split right" tooltip="Split right"
onClick={({ event }) => onClick={({ event }) =>
@ -163,6 +167,7 @@ function ToolbarLeftSection() {
/> />
<ToolbarButton <ToolbarButton
action="toggle-source-audio"
icon={ icon={
<HugeiconsIcon <HugeiconsIcon
icon={isSelectedSourceAudioSeparated ? Unlink02Icon : Link02Icon} icon={isSelectedSourceAudioSeparated ? Unlink02Icon : Link02Icon}
@ -176,6 +181,7 @@ function ToolbarLeftSection() {
/> />
<ToolbarButton <ToolbarButton
action="duplicate-selected"
icon={<HugeiconsIcon icon={Copy01Icon} />} icon={<HugeiconsIcon icon={Copy01Icon} />}
tooltip="Duplicate element" tooltip="Duplicate element"
onClick={({ event }) => onClick={({ event }) =>
@ -191,6 +197,7 @@ function ToolbarLeftSection() {
/> />
<ToolbarButton <ToolbarButton
action="delete-selected"
icon={<HugeiconsIcon icon={Delete02Icon} />} icon={<HugeiconsIcon icon={Delete02Icon} />}
tooltip="Delete element" tooltip="Delete element"
onClick={({ event }) => onClick={({ event }) =>
@ -200,16 +207,15 @@ function ToolbarLeftSection() {
<div className="bg-border mx-1 h-6 w-px" /> <div className="bg-border mx-1 h-6 w-px" />
<Tooltip> <ToolbarButton
<ToolbarButton action="toggle-bookmark"
icon={<HugeiconsIcon icon={Bookmark02Icon} />} icon={<HugeiconsIcon icon={Bookmark02Icon} />}
isActive={isCurrentlyBookmarked} isActive={isCurrentlyBookmarked}
tooltip={isCurrentlyBookmarked ? "Remove bookmark" : "Add bookmark"} tooltip={isCurrentlyBookmarked ? "Remove bookmark" : "Add bookmark"}
onClick={({ event }) => onClick={({ event }) =>
handleAction({ action: "toggle-bookmark", event }) handleAction({ action: "toggle-bookmark", event })
} }
/> />
</Tooltip>
<GraphEditorPopover <GraphEditorPopover
open={graphEditor.open} open={graphEditor.open}
@ -284,6 +290,7 @@ function ToolbarRightSection({
<div className="flex items-center gap-1"> <div className="flex items-center gap-1">
<TooltipProvider delayDuration={500}> <TooltipProvider delayDuration={500}>
<ToolbarButton <ToolbarButton
action="toggle-snapping"
icon={<HugeiconsIcon icon={MagnetIcon} />} icon={<HugeiconsIcon icon={MagnetIcon} />}
isActive={snappingEnabled} isActive={snappingEnabled}
tooltip="Auto snapping" tooltip="Auto snapping"
@ -291,6 +298,7 @@ function ToolbarRightSection({
/> />
<ToolbarButton <ToolbarButton
action="toggle-ripple-editing"
icon={<OcRippleIcon size={24} className="scale-110" />} icon={<OcRippleIcon size={24} className="scale-110" />}
isActive={rippleEditingEnabled} isActive={rippleEditingEnabled}
tooltip="Ripple editing" tooltip="Ripple editing"
@ -337,6 +345,7 @@ function ToolbarButton({
disabled, disabled,
isActive, isActive,
buttonWrapper, buttonWrapper,
action,
}: { }: {
icon: React.ReactNode; icon: React.ReactNode;
tooltip: string; tooltip: string;
@ -344,7 +353,17 @@ function ToolbarButton({
disabled?: boolean; disabled?: boolean;
isActive?: boolean; isActive?: boolean;
buttonWrapper?: (button: React.ReactElement) => React.ReactElement; buttonWrapper?: (button: React.ReactElement) => React.ReactElement;
action?: TActionWithOptionalArgs;
}) { }) {
const { shortcuts } = useKeyboardShortcutsHelp();
const shortcut = action
? shortcuts.find((s) => s.action === action)
: null;
const tooltipContent = shortcut
? `${tooltip} (${shortcut.keys.join(" or ")})`
: tooltip;
const button = ( const button = (
<Button <Button
variant={isActive ? "secondary" : "text"} variant={isActive ? "secondary" : "text"}
@ -370,7 +389,7 @@ function ToolbarButton({
return ( return (
<Tooltip delayDuration={200}> <Tooltip delayDuration={200}>
<TooltipTrigger asChild>{trigger}</TooltipTrigger> <TooltipTrigger asChild>{trigger}</TooltipTrigger>
<TooltipContent>{tooltip}</TooltipContent> <TooltipContent>{tooltipContent}</TooltipContent>
</Tooltip> </Tooltip>
); );
} }