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 <amp@ampcode.com>
This commit is contained in:
Peter Steinberger
2026-08-21 20:37:50 -07:00
committed by GitHub
parent 575146a0a9
commit cf5f4871e8
2 changed files with 35 additions and 1 deletions
@@ -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"),
+11 -1
View File
@@ -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;
}
}