Skip to content

feat(web): Enter on an empty composer steers the next queued message - #6

Open
jagrat7 wants to merge 3 commits into
mainfrom
feat/enter-to-steer-queued-message
Open

jagrat7 wants to merge 3 commits into
mainfrom
feat/enter-to-steer-queued-message

Conversation

@jagrat7

@jagrat7 jagrat7 commented Oct 8, 2026 •

Copy link
Copy Markdown
Owner

Queued messages could only be steered into the running turn with the Steer button or Cmd/Ctrl+Shift+Enter. Pressing Enter in an empty composer did nothing, so the Zed-style "Enter queues, Enter again sends" flow from #1 no longer worked once the queue moved to the server.

Fix

  • ChatView onSend: when the composer has nothing to send, Enter now calls the queue control's existing steerNext, the same path as thread.steerQueuedMessage. It promotes the oldest queued run into a steer of the active run.
  • Enter still does nothing when the queue is empty, there's no active turn, or the provider can't steer. It also does nothing when the composer holds only expired terminal context (the existing warning still shows), for any send key other than plain Enter (Cmd/Ctrl+Enter keeps doing the opposite of the default action), or while editing a queued message (Enter saves the edit). Key repeat is ignored, so holding Enter steers once.
  • The front row's Steer tooltip mentions the Enter gesture alongside the shortcut, but only where plain Enter sends: not at mobile widths, and not when Send shortcut is "mod+Enter always".
  • docs/user/composer.md documents it.

Web and desktop only. Narrow web screens and the mobile app don't send on Enter. Agents already steer through t3_queue_promote_to_steer.

No screenshots: the only visual change is tooltip text.

Test plan

  • vp lint on touched files: no new warnings
  • vp fmt --check clean
  • tsc --noEmit (apps/web) clean
  • vp test run on QueuedRunsControl.test.tsx and keybindings.test.ts: 145 passed
  • Queue a message mid-turn, press Enter on the empty composer, and check it steers the active turn; repeat for the next queued message (web dev build, Codex)
  • Enter with a draft in the composer still sends or queues the draft
  • Ctrl+Enter on an empty composer leaves the queue alone
  • Front row Steer tooltip reads "Send as a steer instead (Enter in an empty composer or Ctrl+Shift+Enter)"
  • Enter while editing a queued message saves the edit and doesn't steer (web dev build, Codex; edited message remains queued after reload)

Built with Claude Opus 5.5 (Claude Code harness in T3 Code).


Devin Review

RetriggerConfidence Score: 5/5

The PR appears safe to merge; no new actionable issue remains.

What we checked:

  • Expired context still blocks steering: ChatView shows the warning first. It calls steerNext only in the other branch.

Summary

This PR lets Enter in an empty desktop composer steer the oldest queued message.

  • The tooltip shows Enter only where plain Enter sends.
  • The docs explain the settings needed for the two-Enter flow.
  • The latest change preserves the warning for expired terminal context.

Reviews (3) · Last reviewed commit: "refactor(web): simplify empty-composer q..." · Reviewed by Greptile

@github-actions github-actions Bot added vouch:trusted PR author is trusted by repo permissions or the VOUCHED list. size:S labels Oct 8, 2026

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Devin Review found 3 potential issues.

Devin Review

Comment thread apps/web/src/components/ChatView.tsx Outdated
expiredTerminalContextCount === 0 &&
submissionIntent !== "background" &&
!directAnnotation &&
queuedRunsControlRef.current?.steerNext(false)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 Opposite-action shortcut steers queued messages

With an empty composer, Cmd/Ctrl+Enter steers a queued message even when dispatchMode selects Queue. The shortcut's opposite-action mode is ignored, so the message enters the running turn unexpectedly.

Learn more

The composer passes the resolved Queue/Steer action as dispatchMode to onSend. With Follow-up behavior set to Steer, Cmd/Ctrl+Enter selects Queue. The new empty-input branch ignores that choice and promotes a saved message instead. The shortcut therefore changes an existing message's delivery even though the user chose Queue and entered no new message.

Example: Set Follow-up behavior to Steer, queue a message with Cmd/Ctrl+Enter while an agent runs, then press Cmd/Ctrl+Enter again with an empty composer. The queued message is steered into the running turn; the selected Queue action cannot perform that promotion.

Recommended fix: Gate empty-composer promotion on the ordinary unmodified Enter submission, not merely on submissionIntent !== "background". Pass the keyboard action or an explicit promotion intent through ChatComposer to onSend, and keep alternate actions from promoting queued runs.

Devin Review


Was this helpful? React with 👍 or 👎 to provide feedback.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 0641931. The empty-composer steer now requires submissionIntent === "foreground" (plain Enter), so Cmd/Ctrl+Enter, which does the opposite of the default action, never promotes a queued run.

Comment thread apps/web/src/components/ChatView.tsx Outdated
expiredTerminalContextCount === 0 &&
submissionIntent !== "background" &&
!directAnnotation &&
queuedRunsControlRef.current?.steerNext(false)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Second Enter misses pending queued message

If Enter is pressed again before a queued message reaches the server projection, steerNext sees no saved run. The second Enter does nothing, so the two-Enter flow fails under normal network latency.

Learn more

A queued message first appears as an optimistic row. QueuedRunsControl marks that row pending without a run ID, while steerNext reads only acknowledged runs from the server projection. An Enter received during the gap cannot promote the newly queued message. The delay is particularly visible on remote connections.

Example: Queue the first follow-up and press Enter again before the server response arrives. The queue displays a pending row, but queued[0] is absent and no steer occurs; the user must press Enter a third time after acknowledgment.

Recommended fix: Record promotion intent for a pending queued message and apply it once its server run ID becomes available, or defer the second action until the initial queue command resolves. Ensure retries and failures clear that intent without promoting a different queued message.

Devin Review


Was this helpful? React with 👍 or 👎 to provide feedback.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Leaving this as is. While the queue send is in flight, sendInFlightRef already drops any second send (ChatView.tsx onSend guard). The only gap left is the short time between the RPC resolving and the projection update arriving. A second Enter in that gap does nothing, and pressing Enter again steers. Deferring a steer until the run appears would add state that has to be cleared on failure and on retry, just to avoid a harmless no-op.

Comment thread apps/web/src/components/ChatView.tsx Outdated
Comment on lines +9104 to +9110
// Sending an empty composer steers the oldest queued message, so Enter
// queues a follow-up and Enter again sends it into the running turn.
if (
expiredTerminalContextCount === 0 &&
submissionIntent !== "background" &&
!directAnnotation &&
queuedRunsControlRef.current?.steerNext(false)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔍 Enter interaction lacks verification evidence

The PR leaves the new Enter flow unchecked. Contribution guidance calls for UI evidence and interaction recordings; a focused check would establish the desktop and web behavior.

Devin Review


Was this helpful? React with 👍 or 👎 to provide feedback.

Comment thread apps/web/src/components/chat/QueuedRunsControl.tsx Outdated
@github-actions github-actions Bot added size:M and removed size:S labels Oct 8, 2026
@jagrat7

jagrat7 commented Oct 8, 2026

Copy link
Copy Markdown
Owner Author

Manual check in the web dev build (Codex, Follow-up behavior = Queue, during a running sleep 90 turn):

  • Enter with a draft queued it, and the queue panel showed one row.
  • Enter on the empty composer promoted that row into the running turn. The row left the queue and the message appeared in the timeline labeled "Steer".
  • With a second message queued, Ctrl+Enter on the empty composer left it queued, and plain Enter then steered it.
  • The front row's Steer tooltip read "Send as a steer instead (Enter in an empty composer or Ctrl+Shift+Enter)".

Not checked: Enter while editing a queued message.

@github-actions github-actions Bot added size:S and removed size:M labels Oct 8, 2026

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:S vouch:trusted PR author is trusted by repo permissions or the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant