Skip to content

Commit 17b5213

Browse files
committed
fix(tui): pin native highlighting ownership
1 parent 14862c9 commit 17b5213

4 files changed

Lines changed: 70 additions & 8 deletions

File tree

‎src/tui/markdown-parser.test.ts‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -172,7 +172,8 @@ describe("block elements", () => {
172172
// The blank body line is not an empty segment array — it still paints ▏.
173173
const blankBody = lines.find(
174174
(line) =>
175-
line.map((s) => s.text).join("") === "▏ " &&
175+
line.length === 1 &&
176+
line[0]?.text === "▏ " &&
176177
line[0]?.codeFence === true,
177178
);
178179
expect(blankBody).toBeDefined();

‎src/tui/syntax-highlight.test.ts‎

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212
import { describe, expect, test } from "bun:test";
1313
import { MarkdownRenderable, RGBA, type CapturedSpan } from "@opentui/core";
1414
import { withTestRenderer, type Harness } from "./harness";
15+
import { appendStreamRow } from "./shell/chrome.js";
16+
import { createAppShell } from "./shell/index.js";
1517
import { transcriptSyntaxStyle } from "./stream";
1618
import { highlightCode } from "./syntax-highlight.js";
1719
import { UI } from "./theme";
@@ -69,6 +71,68 @@ function coloured(spans: CapturedSpan[], text: string, fg: RGBA): boolean {
6971
return spans.some((s) => s.text.includes(text) && s.fg.equals(fg));
7072
}
7173

74+
function hasSyntaxColour(spans: CapturedSpan[], text: string): boolean {
75+
return spans.some(
76+
(span) =>
77+
span.text.includes(text) && SYNTAX_FGS.some((fg) => span.fg.equals(fg)),
78+
);
79+
}
80+
81+
async function expectTranscriptHighlighting(
82+
h: Harness,
83+
supportedLanguage: "js" | "ts",
84+
streaming: boolean,
85+
): Promise<void> {
86+
const shell = createAppShell(h.renderer, {
87+
terminal: { columns: 80, rows: 40 },
88+
wireKeys: false,
89+
});
90+
try {
91+
appendStreamRow(shell, {
92+
role: "assistant",
93+
streaming,
94+
text: [
95+
`\`\`\`${supportedLanguage}`,
96+
"const supported_route = 42;",
97+
"```",
98+
"",
99+
"```python",
100+
"def python_plain():",
101+
' return "visible"',
102+
"```",
103+
].join("\n"),
104+
});
105+
const spans = await settleSpans(
106+
h,
107+
(current) =>
108+
coloured(current, "const", KEYWORD_FG) &&
109+
current.some((span) => span.text.includes("python_plain")),
110+
);
111+
112+
expect(coloured(spans, "const", KEYWORD_FG)).toBe(true);
113+
expect(h.captureCharFrame()).toContain("python_plain");
114+
expect(hasSyntaxColour(spans, "python_plain")).toBe(false);
115+
} finally {
116+
shell.dispose();
117+
}
118+
}
119+
120+
describe("production transcript fenced rendering", () => {
121+
test("settled assistant fences use production syntax ownership", async () => {
122+
await withTestRenderer(
123+
(h) => expectTranscriptHighlighting(h, "js", false),
124+
{ width: 80, height: 40 },
125+
);
126+
}, 30000);
127+
128+
test("streaming assistant fences use production syntax ownership", async () => {
129+
await withTestRenderer((h) => expectTranscriptHighlighting(h, "ts", true), {
130+
width: 80,
131+
height: 40,
132+
});
133+
}, 30000);
134+
});
135+
72136
describe("native fenced rendering", () => {
73137
test("a js fence colours keywords, strings, comments, and numbers", async () => {
74138
await withTestRenderer(

‎src/tui/syntax-highlight.ts‎

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
/// <reference lib="dom" />
2-
31
/**
42
* Fenced-code styling for the synchronous markdown model.
53
*
@@ -8,14 +6,13 @@
86
* MarkdownRenderable/CodeRenderable path with transcriptSyntaxStyle() over the
97
* bundled tree-sitter grammars (javascript, typescript, markdown, zig). This
108
* module keeps the legacy path's geometry with plain segments.
11-
*
12-
* The DOM lib reference replaces the implicit reference highlight.js supplied;
13-
* Bun's fetch, Web Crypto, and timer types rely on those globals repo-wide.
149
*/
1510
import type { StyledSegment } from "./markdown-parser.js";
1611

1712
function plainLines(code: string): StyledSegment[][] {
18-
return code.split("\n").map((text) => [{ text, code: true }]);
13+
return code
14+
.split("\n")
15+
.map((text) => (text.length === 0 ? [] : [{ text, code: true }]));
1916
}
2017

2118
const codeCache = new Map<string, StyledSegment[][]>();

‎tsconfig.base.json‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,6 @@
2323
"module": "ESNext",
2424
"noEmit": true,
2525

26-
"lib": ["ESNext"]
26+
"lib": ["ESNext", "DOM"]
2727
}
2828
}

0 commit comments

Comments
 (0)