From cf5f4871e8f2b8af5abc4f8d78d0403f00d7307e Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Fri, 21 Aug 2026 20:37:50 -0700 Subject: [PATCH] perf(tui): reuse prepared transcript render lines (#127767) Amp-Thread-ID: https://ampcode.com/threads/T-01a02570-1023-77c8-9513-e43f179ee673 Co-authored-by: Amp --- src/tui/components/hyperlink-markdown.test.ts | 24 +++++++++++++++++++ src/tui/components/hyperlink-markdown.ts | 12 +++++++++- 2 files changed, 35 insertions(+), 1 deletion(-) diff --git a/src/tui/components/hyperlink-markdown.test.ts b/src/tui/components/hyperlink-markdown.test.ts index 7e819c0cb3c5..2de9e9975702 100644 --- a/src/tui/components/hyperlink-markdown.test.ts +++ b/src/tui/components/hyperlink-markdown.test.ts @@ -17,6 +17,30 @@ function osc8Targets(raw: string) { } describe("HyperlinkMarkdown", () => { + it("does not reallocate prepared lines for an unchanged same-width redraw", () => { + const markdown = new HyperlinkMarkdown( + "مرحبا [docs](https://example.test/path)", + 0, + 0, + markdownTheme, + ); + + const first = markdown.render(80); + expect(markdown.render(80)).toBe(first); + + const resized = markdown.render(40); + expect(resized).not.toBe(first); + expect(markdown.render(40)).toBe(resized); + + markdown.setText("updated"); + const updated = markdown.render(40); + expect(updated).not.toBe(resized); + expect(markdown.render(40)).toBe(updated); + + markdown.invalidate(); + expect(markdown.render(40)).not.toBe(updated); + }); + it("moves dunder identifiers intact across fenced code wrap boundaries", () => { const markdown = new HyperlinkMarkdown( ["```python", 'if __name__ == "__main__":', "```"].join("\n"), diff --git a/src/tui/components/hyperlink-markdown.ts b/src/tui/components/hyperlink-markdown.ts index 18a64836f97b..9f0347cebde6 100644 --- a/src/tui/components/hyperlink-markdown.ts +++ b/src/tui/components/hyperlink-markdown.ts @@ -24,6 +24,7 @@ function sanitizeMarkdownDisplayText(text: string): string { export class HyperlinkMarkdown implements Component { private inner: Markdown; private urls: string[]; + private cachedRender?: { width: number; lines: string[] }; constructor( text: string, @@ -39,16 +40,25 @@ export class HyperlinkMarkdown implements Component { } render(width: number): string[] { - return addOsc8Hyperlinks(this.inner.render(width), this.urls).map(isolateRtlRenderedLine); + if (this.cachedRender?.width === width) { + return this.cachedRender.lines; + } + const lines = addOsc8Hyperlinks(this.inner.render(width), this.urls).map( + isolateRtlRenderedLine, + ); + this.cachedRender = { width, lines }; + return lines; } setText(text: string): void { const displayText = sanitizeMarkdownDisplayText(text); this.inner.setText(displayText); this.urls = extractUrls(displayText); + this.cachedRender = undefined; } invalidate(): void { this.inner.invalidate(); + this.cachedRender = undefined; } }