Repository navigation
Turn a long text paste into a Pasted text card - #154
Merged
Merged
Conversation
Pasting a log, a spec or a file's contents filled the composer with a wall of text. A paste of 2,000+ characters or 30+ lines now goes in the attachment tray as a card instead, styled after claude.ai's: the opening lines in small faint type over a PASTED badge, with an edit pencil and the usual remove button. Clicking the card shows the whole text; the pencil opens it for editing (saving it empty removes the card). The paste is not a file: it is never saved to assets/. It is stored as a text document block marked as pasted, so the sent message (and a reloaded chat) shows the same card, and the model gets the whole text inline, labelled as text the user pasted. Shorter pastes are unchanged.
A long paste used to be composer text, so the draft saved it: a reload or a trip to another project brought it back. As a card it lived only in memory and was lost. The draft envelope now carries the cards (queued messages' first, like their text), every add, edit or remove saves it, a restore puts them back in the tray, and a send takes them out of the tray with the text. Cards count toward the cloud mirror's size cap, and a card-only composer is kept, not replaced, when the user signs in.
A composer send stopped before its message reached the conversation (Stop while files upload) puts its text back in the box. A long paste used to be part of that text; as a card it was dropped. The card now comes back with the text. Files still don't: their blob URLs are about to be revoked.
While the AI waits on clarifying questions, the composer's text is the reply. A long paste used to be part of that text; as a card it was left out, and a paste-only reply did nothing at all. The cards now go in the reply after the typed text and leave the tray.
Next to the 100px Pasted text card, a file was a 60px square with the file icon stretched across it. Every tray thumbnail is now the same 100px rounded card. An image fills it, with its name along the bottom; any other file shows the file icon small and centred over its name. The marketing pages' composer copies the app's tray, so it changes with it.
The sent message shows a long paste as a Pasted text card, but the queued message drew it as a plain 📄 Pasted text chip that did nothing. It now uses the same card, and clicking it shows the text.
A new card was numbered by counting the cards in the tray, so after removing an earlier one the next paste reused a name still in use: two "Pasted text 2" cards, and the model read both under the same label. A new card now takes the lowest name not in use.
A long paste used to be the message's text, so the project title, the version label (and its GitHub commit message) and the naming/suggestion transcript read it. As a card it is a document block those skipped: a paste-only first message titled the project "New Chat" and its version "Update". They now read the paste after any typed text.
The paste never reaches the box, so a screen reader user heard nothing and found the box empty. Say that it was attached, and under which name.
A click outside the Edit pasted text dialog closed it and threw the edits away without a word. With unsaved changes it now keeps the dialog open and puts the cursor back in the text. Cancel, the close button and Escape still discard on purpose; an unchanged dialog still closes.
The Edit pasted text dialog focuses its 12px text area itself, and iOS Safari zooms the page into any field under 16px when it gets focus. On screens up to 900px the text is 16px, as for the app's other dialog fields.
While a send sets up, the composer is read-only and the browser drops a short paste. A long one still went into the tray as a card, and the send's draft clear then left it out of the draft.
# Conflicts: # package.json # src/js/app.js
reynaldichernando
pushed a commit
to reynaldichernando/builder
that referenced
this pull request
Nov 11, 2026
* Turn a long text paste into a Pasted text card Pasting a log, a spec or a file's contents filled the composer with a wall of text. A paste of 2,000+ characters or 30+ lines now goes in the attachment tray as a card instead, styled after claude.ai's: the opening lines in small faint type over a PASTED badge, with an edit pencil and the usual remove button. Clicking the card shows the whole text; the pencil opens it for editing (saving it empty removes the card). The paste is not a file: it is never saved to assets/. It is stored as a text document block marked as pasted, so the sent message (and a reloaded chat) shows the same card, and the model gets the whole text inline, labelled as text the user pasted. Shorter pastes are unchanged. * Keep a pasted text card in the composer draft A long paste used to be composer text, so the draft saved it: a reload or a trip to another project brought it back. As a card it lived only in memory and was lost. The draft envelope now carries the cards (queued messages' first, like their text), every add, edit or remove saves it, a restore puts them back in the tray, and a send takes them out of the tray with the text. Cards count toward the cloud mirror's size cap, and a card-only composer is kept, not replaced, when the user signs in. * Hand a pasted text card back when its send never lands A composer send stopped before its message reached the conversation (Stop while files upload) puts its text back in the box. A long paste used to be part of that text; as a card it was dropped. The card now comes back with the text. Files still don't: their blob URLs are about to be revoked. * Send a pasted text card with a reply to a clarifying question While the AI waits on clarifying questions, the composer's text is the reply. A long paste used to be part of that text; as a card it was left out, and a paste-only reply did nothing at all. The cards now go in the reply after the typed text and leave the tray. * Size the tray's file thumbnails like the pasted text card Next to the 100px Pasted text card, a file was a 60px square with the file icon stretched across it. Every tray thumbnail is now the same 100px rounded card. An image fills it, with its name along the bottom; any other file shows the file icon small and centred over its name. The marketing pages' composer copies the app's tray, so it changes with it. * Draw a queued message's pasted text as a card The sent message shows a long paste as a Pasted text card, but the queued message drew it as a plain 📄 Pasted text chip that did nothing. It now uses the same card, and clicking it shows the text. * Give each pasted text card its own name A new card was numbered by counting the cards in the tray, so after removing an earlier one the next paste reused a name still in use: two "Pasted text 2" cards, and the model read both under the same label. A new card now takes the lowest name not in use. * Read a pasted text card as the user's words in titles and labels A long paste used to be the message's text, so the project title, the version label (and its GitHub commit message) and the naming/suggestion transcript read it. As a card it is a document block those skipped: a paste-only first message titled the project "New Chat" and its version "Update". They now read the paste after any typed text. * Announce a long paste that became a card The paste never reaches the box, so a screen reader user heard nothing and found the box empty. Say that it was attached, and under which name. * Keep unsaved edits to a pasted card on a click outside A click outside the Edit pasted text dialog closed it and threw the edits away without a word. With unsaved changes it now keeps the dialog open and puts the cursor back in the text. Cancel, the close button and Escape still discard on purpose; an unchanged dialog still closes. * Stop iOS from zooming in when the pasted text editor opens The Edit pasted text dialog focuses its 12px text area itself, and iOS Safari zooms the page into any field under 16px when it gets focus. On screens up to 900px the text is 16px, as for the app's other dialog fields. * Don't take a long paste into a read-only composer While a send sets up, the composer is read-only and the browser drops a short paste. A long one still went into the tray as a card, and the send's draft clear then left it out of the draft.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Pasting a big block of text (a log, a spec, a file's contents) filled the chat input with a wall of text. Now a paste of 2,000+ characters or 30+ lines becomes a card in the attachment tray, styled after how claude.ai does it.
The card
PASTEDbadge, with an edit pencil and the usual × on hover.What the model gets
assets/like file attachments are. It's stored on the user message as a textdocumentblock marked_pasted.prepareHistoryForAIflattens it to plain text, labelledText the user pasted ("Pasted text"):. So the model reads the whole paste inline, the same as before.Details
Tests
scripts/test-paste-as-attachment.mjscovers the size threshold, the tray intake, the model-facing text and the send-path wiring.test-composer-drafts.mjsis updated for the split between pasted text and files.npm testpasses.Review fixes
A long paste used to be composer text, and several things relied on that. Each fix below is its own commit:
npm testpasses, with new checks intest-paste-as-attachment.mjsandtest-composer-drafts.mjs. Each fix was checked in the browser on the local dev server, in light and dark mode and at phone width.