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/README.md +280 -0
- package/dist/index.d.ts +639 -0
- package/dist/index.js +3283 -0
- package/dist/index.js.map +1 -0
- package/dist/style.css +55 -0
- package/package.json +52 -0
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
|