feat(desktop): double-Enter sends the queued turn, and the row says Enter

Empty Enter while busy was a hard no-op, so a queued turn could only be
sent early via the panel's send arrow or Cmd+Shift+K. With prompts
queued, a second Enter now promotes the head and interrupts, mirroring
the idle empty-Enter drain. Nothing queued keeps the old no-op.

The panel's send-now row action switches from a bare up-arrow to the
return glyph, so the row states the keybind the double-send uses.
This commit is contained in:
Brooklyn Nicholson 2026-07-27 22:42:21 -05:00
parent ef26701134
commit 84d77b608c
3 changed files with 18 additions and 7 deletions

View file

@ -724,12 +724,21 @@ export function ChatBar({
return
}
// Empty Enter while busy is a no-op — interrupting is explicit (Stop/Esc),
// never a stray Enter after sending. With a payload, submitDraft queues it.
// Gate on the live DOM payload (not the render-lagged composer state) so a
// message typed fast / via IME while busy still reaches submitDraft() and
// gets queued instead of being mistaken for an empty Enter.
// Empty Enter while busy. With prompts queued this is the double-send:
// the first Enter put the words in the queue, a second sends them now
// (promote + interrupt + drain on settle), mirroring the idle empty-Enter
// drain above. With nothing queued it stays a no-op — interrupting is
// explicit (Stop/Esc), never a stray Enter after sending. Gate on the live
// DOM payload (not the render-lagged composer state) so a message typed
// fast / via IME while busy still reaches submitDraft() and gets queued
// instead of being mistaken for an empty Enter.
if (busy && !hasLivePayload) {
const head = queuedPrompts.find(entry => entry.id !== queueEdit?.entryId)
if (head) {
sendQueuedNow(head.id)
}
return
}

View file

@ -4,7 +4,7 @@ import { Button } from '@/components/ui/button'
import { Codicon } from '@/components/ui/codicon'
import { Tip } from '@/components/ui/tooltip'
import { type Translations, useI18n } from '@/i18n'
import { ArrowUp, iconSize, Pencil, Trash2 } from '@/lib/icons'
import { CornerDownLeft, iconSize, Pencil, Trash2 } from '@/lib/icons'
import { cn } from '@/lib/utils'
import type { QueuedPromptEntry } from '@/store/composer-queue'
@ -103,7 +103,7 @@ export function QueuePanel({
type="button"
variant="ghost"
>
<ArrowUp className={iconSize.xs} />
<CornerDownLeft className={iconSize.xs} />
</Button>
</Tip>
<Tip label={c.queueDelete}>

View file

@ -33,6 +33,7 @@ import {
IconCommand as Command,
IconCopy as Copy,
IconCopy as CopyIcon,
IconCornerDownLeft as CornerDownLeft,
IconCpu as Cpu,
IconCreditCard as CreditCard,
IconDownload as Download,
@ -155,6 +156,7 @@ export {
Command,
Copy,
CopyIcon,
CornerDownLeft,
Cpu,
CreditCard,
Download,