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.
This commit is contained in:
Parvez Salim 2026-04-28 22:18:33 -05:00
parent 4e2240aada
commit a6d210f644
5 changed files with 71 additions and 1 deletions

View file

@ -97,6 +97,11 @@
<!-- {$i18n.t('Only active when the chat input is in focus.')} -->
<!-- {$i18n.t('Only active when the chat input is in focus and an LLM is generating a response.')} -->
<!-- {$i18n.t('Only can be triggered when the chat input is in focus.')} -->
<!-- {$i18n.t('Edit Last User Message')} -->
<!-- {$i18n.t('Playground')} -->
<!-- {$i18n.t('Run')} -->
<!-- {$i18n.t('Add Message')} -->
<!-- {$i18n.t('Toggle Role')} -->
{#each items as shortcut}
<div class="col-span-1 flex items-start">
<ShortcutItem {shortcut} {isMac} />

View file

@ -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 @@
});
</script>
<svelte:window on:keydown={handleKeydown} />
<div class=" flex flex-col justify-between w-full overflow-y-auto h-full">
<div class="mx-auto w-full md:px-0 h-full relative">
<div class=" flex flex-col h-full px-3.5">

View file

@ -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 @@
});
</script>
<svelte:window on:keydown={handleKeydown} />
<div class=" flex flex-col justify-between w-full overflow-y-auto h-full">
<div class="mx-auto w-full md:px-0 h-full">
<div class=" flex flex-col h-full px-4">

View file

@ -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'
}
};

View file

@ -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();
}
});
};