feat(composer): ⌘↩ sends with the other busy action; Settings default (P2) - #9599
Conversation
Design Review (Fable 5) — ✅ PASSDesign-level review of Design-Verdict: PASS A named user gesture (#4608 §3), scoped to the one send mode where the chord is free, with per-slot choices, migration key, and uiPrefs backup all reconciled. The one shape decision worth noting held up under inspection: reusing the legacy unscoped Suggestions
[DESIGN-REVIEWED] e2775e0 |
First Principles Review (Fable 5) — ✅ PASSPremise-level review of All counts verified: First-Principles-Verdict: PASS Verify the intent of The reuse is declared and the test pins that scoped slots keep their choice, so the old leak the pin feared cannot recur — but the standing comment and the new writer now assert opposite rules to the next reader; confirm the reconciliation in What this change shipsInventory (10 items) — 10 justifiedIntent: press ⌘↩/Ctrl+Enter during a running turn to send with the other busy action for that one message, and pick the global busy default in Settings — an ADDITION (issue #4608 plan §3, user report quoted).
[FIRST-PRINCIPLES-REVIEWED] e2775e0 |
UX Review (Fable 5) — 🟡 CONCERNSUX-level review of All evidence reconciled: the two new surfaces (Settings picker, busy-menu chord hint) appear in screenshots in both themes, and the blind reader identified both primary controls confidently. The residual findings are copy-level. Final review: UX-Verdict: CONCERNS Both new controls read cold, but the reader still can't tell whether the Settings default and the send-button choice affect each other. Watch
Evidence gaps
[UX-REVIEWED] e2775e0 |
Opus 4.8 Review — ✅ no blocking findingsReviewed Verdict parsed from the review's SHA-scoped output markers for commit False positive or not applicable? A repository writer can comment: |
8cc845c to
437c897
Compare
GPT 5.6 Review — ✅ no blocking findings (all downgraded on adjudication)GPT 5.6 flagged blocking issues on This comment is updated in place on each push. Review detailsBLOCKING -- website/src/pages/settings/ChatPanel.tsx:305 -- Storage failure is displayed as a successful save (origin: validation) Adjudication (Opus 4.8) — is blocking on each finding proportionate?LOW harm — the failing write is a best-effort localStorage UI preference (Enter-while-busy mode), not data/credentials/config. Conditions confirmed: [ADJUDICATION] e2775e0 total=1 uphold=0 downgrade=1 False positive or not applicable? A repository writer can comment: |
…he default (P2) Refs #4608. Joseph Dombroski (Slack): "command+return changing the queue v steer approach as with Claude/Codex". While the busy split button is showing, ⌘↩ / Ctrl+Enter now performs the OTHER action for that one send — queue when the split says steer, steer when it says queue. Plain Enter is unchanged; idle ⌘↩ is still a plain send. Only in the `enter` send mode: in `ctrl-enter` the modified Enter is the send key, and in `enter-ctrl-newline` it is newline. Settings → Chat gains "What Enter does while the agent is working" (Steer / Queue) — the global default every session without a per-slot split-button choice inherits, persisted under the existing unscoped key the per-slot fallback already reads. The split menu names the flip chord. fireComposer's flag is strictly `=== true`: the callback is also an onClick, and a MouseEvent must read as default, never as flip.
437c897 to
e2775e0
Compare
Refs #4608 — P2 of the shortcut plan (#4608 (comment), §3). Independent of P1 (#9555); the registry row for this chord follows once P1 lands.
Joseph Dombroski on Slack: "command+return changing the queue v steer approach as with Claude/Codex/etc."
What changes
⌘↩ / Ctrl+Enter = the other busy action, for one send. While a turn is running and the split send button is showing, Enter keeps doing what the split says (steer by default, or queue); the modified Enter does the other one for that message only — the Claude Code / Codex gesture. Idle, the modified Enter is still a plain send. Scope: the
entersend mode only. Inctrl-enterthe modified Enter is the send key, and inenter-ctrl-newlinethe user gave it to newline, so those modes keep the split menu as the flip and the Settings copy says so.ChatInput.tsx:fireComposer(alternate?)— strictly=== true, because the same callback is anonClickand a MouseEvent must read as default. The flip is ignored whenever the busy split is not available (idle, stopping, no steer path), so it cannot steer a non-steerable slot.BusySendButton.tsx: the split menu names the concrete flip for the current mode ("⌘↵ queues this message instead" / "…steers with this message instead"), and only when the host says the chord is live (altChordAvailable, i.e. theentersend mode);readBusySendDefault/setBusySendDefaultexpose the global default.Settings → Chat › Composer: "What Enter does while the agent is working" (Steer / Queue). The default every session inherits when its split button was never touched; sessions with a per-slot choice keep it. Persisted under the existing unscoped key the per-slot fallback already reads (that fallback is exactly "the default"), and mounted composers without a scoped choice move live.
Crew-member DMs: #8852 (steer-only composer) is not merged, so nothing to preserve here; when it lands, a steer-only surface should pass no queue path and the flip becomes a no-op by construction.
Screenshots (fixture harness,
scripts/capture-send-key-alt-action.mjs)Tests (written, not run locally — CI runs them)
ChatInput.test.tsx: Ctrl+Enter queues in steer mode and is one-shot; steers in queue mode; plain send when idle; queues when the slot has no steer path; inctrl-entermode it is the send key not a flip; the split button click never flips; the menu names the chord.BusySendButton.slotScope.test.tsx:setBusySendDefaultwrites the unscoped key, moves consumers without a scoped choice live, leaves scoped ones alone, and later slots inherit it.Local gates:
tsc --noEmit -p tsconfig.app.jsonclean on touched files, eslint,I18N_BASE_REF=origin/main node scripts/i18n-check.mjs19/19 PASS,settingsRegistry.gen.tsregenerated (190 entries).i18n
4 new keys (
components.chatInput.alt_action_hint,pages.settings.chatPanel.what_enter_does_while_the_agent_is_working,…busy_alt_action_desc,…busy_alt_action_desc_no_chord) across the 12 hand-maintained catalogs + en-XA regenerated.Review responses (head
437c897a5)busyAltChordLabelduplicatesplatformShortcut('Cmd+Enter'): applied; helper deleted, both call sites useplatformShortcut, so the chord renders the same way as the existing Cmd+Shift+Enter label in the same composer (⌘↵).altChordAvailable(theentermode), and names the concrete action for the current mode ("queues this message instead" / "steers with this message instead"). Test added for both.entermode rests on one Slack line: the plan comment on Make built-in keyboard shortcuts rebindable — fixed Option/Ctrl chords collide with macOS cursor movement #4608 (§3) specifies exactly this: "Running turn: Enter performs the default busy action, ⌘↩ / Ctrl+Enter performs the other one for that single send — the Claude Code / Codex gesture." Idle ⌘↩ is unchanged, so the only users affected are those who pressed ⌘↩ while a turn was running meaning "same as Enter" — the split button still does that.Round 2 (head
e2775e069){{chord}}(으)로,{{chord}}을(를)), hi must address the user as तुम not आप, zh-CN uses curly quotes not corner brackets. All three fixed in the catalogs.