Commit Graph
10 Commits
Author SHA1 Message Date
paseo-bot[bot] 950e1f8f41 Keep an image's alt text and source when copying a selection from an assistant reply (#6181)
* fix(app): copy an assistant image as its Markdown, with alt text

Selecting across an image in an assistant reply copied the rendered img,
which has no alt text and, for data and workspace images, points at a
preview URL. The image frame now carries its Markdown source and alt text
the same way other Markdown elements declare their copy form.

Closes #6163

* fix(app): copy an image selected on its own as its Markdown

A selection inside the rendered image left the image's copy attributes
on the frame outside the range, so nothing was copied as Markdown.

* test(app): name the image-only selection in the copy spec

---------

Co-authored-by: paseo-bot[bot] <266920839+paseo-bot[bot]@users.noreply.github.com>
2026-10-06 02:07:01 +02:00
paseo-bot[bot] 4ed13fadb6 fix(app): keep list numbers when a copy crosses a code block nested in an item (#6158)
A selection starting after an item's marker demotes that item to a paragraph.
When the item holds a code block, Turndown's HTML re-parse splits the paragraph
into several siblings, and counting siblings numbered the next item too high
("4. Done." instead of "2. Done."). Each item now carries its number before
the re-parse.

Closes #6140

Co-authored-by: paseo-bot[bot] <266920839+paseo-bot[bot]@users.noreply.github.com>
2026-10-05 21:52:51 +02:00
paseo-bot[bot] 18412604dd fix(app): copy only the code when a selection touches the block beside it (#6138)
* fix(app): copy a code block without its fence when the drag starts beside it

A drag that starts or ends in the gap beside a code block anchors the
selection at the edge of the neighbouring block. Only the code is
highlighted, but the range crossed the block boundary, so the copy took
the Markdown path and wrapped the code in its fence.

A selection now crosses a code block only when it selects content
outside it. Message rows the range touches without selecting anything
are dropped, and a selection whose content all lies inside one code
block copies as that code.

* fix(app): keep a selected image when copying it beside a code block

An image selected next to a code block has no text, so the selection
counted it as empty and copied only the code. Count images as selected
content so the copy keeps the image and the fence, as it did before.

---------

Co-authored-by: paseo-bot[bot] <266920839+paseo-bot[bot]@users.noreply.github.com>
2026-10-05 19:21:46 +02:00
Mohamed Boudra 135a3b4c9e Find text in assistant messages that streamed in the open chat (#5146)
* fix(app): find text in assistant messages that streamed in the open chat

Chat find failed with the generic "check the host connection" text
whenever the oldest hit sat in a response that streamed while the chat
was open and had more than one Markdown block. Presentation rendered
such a message as one row per block with ids `<messageId>:block:n`,
while hydrated history stayed one row with the raw message id, and every
message-addressed DOM lookup (find reveal, scroll-to-message, history
reveal, the render-cap expansion) only knew the raw id. The reveal step
polled for a row that never existed and gave up after five seconds.

The defect was the dual rendering, so this removes it: every assistant
message is a block group however it arrived, single-paragraph messages
included, so a row id is never a message id. Rows carry
`data-message-id`, `getStreamItemMessageId` is the one way from a row to
its message, and find, scroll-to-message, history reveal and selection
copy address messages through it. Chat find force-mounts every row of
the selected message under virtualization so counts span the whole
message, and pins the chosen occurrence to its row while the scroll
settles. Link reference definitions fold into their neighbouring block
so history never paints an empty row. Find failures now say what went
wrong (connection, history changed, could not show the match) instead
of blaming the connection for everything.

* fix(app): let the Markdown parser decide which blocks are link definitions

A hand-rolled CommonMark regex rejected destinations markdown-it accepts,
such as escaped spaces, so those definitions still became their own empty
row and the neighbouring paragraph lost its link. The block splitter now
parses the block with the same parser: no tokens plus registered
references means definitions only. The two new chat find journeys move
their mechanics into named helpers so the test bodies read as intent.
2026-09-21 22:31:08 +02:00
Mohamed Boudra 3bca9b7a92 fix(app): preserve selected list markers when copying (#3055)
Mouse drags start and end inside rendered list items, so the clipboard serializer dropped item semantics even when the visible marker was selected. Treat the marker as explicit selection intent while keeping text-only selections unmarked.
2026-08-09 08:15:45 +08:00
Mohamed Boudra 75a5065bb3 fix(app): apply clipboard syntax at selection boundaries
Selections contained inside formatting copy only their visible content. Preserve Markdown syntax when the range includes or crosses the semantic element, while retaining list and table structure needed to serialize child boundaries.
2026-08-07 10:43:44 +02:00
Mohamed Boudra 84b735fbea fix(app): preserve nested list clipboard lines
Flattened child list lines were reparented inside the parent item line, so rich paste targets split markers from their content. Keep every flattened line as a sibling and cover cross-structure selection plus inline-code double-click copying.
2026-08-07 10:25:43 +02:00
e99eb7de3d fix(app): keep line breaks and indentation in copied code (#2935)
* fix(app): keep line breaks and indentation in copied code

#2930 strips the pre/code tags from a partly selected code block, which
is right for the Markdown syntax but leaves the code itself going
through Turndown as prose. Turndown collapses whitespace, so two
selected lines arrive as one, the indentation is gone, and
Markdown-significant characters come back escaped.

Divert a partial code selection before that: copy the selected text
verbatim. Fully selected regions are untouched and still produce a
fence, which is what hasSelectedAllContents was already deciding.

The html half needs pre for anything multi-line -- HTML collapses
newlines too, so a rich paste target would lose the lines the same way.
A single line stays undecorated, matching the rule that a partial
selection does not carry its container's formatting.

Two trailing-newline fixes ride along, since both make a paste run code
nobody asked to run. Newlines between rendered lines are their own text
nodes, so a selection that overshoots the end of a line carries one;
strip it. The block's Copy button was handed the raw fence body, which
ends in a newline; hand it the rendered text instead.

* fix(app): strip every trailing blank line from copied code

The Copy button stripped one trailing newline, so a fence whose author
left a blank line before the closing delimiter still put an executable
newline on the clipboard. Pasting that into a terminal runs the last
line, which is the thing the strip existed to prevent.

Both copy paths now share one regex in markup.ts and remove every
trailing break, plus any indentation that followed the last one.

Reported by Greptile on #2935.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* fix(app): infer code copy format from selection boundary

---------

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Co-authored-by: Mohamed Boudra <boudra.moha@gmail.com>
2026-08-06 11:27:38 +00:00
Mohamed Boudra 098122be8d Keep copied assistant selections within their visible boundaries (#2930)
* fix(app): respect assistant copy selection boundaries

Selection serialization reintroduced semantic ancestors even when the user selected only their inner text, causing list markers and code delimiters to expand beyond the visible range. Preserve semantics only for complete selected nodes, and keep rich list payloads compatible with text composers that prefer HTML.

* fix(app): preserve loose list copy breaks
2026-08-06 10:52:40 +02:00
Mohamed Boudra adf33930b3 Preserve assistant selection formatting when copying (#2808)
* fix(app): preserve formatting when copying assistant selections

Browser selection serialized React Native Web layout nodes instead of the assistant message semantics. Recover semantic markup from renderer-owned metadata and reuse the rich clipboard generator used by Copy turn.

* fix(app): preserve assistant copy semantics

Carry source-only Markdown metadata through the selection copy boundary, unwrap generated code links, and cover ordered starts, hard breaks, and fence languages in Playwright. Refresh the Nix dependency hash for the added clipboard dependency.

* fix(app): preserve partial selection context

Wrap selected fragments with their shared semantic ancestor chain and enable GFM table and strikethrough conversion. Cover partial nested selections and GFM output through the browser clipboard.

* fix(app): preserve copied list and autolink text

Adjust partial ordered-list starts for omitted siblings, strip parser-generated anchors before Markdown reconstruction, and refresh the Nix dependency hash computed by CI.

* fix(app): normalize partial selection context

Normalize cloned ordered lists across block boundaries, degrade incomplete table fragments to selected text, and preserve table alignment provenance through Markdown reconstruction.

* fix(app): escape copied table delimiters

Rendered table cells no longer retain source escape characters. Escape literal pipes during DOM-to-Markdown conversion so copied tables preserve their column structure.

* fix(app): preserve authored link semantics

The rendered link boundary conflated parser-generated links with authored autolinks and discarded authored titles. Carry title semantics into the anchor and unwrap only links with explicit generated provenance.

* fix(app): preserve nested copy structure

Blank-line splitting separated loose nested lists before Markdown rendering, leaving no nesting for clipboard serialization to preserve. Derive protected separators from the Markdown block structure and keep copy metadata independent from monospace surface styling.

* fix(app): preserve selected file links

Assistant messages and rich clipboard HTML must apply the same link validation policy. A shared parser keeps file links semantic in both MIME representations while retaining markdown-it's unsafe-scheme rejection.

* fix(app): preserve partial table content

A selected body row wider than its retained header cannot be represented as GFM and is truncated when rich HTML is regenerated. Treat only those over-wide fragments, plus headerless fragments, as incomplete while preserving valid short rows.
2026-08-03 19:02:31 +08:00