Repository navigation
Commit a4039b2
authored
Render transcript Markdown with CommonMark and GitHub's extensions (CF-4 parser slice) (#283)
* Render transcript Markdown with CommonMark and GitHub's extensions
Markdown.tsx's hand-written subset becomes micromark with its GFM
extension (mdast-util-from-markdown 2.0.3, micromark-extension-gfm
3.0.0, mdast-util-gfm 3.1.0, pinned and bundled into the renderer).
The tree renders straight to React elements: tables, task lists (inert
boxes), block quotes, nested lists, strikethrough, footnotes, reference
links and language-tagged fences. Raw HTML stays text, only http(s)
links are links, other targets stay as written, and an image becomes a
link to it instead of being fetched.
Parsing every delta again would cost a long reply about 4 ms a render
here, five times the old subset. While text keeps changing, only its
last block (split at blank lines outside fences) is parsed again, about
0.2 ms a delta; the whole text is parsed once it rests for 300 ms.
Settled renders are cached, so reopening a thread does not parse it
again. First open of a 200-message, 239 KB thread: about 160 ms, against
40 ms before.
Part of #277 (parser slice); semantic turn rendering waits for #274.
Coven-Harness: Claude Code / claude-opus-5-5
* Record full Markdown rendering in status
Coven-Harness: Claude Code / claude-opus-5-5
* Bound the settled Markdown cache by entries and text size
The transcript's Markdown also renders fetched pages, which can be
large. RenderCache keeps the 400 most recent settled renders of text up
to 64,000 characters; longer text renders each time and is never kept.
Coven-Harness: Claude Code / claude-opus-5-5
* Give transcript code blocks a language label and Copy
CodeBlock (components/transcript/) heads a fenced block with its
language ("text" when it has none) and a Copy button. Copy sends the
code through main's clipboard:write and does nothing else: no tool,
approval or turn starts. A status line tells screen readers it copied,
or that copying failed. The e2e copies a block and checks the clipboard
holds exactly its code while the thread stays idle.
Part of #277.
Coven-Harness: Claude Code / claude-opus-5-5
* Read rendered test markup without a regex tag stripper
CodeQL flagged the Markdown test's visible() helper as incomplete
multi-character sanitization: it removed tags with a regex and then
decoded entities. It only reads React's static markup in a test, but
the helper now skips tags character by character and decodes each
entity once, which is also what a reader sees.
Coven-Harness: Claude Code / claude-opus-5-5
* Click Copy again when a late layout change moves the code block
CI run 39112253838 failed the code-block Copy e2e once: Copied never
showed after a 42 ms click, while local runs and two other CI runs
passed. The likely cause: the follow-up suggestion arrives after the
turn ends, grows the composer, and the transcript moves under the
pointer. The test now clicks until Copied shows. A missed click copies
nothing, so this cannot hide a wrong copy, and the clipboard and
nothing-started assertions are unchanged. 9 of 9 local repeats passed.
Coven-Harness: Claude Code / claude-opus-5-5
* Keep a selection and focus in a reply when it finishes streaming
While a reply streamed, Markdown rendered it as memoized pieces; once
the text rested it switched to plain elements from one full parse. The
children's component type changed, so React unmounted every piece and
mounted new DOM: text the reader had selected in the reply, keyboard
focus on a control in it, and a code block's Copied state were lost the
moment it finished (e2e/transcript-continuity.spec.ts, red on 74833f7:
the selection read "" and Copy lost focus).
Both ways now render one Piece per streaming piece. The settled render
parses the whole text and groups its top-level blocks back into those
pieces by source offset, so settling updates each piece where it stands.
Streaming pieces are kept per component instance by text, so an
unchanged piece is still not parsed again.
Part of #277.
Coven-Harness: Claude Code / claude-opus-5-5
* Record that a finishing reply keeps its selection and focus
Coven-Harness: Claude Code / claude-opus-5-5
* Keep table columns readable at the 900x600 minimum
At 900x600 with the Review panel open, the transcript is 265 px wide. A
table with an unbreakable path took 413 px for that column and squeezed
the others to a word: Detail measured 58 px and its row 1,009 px tall.
Cells now keep at least 10em and let a long path or URL wrap inside, so
the table scrolls sideways in its own box (the row is 344 px; nothing is
cut to one word per line).
conversation-rendering.spec.ts checks, in Graphite, Jev and OpenCoven at
900x600: no sideways page scroll, a wide table and a long code line
scroll in their own boxes, every cell is at least about ten characters
wide, and Copy stays visible. It attaches a screenshot per theme
(MODEX_SCREENSHOTS keeps them).
Part of #277.
Coven-Harness: Claude Code / claude-opus-5-5
* Leave the settle-remount fix to #295 and keep the narrow-table fix
#295 (CF-4 completion, another session) is stacked on this branch at
74833f7 and fixes the same remount-at-settle defect as 083b8fb, its own
way, with its own continuity tests and theme captures. Keeping both
would conflict in Markdown.tsx, conversation-rendering.spec.ts and
status.md and make #295's evidence stale. So this restores Markdown.tsx,
markdown-render.test.ts and conversation-rendering.spec.ts to 74833f7,
drops transcript-continuity.spec.ts, and reverts the status clause.
#295 does not touch styles.css, so the 900x600 table fix stays: cells
keep at least 10em and wrap long tokens. Its theme check moves to its
own file, e2e/markdown-narrow.spec.ts, so it cannot conflict with #295.
Coven-Harness: Claude Code / claude-opus-5-51 parent 7ec5f14 commit a4039b2
9 files changed
Lines changed: 1151 additions & 49 deletions
File tree
- apps/desktop
- e2e
- src/renderer
- components
- transcript
- test
- docs
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
38 | 38 | | |
39 | 39 | | |
40 | 40 | | |
| 41 | + | |
41 | 42 | | |
42 | 43 | | |
43 | 44 | | |
44 | 45 | | |
45 | 46 | | |
46 | 47 | | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
47 | 51 | | |
48 | 52 | | |
49 | 53 | | |
| |||
0 commit comments