fix(chat): copy what the bubble shows (#269) + keep a sent message pinned - #272
Open
kfaracik wants to merge 2 commits into
Open
fix(chat): copy what the bubble shows (#269) + keep a sent message pinned#272kfaracik wants to merge 2 commits into
kfaracik wants to merge 2 commits into
Conversation
…eadable (#269) Copying an assistant reply put `message.content` on the clipboard verbatim, including the `<think>…</think>` block and the `[n]` citation markers that are hidden on screen. Pasting that back produced a user bubble that looked almost empty, because `parseThinkingContent` ran for every role and the user branch rendered only the text *before* `<think>` — dropping the block and everything after `</think>`. - Copy now goes through `visibleMessageText`: think blocks stripped, citation markers stripped when the reply is grounded in sources, and a reply that is nothing but reasoning falls back to that reasoning instead of an empty string. - User messages keep every word; only the think markers are dropped, so pasted markup never swallows the bubble. - The think parser was duplicated in `MessageItem` and `messageSources`; both now share `utils/thinking.ts`, which also handles an orphan `</think>`. - New chat titles are built from the marker-free text. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Sending the second or a later message sometimes left it where it was appended instead of pinned below the header, forcing a manual scroll. The pin was a single `scrollToEnd` fired from `onContentSizeChange`, but the list's maximum offset keeps moving for a few hundred milliseconds afterwards: `blankSpace` is applied as the scroll view's bottom contentInset and Reanimated commits it on the UI thread a frame later, the inset is recomputed once the new rows report their heights, and keyboard-controller drives its own per-frame scrollTo while the keyboard dismissed by the send animates out. Whichever lands last wins, so a lone scroll aims at a stale end and falls short. `useScrollSettler` re-asserts the position across that window (animated while the send transition is still running, instant afterwards), `recomputeBlankSpace` re-snaps whenever it moves the inset, a drag cancels the pin so the user is never fought, and the keyboard-dismiss snap no longer skips a send in flight just because the list was scrolled up when the message was written. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
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.
Why
Two independent chat bugs.
1. Copying a reply with
<think>tags (#269). The Copy button putmessage.contenton the clipboard verbatim — including the<think>…</think>block and the[n]citation markers that are hidden on screen. Pasting that back rendered a nearly empty user bubble:parseThinkingContentran for every role, and the user branch rendered only the text before<think>, dropping the block and everything after</think>. The DB row was always intact — the loss was purely visual, but the model did receive the raw markup.2. A sent message sometimes wasn't pinned. From the second message on, the new message occasionally stayed where it was appended instead of moving below the header, forcing a manual scroll. The pin was a single
scrollToEndfired fromonContentSizeChange, but the list's maximum offset keeps moving for a few hundred milliseconds after that:blankSpaceis applied as the scroll view's bottom contentInset (ScrollViewWithBottomPaddingin keyboard-controller), and Reanimated commits it on the UI thread a frame or more after JS sets it;scrollTothe whole time.Whichever lands last wins, so a lone scroll aims at a stale end and falls short. The first message in a chat was masked by the initial-scroll retry loop; later ones had no safety net. Independently, the Android keyboard-dismiss re-snap was gated on
wasAtBottomDuringKeyboard, so sending after scrolling up skipped the last correction.What changed
utils/thinking.ts— one home for the think parser, previously duplicated inMessageItemandmessageSources; also normalises an orphan</think>.utils/messageText.ts—visibleMessageText()decides what Copy puts on the clipboard: think blocks out, citation markers out when the reply is grounded in sources, and a reply that is only reasoning falls back to that reasoning instead of an empty string.useScrollSettlerre-asserts the scroll position across the settling window (animated while the send transition runs, instant afterwards).recomputeBlankSpacere-snaps whenever it moves the inset, a drag cancels the pin so the user is never fought, and a send in flight now outranks the keyboard-dismiss gate. A 300 ms fallback runs the pin even ifonContentSizeChangenever arrives.Test plan
yarn test— 772 passing, incl. new__tests__/messageText.test.ts(15) and__tests__/useScrollSettler.test.ts(8, fake timers: correction order and animation mode, cancel-on-drag, restart mid-window, unmount cleanup).yarn lint,yarn format:check,npx tsc --noEmitclean for the touched files.Closes #269
🤖 Generated with Claude Code