cmux NEXT composer: the caret stays put when the pane gets the keyboard back
Summary
Leo: while typing in the agent pane's composer, the caret jumped to the start of the prompt.
Cause: when the agent pane's web view gives the keyboard to the app (another pane, tab or workspace, the palette) and gets it back, WebKit clears the selection and puts a contenteditable's caret at its start. The prompt editor stays the page's active element, so ProseMirror takes that start caret as the person's, and the next keys land at the front. A textarea gets its old selection back, which is why the classic composer never did this.
Fix (MarkdownField.tsx, keepCaret plugin): the field saves its selection when it or the window loses focus, and when focus comes back without a click it puts that selection back, also over a placement the browser makes in the 100 ms after. A click, any key, a document change or a programmatic caret/value write (setCaret, type, writeText, selectAll) drops the saved selection.
Tests, each typing into the real pane (stand-in acpmux daemon) and checking text, caret and focus after each step, in Chromium and WebKit:
agent-pane-inbound-keeps-caret.test.ts:- Another agent's queued message, its steer, the turn's end, a re-adopt and a reconnect.
- Keys typed while the answer streams, then after each of these: a window refocus, the page hiding and showing, a model change, and the keyboard handed back (to the window only, and to the window and the prompt).
composer-inbound-keeps-caret.test.tsx(jsdom): the Composer re-rendered through inbound records.
Testing
bun test test/agent-pane-inbound-keeps-caret.test.ts (from webviews/), in CI's react-apps-check shards. Local test runs are blocked on this Mac.
Observed / Not run
- Observed, red: on e5ce4362 (tests only), both engines failed the step "keyboard handed back to the window". Expected
fix the abcderetry testwith the caret at 13; gotefix the abcdretry testwith the caret at 1. That is the reported symptom. - Observed, green: on the same head, streaming, window refocus, the page hiding and showing, a model change, inbound queue and steer messages, the turn's end, a re-adopt and a reconnect all kept the caret.
- Other reds on that head (composer placeholder, @ mentions, suggestions, queue layout, working counts) are base reds: the head added only these two test files to base.
- Observed, green: on c25c2ed9 (fix + base merge) and 92c06367 both "keyboard handed back" steps and every earlier step pass in Chromium and WebKit. Remaining reds match base (the + tooltip from #23149,
check-switch-fetcheson base 1369f27 from keptAttachments.ts, stale action surfaces, and transcript timing tests in files this PR does not touch). - Observed, real app: in a fleet build of ad3e9b65 on a capture mini, "fix the retry test" was typed and the caret put after "fix the ". The keyboard went to the sidebar and back, without a click on the prompt, while a prompt sent into the chat streamed its reply. "XYZ" was then typed, and the prompt read "fix the XYZretry test". The clip is cropped to the chat (the Inspector showed the mini's name). The inbound message was the showcase chat's mock reply, not a real steer from another agent.

Full quality:
Changelog
Fixed: The composer's caret no longer jumps to the start of the prompt after you switch to another pane or tab and come back.
Agent session: cosmic-gray
🤖 Generated with Claude Code