@pentoshi/clai 3.11.4 → 3.11.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agent/runner.js +9 -6
- package/dist/agent/runner.js.map +1 -1
- package/dist/agent/tool-history.d.ts +6 -3
- package/dist/agent/tool-history.js +90 -11
- package/dist/agent/tool-history.js.map +1 -1
- package/dist/modes/ask.js +14 -11
- package/dist/modes/ask.js.map +1 -1
- package/dist/tui-v2/bootstrap/capabilities.d.ts +1 -0
- package/dist/tui-v2/bootstrap/capabilities.js +1 -1
- package/dist/tui-v2/bootstrap/capabilities.js.map +1 -1
- package/dist/tui-v2/rendering/streaming-markdown.js +11 -2
- package/dist/tui-v2/rendering/streaming-markdown.js.map +1 -1
- package/dist/ui/code-block.d.ts +40 -0
- package/dist/ui/code-block.js +380 -0
- package/dist/ui/code-block.js.map +1 -0
- package/dist/ui/markdown.js +55 -52
- package/dist/ui/markdown.js.map +1 -1
- package/dist/version.generated.d.ts +2 -2
- package/dist/version.generated.js +2 -2
- package/package.json +1 -1
|
@@ -0,0 +1,380 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fenced code block presentation for chat + pager markdown.
|
|
3
|
+
*
|
|
4
|
+
* Renders a bordered panel with a language label, real syntax colors, exact
|
|
5
|
+
* source indentation, and column-accurate soft wrapping (no ellipsis, no
|
|
6
|
+
* dropped characters, wide glyphs never straddle the right border).
|
|
7
|
+
*/
|
|
8
|
+
import chalk from "chalk";
|
|
9
|
+
import stringWidth from "string-width";
|
|
10
|
+
import { detectThemeHint } from "../tui-v2/bootstrap/capabilities.js";
|
|
11
|
+
import { themeFor } from "../tui-v2/rendering/theme.js";
|
|
12
|
+
import { emptyCarry, highlightLineForPath, } from "../tui-v2/rendering/syntax-highlight.js";
|
|
13
|
+
/** Columns consumed by `│ ` + body + ` │`. */
|
|
14
|
+
export const CODE_BLOCK_CHROME = 4;
|
|
15
|
+
const TAB_WIDTH = 2;
|
|
16
|
+
const HANGING_INDENT = 2;
|
|
17
|
+
/** Narrowest code body worth rendering — the panel is sized to protect it. */
|
|
18
|
+
const MIN_BODY = 8;
|
|
19
|
+
const MIN_WIDTH = CODE_BLOCK_CHROME + MIN_BODY;
|
|
20
|
+
const MAX_WIDTH = 120;
|
|
21
|
+
/** Only honour a soft break past this fraction of the row budget. */
|
|
22
|
+
const SOFT_BREAK_FLOOR = 0.5;
|
|
23
|
+
const GRAPHEME_SEGMENTER = new Intl.Segmenter(undefined, {
|
|
24
|
+
granularity: "grapheme",
|
|
25
|
+
});
|
|
26
|
+
/**
|
|
27
|
+
* Colors resolve from the shared theme tokens — the same syntax palette the
|
|
28
|
+
* diff cards use — against the terminal's light/dark hint, so a light terminal
|
|
29
|
+
* is never handed dark-tuned code colors.
|
|
30
|
+
*/
|
|
31
|
+
function buildPalette() {
|
|
32
|
+
const theme = themeFor(detectThemeHint(process.env));
|
|
33
|
+
return {
|
|
34
|
+
border: chalk.hex(theme.diffGutter),
|
|
35
|
+
label: chalk.hex(theme.muted),
|
|
36
|
+
syntax: {
|
|
37
|
+
plain: chalk.hex(theme.foreground),
|
|
38
|
+
keyword: chalk.hex(theme.synKeyword),
|
|
39
|
+
string: chalk.hex(theme.synString),
|
|
40
|
+
comment: chalk.hex(theme.synComment),
|
|
41
|
+
number: chalk.hex(theme.synNumber),
|
|
42
|
+
function: chalk.hex(theme.synFunction),
|
|
43
|
+
type: chalk.hex(theme.synType),
|
|
44
|
+
property: chalk.hex(theme.synProperty),
|
|
45
|
+
operator: chalk.hex(theme.synOperator),
|
|
46
|
+
punctuation: chalk.hex(theme.muted),
|
|
47
|
+
regex: chalk.hex(theme.synRegex),
|
|
48
|
+
},
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
let resolvedPalette;
|
|
52
|
+
function palette() {
|
|
53
|
+
resolvedPalette ??= buildPalette();
|
|
54
|
+
return resolvedPalette;
|
|
55
|
+
}
|
|
56
|
+
/** Fence info strings that are not already file extensions. */
|
|
57
|
+
const INFO_EXTENSION = {
|
|
58
|
+
typescript: "ts",
|
|
59
|
+
javascript: "js",
|
|
60
|
+
node: "js",
|
|
61
|
+
nodejs: "js",
|
|
62
|
+
react: "jsx",
|
|
63
|
+
python: "py",
|
|
64
|
+
python3: "py",
|
|
65
|
+
ipython: "py",
|
|
66
|
+
shell: "sh",
|
|
67
|
+
shellsession: "sh",
|
|
68
|
+
console: "sh",
|
|
69
|
+
terminal: "sh",
|
|
70
|
+
golang: "go",
|
|
71
|
+
rust: "rs",
|
|
72
|
+
ruby: "rb",
|
|
73
|
+
kotlin: "kt",
|
|
74
|
+
csharp: "cs",
|
|
75
|
+
"c#": "cs",
|
|
76
|
+
"c++": "cpp",
|
|
77
|
+
cplusplus: "cpp",
|
|
78
|
+
objc: "m",
|
|
79
|
+
"objective-c": "m",
|
|
80
|
+
haskell: "hs",
|
|
81
|
+
elixir: "ex",
|
|
82
|
+
erlang: "erl",
|
|
83
|
+
clojure: "clj",
|
|
84
|
+
scheme: "scm",
|
|
85
|
+
julia: "jl",
|
|
86
|
+
docker: "dockerfile",
|
|
87
|
+
make: "makefile",
|
|
88
|
+
terraform: "tf",
|
|
89
|
+
protobuf: "proto",
|
|
90
|
+
postgres: "sql",
|
|
91
|
+
postgresql: "sql",
|
|
92
|
+
plpgsql: "sql",
|
|
93
|
+
sqlite: "sql",
|
|
94
|
+
powershell: "ps1",
|
|
95
|
+
batch: "bat",
|
|
96
|
+
vim: "txt",
|
|
97
|
+
tex: "txt",
|
|
98
|
+
latex: "txt",
|
|
99
|
+
plaintext: "txt",
|
|
100
|
+
plain: "txt",
|
|
101
|
+
text: "txt",
|
|
102
|
+
output: "txt",
|
|
103
|
+
none: "txt",
|
|
104
|
+
};
|
|
105
|
+
/** Short fence tags shown under their full language name. */
|
|
106
|
+
const INFO_LABEL = {
|
|
107
|
+
ts: "typescript",
|
|
108
|
+
js: "javascript",
|
|
109
|
+
py: "python",
|
|
110
|
+
rb: "ruby",
|
|
111
|
+
rs: "rust",
|
|
112
|
+
kt: "kotlin",
|
|
113
|
+
cs: "c#",
|
|
114
|
+
cpp: "c++",
|
|
115
|
+
sh: "shell",
|
|
116
|
+
yml: "yaml",
|
|
117
|
+
md: "markdown",
|
|
118
|
+
ps1: "powershell",
|
|
119
|
+
ex: "elixir",
|
|
120
|
+
hs: "haskell",
|
|
121
|
+
jl: "julia",
|
|
122
|
+
clj: "clojure",
|
|
123
|
+
tf: "terraform",
|
|
124
|
+
};
|
|
125
|
+
const FENCE_OPEN_RE = /^\s*(`{3,}|~{3,})[ \t]*(.*)$/;
|
|
126
|
+
/** Match an opening fence and capture its marker + info string. */
|
|
127
|
+
export function matchCodeFenceOpen(line) {
|
|
128
|
+
const match = FENCE_OPEN_RE.exec(line);
|
|
129
|
+
if (!match)
|
|
130
|
+
return undefined;
|
|
131
|
+
return { marker: match[1], info: (match[2] ?? "").trim() };
|
|
132
|
+
}
|
|
133
|
+
/** A fence closes on a bare run of the same character, at least as long. */
|
|
134
|
+
export function isCodeFenceClose(line, marker) {
|
|
135
|
+
const match = /^\s*(`{3,}|~{3,})\s*$/.exec(line);
|
|
136
|
+
if (!match)
|
|
137
|
+
return false;
|
|
138
|
+
const run = match[1];
|
|
139
|
+
return run[0] === marker[0] && run.length >= marker.length;
|
|
140
|
+
}
|
|
141
|
+
function looksLikePath(info) {
|
|
142
|
+
if (info.includes("/") || info.includes("\\"))
|
|
143
|
+
return true;
|
|
144
|
+
return /^[\w.-]+\.[A-Za-z][\w]*$/.test(info) && !info.startsWith(".");
|
|
145
|
+
}
|
|
146
|
+
export function openCodeFence(marker, info) {
|
|
147
|
+
// Info strings carry attributes models pick up from docs: `ts {1,3}`,
|
|
148
|
+
// `py title="x"`. Only the first token identifies the language.
|
|
149
|
+
const token = (info.split(/[\s,;]+/)[0] ?? "").replace(/^[{"']|["']$/g, "");
|
|
150
|
+
const tag = token.toLowerCase();
|
|
151
|
+
if (looksLikePath(token)) {
|
|
152
|
+
return { marker, label: token, langPath: token, carry: emptyCarry(), pendingBlanks: 0 };
|
|
153
|
+
}
|
|
154
|
+
const extension = INFO_EXTENSION[tag] ?? tag;
|
|
155
|
+
return {
|
|
156
|
+
marker,
|
|
157
|
+
label: INFO_LABEL[tag] ?? (tag || "code"),
|
|
158
|
+
langPath: extension ? `code.${extension}` : "code.txt",
|
|
159
|
+
carry: emptyCarry(),
|
|
160
|
+
pendingBlanks: 0,
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
/** Clamp the panel to the wrap budget so it never overflows the pane. */
|
|
164
|
+
export function codeBlockWidth(wrapWidth) {
|
|
165
|
+
return Math.max(MIN_WIDTH, Math.min(Math.floor(wrapWidth), MAX_WIDTH));
|
|
166
|
+
}
|
|
167
|
+
function rule(count) {
|
|
168
|
+
return "─".repeat(Math.max(0, count));
|
|
169
|
+
}
|
|
170
|
+
function truncateLabel(label, maxWidth) {
|
|
171
|
+
if (maxWidth < 1)
|
|
172
|
+
return "";
|
|
173
|
+
let text = "";
|
|
174
|
+
let width = 0;
|
|
175
|
+
for (const cluster of graphemes(label)) {
|
|
176
|
+
const clusterWidth = stringWidth(cluster);
|
|
177
|
+
if (width + clusterWidth > maxWidth)
|
|
178
|
+
break;
|
|
179
|
+
text += cluster;
|
|
180
|
+
width += clusterWidth;
|
|
181
|
+
}
|
|
182
|
+
return text;
|
|
183
|
+
}
|
|
184
|
+
export function codeBlockTop(label, width) {
|
|
185
|
+
const { border, label: paintLabel } = palette();
|
|
186
|
+
const span = codeBlockWidth(width) - 2;
|
|
187
|
+
const room = span - 4;
|
|
188
|
+
const labelWidth = stringWidth(label);
|
|
189
|
+
const text = room < 1
|
|
190
|
+
? ""
|
|
191
|
+
: labelWidth <= room
|
|
192
|
+
? label
|
|
193
|
+
: `${truncateLabel(label, room - 1)}…`;
|
|
194
|
+
if (!text)
|
|
195
|
+
return border(`╭${rule(span)}╮`);
|
|
196
|
+
return (border("╭─") +
|
|
197
|
+
paintLabel(` ${text} `) +
|
|
198
|
+
border(`${rule(span - 3 - stringWidth(text))}╮`));
|
|
199
|
+
}
|
|
200
|
+
export function codeBlockBottom(width) {
|
|
201
|
+
return palette().border(`╰${rule(codeBlockWidth(width) - 2)}╯`);
|
|
202
|
+
}
|
|
203
|
+
/** Pad a painted body to the panel's inner width and add both borders. */
|
|
204
|
+
export function codeBlockRow(body, bodyWidth, width) {
|
|
205
|
+
const { border } = palette();
|
|
206
|
+
const inner = codeBlockWidth(width) - CODE_BLOCK_CHROME;
|
|
207
|
+
const pad = " ".repeat(Math.max(0, inner - bodyWidth));
|
|
208
|
+
return `${border("│")} ${body}${pad} ${border("│")}`;
|
|
209
|
+
}
|
|
210
|
+
function graphemes(text) {
|
|
211
|
+
return Array.from(GRAPHEME_SEGMENTER.segment(text), ({ segment }) => segment);
|
|
212
|
+
}
|
|
213
|
+
function expandTabs(line) {
|
|
214
|
+
if (!line.includes("\t"))
|
|
215
|
+
return line;
|
|
216
|
+
let out = "";
|
|
217
|
+
let col = 0;
|
|
218
|
+
for (const ch of graphemes(line)) {
|
|
219
|
+
if (ch === "\t") {
|
|
220
|
+
const gap = TAB_WIDTH - (col % TAB_WIDTH);
|
|
221
|
+
out += " ".repeat(gap);
|
|
222
|
+
col += gap;
|
|
223
|
+
continue;
|
|
224
|
+
}
|
|
225
|
+
out += ch;
|
|
226
|
+
col += stringWidth(ch);
|
|
227
|
+
}
|
|
228
|
+
return out;
|
|
229
|
+
}
|
|
230
|
+
function toCells(spans) {
|
|
231
|
+
const text = spans.map((span) => span.text).join("");
|
|
232
|
+
const kinds = [];
|
|
233
|
+
for (const span of spans) {
|
|
234
|
+
for (let i = 0; i < span.text.length; i += 1)
|
|
235
|
+
kinds.push(span.kind);
|
|
236
|
+
}
|
|
237
|
+
const cells = [];
|
|
238
|
+
for (const { segment, index } of GRAPHEME_SEGMENTER.segment(text)) {
|
|
239
|
+
cells.push({
|
|
240
|
+
ch: segment,
|
|
241
|
+
kind: kinds[index] ?? "plain",
|
|
242
|
+
w: stringWidth(segment),
|
|
243
|
+
});
|
|
244
|
+
}
|
|
245
|
+
return cells;
|
|
246
|
+
}
|
|
247
|
+
/** Break after separators so wrapped code splits at token edges when it can. */
|
|
248
|
+
function breaksAfter(ch) {
|
|
249
|
+
return ch === " " || ",;)]}>".includes(ch);
|
|
250
|
+
}
|
|
251
|
+
/**
|
|
252
|
+
* Split one highlighted source line into rows that fit its column budget.
|
|
253
|
+
* Breaks at a token edge when one falls in the back half of the row, otherwise
|
|
254
|
+
* hard-breaks. Separator cells remain in the output so wrapping never changes
|
|
255
|
+
* meaningful code whitespace. Always consumes at least one cell so a glyph
|
|
256
|
+
* wider than the budget cannot stall the loop.
|
|
257
|
+
*/
|
|
258
|
+
function sliceRows(cells, firstBudget, restBudget) {
|
|
259
|
+
const rows = [];
|
|
260
|
+
let start = 0;
|
|
261
|
+
while (start < cells.length) {
|
|
262
|
+
const budget = Math.max(1, rows.length === 0 ? firstBudget : restBudget);
|
|
263
|
+
const floor = budget * SOFT_BREAK_FLOOR;
|
|
264
|
+
let used = 0;
|
|
265
|
+
let end = start;
|
|
266
|
+
let soft = -1;
|
|
267
|
+
while (end < cells.length && used + cells[end].w <= budget) {
|
|
268
|
+
used += cells[end].w;
|
|
269
|
+
end += 1;
|
|
270
|
+
if (used >= floor && breaksAfter(cells[end - 1].ch))
|
|
271
|
+
soft = end;
|
|
272
|
+
}
|
|
273
|
+
if (end >= cells.length) {
|
|
274
|
+
rows.push(cells.slice(start));
|
|
275
|
+
return rows;
|
|
276
|
+
}
|
|
277
|
+
const cut = soft > start ? soft : Math.max(end, start + 1);
|
|
278
|
+
rows.push(cells.slice(start, cut));
|
|
279
|
+
start = cut;
|
|
280
|
+
}
|
|
281
|
+
return rows.length > 0 ? rows : [[]];
|
|
282
|
+
}
|
|
283
|
+
function paint(cells) {
|
|
284
|
+
const { syntax } = palette();
|
|
285
|
+
let text = "";
|
|
286
|
+
let width = 0;
|
|
287
|
+
let run = "";
|
|
288
|
+
let kind = "plain";
|
|
289
|
+
for (const cell of cells) {
|
|
290
|
+
if (cell.kind !== kind) {
|
|
291
|
+
if (run)
|
|
292
|
+
text += syntax[kind](run);
|
|
293
|
+
run = "";
|
|
294
|
+
kind = cell.kind;
|
|
295
|
+
}
|
|
296
|
+
run += cell.ch;
|
|
297
|
+
width += cell.w;
|
|
298
|
+
}
|
|
299
|
+
if (run)
|
|
300
|
+
text += syntax[kind](run);
|
|
301
|
+
return { text, width };
|
|
302
|
+
}
|
|
303
|
+
function leadingSpaces(line) {
|
|
304
|
+
const match = /^ */.exec(line);
|
|
305
|
+
return match ? match[0].length : 0;
|
|
306
|
+
}
|
|
307
|
+
/**
|
|
308
|
+
* Streaming re-renders the whole open fence on every frame, so highlighting +
|
|
309
|
+
* wrapping one source line is memoised on its language, width, and inbound
|
|
310
|
+
* carry. The carry after the line is replayed on a hit so multi-line strings
|
|
311
|
+
* and block comments still chain correctly.
|
|
312
|
+
*/
|
|
313
|
+
const ROW_CACHE = new Map();
|
|
314
|
+
const ROW_CACHE_MAX = 4096;
|
|
315
|
+
function carryKey(carry) {
|
|
316
|
+
return `${carry.inBlockComment ? 1 : 0}${carry.inTripleString ? 1 : 0}${carry.tripleQuote ?? ""}`;
|
|
317
|
+
}
|
|
318
|
+
function snapshotCarry(carry) {
|
|
319
|
+
return {
|
|
320
|
+
inBlockComment: carry.inBlockComment,
|
|
321
|
+
inTripleString: carry.inTripleString,
|
|
322
|
+
tripleQuote: carry.tripleQuote,
|
|
323
|
+
};
|
|
324
|
+
}
|
|
325
|
+
function restoreCarry(target, source) {
|
|
326
|
+
target.inBlockComment = source.inBlockComment;
|
|
327
|
+
target.inTripleString = source.inTripleString;
|
|
328
|
+
target.tripleQuote = source.tripleQuote;
|
|
329
|
+
}
|
|
330
|
+
function cacheRows(key, rows, carry) {
|
|
331
|
+
if (ROW_CACHE.size >= ROW_CACHE_MAX) {
|
|
332
|
+
const oldest = ROW_CACHE.keys().next();
|
|
333
|
+
if (!oldest.done)
|
|
334
|
+
ROW_CACHE.delete(oldest.value);
|
|
335
|
+
}
|
|
336
|
+
ROW_CACHE.set(key, { rows, carry: snapshotCarry(carry) });
|
|
337
|
+
}
|
|
338
|
+
/**
|
|
339
|
+
* Render one source line as complete panel rows. Blank lines are buffered so a
|
|
340
|
+
* block that ends with padding does not push empty rows against the footer.
|
|
341
|
+
*/
|
|
342
|
+
export function codeBlockRows(source, fence, width) {
|
|
343
|
+
const panel = codeBlockWidth(width);
|
|
344
|
+
const inner = panel - CODE_BLOCK_CHROME;
|
|
345
|
+
const line = expandTabs(source.replace(/\s+$/, ""));
|
|
346
|
+
// Leading blanks are dropped outright; interior ones survive as padding once
|
|
347
|
+
// a following content row proves they were not trailing.
|
|
348
|
+
if (line.length === 0) {
|
|
349
|
+
fence.pendingBlanks += 1;
|
|
350
|
+
return [];
|
|
351
|
+
}
|
|
352
|
+
const rows = [];
|
|
353
|
+
for (let i = 0; i < fence.pendingBlanks; i += 1) {
|
|
354
|
+
rows.push(codeBlockRow("", 0, panel));
|
|
355
|
+
}
|
|
356
|
+
fence.pendingBlanks = 0;
|
|
357
|
+
const key = `${panel}\u0000${fence.langPath}\u0000${carryKey(fence.carry)}\u0000${line}`;
|
|
358
|
+
const cached = ROW_CACHE.get(key);
|
|
359
|
+
if (cached) {
|
|
360
|
+
restoreCarry(fence.carry, cached.carry);
|
|
361
|
+
rows.push(...cached.rows);
|
|
362
|
+
return rows;
|
|
363
|
+
}
|
|
364
|
+
const cells = toCells(highlightLineForPath(line, fence.langPath, fence.carry));
|
|
365
|
+
// Continuation rows are indented under the source line so a wrap reads as a
|
|
366
|
+
// wrap. It never eats more than half the body, nor the minimum code columns,
|
|
367
|
+
// so `prefix + body` always fits `inner` and the right border stays flush.
|
|
368
|
+
const indent = Math.min(leadingSpaces(line) + HANGING_INDENT, Math.floor(inner / 2), Math.max(0, inner - MIN_BODY));
|
|
369
|
+
const hang = " ".repeat(indent);
|
|
370
|
+
const chunks = sliceRows(cells, inner, inner - indent);
|
|
371
|
+
const content = chunks.map((chunk, i) => {
|
|
372
|
+
const { text, width: bodyWidth } = paint(chunk);
|
|
373
|
+
const prefix = i === 0 ? "" : hang;
|
|
374
|
+
return codeBlockRow(prefix + text, prefix.length + bodyWidth, panel);
|
|
375
|
+
});
|
|
376
|
+
cacheRows(key, content, fence.carry);
|
|
377
|
+
rows.push(...content);
|
|
378
|
+
return rows;
|
|
379
|
+
}
|
|
380
|
+
//# sourceMappingURL=code-block.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"code-block.js","sourceRoot":"","sources":["../../src/ui/code-block.ts"],"names":[],"mappings":"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"}
|
package/dist/ui/markdown.js
CHANGED
|
@@ -1,26 +1,13 @@
|
|
|
1
1
|
import chalk from "chalk";
|
|
2
2
|
import stringWidth from "string-width";
|
|
3
|
+
import { codeBlockBottom, codeBlockRows, codeBlockTop, codeBlockWidth, isCodeFenceClose, matchCodeFenceOpen, openCodeFence, } from "./code-block.js";
|
|
3
4
|
// Lightweight terminal markdown renderer that styles **bold**, *italic*,
|
|
4
5
|
// `code`, links, headings, lists, blockquotes, hrules, and ```fenced```
|
|
5
6
|
// code blocks. Designed to work both for one-shot strings and for
|
|
6
7
|
// token-streaming inputs (line-buffered).
|
|
7
|
-
const FENCE_OPEN = chalk.dim;
|
|
8
|
-
const FENCE_LINE = chalk.cyan;
|
|
9
|
-
/** Continuation gutter for soft-wrapped code lines (keeps block readable). */
|
|
10
|
-
const FENCE_CONT = chalk.dim("│ ");
|
|
11
8
|
function repeat(char, count) {
|
|
12
9
|
return char.repeat(Math.max(0, count));
|
|
13
10
|
}
|
|
14
|
-
function renderFenceHeader(lang, width = 60) {
|
|
15
|
-
const label = lang || "code";
|
|
16
|
-
const head = `─── ${label} `;
|
|
17
|
-
const barWidth = Math.max(8, Math.min(width, 120));
|
|
18
|
-
return FENCE_OPEN(head + repeat("─", Math.max(0, barWidth - head.length)));
|
|
19
|
-
}
|
|
20
|
-
function renderFenceFooter(width = 60) {
|
|
21
|
-
const barWidth = Math.max(8, Math.min(width, 120));
|
|
22
|
-
return FENCE_OPEN(repeat("─", barWidth));
|
|
23
|
-
}
|
|
24
11
|
// Walk inline markdown tokens and convert them to ANSI styles. Designed to
|
|
25
12
|
// handle one logical line at a time (so it can run after a newline arrives
|
|
26
13
|
// in the token stream).
|
|
@@ -137,23 +124,34 @@ export function renderInlineMarkdown(text) {
|
|
|
137
124
|
}
|
|
138
125
|
return out;
|
|
139
126
|
}
|
|
127
|
+
const DEFAULT_FENCE_WIDTH = 60;
|
|
128
|
+
function panelWidth(state) {
|
|
129
|
+
return codeBlockWidth(state.fenceWidth ?? DEFAULT_FENCE_WIDTH);
|
|
130
|
+
}
|
|
131
|
+
function openFencePanel(state, marker, info) {
|
|
132
|
+
state.inFence = true;
|
|
133
|
+
state.fence = openCodeFence(marker, info);
|
|
134
|
+
return codeBlockTop(state.fence.label, panelWidth(state));
|
|
135
|
+
}
|
|
136
|
+
function closeFencePanel(state) {
|
|
137
|
+
state.inFence = false;
|
|
138
|
+
state.fence = undefined;
|
|
139
|
+
return codeBlockBottom(panelWidth(state));
|
|
140
|
+
}
|
|
141
|
+
/** Fence lines and code bodies as complete panel rows (may be empty). */
|
|
142
|
+
function fencePanelRows(line, state) {
|
|
143
|
+
if (state.inFence && state.fence) {
|
|
144
|
+
if (isCodeFenceClose(line, state.fence.marker)) {
|
|
145
|
+
return [closeFencePanel(state)];
|
|
146
|
+
}
|
|
147
|
+
return codeBlockRows(line, state.fence, panelWidth(state));
|
|
148
|
+
}
|
|
149
|
+
const open = matchCodeFenceOpen(line);
|
|
150
|
+
if (!open)
|
|
151
|
+
return undefined;
|
|
152
|
+
return [openFencePanel(state, open.marker, open.info)];
|
|
153
|
+
}
|
|
140
154
|
function renderBlockLine(line, state) {
|
|
141
|
-
const fenceWidth = state.fenceWidth ?? 60;
|
|
142
|
-
// Code fence open/close
|
|
143
|
-
const fenceMatch = line.match(/^(\s*)```(\w*)\s*(.*)$/);
|
|
144
|
-
if (fenceMatch) {
|
|
145
|
-
if (state.inFence) {
|
|
146
|
-
state.inFence = false;
|
|
147
|
-
state.fenceLang = "";
|
|
148
|
-
return renderFenceFooter(fenceWidth);
|
|
149
|
-
}
|
|
150
|
-
state.inFence = true;
|
|
151
|
-
state.fenceLang = fenceMatch[2] ?? "";
|
|
152
|
-
return renderFenceHeader(state.fenceLang, fenceWidth);
|
|
153
|
-
}
|
|
154
|
-
if (state.inFence) {
|
|
155
|
-
return FENCE_LINE(line);
|
|
156
|
-
}
|
|
157
155
|
// Headings
|
|
158
156
|
const heading = line.match(/^(#{1,6})\s+(.*)$/);
|
|
159
157
|
if (heading) {
|
|
@@ -560,19 +558,11 @@ export function indentAndWrapText(text, indent = " ") {
|
|
|
560
558
|
.join("\n");
|
|
561
559
|
}
|
|
562
560
|
function wrapMarkdownLine(line, wrapWidth, state) {
|
|
563
|
-
// Keep
|
|
561
|
+
// Keep panel chrome + code within wrapWidth (never truncate with …).
|
|
564
562
|
state.fenceWidth = wrapWidth;
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
// Soft-wrap long code lines so they stay inside the fence border.
|
|
569
|
-
// Never truncate with ellipsis — split by columns and keep every character.
|
|
570
|
-
if (state.inFence) {
|
|
571
|
-
// Budget 2 cols for the continuation gutter on wrap lines.
|
|
572
|
-
const budget = Math.max(10, wrapWidth - 2);
|
|
573
|
-
const chunks = wrapAnsiLine(line, budget);
|
|
574
|
-
return chunks.map((chunk, idx) => idx === 0 ? FENCE_LINE(chunk) : FENCE_CONT + FENCE_LINE(chunk));
|
|
575
|
-
}
|
|
563
|
+
const fenceRows = fencePanelRows(line, state);
|
|
564
|
+
if (fenceRows)
|
|
565
|
+
return fenceRows;
|
|
576
566
|
// If it's a horizontal rule, don't wrap it
|
|
577
567
|
if (/^\s*[-*_]{3,}\s*$/.test(line)) {
|
|
578
568
|
return [renderBlockLine(line, state)];
|
|
@@ -837,12 +827,12 @@ function renderTableBlock(rawLines, availWidth) {
|
|
|
837
827
|
export function renderMarkdown(text, width) {
|
|
838
828
|
if (!text)
|
|
839
829
|
return text;
|
|
840
|
-
const state = { inFence: false
|
|
830
|
+
const state = { inFence: false };
|
|
841
831
|
const lines = text.split("\n");
|
|
842
832
|
const resultLines = [];
|
|
843
833
|
const hasWidth = typeof width === "number";
|
|
844
834
|
const cols = width ?? (process.stdout.columns || 80);
|
|
845
|
-
const wrapWidth = hasWidth ? Math.max(
|
|
835
|
+
const wrapWidth = hasWidth ? Math.max(12, cols - 2) : Math.max(40, cols - 6);
|
|
846
836
|
let i = 0;
|
|
847
837
|
while (i < lines.length) {
|
|
848
838
|
const line = lines[i];
|
|
@@ -874,19 +864,31 @@ export function renderMarkdown(text, width) {
|
|
|
874
864
|
}
|
|
875
865
|
i++;
|
|
876
866
|
}
|
|
867
|
+
// A fence still open at the end means the reply is mid-stream or the model
|
|
868
|
+
// never closed it — footer it so the panel always reads as a finished box.
|
|
869
|
+
if (state.inFence) {
|
|
870
|
+
resultLines.push(`${OUTPUT_INDENT}${closeFencePanel(state)}`);
|
|
871
|
+
}
|
|
877
872
|
return resultLines.join("\n");
|
|
878
873
|
}
|
|
879
874
|
// Streaming variant: buffers tokens and emits ANSI-rendered output
|
|
880
|
-
// whenever a complete line arrives.
|
|
881
|
-
//
|
|
875
|
+
// whenever a complete line arrives. Fenced code blocks stream as a bordered,
|
|
876
|
+
// syntax-highlighted panel.
|
|
882
877
|
export function createMarkdownStreamWriter(write) {
|
|
883
|
-
const state = { inFence: false
|
|
878
|
+
const state = { inFence: false };
|
|
884
879
|
let buffer = "";
|
|
880
|
+
let outputEndsWithNewline = true;
|
|
885
881
|
// Table rows are buffered until the block ends so columns can be
|
|
886
882
|
// sized across every row before anything is emitted.
|
|
887
883
|
let tableBuffer = [];
|
|
888
884
|
const cols = process.stdout.columns || 80;
|
|
889
885
|
const wrapWidth = Math.max(40, cols - 6);
|
|
886
|
+
const emit = (chunk) => {
|
|
887
|
+
if (!chunk)
|
|
888
|
+
return;
|
|
889
|
+
write(chunk);
|
|
890
|
+
outputEndsWithNewline = chunk.endsWith("\n");
|
|
891
|
+
};
|
|
890
892
|
const emitLine = (line, withNewline) => {
|
|
891
893
|
const pieces = !state.inFence && BR_RE.test(line) ? line.split(BR_RE_GLOBAL) : [line];
|
|
892
894
|
for (let p = 0; p < pieces.length; p++) {
|
|
@@ -894,10 +896,10 @@ export function createMarkdownStreamWriter(write) {
|
|
|
894
896
|
const lastPiece = p === pieces.length - 1;
|
|
895
897
|
const physical = wrapMarkdownLine(piece, wrapWidth, state);
|
|
896
898
|
for (let q = 0; q < physical.length; q++) {
|
|
897
|
-
|
|
899
|
+
emit(`${OUTPUT_INDENT}${physical[q]}`);
|
|
898
900
|
const isVeryLast = lastPiece && q === physical.length - 1;
|
|
899
901
|
if (!isVeryLast || withNewline)
|
|
900
|
-
|
|
902
|
+
emit("\n");
|
|
901
903
|
}
|
|
902
904
|
}
|
|
903
905
|
};
|
|
@@ -909,7 +911,7 @@ export function createMarkdownStreamWriter(write) {
|
|
|
909
911
|
isTableSeparatorLine(tableBuffer[1]);
|
|
910
912
|
if (looksLikeTable) {
|
|
911
913
|
for (const rendered of renderTableBlock(tableBuffer, wrapWidth)) {
|
|
912
|
-
|
|
914
|
+
emit(`${OUTPUT_INDENT}${rendered}\n`);
|
|
913
915
|
}
|
|
914
916
|
}
|
|
915
917
|
else {
|
|
@@ -949,8 +951,9 @@ export function createMarkdownStreamWriter(write) {
|
|
|
949
951
|
}
|
|
950
952
|
flushTable();
|
|
951
953
|
if (state.inFence) {
|
|
952
|
-
//
|
|
953
|
-
|
|
954
|
+
// Close the panel without inserting a blank row after completed lines.
|
|
955
|
+
const separator = outputEndsWithNewline ? "" : "\n";
|
|
956
|
+
emit(`${separator}${OUTPUT_INDENT}${closeFencePanel(state)}`);
|
|
954
957
|
}
|
|
955
958
|
},
|
|
956
959
|
};
|