Skip to content

fix(ui): strip ATX heading markers from quote chip display - #2214

Merged
Astro-Han merged 3 commits into
apache:mainfrom
cat0825:fix/2213-quote-heading-markers
Aug 5, 2026
Merged

fix(ui): strip ATX heading markers from quote chip display#2214
Astro-Han merged 3 commits into
apache:mainfrom
cat0825:fix/2213-quote-heading-markers

Conversation

@cat0825

@cat0825 cat0825 commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Fixes #2213

Problem

A quote chip can carry markdown source verbatim (selection capture keeps the
raw text), so a heading line like ### Description renders in the chip as
cramped, overlapping literal hashes — it reads as a blob instead of three
distinct #.

The markdown renderer is not at fault: ### mid-line is not a heading marker
per CommonMark (/^(#{1,6}) +(.*)/ anchors at line start), so it correctly
renders as plain text. The issue is presentation of that literal text in the
chip.

Fix

Add stripQuoteHeadingMarkers — display-only cleanup that removes a leading
ATX heading marker (#{1,6} ) from the quote text shown in the chip:

  • ### DescriptionDescription
  • ###Description (no space) → unchanged (not a heading)
  • C# code, #hashtag, mid-text # runs → unchanged (treated as content)

Presentation only: formatTextWithInlineRefs still sends quote.text
verbatim to the model, matching the chip's existing contract.

Tests

packages/ui/src/__tests__/quote-ref-chip.test.ts — 3 tests covering marker
stripping (1–6 hashes, multi-space/tab), and non-heading preservation.

Run:

  • npm --workspace @maka/ui run build (tsc) — passes
  • node --test dist/__tests__/quote-ref-chip.test.js — 3/3 pass
  • Full @maka/ui suite: 324/326 pass; the 2 failures
    (astryx-form-controls-localization) are pre-existing and unrelated
    (Astryx TextInput "Optional" marker localization, not touched by this PR).

A quoted excerpt can carry markdown source verbatim (selection capture keeps
the raw text), so a heading line like `### Description` rendered in the chip
as cramped literal hashes. Strip a leading `#{1,6} ` marker at display time
only; the model still receives the excerpt verbatim via
formatTextWithInlineRefs. Mid-text `#` runs are treated as content and left
alone.
@Astro-Han

Copy link
Copy Markdown
Contributor

Clean minimal fix — the regex is correct against CommonMark (####### x correctly left alone), the model still gets quote.text verbatim (model-history.ts:906), and the pure-function tests are solid. One gap worth closing before merge, plus small notes:

  • P1 — the pre-send drawer shows the same bug: quotes created via text selection carry no label (chat-view.tsx:603-607 → use-app-shell-composer-quotes.ts:27-34), so the pending-quotes Token at composer.tsx:1234 falls back to raw quote.text.slice(0, 48)### Description still renders as cramped hashes in the drawer, one step before the chip you fixed. One line: strip only the fallback branch — label={quote.label?.trim() || stripQuoteHeadingMarkers(quote.text.slice(0, 48)) || copy.pastedQuoteLabel} (covers the quote-companion composer too, same shared component).
  • P2 — test gap: add assert.equal(stripQuoteHeadingMarkers('####### x'), '####### x') — the one case where a naive /^#+ / would diverge.
  • P3 — docstring: "the only marker that can survive is #{1,6} at the start" overstates — selection normalization (use-message-selection-quote.ts:46) also preserves - , > , 1. prefixes; they're just out of scope.

P1 is a few lines (and pre-send, users see the very symptom #2213 describes); everything else can be follow-ups. Nice work otherwise! 🚢

cat0825 added 2 commits August 5, 2026 22:49
)

Maintainer review (P1): the pending-quotes Token in the composer drawer
falls back to raw quote.text when no label is set, so '### Description'
still rendered as cramped hashes one step before the chip. Route the
fallback through stripQuoteHeadingMarkers too.

Also add the '####### x' and '######x' boundary cases to the pure-function
tests (P2) and soften the docstring claim about which markers survive
selection normalization (P3).
Keep both sides of the composer.tsx import block: upstream's
WorkspacePicker/ComposerDraftPersistence additions and our
stripQuoteHeadingMarkers import.
@cat0825

cat0825 commented Aug 5, 2026

Copy link
Copy Markdown
Contributor Author

Addressed all three review points on fix/2213-quote-heading-markers (plus synced upstream main, fixing the composer.tsx import conflict with #2230):

  • P1: composer.tsx pending-quotes Token label fallback now routes through stripQuoteHeadingMarkersquote.label?.trim() || stripQuoteHeadingMarkers(quote.text.slice(0, 48)) || copy.pastedQuoteLabel. Covers the quote-companion composer too (shared component).
  • P2: added ####### x and ######x boundary cases to the pure-function tests.
  • P3: softened the docstring — other line prefixes (- , > , 1. ) survive selection normalization; only the ATX marker is stripped, by design.

Verification: @maka/core + @maka/ui tsc builds pass, quote-ref-chip.test.js 3/3, biome clean.

@Astro-Han

Copy link
Copy Markdown
Contributor

Thanks for this — the fix is clean and I verified both display surfaces: the pre-send token in the composer (label-less selection quotes) and the sent chip's displayText path both strip the marker, while the model still receives quote.text verbatim (display-only contract intact). The branch's net diff against upstream main is exactly the 3 files — the extra commits in history are the upstream merge (the #2237 perf commit is already in main; nothing unrelated lands). Merge is clean, and the 14 assertions genuinely pin the regex (they'd fail pre-fix).

Three optional follow-ups, none blocking:

  • The doc comment says CommonMark allows "space (or end of line)" after the marker, but the regex only handles space/tab — a quote that is exactly ### survives unstripped (and stays truthy, so the fallback label never fires). (?:[ \t]+|$) would match the documented contract.
  • bug(ui): markdown heading markers (###) shown verbatim in quote chips render as cramped literal text #2213's screenshot repro (…/issue.md ### Description, marker mid-string) is pinned as unchanged by the tests — CommonMark-correct, but worth confirming against the live app whether the reported visual actually had the marker at line start (if not, the issue needs a note on what exactly this PR fixes).
  • Test coverage is pure-function only: the sent-chip render path, the composer fallback chain, and unicode-after-marker (### 中文标题) are untested.

Happy to approve — these can be tracked as follow-ups.

@Astro-Han
Astro-Han merged commit 07c1e68 into apache:main Aug 5, 2026
12 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

bug(ui): markdown heading markers (###) shown verbatim in quote chips render as cramped literal text

2 participants