refactor(web): scope workflow hotkeys (#36860)

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
This commit is contained in:
yyh 2026-05-31 21:14:13 +08:00 committed by GitHub
parent ec5404cc9d
commit 2fe8c48255
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
15 changed files with 131 additions and 126 deletions

View File

@ -3405,14 +3405,6 @@
"count": 2
}
},
"web/app/components/workflow/header/run-mode.tsx": {
"no-console": {
"count": 1
},
"ts/no-explicit-any": {
"count": 1
}
},
"web/app/components/workflow/header/test-run-menu.tsx": {
"erasable-syntax-only/enums": {
"count": 1

View File

@ -340,7 +340,6 @@ vi.mock('../syncing-data-modal', () => ({
vi.mock('../shortcuts/use-workflow-hotkeys', () => ({
useWorkflowHotkeys: workflowHookMocks.useShortcuts,
useWorkflowShortcut: vi.fn(),
}))
vi.mock('../hooks', () => ({

View File

@ -1,4 +1,5 @@
import type { ReactNode } from 'react'
import type { TestRunMenuRef } from '../test-run-menu'
import { fireEvent, render, screen } from '@testing-library/react'
import * as React from 'react'
import { WorkflowRunningStatus } from '@/app/components/workflow/types'
@ -13,6 +14,10 @@ const mockHandleWorkflowRunAllTriggersInWorkflow = vi.fn()
const mockHandleStopRun = vi.fn()
const mockNotify = vi.fn()
const mockTrackEvent = vi.fn()
const hotkeyRegistrations = vi.hoisted(() => new Map<string, {
callback: () => void
options?: { ignoreInputs?: boolean }
}>())
let mockWarningNodes: Array<{ id: string }> = []
let mockWorkflowRunningData: { result: { status: WorkflowRunningStatus }, task_id: string } | undefined
@ -42,9 +47,15 @@ vi.mock('@/app/components/workflow/store/workflow', () => ({
selector({ workflowRunningData: mockWorkflowRunningData, isListening: mockIsListening }),
}))
vi.mock('@/app/components/workflow/shortcuts/use-workflow-hotkeys', () => ({
useWorkflowShortcut: vi.fn(),
}))
vi.mock('@tanstack/react-hotkeys', async (importOriginal) => {
const actual = await importOriginal<typeof import('@tanstack/react-hotkeys')>()
return {
...actual,
useHotkey: (hotkey: string, callback: () => void, options?: { ignoreInputs?: boolean }) => {
hotkeyRegistrations.set(hotkey, { callback, options })
},
}
})
vi.mock('../../hooks/use-dynamic-test-run-options', () => ({
useDynamicTestRunOptions: () => mockDynamicOptions,
@ -77,21 +88,23 @@ vi.mock('@/app/components/base/icons/src/vender/line/mediaAndDevices', () => ({
vi.mock('../test-run-menu', async (importOriginal) => {
const actual = await importOriginal<typeof import('../test-run-menu')>()
const TestRunMenuMock = ({ children, options, onSelect, ref }: { children: ReactNode, options: Array<{ type: TriggerType, nodeId?: string, relatedNodeIds?: string[] }>, onSelect: (option: { type: TriggerType, nodeId?: string, relatedNodeIds?: string[] }) => void, ref?: React.Ref<TestRunMenuRef> }) => {
React.useImperativeHandle(ref, () => ({
toggle: vi.fn(),
}))
return (
<div>
<button data-testid="trigger-option" onClick={() => onSelect(options[0]!)}>
Trigger option
</button>
{children}
</div>
)
}
return {
...actual,
default: React.forwardRef(({ children, options, onSelect }: { children: ReactNode, options: Array<{ type: TriggerType, nodeId?: string, relatedNodeIds?: string[] }>, onSelect: (option: { type: TriggerType, nodeId?: string, relatedNodeIds?: string[] }) => void }, ref) => {
React.useImperativeHandle(ref, () => ({
toggle: vi.fn(),
}))
return (
<div>
<button data-testid="trigger-option" onClick={() => onSelect(options[0]!)}>
Trigger option
</button>
{children}
</div>
)
}),
default: TestRunMenuMock,
}
})
@ -101,6 +114,7 @@ describe('RunMode', () => {
mockWarningNodes = []
mockWorkflowRunningData = undefined
mockIsListening = false
hotkeyRegistrations.clear()
mockDynamicOptions = [
{ type: TriggerType.UserInput, nodeId: 'start-node' },
]
@ -150,4 +164,12 @@ describe('RunMode', () => {
expect(screen.getByText(/listening/i))!.toBeInTheDocument()
})
it('should register the test run menu shortcut as a page command outside text inputs', () => {
render(<RunMode />)
expect(hotkeyRegistrations.get('Alt+R')?.options).toEqual(
expect.objectContaining({ ignoreInputs: true }),
)
})
})

View File

@ -2,7 +2,10 @@ import { act, fireEvent, render, screen } from '@testing-library/react'
import VersionHistoryButton from '../version-history-button'
let mockTheme: 'light' | 'dark' = 'light'
const workflowShortcutHandlers = vi.hoisted(() => new Map<string, () => void | Promise<void>>())
const hotkeyRegistrations = vi.hoisted(() => new Map<string, {
callback: () => void
options?: { ignoreInputs?: boolean }
}>())
vi.mock('@/hooks/use-theme', () => ({
default: () => ({
@ -10,11 +13,15 @@ vi.mock('@/hooks/use-theme', () => ({
}),
}))
vi.mock('../../shortcuts/use-workflow-hotkeys', () => ({
useWorkflowShortcut: (id: string, callback: () => void | Promise<void>) => {
workflowShortcutHandlers.set(id, callback)
},
}))
vi.mock('@tanstack/react-hotkeys', async (importOriginal) => {
const actual = await importOriginal<typeof import('@tanstack/react-hotkeys')>()
return {
...actual,
useHotkey: (hotkey: string, callback: () => void, options?: { ignoreInputs?: boolean }) => {
hotkeyRegistrations.set(hotkey, { callback, options })
},
}
})
vi.mock('@langgenius/dify-ui/tooltip', () => ({
Tooltip: ({ children }: { children: React.ReactNode }) => <>{children}</>,
@ -25,7 +32,7 @@ vi.mock('@langgenius/dify-ui/tooltip', () => ({
describe('VersionHistoryButton', () => {
beforeEach(() => {
vi.clearAllMocks()
workflowShortcutHandlers.clear()
hotkeyRegistrations.clear()
mockTheme = 'light'
})
@ -43,10 +50,13 @@ describe('VersionHistoryButton', () => {
render(<VersionHistoryButton onClick={onClick} />)
await act(async () => {
await workflowShortcutHandlers.get('workflow.version-history')?.()
hotkeyRegistrations.get('Mod+Shift+H')?.callback()
})
expect(onClick).toHaveBeenCalledTimes(1)
expect(hotkeyRegistrations.get('Mod+Shift+H')?.options).toEqual(
expect.objectContaining({ ignoreInputs: true }),
)
})
it('should render the tooltip popup content on hover', async () => {

View File

@ -1,6 +1,8 @@
import type { TestRunMenuRef, TriggerOption } from './test-run-menu'
import type { EventEmitterValue } from '@/context/event-emitter'
import { cn } from '@langgenius/dify-ui/cn'
import { toast } from '@langgenius/dify-ui/toast'
import { useHotkey } from '@tanstack/react-hotkeys'
import * as React from 'react'
import { useCallback, useRef } from 'react'
import { useTranslation } from 'react-i18next'
@ -8,18 +10,21 @@ import { trackEvent } from '@/app/components/base/amplitude'
import { StopCircle } from '@/app/components/base/icons/src/vender/line/mediaAndDevices'
import { useWorkflowRun, useWorkflowRunValidation, useWorkflowStartRun } from '@/app/components/workflow/hooks'
import { ShortcutKbd } from '@/app/components/workflow/shortcuts/shortcut-kbd'
import { useWorkflowShortcut } from '@/app/components/workflow/shortcuts/use-workflow-hotkeys'
import { useStore } from '@/app/components/workflow/store/workflow'
import { WorkflowRunningStatus } from '@/app/components/workflow/types'
import { EVENT_WORKFLOW_STOP } from '@/app/components/workflow/variable-inspect/types'
import { useEventEmitterContextContext } from '@/context/event-emitter'
import { useDynamicTestRunOptions } from '../hooks/use-dynamic-test-run-options'
import { TEST_RUN_MENU_HOTKEY } from './shortcuts'
import TestRunMenu, { TriggerType } from './test-run-menu'
type RunModeProps = {
text?: string
}
const isWorkflowStopEvent = (value: EventEmitterValue) =>
typeof value !== 'string' && value.type === EVENT_WORKFLOW_STOP
const RunMode = ({
text,
}: RunModeProps) => {
@ -46,7 +51,9 @@ const RunMode = ({
testRunMenuRef.current?.toggle()
}, [])
useWorkflowShortcut('workflow.open-test-run-menu', handleToggleTestRunMenu)
useHotkey(TEST_RUN_MENU_HOTKEY, handleToggleTestRunMenu, {
ignoreInputs: true,
})
const handleStop = useCallback(() => {
handleStopRun(workflowRunningData?.task_id || '')
@ -88,15 +95,11 @@ const RunMode = ({
handleWorkflowRunAllTriggersInWorkflow(targetNodeIds)
trackEvent('app_start_action_time', { action_type: 'all' })
}
else {
// Placeholder for trigger-specific execution logic for schedule, webhook, plugin types
console.log('TODO: Handle trigger execution for type:', option.type, 'nodeId:', option.nodeId)
}
}, [warningNodes, t, handleWorkflowStartRunInWorkflow, handleWorkflowTriggerScheduleRunInWorkflow, handleWorkflowTriggerWebhookRunInWorkflow, handleWorkflowTriggerPluginRunInWorkflow, handleWorkflowRunAllTriggersInWorkflow])
const { eventEmitter } = useEventEmitterContextContext()
eventEmitter?.useSubscription((v: any) => {
if (v.type === EVENT_WORKFLOW_STOP)
eventEmitter?.useSubscription((v: EventEmitterValue) => {
if (isWorkflowStopEvent(v))
handleStop()
})
@ -131,7 +134,7 @@ const RunMode = ({
>
<span aria-hidden className="mr-1 i-ri-play-large-line size-4" />
{text ?? t('common.run', { ns: 'workflow' })}
<ShortcutKbd shortcut="workflow.open-test-run-menu" textColor="secondary" />
<ShortcutKbd hotkey={TEST_RUN_MENU_HOTKEY} textColor="secondary" />
</button>
</TestRunMenu>
)

View File

@ -0,0 +1 @@
export const TEST_RUN_MENU_HOTKEY = 'Alt+R'

View File

@ -6,12 +6,14 @@ import {
TooltipContent,
TooltipTrigger,
} from '@langgenius/dify-ui/tooltip'
import { useHotkey } from '@tanstack/react-hotkeys'
import * as React from 'react'
import { useCallback } from 'react'
import { useTranslation } from 'react-i18next'
import useTheme from '@/hooks/use-theme'
import { ShortcutKbd } from '../shortcuts/shortcut-kbd'
import { useWorkflowShortcut } from '../shortcuts/use-workflow-hotkeys'
const VERSION_HISTORY_HOTKEY = 'Mod+Shift+H'
type VersionHistoryButtonProps = {
onClick: () => Promise<unknown> | unknown
@ -24,7 +26,7 @@ const PopupContent = React.memo(() => {
<div className="px-0.5 system-xs-medium text-text-secondary">
{t('common.versionHistory', { ns: 'workflow' })}
</div>
<ShortcutKbd shortcut="workflow.version-history" bgColor="gray" textColor="secondary" />
<ShortcutKbd hotkey={VERSION_HISTORY_HOTKEY} bgColor="gray" textColor="secondary" />
</div>
)
})
@ -39,8 +41,10 @@ const VersionHistoryButton: FC<VersionHistoryButtonProps> = ({
await onClick?.()
}, [onClick])
useWorkflowShortcut('workflow.version-history', () => {
handleViewVersionHistory()
useHotkey(VERSION_HISTORY_HOTKEY, () => {
void handleViewVersionHistory()
}, {
ignoreInputs: true,
})
return (

View File

@ -12,6 +12,9 @@ type KeyPressRegistration = {
ignoreInputs?: boolean
preventDefault?: boolean
stopPropagation?: boolean
meta?: {
scope?: string
}
}
}
@ -187,6 +190,9 @@ describe('useShortcuts', () => {
renderWorkflowHook(() => useWorkflowHotkeys())
const deleteShortcut = findRegistration(registration => registration.keyFilter === 'Delete')
expect(deleteShortcut.options?.meta).toEqual(
expect.objectContaining({ scope: 'workflow-canvas' }),
)
const bodyEvent = createKeyboardEvent()
triggerShortcut(deleteShortcut, bodyEvent)

View File

@ -1,6 +1,6 @@
import type { RegisterableHotkey } from '@tanstack/react-hotkeys'
import type { ReactNode } from 'react'
import type { WorkflowShortcutId } from '@/app/components/workflow/shortcuts/definitions'
import type { WorkflowCanvasShortcutId } from '@/app/components/workflow/shortcuts/definitions'
import { ShortcutKbd } from '@/app/components/workflow/shortcuts/shortcut-kbd'
export const NODE_ACTIONS_MENU_WIDTH_CLASS_NAME = 'w-[240px] rounded-lg'
@ -14,7 +14,7 @@ export function NodeActionsMenuItemContent({
}: {
children: ReactNode
hotkey?: RegisterableHotkey | (string & {})
shortcut?: WorkflowShortcutId
shortcut?: WorkflowCanvasShortcutId
}) {
return (
<>

View File

@ -1,9 +1,11 @@
import type { FC } from 'react'
import { Button } from '@langgenius/dify-ui/button'
import { useHotkey } from '@tanstack/react-hotkeys'
import * as React from 'react'
import { useTranslation } from 'react-i18next'
import { ShortcutKbd } from '@/app/components/workflow/shortcuts/shortcut-kbd'
import { useWorkflowShortcut } from '@/app/components/workflow/shortcuts/use-workflow-hotkeys'
const JSON_SCHEMA_CONFIRM_HOTKEY = 'Mod+Enter'
type AdvancedActionsProps = {
isConfirmDisabled: boolean
@ -18,7 +20,7 @@ const AdvancedActions: FC<AdvancedActionsProps> = ({
}) => {
const { t } = useTranslation()
useWorkflowShortcut('workflow.json-schema-confirm', () => {
useHotkey(JSON_SCHEMA_CONFIRM_HOTKEY, () => {
onConfirm()
}, {
enabled: !isConfirmDisabled,
@ -38,7 +40,7 @@ const AdvancedActions: FC<AdvancedActionsProps> = ({
onClick={onConfirm}
>
<span>{t('operation.confirm', { ns: 'common' })}</span>
<ShortcutKbd shortcut="workflow.json-schema-confirm" bgColor="white" />
<ShortcutKbd hotkey={JSON_SCHEMA_CONFIRM_HOTKEY} bgColor="white" />
</Button>
</div>
)

View File

@ -1,5 +1,5 @@
import type { ReactElement } from 'react'
import type { WorkflowShortcutId } from '../shortcuts/definitions'
import type { WorkflowCanvasShortcutId } from '../shortcuts/definitions'
import {
Tooltip,
TooltipContent,
@ -11,7 +11,7 @@ import { ShortcutKbd } from '../shortcuts/shortcut-kbd'
type TipPopupProps = {
title: string
children: ReactElement
shortcut?: WorkflowShortcutId
shortcut?: WorkflowCanvasShortcutId
}
const TipPopup = ({
title,

View File

@ -9,6 +9,7 @@ import {
useCallback,
} from 'react'
import { useTranslation } from 'react-i18next'
import { TEST_RUN_MENU_HOTKEY } from './header/shortcuts'
import {
useDSL,
useIsChatMode,
@ -120,7 +121,7 @@ export function PanelContextmenu({
onClick={handleRunAction}
>
{isChatMode ? t('common.debugAndPreview', { ns: 'workflow' }) : t('common.run', { ns: 'workflow' })}
{!isChatMode && <ShortcutKbd shortcut="workflow.open-test-run-menu" />}
{!isChatMode && <ShortcutKbd hotkey={TEST_RUN_MENU_HOTKEY} />}
</ContextMenuItem>
</ContextMenuGroup>
<ContextMenuSeparator />

View File

@ -1,11 +1,10 @@
import type { RegisterableHotkey } from '@tanstack/react-hotkeys'
export type WorkflowShortcutId
export type WorkflowCanvasShortcutId
= | 'workflow.delete'
| 'workflow.copy'
| 'workflow.paste'
| 'workflow.duplicate'
| 'workflow.open-test-run-menu'
| 'workflow.undo'
| 'workflow.redo'
| 'workflow.pointer-mode'
@ -20,25 +19,23 @@ export type WorkflowShortcutId
| 'workflow.zoom-in'
| 'workflow.download-import-log'
| 'workflow.dim-other-nodes'
| 'workflow.json-schema-confirm'
| 'workflow.version-history'
export type WorkflowHotkeyMeta = {
id: WorkflowShortcutId
scope: 'workflow'
export type WorkflowCanvasHotkeyMeta = {
id: WorkflowCanvasShortcutId
scope: 'workflow-canvas'
name: string
description: string
}
export type WorkflowShortcutDefinition = {
id: WorkflowShortcutId
export type WorkflowCanvasShortcutDefinition = {
id: WorkflowCanvasShortcutId
hotkeys: readonly RegisterableHotkey[]
displayHotkey?: RegisterableHotkey | (string & {})
name: string
description: string
}
export const WORKFLOW_SHORTCUTS: Record<WorkflowShortcutId, WorkflowShortcutDefinition> = {
export const WORKFLOW_CANVAS_SHORTCUTS: Record<WorkflowCanvasShortcutId, WorkflowCanvasShortcutDefinition> = {
'workflow.delete': {
id: 'workflow.delete',
hotkeys: ['Delete', 'Backspace'],
@ -64,12 +61,6 @@ export const WORKFLOW_SHORTCUTS: Record<WorkflowShortcutId, WorkflowShortcutDefi
name: 'Duplicate',
description: 'Duplicate selected workflow nodes',
},
'workflow.open-test-run-menu': {
id: 'workflow.open-test-run-menu',
hotkeys: ['Alt+R'],
name: 'Open test run menu',
description: 'Open the workflow test run menu',
},
'workflow.undo': {
id: 'workflow.undo',
hotkeys: ['Mod+Z'],
@ -157,21 +148,9 @@ export const WORKFLOW_SHORTCUTS: Record<WorkflowShortcutId, WorkflowShortcutDefi
name: 'Dim other nodes',
description: 'Dim nodes outside the current workflow selection',
},
'workflow.json-schema-confirm': {
id: 'workflow.json-schema-confirm',
hotkeys: ['Mod+Enter'],
name: 'Confirm JSON schema edit',
description: 'Confirm the current JSON schema edit',
},
'workflow.version-history': {
id: 'workflow.version-history',
hotkeys: ['Mod+Shift+H'],
name: 'Version history',
description: 'Open workflow version history',
},
}
export const getWorkflowShortcutDisplayHotkey = (id: WorkflowShortcutId): RegisterableHotkey | (string & {}) => {
const shortcut = WORKFLOW_SHORTCUTS[id]
export const getWorkflowCanvasShortcutDisplayHotkey = (id: WorkflowCanvasShortcutId): RegisterableHotkey | (string & {}) => {
const shortcut = WORKFLOW_CANVAS_SHORTCUTS[id]
return shortcut.displayHotkey ?? shortcut.hotkeys[0]!
}

View File

@ -1,13 +1,13 @@
import type { KbdColor } from '@langgenius/dify-ui/kbd'
import type { FormatDisplayOptions, RegisterableHotkey } from '@tanstack/react-hotkeys'
import type { WorkflowShortcutId } from './definitions'
import type { WorkflowCanvasShortcutId } from './definitions'
import { cn } from '@langgenius/dify-ui/cn'
import { Kbd, KbdGroup } from '@langgenius/dify-ui/kbd'
import { formatForDisplay } from '@tanstack/react-hotkeys'
import { getWorkflowShortcutDisplayHotkey } from './definitions'
import { getWorkflowCanvasShortcutDisplayHotkey } from './definitions'
type ShortcutKbdProps = {
shortcut?: WorkflowShortcutId
shortcut?: WorkflowCanvasShortcutId
hotkey?: RegisterableHotkey | (string & {})
className?: string
textColor?: 'default' | 'secondary'
@ -38,7 +38,7 @@ export const ShortcutKbd = ({
bgColor = 'gray',
platform,
}: ShortcutKbdProps) => {
const displayHotkey = hotkey ?? (shortcut ? getWorkflowShortcutDisplayHotkey(shortcut) : undefined)
const displayHotkey = hotkey ?? (shortcut ? getWorkflowCanvasShortcutDisplayHotkey(shortcut) : undefined)
if (!displayHotkey)
return null
@ -52,9 +52,9 @@ export const ShortcutKbd = ({
)}
>
{
displayKeys.map((key, index) => (
displayKeys.map(key => (
<Kbd
key={`${key}-${index}`}
key={key}
color={bgColor}
className={cn(textColor === 'secondary' && 'text-text-tertiary')}
>

View File

@ -3,7 +3,7 @@ import type {
UseHotkeyDefinition,
UseHotkeyOptions,
} from '@tanstack/react-hotkeys'
import type { WorkflowHotkeyMeta, WorkflowShortcutDefinition, WorkflowShortcutId } from './definitions'
import type { WorkflowCanvasHotkeyMeta, WorkflowCanvasShortcutDefinition } from './definitions'
import { useHotkeys, useKeyHold } from '@tanstack/react-hotkeys'
import { useCallback, useEffect, useMemo, useRef } from 'react'
import { useReactFlow } from 'reactflow'
@ -19,7 +19,7 @@ import {
subscribeWorkflowCommand,
WorkflowCommand,
} from './commands'
import { WORKFLOW_SHORTCUTS } from './definitions'
import { WORKFLOW_CANVAS_SHORTCUTS } from './definitions'
const workflowHotkeyOptions = {
ignoreInputs: true,
@ -37,7 +37,7 @@ const isInputLikeElement = (element: Element | null) => {
}
const toHotkeyDefinitions = (
shortcut: WorkflowShortcutDefinition,
shortcut: WorkflowCanvasShortcutDefinition,
callback: HotkeyCallback,
options?: UseHotkeyOptions,
): UseHotkeyDefinition[] => {
@ -48,28 +48,14 @@ const toHotkeyDefinitions = (
...options,
meta: {
id: shortcut.id,
scope: 'workflow',
scope: 'workflow-canvas',
name: shortcut.name,
description: shortcut.description,
} satisfies WorkflowHotkeyMeta,
} satisfies WorkflowCanvasHotkeyMeta,
},
}))
}
export const useWorkflowShortcut = (
id: WorkflowShortcutId,
callback: HotkeyCallback,
options?: UseHotkeyOptions,
) => {
const shortcut = WORKFLOW_SHORTCUTS[id]
const hotkeys = useMemo(
() => toHotkeyDefinitions(shortcut, callback, options),
[callback, options, shortcut],
)
useHotkeys(hotkeys, workflowHotkeyOptions)
}
export const useWorkflowHotkeys = (): void => {
const {
handleNodesCopy,
@ -139,71 +125,71 @@ export const useWorkflowHotkeys = (): void => {
}, [handleToggleMaximizeCanvas])
const hotkeys = useMemo<UseHotkeyDefinition[]>(() => [
...toHotkeyDefinitions(WORKFLOW_SHORTCUTS['workflow.delete'], () => {
...toHotkeyDefinitions(WORKFLOW_CANVAS_SHORTCUTS['workflow.delete'], () => {
handleNodesDelete()
handleEdgeDelete()
}),
...toHotkeyDefinitions(WORKFLOW_SHORTCUTS['workflow.copy'], handleCopy, {
...toHotkeyDefinitions(WORKFLOW_CANVAS_SHORTCUTS['workflow.copy'], handleCopy, {
preventDefault: false,
stopPropagation: false,
enabled: !showDebugAndPreviewPanel,
}),
...toHotkeyDefinitions(WORKFLOW_SHORTCUTS['workflow.paste'], () => {
...toHotkeyDefinitions(WORKFLOW_CANVAS_SHORTCUTS['workflow.paste'], () => {
handleNodesPaste()
}, {
enabled: !showDebugAndPreviewPanel,
}),
...toHotkeyDefinitions(WORKFLOW_SHORTCUTS['workflow.duplicate'], () => {
...toHotkeyDefinitions(WORKFLOW_CANVAS_SHORTCUTS['workflow.duplicate'], () => {
handleNodesDuplicate()
}),
...toHotkeyDefinitions(WORKFLOW_SHORTCUTS['workflow.undo'], () => {
...toHotkeyDefinitions(WORKFLOW_CANVAS_SHORTCUTS['workflow.undo'], () => {
handleHistoryBack()
}, {
enabled: !showDebugAndPreviewPanel && historyShortcutsEnabled,
}),
...toHotkeyDefinitions(WORKFLOW_SHORTCUTS['workflow.redo'], () => {
...toHotkeyDefinitions(WORKFLOW_CANVAS_SHORTCUTS['workflow.redo'], () => {
handleHistoryForward()
}, {
enabled: !showDebugAndPreviewPanel && historyShortcutsEnabled,
}),
...toHotkeyDefinitions(WORKFLOW_SHORTCUTS['workflow.hand-mode'], () => {
...toHotkeyDefinitions(WORKFLOW_CANVAS_SHORTCUTS['workflow.hand-mode'], () => {
handleModeHand()
}),
...toHotkeyDefinitions(WORKFLOW_SHORTCUTS['workflow.pointer-mode'], () => {
...toHotkeyDefinitions(WORKFLOW_CANVAS_SHORTCUTS['workflow.pointer-mode'], () => {
handleModePointer()
}),
...toHotkeyDefinitions(WORKFLOW_SHORTCUTS['workflow.comment-mode'], () => {
...toHotkeyDefinitions(WORKFLOW_CANVAS_SHORTCUTS['workflow.comment-mode'], () => {
handleModeComment()
}, {
enabled: isCommentModeAvailable,
}),
...toHotkeyDefinitions(WORKFLOW_SHORTCUTS['workflow.organize'], () => {
...toHotkeyDefinitions(WORKFLOW_CANVAS_SHORTCUTS['workflow.organize'], () => {
handleLayout()
}),
...toHotkeyDefinitions(WORKFLOW_SHORTCUTS['workflow.toggle-maximize'], () => {
...toHotkeyDefinitions(WORKFLOW_CANVAS_SHORTCUTS['workflow.toggle-maximize'], () => {
handleToggleMaximizeCanvas()
}),
...toHotkeyDefinitions(WORKFLOW_SHORTCUTS['workflow.zoom-to-fit'], () => {
...toHotkeyDefinitions(WORKFLOW_CANVAS_SHORTCUTS['workflow.zoom-to-fit'], () => {
fitView()
handleSyncWorkflowDraft()
}),
...toHotkeyDefinitions(WORKFLOW_SHORTCUTS['workflow.zoom-to-100'], () => {
...toHotkeyDefinitions(WORKFLOW_CANVAS_SHORTCUTS['workflow.zoom-to-100'], () => {
zoomTo(1)
handleSyncWorkflowDraft()
}),
...toHotkeyDefinitions(WORKFLOW_SHORTCUTS['workflow.zoom-to-50'], () => {
...toHotkeyDefinitions(WORKFLOW_CANVAS_SHORTCUTS['workflow.zoom-to-50'], () => {
zoomTo(0.5)
handleSyncWorkflowDraft()
}),
...toHotkeyDefinitions(WORKFLOW_SHORTCUTS['workflow.zoom-out'], () => {
...toHotkeyDefinitions(WORKFLOW_CANVAS_SHORTCUTS['workflow.zoom-out'], () => {
constrainedZoomOut()
handleSyncWorkflowDraft()
}),
...toHotkeyDefinitions(WORKFLOW_SHORTCUTS['workflow.zoom-in'], () => {
...toHotkeyDefinitions(WORKFLOW_CANVAS_SHORTCUTS['workflow.zoom-in'], () => {
constrainedZoomIn()
handleSyncWorkflowDraft()
}),
...toHotkeyDefinitions(WORKFLOW_SHORTCUTS['workflow.download-import-log'], () => {
...toHotkeyDefinitions(WORKFLOW_CANVAS_SHORTCUTS['workflow.download-import-log'], () => {
collaborationManager.downloadGraphImportLog()
}),
], [