fix: render Markdown in conversation viewer - #211
Closed
zeroknots wants to merge 1 commit into
Closed
Conversation
Owner
tintinweb
added a commit
that referenced
this pull request
Aug 24, 2026
…eable The viewer wrapped every line with wrapTextWithAnsi, so assistant markdown showed as raw fences and `#` markers, and tool results were cut at 500 characters (#210). Assistant text now renders as markdown; tool results do not, by default. A markdown pass over a tool result is lossy in ways that read as the tool misbehaving: `# section` in a shell script loses its `#`, `3) 7) 9)` comes back renumbered `3. 4. 5.`, a `---` line is swallowed as a setext heading, and indented output is re-fenced. Assistant text is authored as markdown; a tool result is arbitrary bytes. viewerMarkdown (off | assistant | all) picks the scope, and `m` in the viewer cycles it and persists the choice. That escape hatch is what makes rendering safe to default on at all. The 500-char cap is raised to 16k rather than removed, and now covers bashExecution too. It bounds render cost, not just display: buildContentLines runs on every render and on every scroll key, where an uncapped 200KB result costs ~19ms per keystroke to re-parse against ~0.04ms for a capped wrap. One Markdown per message in a WeakMap, so the component's own cache applies. Code fences use pi's getMarkdownTheme() for syntax highlighting, probed rather than try/caught around the call: its functions read the global theme lazily, so an uninitialized theme throws inside render() and would take the overlay with it. Falls back to a theme built from the viewer's own Theme. Supersedes #211 — thanks @zeroknots. Co-authored-by: zeroknots <zeroknots@protonmail.com>
tintinweb
added a commit
that referenced
this pull request
Aug 24, 2026
…eable (#259) * fix(ui): render markdown in the conversation viewer, scoped and toggleable The viewer wrapped every line with wrapTextWithAnsi, so assistant markdown showed as raw fences and `#` markers, and tool results were cut at 500 characters (#210). Assistant text now renders as markdown; tool results do not, by default. A markdown pass over a tool result is lossy in ways that read as the tool misbehaving: `# section` in a shell script loses its `#`, `3) 7) 9)` comes back renumbered `3. 4. 5.`, a `---` line is swallowed as a setext heading, and indented output is re-fenced. Assistant text is authored as markdown; a tool result is arbitrary bytes. viewerMarkdown (off | assistant | all) picks the scope, and `m` in the viewer cycles it and persists the choice. That escape hatch is what makes rendering safe to default on at all. The 500-char cap is raised to 16k rather than removed, and now covers bashExecution too. It bounds render cost, not just display: buildContentLines runs on every render and on every scroll key, where an uncapped 200KB result costs ~19ms per keystroke to re-parse against ~0.04ms for a capped wrap. One Markdown per message in a WeakMap, so the component's own cache applies. Code fences use pi's getMarkdownTheme() for syntax highlighting, probed rather than try/caught around the call: its functions read the global theme lazily, so an uninitialized theme throws inside render() and would take the overlay with it. Falls back to a theme built from the viewer's own Theme. Supersedes #211 — thanks @zeroknots. Co-authored-by: zeroknots <zeroknots@protonmail.com> * fix(ui): survive a Markdown render that throws, and warn when `m` can't persist Fuzzing the viewer's Markdown path with hostile tool output — null bytes, lone surrogates, control characters, unterminated fences and tables, mid-word ANSI, ZWJ emoji, RTL marks — found one input that throws: ~54 nested blockquotes overflow pi-tui's recursive renderer with a RangeError. buildContentLines runs inside render() and again on every scroll key, so that took the overlay down for content the literal path displays fine. markdownLines now degrades to literal wrapping on any throw and remembers the failure, which would otherwise repeat on every render and every keystroke; the flag clears when the message's text changes. Catching rather than special-casing blockquote depth, because fuzzing cannot prove that is the only such input. The three literal-wrapping paths — `off` mode, non-`all` tool results and bash output — collapse into the rawLines() helper the fallback returns. Separately, persistSettings discarded saveAndEmitChanged's return, so a failed write from the `m` key was silent: the mode applied for the session while looking persisted. It now warns on failure and stays quiet on success, matching every other settings path. Tests: the fallback keeps content visible instead of throwing; results stay dim on the literal path, untested before rawLines took ownership of it; and a tool result growing past the cap keeps its held-back count moving while the cached parse is correctly reused. * update readme/doc * fix fialing test * fix(agents): read agent files that begin with a UTF-8 BOM Editors across the Windows/CJK world write UTF-8 with a BOM, and pi's parser did not look past one before 0.84.3: the fence missed, frontmatter came back empty, and the whole file became the body. Such an agent lost every field — and `tools: none` going missing left it holding bash, edit and write, a wider grant than its author wrote. `/agents` then refused to toggle it, calling a file with frontmatter frontmatter-less. Normalised at the read boundary (parseAgentFrontmatter), the one place agent files are read, rather than detected per pi version — one behaviour across the whole supported peer range. The write side looks past the BOM too and leaves it in the file, since it belongs to the encoding, not the block. Tests drive the real loader over a real BOM'd Chinese agent file, including the BOM+CRLF combination a Windows editor actually produces. * fix test --------- Co-authored-by: zeroknots <zeroknots@protonmail.com>
Owner
|
superseded by #259 |
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.
Summary
Render assistant messages and tool results in the FleetView conversation overlay with Pi's Markdown component. This fixes raw Markdown output from tools such as context-mode's ctx_execute and removes the fixed 500-character result truncation so the existing scrollable viewer can show the complete result.
Closes #210.
Changes
Testing