archymedes-cli 2.6.0 → 3.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.
@@ -1,1169 +0,0 @@
1
- import {
2
- ANSI_PALETTE,
3
- ASCII_GLYPHS,
4
- BOLD,
5
- DIM,
6
- ITALIC,
7
- STRIKE,
8
- UNICODE_GLYPHS,
9
- borderGlyphsFor,
10
- codeStyle,
11
- highlightSyntax,
12
- newSyntaxState,
13
- paint,
14
- rainbowText,
15
- rgbTo256,
16
- roleCode,
17
- visibleWidth
18
- } from "./archymedes-s4qcy0q4.mjs";
19
-
20
- // packages/archymedes-cli/src/terminal/output.ts
21
- var terminalStream = {
22
- write(text) {
23
- process.stdout.write(text);
24
- },
25
- get columns() {
26
- return process.stdout.columns;
27
- },
28
- get rows() {
29
- return process.stdout.rows;
30
- }
31
- };
32
- var DEFAULT_SCROLLBACK_LINES = 5000;
33
-
34
- class LineLog {
35
- limit;
36
- committed = [];
37
- partial = "";
38
- droppedLines = 0;
39
- constructor(limit = DEFAULT_SCROLLBACK_LINES) {
40
- this.limit = limit;
41
- }
42
- write(text) {
43
- if (text === "")
44
- return;
45
- const parts = (this.partial + text).split(`
46
- `);
47
- this.partial = parts.pop() ?? "";
48
- for (const line of parts)
49
- this.commit(line);
50
- }
51
- commit(line) {
52
- this.committed.push(line);
53
- if (this.committed.length > this.limit) {
54
- this.committed.shift();
55
- this.droppedLines += 1;
56
- }
57
- }
58
- flush() {
59
- if (this.partial === "")
60
- return;
61
- this.commit(this.partial);
62
- this.partial = "";
63
- }
64
- get lines() {
65
- return this.committed;
66
- }
67
- get pending() {
68
- return this.partial;
69
- }
70
- get size() {
71
- return this.committed.length;
72
- }
73
- get dropped() {
74
- return this.droppedLines;
75
- }
76
- tail(count) {
77
- return count <= 0 ? [] : this.committed.slice(Math.max(0, this.committed.length - count));
78
- }
79
- clear() {
80
- this.committed.length = 0;
81
- this.partial = "";
82
- this.droppedLines = 0;
83
- }
84
- }
85
-
86
- class TabSink {
87
- downstream;
88
- log;
89
- live;
90
- constructor(downstream = terminalStream, options = {}) {
91
- this.downstream = downstream;
92
- this.live = options.live ?? false;
93
- this.log = new LineLog(options.limit);
94
- }
95
- get isLive() {
96
- return this.live;
97
- }
98
- setLive(live) {
99
- if (!live)
100
- this.log.flush();
101
- this.live = live;
102
- }
103
- write(text) {
104
- this.log.write(text);
105
- if (this.live)
106
- this.downstream.write(text);
107
- }
108
- get columns() {
109
- return this.downstream.columns;
110
- }
111
- get rows() {
112
- return this.downstream.rows;
113
- }
114
- }
115
-
116
- class OutputRouter {
117
- target;
118
- constructor(target = terminalStream) {
119
- this.target = target;
120
- }
121
- get current() {
122
- return this.target;
123
- }
124
- route(target) {
125
- this.target = target;
126
- }
127
- write(text) {
128
- this.target.write(text);
129
- }
130
- get columns() {
131
- return this.target.columns;
132
- }
133
- get rows() {
134
- return this.target.rows;
135
- }
136
- }
137
- function replayLines(log, count) {
138
- const lines = log.tail(count);
139
- return { lines, dropped: log.dropped, omitted: Math.max(0, log.size - lines.length) };
140
- }
141
-
142
- // packages/archymedes-cli/src/render/markdown.ts
143
- var colour = (role, palette) => palette?.[role] || ANSI_PALETTE[role];
144
- var INLINE_PATTERN = /(?<!\\)(`[^`\n]+`)|(?<!\\)(\*\*[^*\n]+\*\*)|(?<!\\)(__[^_\n]+__)|(?<!\\)(~~[^~\n]+~~)|(?<!\\)(\*[^*\n]+\*)|(?<!\\)(_[^_\n]+_)|(!?\[([^\]\n]+)\]\((https?:\/\/[^\s)\n]+)\))|(<(https?:\/\/[^>\s]+)>)/g;
145
- var unescapeMarkdown = (text) => text.replace(/\\([\\`*_[\]{}()#+.!~>-])/g, "$1");
146
- function parseInline(line, palette) {
147
- const tokens = [];
148
- let lastIndex = 0;
149
- INLINE_PATTERN.lastIndex = 0;
150
- for (let match = INLINE_PATTERN.exec(line);match !== null; match = INLINE_PATTERN.exec(line)) {
151
- if (match.index > lastIndex)
152
- tokens.push({ text: unescapeMarkdown(line.slice(lastIndex, match.index)), code: "" });
153
- const [whole, code, boldStars, boldUnderscores, strike, italicStars, italicUnderscores, link, linkLabel, linkUrl, autolink, autolinkUrl] = match;
154
- if (code !== undefined)
155
- tokens.push({ text: code.slice(1, -1), code: colour("primary", palette) });
156
- else if (boldStars !== undefined)
157
- tokens.push({ text: boldStars.slice(2, -2), code: BOLD });
158
- else if (boldUnderscores !== undefined)
159
- tokens.push({ text: boldUnderscores.slice(2, -2), code: BOLD });
160
- else if (strike !== undefined)
161
- tokens.push({ text: strike.slice(2, -2), code: STRIKE });
162
- else if (italicStars !== undefined)
163
- tokens.push({ text: italicStars.slice(1, -1), code: ITALIC });
164
- else if (italicUnderscores !== undefined)
165
- tokens.push({ text: italicUnderscores.slice(1, -1), code: ITALIC });
166
- else if (link !== undefined)
167
- tokens.push({ text: `${link.startsWith("!") ? `image: ${linkLabel}` : linkLabel} (${linkUrl})`, code: colour("primary", palette) });
168
- else if (autolink !== undefined)
169
- tokens.push({ text: autolinkUrl, code: colour("primary", palette) });
170
- lastIndex = match.index + whole.length;
171
- }
172
- if (lastIndex < line.length)
173
- tokens.push({ text: unescapeMarkdown(line.slice(lastIndex)), code: "" });
174
- return tokens.filter((token) => token.text.length > 0);
175
- }
176
- function wrapTokens(tokens, width, options) {
177
- const firstPrefix = options.firstPrefix ?? "";
178
- const continuationPrefix = options.continuationPrefix ?? "";
179
- const lines = [];
180
- let current = "";
181
- let currentWidth = 0;
182
- let prefix = firstPrefix;
183
- let prefixWidth = visibleWidth(firstPrefix);
184
- const limit = Math.max(1, width);
185
- const flush = () => {
186
- lines.push(`${prefix}${current}`.replace(/[ \t]+$/, ""));
187
- current = "";
188
- currentWidth = 0;
189
- prefix = continuationPrefix;
190
- prefixWidth = visibleWidth(continuationPrefix);
191
- };
192
- for (const token of tokens) {
193
- for (const word of token.text.split(/(\s+)/)) {
194
- if (word === "")
195
- continue;
196
- const wordWidth = visibleWidth(word);
197
- if (/^\s+$/.test(word)) {
198
- if (currentWidth > 0 && prefixWidth + currentWidth + wordWidth <= limit) {
199
- current += word;
200
- currentWidth += wordWidth;
201
- }
202
- continue;
203
- }
204
- if (currentWidth > 0 && prefixWidth + currentWidth + wordWidth > limit)
205
- flush();
206
- if (prefixWidth + wordWidth > limit) {
207
- let remaining = word;
208
- while (remaining.length > 0) {
209
- const available = Math.max(1, limit - prefixWidth - currentWidth);
210
- if (visibleWidth(remaining) <= available)
211
- break;
212
- const head = [...remaining].slice(0, available).join("");
213
- current += paint(head, token.code, options.depth);
214
- remaining = [...remaining].slice(available).join("");
215
- flush();
216
- }
217
- if (remaining.length > 0) {
218
- current += paint(remaining, token.code, options.depth);
219
- currentWidth += visibleWidth(remaining);
220
- }
221
- continue;
222
- }
223
- current += paint(word, token.code, options.depth);
224
- currentWidth += wordWidth;
225
- }
226
- }
227
- if (current !== "" || lines.length === 0)
228
- lines.push(`${prefix}${current}`.replace(/[ \t]+$/, ""));
229
- return lines;
230
- }
231
- function newMarkdownState() {
232
- return { inFence: false, fenceLanguage: "", fenceLine: 0, fenceSyntax: newSyntaxState() };
233
- }
234
- var HEADING = /^(#{1,6})\s+(.*)$/;
235
- var BULLET = /^(\s*)([-*+])\s+(.*)$/;
236
- var TASK = /^(\s*)[-*+]\s+\[([ xX])\]\s+(.*)$/;
237
- var NUMBERED = /^(\s*)(\d+[.)])\s+(.*)$/;
238
- var BLOCKQUOTE = /^>\s?(.*)$/;
239
- var RULE = /^\s*([-*_])\s*(?:\1\s*){2,}$/;
240
- var FENCE = /^\s*```(.*)$/;
241
- var TABLE_SEPARATOR_CELL = /^:?-{3,}:?$/;
242
- function tableCells(line) {
243
- if (!line.includes("|"))
244
- return null;
245
- const trimmed = line.trim().replace(/^\|/, "").replace(/\|$/, "");
246
- const cells = trimmed.split("|").map((cell) => cell.trim());
247
- return cells.length >= 2 ? cells : null;
248
- }
249
- function fitCell(text, width, glyphs = UNICODE_GLYPHS) {
250
- let result = "";
251
- for (const character of text) {
252
- if (visibleWidth(result + character) > width) {
253
- while (visibleWidth(result) > Math.max(0, width - visibleWidth(glyphs.ellipsis)))
254
- result = [...result].slice(0, -1).join("");
255
- result += glyphs.ellipsis;
256
- return result + " ".repeat(Math.max(0, width - visibleWidth(result)));
257
- }
258
- result += character;
259
- }
260
- return result + " ".repeat(Math.max(0, width - visibleWidth(result)));
261
- }
262
- function renderTableLine(cells, separator, width, depth, glyphs) {
263
- const cellWidth = Math.max(3, Math.floor((Math.max(width, 12) - cells.length - 1) / cells.length));
264
- if (separator)
265
- return paint(`${glyphs.boxTeeLeft}${cells.map(() => glyphs.boxHorizontal.repeat(cellWidth)).join(glyphs.boxCross)}${glyphs.boxTeeRight}`, DIM, depth);
266
- const contentWidth = Math.max(1, cellWidth - 2);
267
- const rendered = cells.map((cell) => fitCell(parseInline(cell).map((token) => token.text).join(""), contentWidth, glyphs));
268
- return `${glyphs.boxVertical}${rendered.map((cell) => ` ${cell} `).join(glyphs.boxVertical)}${glyphs.boxVertical}`;
269
- }
270
- function renderMarkdownLine(line, state, options) {
271
- const { width, depth, palette } = options;
272
- const glyphs = options.glyphs ?? UNICODE_GLYPHS;
273
- const fence = FENCE.exec(line);
274
- if (fence) {
275
- const ruleWidth = Math.max(4, Math.min(width - 4, 40));
276
- if (state.inFence) {
277
- state.inFence = false;
278
- state.fenceLanguage = "";
279
- state.fenceLine = 0;
280
- state.fenceSyntax = newSyntaxState();
281
- return [paint(` ${glyphs.boxBottomLeft}${glyphs.boxHorizontal.repeat(ruleWidth)}`, DIM, depth)];
282
- }
283
- state.inFence = true;
284
- state.fenceLanguage = fence[1].trim();
285
- state.fenceLine = 0;
286
- state.fenceSyntax = newSyntaxState();
287
- const label = state.fenceLanguage ? ` ${state.fenceLanguage} ` : "";
288
- const drawn = 1 + visibleWidth(label);
289
- return [`${paint(` ${glyphs.boxTopLeft}${glyphs.boxHorizontal}`, DIM, depth)}${label ? paint(label, `${BOLD}${colour("primary", palette)}`, depth) : ""}${paint(glyphs.boxHorizontal.repeat(Math.max(0, ruleWidth - drawn)), DIM, depth)}`];
290
- }
291
- if (state.inFence) {
292
- state.fenceLine += 1;
293
- const style = codeStyle();
294
- const number = style.lineNumbers ? `${paint(String(state.fenceLine).padStart(3), DIM, depth)} ` : "";
295
- const code = style.colors === "vscode" ? highlightSyntax(line, { language: state.fenceLanguage, depth, palette, state: state.fenceSyntax }) : paint(line, colour("success", palette), depth);
296
- return [`${paint(` ${glyphs.boxVertical} `, DIM, depth)}${number}${code}`];
297
- }
298
- const table = tableCells(line);
299
- if (table) {
300
- const separator = table.every((cell) => TABLE_SEPARATOR_CELL.test(cell));
301
- return [renderTableLine(table, separator, width, depth, glyphs)];
302
- }
303
- if (RULE.test(line) && line.trim().length > 0) {
304
- return [paint(" " + glyphs.boxHorizontal.repeat(Math.max(4, Math.min(width - 4, 40))), DIM, depth)];
305
- }
306
- const heading = HEADING.exec(line);
307
- if (heading) {
308
- const level = heading[1].length;
309
- const code = level <= 2 ? `${BOLD}${colour("primary", palette)}` : BOLD;
310
- const tokens = parseInline(heading[2]).map((token) => ({ text: token.text, code }));
311
- return wrapTokens(tokens, width, { depth });
312
- }
313
- const task = TASK.exec(line);
314
- if (task) {
315
- return wrapTokens(parseInline(task[3], palette), width, {
316
- firstPrefix: `${task[1]}${paint(task[2].trim() ? glyphs.checkboxDone : glyphs.checkbox, task[2].trim() ? colour("success", palette) : colour("secondary", palette), depth)} `,
317
- continuationPrefix: `${task[1]} `,
318
- depth
319
- });
320
- }
321
- const bullet = BULLET.exec(line);
322
- if (bullet) {
323
- const indent = bullet[1];
324
- return wrapTokens(parseInline(bullet[3], palette), width, {
325
- firstPrefix: `${indent}${paint(glyphs.bullet, colour("secondary", palette), depth)} `,
326
- continuationPrefix: `${indent} `,
327
- depth
328
- });
329
- }
330
- const numbered = NUMBERED.exec(line);
331
- if (numbered) {
332
- const indent = numbered[1];
333
- const marker = numbered[2];
334
- return wrapTokens(parseInline(numbered[3], palette), width, {
335
- firstPrefix: `${indent}${paint(marker, colour("secondary", palette), depth)} `,
336
- continuationPrefix: `${indent}${" ".repeat(marker.length + 1)}`,
337
- depth
338
- });
339
- }
340
- const quote = BLOCKQUOTE.exec(line);
341
- if (quote) {
342
- return wrapTokens([{ text: quote[1], code: DIM }], width, {
343
- firstPrefix: paint(`${glyphs.boxVertical} `, colour("accent", palette), depth),
344
- continuationPrefix: paint(`${glyphs.boxVertical} `, colour("accent", palette), depth),
345
- depth
346
- });
347
- }
348
- if (line.trim() === "")
349
- return [""];
350
- return wrapTokens(parseInline(line, palette), width, { depth });
351
- }
352
- function renderMarkdown(text, options) {
353
- const state = newMarkdownState();
354
- const glyphs = options.glyphs ?? UNICODE_GLYPHS;
355
- const rendered = text.split(`
356
- `).flatMap((line) => renderMarkdownLine(line, state, options));
357
- if (state.inFence)
358
- rendered.push(paint(` ${glyphs.boxBottomLeft}${glyphs.boxHorizontal.repeat(4)}`, DIM, options.depth));
359
- return rendered.join(`
360
- `);
361
- }
362
-
363
- // packages/archymedes-cli/src/render/tui.ts
364
- function archymedesSpinnerFrame(index, glyphs = UNICODE_GLYPHS) {
365
- const frames = glyphs.spinnerFrames;
366
- const normalized = Number.isFinite(index) ? Math.max(0, Math.floor(index)) : 0;
367
- return frames[normalized % frames.length];
368
- }
369
-
370
- class Spinner {
371
- onTick;
372
- intervalMs;
373
- glyphs;
374
- delayMs;
375
- frame = 0;
376
- timer;
377
- delay;
378
- constructor(onTick, intervalMs = 120, glyphs = UNICODE_GLYPHS, delayMs = 0) {
379
- this.onTick = onTick;
380
- this.intervalMs = intervalMs;
381
- this.glyphs = glyphs;
382
- this.delayMs = delayMs;
383
- }
384
- start() {
385
- if (this.timer || this.delay)
386
- return;
387
- const begin = () => {
388
- this.delay = undefined;
389
- this.onTick();
390
- this.timer = setInterval(() => {
391
- this.frame = (this.frame + 1) % this.glyphs.spinnerFrames.length;
392
- this.onTick();
393
- }, this.intervalMs);
394
- };
395
- if (this.delayMs <= 0)
396
- begin();
397
- else
398
- this.delay = setTimeout(begin, this.delayMs);
399
- }
400
- stop() {
401
- if (this.delay) {
402
- clearTimeout(this.delay);
403
- this.delay = undefined;
404
- }
405
- if (!this.timer)
406
- return;
407
- clearInterval(this.timer);
408
- this.timer = undefined;
409
- }
410
- get glyph() {
411
- return archymedesSpinnerFrame(this.frame, this.glyphs);
412
- }
413
- }
414
- function formatCountdown(remainingMs) {
415
- const totalSeconds = Math.max(0, Math.ceil(remainingMs / 1000));
416
- if (totalSeconds < 60)
417
- return `${totalSeconds}s`;
418
- const minutes = Math.floor(totalSeconds / 60);
419
- const seconds = totalSeconds % 60;
420
- return `${minutes}m ${String(seconds).padStart(2, "0")}s`;
421
- }
422
-
423
- class CountdownTimer {
424
- onTick;
425
- onDone;
426
- intervalMs;
427
- timer;
428
- endsAt;
429
- constructor(durationMs, onTick, onDone, intervalMs = 1000) {
430
- this.onTick = onTick;
431
- this.onDone = onDone;
432
- this.intervalMs = intervalMs;
433
- this.endsAt = Date.now() + Math.max(0, durationMs);
434
- }
435
- get remaining() {
436
- return Math.max(0, this.endsAt - Date.now());
437
- }
438
- start() {
439
- if (this.timer)
440
- return;
441
- if (this.remaining <= 0) {
442
- this.onDone();
443
- return;
444
- }
445
- this.onTick(this.remaining);
446
- this.timer = setInterval(() => {
447
- const left = this.remaining;
448
- if (left <= 0) {
449
- this.stop();
450
- this.onDone();
451
- return;
452
- }
453
- this.onTick(left);
454
- }, this.intervalMs);
455
- }
456
- stop() {
457
- if (!this.timer)
458
- return;
459
- clearInterval(this.timer);
460
- this.timer = undefined;
461
- }
462
- }
463
-
464
- class Spring {
465
- angularFrequency;
466
- dampingRatio;
467
- constructor(angularFrequency, dampingRatio) {
468
- this.angularFrequency = angularFrequency;
469
- this.dampingRatio = dampingRatio;
470
- }
471
- update(position, velocity, target, dt) {
472
- const omega = this.angularFrequency;
473
- const zeta = this.dampingRatio;
474
- const f = 1 + 2 * dt * zeta * omega;
475
- const oo = omega * omega;
476
- const hoo = dt * oo;
477
- const hhoo = dt * hoo;
478
- const detInv = 1 / (f + hhoo);
479
- const detX = f * position + dt * velocity + hhoo * target;
480
- const detV = velocity + hoo * (target - position);
481
- return [detX * detInv, detV * detInv];
482
- }
483
- }
484
-
485
- class SpringAnimator {
486
- onTick;
487
- spring;
488
- position;
489
- velocity = 0;
490
- target;
491
- timer;
492
- intervalMs;
493
- epsilon;
494
- constructor(initial, onTick, options = {}) {
495
- this.onTick = onTick;
496
- this.position = initial;
497
- this.target = initial;
498
- this.spring = new Spring(options.angularFrequency ?? 18, options.dampingRatio ?? 0.86);
499
- this.intervalMs = options.intervalMs ?? 40;
500
- this.epsilon = options.epsilon ?? 0.01;
501
- }
502
- get value() {
503
- return this.position;
504
- }
505
- get settled() {
506
- return !this.timer;
507
- }
508
- retarget(target) {
509
- this.target = target;
510
- if (Math.abs(this.target - this.position) < this.epsilon && Math.abs(this.velocity) < this.epsilon) {
511
- this.position = target;
512
- this.onTick(target);
513
- return;
514
- }
515
- this.ensureRunning();
516
- }
517
- snapTo(target) {
518
- this.stop();
519
- this.position = target;
520
- this.velocity = 0;
521
- this.target = target;
522
- this.onTick(target);
523
- }
524
- ensureRunning() {
525
- if (this.timer)
526
- return;
527
- this.timer = setInterval(() => {
528
- const dt = this.intervalMs / 1000;
529
- const [pos, vel] = this.spring.update(this.position, this.velocity, this.target, dt);
530
- this.position = pos;
531
- this.velocity = vel;
532
- if (Math.abs(this.target - pos) < this.epsilon && Math.abs(vel) < this.epsilon) {
533
- this.position = this.target;
534
- this.stop();
535
- this.onTick(this.target);
536
- return;
537
- }
538
- this.onTick(pos);
539
- }, this.intervalMs);
540
- }
541
- stop() {
542
- if (!this.timer)
543
- return;
544
- clearInterval(this.timer);
545
- this.timer = undefined;
546
- }
547
- }
548
- function formatHeaderSegments(segments, width, separator = " · ") {
549
- const limit = Math.max(0, Math.floor(width));
550
- if (limit === 0 || segments.length === 0)
551
- return "";
552
- let kept = [...segments];
553
- while (kept.length > 0) {
554
- const rendered = kept.map((segment) => segment.full).join(separator);
555
- if (visibleWidth(rendered) <= limit)
556
- return rendered;
557
- kept = kept.slice(0, -1);
558
- }
559
- const first = segments[0];
560
- const compact = first.compact ?? first.full;
561
- return visibleWidth(compact) <= limit ? compact : "";
562
- }
563
- function formatElapsed(ms) {
564
- const seconds = ms / 1000;
565
- return seconds < 60 ? `${seconds.toFixed(1)}s` : `${Math.floor(seconds / 60)}m ${Math.round(seconds % 60)}s`;
566
- }
567
- function formatTokens(tokens) {
568
- if (tokens < 1000)
569
- return `${tokens} tokens`;
570
- return `${(tokens / 1000).toFixed(1)}k tokens`;
571
- }
572
- function sparkline(values, glyphs = UNICODE_GLYPHS) {
573
- const levels = glyphs.sparkLevels;
574
- if (values.length === 0)
575
- return "";
576
- const max = Math.max(...values);
577
- if (max <= 0)
578
- return levels[0].repeat(values.length);
579
- return values.map((value) => levels[Math.min(levels.length - 1, Math.floor(Math.max(0, value) / max * (levels.length - 1)))]).join("");
580
- }
581
- var PARTIAL_BLOCKS = ["", "▏", "▎", "▍", "▌", "▋", "▊", "▉"];
582
- var FULL_BLOCK = "█";
583
- var TRACK_BLOCK = "░";
584
- var DEFAULT_GRADIENT_FROM = { r: 124, g: 197, b: 255 };
585
- var DEFAULT_GRADIENT_TO = { r: 255, g: 207, b: 107 };
586
- function lerp(from, to, t) {
587
- return { r: from.r + (to.r - from.r) * t, g: from.g + (to.g - from.g) * t, b: from.b + (to.b - from.b) * t };
588
- }
589
- function paintRgb(text, rgb, depth) {
590
- if (depth === "none")
591
- return text;
592
- const r = Math.round(rgb.r);
593
- const g = Math.round(rgb.g);
594
- const b = Math.round(rgb.b);
595
- const code = depth === "truecolor" ? `\x1B[38;2;${r};${g};${b}m` : `\x1B[38;5;${rgbTo256({ r, g, b })}m`;
596
- return `${code}${text}\x1B[0m`;
597
- }
598
- function progressBar(fraction, width, options) {
599
- const clamped = Math.max(0, Math.min(1, Number.isFinite(fraction) ? fraction : 0));
600
- const cells = Math.max(1, Math.floor(width));
601
- const from = options.from ?? DEFAULT_GRADIENT_FROM;
602
- const to = options.to ?? DEFAULT_GRADIENT_TO;
603
- const ascii = options.glyphs === ASCII_GLYPHS;
604
- const eighths = ascii ? cells : cells * 8;
605
- const filledEighths = Math.round(clamped * eighths);
606
- let bar = "";
607
- for (let index = 0;index < cells; index += 1) {
608
- const t = cells <= 1 ? 0 : index / (cells - 1);
609
- const rgb = lerp(from, to, t);
610
- if (ascii) {
611
- bar += index < filledEighths ? paintRgb("#", rgb, options.depth) : "-";
612
- continue;
613
- }
614
- const cellStart = index * 8;
615
- if (filledEighths >= cellStart + 8)
616
- bar += paintRgb(FULL_BLOCK, rgb, options.depth);
617
- else if (filledEighths > cellStart)
618
- bar += paintRgb(PARTIAL_BLOCKS[filledEighths - cellStart], rgb, options.depth);
619
- else
620
- bar += options.depth === "none" ? TRACK_BLOCK : `\x1B[2m${TRACK_BLOCK}\x1B[0m`;
621
- }
622
- return bar;
623
- }
624
- function table(columns, rows, options) {
625
- const glyphs = options.glyphs ?? UNICODE_GLYPHS;
626
- const border = borderGlyphsFor(options.borderStyle ?? "round", glyphs);
627
- const terminalWidth = Math.max(1, options.width ?? process.stdout.columns ?? 80);
628
- const count = columns.length;
629
- const widths = columns.map((header, index) => Math.max(visibleWidth(header), ...rows.map((row2) => visibleWidth(row2[index] ?? ""))));
630
- const budget = terminalWidth - (count + 1) - count * 2;
631
- while (widths.reduce((sum, width) => sum + width, 0) > Math.max(count, budget)) {
632
- const widest = widths.indexOf(Math.max(...widths));
633
- if (widths[widest] <= 3)
634
- break;
635
- widths[widest] -= 1;
636
- }
637
- const cell = (text, width) => {
638
- const clipped = visibleWidth(text) > width ? `${sliceToWidth(text, Math.max(0, width - visibleWidth(glyphs.ellipsis)))}${glyphs.ellipsis}` : text;
639
- return `${clipped}${" ".repeat(Math.max(0, width - visibleWidth(clipped)))}`;
640
- };
641
- const rule = (left, mid, right) => `${left}${widths.map((width) => border.horizontal.repeat(width + 2)).join(mid)}${right}`;
642
- const row = (cells) => `${border.vertical} ${widths.map((width, index) => cell(cells[index] ?? "", width)).join(` ${border.vertical} `)} ${border.vertical}`;
643
- return [
644
- rule(border.topLeft, border.horizontal, border.topRight),
645
- row(columns.map((header) => paint(header, DIM, options.depth))),
646
- rule(border.teeLeft, border.cross, border.teeRight),
647
- ...rows.map((line) => row(line)),
648
- rule(border.bottomLeft, border.horizontal, border.bottomRight)
649
- ].join(`
650
- `);
651
- }
652
- function scrollPercent(offsetFromTop, contentLines, viewportHeight) {
653
- const maxOffset = Math.max(0, contentLines - viewportHeight);
654
- if (maxOffset === 0)
655
- return 1;
656
- return Math.max(0, Math.min(1, offsetFromTop / maxOffset));
657
- }
658
- function scrollIndicator(fraction) {
659
- if (fraction >= 1)
660
- return "Bot";
661
- if (fraction <= 0)
662
- return "Top";
663
- return `${Math.round(fraction * 100)}%`;
664
- }
665
- function paginator(current, total, options = {}) {
666
- const count = Math.max(1, Math.floor(total));
667
- const index = Math.max(0, Math.min(Math.floor(current), count - 1));
668
- if (options.style === "dots") {
669
- const glyphs = options.glyphs ?? UNICODE_GLYPHS;
670
- return Array.from({ length: count }, (_, position) => position === index ? glyphs.bullet : glyphs.middot).join(" ");
671
- }
672
- return `${index + 1}/${count}`;
673
- }
674
- var VERBS = ["Thinking", "Reasoning", "Tracing", "Considering"];
675
- function thinkingVerb(elapsedMs) {
676
- return VERBS[Math.floor(Math.max(0, elapsedMs) / 6000) % VERBS.length];
677
- }
678
- function activityLabel(phase, operation, elapsedMs) {
679
- if (phase === "thinking")
680
- return thinkingVerb(elapsedMs);
681
- switch (operation) {
682
- case "read_file":
683
- return "Reading file";
684
- case "list_files":
685
- case "glob_files":
686
- case "grep_files":
687
- return "Searching workspace";
688
- case "write_file":
689
- case "edit_file":
690
- return "Editing workspace";
691
- case "run_command":
692
- return "Running command";
693
- case "web_search":
694
- return "Searching the web";
695
- case "deep_research":
696
- return "Researching in depth";
697
- case "todo_write":
698
- return "Updating plan";
699
- default:
700
- return "Running operation";
701
- }
702
- }
703
- function stepProgress(done, total, options) {
704
- const safeTotal = Math.max(0, Math.floor(Number.isFinite(total) ? total : 0));
705
- const safeDone = Math.max(0, Math.min(safeTotal, Math.floor(Number.isFinite(done) ? done : 0)));
706
- const counted = `${safeDone}/${safeTotal}`;
707
- const label = options.label ? `${options.label} ` : "";
708
- if (safeTotal === 0)
709
- return `${label}${counted}`;
710
- const width = Math.floor(options.width ?? 0);
711
- if (width <= 0)
712
- return `${label}${counted}`;
713
- const bar = progressBar(safeDone / safeTotal, width, {
714
- depth: options.depth,
715
- glyphs: options.glyphs ?? UNICODE_GLYPHS,
716
- ...options.from ? { from: options.from } : {},
717
- ...options.to ? { to: options.to } : {}
718
- });
719
- return `${label}${counted} ${bar}`;
720
- }
721
- function formatStatusLine(fields, width, depth, glyphs = UNICODE_GLYPHS, accent = ANSI_PALETTE.primary) {
722
- const label = activityLabel(fields.phase ?? "thinking", fields.operation, fields.elapsedMs);
723
- const ellipsis = glyphs.ellipsis;
724
- const separator = ` ${glyphs.middot} `;
725
- const left = `${fields.spinnerGlyph} ${label}${ellipsis}`;
726
- const paintedLeft = `${paint(fields.spinnerGlyph, accent, depth)} ${paint(`${label}${ellipsis}`, DIM, depth)}`;
727
- const optional = [
728
- fields.cost,
729
- fields.tokens > 0 ? formatTokens(fields.tokens) : "",
730
- fields.toolCalls > 0 ? `${fields.toolCalls} tool${fields.toolCalls === 1 ? "" : "s"}` : "",
731
- fields.badge ?? "",
732
- fields.steps && fields.steps.total > 0 ? stepProgress(fields.steps.done, fields.steps.total, { label: fields.steps.label ?? "steps", depth: "none" }) : "",
733
- fields.allowance?.text ?? "",
734
- fields.mode,
735
- fields.balance ?? ""
736
- ].filter((segment) => segment !== "");
737
- let kept = [...optional].reverse();
738
- for (;; ) {
739
- const right = [...kept, formatElapsed(fields.elapsedMs)].join(`${separator}`);
740
- const total = visibleWidth(left) + 1 + right.length;
741
- if (total <= width || kept.length === 0) {
742
- if (total > width) {
743
- if (visibleWidth(left) <= width)
744
- return paintedLeft;
745
- const compact = `${fields.spinnerGlyph} ${ellipsis}`;
746
- if (visibleWidth(compact) <= width)
747
- return `${paint(fields.spinnerGlyph, accent, depth)} ${paint(ellipsis, DIM, depth)}`;
748
- return width > 0 ? paint(glyphs.star, accent, depth) : "";
749
- }
750
- const gap = Math.max(1, width - visibleWidth(left) - right.length);
751
- const warned = fields.allowance?.warning ? fields.allowance.text : undefined;
752
- const paintedRight = warned && kept.includes(warned) ? [...kept, formatElapsed(fields.elapsedMs)].map((segment) => paint(segment, segment === warned ? roleCode("warning", undefined, depth) : DIM, depth)).join(paint(separator, DIM, depth)) : paint(right, DIM, depth);
753
- return `${paintedLeft}${" ".repeat(gap)}${paintedRight}`;
754
- }
755
- kept = kept.slice(0, -1);
756
- }
757
- }
758
-
759
- class StatusBar {
760
- stream;
761
- linesDrawn = 0;
762
- constructor(stream = terminalStream) {
763
- this.stream = stream;
764
- }
765
- render(fields, depth, glyphs = UNICODE_GLYPHS, accent) {
766
- this.clear();
767
- const width = this.stream.columns ?? 80;
768
- const line = formatStatusLine(fields, width, depth, glyphs, accent);
769
- this.stream.write(`${line}
770
- `);
771
- this.linesDrawn = 1;
772
- }
773
- renderLine(text) {
774
- this.clear();
775
- if (text === "")
776
- return;
777
- this.stream.write(`${text}
778
- `);
779
- this.linesDrawn = 1;
780
- }
781
- clear() {
782
- if (this.linesDrawn === 0)
783
- return;
784
- for (let index = 0;index < this.linesDrawn; index += 1) {
785
- this.stream.write("\x1B[1A\x1B[2K");
786
- }
787
- this.linesDrawn = 0;
788
- }
789
- }
790
- function rowsOccupied(text, columns) {
791
- const width = visibleWidth(text);
792
- if (width === 0)
793
- return 1;
794
- return Math.ceil(width / Math.max(1, columns));
795
- }
796
-
797
- class ReplaceableBlock {
798
- stream;
799
- columns;
800
- entries = [];
801
- rows = 0;
802
- constructor(stream = terminalStream, columns = () => process.stdout.columns ?? 80) {
803
- this.stream = stream;
804
- this.columns = columns;
805
- }
806
- append(text) {
807
- this.entries.push(text);
808
- this.stream.write(`${text}
809
- `);
810
- this.rows += rowsOccupied(text, this.columns());
811
- return this.entries.length - 1;
812
- }
813
- update(index, text) {
814
- if (index < 0 || index >= this.entries.length)
815
- return false;
816
- this.entries[index] = text;
817
- for (let row = 0;row < this.rows; row += 1)
818
- this.stream.write("\x1B[1A\x1B[2K");
819
- this.stream.write("\r");
820
- this.rows = 0;
821
- for (const entry of this.entries) {
822
- this.stream.write(`${entry}
823
- `);
824
- this.rows += rowsOccupied(entry, this.columns());
825
- }
826
- return true;
827
- }
828
- updateAll(texts) {
829
- for (let row = 0;row < this.rows; row += 1)
830
- this.stream.write("\x1B[1A\x1B[2K");
831
- this.stream.write("\r");
832
- for (let index = 0;index < texts.length && index < this.entries.length; index += 1)
833
- this.entries[index] = texts[index];
834
- this.rows = 0;
835
- for (const entry of this.entries) {
836
- this.stream.write(`${entry}
837
- `);
838
- this.rows += rowsOccupied(entry, this.columns());
839
- }
840
- }
841
- forget() {
842
- this.entries = [];
843
- this.rows = 0;
844
- }
845
- get active() {
846
- return this.entries.length > 0;
847
- }
848
- }
849
-
850
- class MarkdownStream {
851
- stream;
852
- depth;
853
- columns;
854
- live;
855
- glyphs;
856
- palette;
857
- pending = "";
858
- atLineStart = true;
859
- state = newMarkdownState();
860
- constructor(stream = terminalStream, depth = "none", columns = () => process.stdout.columns ?? 80, live = true, glyphs = UNICODE_GLYPHS, palette) {
861
- this.stream = stream;
862
- this.depth = depth;
863
- this.columns = columns;
864
- this.live = live;
865
- this.glyphs = glyphs;
866
- this.palette = palette;
867
- }
868
- get active() {
869
- return this.live ? this.pending !== "" : !this.atLineStart;
870
- }
871
- push(text) {
872
- if (!this.live) {
873
- if (text === "")
874
- return;
875
- this.stream.write(text);
876
- this.atLineStart = text.endsWith(`
877
- `);
878
- return;
879
- }
880
- const parts = text.split(`
881
- `);
882
- for (let index = 0;index < parts.length; index += 1) {
883
- if (index > 0)
884
- this.finalizeLine();
885
- if (parts[index] !== "") {
886
- this.pending += parts[index];
887
- this.stream.write(parts[index]);
888
- }
889
- }
890
- }
891
- end() {
892
- if (!this.live) {
893
- if (!this.atLineStart) {
894
- this.stream.write(`
895
- `);
896
- this.atLineStart = true;
897
- }
898
- return;
899
- }
900
- if (this.pending !== "")
901
- this.finalizeLine();
902
- if (this.state.inFence) {
903
- const rendered = renderMarkdownLine("```", this.state, { width: this.columns(), depth: this.depth, glyphs: this.glyphs, palette: this.palette });
904
- this.stream.write(`${rendered.join(`
905
- `)}
906
- `);
907
- }
908
- }
909
- reset() {
910
- this.pending = "";
911
- this.atLineStart = true;
912
- this.state = newMarkdownState();
913
- }
914
- finalizeLine() {
915
- this.erasePending();
916
- const rendered = renderMarkdownLine(this.pending, this.state, { width: this.columns(), depth: this.depth, glyphs: this.glyphs, palette: this.palette });
917
- this.stream.write(`${rendered.join(`
918
- `)}
919
- `);
920
- this.pending = "";
921
- }
922
- erasePending() {
923
- if (this.pending === "")
924
- return;
925
- const rows = rowsOccupied(this.pending, this.columns());
926
- this.stream.write("\r\x1B[2K");
927
- for (let index = 1;index < rows; index += 1)
928
- this.stream.write("\x1B[1A\x1B[2K");
929
- }
930
- }
931
- function box(lines, options) {
932
- const terminalWidth = options.width ?? process.stdout.columns ?? 80;
933
- const glyphs = options.glyphs ?? UNICODE_GLYPHS;
934
- const border = borderGlyphsFor(options.borderStyle ?? options.palette?.borderStyle ?? "round", glyphs);
935
- const titleWidth = options.title ? visibleWidth(options.title) : 0;
936
- const titlePaint = roleCode(TONE_ROLE[options.titleColor ?? "cyan"], options.palette, options.depth);
937
- const rainbowBorder = options.borderColor === "rainbow";
938
- const edge = (text, phase = 0) => {
939
- if (options.borderColor === undefined)
940
- return text;
941
- return rainbowBorder ? rainbowText(text, options.depth, phase) : paint(text, roleCode(TONE_ROLE[options.borderColor], options.palette, options.depth), options.depth);
942
- };
943
- const contentWidth = Math.min(Math.max(titleWidth, ...lines.map((line) => visibleWidth(line)), 1), Math.max(1, terminalWidth - 4));
944
- const horizontal = border.horizontal.repeat(contentWidth + 2);
945
- const top = options.title ? `${edge(`${border.topLeft}${border.horizontal}`, 0)} ${paint(options.title, titlePaint, options.depth)} ${edge(`${border.horizontal.repeat(Math.max(0, contentWidth - titleWidth - 1))}${border.topRight}`, 0.08)}` : edge(`${border.topLeft}${horizontal}${border.topRight}`, 0);
946
- const bottomPhase = (lines.length + 1) * 0.1;
947
- const bottom = edge(`${border.bottomLeft}${horizontal}${border.bottomRight}`, bottomPhase);
948
- const body = lines.map((line, index) => {
949
- const clipped = visibleWidth(line) > contentWidth ? `${sliceToWidth(line, contentWidth - visibleWidth(glyphs.ellipsis))}${glyphs.ellipsis}` : line;
950
- const rowPhase = (index + 1) * 0.1;
951
- return `${edge(border.vertical, rowPhase)} ${clipped}${" ".repeat(Math.max(0, contentWidth - visibleWidth(clipped)))} ${edge(border.vertical, rowPhase)}`;
952
- });
953
- return [top, ...body, bottom].join(`
954
- `);
955
- }
956
- function sliceToWidth(text, width) {
957
- let taken = "";
958
- let used = 0;
959
- for (const token of text.match(/\x1b\[[0-9;]*m|[\s\S]/gu) ?? []) {
960
- if (token.startsWith("\x1B")) {
961
- taken += token;
962
- continue;
963
- }
964
- const next = visibleWidth(token);
965
- if (used + next > width)
966
- break;
967
- taken += token;
968
- used += next;
969
- }
970
- return taken;
971
- }
972
- function padToWidth(text, width, align = "left") {
973
- const size = visibleWidth(text);
974
- if (size >= width)
975
- return sliceToWidth(text, width);
976
- const slack = width - size;
977
- if (align === "right")
978
- return " ".repeat(slack) + text;
979
- if (align === "center")
980
- return " ".repeat(Math.floor(slack / 2)) + text + " ".repeat(Math.ceil(slack / 2));
981
- return text + " ".repeat(slack);
982
- }
983
- function joinHorizontal(left, right, options) {
984
- const separator = options.separator ?? " ";
985
- return `${padToWidth(left, options.leftWidth, options.leftAlign)}${separator}${padToWidth(right, options.rightWidth, options.rightAlign)}`;
986
- }
987
- var TONE_ROLE = { cyan: "primary", green: "success", yellow: "warning", red: "error" };
988
- var MODE_ROLE = { plan: "warning", auto: "success", build: "primary", defender: "error" };
989
- var PROMPT_PREFIX_COLUMNS = 4;
990
- var PROMPT_CHROME_COLUMNS = 6;
991
- function promptStatusRoom(mode, workspace, width, glyphs = UNICODE_GLYPHS) {
992
- const compactTitle = `${glyphs.star} ${mode}`;
993
- return Math.max(0, Math.max(12, width) - visibleWidth(compactTitle) - PROMPT_CHROME_COLUMNS - 3);
994
- }
995
- function renderPromptBox(options) {
996
- const { mode, workspace, depth } = options;
997
- const glyphs = options.glyphs ?? UNICODE_GLYPHS;
998
- const border = borderGlyphsFor(options.palette?.borderStyle ?? options.borderStyle ?? "round", glyphs);
999
- const width = Math.max(12, options.width);
1000
- const primary = roleCode("primary", options.palette, depth);
1001
- const modeColor = roleCode(MODE_ROLE[mode] ?? "primary", options.palette, depth);
1002
- const horizontal = (count) => paint(border.horizontal.repeat(Math.max(0, count)), primary, depth);
1003
- const CHROME = PROMPT_CHROME_COLUMNS;
1004
- const status = options.status ?? "";
1005
- const separator = ` ${glyphs.middot} `;
1006
- const full = `${paint(glyphs.star, primary, depth)} ${paint("archymedes", primary, depth)}${separator}${paint(mode, modeColor, depth)}`;
1007
- const compact = `${paint(glyphs.star, primary, depth)} ${paint(mode, modeColor, depth)}`;
1008
- const statusRoom = status === "" ? 0 : visibleWidth(status) + 2;
1009
- const showStatus = status !== "" && width - CHROME - 1 - statusRoom - visibleWidth(compact) >= 0;
1010
- const room = width - CHROME - 1 - (showStatus ? statusRoom : 0);
1011
- const workspaceRoom = room - visibleWidth(full) - visibleWidth(separator);
1012
- let title;
1013
- if (workspaceRoom >= 4) {
1014
- const shown = visibleWidth(workspace) <= workspaceRoom ? workspace : `${sliceToWidth(workspace, Math.max(0, workspaceRoom - visibleWidth(glyphs.ellipsis)))}${glyphs.ellipsis}`;
1015
- title = `${full}${separator}${paint(shown, DIM, depth)}`;
1016
- } else if (visibleWidth(full) <= room)
1017
- title = full;
1018
- else if (visibleWidth(compact) <= room)
1019
- title = compact;
1020
- else
1021
- title = paint(glyphs.star, primary, depth);
1022
- const tail = showStatus ? ` ${status} ` : "";
1023
- const filler = Math.max(1, width - visibleWidth(title) - visibleWidth(tail) - CHROME);
1024
- const top = `${paint(`${border.topLeft}${border.horizontal}`, primary, depth)} ${title} ${horizontal(filler)}${tail}${horizontal(1)}${paint(border.topRight, primary, depth)}`;
1025
- const prefix = `${paint(border.vertical, primary, depth)} ${paint(glyphs.caret, modeColor, depth)} `;
1026
- const bottom = `${paint(border.bottomLeft, primary, depth)}${horizontal(width - 2)}${paint(border.bottomRight, primary, depth)}`;
1027
- return { top, prefix, bottom };
1028
- }
1029
- function dropupPaintInPlace(lines) {
1030
- if (lines.length === 0)
1031
- return "";
1032
- const parts = ["\x1B7\x1B[?25l", `\x1B[${lines.length + 1}A`];
1033
- for (const line of lines)
1034
- parts.push(`\r\x1B[2K${line}\x1B[1B`);
1035
- parts.push("\x1B8\x1B[?25h");
1036
- return parts.join("");
1037
- }
1038
- function dropupEraseBlock(currentRows) {
1039
- return `\x1B[?25l\x1B[${currentRows + 1}A\r\x1B[0J`;
1040
- }
1041
-
1042
- class PromptBox {
1043
- stream;
1044
- options;
1045
- drawn = false;
1046
- borderCleared = false;
1047
- suggestions = [];
1048
- constructor(stream = terminalStream, options = { depth: "none" }) {
1049
- this.stream = stream;
1050
- this.options = options;
1051
- }
1052
- get isDrawn() {
1053
- return this.drawn;
1054
- }
1055
- get width() {
1056
- return Math.max(1, this.options.columns?.() ?? this.stream.columns ?? 80);
1057
- }
1058
- draw(mode, workspace, status) {
1059
- const { prefix } = this.paintBlock(mode, workspace, status);
1060
- this.drawn = true;
1061
- this.borderCleared = false;
1062
- return prefix;
1063
- }
1064
- paintBlock(mode, workspace, status) {
1065
- const { top, prefix, bottom } = renderPromptBox({
1066
- mode,
1067
- workspace,
1068
- depth: this.options.depth,
1069
- palette: this.options.palette?.(),
1070
- width: this.width,
1071
- ...status === undefined ? {} : { status },
1072
- ...this.options.glyphs === undefined ? {} : { glyphs: this.options.glyphs },
1073
- ...this.options.borderStyle === undefined ? {} : { borderStyle: this.options.borderStyle }
1074
- });
1075
- for (const line of this.suggestions)
1076
- this.stream.write(`${line}
1077
- `);
1078
- this.stream.write(`${top}
1079
- `);
1080
- this.stream.write(`
1081
- ${bottom}`);
1082
- this.stream.write("\x1B[1A\r");
1083
- return { prefix };
1084
- }
1085
- get suggestionRows() {
1086
- return this.suggestions.length;
1087
- }
1088
- setSuggestions(lines, bar) {
1089
- if (!this.drawn)
1090
- return { moved: false };
1091
- const next = [...lines];
1092
- if (next.length === this.suggestions.length) {
1093
- if (next.every((line, index) => line === this.suggestions[index]))
1094
- return { moved: false };
1095
- this.suggestions = next;
1096
- if (next.length > 0)
1097
- this.stream.write(dropupPaintInPlace(next));
1098
- return { moved: false };
1099
- }
1100
- this.stream.write(dropupEraseBlock(this.suggestions.length));
1101
- this.suggestions = next;
1102
- this.paintBlock(bar.mode, bar.workspace, bar.status);
1103
- this.stream.write("\x1B[?25h");
1104
- this.borderCleared = false;
1105
- return { moved: true };
1106
- }
1107
- restoreBottomBorder(mode, workspace, status) {
1108
- if (!this.drawn || this.borderCleared)
1109
- return;
1110
- const { bottom } = renderPromptBox({
1111
- mode,
1112
- workspace,
1113
- depth: this.options.depth,
1114
- palette: this.options.palette?.(),
1115
- width: this.width,
1116
- ...status === undefined ? {} : { status },
1117
- ...this.options.glyphs === undefined ? {} : { glyphs: this.options.glyphs },
1118
- ...this.options.borderStyle === undefined ? {} : { borderStyle: this.options.borderStyle }
1119
- });
1120
- this.stream.write(`\x1B7\x1B[?25l\x1B[1B\r\x1B[2K${bottom}\x1B8\x1B[?25h`);
1121
- }
1122
- erase(submitted) {
1123
- if (!this.drawn)
1124
- return;
1125
- const rows = Math.floor((PROMPT_PREFIX_COLUMNS + visibleWidth(submitted) - 1) / this.width) + 3 + this.suggestions.length;
1126
- for (let index = 0;index < rows; index += 1)
1127
- this.stream.write("\x1B[1A\x1B[2K");
1128
- this.suggestions = [];
1129
- this.drawn = false;
1130
- }
1131
- dropBorder(remainder) {
1132
- if (!this.drawn || this.borderCleared || remainder === "")
1133
- return;
1134
- this.stream.write("\r\x1B[2K");
1135
- this.stream.write(remainder);
1136
- this.borderCleared = true;
1137
- }
1138
- }
1139
- function wrapPlain(text, width) {
1140
- const lines = [];
1141
- let current = "";
1142
- for (const word of text.split(/\s+/)) {
1143
- if (word === "")
1144
- continue;
1145
- const candidate = current === "" ? word : `${current} ${word}`;
1146
- if (visibleWidth(candidate) <= width) {
1147
- current = candidate;
1148
- continue;
1149
- }
1150
- if (current !== "")
1151
- lines.push(current);
1152
- if (visibleWidth(word) > width) {
1153
- let rest = word;
1154
- while (visibleWidth(rest) > width) {
1155
- const chunk = sliceToWidth(rest, width);
1156
- lines.push(chunk);
1157
- rest = rest.slice(chunk.length);
1158
- }
1159
- current = rest;
1160
- } else {
1161
- current = word;
1162
- }
1163
- }
1164
- if (current !== "")
1165
- lines.push(current);
1166
- return lines.length === 0 ? [""] : lines;
1167
- }
1168
-
1169
- export { terminalStream, TabSink, OutputRouter, replayLines, renderMarkdown, archymedesSpinnerFrame, Spinner, formatCountdown, CountdownTimer, SpringAnimator, formatHeaderSegments, sparkline, progressBar, table, scrollPercent, scrollIndicator, paginator, stepProgress, formatStatusLine, StatusBar, rowsOccupied, ReplaceableBlock, MarkdownStream, box, sliceToWidth, padToWidth, joinHorizontal, PROMPT_PREFIX_COLUMNS, promptStatusRoom, renderPromptBox, PromptBox, wrapPlain };