diff --git a/packages/frontend/@n8n/i18n/src/locales/en.json b/packages/frontend/@n8n/i18n/src/locales/en.json index 4622f738f86..012c45c08e4 100644 --- a/packages/frontend/@n8n/i18n/src/locales/en.json +++ b/packages/frontend/@n8n/i18n/src/locales/en.json @@ -360,6 +360,9 @@ "aiAssistant.coachmark.title": "Ask mode enabled", "aiAssistant.coachmark.body": "Ask questions about n8n, get help with errors, or learn about automation. Switch to Build anytime to create workflows.", "aiAssistant.coachmark.gotIt": "Got it", + "nodeCreator.shortcutCoachmark.title": "Press N to browse nodes", + "nodeCreator.shortcutCoachmark.body": "We changed the shortcut to improve keyboard navigation", + "nodeCreator.shortcutCoachmark.gotIt": "Got it", "aiAssistant.askMode.emptyState.title": "Ask n8n AI", "aiAssistant.askMode.emptyState.body1": "Ask anything about n8n, your workflow, or how to accomplish a task. This won't use any of your AI credits.", "aiAssistant.askMode.emptyState.body2": "Look for the", diff --git a/packages/frontend/editor-ui/src/Interface.ts b/packages/frontend/editor-ui/src/Interface.ts index ebf2455f2a7..c500e98df3f 100644 --- a/packages/frontend/editor-ui/src/Interface.ts +++ b/packages/frontend/editor-ui/src/Interface.ts @@ -678,7 +678,7 @@ export type NodeCreatorOpenSource = | 'plus_endpoint' | 'add_input_endpoint' | 'trigger_placeholder_button' - | 'tab' + | 'node_shortcut' | 'replace_node_action' | 'node_connection_action' | 'node_connection_drop' diff --git a/packages/frontend/editor-ui/src/app/constants/nodeCreator.ts b/packages/frontend/editor-ui/src/app/constants/nodeCreator.ts index 7ae167321c7..29cae15bb14 100644 --- a/packages/frontend/editor-ui/src/app/constants/nodeCreator.ts +++ b/packages/frontend/editor-ui/src/app/constants/nodeCreator.ts @@ -12,7 +12,7 @@ export const NODE_CREATOR_OPEN_SOURCES: Record< ADD_INPUT_ENDPOINT: 'add_input_endpoint', TRIGGER_PLACEHOLDER_BUTTON: 'trigger_placeholder_button', ADD_NODE_BUTTON: 'add_node_button', - TAB: 'tab', + NODE_SHORTCUT: 'node_shortcut', NODE_CONNECTION_ACTION: 'node_connection_action', REPLACE_NODE_ACTION: 'replace_node_action', NODE_CONNECTION_DROP: 'node_connection_drop', diff --git a/packages/frontend/editor-ui/src/app/views/NodeView.vue b/packages/frontend/editor-ui/src/app/views/NodeView.vue index 67cc4e0b0e8..c0f61c67027 100644 --- a/packages/frontend/editor-ui/src/app/views/NodeView.vue +++ b/packages/frontend/editor-ui/src/app/views/NodeView.vue @@ -1439,7 +1439,7 @@ function registerCustomActions() { ndvStore.unsetActiveNodeName(); void nextTick(() => { - void onOpenNodeCreatorForTriggerNodes(NODE_CREATOR_OPEN_SOURCES.TAB); + void onOpenNodeCreatorForTriggerNodes(NODE_CREATOR_OPEN_SOURCES.NODE_SHORTCUT); }); }, }); diff --git a/packages/frontend/editor-ui/src/features/shared/commandBar/composables/useNodeCommands.ts b/packages/frontend/editor-ui/src/features/shared/commandBar/composables/useNodeCommands.ts index 7bb7aaeb53d..24609862627 100644 --- a/packages/frontend/editor-ui/src/features/shared/commandBar/composables/useNodeCommands.ts +++ b/packages/frontend/editor-ui/src/features/shared/commandBar/composables/useNodeCommands.ts @@ -153,7 +153,7 @@ export function useNodeCommands(options: { props: { title: i18n.baseText('commandBar.nodes.addNode'), shortcut: { - keys: ['tab'], + keys: ['n'], }, }, }, diff --git a/packages/frontend/editor-ui/src/features/shared/nodeCreator/components/NodeCreator.vue b/packages/frontend/editor-ui/src/features/shared/nodeCreator/components/NodeCreator.vue index b2d7c150292..5f3ccd1d652 100644 --- a/packages/frontend/editor-ui/src/features/shared/nodeCreator/components/NodeCreator.vue +++ b/packages/frontend/editor-ui/src/features/shared/nodeCreator/components/NodeCreator.vue @@ -135,10 +135,6 @@ registerKeyHook('NodeCreatorCloseEscape', { keyboardKeys: ['Escape'], handler: () => emit('closeNodeCreator'), }); -registerKeyHook('NodeCreatorCloseTab', { - keyboardKeys: ['Tab'], - handler: () => emit('closeNodeCreator'), -}); watch( () => ({ diff --git a/packages/frontend/editor-ui/src/features/shared/nodeCreator/components/NodeCreatorShortcutCoachmark.vue b/packages/frontend/editor-ui/src/features/shared/nodeCreator/components/NodeCreatorShortcutCoachmark.vue new file mode 100644 index 00000000000..db22b93690f --- /dev/null +++ b/packages/frontend/editor-ui/src/features/shared/nodeCreator/components/NodeCreatorShortcutCoachmark.vue @@ -0,0 +1,95 @@ + + + + + diff --git a/packages/frontend/editor-ui/src/features/shared/nodeCreator/composables/useKeyboardNavigation.ts b/packages/frontend/editor-ui/src/features/shared/nodeCreator/composables/useKeyboardNavigation.ts index 5269f9acf2c..2e5e063b123 100644 --- a/packages/frontend/editor-ui/src/features/shared/nodeCreator/composables/useKeyboardNavigation.ts +++ b/packages/frontend/editor-ui/src/features/shared/nodeCreator/composables/useKeyboardNavigation.ts @@ -9,15 +9,7 @@ interface KeyHook { } export const KEYBOARD_ID_ATTR = 'data-keyboard-nav-id'; -export const WATCHED_KEYS = [ - 'ArrowUp', - 'ArrowDown', - 'ArrowLeft', - 'ArrowRight', - 'Enter', - 'Escape', - 'Tab', -]; +export const WATCHED_KEYS = ['ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight', 'Enter', 'Escape']; export const useKeyboardNavigation = defineStore('nodeCreatorKeyboardNavigation', () => { const selectableItems = ref>>([]); diff --git a/packages/frontend/editor-ui/src/features/shared/nodeCreator/composables/useNodeCreatorShortcutCoachmark.ts b/packages/frontend/editor-ui/src/features/shared/nodeCreator/composables/useNodeCreatorShortcutCoachmark.ts new file mode 100644 index 00000000000..976322d5ff3 --- /dev/null +++ b/packages/frontend/editor-ui/src/features/shared/nodeCreator/composables/useNodeCreatorShortcutCoachmark.ts @@ -0,0 +1,35 @@ +import { computed, onScopeDispose, ref } from 'vue'; +import { useCalloutHelpers } from '@/app/composables/useCalloutHelpers'; +import { canvasEventBus } from '@/features/workflows/canvas/canvas.eventBus'; + +export const NODE_CREATOR_SHORTCUT_COACHMARK_KEY = 'node-creator-shortcut-coachmark'; + +export function useNodeCreatorShortcutCoachmark() { + const { isCalloutDismissed, dismissCallout } = useCalloutHelpers(); + + const isTabPressed = ref(false); + + const shouldShowCoachmark = computed(() => { + return isTabPressed.value && !isCalloutDismissed(NODE_CREATOR_SHORTCUT_COACHMARK_KEY); + }); + + function onDeprecatedTabShortcut() { + isTabPressed.value = true; + } + + canvasEventBus.on('deprecated:tab-shortcut', onDeprecatedTabShortcut); + + onScopeDispose(() => { + canvasEventBus.off('deprecated:tab-shortcut', onDeprecatedTabShortcut); + }); + + async function onDismissCoachmark() { + isTabPressed.value = false; + await dismissCallout(NODE_CREATOR_SHORTCUT_COACHMARK_KEY); + } + + return { + shouldShowCoachmark, + onDismissCoachmark, + }; +} diff --git a/packages/frontend/editor-ui/src/features/shared/nodeCreator/views/NodeCreation.vue b/packages/frontend/editor-ui/src/features/shared/nodeCreator/views/NodeCreation.vue index 16e96ef171e..2019a260586 100644 --- a/packages/frontend/editor-ui/src/features/shared/nodeCreator/views/NodeCreation.vue +++ b/packages/frontend/editor-ui/src/features/shared/nodeCreator/views/NodeCreation.vue @@ -17,6 +17,8 @@ import type { } from '@/Interface'; import { useActions } from '../composables/useActions'; import KeyboardShortcutTooltip from '@/app/components/KeyboardShortcutTooltip.vue'; +import NodeCreatorShortcutCoachmark from '../components/NodeCreatorShortcutCoachmark.vue'; +import { useNodeCreatorShortcutCoachmark } from '../composables/useNodeCreatorShortcutCoachmark'; import { useI18n } from '@n8n/i18n'; import { useTelemetry } from '@/app/composables/useTelemetry'; import { useAssistantStore } from '@/features/ai/assistant/assistant.store'; @@ -57,6 +59,7 @@ const builderStore = useBuilderStore(); const chatPanelStore = useChatPanelStore(); const { getAddedNodesAndConnections } = useActions(); +const { shouldShowCoachmark, onDismissCoachmark } = useNodeCreatorShortcutCoachmark(); const sidePanelTooltip = computed(() => { if (setupPanelStore.isFeatureEnabled) { @@ -144,19 +147,21 @@ function openCommandBar(event: MouseEvent) {