From a6d210f644eb4c863c235a0ef8e76b20d98c8e70 Mon Sep 17 00:00:00 2001 From: Parvez Salim Date: Tue, 28 Apr 2026 22:18:33 -0500 Subject: [PATCH] feat: add keyboard shortcuts for playground and edit last user message Closes #1008 - Ctrl/Cmd+Enter: run/submit in Playground Chat and Completions - Ctrl/Cmd+Shift+A: add message in Playground Chat (mirrors Add button, auto-toggles role) - Ctrl/Cmd+Shift+X: toggle user/assistant role in Playground Chat - Ctrl/Cmd+E: edit last user message from anywhere in the main chat Playground shortcuts handled locally via svelte:window in each component. Global layout handler covers Ctrl/Cmd+E using getElementsByClassName pattern. --- src/lib/components/chat/ShortcutsModal.svelte | 5 ++++ src/lib/components/playground/Chat.svelte | 20 +++++++++++++ .../components/playground/Completions.svelte | 13 ++++++++ src/lib/shortcuts.ts | 30 ++++++++++++++++++- src/routes/(app)/+layout.svelte | 4 +++ 5 files changed, 71 insertions(+), 1 deletion(-) diff --git a/src/lib/components/chat/ShortcutsModal.svelte b/src/lib/components/chat/ShortcutsModal.svelte index 965229c22c..6835ba1c06 100644 --- a/src/lib/components/chat/ShortcutsModal.svelte +++ b/src/lib/components/chat/ShortcutsModal.svelte @@ -97,6 +97,11 @@ + + + + + {#each items as shortcut}
diff --git a/src/lib/components/playground/Chat.svelte b/src/lib/components/playground/Chat.svelte index 1ac0678892..52325c0540 100644 --- a/src/lib/components/playground/Chat.svelte +++ b/src/lib/components/playground/Chat.svelte @@ -288,6 +288,24 @@ toast.success($i18n.t('Chat exported successfully')); }; + const handleKeydown = (event: KeyboardEvent) => { + const mod = event.ctrlKey || event.metaKey; + + if (mod && !event.shiftKey && event.key === 'Enter') { + if (!loading) { + event.preventDefault(); + submitHandler(); + } + } else if (mod && event.shiftKey && event.key.toLowerCase() === 'a') { + event.preventDefault(); + addHandler(); + role = role === 'user' ? 'assistant' : 'user'; + } else if (mod && event.shiftKey && event.key.toLowerCase() === 'x') { + event.preventDefault(); + role = role === 'user' ? 'assistant' : 'user'; + } + }; + onMount(async () => { if ($user?.role !== 'admin') { await goto('/'); @@ -304,6 +322,8 @@ }); + +
diff --git a/src/lib/components/playground/Completions.svelte b/src/lib/components/playground/Completions.svelte index e69aae35da..c5e589527e 100644 --- a/src/lib/components/playground/Completions.svelte +++ b/src/lib/components/playground/Completions.svelte @@ -103,6 +103,17 @@ } }; + const handleKeydown = (event: KeyboardEvent) => { + const mod = event.ctrlKey || event.metaKey; + + if (mod && !event.shiftKey && event.key === 'Enter') { + if (!loading) { + event.preventDefault(); + submitHandler(); + } + } + }; + onMount(async () => { if ($user?.role !== 'admin') { await goto('/'); @@ -119,6 +130,8 @@ }); + +
diff --git a/src/lib/shortcuts.ts b/src/lib/shortcuts.ts index 9879b8110d..677d1bf1ac 100644 --- a/src/lib/shortcuts.ts +++ b/src/lib/shortcuts.ts @@ -40,7 +40,13 @@ export enum Shortcut { REGENERATE_RESPONSE = 'regenerateResponse', COPY_LAST_CODE_BLOCK = 'copyLastCodeBlock', COPY_LAST_RESPONSE = 'copyLastResponse', - STOP_GENERATING = 'stopGenerating' + STOP_GENERATING = 'stopGenerating', + EDIT_LAST_MESSAGE = 'editLastMessage', + + //Playground + PLAYGROUND_SUBMIT = 'playgroundSubmit', + PLAYGROUND_ADD_MESSAGE = 'playgroundAddMessage', + PLAYGROUND_TOGGLE_ROLE = 'playgroundToggleRole' } export const shortcuts: ShortcutRegistry = { @@ -164,5 +170,27 @@ export const shortcuts: ShortcutRegistry = { name: 'Copy Last Code Block', keys: ['mod', 'shift', ';'], category: 'Message' + }, + [Shortcut.EDIT_LAST_MESSAGE]: { + name: 'Edit Last User Message', + keys: ['mod', 'E'], + category: 'Message' + }, + + //Playground + [Shortcut.PLAYGROUND_SUBMIT]: { + name: 'Run', + keys: ['mod', 'Enter'], + category: 'Playground' + }, + [Shortcut.PLAYGROUND_ADD_MESSAGE]: { + name: 'Add Message', + keys: ['mod', 'shift', 'A'], + category: 'Playground' + }, + [Shortcut.PLAYGROUND_TOGGLE_ROLE]: { + name: 'Toggle Role', + keys: ['mod', 'shift', 'X'], + category: 'Playground' } }; diff --git a/src/routes/(app)/+layout.svelte b/src/routes/(app)/+layout.svelte index 772ae6fe2d..c8a81ecbf9 100644 --- a/src/routes/(app)/+layout.svelte +++ b/src/routes/(app)/+layout.svelte @@ -311,6 +311,10 @@ console.log('Shortcut triggered: REGENERATE_RESPONSE'); event.preventDefault(); [...document.getElementsByClassName('regenerate-response-button')]?.at(-1)?.click(); + } else if (isShortcutMatch(event, shortcuts[Shortcut.EDIT_LAST_MESSAGE])) { + console.log('Shortcut triggered: EDIT_LAST_MESSAGE'); + event.preventDefault(); + [...document.getElementsByClassName('edit-user-message-button')]?.at(-1)?.click(); } }); };