Repository navigation
cmux NEXT composer: a long paste scrolls to its end, caret after it - #22789
Conversation
…et after it (red) The composer field caps at eight lines and scrolls inside. A long paste left it at the top: the caret and the last pasted line out of view. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15ba2-63d6-7533-b232-21f35d99d2b2&host=cosmic-gray
Milkdown's clipboard plugin dispatches the pasted slice without scrollIntoView, so ProseMirror kept the field's scroll where it was. MarkdownField now marks a paste the surface did not consume and appends a scrollIntoView to the transaction that inserts it (whichever plugin dispatches it); the markup-text paste path scrolls too. Every surface on MarkdownField gets it: the chat composer, Quick Composer, sub-sessions and Chief/Home (PromptEditor). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15ba2-63d6-7533-b232-21f35d99d2b2&host=cosmic-gray
|
cmux status (one comment, edited in place; each line is updated by the job that owns it) Review App of this head (app.zip, kept 1 day)
Pending: CI is running on Last result: Probably yours: Written by red: web / react-apps-check (1); no finished base run on 37b2e60 to compare; do not merge until green; see lane-rules
Agents: Tour of |
|
Bead |
|
Found 1 test failure on Blacksmith runners: Failure
|
|
owner: 01a15ba2-63d6-7533-b232-21f35d99d2b2 |
|
Shipped in:
|
|
Shipped in:
|
…wn text block
Leo 2026-10-21, as in Claude: a paste of more than 20 lines or 2,000
characters into the composer no longer lands as pages of prompt. It becomes a
"Pasted text" attachment chip in the chips row (its first words over
"Pasted text · N lines"), and the prompt keeps what was typed and its caret.
- pastedText.ts holds the limits (PASTE_CHIP_LINES, PASTE_CHIP_CHARS), the
label and line count, and the one wire-shape helper, pastedTextBlocks: each
paste goes to the agent as its own ACP text block after the prompt's, with
_meta {"cmux":{"pasted":true}} (promptBlocks). The bubble and queue text stay
the typed prompt.
- The field reports a paste to onBeforeInput as such (state.paste, with the
clipboard's plain text as copied); the composer makes the chip there
(composer/pastedChips.ts). A paste under the limit goes in as before and
still scrolls to its end (#22789).
- Paste as Plain Text keeps the raw paste: the prompt menu's item and the
Cmd-Shift-V key (Paste and Match Style, a system menu key the bridge sends no
keycap for) mark the next paste, within 2 s, as raw.
- Click on the chip: the simple expand. Its text goes back into the prompt at
the caret as a paste of it, and the chip goes. Its right-click menu has Edit
in Prompt and Remove; x removes it. (No Backspace-removes-chip exists for
image chips, so none here.)
- A pasted chip is kept with the chat's unsent chips like pictures (#23139):
kind "pasted" with its text; the host keeps both as they are.
- Strings: composer.pastedText, composer.pastedText.edit and the plural
composer.pastedText.lines, in the catalog's 21 languages.
- Gallery: agent-pane.composer#text-pasted. composer-paste-end now pastes 15
lines, under the chip limit.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15ba2-63d6-7533-b232-21f35d99d2b2&host=cosmic-gray
* test(acpmux): a huge paste into the composer is a Pasted text chip (red) A paste over 20 lines or 2,000 characters becomes a "Pasted text" chip (first words, line count) and the prompt keeps what was typed; a smaller paste goes in as text; Paste as Plain Text (Cmd-Shift-V) keeps the raw paste; x removes the chip; a click puts its text back in the prompt; and the text goes to the agent as its own text block after the prompt's, with _meta.cmux.pasted. Fails on the base: no chip exists, every paste lands in the prompt. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15ba2-63d6-7533-b232-21f35d99d2b2&host=cosmic-gray * cmux NEXT composer: a huge paste is a Pasted text chip, sent as its own text block Leo 2026-10-21, as in Claude: a paste of more than 20 lines or 2,000 characters into the composer no longer lands as pages of prompt. It becomes a "Pasted text" attachment chip in the chips row (its first words over "Pasted text · N lines"), and the prompt keeps what was typed and its caret. - pastedText.ts holds the limits (PASTE_CHIP_LINES, PASTE_CHIP_CHARS), the label and line count, and the one wire-shape helper, pastedTextBlocks: each paste goes to the agent as its own ACP text block after the prompt's, with _meta {"cmux":{"pasted":true}} (promptBlocks). The bubble and queue text stay the typed prompt. - The field reports a paste to onBeforeInput as such (state.paste, with the clipboard's plain text as copied); the composer makes the chip there (composer/pastedChips.ts). A paste under the limit goes in as before and still scrolls to its end (#22789). - Paste as Plain Text keeps the raw paste: the prompt menu's item and the Cmd-Shift-V key (Paste and Match Style, a system menu key the bridge sends no keycap for) mark the next paste, within 2 s, as raw. - Click on the chip: the simple expand. Its text goes back into the prompt at the caret as a paste of it, and the chip goes. Its right-click menu has Edit in Prompt and Remove; x removes it. (No Backspace-removes-chip exists for image chips, so none here.) - A pasted chip is kept with the chat's unsent chips like pictures (#23139): kind "pasted" with its text; the host keeps both as they are. - Strings: composer.pastedText, composer.pastedText.edit and the plural composer.pastedText.lines, in the catalog's 21 languages. - Gallery: agent-pane.composer#text-pasted. composer-paste-end now pastes 15 lines, under the chip limit. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15ba2-63d6-7533-b232-21f35d99d2b2&host=cosmic-gray * test(acpmux): pasted text reaches every harness whole and expands exactly (red) Review of #23396: - uniichat-agent and the CLI agent with --last-block send only the last text block, so a typed question and a pasted log reached the agent as the log alone. A pasted block (_meta.cmux.pasted) now goes with the typed text, a blank line between them (cli_agent.rs, uniichat_agent.rs tests). - A chip's click must put its text back exactly as a raw paste does: blank lines kept, none added between lines. - Paste as Plain Text by the key's position (KeyV on a non-Latin layout) keeps the paste raw; one that pasted only a file arms nothing for later. - The bubble shows a pasted chip's text as acpmux's echo will (text blocks joined by a newline), and Retry/Edit, which send the echoed text, drop the pasted chip. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15ba2-63d6-7533-b232-21f35d99d2b2&host=cosmic-gray * cmux NEXT composer: pasted text reaches every harness whole; a chip expands exactly Review of #23396: - acpmux's CLI agent (--last-block) and uniichat-agent sent only the last text block, so "why does this fail?" with a pasted log reached the agent as the log alone. A block marked _meta.cmux.pasted now always goes with the typed text, after a blank line (cli_agent::prompt_text, which uniichat-agent now uses); prompts without a paste go as before. - A chip's click put its text back one paragraph per line, blank lines dropped. MarkdownField's pasteText now pastes with the text as the clipboard's plain text, so it takes a person's paste path (Milkdown's markdown paste) and the prompt holds the text exactly as pasted. - The bubble and queue row show a pasted chip's text as acpmux's echo will (text blocks joined by a newline), so a send of only a paste is not an empty bubble and nothing jumps when the echo lands. - Retry and Edit send the echoed text, which has each paste: the pasted chip is no longer sent with it again (promptAttachments). - Paste as Plain Text: Cmd-Shift-V by the key's position (KeyV) counts on a non-Latin layout, and any paste, one of only files too, ends its turn. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15ba2-63d6-7533-b232-21f35d99d2b2&host=cosmic-gray * cmux NEXT composer: the pasted chip's edit hint is a title, not aria-description Oxlint's jsx-a11y role-supports-aria-props warns on aria-description on a button. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15ba2-63d6-7533-b232-21f35d99d2b2&host=cosmic-gray * test(acpmux): the paste-chip test waits for the editor, not a fixed time check-fixed-waits (new on base) counts every fixed setTimeout wait. Opening waits for the combobox with until(), and a paste yields one macrotask. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15ba2-63d6-7533-b232-21f35d99d2b2&host=cosmic-gray * cmux NEXT composer: the paste chip counts and names as the transcript's, and the bubble has its chip at once The composer's chip uses promptCollapse.ts pasteLines/pasteName and transcript.pastedLines (#23431) in place of its own copies. The optimistic bubble carries the paste ranges user_message.pasted will name (#23407), so the transcript chip shows from the send on. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15ba2-63d6-7533-b232-21f35d99d2b2&host=cosmic-gray * test(acpmux): paste-chip jsdom tests start from an empty prompt and measure no layout Every test in composer-paste-text opens chat "chat-a", and the composer keeps a chat's unsent prompt in the page's localStorage remount cache (composerDraft.ts). The jsdom window is shared across the file, so the first test's typed "why does this fail?" (and later pastes) came back in the next tests' prompts: the 2,000-character and x tests saw that text, and the send test sent it joined to its own. afterEach now clears localStorage once the root is unmounted. A chip's click puts its text back as a paste, and ProseMirror scrolls a paste into view by measuring a Range. jsdom's Range has no getClientRects, so the click threw inside scrollToSelection, which also left act() overlapping and the next test's handle null. The test gives Range an empty box, as markdown-comments.test.ts does; the product path is unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15ba2-63d6-7533-b232-21f35d99d2b2&host=cosmic-gray * test(acpmux): the long-paste browser tests paste under the chip's limit and still overflow A paste over 20 lines or 2,000 characters is now a Pasted text chip, so composer-scroll-fade's 40-line paste never reached the prompt (each test timed out waiting for its last line), and composer-paste-end's 15 short lines, which this PR chose to stay raw, did not overflow the field: the prompt shows pasted lines joined as one paragraph, and 15 short ones wrap to about three rows, under the field's eight-line cap. The small-paste path is the one on base (take() returns false under the limit and the field pastes as before); only the fixtures were too small. Both now paste long lines under both limits (15 and 20 lines of 70 to 90 characters), which wrap well past eight rows at the tests' 900 px viewport. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15ba2-63d6-7533-b232-21f35d99d2b2&host=cosmic-gray --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…s the same Pasted chip (#23622) * test(acpmux): a huge paste into the composer is a Pasted text chip (red) A paste over 20 lines or 2,000 characters becomes a "Pasted text" chip (first words, line count) and the prompt keeps what was typed; a smaller paste goes in as text; Paste as Plain Text (Cmd-Shift-V) keeps the raw paste; x removes the chip; a click puts its text back in the prompt; and the text goes to the agent as its own text block after the prompt's, with _meta.cmux.pasted. Fails on the base: no chip exists, every paste lands in the prompt. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15ba2-63d6-7533-b232-21f35d99d2b2&host=cosmic-gray * cmux NEXT composer: a huge paste is a Pasted text chip, sent as its own text block Leo 2026-10-21, as in Claude: a paste of more than 20 lines or 2,000 characters into the composer no longer lands as pages of prompt. It becomes a "Pasted text" attachment chip in the chips row (its first words over "Pasted text · N lines"), and the prompt keeps what was typed and its caret. - pastedText.ts holds the limits (PASTE_CHIP_LINES, PASTE_CHIP_CHARS), the label and line count, and the one wire-shape helper, pastedTextBlocks: each paste goes to the agent as its own ACP text block after the prompt's, with _meta {"cmux":{"pasted":true}} (promptBlocks). The bubble and queue text stay the typed prompt. - The field reports a paste to onBeforeInput as such (state.paste, with the clipboard's plain text as copied); the composer makes the chip there (composer/pastedChips.ts). A paste under the limit goes in as before and still scrolls to its end (#22789). - Paste as Plain Text keeps the raw paste: the prompt menu's item and the Cmd-Shift-V key (Paste and Match Style, a system menu key the bridge sends no keycap for) mark the next paste, within 2 s, as raw. - Click on the chip: the simple expand. Its text goes back into the prompt at the caret as a paste of it, and the chip goes. Its right-click menu has Edit in Prompt and Remove; x removes it. (No Backspace-removes-chip exists for image chips, so none here.) - A pasted chip is kept with the chat's unsent chips like pictures (#23139): kind "pasted" with its text; the host keeps both as they are. - Strings: composer.pastedText, composer.pastedText.edit and the plural composer.pastedText.lines, in the catalog's 21 languages. - Gallery: agent-pane.composer#text-pasted. composer-paste-end now pastes 15 lines, under the chip limit. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15ba2-63d6-7533-b232-21f35d99d2b2&host=cosmic-gray * test(acpmux): pasted text reaches every harness whole and expands exactly (red) Review of #23396: - uniichat-agent and the CLI agent with --last-block send only the last text block, so a typed question and a pasted log reached the agent as the log alone. A pasted block (_meta.cmux.pasted) now goes with the typed text, a blank line between them (cli_agent.rs, uniichat_agent.rs tests). - A chip's click must put its text back exactly as a raw paste does: blank lines kept, none added between lines. - Paste as Plain Text by the key's position (KeyV on a non-Latin layout) keeps the paste raw; one that pasted only a file arms nothing for later. - The bubble shows a pasted chip's text as acpmux's echo will (text blocks joined by a newline), and Retry/Edit, which send the echoed text, drop the pasted chip. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15ba2-63d6-7533-b232-21f35d99d2b2&host=cosmic-gray * cmux NEXT composer: pasted text reaches every harness whole; a chip expands exactly Review of #23396: - acpmux's CLI agent (--last-block) and uniichat-agent sent only the last text block, so "why does this fail?" with a pasted log reached the agent as the log alone. A block marked _meta.cmux.pasted now always goes with the typed text, after a blank line (cli_agent::prompt_text, which uniichat-agent now uses); prompts without a paste go as before. - A chip's click put its text back one paragraph per line, blank lines dropped. MarkdownField's pasteText now pastes with the text as the clipboard's plain text, so it takes a person's paste path (Milkdown's markdown paste) and the prompt holds the text exactly as pasted. - The bubble and queue row show a pasted chip's text as acpmux's echo will (text blocks joined by a newline), so a send of only a paste is not an empty bubble and nothing jumps when the echo lands. - Retry and Edit send the echoed text, which has each paste: the pasted chip is no longer sent with it again (promptAttachments). - Paste as Plain Text: Cmd-Shift-V by the key's position (KeyV) counts on a non-Latin layout, and any paste, one of only files too, ends its turn. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15ba2-63d6-7533-b232-21f35d99d2b2&host=cosmic-gray * cmux NEXT composer: the pasted chip's edit hint is a title, not aria-description Oxlint's jsx-a11y role-supports-aria-props warns on aria-description on a button. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15ba2-63d6-7533-b232-21f35d99d2b2&host=cosmic-gray * test(acpmux): the paste-chip test waits for the editor, not a fixed time check-fixed-waits (new on base) counts every fixed setTimeout wait. Opening waits for the combobox with until(), and a paste yields one macrotask. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15ba2-63d6-7533-b232-21f35d99d2b2&host=cosmic-gray * cmux NEXT composer: the paste chip counts and names as the transcript's, and the bubble has its chip at once The composer's chip uses promptCollapse.ts pasteLines/pasteName and transcript.pastedLines (#23431) in place of its own copies. The optimistic bubble carries the paste ranges user_message.pasted will name (#23407), so the transcript chip shows from the send on. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15ba2-63d6-7533-b232-21f35d99d2b2&host=cosmic-gray * test(acpmux): paste-chip jsdom tests start from an empty prompt and measure no layout Every test in composer-paste-text opens chat "chat-a", and the composer keeps a chat's unsent prompt in the page's localStorage remount cache (composerDraft.ts). The jsdom window is shared across the file, so the first test's typed "why does this fail?" (and later pastes) came back in the next tests' prompts: the 2,000-character and x tests saw that text, and the send test sent it joined to its own. afterEach now clears localStorage once the root is unmounted. A chip's click puts its text back as a paste, and ProseMirror scrolls a paste into view by measuring a Range. jsdom's Range has no getClientRects, so the click threw inside scrollToSelection, which also left act() overlapping and the next test's handle null. The test gives Range an empty box, as markdown-comments.test.ts does; the product path is unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15ba2-63d6-7533-b232-21f35d99d2b2&host=cosmic-gray * test(acpmux): the long-paste browser tests paste under the chip's limit and still overflow A paste over 20 lines or 2,000 characters is now a Pasted text chip, so composer-scroll-fade's 40-line paste never reached the prompt (each test timed out waiting for its last line), and composer-paste-end's 15 short lines, which this PR chose to stay raw, did not overflow the field: the prompt shows pasted lines joined as one paragraph, and 15 short ones wrap to about three rows, under the field's eight-line cap. The small-paste path is the one on base (take() returns false under the limit and the field pastes as before); only the fixtures were too small. Both now paste long lines under both limits (15 and 20 lines of 70 to 90 characters), which wrap well past eight rows at the tests' 900 px viewport. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15ba2-63d6-7533-b232-21f35d99d2b2&host=cosmic-gray * webviews: a long paste round-trips composer to transcript as the same Pasted chip A JSDOM test runs the real composer (type, paste 2,300 lines, Send), the real AcpmuxDirectClient against a fake acpmux that records the sent blocks as hub/session.rs prompt_text_parts does (text blocks joined with a newline, UTF-16 ranges of the _meta.cmux.pasted ones), and the real UserBubble. It checks the typed text comes first and the chip after, and that the pending bubble drawn at send matches the one drawn from acpmux's user_message echo. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15ba3-d09a-7940-9fd6-158e10477c0e&host=cosmic-gray * Take feat-cmux-next's Localizable.xcstrings; this PR adds no strings The base merge's string-catalog driver reordered the catalog; this branch changes no strings. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15ba3-d09a-7940-9fd6-158e10477c0e&host=cosmic-gray --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…past (#23626) * test(acpmux): a huge paste into the composer is a Pasted text chip (red) A paste over 20 lines or 2,000 characters becomes a "Pasted text" chip (first words, line count) and the prompt keeps what was typed; a smaller paste goes in as text; Paste as Plain Text (Cmd-Shift-V) keeps the raw paste; x removes the chip; a click puts its text back in the prompt; and the text goes to the agent as its own text block after the prompt's, with _meta.cmux.pasted. Fails on the base: no chip exists, every paste lands in the prompt. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15ba2-63d6-7533-b232-21f35d99d2b2&host=cosmic-gray * cmux NEXT composer: a huge paste is a Pasted text chip, sent as its own text block Leo 2026-10-21, as in Claude: a paste of more than 20 lines or 2,000 characters into the composer no longer lands as pages of prompt. It becomes a "Pasted text" attachment chip in the chips row (its first words over "Pasted text · N lines"), and the prompt keeps what was typed and its caret. - pastedText.ts holds the limits (PASTE_CHIP_LINES, PASTE_CHIP_CHARS), the label and line count, and the one wire-shape helper, pastedTextBlocks: each paste goes to the agent as its own ACP text block after the prompt's, with _meta {"cmux":{"pasted":true}} (promptBlocks). The bubble and queue text stay the typed prompt. - The field reports a paste to onBeforeInput as such (state.paste, with the clipboard's plain text as copied); the composer makes the chip there (composer/pastedChips.ts). A paste under the limit goes in as before and still scrolls to its end (#22789). - Paste as Plain Text keeps the raw paste: the prompt menu's item and the Cmd-Shift-V key (Paste and Match Style, a system menu key the bridge sends no keycap for) mark the next paste, within 2 s, as raw. - Click on the chip: the simple expand. Its text goes back into the prompt at the caret as a paste of it, and the chip goes. Its right-click menu has Edit in Prompt and Remove; x removes it. (No Backspace-removes-chip exists for image chips, so none here.) - A pasted chip is kept with the chat's unsent chips like pictures (#23139): kind "pasted" with its text; the host keeps both as they are. - Strings: composer.pastedText, composer.pastedText.edit and the plural composer.pastedText.lines, in the catalog's 21 languages. - Gallery: agent-pane.composer#text-pasted. composer-paste-end now pastes 15 lines, under the chip limit. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15ba2-63d6-7533-b232-21f35d99d2b2&host=cosmic-gray * test(acpmux): pasted text reaches every harness whole and expands exactly (red) Review of #23396: - uniichat-agent and the CLI agent with --last-block send only the last text block, so a typed question and a pasted log reached the agent as the log alone. A pasted block (_meta.cmux.pasted) now goes with the typed text, a blank line between them (cli_agent.rs, uniichat_agent.rs tests). - A chip's click must put its text back exactly as a raw paste does: blank lines kept, none added between lines. - Paste as Plain Text by the key's position (KeyV on a non-Latin layout) keeps the paste raw; one that pasted only a file arms nothing for later. - The bubble shows a pasted chip's text as acpmux's echo will (text blocks joined by a newline), and Retry/Edit, which send the echoed text, drop the pasted chip. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15ba2-63d6-7533-b232-21f35d99d2b2&host=cosmic-gray * cmux NEXT composer: pasted text reaches every harness whole; a chip expands exactly Review of #23396: - acpmux's CLI agent (--last-block) and uniichat-agent sent only the last text block, so "why does this fail?" with a pasted log reached the agent as the log alone. A block marked _meta.cmux.pasted now always goes with the typed text, after a blank line (cli_agent::prompt_text, which uniichat-agent now uses); prompts without a paste go as before. - A chip's click put its text back one paragraph per line, blank lines dropped. MarkdownField's pasteText now pastes with the text as the clipboard's plain text, so it takes a person's paste path (Milkdown's markdown paste) and the prompt holds the text exactly as pasted. - The bubble and queue row show a pasted chip's text as acpmux's echo will (text blocks joined by a newline), so a send of only a paste is not an empty bubble and nothing jumps when the echo lands. - Retry and Edit send the echoed text, which has each paste: the pasted chip is no longer sent with it again (promptAttachments). - Paste as Plain Text: Cmd-Shift-V by the key's position (KeyV) counts on a non-Latin layout, and any paste, one of only files too, ends its turn. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15ba2-63d6-7533-b232-21f35d99d2b2&host=cosmic-gray * cmux NEXT composer: the pasted chip's edit hint is a title, not aria-description Oxlint's jsx-a11y role-supports-aria-props warns on aria-description on a button. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15ba2-63d6-7533-b232-21f35d99d2b2&host=cosmic-gray * test(acpmux): the paste-chip test waits for the editor, not a fixed time check-fixed-waits (new on base) counts every fixed setTimeout wait. Opening waits for the combobox with until(), and a paste yields one macrotask. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15ba2-63d6-7533-b232-21f35d99d2b2&host=cosmic-gray * cmux NEXT composer: the paste chip counts and names as the transcript's, and the bubble has its chip at once The composer's chip uses promptCollapse.ts pasteLines/pasteName and transcript.pastedLines (#23431) in place of its own copies. The optimistic bubble carries the paste ranges user_message.pasted will name (#23407), so the transcript chip shows from the send on. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15ba2-63d6-7533-b232-21f35d99d2b2&host=cosmic-gray * test(acpmux): paste-chip jsdom tests start from an empty prompt and measure no layout Every test in composer-paste-text opens chat "chat-a", and the composer keeps a chat's unsent prompt in the page's localStorage remount cache (composerDraft.ts). The jsdom window is shared across the file, so the first test's typed "why does this fail?" (and later pastes) came back in the next tests' prompts: the 2,000-character and x tests saw that text, and the send test sent it joined to its own. afterEach now clears localStorage once the root is unmounted. A chip's click puts its text back as a paste, and ProseMirror scrolls a paste into view by measuring a Range. jsdom's Range has no getClientRects, so the click threw inside scrollToSelection, which also left act() overlapping and the next test's handle null. The test gives Range an empty box, as markdown-comments.test.ts does; the product path is unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15ba2-63d6-7533-b232-21f35d99d2b2&host=cosmic-gray * test(acpmux): the long-paste browser tests paste under the chip's limit and still overflow A paste over 20 lines or 2,000 characters is now a Pasted text chip, so composer-scroll-fade's 40-line paste never reached the prompt (each test timed out waiting for its last line), and composer-paste-end's 15 short lines, which this PR chose to stay raw, did not overflow the field: the prompt shows pasted lines joined as one paragraph, and 15 short ones wrap to about three rows, under the field's eight-line cap. The small-paste path is the one on base (take() returns false under the limit and the field pastes as before); only the fixtures were too small. Both now paste long lines under both limits (15 and 20 lines of 70 to 90 characters), which wrap well past eight rows at the tests' 900 px viewport. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15ba2-63d6-7533-b232-21f35d99d2b2&host=cosmic-gray * cmux NEXT composer: test that the attachment row fades at its scrolled sides A row of chips wider than the composer scrolls sideways. The test expects the side with chips past it to fade like the prompt's edges: data-fade-left / data-fade-right, an alpha mask by the prompt's band (--acpmux-md-fade-band), none at a side with nothing past it, and the composer behind showing through the faded side. Chromium and WebKit. Red until the row fades. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15bf1-fdc7-79b0-b869-8ac78b0edd90&host=cosmic-gray * cmux NEXT composer: the attachment row fades at the sides it scrolls past A row of chips wider than the composer scrolls sideways. Its side with chips past it is now marked (data-fade-left / data-fade-right, followScrollEdges across) and faded with an alpha mask on the row itself, as the prompt's edges are, so the composer's glass shows through. The band is the prompt's: MarkdownField.tsx exports FADE_BAND_PX and the row sets --acpmux-md-fade-band from it. Reduce Transparency keeps plain sides. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15bf1-fdc7-79b0-b869-8ac78b0edd90&host=cosmic-gray --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
A long paste into the composer now scrolls the field to its end, with the caret right after the pasted text, as Claude desktop does.
The prompt field grows to eight lines and then scrolls. Milkdown's clipboard plugin inserts a paste without asking ProseMirror to scroll, so pasting 40 lines into a short draft left the field at the top and the caret out of view. The field now marks a paste that no surface handled itself. A small
appendTransactionthen addsscrollIntoViewto whichever transaction inserts it. The markup-text paste path (HTML or JSX pasted as text) scrolls too.This is one path in
MarkdownField, so it covers every surface that uses it: chat, Quick Composer, sub-sessions and the Chief/Home composer. The shell field is a plain textarea, which already scrolls on paste.Changelog
Test
webviews/test/composer-paste-end.test.ts(browser lane, Chromium and WebKit): type a 3-line draft, paste 40 lines, then check that the field scrolled, the caret is at the end, and the caret and "pasted line 40" are inside the field's visible box. The test is committed red before the fix (85b0249), and the fix is eb2c7d4.Observed / Not run
vp fmton the changed files.bun run check, and web lint. Agents don't run bun or playwright on this Mac, so CI is the first run. The red-on-base claim comes from reading the code, not from a run.Agent session: cosmic-gray
🤖 Generated with Claude Code
Need help on this PR? Tag
@codesmith-botwith what you need.Summary by cubic
Fixes pasting a long text into the composer so the field scrolls to its end, with the caret right after the pasted text.
Milkdown's clipboard plugin inserts a paste without asking ProseMirror to scroll, so pasting 40 lines into a short draft left the field at the top with the caret out of view.
MarkdownFieldnow flags a paste that no surface handled, and anappendTransactionaddsscrollIntoViewto the transaction that inserts it; the markup-text paste path scrolls too. Because the fix lives inMarkdownField, it covers chat, Quick Composer, sub-sessions, and the Chief/Home composer; the shell field is a plain textarea that already scrolls on paste.Adds a browser test (Chromium and WebKit) that pastes 40 lines into a 3-line draft and verifies the field scrolled, the caret sits at the end, and the caret and last pasted line are in view.
Written for commit a631a7d. Summary will update on new commits.