From 406d7a67f03f15f9c7c23edd261cfa13a53b48fe Mon Sep 17 00:00:00 2001 From: ethernet Date: Thu, 23 Jul 2026 00:13:58 -0400 Subject: [PATCH] fix(desktop): split steer and queue keyboard shortcuts (#69797) Keep Enter as the Cursor-style live-turn steering gesture and assign Ctrl/Cmd+Enter to queue the current draft. Align keybind settings, tooltips, translations, and the Electron queue-boundary coverage. --- apps/desktop/e2e/queue-turn-boundary.spec.ts | 19 ++++++++++--------- .../src/app/chat/composer/controls.test.tsx | 8 ++++---- .../src/app/chat/composer/controls.tsx | 4 ++-- apps/desktop/src/app/chat/composer/index.tsx | 18 +++++++++++++----- apps/desktop/src/i18n/en.ts | 1 + apps/desktop/src/i18n/zh.ts | 1 + apps/desktop/src/lib/keybinds/actions.ts | 3 ++- 7 files changed, 33 insertions(+), 21 deletions(-) diff --git a/apps/desktop/e2e/queue-turn-boundary.spec.ts b/apps/desktop/e2e/queue-turn-boundary.spec.ts index 27cffd7a625a..2308d321a553 100644 --- a/apps/desktop/e2e/queue-turn-boundary.spec.ts +++ b/apps/desktop/e2e/queue-turn-boundary.spec.ts @@ -26,12 +26,18 @@ async function send(page: Page, text: string): Promise { async function steer(page: Page, text: string): Promise { const composer = page.locator('[contenteditable="true"]').first() - const primary = page.locator('[data-slot="composer-root"] button[type="submit"]') await composer.click() await composer.type(text, { delay: 5 }) - await expect(primary).toHaveAttribute('aria-label', /Steer/) - await primary.click() + await page.keyboard.press('Enter') +} + +async function queue(page: Page, text: string): Promise { + const composer = page.locator('[contenteditable="true"]').first() + + await composer.click() + await composer.type(text, { delay: 5 }) + await page.keyboard.press('Control+Enter') } async function transcriptMessageOrder(page: Page): Promise { @@ -72,15 +78,10 @@ test.describe('queued prompt turn boundary', () => { test('submits a queued prompt only after the active turn completes', async () => { const { mock, page } = fixture! - const composer = page.locator('[contenteditable="true"]').first() - const queue = page.locator('[data-slot="composer-root"] button[aria-label="Queue message"]') await send(page, ACTIVE_PROMPT) await mock.waitForHeldStream() - - await composer.click() - await composer.type(QUEUED_PROMPT, { delay: 5 }) - await queue.click() + await queue(page, QUEUED_PROMPT) await expect(page.getByText('1 Queued')).toBeVisible() // The mock keeps the active SSE stream open, so a queued prompt has no diff --git a/apps/desktop/src/app/chat/composer/controls.test.tsx b/apps/desktop/src/app/chat/composer/controls.test.tsx index a2186622690f..90d38d274954 100644 --- a/apps/desktop/src/app/chat/composer/controls.test.tsx +++ b/apps/desktop/src/app/chat/composer/controls.test.tsx @@ -65,15 +65,15 @@ describe('ComposerControls shortcut tooltips', () => { await expectShortcutTooltip('Send', '↵') }) - it('shows Ctrl+Enter for Steer', async () => { + it('shows Enter for Steer', async () => { renderControls({ busy: true, busyAction: 'steer' }) - await expectShortcutTooltip('Steer the current run', 'Ctrl+↵') + await expectShortcutTooltip('Steer the current run', '↵') }) - it('shows Enter for Queue', async () => { + it('shows Ctrl+Enter for Queue', async () => { renderControls({ busy: true, busyAction: 'queue' }) - await expectShortcutTooltip('Queue message', '↵') + await expectShortcutTooltip('Queue message', 'Ctrl+↵') }) }) diff --git a/apps/desktop/src/app/chat/composer/controls.tsx b/apps/desktop/src/app/chat/composer/controls.tsx index 47f253001da5..e56525476a47 100644 --- a/apps/desktop/src/app/chat/composer/controls.tsx +++ b/apps/desktop/src/app/chat/composer/controls.tsx @@ -81,7 +81,7 @@ export function ComposerControls({ {busyAction === 'steer' ? ( - }> + }>