1ch 0.1.0

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/index.js ADDED
@@ -0,0 +1,3283 @@
1
+ // src/termui/table-tag.ts
2
+ var TABLE_TAG = /* @__PURE__ */ Symbol("tableLine");
3
+ function toTaggedLine(line) {
4
+ return line;
5
+ }
6
+ function markTableLine(line) {
7
+ toTaggedLine(line)[TABLE_TAG] = true;
8
+ return line;
9
+ }
10
+ function isTableLine(line) {
11
+ return toTaggedLine(line)[TABLE_TAG] === true;
12
+ }
13
+ function preserveTableTag(source, target) {
14
+ if (toTaggedLine(source)[TABLE_TAG]) toTaggedLine(target)[TABLE_TAG] = true;
15
+ }
16
+
17
+ // src/termui/base.ts
18
+ function normalizedWidth(width) {
19
+ if (!Number.isFinite(width)) return 0;
20
+ return Math.max(0, Math.floor(width));
21
+ }
22
+
23
+ // src/termui/text.ts
24
+ function pad(s, w, right) {
25
+ if (s.length >= w) return s.slice(0, w);
26
+ const space = " ".repeat(w - s.length);
27
+ return right ? space + s : s + space;
28
+ }
29
+ function hl(n) {
30
+ return "\u2500".repeat(normalizedWidth(n));
31
+ }
32
+ function bar(val, max, w) {
33
+ const width = normalizedWidth(w);
34
+ if (width <= 0) return "";
35
+ if (!Number.isFinite(max) || max <= 0) return "\u2591".repeat(width);
36
+ const ratio = Number.isFinite(val) ? val / max : 0;
37
+ const filled = Math.round(Math.min(1, Math.max(0, ratio)) * width);
38
+ return "\u2588".repeat(filled) + "\u2591".repeat(width - filled);
39
+ }
40
+ function spark(data, w) {
41
+ const width = normalizedWidth(w);
42
+ if (width <= 0 || data.length === 0) return "";
43
+ const ticks = "\u2581\u2582\u2583\u2584\u2585\u2586\u2587\u2588";
44
+ const v = data.slice(-width);
45
+ const mn = Math.min(...v);
46
+ const mx = Math.max(...v);
47
+ const rg = mx - mn || 1;
48
+ return v.map((x) => ticks[Math.round((x - mn) / rg * 7)]).join("");
49
+ }
50
+ function segLen(seg) {
51
+ return typeof seg === "string" ? seg.length : seg.text.length;
52
+ }
53
+ function lineLen(line) {
54
+ let n = 0;
55
+ for (const seg of line) n += segLen(seg);
56
+ return n;
57
+ }
58
+ function sliceSeg(seg, end) {
59
+ if (end <= 0) return null;
60
+ if (typeof seg === "string") {
61
+ const text2 = seg.slice(0, end);
62
+ return text2.length > 0 ? text2 : null;
63
+ }
64
+ const text = seg.text.slice(0, end);
65
+ if (text.length === 0) return null;
66
+ return { ...seg, text };
67
+ }
68
+ function truncateLine(line, w) {
69
+ if (w <= 0) return [];
70
+ const out = [];
71
+ let remaining = w;
72
+ for (const seg of line) {
73
+ if (remaining <= 0) break;
74
+ const len = segLen(seg);
75
+ if (len <= remaining) {
76
+ out.push(seg);
77
+ remaining -= len;
78
+ continue;
79
+ }
80
+ const clipped = sliceSeg(seg, remaining);
81
+ if (clipped) out.push(clipped);
82
+ remaining = 0;
83
+ }
84
+ return out;
85
+ }
86
+ function padLine(line, w) {
87
+ if (w <= 0) return [];
88
+ const used = lineLen(line);
89
+ if (used > w) return truncateLine(line, w);
90
+ if (used === w) return line;
91
+ return [...line, { text: " ".repeat(w - used), noSelect: true }];
92
+ }
93
+ function wrapText(text, w) {
94
+ if (w <= 0) return [""];
95
+ if (text.length <= w) return [text];
96
+ const result = [];
97
+ let pos = 0;
98
+ while (pos < text.length) {
99
+ if (pos + w >= text.length) {
100
+ result.push(text.slice(pos));
101
+ break;
102
+ }
103
+ let breakAt = text.lastIndexOf(" ", pos + w);
104
+ if (breakAt <= pos) {
105
+ result.push(text.slice(pos, pos + w));
106
+ pos += w;
107
+ } else {
108
+ result.push(text.slice(pos, breakAt));
109
+ pos = breakAt + 1;
110
+ }
111
+ }
112
+ return result.length > 0 ? result : [""];
113
+ }
114
+
115
+ // src/termui/layout.ts
116
+ function borderTop(w, title, color) {
117
+ const width = normalizedWidth(w);
118
+ if (width <= 0) return [];
119
+ if (width === 1) return [{ text: "\u250C", color, noSelect: true }];
120
+ if (title) {
121
+ const maxTitle = Math.max(0, width - 6);
122
+ const safeTitle = title.length > maxTitle ? title.slice(0, maxTitle) : title;
123
+ const titleStr = "\u2500 " + safeTitle + " ";
124
+ const remaining = Math.max(0, width - 2 - titleStr.length);
125
+ return [
126
+ { text: "\u250C" + titleStr + hl(remaining) + "\u2510", color, noSelect: true }
127
+ ];
128
+ }
129
+ return [{ text: "\u250C" + hl(width - 2) + "\u2510", color, noSelect: true }];
130
+ }
131
+ function borderBottom(w, color) {
132
+ const width = normalizedWidth(w);
133
+ if (width <= 0) return [];
134
+ if (width === 1) return [{ text: "\u2514", color, noSelect: true }];
135
+ return [{ text: "\u2514" + hl(width - 2) + "\u2518", color, noSelect: true }];
136
+ }
137
+ function lines(content) {
138
+ return (w) => {
139
+ const ls = typeof content === "function" ? content(w) : content;
140
+ return ls.map((line) => padLine(line, w));
141
+ };
142
+ }
143
+ function box(content, opts) {
144
+ return (w) => {
145
+ const width = normalizedWidth(w);
146
+ if (width < 2) return content(width);
147
+ const bc = opts?.borderColor;
148
+ const inner = width - 2;
149
+ const contentBlock = content(inner);
150
+ const result = [];
151
+ result.push(borderTop(width, opts?.title, bc));
152
+ for (const line of contentBlock) {
153
+ result.push([
154
+ { text: "\u2502", color: bc, noSelect: true },
155
+ ...padLine(line, inner),
156
+ { text: "\u2502", color: bc, noSelect: true }
157
+ ]);
158
+ }
159
+ result.push(borderBottom(width, bc));
160
+ return result;
161
+ };
162
+ }
163
+ function hstack(children, opts) {
164
+ return (w) => {
165
+ const width = normalizedWidth(w);
166
+ const n = children.length;
167
+ if (n === 0) return [];
168
+ const gap = normalizedWidth(opts?.gap ?? 1);
169
+ const totalGap = gap * (n - 1);
170
+ let childWidths;
171
+ if (opts?.widths && opts.widths.length === n) {
172
+ childWidths = opts.widths.map((cw) => normalizedWidth(cw));
173
+ } else {
174
+ const available = Math.max(0, width - totalGap);
175
+ const base = Math.floor(available / n);
176
+ const remainder = available - base * n;
177
+ childWidths = children.map((_, i) => base + (i < remainder ? 1 : 0));
178
+ }
179
+ const blocks = children.map((child, i) => child(childWidths[i]));
180
+ const maxLines = Math.max(...blocks.map((b) => b.length));
181
+ const gapStr = gap > 0 ? " ".repeat(gap) : "";
182
+ const result = [];
183
+ for (let i = 0; i < maxLines; i++) {
184
+ const line = [];
185
+ for (let j = 0; j < blocks.length; j++) {
186
+ if (j > 0 && gapStr) line.push(gapStr);
187
+ const childLine = blocks[j][i];
188
+ if (childLine) {
189
+ line.push(...padLine(childLine, childWidths[j]));
190
+ } else {
191
+ line.push(" ".repeat(childWidths[j]));
192
+ }
193
+ }
194
+ result.push(line);
195
+ }
196
+ return result;
197
+ };
198
+ }
199
+ function vstack(...children) {
200
+ return (w) => {
201
+ const result = [];
202
+ for (const child of children) {
203
+ result.push(...child(w));
204
+ }
205
+ return result;
206
+ };
207
+ }
208
+ function separator(color) {
209
+ return (w) => [[{ text: hl(w), color, noSelect: true }]];
210
+ }
211
+ function blank() {
212
+ return (w) => [[" ".repeat(normalizedWidth(w))]];
213
+ }
214
+
215
+ // src/termui/table.ts
216
+ function tableSep(ws, l, m, r, color) {
217
+ const line = [{ text: l, color, noSelect: true }];
218
+ for (let i = 0; i < ws.length; i++) {
219
+ line.push({ text: hl(ws[i]), color, noSelect: true, cell: i });
220
+ line.push({ text: i < ws.length - 1 ? m : r, color, noSelect: true });
221
+ }
222
+ return line;
223
+ }
224
+ function tableTop(ws, color) {
225
+ return tableSep(ws, "\u250C", "\u252C", "\u2510", color);
226
+ }
227
+ function tableMid(ws, color) {
228
+ return tableSep(ws, "\u251C", "\u253C", "\u2524", color);
229
+ }
230
+ function tableBot(ws, color) {
231
+ return tableSep(ws, "\u2514", "\u2534", "\u2518", color);
232
+ }
233
+ function table(columns, data, opts) {
234
+ return (w) => {
235
+ const bc = opts?.borderColor;
236
+ const hc = opts?.headerColor;
237
+ const cp = normalizedWidth(opts?.cellPadding ?? 1);
238
+ const nSep = columns.length + 1;
239
+ const minColWidth = 1;
240
+ const widthBudget = Math.max(0, normalizedWidth(w) - nSep);
241
+ const padStr = " ".repeat(cp);
242
+ const ws = columns.map((col) => {
243
+ if (col.width) return Math.max(minColWidth, col.width);
244
+ const displayTitle = col.title ?? col.key;
245
+ const titleW = displayTitle.length + 2 * cp;
246
+ const dataW = Math.max(
247
+ ...data.map((row) => String(row[col.key] ?? "").length + 2 * cp),
248
+ 0
249
+ );
250
+ return Math.max(minColWidth, titleW, dataW);
251
+ });
252
+ if (opts?.fillWidth && widthBudget > 0) {
253
+ const used = ws.reduce((a, b) => a + b, 0);
254
+ const remaining = widthBudget - used;
255
+ if (remaining > 0 && ws.length > 0) {
256
+ const even = Math.floor(remaining / ws.length);
257
+ const rem = remaining % ws.length;
258
+ for (let i = 0; i < ws.length; i++) {
259
+ ws[i] = ws[i] + even + (i < rem ? 1 : 0);
260
+ }
261
+ }
262
+ }
263
+ if (widthBudget > 0) {
264
+ let used = ws.reduce((a, b) => a + b, 0);
265
+ let overflow = used - widthBudget;
266
+ while (overflow > 0) {
267
+ let idx = -1;
268
+ let widest = 0;
269
+ for (let i = 0; i < ws.length; i++) {
270
+ if (ws[i] > minColWidth && ws[i] > widest) {
271
+ widest = ws[i];
272
+ idx = i;
273
+ }
274
+ }
275
+ if (idx === -1) break;
276
+ ws[idx] = ws[idx] - 1;
277
+ overflow -= 1;
278
+ }
279
+ }
280
+ const result = [];
281
+ result.push(tableTop(ws, bc));
282
+ const headerLine = [{ text: "\u2502", color: bc, noSelect: true }];
283
+ for (let i = 0; i < columns.length; i++) {
284
+ const col = columns[i];
285
+ const displayTitle = col.title ?? col.key;
286
+ const innerW = Math.max(0, ws[i] - 2 * cp);
287
+ const text = padStr + pad(displayTitle, innerW, col.align === "right") + padStr;
288
+ headerLine.push({
289
+ text,
290
+ color: col.headerColor ?? hc,
291
+ bold: col.headerBold ?? true,
292
+ cell: i
293
+ });
294
+ headerLine.push({ text: "\u2502", color: bc, noSelect: true });
295
+ }
296
+ result.push(headerLine);
297
+ result.push(tableMid(ws, bc));
298
+ for (const row of data) {
299
+ const wrapped = columns.map((col, ci) => {
300
+ const val = String(row[col.key] ?? "");
301
+ const innerW = Math.max(1, ws[ci] - 2 * cp);
302
+ return wrapText(val, innerW);
303
+ });
304
+ const rowHeight = Math.max(...wrapped.map((c) => c.length), 1);
305
+ for (let ln = 0; ln < rowHeight; ln++) {
306
+ const dataLine = [{ text: "\u2502", color: bc, noSelect: true }];
307
+ for (let i = 0; i < columns.length; i++) {
308
+ const col = columns[i];
309
+ const cellLine = wrapped[i][ln] ?? "";
310
+ const innerW = Math.max(0, ws[i] - 2 * cp);
311
+ const text = padStr + pad(cellLine, innerW, col.align === "right") + padStr;
312
+ const color = typeof col.color === "function" ? col.color(row[col.key], row) : col.color;
313
+ const dim = typeof col.dim === "function" ? col.dim(row[col.key], row) : col.dim;
314
+ dataLine.push({ text, color: color || void 0, dim: dim || void 0, cell: i });
315
+ dataLine.push({ text: "\u2502", color: bc, noSelect: true });
316
+ }
317
+ result.push(dataLine);
318
+ }
319
+ }
320
+ result.push(tableBot(ws, bc));
321
+ for (const line of result) markTableLine(line);
322
+ return result;
323
+ };
324
+ }
325
+
326
+ // src/termui/widgets.ts
327
+ function statusbar(left, right, opts) {
328
+ return (w) => {
329
+ const rightLen = right.reduce(
330
+ (acc, s) => acc + (typeof s === "string" ? s.length : s.text.length),
331
+ 0
332
+ );
333
+ const leftWidth = Math.max(0, w - rightLen);
334
+ const paddedLeft = padLine(left, leftWidth);
335
+ const theme = opts?.theme;
336
+ const bg = opts?.bg ?? theme?.components.statusbar.bg;
337
+ const color = opts?.color ?? theme?.components.statusbar.fg;
338
+ const line = [...paddedLeft, ...right].map((seg) => {
339
+ if (typeof seg === "string") {
340
+ return bg || color ? { text: seg, bg, color } : seg;
341
+ }
342
+ return { ...seg, bg: seg.bg ?? bg, color: seg.color ?? color };
343
+ });
344
+ return [padLine(line, w)];
345
+ };
346
+ }
347
+ function tabs(names, active, opts) {
348
+ return (w) => {
349
+ const line = [];
350
+ const onSelect = opts?.onSelect;
351
+ const t = opts?.theme;
352
+ for (let i = 0; i < names.length; i++) {
353
+ const isActive = i === active;
354
+ const name = names[i];
355
+ const label = " " + (isActive ? name.toUpperCase() : name) + " ";
356
+ const onClick = opts?.onClicks?.[i] ?? (onSelect ? () => onSelect(i) : void 0);
357
+ if (isActive) {
358
+ line.push({
359
+ text: label,
360
+ color: opts?.activeColor ?? t?.components.tabs.activeFg,
361
+ bg: opts?.activeBg ?? t?.components.tabs.activeBg,
362
+ onClick
363
+ });
364
+ } else {
365
+ line.push({
366
+ text: label,
367
+ color: opts?.inactiveColor ?? t?.components.tabs.inactiveFg,
368
+ onClick
369
+ });
370
+ }
371
+ if (i < names.length - 1) {
372
+ line.push({ text: "\u2502", color: opts?.separatorColor ?? t?.components.tabs.separator, noSelect: true });
373
+ }
374
+ }
375
+ return [padLine(line, w)];
376
+ };
377
+ }
378
+ function badge(label, opts) {
379
+ const t = opts?.theme;
380
+ return {
381
+ text: label,
382
+ color: opts?.color ?? t?.components.badge.fg,
383
+ bg: opts?.bg ?? t?.components.badge.bg,
384
+ onClick: opts?.onClick
385
+ };
386
+ }
387
+
388
+ // src/termui/json.ts
389
+ function fromJsonNode(node) {
390
+ switch (node.type) {
391
+ case "vstack":
392
+ return vstack(...node.children.map(fromJsonNode));
393
+ case "hstack":
394
+ return hstack(node.children.map(fromJsonNode), {
395
+ gap: node.gap,
396
+ widths: node.widths
397
+ });
398
+ case "box":
399
+ return box(vstack(...node.children.map(fromJsonNode)), {
400
+ title: node.title,
401
+ borderColor: node.borderColor
402
+ });
403
+ case "lines":
404
+ return lines(node.content);
405
+ case "separator":
406
+ return separator(node.color);
407
+ case "blank":
408
+ return blank();
409
+ case "table":
410
+ return table(node.columns, node.data, {
411
+ borderColor: node.borderColor,
412
+ fillWidth: node.fillWidth
413
+ });
414
+ case "statusbar":
415
+ return statusbar(node.left, node.right, {
416
+ bg: node.bg,
417
+ color: node.color
418
+ });
419
+ case "tabs":
420
+ return tabs(node.names, node.active, {
421
+ activeColor: node.activeColor,
422
+ activeBg: node.activeBg,
423
+ inactiveColor: node.inactiveColor,
424
+ separatorColor: node.separatorColor
425
+ });
426
+ case "bar":
427
+ return (w) => {
428
+ const labelStr = node.label ? " " + node.label : "";
429
+ const barW = Math.max(0, w - 1 - labelStr.length);
430
+ return [[" ", { text: bar(node.value, node.max, barW), color: node.color }, labelStr]];
431
+ };
432
+ case "text":
433
+ return lines([node.segments]);
434
+ }
435
+ }
436
+
437
+ // src/termui/tree-list.ts
438
+ function tree(data, opts) {
439
+ return (w) => {
440
+ const result = [];
441
+ function walk(nodes, prefix) {
442
+ for (let i = 0; i < nodes.length; i++) {
443
+ const node = nodes[i];
444
+ const last = i === nodes.length - 1;
445
+ const hasKids = node.children && node.children.length > 0;
446
+ const connector = last ? "\u2514\u2500\u2500 " : "\u251C\u2500\u2500 ";
447
+ const line = [];
448
+ if (prefix) line.push({ text: prefix, color: opts?.branchColor ?? opts?.color, noSelect: true });
449
+ line.push({ text: connector, color: opts?.branchColor ?? opts?.color, noSelect: true });
450
+ line.push({ text: node.label, color: hasKids ? opts?.color : opts?.leafColor ?? opts?.color });
451
+ result.push(padLine(line, w));
452
+ if (hasKids) {
453
+ walk(node.children, prefix + (last ? " " : "\u2502 "));
454
+ }
455
+ }
456
+ }
457
+ walk(data, "");
458
+ return result;
459
+ };
460
+ }
461
+ function list(items, opts) {
462
+ return (w) => {
463
+ const result = [];
464
+ const ordered = opts?.ordered ?? false;
465
+ const digits = String(items.length).length;
466
+ for (let i = 0; i < items.length; i++) {
467
+ const prefix = ordered ? ` ${String(i + 1).padStart(digits, " ")}. ` : " * ";
468
+ const indent = " ".repeat(prefix.length);
469
+ const wrapped = wrapText(items[i], w - prefix.length);
470
+ for (let j = 0; j < wrapped.length; j++) {
471
+ const line = [];
472
+ if (j === 0) {
473
+ line.push({ text: prefix, color: opts?.bulletColor });
474
+ } else {
475
+ line.push(indent);
476
+ }
477
+ line.push(opts?.color ? { text: wrapped[j], color: opts.color } : wrapped[j]);
478
+ result.push(padLine(line, w));
479
+ }
480
+ }
481
+ return result;
482
+ };
483
+ }
484
+
485
+ // src/tokenize.ts
486
+ function rules(pairs) {
487
+ return pairs.map(([pattern, type]) => ({
488
+ pattern: new RegExp(pattern.source, "y"),
489
+ type
490
+ }));
491
+ }
492
+ var JS_RULES = rules([
493
+ [/\/\/.*/, "comment"],
494
+ [/\/\*[\s\S]*?\*\//, "comment"],
495
+ [/"(?:[^"\\]|\\.)*"/, "string"],
496
+ [/'(?:[^'\\]|\\.)*'/, "string"],
497
+ [/`(?:[^`\\]|\\.)*`/, "string"],
498
+ [/\b(?:const|let|var|function|return|if|else|for|while|do|class|import|export|from|async|await|new|throw|try|catch|finally|typeof|instanceof|in|of|void|delete|switch|case|break|continue|default|yield|extends|implements|interface|type|as|this|super|null|undefined|true|false)\b/, "keyword"],
499
+ [/\b\d[\d_.]*(?:\.\d[\d_]*)?(?:[eE][+-]?\d+)?\b/, "number"],
500
+ [/\b[A-Z][a-zA-Z0-9]*\b/, "type"],
501
+ [/\b[a-zA-Z_$][\w$]*(?=\s*\()/, "function"],
502
+ [/=>/, "punctuation"],
503
+ [/[{}()\[\];:.,<>=!+\-*/%&|?@^~]/, "punctuation"]
504
+ ]);
505
+ var JSON_RULES = rules([
506
+ [/"(?:[^"\\]|\\.)*"(?=\s*:)/, "function"],
507
+ [/"(?:[^"\\]|\\.)*"/, "string"],
508
+ [/\b(?:true|false|null)\b/, "keyword"],
509
+ [/-?\b\d[\d_.]*(?:\.\d[\d_]*)?(?:[eE][+-]?\d+)?\b/, "number"],
510
+ [/[{}\[\]:,]/, "punctuation"]
511
+ ]);
512
+ var BASH_RULES = rules([
513
+ [/#.*/, "comment"],
514
+ [/"(?:[^"\\]|\\.)*"/, "string"],
515
+ [/'[^']*'/, "string"],
516
+ [/\$\{[^}]*\}/, "type"],
517
+ [/\$[A-Za-z_]\w*/, "type"],
518
+ [/\b(?:if|then|else|elif|fi|for|do|done|while|until|case|esac|function|in|return|exit|echo|export|local|readonly|source|alias|cd|ls|rm|cp|mv|mkdir|grep|sed|awk|cat|chmod|chown|sudo|apt|brew|npm|npx|bun|yarn|git|docker|curl|wget|pipe)\b/, "keyword"],
519
+ [/\b\d+\b/, "number"],
520
+ [/[|;&<>()\[\]{}]/, "punctuation"]
521
+ ]);
522
+ var GENERIC_RULES = rules([
523
+ [/\/\/.*/, "comment"],
524
+ [/#.*/, "comment"],
525
+ [/"(?:[^"\\]|\\.)*"/, "string"],
526
+ [/'(?:[^'\\]|\\.)*'/, "string"],
527
+ [/\b\d[\d_.]*(?:\.\d[\d_]*)?(?:[eE][+-]?\d+)?\b/, "number"],
528
+ [/\b[A-Z][a-zA-Z0-9]*\b/, "type"],
529
+ [/[{}()\[\];:.,<>=!+\-*/%&|?@^~]/, "punctuation"]
530
+ ]);
531
+ function rulesFor(lang) {
532
+ if (!lang) return GENERIC_RULES;
533
+ const l = lang.toLowerCase();
534
+ if (l === "json" || l === "jsonc") return JSON_RULES;
535
+ if (l === "bash" || l === "sh" || l === "zsh" || l === "shell") return BASH_RULES;
536
+ if (l === "js" || l === "jsx" || l === "ts" || l === "tsx" || l === "javascript" || l === "typescript") return JS_RULES;
537
+ return GENERIC_RULES;
538
+ }
539
+ function tokenizeLine(line, langRules) {
540
+ const tokens = [];
541
+ let pos = 0;
542
+ while (pos < line.length) {
543
+ if (line[pos] === " " || line[pos] === " ") {
544
+ let ws = pos;
545
+ while (ws < line.length && (line[ws] === " " || line[ws] === " ")) ws++;
546
+ tokens.push({ text: line.slice(pos, ws), type: "default" });
547
+ pos = ws;
548
+ continue;
549
+ }
550
+ let matched = false;
551
+ for (const rule of langRules) {
552
+ rule.pattern.lastIndex = pos;
553
+ const m = rule.pattern.exec(line);
554
+ if (m && m.index === pos) {
555
+ tokens.push({ text: m[0], type: rule.type });
556
+ pos += m[0].length;
557
+ matched = true;
558
+ break;
559
+ }
560
+ }
561
+ if (!matched) {
562
+ const start = pos;
563
+ pos++;
564
+ while (pos < line.length && line[pos] !== " " && line[pos] !== " ") {
565
+ let anyMatch = false;
566
+ for (const rule of langRules) {
567
+ rule.pattern.lastIndex = pos;
568
+ const m = rule.pattern.exec(line);
569
+ if (m && m.index === pos) {
570
+ anyMatch = true;
571
+ break;
572
+ }
573
+ }
574
+ if (anyMatch) break;
575
+ pos++;
576
+ }
577
+ tokens.push({ text: line.slice(start, pos), type: "default" });
578
+ }
579
+ }
580
+ return tokens;
581
+ }
582
+ function tokenize(source, language) {
583
+ const langRules = rulesFor(language);
584
+ return source.split("\n").map((line) => tokenizeLine(line, langRules));
585
+ }
586
+
587
+ // src/theme.ts
588
+ var DARK_PALETTE = {
589
+ black: "#0c0c0c",
590
+ red: "#e06c75",
591
+ green: "#98c379",
592
+ yellow: "#e5c07b",
593
+ blue: "#61afef",
594
+ magenta: "#c678dd",
595
+ cyan: "#56b6c2",
596
+ white: "#cccccc",
597
+ brightBlack: "#555555",
598
+ brightWhite: "#ffffff"
599
+ };
600
+ var DARK_SEMANTIC = {
601
+ frameBg: "#0c0c0c",
602
+ frameFg: "#cccccc",
603
+ frameMuted: "#555555",
604
+ border: "#555555",
605
+ borderStrong: "#808080",
606
+ inverseFg: "#0c0c0c",
607
+ inverseBg: "#ffffff",
608
+ info: "#56b6c2",
609
+ success: "#98c379",
610
+ warn: "#e5c07b",
611
+ danger: "#e06c75"
612
+ };
613
+ var DARK_SYNTAX = {
614
+ default: "#ffffff",
615
+ keyword: "#c678dd",
616
+ string: "#98c379",
617
+ number: "#e5c07b",
618
+ comment: "#555555",
619
+ punctuation: "#555555",
620
+ function: "#61afef",
621
+ type: "#56b6c2"
622
+ };
623
+ var DARK_MARKDOWN = {
624
+ headingMarker: "#555555",
625
+ headingBoldMaxLevel: 3,
626
+ headings: {
627
+ h1: "#e5c07b",
628
+ h2: "#56b6c2",
629
+ h3: "#98c379",
630
+ h4: "#c678dd",
631
+ h5: "#cccccc",
632
+ h6: "#cccccc"
633
+ },
634
+ paragraph: "#cccccc",
635
+ unorderedBullet: "#56b6c2",
636
+ orderedMarker: "#56b6c2",
637
+ tableHeader: "#56b6c2",
638
+ tableBorder: "#555555",
639
+ quoteMarker: "#555555",
640
+ quoteText: "#555555",
641
+ separator: "#555555",
642
+ codeText: "#ffffff",
643
+ codeEmpty: "#555555",
644
+ codeBorder: "#555555"
645
+ };
646
+ var DARK_COMPONENTS = {
647
+ tabs: {
648
+ activeFg: "#0c0c0c",
649
+ activeBg: "#e5c07b",
650
+ inactiveFg: "#555555",
651
+ separator: "#555555"
652
+ },
653
+ statusbar: {
654
+ fg: "#0c0c0c",
655
+ bg: "#98c379"
656
+ },
657
+ badge: {
658
+ fg: "#0c0c0c",
659
+ bg: "#ffffff"
660
+ },
661
+ button: {
662
+ fg: "#0c0c0c",
663
+ bg: "#ffffff"
664
+ }
665
+ };
666
+ var LIGHT_PALETTE = {
667
+ black: "#1f2328",
668
+ red: "#cf222e",
669
+ green: "#1a7f37",
670
+ yellow: "#9a6700",
671
+ blue: "#0969da",
672
+ magenta: "#8250df",
673
+ cyan: "#1b7c83",
674
+ white: "#30363d",
675
+ brightBlack: "#6e7781",
676
+ brightWhite: "#0f172a"
677
+ };
678
+ var LIGHT_SEMANTIC = {
679
+ frameBg: "#f6f8fa",
680
+ frameFg: "#1f2328",
681
+ frameMuted: "#6e7781",
682
+ border: "#d0d7de",
683
+ borderStrong: "#8c959f",
684
+ inverseFg: "#f6f8fa",
685
+ inverseBg: "#0f172a",
686
+ info: "#0969da",
687
+ success: "#1a7f37",
688
+ warn: "#9a6700",
689
+ danger: "#cf222e"
690
+ };
691
+ var LIGHT_SYNTAX = {
692
+ default: "#1f2328",
693
+ keyword: "#8250df",
694
+ string: "#116329",
695
+ number: "#9a6700",
696
+ comment: "#6e7781",
697
+ punctuation: "#6e7781",
698
+ function: "#0969da",
699
+ type: "#1b7c83"
700
+ };
701
+ var LIGHT_MARKDOWN = {
702
+ headingMarker: "#6e7781",
703
+ headingBoldMaxLevel: 3,
704
+ headings: {
705
+ h1: "#9a6700",
706
+ h2: "#0969da",
707
+ h3: "#1a7f37",
708
+ h4: "#8250df",
709
+ h5: "#1f2328",
710
+ h6: "#1f2328"
711
+ },
712
+ paragraph: "#1f2328",
713
+ unorderedBullet: "#0969da",
714
+ orderedMarker: "#0969da",
715
+ tableHeader: "#0969da",
716
+ tableBorder: "#d0d7de",
717
+ quoteMarker: "#6e7781",
718
+ quoteText: "#6e7781",
719
+ separator: "#d0d7de",
720
+ codeText: "#1f2328",
721
+ codeEmpty: "#6e7781",
722
+ codeBorder: "#d0d7de"
723
+ };
724
+ var LIGHT_COMPONENTS = {
725
+ tabs: {
726
+ activeFg: "#f6f8fa",
727
+ activeBg: "#0969da",
728
+ inactiveFg: "#6e7781",
729
+ separator: "#8c959f"
730
+ },
731
+ statusbar: {
732
+ fg: "#f6f8fa",
733
+ bg: "#0969da"
734
+ },
735
+ badge: {
736
+ fg: "#f6f8fa",
737
+ bg: "#0f172a"
738
+ },
739
+ button: {
740
+ fg: "#f6f8fa",
741
+ bg: "#0f172a"
742
+ }
743
+ };
744
+ var defaultThemeSpec = {
745
+ id: "termui-default",
746
+ label: "TermUI Default",
747
+ version: 1,
748
+ modes: {
749
+ dark: {
750
+ palette: DARK_PALETTE,
751
+ semantic: DARK_SEMANTIC,
752
+ syntax: DARK_SYNTAX,
753
+ markdown: DARK_MARKDOWN,
754
+ components: DARK_COMPONENTS
755
+ },
756
+ light: {
757
+ palette: LIGHT_PALETTE,
758
+ semantic: LIGHT_SEMANTIC,
759
+ syntax: LIGHT_SYNTAX,
760
+ markdown: LIGHT_MARKDOWN,
761
+ components: LIGHT_COMPONENTS
762
+ }
763
+ }
764
+ };
765
+ var builtinThemes = {
766
+ default: defaultThemeSpec
767
+ };
768
+ function resolveThemeMode(preference, systemMode = "dark") {
769
+ return preference === "system" ? systemMode : preference;
770
+ }
771
+ function resolveTheme(spec, preference = "dark", opts) {
772
+ const mode = resolveThemeMode(preference, opts?.systemMode ?? "dark");
773
+ const modeTokens = spec.modes[mode] ?? defaultThemeSpec.modes[mode];
774
+ return {
775
+ id: spec.id,
776
+ label: spec.label,
777
+ version: spec.version,
778
+ mode,
779
+ palette: modeTokens.palette,
780
+ semantic: modeTokens.semantic,
781
+ syntax: modeTokens.syntax,
782
+ markdown: modeTokens.markdown,
783
+ components: modeTokens.components,
784
+ black: modeTokens.palette.black,
785
+ red: modeTokens.palette.red,
786
+ green: modeTokens.palette.green,
787
+ yellow: modeTokens.palette.yellow,
788
+ blue: modeTokens.palette.blue,
789
+ magenta: modeTokens.palette.magenta,
790
+ cyan: modeTokens.palette.cyan,
791
+ white: modeTokens.palette.white,
792
+ brightBlack: modeTokens.palette.brightBlack,
793
+ brightWhite: modeTokens.palette.brightWhite,
794
+ border: modeTokens.semantic.border,
795
+ bg: modeTokens.semantic.frameBg
796
+ };
797
+ }
798
+ var dark = resolveTheme(defaultThemeSpec, "dark");
799
+ var light = resolveTheme(defaultThemeSpec, "light");
800
+ function themeToCssVars(theme) {
801
+ return {
802
+ "--termui-bg": theme.semantic.frameBg,
803
+ "--termui-fg": theme.semantic.frameFg,
804
+ "--termui-muted": theme.semantic.frameMuted,
805
+ "--termui-border": theme.semantic.border,
806
+ "--termui-border-strong": theme.semantic.borderStrong,
807
+ "--termui-info": theme.semantic.info,
808
+ "--termui-success": theme.semantic.success,
809
+ "--termui-warn": theme.semantic.warn,
810
+ "--termui-danger": theme.semantic.danger,
811
+ "--termui-inverse-fg": theme.semantic.inverseFg,
812
+ "--termui-inverse-bg": theme.semantic.inverseBg
813
+ };
814
+ }
815
+ function parseThemeSpec(json) {
816
+ let value;
817
+ try {
818
+ value = JSON.parse(json);
819
+ } catch (error) {
820
+ return {
821
+ ok: false,
822
+ issues: [{ path: "$", message: error instanceof Error ? error.message : "Invalid JSON" }]
823
+ };
824
+ }
825
+ return validateThemeSpec(value);
826
+ }
827
+ function isRecord(value) {
828
+ return value !== null && typeof value === "object" && !Array.isArray(value);
829
+ }
830
+ function issue(path, message) {
831
+ return { path, message };
832
+ }
833
+ function isColorValue(value) {
834
+ const v = value.trim();
835
+ if (!v) return false;
836
+ if (/^#(?:[0-9a-fA-F]{3,4}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$/.test(v)) return true;
837
+ if (/^(?:rgb|rgba|hsl|hsla|oklch|oklab|lab|lch|color)\(.+\)$/i.test(v)) return true;
838
+ if (/^var\(--[a-zA-Z0-9-_]+\)$/.test(v)) return true;
839
+ if (/^[a-zA-Z]+$/.test(v)) return true;
840
+ return false;
841
+ }
842
+ function validateColorField(value, path, issues) {
843
+ if (typeof value !== "string") {
844
+ issues.push(issue(path, "Expected a string color value"));
845
+ return;
846
+ }
847
+ if (!isColorValue(value)) {
848
+ issues.push(issue(path, "Invalid CSS color value"));
849
+ }
850
+ }
851
+ function validateHeadingBoldMaxLevel(value, path, issues) {
852
+ if (typeof value !== "number" || !Number.isInteger(value)) {
853
+ issues.push(issue(path, "Expected an integer between 1 and 6"));
854
+ return;
855
+ }
856
+ if (value < 1 || value > 6) {
857
+ issues.push(issue(path, "Expected an integer between 1 and 6"));
858
+ }
859
+ }
860
+ function validateTokenGroup(value, path, fields, issues) {
861
+ if (!isRecord(value)) {
862
+ issues.push(issue(path, "Expected an object"));
863
+ return;
864
+ }
865
+ for (const field of fields) {
866
+ validateColorField(value[field], `${path}.${field}`, issues);
867
+ }
868
+ }
869
+ function validateMarkdownTokens(value, path, issues) {
870
+ if (!isRecord(value)) {
871
+ issues.push(issue(path, "Expected an object"));
872
+ return;
873
+ }
874
+ validateColorField(value.headingMarker, `${path}.headingMarker`, issues);
875
+ validateHeadingBoldMaxLevel(value.headingBoldMaxLevel, `${path}.headingBoldMaxLevel`, issues);
876
+ if (!isRecord(value.headings)) {
877
+ issues.push(issue(`${path}.headings`, "Expected an object"));
878
+ } else {
879
+ for (const level of ["h1", "h2", "h3", "h4", "h5", "h6"]) {
880
+ validateColorField(value.headings[level], `${path}.headings.${level}`, issues);
881
+ }
882
+ }
883
+ for (const field of [
884
+ "paragraph",
885
+ "unorderedBullet",
886
+ "orderedMarker",
887
+ "tableHeader",
888
+ "tableBorder",
889
+ "quoteMarker",
890
+ "quoteText",
891
+ "separator",
892
+ "codeText",
893
+ "codeEmpty",
894
+ "codeBorder"
895
+ ]) {
896
+ validateColorField(value[field], `${path}.${field}`, issues);
897
+ }
898
+ }
899
+ function validateComponentTokens(value, path, issues) {
900
+ if (!isRecord(value)) {
901
+ issues.push(issue(path, "Expected an object"));
902
+ return;
903
+ }
904
+ validateTokenGroup(
905
+ value.tabs,
906
+ `${path}.tabs`,
907
+ ["activeFg", "activeBg", "inactiveFg", "separator"],
908
+ issues
909
+ );
910
+ validateTokenGroup(value.statusbar, `${path}.statusbar`, ["fg", "bg"], issues);
911
+ validateTokenGroup(value.badge, `${path}.badge`, ["fg", "bg"], issues);
912
+ validateTokenGroup(value.button, `${path}.button`, ["fg", "bg"], issues);
913
+ }
914
+ function validateModeTokens(value, path, issues) {
915
+ if (!isRecord(value)) {
916
+ issues.push(issue(path, "Expected an object"));
917
+ return;
918
+ }
919
+ validateTokenGroup(
920
+ value.palette,
921
+ `${path}.palette`,
922
+ [
923
+ "black",
924
+ "red",
925
+ "green",
926
+ "yellow",
927
+ "blue",
928
+ "magenta",
929
+ "cyan",
930
+ "white",
931
+ "brightBlack",
932
+ "brightWhite"
933
+ ],
934
+ issues
935
+ );
936
+ validateTokenGroup(
937
+ value.semantic,
938
+ `${path}.semantic`,
939
+ [
940
+ "frameBg",
941
+ "frameFg",
942
+ "frameMuted",
943
+ "border",
944
+ "borderStrong",
945
+ "inverseFg",
946
+ "inverseBg",
947
+ "info",
948
+ "success",
949
+ "warn",
950
+ "danger"
951
+ ],
952
+ issues
953
+ );
954
+ validateTokenGroup(
955
+ value.syntax,
956
+ `${path}.syntax`,
957
+ [
958
+ "default",
959
+ "keyword",
960
+ "string",
961
+ "number",
962
+ "comment",
963
+ "punctuation",
964
+ "function",
965
+ "type"
966
+ ],
967
+ issues
968
+ );
969
+ validateMarkdownTokens(value.markdown, `${path}.markdown`, issues);
970
+ validateComponentTokens(value.components, `${path}.components`, issues);
971
+ }
972
+ function validateThemeSpec(value) {
973
+ const issues = [];
974
+ if (!isRecord(value)) {
975
+ return { ok: false, issues: [issue("$", "Expected an object")] };
976
+ }
977
+ if (typeof value.id !== "string" || !value.id.trim()) {
978
+ issues.push(issue("$.id", "Expected a non-empty string"));
979
+ }
980
+ if (typeof value.label !== "string" || !value.label.trim()) {
981
+ issues.push(issue("$.label", "Expected a non-empty string"));
982
+ }
983
+ if (typeof value.version !== "number" || !Number.isInteger(value.version) || value.version < 1) {
984
+ issues.push(issue("$.version", "Expected a positive integer"));
985
+ }
986
+ if (!isRecord(value.modes)) {
987
+ issues.push(issue("$.modes", "Expected an object with light/dark modes"));
988
+ } else {
989
+ validateModeTokens(value.modes.dark, "$.modes.dark", issues);
990
+ validateModeTokens(value.modes.light, "$.modes.light", issues);
991
+ }
992
+ if (issues.length > 0) {
993
+ return { ok: false, issues };
994
+ }
995
+ return {
996
+ ok: true,
997
+ issues: [],
998
+ theme: value
999
+ };
1000
+ }
1001
+
1002
+ // src/termui/code-diff.ts
1003
+ function tokenColor(type, theme) {
1004
+ switch (type) {
1005
+ case "keyword":
1006
+ return theme.syntax.keyword;
1007
+ case "string":
1008
+ return theme.syntax.string;
1009
+ case "number":
1010
+ return theme.syntax.number;
1011
+ case "comment":
1012
+ return theme.syntax.comment;
1013
+ case "punctuation":
1014
+ return theme.syntax.punctuation;
1015
+ case "function":
1016
+ return theme.syntax.function;
1017
+ case "type":
1018
+ return theme.syntax.type;
1019
+ default:
1020
+ return theme.syntax.default;
1021
+ }
1022
+ }
1023
+ function code(source, opts) {
1024
+ const t = opts?.theme ?? dark;
1025
+ const tokens = tokenize(source, opts?.language);
1026
+ return box(
1027
+ lines(
1028
+ tokens.length > 0 ? tokens.map(
1029
+ (lineTokens) => lineTokens.length === 0 ? [{ text: " ", color: t.markdown.codeEmpty }] : lineTokens.map((tok) => ({ text: tok.text, color: tokenColor(tok.type, t) }))
1030
+ ) : [[{ text: " ", color: t.markdown.codeEmpty, dim: true }]]
1031
+ ),
1032
+ {
1033
+ title: opts?.title ?? (opts?.language ? `code:${opts.language}` : "code"),
1034
+ borderColor: opts?.borderColor ?? t.markdown.codeBorder
1035
+ }
1036
+ );
1037
+ }
1038
+ function diff(value, opts) {
1039
+ const t = opts?.theme ?? dark;
1040
+ const addC = opts?.addColor ?? t.semantic.success;
1041
+ const remC = opts?.removeColor ?? t.semantic.danger;
1042
+ const metC = opts?.metaColor ?? t.semantic.info;
1043
+ return lines((_) => {
1044
+ return value.replace(/\r\n/g, "\n").split("\n").map((row) => {
1045
+ if (row.startsWith("@@")) return [{ text: row, color: metC }];
1046
+ if (row.startsWith("+")) return [{ text: row, color: addC }];
1047
+ if (row.startsWith("-")) return [{ text: row, color: remC }];
1048
+ return [{ text: row, dim: true }];
1049
+ });
1050
+ });
1051
+ }
1052
+
1053
+ // src/document/common.ts
1054
+ function headingLevelKey(level) {
1055
+ if (level <= 1) return "h1";
1056
+ if (level === 2) return "h2";
1057
+ if (level === 3) return "h3";
1058
+ if (level === 4) return "h4";
1059
+ if (level === 5) return "h5";
1060
+ return "h6";
1061
+ }
1062
+ function headingColor(level, theme, overrides) {
1063
+ const key = headingLevelKey(level);
1064
+ return overrides?.headings?.[key] ?? theme.markdown.headings[key];
1065
+ }
1066
+ function isNumericValue(value) {
1067
+ if (typeof value === "number") return Number.isFinite(value);
1068
+ if (typeof value !== "string") return false;
1069
+ const trimmed = value.trim();
1070
+ return /^-?\d+(?:\.\d+)?(?:[eE][+-]?\d+)?$/.test(trimmed);
1071
+ }
1072
+ function wrapText2(text, maxWidth) {
1073
+ const width = Math.max(1, maxWidth);
1074
+ const normalized = text.replace(/\s+/g, " ").trim();
1075
+ if (!normalized) return [""];
1076
+ const linesOut = [];
1077
+ let rest = normalized;
1078
+ while (rest.length > width) {
1079
+ const slice = rest.slice(0, width + 1);
1080
+ const splitAt = slice.lastIndexOf(" ");
1081
+ const end = splitAt > 0 ? splitAt : width;
1082
+ linesOut.push(rest.slice(0, end).trimEnd());
1083
+ rest = rest.slice(end).trimStart();
1084
+ }
1085
+ linesOut.push(rest);
1086
+ return linesOut;
1087
+ }
1088
+ function renderPrefixedText(text, width, prefix, continuationPrefix, textStyle) {
1089
+ const prefixText = typeof prefix === "string" ? prefix : prefix.text;
1090
+ const wrapped = wrapText2(text, width - prefixText.length);
1091
+ return wrapped.map((chunk, index) => {
1092
+ if (index === 0) {
1093
+ return [prefix, textStyle ? { text: chunk, ...textStyle } : chunk];
1094
+ }
1095
+ return [continuationPrefix, textStyle ? { text: chunk, ...textStyle } : chunk];
1096
+ });
1097
+ }
1098
+ function normalizeHtmlText(text) {
1099
+ return text.replace(/\s+/g, " ").trim();
1100
+ }
1101
+ function isRecord2(value) {
1102
+ return value !== null && typeof value === "object" && !Array.isArray(value);
1103
+ }
1104
+ function getValueByPath(source, path) {
1105
+ const parts = path.split(".");
1106
+ let current = source;
1107
+ for (const part of parts) {
1108
+ if (!isRecord2(current)) return void 0;
1109
+ current = current[part];
1110
+ }
1111
+ return current;
1112
+ }
1113
+ function resolveThemeColor(value, theme) {
1114
+ if (!value) return void 0;
1115
+ const raw = value.trim();
1116
+ if (!raw) return void 0;
1117
+ const topLevel = theme[raw];
1118
+ if (typeof topLevel === "string") return topLevel;
1119
+ const nested = getValueByPath(theme, raw);
1120
+ if (typeof nested === "string") return nested;
1121
+ return raw;
1122
+ }
1123
+ function getColorAttr(el, theme, names) {
1124
+ for (const name of names) {
1125
+ const value = resolveThemeColor(el.getAttribute(name), theme);
1126
+ if (value) return value;
1127
+ }
1128
+ return void 0;
1129
+ }
1130
+ function parseIntegerAttr(el, name) {
1131
+ const raw = el.getAttribute(name);
1132
+ if (!raw) return void 0;
1133
+ const n = Number.parseInt(raw.trim(), 10);
1134
+ return Number.isFinite(n) ? n : void 0;
1135
+ }
1136
+ function parseCodeLanguage(el) {
1137
+ const explicit = el.getAttribute("language") ?? el.getAttribute("data-language");
1138
+ if (explicit && explicit.trim()) return explicit.trim();
1139
+ const klass = el.getAttribute("class") ?? "";
1140
+ const match = klass.match(/\blanguage-([\w+-]+)\b/);
1141
+ return match?.[1];
1142
+ }
1143
+
1144
+ // src/document/markdown.ts
1145
+ function isBlockStart(line) {
1146
+ const trimmed = line.trim();
1147
+ if (!trimmed) return false;
1148
+ if (/^#{1,6}\s+/.test(trimmed)) return true;
1149
+ if (/^```/.test(trimmed)) return true;
1150
+ if (/^>\s?/.test(trimmed)) return true;
1151
+ if (/^[-*+]\s+/.test(trimmed)) return true;
1152
+ if (/^\d+\.\s+/.test(trimmed)) return true;
1153
+ const collapsed = trimmed.replace(/\s+/g, "");
1154
+ return /^(-{3,}|\*{3,}|_{3,})$/.test(collapsed);
1155
+ }
1156
+ function parsePipeCells(line) {
1157
+ const trimmed = line.trim();
1158
+ if (!trimmed.includes("|")) return null;
1159
+ let core = trimmed;
1160
+ if (core.startsWith("|")) core = core.slice(1);
1161
+ if (core.endsWith("|")) core = core.slice(0, -1);
1162
+ const cells = core.split("|").map((cell) => cell.trim());
1163
+ return cells.length >= 2 ? cells : null;
1164
+ }
1165
+ function isTableDividerLine(line) {
1166
+ const cells = parsePipeCells(line);
1167
+ if (!cells) return false;
1168
+ return cells.every((cell) => /^:?-{3,}:?$/.test(cell));
1169
+ }
1170
+ function parseMarkdown(markdown) {
1171
+ const rows = markdown.replace(/\r\n/g, "\n").split("\n");
1172
+ const blocks = [];
1173
+ let i = 0;
1174
+ while (i < rows.length) {
1175
+ const raw = rows[i] ?? "";
1176
+ const trimmed = raw.trim();
1177
+ if (!trimmed) {
1178
+ i += 1;
1179
+ continue;
1180
+ }
1181
+ const codeStart = trimmed.match(/^```([\w+-]+)?\s*$/);
1182
+ if (codeStart) {
1183
+ i += 1;
1184
+ const codeLines = [];
1185
+ while (i < rows.length && !(rows[i] ?? "").trim().startsWith("```")) {
1186
+ codeLines.push((rows[i] ?? "").replace(/\t/g, " "));
1187
+ i += 1;
1188
+ }
1189
+ if (i < rows.length) i += 1;
1190
+ blocks.push({
1191
+ type: "code",
1192
+ language: codeStart[1],
1193
+ lines: codeLines
1194
+ });
1195
+ continue;
1196
+ }
1197
+ const heading = trimmed.match(/^(#{1,6})\s+(.*)$/);
1198
+ if (heading) {
1199
+ blocks.push({
1200
+ type: "heading",
1201
+ level: heading[1].length,
1202
+ text: heading[2].trim()
1203
+ });
1204
+ i += 1;
1205
+ continue;
1206
+ }
1207
+ const collapsed = trimmed.replace(/\s+/g, "");
1208
+ if (/^(-{3,}|\*{3,}|_{3,})$/.test(collapsed)) {
1209
+ blocks.push({ type: "separator" });
1210
+ i += 1;
1211
+ continue;
1212
+ }
1213
+ const maybeHeaderCells = parsePipeCells(raw);
1214
+ const nextLine = rows[i + 1] ?? "";
1215
+ if (maybeHeaderCells && isTableDividerLine(nextLine)) {
1216
+ const headers = maybeHeaderCells;
1217
+ i += 2;
1218
+ const dataRows = [];
1219
+ while (i < rows.length) {
1220
+ const rowCells = parsePipeCells(rows[i] ?? "");
1221
+ if (!rowCells) break;
1222
+ const normalized = headers.map((_, idx) => rowCells[idx] ?? "");
1223
+ dataRows.push(normalized);
1224
+ i += 1;
1225
+ }
1226
+ blocks.push({ type: "table", headers, rows: dataRows });
1227
+ continue;
1228
+ }
1229
+ if (/^>\s?/.test(trimmed)) {
1230
+ const quoteLines = [];
1231
+ while (i < rows.length) {
1232
+ const m = (rows[i] ?? "").match(/^\s*>\s?(.*)$/);
1233
+ if (!m) break;
1234
+ quoteLines.push(m[1]);
1235
+ i += 1;
1236
+ }
1237
+ blocks.push({ type: "quote", lines: quoteLines });
1238
+ continue;
1239
+ }
1240
+ const ulStart = trimmed.match(/^[-*+]\s+(.*)$/);
1241
+ if (ulStart) {
1242
+ const items = [];
1243
+ while (i < rows.length) {
1244
+ const current = (rows[i] ?? "").trim();
1245
+ const item = current.match(/^[-*+]\s+(.*)$/);
1246
+ if (item) {
1247
+ items.push(item[1].trim());
1248
+ i += 1;
1249
+ continue;
1250
+ }
1251
+ const continuation = (rows[i] ?? "").match(/^\s{2,}(.*)$/);
1252
+ if (continuation && items.length > 0) {
1253
+ const last = items[items.length - 1] ?? "";
1254
+ items[items.length - 1] = (last + " " + continuation[1].trim()).trim();
1255
+ i += 1;
1256
+ continue;
1257
+ }
1258
+ break;
1259
+ }
1260
+ blocks.push({ type: "unordered-list", items });
1261
+ continue;
1262
+ }
1263
+ const olStart = trimmed.match(/^(\d+)\.\s+(.*)$/);
1264
+ if (olStart) {
1265
+ const items = [];
1266
+ const start = Number(olStart[1]);
1267
+ while (i < rows.length) {
1268
+ const current = (rows[i] ?? "").trim();
1269
+ const item = current.match(/^\d+\.\s+(.*)$/);
1270
+ if (item) {
1271
+ items.push(item[1].trim());
1272
+ i += 1;
1273
+ continue;
1274
+ }
1275
+ const continuation = (rows[i] ?? "").match(/^\s{2,}(.*)$/);
1276
+ if (continuation && items.length > 0) {
1277
+ const last = items[items.length - 1] ?? "";
1278
+ items[items.length - 1] = (last + " " + continuation[1].trim()).trim();
1279
+ i += 1;
1280
+ continue;
1281
+ }
1282
+ break;
1283
+ }
1284
+ blocks.push({ type: "ordered-list", start, items });
1285
+ continue;
1286
+ }
1287
+ const paragraphRows = [trimmed];
1288
+ i += 1;
1289
+ while (i < rows.length) {
1290
+ const nextRaw = rows[i] ?? "";
1291
+ const next = (rows[i] ?? "").trim();
1292
+ const maybeTableStart = parsePipeCells(nextRaw) !== null && isTableDividerLine(rows[i + 1] ?? "");
1293
+ if (!next || isBlockStart(next) || maybeTableStart) break;
1294
+ paragraphRows.push(next);
1295
+ i += 1;
1296
+ }
1297
+ blocks.push({ type: "paragraph", text: paragraphRows.join(" ") });
1298
+ }
1299
+ return blocks;
1300
+ }
1301
+ function renderMarkdownBlock(block, theme, overrides) {
1302
+ const md = theme.markdown;
1303
+ switch (block.type) {
1304
+ case "heading":
1305
+ return (w) => renderPrefixedText(
1306
+ block.text,
1307
+ w,
1308
+ {
1309
+ text: "#".repeat(block.level) + " ",
1310
+ color: overrides?.headingMarker ?? md.headingMarker
1311
+ },
1312
+ " ".repeat(block.level + 1),
1313
+ {
1314
+ color: headingColor(block.level, theme, overrides),
1315
+ bold: block.level <= (overrides?.headingBoldMaxLevel ?? md.headingBoldMaxLevel)
1316
+ }
1317
+ );
1318
+ case "paragraph":
1319
+ return (w) => wrapText2(block.text, w).map((line) => [
1320
+ { text: line, color: overrides?.paragraph ?? md.paragraph }
1321
+ ]);
1322
+ case "unordered-list":
1323
+ return (w) => {
1324
+ const out = [];
1325
+ for (const item of block.items) {
1326
+ out.push(
1327
+ ...renderPrefixedText(
1328
+ item,
1329
+ w,
1330
+ { text: " \u2022 ", color: overrides?.unorderedBullet ?? md.unorderedBullet },
1331
+ " "
1332
+ )
1333
+ );
1334
+ }
1335
+ return out;
1336
+ };
1337
+ case "ordered-list":
1338
+ return (w) => {
1339
+ const out = [];
1340
+ const lastNumber = block.start + block.items.length - 1;
1341
+ const digits = String(Math.max(block.start, lastNumber)).length;
1342
+ for (let i = 0; i < block.items.length; i++) {
1343
+ const n = String(block.start + i).padStart(digits, " ");
1344
+ const prefix = {
1345
+ text: `${n}. `,
1346
+ color: overrides?.orderedMarker ?? md.orderedMarker
1347
+ };
1348
+ out.push(...renderPrefixedText(block.items[i], w, prefix, " ".repeat(digits + 2)));
1349
+ }
1350
+ return out;
1351
+ };
1352
+ case "table": {
1353
+ const keys = block.headers.map((_, i) => `c${i}`);
1354
+ const columns = block.headers.map((header, i) => ({
1355
+ key: keys[i],
1356
+ title: header || `col_${i + 1}`,
1357
+ align: block.rows.every((row) => isNumericValue(row[i] ?? "")) ? "right" : "left",
1358
+ headerColor: overrides?.tableHeader ?? md.tableHeader
1359
+ }));
1360
+ const data = block.rows.map((row) => {
1361
+ const out = {};
1362
+ keys.forEach((key, i) => {
1363
+ out[key] = row[i] ?? "";
1364
+ });
1365
+ return out;
1366
+ });
1367
+ return table(columns, data, {
1368
+ borderColor: overrides?.tableBorder ?? md.tableBorder
1369
+ });
1370
+ }
1371
+ case "quote":
1372
+ return (w) => {
1373
+ const out = [];
1374
+ for (const quoteLine of block.lines) {
1375
+ out.push(
1376
+ ...renderPrefixedText(
1377
+ quoteLine,
1378
+ w,
1379
+ { text: "\u258C ", color: overrides?.quoteMarker ?? md.quoteMarker },
1380
+ " ",
1381
+ { color: overrides?.quoteText ?? md.quoteText, dim: true }
1382
+ )
1383
+ );
1384
+ }
1385
+ return out;
1386
+ };
1387
+ case "separator":
1388
+ return separator(overrides?.separator ?? md.separator);
1389
+ case "code":
1390
+ return box(
1391
+ lines(
1392
+ block.lines.length > 0 ? block.lines.map((line) => [{ text: line || " ", color: overrides?.codeText ?? md.codeText }]) : [[{ text: " ", color: overrides?.codeEmpty ?? md.codeEmpty, dim: true }]]
1393
+ ),
1394
+ {
1395
+ title: block.language ? `code:${block.language}` : "code",
1396
+ borderColor: overrides?.codeBorder ?? md.codeBorder
1397
+ }
1398
+ );
1399
+ }
1400
+ }
1401
+ function fromMarkdown(markdown, opts) {
1402
+ const theme = opts?.theme ?? dark;
1403
+ const blocks = parseMarkdown(markdown);
1404
+ const layouts = blocks.map((block) => renderMarkdownBlock(block, theme, opts?.overrides));
1405
+ return (w) => {
1406
+ if (layouts.length === 0) return blank()(w);
1407
+ const out = [];
1408
+ layouts.forEach((layout, index) => {
1409
+ if (index > 0) out.push(...blank()(w));
1410
+ out.push(...layout(w));
1411
+ });
1412
+ return out;
1413
+ };
1414
+ }
1415
+
1416
+ // src/document/html.ts
1417
+ function htmlParagraph(text, color) {
1418
+ return (w) => {
1419
+ const wrapped = wrapText2(text, w);
1420
+ if (!color) return wrapped.map((line) => [line]);
1421
+ return wrapped.map((line) => [{ text: line, color }]);
1422
+ };
1423
+ }
1424
+ function htmlHeading(level, text, theme, overrides, markerColor) {
1425
+ const md = theme.markdown;
1426
+ return (w) => renderPrefixedText(
1427
+ text,
1428
+ w,
1429
+ {
1430
+ text: "#".repeat(level) + " ",
1431
+ color: markerColor ?? overrides?.headingMarker ?? md.headingMarker
1432
+ },
1433
+ " ".repeat(level + 1),
1434
+ {
1435
+ color: headingColor(level, theme, overrides),
1436
+ bold: level <= (overrides?.headingBoldMaxLevel ?? md.headingBoldMaxLevel)
1437
+ }
1438
+ );
1439
+ }
1440
+ function collectDirectChildElements(el) {
1441
+ return Array.from(el.childNodes).filter((n) => n.nodeType === 1);
1442
+ }
1443
+ function collectHtmlChildLayouts(el, theme, overrides) {
1444
+ const layouts = [];
1445
+ for (const child of Array.from(el.childNodes)) {
1446
+ const layout = renderHtmlNode(child, theme, overrides);
1447
+ if (layout) layouts.push(layout);
1448
+ }
1449
+ return layouts;
1450
+ }
1451
+ function htmlContainer(el, theme, overrides) {
1452
+ const layouts = collectHtmlChildLayouts(el, theme, overrides);
1453
+ if (layouts.length === 0) return blank();
1454
+ const withSpacing = [];
1455
+ layouts.forEach((layout, index) => {
1456
+ if (index > 0) withSpacing.push(blank());
1457
+ withSpacing.push(layout);
1458
+ });
1459
+ return (w) => withSpacing.flatMap((layout) => layout(w));
1460
+ }
1461
+ function htmlList(el, ordered, theme, overrides) {
1462
+ const items = collectDirectChildElements(el).filter(
1463
+ (child) => child.tagName.toLowerCase() === "li"
1464
+ );
1465
+ if (items.length === 0) return blank();
1466
+ return (w) => {
1467
+ const out = [];
1468
+ const start = ordered ? parseIntegerAttr(el, "start") ?? 1 : 1;
1469
+ const digits = ordered ? String(start + items.length - 1).length : 0;
1470
+ items.forEach((item, index) => {
1471
+ const text = normalizeHtmlText(item.textContent ?? "");
1472
+ if (!text) return;
1473
+ if (ordered) {
1474
+ const n = String(start + index).padStart(digits, " ");
1475
+ const prefix = {
1476
+ text: `${n}. `,
1477
+ color: overrides?.orderedMarker ?? theme.markdown.orderedMarker
1478
+ };
1479
+ out.push(...renderPrefixedText(text, w, prefix, " ".repeat(digits + 2)));
1480
+ } else {
1481
+ out.push(
1482
+ ...renderPrefixedText(
1483
+ text,
1484
+ w,
1485
+ { text: " \u2022 ", color: overrides?.unorderedBullet ?? theme.markdown.unorderedBullet },
1486
+ " "
1487
+ )
1488
+ );
1489
+ }
1490
+ });
1491
+ return out.length > 0 ? out : blank()(w);
1492
+ };
1493
+ }
1494
+ function collectTableRowElements(tableEl) {
1495
+ const rows = [];
1496
+ for (const child of collectDirectChildElements(tableEl)) {
1497
+ const tag = child.tagName.toLowerCase();
1498
+ if (tag === "tr") {
1499
+ rows.push(child);
1500
+ continue;
1501
+ }
1502
+ if (tag === "thead" || tag === "tbody" || tag === "tfoot") {
1503
+ for (const row of collectDirectChildElements(child)) {
1504
+ if (row.tagName.toLowerCase() === "tr") rows.push(row);
1505
+ }
1506
+ }
1507
+ }
1508
+ return rows;
1509
+ }
1510
+ function tableRowCells(rowEl) {
1511
+ const parentTag = rowEl.parentElement?.tagName.toLowerCase();
1512
+ const inHead = parentTag === "thead";
1513
+ const cells = [];
1514
+ for (const child of collectDirectChildElements(rowEl)) {
1515
+ const tag = child.tagName.toLowerCase();
1516
+ if (tag !== "th" && tag !== "td") continue;
1517
+ cells.push({
1518
+ text: normalizeHtmlText(child.textContent ?? ""),
1519
+ header: inHead || tag === "th"
1520
+ });
1521
+ }
1522
+ return cells;
1523
+ }
1524
+ function htmlTable(el, theme, overrides) {
1525
+ const rowEls = collectTableRowElements(el);
1526
+ if (rowEls.length === 0) {
1527
+ return lines([[{ text: "(empty table)", color: theme.semantic.frameMuted, dim: true }]]);
1528
+ }
1529
+ const rows = rowEls.map(tableRowCells).filter((cells) => cells.length > 0);
1530
+ if (rows.length === 0) {
1531
+ return lines([[{ text: "(empty table)", color: theme.semantic.frameMuted, dim: true }]]);
1532
+ }
1533
+ const firstRow = rows[0];
1534
+ const hasHeader = firstRow.some((cell) => cell.header);
1535
+ const headers = hasHeader ? firstRow.map((cell, index) => cell.text || `col_${index + 1}`) : firstRow.map((_, index) => `col_${index + 1}`);
1536
+ const dataRows = rows.slice(hasHeader ? 1 : 0).map((row) => headers.map((_, index) => row[index]?.text ?? ""));
1537
+ const keys = headers.map((_, index) => `c${index}`);
1538
+ const headerColor = getColorAttr(el, theme, ["header-color", "data-header-color"]) ?? overrides?.tableHeader ?? theme.markdown.tableHeader;
1539
+ const borderColor = getColorAttr(el, theme, ["border-color", "data-border-color"]) ?? overrides?.tableBorder ?? theme.markdown.tableBorder;
1540
+ const columns = headers.map((header, i) => ({
1541
+ key: keys[i],
1542
+ title: header,
1543
+ align: dataRows.every((row) => isNumericValue(row[i] ?? "")) ? "right" : "left",
1544
+ headerColor
1545
+ }));
1546
+ const data = dataRows.map((row) => {
1547
+ const out = {};
1548
+ keys.forEach((key, index) => {
1549
+ out[key] = row[index] ?? "";
1550
+ });
1551
+ return out;
1552
+ });
1553
+ return table(columns, data, { borderColor });
1554
+ }
1555
+ function htmlPre(el, theme, overrides) {
1556
+ const raw = (el.textContent ?? "").replace(/\r\n/g, "\n");
1557
+ const trimmed = raw.replace(/^\n+|\n+$/g, "");
1558
+ const linesOut = trimmed.length > 0 ? trimmed.split("\n").map((line) => line.replace(/\t/g, " ")) : [];
1559
+ const language = parseCodeLanguage(el) ?? parseCodeLanguage(el.querySelector("code") ?? el);
1560
+ const textColor = getColorAttr(el, theme, ["text-color", "data-text-color", "color", "data-color"]) ?? overrides?.codeText ?? theme.markdown.codeText;
1561
+ const borderColor = getColorAttr(el, theme, ["border-color", "data-border-color"]) ?? overrides?.codeBorder ?? theme.markdown.codeBorder;
1562
+ const emptyColor = overrides?.codeEmpty ?? theme.markdown.codeEmpty;
1563
+ return box(
1564
+ lines(
1565
+ linesOut.length > 0 ? linesOut.map((line) => [{ text: line || " ", color: textColor }]) : [[{ text: " ", color: emptyColor, dim: true }]]
1566
+ ),
1567
+ {
1568
+ title: language ? `code:${language}` : "code",
1569
+ borderColor
1570
+ }
1571
+ );
1572
+ }
1573
+ function renderHtmlNode(node, theme, overrides) {
1574
+ if (node.nodeType === 3) {
1575
+ const text2 = normalizeHtmlText(node.textContent ?? "");
1576
+ if (!text2) return null;
1577
+ return htmlParagraph(text2, theme.markdown.paragraph);
1578
+ }
1579
+ if (node.nodeType !== 1) return null;
1580
+ const el = node;
1581
+ const tag = el.tagName.toLowerCase();
1582
+ if (/^h[1-6]$/.test(tag)) {
1583
+ const level = Number.parseInt(tag[1] ?? "1", 10) || 1;
1584
+ const text2 = normalizeHtmlText(el.textContent ?? "");
1585
+ if (!text2) return null;
1586
+ return htmlHeading(
1587
+ level,
1588
+ text2,
1589
+ theme,
1590
+ overrides,
1591
+ getColorAttr(el, theme, ["marker-color", "data-marker-color"])
1592
+ );
1593
+ }
1594
+ if (tag === "p") {
1595
+ const text2 = normalizeHtmlText(el.textContent ?? "");
1596
+ if (!text2) return null;
1597
+ const color2 = getColorAttr(el, theme, ["color", "data-color"]) ?? overrides?.paragraph ?? theme.markdown.paragraph;
1598
+ return htmlParagraph(text2, color2);
1599
+ }
1600
+ if (tag === "ul") return htmlList(el, false, theme, overrides);
1601
+ if (tag === "ol") return htmlList(el, true, theme, overrides);
1602
+ if (tag === "blockquote") {
1603
+ const text2 = normalizeHtmlText(el.textContent ?? "");
1604
+ if (!text2) return null;
1605
+ return (w) => renderPrefixedText(
1606
+ text2,
1607
+ w,
1608
+ {
1609
+ text: "\u258C ",
1610
+ color: getColorAttr(el, theme, ["marker-color", "data-marker-color"]) ?? overrides?.quoteMarker ?? theme.markdown.quoteMarker
1611
+ },
1612
+ " ",
1613
+ { color: overrides?.quoteText ?? theme.markdown.quoteText, dim: true }
1614
+ );
1615
+ }
1616
+ if (tag === "hr") {
1617
+ return separator(
1618
+ getColorAttr(el, theme, ["color", "data-color"]) ?? overrides?.separator ?? theme.markdown.separator
1619
+ );
1620
+ }
1621
+ if (tag === "pre") return htmlPre(el, theme, overrides);
1622
+ if (tag === "table") return htmlTable(el, theme, overrides);
1623
+ if (tag === "term" || tag === "main" || tag === "section" || tag === "article" || tag === "div" || tag === "header" || tag === "footer" || tag === "nav" || tag === "body") {
1624
+ return htmlContainer(el, theme, overrides);
1625
+ }
1626
+ const text = normalizeHtmlText(el.textContent ?? "");
1627
+ if (!text) return htmlContainer(el, theme, overrides);
1628
+ const color = getColorAttr(el, theme, ["color", "data-color"]);
1629
+ return htmlParagraph(text, color ?? theme.markdown.paragraph);
1630
+ }
1631
+ function fromHtml(html, opts) {
1632
+ const theme = opts?.theme ?? dark;
1633
+ if (typeof DOMParser === "undefined") {
1634
+ return fromMarkdown(html, { theme, overrides: opts?.overrides });
1635
+ }
1636
+ const doc = new DOMParser().parseFromString(html, "text/html");
1637
+ const host = doc.body.querySelector("term");
1638
+ const root = host ?? doc.body;
1639
+ if (!root) return blank();
1640
+ return htmlContainer(root, theme, opts?.overrides);
1641
+ }
1642
+
1643
+ // src/document/json.ts
1644
+ var JSON_NODE_TYPES = /* @__PURE__ */ new Set([
1645
+ "vstack",
1646
+ "hstack",
1647
+ "box",
1648
+ "lines",
1649
+ "separator",
1650
+ "blank",
1651
+ "table",
1652
+ "statusbar",
1653
+ "tabs",
1654
+ "bar",
1655
+ "text"
1656
+ ]);
1657
+ function isJsonNode(value) {
1658
+ if (!value || typeof value !== "object" || Array.isArray(value)) return false;
1659
+ const type = value.type;
1660
+ return typeof type === "string" && JSON_NODE_TYPES.has(type);
1661
+ }
1662
+ function isRecord3(value) {
1663
+ return value !== null && typeof value === "object" && !Array.isArray(value);
1664
+ }
1665
+ function primitiveStyle(value, theme) {
1666
+ if (typeof value === "string") return { color: theme.green };
1667
+ if (typeof value === "number") return { color: theme.yellow };
1668
+ if (typeof value === "boolean") {
1669
+ return { color: value ? theme.green : theme.red, bold: true };
1670
+ }
1671
+ if (value === null) return { color: theme.brightBlack, dim: true };
1672
+ return void 0;
1673
+ }
1674
+ function primitiveText(value) {
1675
+ if (value === null) return "null";
1676
+ if (typeof value === "string") return value;
1677
+ if (typeof value === "number" || typeof value === "boolean") return String(value);
1678
+ if (value === void 0) return "undefined";
1679
+ return String(value);
1680
+ }
1681
+ function isPrimitiveJsonValue(value) {
1682
+ return value === null || typeof value === "string" || typeof value === "number" || typeof value === "boolean";
1683
+ }
1684
+ function isPrimitiveArrayValue(value) {
1685
+ return Array.isArray(value) && value.every((item) => isPrimitiveJsonValue(item));
1686
+ }
1687
+ function segmentLength(segment) {
1688
+ return typeof segment === "string" ? segment.length : segment.text.length;
1689
+ }
1690
+ function lineLength(line) {
1691
+ let total = 0;
1692
+ for (const segment of line) total += segmentLength(segment);
1693
+ return total;
1694
+ }
1695
+ function primitiveSegment(value, theme) {
1696
+ const text = primitiveText(value);
1697
+ const style = primitiveStyle(value, theme);
1698
+ return style ? { text, ...style } : text;
1699
+ }
1700
+ function indentLayout(layout, indent) {
1701
+ return (w) => {
1702
+ if (indent <= 0) return layout(w);
1703
+ const pad2 = " ".repeat(indent);
1704
+ if (w <= indent) return layout(w);
1705
+ return layout(w - indent).map((line) => [pad2, ...line]);
1706
+ };
1707
+ }
1708
+ function sectionHeader(title, theme) {
1709
+ return (w) => {
1710
+ const label = ` ${title} `;
1711
+ const remaining = Math.max(0, w - label.length);
1712
+ return [[
1713
+ { text: label, color: theme.brightBlack, bold: true },
1714
+ { text: "\u2500".repeat(remaining), color: theme.border }
1715
+ ]];
1716
+ };
1717
+ }
1718
+ function isShallowSectionValue(value) {
1719
+ if (isRecord3(value)) {
1720
+ const vals = Object.values(value);
1721
+ return vals.every((item) => isPrimitiveJsonValue(item) || isPrimitiveArrayValue(item));
1722
+ }
1723
+ if (Array.isArray(value)) return value.every((item) => isPrimitiveJsonValue(item));
1724
+ return isPrimitiveJsonValue(value);
1725
+ }
1726
+ function stackWithSpacing(children) {
1727
+ if (children.length === 0) return blank();
1728
+ if (children.length === 1) return children[0];
1729
+ const layouts = [];
1730
+ children.forEach((child, index) => {
1731
+ if (index > 0) layouts.push(blank());
1732
+ layouts.push(child);
1733
+ });
1734
+ return vstack(...layouts);
1735
+ }
1736
+ function renderPrimitiveValue(value, theme) {
1737
+ return (w) => {
1738
+ const text = primitiveText(value);
1739
+ const style = primitiveStyle(value, theme);
1740
+ const rows = text.split("\n");
1741
+ const out = [];
1742
+ for (const row of rows) {
1743
+ const wrapped = wrapText2(row, w);
1744
+ for (const chunk of wrapped) {
1745
+ out.push([style ? { text: chunk, ...style } : chunk]);
1746
+ }
1747
+ }
1748
+ return out.length > 0 ? out : [[{ text: " ", dim: true }]];
1749
+ };
1750
+ }
1751
+ function renderStandalonePrimitiveArray(value, theme) {
1752
+ return (w) => {
1753
+ if (value.length === 0) {
1754
+ return [[{ text: "(empty array)", color: theme.brightBlack, dim: true }]];
1755
+ }
1756
+ const out = [];
1757
+ const digits = String(value.length).length;
1758
+ value.forEach((item, index) => {
1759
+ const prefix = ` ${String(index + 1).padStart(digits, " ")}. `;
1760
+ out.push(
1761
+ ...renderPrefixedText(
1762
+ primitiveText(item),
1763
+ w,
1764
+ { text: prefix, color: theme.brightBlack },
1765
+ " ".repeat(prefix.length),
1766
+ primitiveStyle(item, theme)
1767
+ )
1768
+ );
1769
+ });
1770
+ return out;
1771
+ };
1772
+ }
1773
+ function isTabularRecordArray(value) {
1774
+ if (value.length === 0) return false;
1775
+ if (!value.every((item) => isRecord3(item))) return false;
1776
+ const keys = Object.keys(value[0]);
1777
+ if (keys.length < 2) return false;
1778
+ return value.every((row) => {
1779
+ const rowKeys = Object.keys(row);
1780
+ if (rowKeys.length !== keys.length) return false;
1781
+ if (!keys.every((key) => rowKeys.includes(key))) return false;
1782
+ return keys.every((key) => isPrimitiveJsonValue(row[key]));
1783
+ });
1784
+ }
1785
+ function renderTabularRecordArray(rows, theme) {
1786
+ const keys = Object.keys(rows[0] ?? {});
1787
+ const columns = keys.map((key) => {
1788
+ const values = rows.map((row) => row[key]);
1789
+ const isBoolCol = values.every((v) => typeof v === "boolean");
1790
+ const isNumCol = values.every((v) => isNumericValue(v));
1791
+ return {
1792
+ key,
1793
+ title: key,
1794
+ align: isNumCol ? "right" : "left",
1795
+ headerColor: theme.cyan,
1796
+ color: isBoolCol ? (value) => value === true ? theme.green : value === false ? theme.red : void 0 : isNumCol ? theme.yellow : void 0
1797
+ };
1798
+ });
1799
+ return table(columns, rows, {
1800
+ borderColor: theme.border
1801
+ });
1802
+ }
1803
+ function renderArrayValue(value, theme) {
1804
+ if (value.length === 0) {
1805
+ return lines([[{ text: "(empty array)", color: theme.brightBlack, dim: true }]]);
1806
+ }
1807
+ if (isTabularRecordArray(value)) {
1808
+ return renderTabularRecordArray(value, theme);
1809
+ }
1810
+ if (value.every((item) => isPrimitiveJsonValue(item))) {
1811
+ return renderStandalonePrimitiveArray(value, theme);
1812
+ }
1813
+ const children = value.map(
1814
+ (item, index) => renderJsonSection(`item ${index + 1}`, item, theme)
1815
+ );
1816
+ return stackWithSpacing(children);
1817
+ }
1818
+ function renderPrimitiveRows(rows, theme) {
1819
+ return (w) => {
1820
+ const out = [];
1821
+ const keyWidth = Math.max(...rows.map((row) => row.key.length), 0);
1822
+ for (const row of rows) {
1823
+ const keyPrefix = ` ${row.key.padEnd(keyWidth)}: `;
1824
+ if (row.kind === "primitive") {
1825
+ out.push(
1826
+ ...renderPrefixedText(
1827
+ primitiveText(row.value),
1828
+ w,
1829
+ { text: keyPrefix, color: theme.brightBlack },
1830
+ " ".repeat(keyPrefix.length),
1831
+ primitiveStyle(row.value, theme)
1832
+ )
1833
+ );
1834
+ continue;
1835
+ }
1836
+ if (row.value.length === 0) {
1837
+ out.push([
1838
+ { text: keyPrefix, color: theme.brightBlack },
1839
+ { text: "(empty)", color: theme.brightBlack, dim: true }
1840
+ ]);
1841
+ continue;
1842
+ }
1843
+ const inlineSegments = [];
1844
+ row.value.forEach((item, index) => {
1845
+ if (index > 0) inlineSegments.push({ text: ", ", color: theme.brightBlack });
1846
+ inlineSegments.push(primitiveSegment(item, theme));
1847
+ });
1848
+ const inlineLine = [{ text: keyPrefix, color: theme.brightBlack }, ...inlineSegments];
1849
+ if (lineLength(inlineLine) <= w) {
1850
+ out.push(inlineLine);
1851
+ continue;
1852
+ }
1853
+ out.push([{ text: ` ${row.key}:`, color: theme.brightBlack }]);
1854
+ const digits = String(row.value.length).length;
1855
+ row.value.forEach((item, index) => {
1856
+ const listPrefix = ` ${String(index + 1).padStart(digits, " ")}. `;
1857
+ out.push(
1858
+ ...renderPrefixedText(
1859
+ primitiveText(item),
1860
+ w,
1861
+ { text: listPrefix, color: theme.brightBlack },
1862
+ " ".repeat(listPrefix.length),
1863
+ primitiveStyle(item, theme)
1864
+ )
1865
+ );
1866
+ });
1867
+ }
1868
+ return out;
1869
+ };
1870
+ }
1871
+ function renderObjectValue(value, theme) {
1872
+ const entries = Object.entries(value);
1873
+ if (entries.length === 0) {
1874
+ return lines([[{ text: "(empty object)", color: theme.brightBlack, dim: true }]]);
1875
+ }
1876
+ const groups = [];
1877
+ let pendingRows = [];
1878
+ const flushRows = () => {
1879
+ if (pendingRows.length === 0) return;
1880
+ groups.push(renderPrimitiveRows(pendingRows, theme));
1881
+ pendingRows = [];
1882
+ };
1883
+ for (const [key, item] of entries) {
1884
+ if (isPrimitiveJsonValue(item)) {
1885
+ pendingRows.push({ kind: "primitive", key, value: item });
1886
+ continue;
1887
+ }
1888
+ if (isPrimitiveArrayValue(item)) {
1889
+ pendingRows.push({ kind: "array", key, value: item });
1890
+ continue;
1891
+ }
1892
+ flushRows();
1893
+ groups.push(renderJsonSection(key, item, theme));
1894
+ }
1895
+ flushRows();
1896
+ return stackWithSpacing(groups);
1897
+ }
1898
+ function renderJsonContent(value, theme) {
1899
+ if (isRecord3(value)) return renderObjectValue(value, theme);
1900
+ if (Array.isArray(value)) return renderArrayValue(value, theme);
1901
+ return renderPrimitiveValue(value, theme);
1902
+ }
1903
+ function renderJsonSection(title, value, theme) {
1904
+ if (Array.isArray(value) && isTabularRecordArray(value)) {
1905
+ return vstack(sectionHeader(title, theme), indentLayout(renderJsonContent(value, theme), 1));
1906
+ }
1907
+ if (isShallowSectionValue(value)) {
1908
+ return vstack(sectionHeader(title, theme), indentLayout(renderJsonContent(value, theme), 1));
1909
+ }
1910
+ return box(renderJsonContent(value, theme), {
1911
+ title,
1912
+ borderColor: theme.border
1913
+ });
1914
+ }
1915
+ function fromJson(value, opts) {
1916
+ const theme = opts?.theme ?? dark;
1917
+ let parsedValue = value;
1918
+ if (typeof value === "string") {
1919
+ try {
1920
+ parsedValue = JSON.parse(value);
1921
+ } catch {
1922
+ parsedValue = value;
1923
+ }
1924
+ }
1925
+ if (isJsonNode(parsedValue)) return fromJsonNode(parsedValue);
1926
+ const content = renderJsonContent(parsedValue, theme);
1927
+ if (opts?.title) {
1928
+ return box(content, {
1929
+ title: opts.title,
1930
+ borderColor: theme.border
1931
+ });
1932
+ }
1933
+ return content;
1934
+ }
1935
+
1936
+ // src/document/index.ts
1937
+ function fromDocument(source, opts) {
1938
+ if (source.format === "markdown") {
1939
+ return fromMarkdown(source.value, {
1940
+ theme: opts?.theme,
1941
+ overrides: opts?.markdownOverrides
1942
+ });
1943
+ }
1944
+ if (source.format === "html") {
1945
+ return fromHtml(source.value, {
1946
+ theme: opts?.theme,
1947
+ overrides: opts?.markdownOverrides
1948
+ });
1949
+ }
1950
+ return fromJson(source.value, {
1951
+ theme: opts?.theme,
1952
+ title: opts?.jsonTitle
1953
+ });
1954
+ }
1955
+
1956
+ // src/theme-provider.tsx
1957
+ import {
1958
+ createContext,
1959
+ useContext,
1960
+ useEffect,
1961
+ useMemo,
1962
+ useState
1963
+ } from "react";
1964
+ import { jsx } from "react/jsx-runtime";
1965
+ var FALLBACK_THEME = resolveTheme(defaultThemeSpec, "dark");
1966
+ var noopThemeSpec = () => {
1967
+ };
1968
+ var noopModePreference = () => {
1969
+ };
1970
+ var TermThemeContext = createContext({
1971
+ themeSpec: defaultThemeSpec,
1972
+ modePreference: "dark",
1973
+ systemMode: "dark",
1974
+ theme: FALLBACK_THEME,
1975
+ setThemeSpec: noopThemeSpec,
1976
+ setModePreference: noopModePreference
1977
+ });
1978
+ function readSystemMode() {
1979
+ if (typeof window === "undefined" || typeof window.matchMedia !== "function") {
1980
+ return "dark";
1981
+ }
1982
+ return window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
1983
+ }
1984
+ function TermThemeProvider({
1985
+ children,
1986
+ initialTheme = defaultThemeSpec,
1987
+ initialMode = "system"
1988
+ }) {
1989
+ const [themeSpec, setThemeSpec] = useState(initialTheme);
1990
+ const [modePreference, setModePreference] = useState(initialMode);
1991
+ const [systemMode, setSystemMode] = useState(readSystemMode);
1992
+ useEffect(() => {
1993
+ if (typeof window === "undefined" || typeof window.matchMedia !== "function") return;
1994
+ const media = window.matchMedia("(prefers-color-scheme: dark)");
1995
+ const onChange = (event) => {
1996
+ setSystemMode(event.matches ? "dark" : "light");
1997
+ };
1998
+ if (typeof media.addEventListener === "function") {
1999
+ media.addEventListener("change", onChange);
2000
+ return () => media.removeEventListener("change", onChange);
2001
+ }
2002
+ media.addListener(onChange);
2003
+ return () => media.removeListener(onChange);
2004
+ }, []);
2005
+ const theme = useMemo(
2006
+ () => resolveTheme(themeSpec, modePreference, { systemMode }),
2007
+ [themeSpec, modePreference, systemMode]
2008
+ );
2009
+ const value = useMemo(
2010
+ () => ({
2011
+ themeSpec,
2012
+ modePreference,
2013
+ systemMode,
2014
+ theme,
2015
+ setThemeSpec,
2016
+ setModePreference
2017
+ }),
2018
+ [themeSpec, modePreference, systemMode, theme, setThemeSpec, setModePreference]
2019
+ );
2020
+ return /* @__PURE__ */ jsx(TermThemeContext.Provider, { value, children });
2021
+ }
2022
+ function useTermTheme() {
2023
+ return useContext(TermThemeContext);
2024
+ }
2025
+
2026
+ // src/render.tsx
2027
+ import {
2028
+ memo,
2029
+ useEffect as useEffect2,
2030
+ useLayoutEffect,
2031
+ useMemo as useMemo2,
2032
+ useRef,
2033
+ useSyncExternalStore
2034
+ } from "react";
2035
+
2036
+ // src/reconciler.ts
2037
+ import Reconciler from "react-reconciler";
2038
+ import { DefaultEventPriority } from "react-reconciler/constants";
2039
+ function isRecord4(value) {
2040
+ return value !== null && typeof value === "object" && !Array.isArray(value);
2041
+ }
2042
+ function asString(value) {
2043
+ return typeof value === "string" ? value : void 0;
2044
+ }
2045
+ function asNumber(value) {
2046
+ return typeof value === "number" ? value : void 0;
2047
+ }
2048
+ function asBoolean(value) {
2049
+ return typeof value === "boolean" ? value : void 0;
2050
+ }
2051
+ function asStringArray(value) {
2052
+ return Array.isArray(value) ? value.filter((item) => typeof item === "string") : [];
2053
+ }
2054
+ function asNumberArray(value) {
2055
+ if (!Array.isArray(value)) return void 0;
2056
+ const out = [];
2057
+ for (const item of value) {
2058
+ if (typeof item !== "number") return void 0;
2059
+ out.push(item);
2060
+ }
2061
+ return out;
2062
+ }
2063
+ function asRecordArray(value) {
2064
+ if (!Array.isArray(value)) return [];
2065
+ const out = [];
2066
+ for (const item of value) {
2067
+ if (isRecord4(item)) out.push(item);
2068
+ }
2069
+ return out;
2070
+ }
2071
+ function isMarkdownOverrides(value) {
2072
+ if (!isRecord4(value)) return false;
2073
+ if (value.headingMarker !== void 0 && typeof value.headingMarker !== "string") return false;
2074
+ if (value.headingBoldMaxLevel !== void 0 && typeof value.headingBoldMaxLevel !== "number") return false;
2075
+ if (value.paragraph !== void 0 && typeof value.paragraph !== "string") return false;
2076
+ if (value.unorderedBullet !== void 0 && typeof value.unorderedBullet !== "string") return false;
2077
+ if (value.orderedMarker !== void 0 && typeof value.orderedMarker !== "string") return false;
2078
+ if (value.tableHeader !== void 0 && typeof value.tableHeader !== "string") return false;
2079
+ if (value.tableBorder !== void 0 && typeof value.tableBorder !== "string") return false;
2080
+ if (value.quoteMarker !== void 0 && typeof value.quoteMarker !== "string") return false;
2081
+ if (value.quoteText !== void 0 && typeof value.quoteText !== "string") return false;
2082
+ if (value.separator !== void 0 && typeof value.separator !== "string") return false;
2083
+ if (value.codeText !== void 0 && typeof value.codeText !== "string") return false;
2084
+ if (value.codeEmpty !== void 0 && typeof value.codeEmpty !== "string") return false;
2085
+ if (value.codeBorder !== void 0 && typeof value.codeBorder !== "string") return false;
2086
+ if (value.headings !== void 0) {
2087
+ if (!isRecord4(value.headings)) return false;
2088
+ if (Object.values(value.headings).some(
2089
+ (headingColor2) => typeof headingColor2 !== "string"
2090
+ )) {
2091
+ return false;
2092
+ }
2093
+ }
2094
+ return true;
2095
+ }
2096
+ function asMarkdownOverrides(value) {
2097
+ return isMarkdownOverrides(value) ? value : void 0;
2098
+ }
2099
+ function isSegment(value) {
2100
+ if (typeof value === "string") return true;
2101
+ if (!isRecord4(value)) return false;
2102
+ if (typeof value.text !== "string") return false;
2103
+ if (value.color !== void 0 && typeof value.color !== "string") return false;
2104
+ if (value.bg !== void 0 && typeof value.bg !== "string") return false;
2105
+ if (value.bold !== void 0 && typeof value.bold !== "boolean") return false;
2106
+ if (value.dim !== void 0 && typeof value.dim !== "boolean") return false;
2107
+ if (value.inverted !== void 0 && typeof value.inverted !== "boolean") return false;
2108
+ if (value.blink !== void 0 && typeof value.blink !== "boolean") return false;
2109
+ if (value.onClick !== void 0 && typeof value.onClick !== "function") return false;
2110
+ if (value.noSelect !== void 0 && typeof value.noSelect !== "boolean") return false;
2111
+ if (value.cell !== void 0 && typeof value.cell !== "number") return false;
2112
+ return true;
2113
+ }
2114
+ function isColumn(value) {
2115
+ if (!isRecord4(value)) return false;
2116
+ if (typeof value.key !== "string") return false;
2117
+ if (value.title !== void 0 && typeof value.title !== "string") return false;
2118
+ if (value.width !== void 0 && typeof value.width !== "number") return false;
2119
+ if (value.align !== void 0 && value.align !== "left" && value.align !== "right") return false;
2120
+ if (value.headerColor !== void 0 && typeof value.headerColor !== "string") return false;
2121
+ if (value.headerBold !== void 0 && typeof value.headerBold !== "boolean") return false;
2122
+ if (value.color !== void 0 && typeof value.color !== "string" && typeof value.color !== "function") return false;
2123
+ if (value.dim !== void 0 && typeof value.dim !== "boolean" && typeof value.dim !== "function") return false;
2124
+ return true;
2125
+ }
2126
+ function asColumns(value) {
2127
+ if (!Array.isArray(value)) return [];
2128
+ const out = [];
2129
+ for (const item of value) {
2130
+ if (isColumn(item)) out.push(item);
2131
+ }
2132
+ return out;
2133
+ }
2134
+ function isTreeNode(value) {
2135
+ if (!isRecord4(value)) return false;
2136
+ if (typeof value.label !== "string") return false;
2137
+ if (value.children === void 0) return true;
2138
+ if (!Array.isArray(value.children)) return false;
2139
+ return value.children.every((child) => isTreeNode(child));
2140
+ }
2141
+ function asTreeNodes(value) {
2142
+ if (!Array.isArray(value)) return [];
2143
+ const out = [];
2144
+ for (const item of value) {
2145
+ if (isTreeNode(item)) out.push(item);
2146
+ }
2147
+ return out;
2148
+ }
2149
+ function isLayoutFn(value) {
2150
+ return typeof value === "function";
2151
+ }
2152
+ function isSelectHandler(value) {
2153
+ return typeof value === "function";
2154
+ }
2155
+ var TermNode = class {
2156
+ type;
2157
+ props;
2158
+ children = [];
2159
+ parent = null;
2160
+ text;
2161
+ constructor(type, props) {
2162
+ this.type = type;
2163
+ this.props = props;
2164
+ }
2165
+ appendChild(child) {
2166
+ child.parent = this;
2167
+ this.children.push(child);
2168
+ }
2169
+ removeChild(child) {
2170
+ const idx = this.children.indexOf(child);
2171
+ if (idx !== -1) {
2172
+ this.children.splice(idx, 1);
2173
+ child.parent = null;
2174
+ }
2175
+ }
2176
+ insertBefore(child, before) {
2177
+ child.parent = this;
2178
+ const idx = this.children.indexOf(before);
2179
+ if (idx !== -1) {
2180
+ this.children.splice(idx, 0, child);
2181
+ } else {
2182
+ this.children.push(child);
2183
+ }
2184
+ }
2185
+ };
2186
+ var TermRoot = class {
2187
+ children = [];
2188
+ theme;
2189
+ markdownOverrides;
2190
+ _layoutFn = blank();
2191
+ _listeners = /* @__PURE__ */ new Set();
2192
+ constructor(theme, markdownOverrides) {
2193
+ this.theme = theme;
2194
+ this.markdownOverrides = markdownOverrides;
2195
+ }
2196
+ appendChild(child) {
2197
+ child.parent = null;
2198
+ this.children.push(child);
2199
+ }
2200
+ removeChild(child) {
2201
+ const idx = this.children.indexOf(child);
2202
+ if (idx !== -1) {
2203
+ this.children.splice(idx, 1);
2204
+ child.parent = null;
2205
+ }
2206
+ }
2207
+ insertBefore(child, before) {
2208
+ child.parent = null;
2209
+ const idx = this.children.indexOf(before);
2210
+ if (idx !== -1) {
2211
+ this.children.splice(idx, 0, child);
2212
+ } else {
2213
+ this.children.push(child);
2214
+ }
2215
+ }
2216
+ commitTree() {
2217
+ this._layoutFn = compileTermChildren(
2218
+ this.children,
2219
+ this.theme,
2220
+ this.markdownOverrides
2221
+ );
2222
+ for (const cb of this._listeners) cb();
2223
+ }
2224
+ subscribe = (listener) => {
2225
+ this._listeners.add(listener);
2226
+ return () => {
2227
+ this._listeners.delete(listener);
2228
+ };
2229
+ };
2230
+ getLayoutFn = () => this._layoutFn;
2231
+ };
2232
+ function extractTermText(children) {
2233
+ return children.map((c) => {
2234
+ if (c.type === "t-text") return c.text ?? "";
2235
+ return extractTermText(c.children);
2236
+ }).join("");
2237
+ }
2238
+ function stackWithGap(children, gap = 0) {
2239
+ if (children.length === 0) return blank();
2240
+ if (children.length === 1 && gap <= 0) return children[0];
2241
+ const layouts = [];
2242
+ for (let i = 0; i < children.length; i++) {
2243
+ if (i > 0) {
2244
+ for (let j = 0; j < gap; j++) layouts.push(blank());
2245
+ }
2246
+ layouts.push(children[i]);
2247
+ }
2248
+ return vstack(...layouts);
2249
+ }
2250
+ function compileTermChildren(children, theme, mdOverrides) {
2251
+ const out = [];
2252
+ for (const child of children) {
2253
+ const layout = compileTermNode(child, theme, mdOverrides);
2254
+ if (layout) out.push(layout);
2255
+ }
2256
+ if (out.length === 0) return blank();
2257
+ if (out.length === 1) return out[0];
2258
+ return vstack(...out);
2259
+ }
2260
+ var tabHandlerCache = /* @__PURE__ */ new WeakMap();
2261
+ function cachedTabHandler(onSelect, index) {
2262
+ let map = tabHandlerCache.get(onSelect);
2263
+ if (!map) {
2264
+ map = /* @__PURE__ */ new Map();
2265
+ tabHandlerCache.set(onSelect, map);
2266
+ }
2267
+ let handler = map.get(index);
2268
+ if (!handler) {
2269
+ handler = () => onSelect(index);
2270
+ map.set(index, handler);
2271
+ }
2272
+ return handler;
2273
+ }
2274
+ function toSegments(value) {
2275
+ if (typeof value === "string") return [value];
2276
+ if (!Array.isArray(value)) return [];
2277
+ const out = [];
2278
+ for (const segment of value) {
2279
+ if (isSegment(segment)) out.push(segment);
2280
+ }
2281
+ return out;
2282
+ }
2283
+ function compileRaw(p, children) {
2284
+ const color = asString(p.color);
2285
+ const bg = asString(p.bg);
2286
+ const bold = asBoolean(p.bold);
2287
+ const dim = asBoolean(p.dim);
2288
+ const blink = asBoolean(p.blink);
2289
+ const inverted = asBoolean(p.inverted);
2290
+ const onClick = typeof p.onClick === "function" ? p.onClick : void 0;
2291
+ const rawText = asString(p.text) ?? extractTermText(children);
2292
+ const text = rawText.replace(/\r\n/g, "\n");
2293
+ const rows = text.length > 0 ? text.split("\n") : [" "];
2294
+ if (!color && !bg && bold === void 0 && dim === void 0 && blink === void 0 && inverted === void 0 && !onClick) {
2295
+ return lines(rows.map((row) => [row]));
2296
+ }
2297
+ return lines(
2298
+ rows.map((row) => [
2299
+ {
2300
+ text: row,
2301
+ color,
2302
+ bg,
2303
+ bold,
2304
+ dim,
2305
+ blink,
2306
+ inverted,
2307
+ onClick
2308
+ }
2309
+ ])
2310
+ );
2311
+ }
2312
+ function compileLine(children) {
2313
+ const segments = [];
2314
+ for (const child of children) {
2315
+ if (child.type === "t-text") {
2316
+ segments.push(child.text ?? "");
2317
+ continue;
2318
+ }
2319
+ if (child.type === "t-span") {
2320
+ const cp = child.props;
2321
+ const text = asString(cp.text) ?? extractTermText(child.children);
2322
+ const color = asString(cp.color);
2323
+ const bg = asString(cp.bg);
2324
+ const bold = asBoolean(cp.bold);
2325
+ const dim = asBoolean(cp.dim);
2326
+ const inverted = asBoolean(cp.inverted);
2327
+ const blink = asBoolean(cp.blink);
2328
+ if (color || bg || bold !== void 0 || dim !== void 0 || inverted !== void 0 || blink !== void 0) {
2329
+ segments.push({
2330
+ text,
2331
+ color,
2332
+ bg,
2333
+ bold,
2334
+ dim,
2335
+ inverted,
2336
+ blink
2337
+ });
2338
+ } else {
2339
+ segments.push(text);
2340
+ }
2341
+ } else {
2342
+ segments.push(extractTermText(child.children));
2343
+ }
2344
+ }
2345
+ return lines([segments]);
2346
+ }
2347
+ function compileTermNode(node, theme, mdOverrides) {
2348
+ if (node.type === "t-text") {
2349
+ return node.text ? lines([[node.text]]) : null;
2350
+ }
2351
+ const p = node.props;
2352
+ switch (node.type) {
2353
+ case "t-vstack": {
2354
+ const ch = compileChildLayouts(node.children, theme, mdOverrides);
2355
+ const gap = asNumber(p.gap) ?? 0;
2356
+ return stackWithGap(ch, gap);
2357
+ }
2358
+ case "t-hstack": {
2359
+ const ch = compileChildLayouts(node.children, theme, mdOverrides);
2360
+ if (ch.length === 0) return blank();
2361
+ return hstack(ch, {
2362
+ gap: asNumber(p.gap),
2363
+ widths: asNumberArray(p.widths)
2364
+ });
2365
+ }
2366
+ case "t-box": {
2367
+ const ch = compileChildLayouts(node.children, theme, mdOverrides);
2368
+ const gap = asNumber(p.gap) ?? 0;
2369
+ return box(stackWithGap(ch, gap), {
2370
+ title: asString(p.title),
2371
+ borderColor: asString(p.borderColor) ?? theme.semantic.border
2372
+ });
2373
+ }
2374
+ case "t-separator":
2375
+ return separator(asString(p.color) ?? theme.semantic.border);
2376
+ case "t-blank":
2377
+ return blank();
2378
+ case "t-raw":
2379
+ return compileRaw(p, node.children);
2380
+ case "t-line":
2381
+ return compileLine(node.children);
2382
+ case "t-layout": {
2383
+ const layout = p.layout;
2384
+ return isLayoutFn(layout) ? layout : blank();
2385
+ }
2386
+ case "t-table": {
2387
+ const columns = asColumns(p.columns);
2388
+ const data = asRecordArray(p.data);
2389
+ return table(columns, data, {
2390
+ borderColor: asString(p.borderColor) ?? theme.semantic.border,
2391
+ fillWidth: asBoolean(p.fillWidth),
2392
+ headerColor: asString(p.headerColor),
2393
+ cellPadding: asNumber(p.cellPadding)
2394
+ });
2395
+ }
2396
+ case "t-tab":
2397
+ return null;
2398
+ case "t-tabs": {
2399
+ const tabNodes = node.children.filter((c) => c.type === "t-tab");
2400
+ const names = tabNodes.map((t) => asString(t.props.name) ?? "");
2401
+ const active = asNumber(p.active) ?? 0;
2402
+ const onSelect = isSelectHandler(p.onSelect) ? p.onSelect : void 0;
2403
+ const onClicks = onSelect ? names.map((_, i) => cachedTabHandler(onSelect, i)) : void 0;
2404
+ const tabBar = tabs(names, active, {
2405
+ activeColor: asString(p.activeColor),
2406
+ activeBg: asString(p.activeBg),
2407
+ inactiveColor: asString(p.inactiveColor),
2408
+ separatorColor: asString(p.separatorColor),
2409
+ onClicks,
2410
+ theme
2411
+ });
2412
+ const activeNode = tabNodes[active];
2413
+ if (!activeNode || activeNode.children.length === 0) return tabBar;
2414
+ const content = compileChildLayouts(activeNode.children, theme, mdOverrides);
2415
+ if (content.length === 0) return tabBar;
2416
+ return vstack(tabBar, stackWithGap(content, 0));
2417
+ }
2418
+ case "t-statusbar":
2419
+ return statusbar(toSegments(p.left), toSegments(p.right), {
2420
+ bg: asString(p.bg),
2421
+ color: asString(p.color),
2422
+ theme
2423
+ });
2424
+ case "t-markdown":
2425
+ return fromMarkdown(asString(p.value) ?? "", {
2426
+ theme,
2427
+ overrides: asMarkdownOverrides(p.themeOverrides) ?? mdOverrides
2428
+ });
2429
+ case "t-json":
2430
+ return fromJson(p.value, {
2431
+ theme,
2432
+ title: asString(p.title)
2433
+ });
2434
+ case "t-html":
2435
+ return fromHtml(asString(p.value) ?? "", {
2436
+ theme,
2437
+ overrides: asMarkdownOverrides(p.themeOverrides) ?? mdOverrides
2438
+ });
2439
+ case "t-tree":
2440
+ return tree(asTreeNodes(p.data), {
2441
+ color: asString(p.color),
2442
+ branchColor: asString(p.branchColor) ?? theme.semantic.border,
2443
+ leafColor: asString(p.leafColor)
2444
+ });
2445
+ case "t-code":
2446
+ return code(asString(p.code) ?? "", {
2447
+ language: asString(p.language),
2448
+ title: asString(p.title),
2449
+ borderColor: asString(p.borderColor) ?? theme.markdown.codeBorder,
2450
+ theme
2451
+ });
2452
+ case "t-diff":
2453
+ return diff(asString(p.value) ?? "", {
2454
+ addColor: asString(p.addColor),
2455
+ removeColor: asString(p.removeColor),
2456
+ metaColor: asString(p.metaColor),
2457
+ theme
2458
+ });
2459
+ case "t-bar": {
2460
+ const label = asString(p.label) ?? "";
2461
+ const value = asNumber(p.value) ?? 0;
2462
+ const max = asNumber(p.max) ?? 100;
2463
+ const barColor = asString(p.color);
2464
+ return (w) => {
2465
+ const safeMax = Number.isFinite(max) && max > 0 ? max : 0;
2466
+ const safeValue = Number.isFinite(value) ? value : 0;
2467
+ const pct = safeMax > 0 ? Math.round(safeValue / safeMax * 100) : 0;
2468
+ const pctStr = `${String(pct).padStart(3)}%`;
2469
+ const barW = Math.max(0, w - label.length - 2 - 1 - pctStr.length);
2470
+ const barStr = bar(safeValue, safeMax, barW);
2471
+ const segments = [
2472
+ ` ${label} `,
2473
+ barColor ? { text: barStr, color: barColor } : barStr,
2474
+ ` ${pctStr}`
2475
+ ];
2476
+ return [segments];
2477
+ };
2478
+ }
2479
+ case "t-spark": {
2480
+ const data = Array.isArray(p.data) ? p.data.filter((value) => typeof value === "number") : [];
2481
+ const sparkColor = asString(p.color);
2482
+ return (w) => {
2483
+ const sparkW = Math.max(0, w - 2);
2484
+ const sparkStr = spark(data, sparkW);
2485
+ const segments = [
2486
+ " ",
2487
+ sparkColor ? { text: sparkStr, color: sparkColor } : sparkStr
2488
+ ];
2489
+ return [segments];
2490
+ };
2491
+ }
2492
+ case "t-list":
2493
+ return list(asStringArray(p.items), {
2494
+ ordered: asBoolean(p.ordered),
2495
+ bulletColor: asString(p.bulletColor),
2496
+ color: asString(p.color)
2497
+ });
2498
+ case "t-button": {
2499
+ const label = asString(p.label) ?? "";
2500
+ const btnColor = asString(p.color);
2501
+ const btnBg = asString(p.bg);
2502
+ const onClick = typeof p.onClick === "function" ? p.onClick : void 0;
2503
+ const resolvedBtnColor = btnColor ?? theme.components.button.fg;
2504
+ const resolvedBtnBg = btnBg ?? theme.components.button.bg;
2505
+ const decorated = `[ ${label} ]`;
2506
+ return (w) => {
2507
+ const lp = Math.max(0, Math.floor((w - decorated.length) / 2));
2508
+ const rp = Math.max(0, w - lp - decorated.length);
2509
+ const segments = [];
2510
+ if (lp > 0) segments.push(" ".repeat(lp));
2511
+ segments.push({ text: decorated, color: resolvedBtnColor, bg: resolvedBtnBg, onClick });
2512
+ if (rp > 0) segments.push(" ".repeat(rp));
2513
+ return [segments];
2514
+ };
2515
+ }
2516
+ default: {
2517
+ const ch = compileChildLayouts(node.children, theme, mdOverrides);
2518
+ return ch.length > 0 ? stackWithGap(ch, 0) : null;
2519
+ }
2520
+ }
2521
+ }
2522
+ function compileChildLayouts(children, theme, mdOverrides) {
2523
+ const out = [];
2524
+ for (const child of children) {
2525
+ const layout = compileTermNode(child, theme, mdOverrides);
2526
+ if (layout) out.push(layout);
2527
+ }
2528
+ return out;
2529
+ }
2530
+ var noop = () => {
2531
+ };
2532
+ var currentUpdatePriority = 0;
2533
+ var hostTransitionContext = {
2534
+ $$typeof: /* @__PURE__ */ Symbol.for("react.context"),
2535
+ Consumer: null,
2536
+ Provider: null,
2537
+ _currentValue: null,
2538
+ _currentValue2: null,
2539
+ _threadCount: 0
2540
+ };
2541
+ var hostConfig = {
2542
+ supportsMutation: true,
2543
+ supportsPersistence: false,
2544
+ supportsHydration: false,
2545
+ isPrimaryRenderer: false,
2546
+ // ── Core ────────────────────────────────────────────────────
2547
+ createInstance(type, props) {
2548
+ return new TermNode(type, { ...props });
2549
+ },
2550
+ createTextInstance(text) {
2551
+ const node = new TermNode("t-text", {});
2552
+ node.text = text;
2553
+ return node;
2554
+ },
2555
+ appendInitialChild(parent, child) {
2556
+ parent.appendChild(child);
2557
+ },
2558
+ finalizeInitialChildren() {
2559
+ return false;
2560
+ },
2561
+ shouldSetTextContent() {
2562
+ return false;
2563
+ },
2564
+ getRootHostContext() {
2565
+ return {};
2566
+ },
2567
+ getChildHostContext(ctx) {
2568
+ return ctx;
2569
+ },
2570
+ getPublicInstance(instance) {
2571
+ return instance;
2572
+ },
2573
+ prepareForCommit() {
2574
+ return null;
2575
+ },
2576
+ resetAfterCommit(container) {
2577
+ container.commitTree();
2578
+ },
2579
+ preparePortalMount: noop,
2580
+ scheduleTimeout: setTimeout,
2581
+ cancelTimeout: clearTimeout,
2582
+ noTimeout: -1,
2583
+ supportsMicrotasks: true,
2584
+ scheduleMicrotask: queueMicrotask,
2585
+ // ── Mutation ────────────────────────────────────────────────
2586
+ appendChild(parent, child) {
2587
+ parent.appendChild(child);
2588
+ },
2589
+ appendChildToContainer(container, child) {
2590
+ container.appendChild(child);
2591
+ },
2592
+ removeChild(parent, child) {
2593
+ parent.removeChild(child);
2594
+ },
2595
+ removeChildFromContainer(container, child) {
2596
+ container.removeChild(child);
2597
+ },
2598
+ insertBefore(parent, child, before) {
2599
+ parent.insertBefore(child, before);
2600
+ },
2601
+ insertInContainerBefore(container, child, before) {
2602
+ container.insertBefore(child, before);
2603
+ },
2604
+ commitUpdate(instance, _type, _prevProps, nextProps) {
2605
+ instance.props = { ...nextProps };
2606
+ },
2607
+ commitTextUpdate(textInstance, _oldText, newText) {
2608
+ textInstance.text = newText;
2609
+ },
2610
+ hideInstance: noop,
2611
+ hideTextInstance: noop,
2612
+ unhideInstance: noop,
2613
+ unhideTextInstance: noop,
2614
+ clearContainer(container) {
2615
+ container.children.length = 0;
2616
+ },
2617
+ // ── Required stubs (React 19) ─────────────────────────────
2618
+ getInstanceFromNode() {
2619
+ return null;
2620
+ },
2621
+ beforeActiveInstanceBlur: noop,
2622
+ afterActiveInstanceBlur: noop,
2623
+ prepareScopeUpdate: noop,
2624
+ getInstanceFromScope() {
2625
+ return null;
2626
+ },
2627
+ detachDeletedInstance: noop,
2628
+ // Priority
2629
+ setCurrentUpdatePriority(newPriority) {
2630
+ currentUpdatePriority = newPriority;
2631
+ },
2632
+ getCurrentUpdatePriority() {
2633
+ return currentUpdatePriority;
2634
+ },
2635
+ resolveUpdatePriority() {
2636
+ return currentUpdatePriority || DefaultEventPriority;
2637
+ },
2638
+ // Forms
2639
+ resetFormInstance: noop,
2640
+ // Paint callback
2641
+ requestPostPaintCallback: noop,
2642
+ // Transitions
2643
+ shouldAttemptEagerTransition() {
2644
+ return false;
2645
+ },
2646
+ trackSchedulerEvent: noop,
2647
+ // Events
2648
+ resolveEventType() {
2649
+ return null;
2650
+ },
2651
+ resolveEventTimeStamp() {
2652
+ return Date.now();
2653
+ },
2654
+ // Suspense commit
2655
+ maySuspendCommit() {
2656
+ return false;
2657
+ },
2658
+ preloadInstance() {
2659
+ return true;
2660
+ },
2661
+ startSuspendingCommit: noop,
2662
+ suspendInstance: noop,
2663
+ waitForCommitToBeReady() {
2664
+ return null;
2665
+ },
2666
+ NotPendingTransition: null,
2667
+ HostTransitionContext: hostTransitionContext
2668
+ };
2669
+ var reconciler = Reconciler(hostConfig);
2670
+ function createTermContainer(theme, markdownOverrides) {
2671
+ const root = new TermRoot(theme, markdownOverrides);
2672
+ const container = reconciler.createContainer(
2673
+ root,
2674
+ 0,
2675
+ // LegacyRoot
2676
+ null,
2677
+ false,
2678
+ null,
2679
+ "",
2680
+ (err) => console.error("termui uncaught:", err),
2681
+ (err) => console.error("termui caught:", err),
2682
+ (err) => console.error("termui recoverable:", err),
2683
+ noop
2684
+ );
2685
+ return { root, container };
2686
+ }
2687
+ function updateTermContainer(container, element) {
2688
+ reconciler.updateContainer(element, container, null, null);
2689
+ }
2690
+ function flushTermSync(fn) {
2691
+ if (fn) {
2692
+ reconciler.flushSync(fn);
2693
+ } else {
2694
+ reconciler.flushSync();
2695
+ }
2696
+ }
2697
+
2698
+ // src/render.tsx
2699
+ import { Fragment, jsx as jsx2, jsxs } from "react/jsx-runtime";
2700
+ function segmentStyle(seg, theme) {
2701
+ const style = {};
2702
+ if (seg.inverted) {
2703
+ style.color = seg.color ?? theme.semantic.inverseFg;
2704
+ style.backgroundColor = seg.bg ?? theme.semantic.inverseBg;
2705
+ } else {
2706
+ if (seg.color) style.color = seg.color;
2707
+ if (seg.bg) style.backgroundColor = seg.bg;
2708
+ }
2709
+ if (seg.bold) style.fontWeight = "bold";
2710
+ if (seg.dim) style.opacity = 0.6;
2711
+ if (seg.blink) style.animation = "bk 1s step-end infinite";
2712
+ if (seg.onClick) style.cursor = "pointer";
2713
+ if (seg.noSelect) style.userSelect = "none";
2714
+ return style;
2715
+ }
2716
+ var TermSegment = memo(
2717
+ function TermSegment2({ seg, theme }) {
2718
+ if (typeof seg === "string") return /* @__PURE__ */ jsx2("span", { children: seg });
2719
+ const style = segmentStyle(seg, theme);
2720
+ return /* @__PURE__ */ jsx2("span", { className: seg.onClick ? "termui-clickable" : void 0, style, onClick: seg.onClick, children: seg.text });
2721
+ },
2722
+ (prev, next) => prev.seg === next.seg && prev.theme === next.theme
2723
+ );
2724
+ function renderSegment(seg, key, theme) {
2725
+ return /* @__PURE__ */ jsx2(TermSegment, { seg, theme }, key);
2726
+ }
2727
+ function sameSegment(a, b) {
2728
+ if (typeof a === "string" || typeof b === "string") return a === b;
2729
+ return a.text === b.text && a.color === b.color && a.bg === b.bg && a.bold === b.bold && a.dim === b.dim && a.inverted === b.inverted && a.blink === b.blink && a.onClick === b.onClick && a.noSelect === b.noSelect && a.cell === b.cell;
2730
+ }
2731
+ function sameLine(a, b) {
2732
+ if (a.length !== b.length) return false;
2733
+ for (let i = 0; i < a.length; i++) {
2734
+ if (!sameSegment(a[i], b[i])) return false;
2735
+ }
2736
+ return true;
2737
+ }
2738
+ function reconcileLine(prevLine, nextLine) {
2739
+ if (!prevLine || prevLine.length === 0) return nextLine;
2740
+ if (sameLine(prevLine, nextLine)) return prevLine;
2741
+ const reconciled = new Array(nextLine.length);
2742
+ for (let i = 0; i < nextLine.length; i++) {
2743
+ const nextSeg = nextLine[i];
2744
+ const prevSeg = prevLine[i];
2745
+ reconciled[i] = prevSeg !== void 0 && sameSegment(prevSeg, nextSeg) ? prevSeg : nextSeg;
2746
+ }
2747
+ preserveTableTag(nextLine, reconciled);
2748
+ return reconciled;
2749
+ }
2750
+ function reconcileBlock(prevBlock, nextBlock) {
2751
+ if (!prevBlock) return nextBlock;
2752
+ let changed = prevBlock.length !== nextBlock.length;
2753
+ const next = new Array(nextBlock.length);
2754
+ for (let i = 0; i < nextBlock.length; i++) {
2755
+ const line = reconcileLine(prevBlock[i], nextBlock[i]);
2756
+ next[i] = line;
2757
+ if (!changed && line !== prevBlock[i]) changed = true;
2758
+ }
2759
+ return changed ? next : prevBlock;
2760
+ }
2761
+ function useReconciledBlock(block) {
2762
+ const prevRef = useRef(null);
2763
+ return useMemo2(() => {
2764
+ const prev = prevRef.current;
2765
+ if (!prev) {
2766
+ prevRef.current = block;
2767
+ return block;
2768
+ }
2769
+ const next = reconcileBlock(prev, block);
2770
+ prevRef.current = next;
2771
+ return next;
2772
+ }, [block]);
2773
+ }
2774
+ var TermLine = memo(function TermLine2({ line, theme }) {
2775
+ return /* @__PURE__ */ jsx2("div", { className: "termui-line", children: line.map((seg, i) => renderSegment(seg, i, theme)) });
2776
+ });
2777
+ function isAllNoSelect(line) {
2778
+ for (const seg of line) {
2779
+ if (typeof seg === "string") return false;
2780
+ if (!seg.noSelect) return false;
2781
+ }
2782
+ return true;
2783
+ }
2784
+ function segText(seg) {
2785
+ return typeof seg === "string" ? seg : seg.text;
2786
+ }
2787
+ function trimSeg(seg) {
2788
+ if (typeof seg === "string") {
2789
+ const t2 = seg.trimEnd();
2790
+ return t2 === seg ? seg : t2;
2791
+ }
2792
+ const t = seg.text.trimEnd();
2793
+ return t === seg.text ? seg : { ...seg, text: t };
2794
+ }
2795
+ function getTableCols(lines2) {
2796
+ const row = lines2.find((l) => !isAllNoSelect(l));
2797
+ if (!row) return null;
2798
+ const cols = [];
2799
+ for (let i = 0; i < row.length; i++) {
2800
+ const seg = row[i];
2801
+ if (typeof seg !== "string" && seg.cell !== void 0 && !seg.noSelect) {
2802
+ const prev = row[i - 1];
2803
+ const bw = prev ? segText(prev).length : 0;
2804
+ cols.push({ totalWidth: bw + seg.text.length });
2805
+ }
2806
+ }
2807
+ const last = row[row.length - 1];
2808
+ const rightBorderW = last ? segText(last).length : 1;
2809
+ return { cols, rightBorderW };
2810
+ }
2811
+ var TermTableBorderRow = memo(function TermTableBorderRow2({
2812
+ line,
2813
+ totalCols,
2814
+ theme
2815
+ }) {
2816
+ return /* @__PURE__ */ jsx2("tr", { className: "termui-line", children: /* @__PURE__ */ jsx2("td", { colSpan: totalCols, className: "termui-border-cell", children: line.map((seg, i) => renderSegment(seg, i, theme)) }) });
2817
+ });
2818
+ var TermTableContentRow = memo(function TermTableContentRow2({ line, theme }) {
2819
+ const cells = [];
2820
+ for (let i = 0; i < line.length; i++) {
2821
+ const seg = line[i];
2822
+ if (typeof seg !== "string" && seg.cell !== void 0 && !seg.noSelect) {
2823
+ const prev = line[i - 1];
2824
+ const borderChar = prev ? segText(prev) : "";
2825
+ const borderColor = prev && typeof prev !== "string" ? prev.color : void 0;
2826
+ cells.push(
2827
+ /* @__PURE__ */ jsxs("td", { className: "termui-cell", children: [
2828
+ /* @__PURE__ */ jsx2("span", { className: "termui-border-span", style: borderColor ? { color: borderColor } : void 0, children: borderChar }),
2829
+ renderSegment(trimSeg(seg), 0, theme)
2830
+ ] }, seg.cell)
2831
+ );
2832
+ }
2833
+ }
2834
+ const last = line[line.length - 1];
2835
+ if (last && typeof last !== "string" && last.noSelect) {
2836
+ cells.push(
2837
+ /* @__PURE__ */ jsx2("td", { className: "termui-border-cell", children: renderSegment(last, 0, theme) }, "rb")
2838
+ );
2839
+ }
2840
+ return /* @__PURE__ */ jsx2("tr", { className: "termui-line", children: cells });
2841
+ });
2842
+ var TermTable = memo(
2843
+ function TermTable2({ lines: lines2, theme }) {
2844
+ const info = getTableCols(lines2);
2845
+ if (!info) {
2846
+ return /* @__PURE__ */ jsx2(Fragment, { children: lines2.map((line, i) => /* @__PURE__ */ jsx2(TermLine, { line, theme }, i)) });
2847
+ }
2848
+ const totalCols = info.cols.length + 1;
2849
+ return /* @__PURE__ */ jsxs("table", { className: "termui-table", children: [
2850
+ /* @__PURE__ */ jsxs("colgroup", { children: [
2851
+ info.cols.map((col, i) => /* @__PURE__ */ jsx2("col", { style: { width: col.totalWidth + "ch" } }, i)),
2852
+ /* @__PURE__ */ jsx2("col", { style: { width: info.rightBorderW + "ch" } }, "rb")
2853
+ ] }),
2854
+ /* @__PURE__ */ jsx2("tbody", { children: lines2.map((line, i) => {
2855
+ if (isAllNoSelect(line)) {
2856
+ return /* @__PURE__ */ jsx2(TermTableBorderRow, { line, totalCols, theme }, i);
2857
+ }
2858
+ return /* @__PURE__ */ jsx2(TermTableContentRow, { line, theme }, i);
2859
+ }) })
2860
+ ] });
2861
+ },
2862
+ (prev, next) => {
2863
+ if (prev.theme !== next.theme) return false;
2864
+ const a = prev.lines, b = next.lines;
2865
+ if (a.length !== b.length) return false;
2866
+ for (let i = 0; i < a.length; i++) {
2867
+ if (a[i] !== b[i]) return false;
2868
+ }
2869
+ return true;
2870
+ }
2871
+ );
2872
+ var TermBlock = memo(
2873
+ function TermBlock2({ block, theme }) {
2874
+ const elements = [];
2875
+ let tableLines = null;
2876
+ let tableStart = 0;
2877
+ const flushTable = () => {
2878
+ if (tableLines) {
2879
+ elements.push(/* @__PURE__ */ jsx2(TermTable, { lines: tableLines, theme }, `t${tableStart}`));
2880
+ tableLines = null;
2881
+ }
2882
+ };
2883
+ for (let i = 0; i < block.length; i++) {
2884
+ const line = block[i];
2885
+ if (isTableLine(line)) {
2886
+ if (!tableLines) {
2887
+ tableLines = [];
2888
+ tableStart = i;
2889
+ }
2890
+ tableLines.push(line);
2891
+ } else {
2892
+ flushTable();
2893
+ elements.push(/* @__PURE__ */ jsx2(TermLine, { line, theme }, i));
2894
+ }
2895
+ }
2896
+ flushTable();
2897
+ return /* @__PURE__ */ jsx2(Fragment, { children: elements });
2898
+ },
2899
+ (prev, next) => prev.block === next.block && prev.theme === next.theme
2900
+ );
2901
+ function TermFrame({ width, theme, className, style, children }) {
2902
+ const frameStyle = {
2903
+ width: width + "ch",
2904
+ ...themeToCssVars(theme),
2905
+ ...style
2906
+ };
2907
+ return /* @__PURE__ */ jsx2(
2908
+ "div",
2909
+ {
2910
+ className: `termui-frame${className ? ` ${className}` : ""}`,
2911
+ style: frameStyle,
2912
+ children
2913
+ }
2914
+ );
2915
+ }
2916
+ function TermUIBase({
2917
+ block,
2918
+ children,
2919
+ width = 80,
2920
+ theme,
2921
+ themeSpec,
2922
+ mode,
2923
+ markdownOverrides,
2924
+ className,
2925
+ style
2926
+ }) {
2927
+ const themeContext = useTermTheme();
2928
+ const resolvedTheme = useMemo2(() => {
2929
+ if (theme) return theme;
2930
+ if (!themeSpec && !mode) return themeContext.theme;
2931
+ return resolveTheme(themeSpec ?? themeContext.themeSpec, mode ?? themeContext.modePreference, {
2932
+ systemMode: themeContext.systemMode
2933
+ });
2934
+ }, [
2935
+ theme,
2936
+ themeSpec,
2937
+ mode,
2938
+ themeContext.theme,
2939
+ themeContext.themeSpec,
2940
+ themeContext.modePreference,
2941
+ themeContext.systemMode
2942
+ ]);
2943
+ const containerRef = useRef(null);
2944
+ if (!containerRef.current && children) {
2945
+ containerRef.current = createTermContainer(resolvedTheme, markdownOverrides);
2946
+ }
2947
+ const root = containerRef.current?.root;
2948
+ const container = containerRef.current?.container;
2949
+ const layoutFn = useSyncExternalStore(
2950
+ root?.subscribe ?? noopSubscribe,
2951
+ root?.getLayoutFn ?? getBlankLayout,
2952
+ root?.getLayoutFn ?? getBlankLayout
2953
+ );
2954
+ useLayoutEffect(() => {
2955
+ if (!root || !container) return;
2956
+ root.theme = resolvedTheme;
2957
+ root.markdownOverrides = markdownOverrides;
2958
+ updateTermContainer(container, children ?? null);
2959
+ }, [root, container, children, resolvedTheme, markdownOverrides]);
2960
+ useEffect2(() => {
2961
+ return () => {
2962
+ if (containerRef.current) {
2963
+ updateTermContainer(containerRef.current.container, null);
2964
+ }
2965
+ };
2966
+ }, []);
2967
+ const computedBlock = block ?? (children ? layoutFn(width) : []);
2968
+ const reconciledBlock = useReconciledBlock(computedBlock);
2969
+ return /* @__PURE__ */ jsx2(TermFrame, { width, theme: resolvedTheme, className, style, children: /* @__PURE__ */ jsx2(TermBlock, { block: reconciledBlock, theme: resolvedTheme }) });
2970
+ }
2971
+ var _blankLayout = () => [];
2972
+ function noopSubscribe() {
2973
+ return () => {
2974
+ };
2975
+ }
2976
+ function getBlankLayout() {
2977
+ return _blankLayout;
2978
+ }
2979
+ var TermUI = memo(TermUIBase);
2980
+ var render_default = TermUI;
2981
+ function TermDocument({
2982
+ source,
2983
+ width = 80,
2984
+ theme,
2985
+ themeSpec,
2986
+ mode,
2987
+ jsonTitle,
2988
+ markdownOverrides,
2989
+ className,
2990
+ style
2991
+ }) {
2992
+ const themeContext = useTermTheme();
2993
+ const resolvedTheme = useMemo2(() => {
2994
+ if (theme) return theme;
2995
+ if (!themeSpec && !mode) return themeContext.theme;
2996
+ return resolveTheme(themeSpec ?? themeContext.themeSpec, mode ?? themeContext.modePreference, {
2997
+ systemMode: themeContext.systemMode
2998
+ });
2999
+ }, [
3000
+ theme,
3001
+ themeSpec,
3002
+ mode,
3003
+ themeContext.theme,
3004
+ themeContext.themeSpec,
3005
+ themeContext.modePreference,
3006
+ themeContext.systemMode
3007
+ ]);
3008
+ const layout = useMemo2(
3009
+ () => fromDocument(source, { theme: resolvedTheme, jsonTitle, markdownOverrides }),
3010
+ [source, resolvedTheme, jsonTitle, markdownOverrides]
3011
+ );
3012
+ const block = useMemo2(() => layout(width), [layout, width]);
3013
+ return /* @__PURE__ */ jsx2(TermUI, { block, width, theme: resolvedTheme, className, style });
3014
+ }
3015
+
3016
+ // src/term-react.tsx
3017
+ import { createElement } from "react";
3018
+ function TVStack({ children, gap }) {
3019
+ return createElement("t-vstack", { gap }, children);
3020
+ }
3021
+ function THStack({ children, gap, widths }) {
3022
+ return createElement("t-hstack", { gap, widths }, children);
3023
+ }
3024
+ function TBox({ children, title, borderColor, gap }) {
3025
+ return createElement("t-box", { title, borderColor, gap }, children);
3026
+ }
3027
+ function TSeparator({ color }) {
3028
+ return createElement("t-separator", { color });
3029
+ }
3030
+ function TBlank() {
3031
+ return createElement("t-blank", {});
3032
+ }
3033
+ function TRaw({ children, text, color, bg, bold, dim, blink, inverted, onClick }) {
3034
+ return createElement(
3035
+ "t-raw",
3036
+ { text, color, bg, bold, dim, blink, inverted, onClick },
3037
+ children
3038
+ );
3039
+ }
3040
+ function TTable({
3041
+ columns,
3042
+ data,
3043
+ borderColor,
3044
+ fillWidth,
3045
+ headerColor,
3046
+ cellPadding
3047
+ }) {
3048
+ return createElement("t-table", {
3049
+ columns,
3050
+ data,
3051
+ borderColor,
3052
+ fillWidth,
3053
+ headerColor,
3054
+ cellPadding
3055
+ });
3056
+ }
3057
+ function TTab({ name, children }) {
3058
+ return createElement("t-tab", { name }, children);
3059
+ }
3060
+ function TTabs({
3061
+ children,
3062
+ active,
3063
+ activeColor,
3064
+ activeBg,
3065
+ inactiveColor,
3066
+ separatorColor,
3067
+ onSelect
3068
+ }) {
3069
+ return createElement("t-tabs", {
3070
+ active,
3071
+ activeColor,
3072
+ activeBg,
3073
+ inactiveColor,
3074
+ separatorColor,
3075
+ onSelect
3076
+ }, children);
3077
+ }
3078
+ function TStatusbar({ left, right, bg, color }) {
3079
+ return createElement("t-statusbar", { left, right, bg, color });
3080
+ }
3081
+ function TMarkdown({ value, themeOverrides }) {
3082
+ return createElement("t-markdown", { value, themeOverrides });
3083
+ }
3084
+ function TJson({ value, title }) {
3085
+ return createElement("t-json", { value, title });
3086
+ }
3087
+ function THtml({ value, themeOverrides }) {
3088
+ return createElement("t-html", { value, themeOverrides });
3089
+ }
3090
+ function TLine({ children }) {
3091
+ return createElement("t-line", {}, children);
3092
+ }
3093
+ function TSpan({ children, text, color, bg, bold, dim, inverted, blink }) {
3094
+ return createElement(
3095
+ "t-span",
3096
+ { text, color, bg, bold, dim, inverted, blink },
3097
+ children
3098
+ );
3099
+ }
3100
+ function TLayout({ layout }) {
3101
+ return createElement("t-layout", { layout });
3102
+ }
3103
+ function TTree({ data, color, branchColor, leafColor }) {
3104
+ return createElement("t-tree", { data, color, branchColor, leafColor });
3105
+ }
3106
+ function TCode({ code: code2, language, title, borderColor }) {
3107
+ return createElement("t-code", { code: code2, language, title, borderColor });
3108
+ }
3109
+ function TDiff({ value, addColor, removeColor, metaColor }) {
3110
+ return createElement("t-diff", { value, addColor, removeColor, metaColor });
3111
+ }
3112
+ function TBar({ label, value, max, color }) {
3113
+ return createElement("t-bar", { label, value, max, color });
3114
+ }
3115
+ function TSpark({ data, color }) {
3116
+ return createElement("t-spark", { data, color });
3117
+ }
3118
+ function TList({ items, ordered, bulletColor, color }) {
3119
+ return createElement("t-list", { items, ordered, bulletColor, color });
3120
+ }
3121
+ function TButton({ label, onClick, color, bg }) {
3122
+ return createElement("t-button", { label, onClick, color, bg });
3123
+ }
3124
+ function renderTermReact(tree2, opts) {
3125
+ const resolvedTheme = opts?.theme ?? resolveTheme(opts?.themeSpec ?? defaultThemeSpec, opts?.mode ?? "dark");
3126
+ const { root, container } = createTermContainer(
3127
+ resolvedTheme,
3128
+ opts?.markdownOverrides
3129
+ );
3130
+ flushTermSync(() => updateTermContainer(container, tree2));
3131
+ const layout = root.getLayoutFn();
3132
+ updateTermContainer(container, null);
3133
+ return (w) => layout(w);
3134
+ }
3135
+
3136
+ // src/hooks.ts
3137
+ import { useState as useState2, useEffect as useEffect3, useRef as useRef2 } from "react";
3138
+ var FRAMES = ["\u280B", "\u2819", "\u2839", "\u2838", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
3139
+ function useSpinner(ms = 80) {
3140
+ const [frame, setFrame] = useState2(0);
3141
+ useEffect3(() => {
3142
+ const id = setInterval(() => setFrame((v) => (v + 1) % FRAMES.length), ms);
3143
+ return () => clearInterval(id);
3144
+ }, [ms]);
3145
+ return FRAMES[frame % FRAMES.length];
3146
+ }
3147
+ function useTick(ms = 500) {
3148
+ const [tick, setTick] = useState2(0);
3149
+ useEffect3(() => {
3150
+ const id = setInterval(() => setTick((v) => v + 1), ms);
3151
+ return () => clearInterval(id);
3152
+ }, [ms]);
3153
+ return tick;
3154
+ }
3155
+ function useTermWidth(ref, fallback = 80) {
3156
+ const [width, setWidth] = useState2(fallback);
3157
+ useEffect3(() => {
3158
+ const el = ref.current;
3159
+ if (!el) return;
3160
+ const m = document.createElement("span");
3161
+ m.style.cssText = "font:inherit;visibility:hidden;position:absolute;white-space:pre";
3162
+ m.textContent = "0".repeat(100);
3163
+ el.appendChild(m);
3164
+ const ch = m.getBoundingClientRect().width / 100;
3165
+ el.removeChild(m);
3166
+ if (ch <= 0) return;
3167
+ const update = () => setWidth(Math.floor(el.clientWidth / ch) || fallback);
3168
+ update();
3169
+ const ro = new ResizeObserver(update);
3170
+ ro.observe(el);
3171
+ return () => ro.disconnect();
3172
+ }, [ref, fallback]);
3173
+ return width;
3174
+ }
3175
+ function useStreamingText(text, lerp = 0.08) {
3176
+ const visibleRef = useRef2(0);
3177
+ const prevTextRef = useRef2("");
3178
+ const [sliceEnd, setSliceEnd] = useState2(0);
3179
+ if (!text.startsWith(prevTextRef.current)) {
3180
+ visibleRef.current = text.length;
3181
+ }
3182
+ prevTextRef.current = text;
3183
+ useEffect3(() => {
3184
+ const target = text.length;
3185
+ if (visibleRef.current >= target) {
3186
+ setSliceEnd(target);
3187
+ return;
3188
+ }
3189
+ let raf;
3190
+ let lastTime = performance.now();
3191
+ const tick = (now) => {
3192
+ const dt = (now - lastTime) / 16.67;
3193
+ lastTime = now;
3194
+ const current = visibleRef.current;
3195
+ const diff2 = target - current;
3196
+ if (diff2 < 0.5) {
3197
+ visibleRef.current = target;
3198
+ setSliceEnd(target);
3199
+ return;
3200
+ }
3201
+ const step = Math.max(1, diff2 * lerp * dt);
3202
+ visibleRef.current = Math.min(current + step, target);
3203
+ const next = Math.floor(visibleRef.current);
3204
+ setSliceEnd((prev) => prev !== next ? next : prev);
3205
+ raf = requestAnimationFrame(tick);
3206
+ };
3207
+ raf = requestAnimationFrame(tick);
3208
+ return () => cancelAnimationFrame(raf);
3209
+ }, [text.length, lerp]);
3210
+ return text.slice(0, sliceEnd);
3211
+ }
3212
+ export {
3213
+ TBar,
3214
+ TBlank,
3215
+ TBox,
3216
+ TButton,
3217
+ TCode,
3218
+ TDiff,
3219
+ THStack,
3220
+ THtml,
3221
+ TJson,
3222
+ TLayout,
3223
+ TLine,
3224
+ TList,
3225
+ TMarkdown,
3226
+ TRaw,
3227
+ TSeparator,
3228
+ TSpan,
3229
+ TSpark,
3230
+ TStatusbar,
3231
+ TTab,
3232
+ TTable,
3233
+ TTabs,
3234
+ TTree,
3235
+ TVStack,
3236
+ TermDocument,
3237
+ TermThemeProvider,
3238
+ render_default as TermUI,
3239
+ badge,
3240
+ bar,
3241
+ blank,
3242
+ box,
3243
+ builtinThemes,
3244
+ code,
3245
+ dark,
3246
+ defaultThemeSpec,
3247
+ diff,
3248
+ fromDocument,
3249
+ fromHtml,
3250
+ fromJson,
3251
+ fromJsonNode,
3252
+ fromMarkdown,
3253
+ hl,
3254
+ hstack,
3255
+ isTableLine,
3256
+ light,
3257
+ lines,
3258
+ list,
3259
+ pad,
3260
+ padLine,
3261
+ parseThemeSpec,
3262
+ preserveTableTag,
3263
+ renderTermReact,
3264
+ resolveTheme,
3265
+ resolveThemeMode,
3266
+ separator,
3267
+ spark,
3268
+ statusbar,
3269
+ table,
3270
+ tabs,
3271
+ themeToCssVars,
3272
+ tokenColor,
3273
+ tokenize,
3274
+ tree,
3275
+ useSpinner,
3276
+ useStreamingText,
3277
+ useTermTheme,
3278
+ useTermWidth,
3279
+ useTick,
3280
+ validateThemeSpec,
3281
+ vstack
3282
+ };
3283
+ //# sourceMappingURL=index.js.map