fix(web-presenter): dim-Markdown web fallback, memoize fetch conversion, strip note residue

Route a `web` result card's raw-content fallback through the TUI's dim
Markdown path (render() only recognized `card: 'generic'` as markdown
content, so web fallback rendered as bare undimmed text).

Memoize renderFetchOutput per (result, maxOutputChars) so the registry's
twin output.render / output.presentationMeta calls on the same frozen
result run one HTML->markdown conversion instead of two.

Remove the trailing `</content>`/`</invoke>` protocol residue from both
sides of the web-result-card Agent Note and re-record the pairing.
This commit is contained in:
Chinesezjc
2026-07-30 20:48:19 +08:00
parent 83c93bb18f
commit 689dcfe7d5
7 changed files with 90 additions and 11 deletions

View File

@@ -389,10 +389,17 @@ export class ToolCardComponent implements Component {
const glyph = this.result === undefined ? '○' : '●'
const rawBody = this.renderBody()
const view = this.resultView ?? this.callView
const genericContent = view.card === 'generic' ? view.content ?? this.result?.content : undefined
const unknownXml = this.definition === undefined && genericContent !== undefined
// A generic card's own content, or a web card's fallback to the raw result
// content (the `web` view carries no `content` copy), both render as one dim
// Markdown block below, so links/lists/headings keep the unified dim styling
// rather than reading as bare text. Terminal and diff cards own their body
// styling, so they are excluded (mirrors renderBody's fallback at line 511).
const markdownContent = view.card === 'generic'
? view.content ?? this.result?.content
: view.card === 'web' ? this.result?.content : undefined
const unknownXml = this.definition === undefined && markdownContent !== undefined
? renderUnknownXml(
displayText(contentText(genericContent)),
displayText(contentText(markdownContent)),
this.maxOutputLines,
this.visibility === 'expanded',
displayText,
@@ -405,7 +412,7 @@ export class ToolCardComponent implements Component {
// A generic card renders title and result as one Markdown document, so the
// document's own block spacing is preserved, then dims every row — the whole
// card body reads as one dim block under the status-colored header.
const body = unknownXml ?? (genericContent !== undefined && rawBody.lines.length > 0
const body = unknownXml ?? (markdownContent !== undefined && rawBody.lines.length > 0
? this.dimBody(rawBody, width)
: [...rawBody.prelude, ...rawBody.lines])
const visibleBody = unknownXml !== undefined || this.visibility === 'expanded'
@@ -503,8 +510,9 @@ export class ToolCardComponent implements Component {
return { prelude: [...hunks, footer], lines: [] }
}
// The web card carries no `content` copy, so a `web` result view falls back
// to the raw result content here (`view.card === 'generic'` narrows the union,
// mirroring line 392).
// to the raw result content here (`view.card === 'generic'` narrows the
// generic union arm; a `web` card takes the same fallback, mirroring the
// `markdownContent` selection in render()).
const content = (view.card === 'generic' ? view.content : undefined) ?? this.result?.content
const prelude: string[] = []
const lines: string[] = []