@standardagents/code 0.6.7 → 0.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +236 -106
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -2228,6 +2228,92 @@ async function readClipboardImage() {
|
|
|
2228
2228
|
function imagePlaceholder(seq) {
|
|
2229
2229
|
return `[#Image ${seq}]`;
|
|
2230
2230
|
}
|
|
2231
|
+
var INPUT_BOX_MARGIN = 1;
|
|
2232
|
+
function inputBoxBorderColor() {
|
|
2233
|
+
return "\x1B[38;5;240m";
|
|
2234
|
+
}
|
|
2235
|
+
function borderLevelDots(level) {
|
|
2236
|
+
const n = Math.max(1, Math.min(5, level));
|
|
2237
|
+
return "\u25CF".repeat(n) + "\u25CB".repeat(5 - n);
|
|
2238
|
+
}
|
|
2239
|
+
function borderLevelDotsStyled(level, levelColor, emptyColor = "\x1B[38;5;240m", reset = "\x1B[0m") {
|
|
2240
|
+
const n = Math.max(1, Math.min(5, level));
|
|
2241
|
+
let out = "";
|
|
2242
|
+
for (let i = 1; i <= 5; i++) {
|
|
2243
|
+
out += i <= n ? `${levelColor}\u25CF${reset}` : `${emptyColor}\u25CB${reset}`;
|
|
2244
|
+
}
|
|
2245
|
+
return out;
|
|
2246
|
+
}
|
|
2247
|
+
function inputBoxGeometry(cols2) {
|
|
2248
|
+
const margin = INPUT_BOX_MARGIN;
|
|
2249
|
+
const boxW = Math.max(12, cols2 - 1 - margin);
|
|
2250
|
+
const contentW = Math.max(4, boxW - 4);
|
|
2251
|
+
const leftPad = margin + 2;
|
|
2252
|
+
return { margin, boxW, contentW, leftPad };
|
|
2253
|
+
}
|
|
2254
|
+
function boxVisibleWidth(s) {
|
|
2255
|
+
let w = 0;
|
|
2256
|
+
for (const ch of s.replace(/\x1b\[[0-9;]*m/g, "")) {
|
|
2257
|
+
const cp = ch.codePointAt(0);
|
|
2258
|
+
w += isWideCodePoint(cp) ? 2 : 1;
|
|
2259
|
+
}
|
|
2260
|
+
return w;
|
|
2261
|
+
}
|
|
2262
|
+
function buildBoxTop(boxW, levelText, borderColor, levelColor, reset = "\x1B[0m", levelStyled) {
|
|
2263
|
+
const labelPlain = ` ${levelText} `;
|
|
2264
|
+
const rightFill = 1;
|
|
2265
|
+
const budget = Math.max(1, boxW - 2 - rightFill);
|
|
2266
|
+
const plain = labelPlain.length > budget ? labelPlain.slice(0, Math.max(1, budget)) : labelPlain;
|
|
2267
|
+
const leftFill = Math.max(1, boxW - 2 - plain.length - rightFill);
|
|
2268
|
+
const mid = levelStyled !== void 0 ? ` ${levelStyled} ` : `${levelColor}${plain}${reset}`;
|
|
2269
|
+
return `${borderColor}\u256D${"\u2500".repeat(leftFill)}${reset}` + mid + `${borderColor}${"\u2500".repeat(rightFill)}\u256E${reset}`;
|
|
2270
|
+
}
|
|
2271
|
+
function buildBoxBottom(boxW, borderColor, reset = "\x1B[0m") {
|
|
2272
|
+
return `${borderColor}\u2570${"\u2500".repeat(Math.max(1, boxW - 2))}\u256F${reset}`;
|
|
2273
|
+
}
|
|
2274
|
+
function buildBoxTopPlain(boxW, borderColor, reset = "\x1B[0m") {
|
|
2275
|
+
return `${borderColor}\u256D${"\u2500".repeat(Math.max(1, boxW - 2))}\u256E${reset}`;
|
|
2276
|
+
}
|
|
2277
|
+
function buildBoxBody(content, contentW, borderColor, reset = "\x1B[0m") {
|
|
2278
|
+
const vw = boxVisibleWidth(content);
|
|
2279
|
+
const pad = Math.max(0, contentW - vw);
|
|
2280
|
+
return `${borderColor}\u2502${reset} ${content}${" ".repeat(pad)} ${borderColor}\u2502${reset}`;
|
|
2281
|
+
}
|
|
2282
|
+
function wrapInputBodyLines(inputBuffer, prefix, prefixWidth, contentW) {
|
|
2283
|
+
const indent = " ".repeat(Math.max(0, prefixWidth));
|
|
2284
|
+
const lines = inputBuffer.split("\n");
|
|
2285
|
+
const out = [];
|
|
2286
|
+
let isFirstPhysical = true;
|
|
2287
|
+
for (let i = 0; i < lines.length; i++) {
|
|
2288
|
+
const text = lines[i];
|
|
2289
|
+
let remaining = text;
|
|
2290
|
+
do {
|
|
2291
|
+
const lead = isFirstPhysical ? prefix : indent;
|
|
2292
|
+
const avail = Math.max(0, contentW - prefixWidth);
|
|
2293
|
+
let take = Math.min(remaining.length, avail);
|
|
2294
|
+
if (take === 0 && avail === 0 && remaining.length > 0) take = 1;
|
|
2295
|
+
const chunk = remaining.slice(0, take);
|
|
2296
|
+
remaining = remaining.slice(take);
|
|
2297
|
+
out.push(lead + chunk);
|
|
2298
|
+
isFirstPhysical = false;
|
|
2299
|
+
if (remaining.length === 0) break;
|
|
2300
|
+
} while (remaining.length > 0);
|
|
2301
|
+
}
|
|
2302
|
+
if (out.length === 0) out.push(prefix);
|
|
2303
|
+
return out;
|
|
2304
|
+
}
|
|
2305
|
+
function buildInputBoxRows(opts) {
|
|
2306
|
+
const { cols: cols2, inputBuffer, prefix, prefixWidth, level, levelColor } = opts;
|
|
2307
|
+
const borderColor = opts.borderColor ?? inputBoxBorderColor();
|
|
2308
|
+
const geo = inputBoxGeometry(cols2);
|
|
2309
|
+
const body = wrapInputBodyLines(inputBuffer, prefix, prefixWidth, geo.contentW);
|
|
2310
|
+
const dots = borderLevelDots(level);
|
|
2311
|
+
const dotsStyled = borderLevelDotsStyled(level, levelColor);
|
|
2312
|
+
const top = buildBoxTop(geo.boxW, dots, borderColor, levelColor, "\x1B[0m", dotsStyled);
|
|
2313
|
+
const bottom = buildBoxBottom(geo.boxW, borderColor);
|
|
2314
|
+
const pad = " ".repeat(geo.margin);
|
|
2315
|
+
return [pad + top, ...body.map((b) => pad + buildBoxBody(b, geo.contentW, borderColor)), pad + bottom];
|
|
2316
|
+
}
|
|
2231
2317
|
var C = {
|
|
2232
2318
|
reset: "\x1B[0m",
|
|
2233
2319
|
dim: "\x1B[2m",
|
|
@@ -2415,10 +2501,21 @@ var Tui = class _Tui {
|
|
|
2415
2501
|
}
|
|
2416
2502
|
/** Tear down the bottom region and restore the terminal (called on quit). */
|
|
2417
2503
|
end() {
|
|
2504
|
+
this.started = false;
|
|
2505
|
+
this.working = false;
|
|
2506
|
+
this.subagents = [];
|
|
2418
2507
|
if (this.quitTimer) clearTimeout(this.quitTimer);
|
|
2419
2508
|
this.quitTimer = null;
|
|
2420
2509
|
if (this.streamIdleTimer) clearTimeout(this.streamIdleTimer);
|
|
2421
2510
|
this.streamIdleTimer = null;
|
|
2511
|
+
if (this.streamRedrawTimer) {
|
|
2512
|
+
clearTimeout(this.streamRedrawTimer);
|
|
2513
|
+
this.streamRedrawTimer = null;
|
|
2514
|
+
}
|
|
2515
|
+
if (this.spinnerTimer) {
|
|
2516
|
+
clearInterval(this.spinnerTimer);
|
|
2517
|
+
this.spinnerTimer = null;
|
|
2518
|
+
}
|
|
2422
2519
|
this.clearBottom();
|
|
2423
2520
|
process.stdout.write("\x1B[?2004l\x1B[?25h");
|
|
2424
2521
|
}
|
|
@@ -2651,45 +2748,58 @@ var Tui = class _Tui {
|
|
|
2651
2748
|
}
|
|
2652
2749
|
// ─── input layout + vertical caret movement ────────────────────────────────
|
|
2653
2750
|
/**
|
|
2654
|
-
* The input's physical rows (same wrapping math as
|
|
2655
|
-
* lines split on "\n", line 0 led by the prompt prefix, each wrapping
|
|
2656
|
-
*
|
|
2657
|
-
* buffer index of its first character, its character count, and
|
|
2658
|
-
* column its first character renders at (only
|
|
2659
|
-
* prompt). Drives ↑/↓: row 0 is "the top
|
|
2660
|
-
* anything below moves the caret instead.
|
|
2751
|
+
* The input's physical rows (same wrapping math as the boxed input body:
|
|
2752
|
+
* logical lines split on "\n", line 0 led by the prompt prefix, each wrapping
|
|
2753
|
+
* at the box content width) plus where the caret sits among them. Each row
|
|
2754
|
+
* records the buffer index of its first character, its character count, and
|
|
2755
|
+
* the visual column its first character renders at (only the first physical
|
|
2756
|
+
* row of line 0 is offset, by the prompt). Drives ↑/↓: row 0 is "the top
|
|
2757
|
+
* line" (history recall territory), anything below moves the caret instead.
|
|
2661
2758
|
*/
|
|
2662
2759
|
inputLayout() {
|
|
2663
2760
|
const cols2 = process.stdout.columns || 80;
|
|
2761
|
+
const { contentW } = inputBoxGeometry(cols2);
|
|
2664
2762
|
const pw = this.visibleWidth(this.promptPrefix());
|
|
2665
2763
|
const lines = this.inputBuffer.split("\n");
|
|
2666
2764
|
const rows = [];
|
|
2667
2765
|
let offset = 0;
|
|
2668
2766
|
for (let i = 0; i < lines.length; i++) {
|
|
2669
|
-
const lead = i === 0 ? pw : 0;
|
|
2670
2767
|
const len = lines[i].length;
|
|
2671
|
-
|
|
2672
|
-
|
|
2673
|
-
|
|
2674
|
-
const
|
|
2675
|
-
|
|
2676
|
-
|
|
2768
|
+
let remaining = len;
|
|
2769
|
+
let charStart = 0;
|
|
2770
|
+
do {
|
|
2771
|
+
const avail = Math.max(0, contentW - pw);
|
|
2772
|
+
let take = Math.min(remaining, avail);
|
|
2773
|
+
if (take === 0 && avail === 0 && remaining > 0) take = 1;
|
|
2774
|
+
rows.push({ start: offset + charStart, len: take, colOffset: pw });
|
|
2775
|
+
charStart += take;
|
|
2776
|
+
remaining -= take;
|
|
2777
|
+
} while (remaining > 0);
|
|
2677
2778
|
offset += len + 1;
|
|
2678
2779
|
}
|
|
2679
|
-
let
|
|
2680
|
-
let
|
|
2681
|
-
|
|
2682
|
-
|
|
2683
|
-
|
|
2780
|
+
let caretRow = 0;
|
|
2781
|
+
let caretCol = 0;
|
|
2782
|
+
let found = false;
|
|
2783
|
+
for (let r = 0; r < rows.length; r++) {
|
|
2784
|
+
const row = rows[r];
|
|
2785
|
+
const rowEnd = row.start + row.len;
|
|
2786
|
+
const isLast = r === rows.length - 1;
|
|
2787
|
+
const nextStart = isLast ? rowEnd : rows[r + 1].start;
|
|
2788
|
+
if (this.cursorPos < rowEnd || this.cursorPos === rowEnd && (isLast || nextStart > rowEnd)) {
|
|
2789
|
+
caretRow = r;
|
|
2790
|
+
caretCol = row.colOffset + (this.cursorPos - row.start);
|
|
2791
|
+
found = true;
|
|
2792
|
+
break;
|
|
2793
|
+
}
|
|
2794
|
+
}
|
|
2795
|
+
if (!found && rows.length) {
|
|
2796
|
+
caretRow = rows.length - 1;
|
|
2797
|
+
const row = rows[caretRow];
|
|
2798
|
+
caretCol = row.colOffset + row.len;
|
|
2684
2799
|
}
|
|
2685
|
-
|
|
2686
|
-
|
|
2687
|
-
for (let i = 0; i < caretLine; i++) {
|
|
2688
|
-
const lead = i === 0 ? pw : 0;
|
|
2689
|
-
caretRow += Math.max(1, Math.ceil((lead + lines[i].length) / cols2));
|
|
2800
|
+
while (caretRow >= rows.length) {
|
|
2801
|
+
rows.push({ start: this.inputBuffer.length, len: 0, colOffset: 0 });
|
|
2690
2802
|
}
|
|
2691
|
-
const caretCol = caretCell % cols2;
|
|
2692
|
-
while (caretRow >= rows.length) rows.push({ start: this.inputBuffer.length, len: 0, colOffset: 0 });
|
|
2693
2803
|
return { rows, caretRow, caretCol, rowCount: rows.length };
|
|
2694
2804
|
}
|
|
2695
2805
|
/** Move the caret one visual row up/down, keeping the column when possible. */
|
|
@@ -2814,19 +2924,19 @@ var Tui = class _Tui {
|
|
|
2814
2924
|
return parts.length ? `${C.gray}${parts.join(" ")}${C.reset}` : "";
|
|
2815
2925
|
}
|
|
2816
2926
|
/**
|
|
2817
|
-
*
|
|
2818
|
-
*
|
|
2819
|
-
*
|
|
2820
|
-
*
|
|
2927
|
+
* Context-to-compaction gauge, right-aligned on the status line: just "N%"
|
|
2928
|
+
* in dull grey. Shown whenever we know a value (any fill — 5%, 10%, 20%…).
|
|
2929
|
+
* The percentage is scaled so 100% = where background compaction triggers,
|
|
2930
|
+
* not the raw model window.
|
|
2821
2931
|
*/
|
|
2822
2932
|
contextGaugeText() {
|
|
2823
|
-
if (this.contextPct == null
|
|
2824
|
-
return
|
|
2933
|
+
if (this.contextPct == null) return "";
|
|
2934
|
+
return `\x1B[38;5;240m${this.contextPct}%${C.reset}`;
|
|
2825
2935
|
}
|
|
2826
2936
|
/**
|
|
2827
2937
|
* Set the context-window fill percentage (0–100), or null to hide it.
|
|
2828
|
-
* Driven by the runtime's `context_usage` KV
|
|
2829
|
-
*
|
|
2938
|
+
* Driven by the runtime's `context_usage` KV, scaled to the compaction
|
|
2939
|
+
* trigger (see index.ts poll). Always painted on the status line far right.
|
|
2830
2940
|
*/
|
|
2831
2941
|
setContextPct(pct) {
|
|
2832
2942
|
const next = pct == null ? null : Math.max(0, Math.min(100, Math.round(pct)));
|
|
@@ -3024,10 +3134,14 @@ var Tui = class _Tui {
|
|
|
3024
3134
|
if (up > 0) process.stdout.write(`\x1B[${up}A`);
|
|
3025
3135
|
}
|
|
3026
3136
|
/**
|
|
3027
|
-
* Render the bottom region:
|
|
3028
|
-
* (
|
|
3029
|
-
* Uses only relative cursor moves so it survives terminal scrolling when
|
|
3030
|
-
* region grows near the bottom of the screen.
|
|
3137
|
+
* Render the bottom region: status + subagents above a side-margined expanding
|
|
3138
|
+
* input box (level in the top border far right), with the caret on the input
|
|
3139
|
+
* body. Uses only relative cursor moves so it survives terminal scrolling when
|
|
3140
|
+
* the region grows near the bottom of the screen.
|
|
3141
|
+
*
|
|
3142
|
+
* Layout (top → bottom):
|
|
3143
|
+
* preview · ruler · notice · quit · status · subagents · goal ·
|
|
3144
|
+
* box top (level) · box body · box bottom · palette
|
|
3031
3145
|
*/
|
|
3032
3146
|
renderBottom() {
|
|
3033
3147
|
if (!this.started || this.takeoverHandler) return;
|
|
@@ -3043,69 +3157,66 @@ var Tui = class _Tui {
|
|
|
3043
3157
|
};
|
|
3044
3158
|
const previewLines = this.streamPreviewLines(cols2);
|
|
3045
3159
|
for (const line of previewLines) writeHudRow(line);
|
|
3046
|
-
const rulerRows = 1;
|
|
3047
3160
|
writeHudRow(`${C.dim}${"\u2500".repeat(cols2)}${C.reset}`);
|
|
3161
|
+
const geo = inputBoxGeometry(cols2);
|
|
3162
|
+
const workPad = " ".repeat(geo.margin);
|
|
3163
|
+
const workCols = Math.max(8, cols2 - geo.margin);
|
|
3048
3164
|
const noticeLine = this.connected ? null : `${C.yellow}\u26A0 lost connection to the workspace \u2014 reconnecting\u2026${C.reset}`;
|
|
3049
|
-
|
|
3050
|
-
if (noticeLine) writeHudRow(noticeLine);
|
|
3165
|
+
if (noticeLine) writeHudRow(workPad + noticeLine);
|
|
3051
3166
|
const quitLine = this.quitArmed ? `${C.dim}Press Control-C again to exit${C.reset}` : null;
|
|
3052
|
-
|
|
3053
|
-
|
|
3167
|
+
if (quitLine) writeHudRow(workPad + quitLine);
|
|
3168
|
+
const statusLine = this.statusLineText(workCols);
|
|
3169
|
+
if (statusLine) writeHudRow(workPad + statusLine);
|
|
3054
3170
|
const frame = FRAMES[Math.floor(Date.now() / SPINNER_MS) % FRAMES.length];
|
|
3055
3171
|
for (const sub of this.subagents) {
|
|
3056
3172
|
const color = sub.agentName === COMPACTION_AGENT ? COMPACTION_COLOR : SUBAGENT_COLORS[this.subagentColorByID.get(sub.id) ?? 0];
|
|
3057
|
-
const budget =
|
|
3173
|
+
const budget = workCols - 10;
|
|
3058
3174
|
let label = sub.label;
|
|
3059
3175
|
if (budget < 1) label = "";
|
|
3060
3176
|
else if (label.length > budget) label = label.slice(0, Math.max(0, budget - 1)) + "\u2026";
|
|
3061
3177
|
const line = `${color}${frame}${C.reset} ${color}${label}${C.reset} ${C.dim}working${C.reset}`;
|
|
3062
|
-
writeHudRow(line);
|
|
3063
|
-
}
|
|
3064
|
-
const
|
|
3065
|
-
const
|
|
3066
|
-
if (statusLine) writeHudRow(statusLine);
|
|
3067
|
-
const goalLines = this.goalLines(cols2);
|
|
3068
|
-
for (const line of goalLines) writeHudRow(line);
|
|
3069
|
-
const aboveRows = previewLines.length + rulerRows + noticeRows + quitRows + this.subagents.length + statusRows + goalLines.length;
|
|
3178
|
+
writeHudRow(workPad + line);
|
|
3179
|
+
}
|
|
3180
|
+
const goalLines = this.goalLines(workCols);
|
|
3181
|
+
for (const line of goalLines) writeHudRow(workPad + line);
|
|
3070
3182
|
const prefix = this.promptPrefix();
|
|
3071
3183
|
const pw = this.visibleWidth(prefix);
|
|
3072
|
-
const
|
|
3073
|
-
|
|
3074
|
-
|
|
3075
|
-
|
|
3076
|
-
|
|
3077
|
-
|
|
3078
|
-
|
|
3079
|
-
|
|
3080
|
-
|
|
3081
|
-
|
|
3082
|
-
|
|
3083
|
-
|
|
3084
|
-
|
|
3085
|
-
|
|
3086
|
-
|
|
3087
|
-
const
|
|
3184
|
+
const boxRows = buildInputBoxRows({
|
|
3185
|
+
cols: cols2,
|
|
3186
|
+
inputBuffer: this.inputBuffer,
|
|
3187
|
+
prefix,
|
|
3188
|
+
prefixWidth: pw,
|
|
3189
|
+
level: this.level,
|
|
3190
|
+
levelColor: this.levelColor(),
|
|
3191
|
+
borderColor: inputBoxBorderColor()
|
|
3192
|
+
});
|
|
3193
|
+
const boxTop = boxRows[0];
|
|
3194
|
+
const boxBottom = boxRows[boxRows.length - 1];
|
|
3195
|
+
const boxBody = boxRows.slice(1, -1);
|
|
3196
|
+
writeHudRow(boxTop);
|
|
3197
|
+
const aboveBodyWidths = hudWidths.slice();
|
|
3198
|
+
const aboveBodyRows = aboveBodyWidths.length;
|
|
3199
|
+
for (const row of boxBody) writeHudRow(row);
|
|
3200
|
+
writeHudRow(boxBottom);
|
|
3088
3201
|
const paletteBlock = this.paletteBlockLines(cols2);
|
|
3089
|
-
for (const line of paletteBlock)
|
|
3090
|
-
|
|
3091
|
-
|
|
3092
|
-
|
|
3093
|
-
|
|
3094
|
-
|
|
3095
|
-
|
|
3096
|
-
|
|
3097
|
-
tail.push("\r");
|
|
3098
|
-
const up = inputRows - 1 - caretRow;
|
|
3099
|
-
if (up > 0) tail.push(`\x1B[${up}A`);
|
|
3100
|
-
if (caretCol > 0) tail.push(`\x1B[${caretCol}C`);
|
|
3101
|
-
this.lastCursorRow = aboveRows + caretRow;
|
|
3102
|
-
} else {
|
|
3103
|
-
this.lastCursorRow = aboveRows + (inputRows - 1);
|
|
3104
|
-
}
|
|
3105
|
-
this.drawnHudWidths = hudWidths;
|
|
3202
|
+
for (const line of paletteBlock) writeHudRow(line);
|
|
3203
|
+
const layout = this.inputLayout();
|
|
3204
|
+
const bodyRowCount = Math.max(1, boxBody.length);
|
|
3205
|
+
const caretBodyRow = Math.max(0, Math.min(layout.caretRow, bodyRowCount - 1));
|
|
3206
|
+
const caretRegionRow = aboveBodyRows + caretBodyRow;
|
|
3207
|
+
const caretScreenCol = Math.min(rowCap - 1, geo.leftPad + layout.caretCol);
|
|
3208
|
+
this.lastCursorRow = caretRegionRow;
|
|
3209
|
+
this.drawnHudWidths = aboveBodyWidths;
|
|
3106
3210
|
this.lastDrawnCols = cols2;
|
|
3107
3211
|
this.bottomDrawn = true;
|
|
3108
|
-
const
|
|
3212
|
+
const totalRows = hudRows.length;
|
|
3213
|
+
const up = Math.max(0, totalRows - 1 - caretRegionRow);
|
|
3214
|
+
let out = "\x1B[J" + hudRows.join("\r\n");
|
|
3215
|
+
if (totalRows > 0) {
|
|
3216
|
+
out += "\r";
|
|
3217
|
+
if (up > 0) out += `\x1B[${up}A`;
|
|
3218
|
+
if (caretScreenCol > 0) out += `\x1B[${caretScreenCol}C`;
|
|
3219
|
+
}
|
|
3109
3220
|
process.stdout.write(out);
|
|
3110
3221
|
}
|
|
3111
3222
|
clearBottom() {
|
|
@@ -3520,44 +3631,62 @@ var Tui = class _Tui {
|
|
|
3520
3631
|
};
|
|
3521
3632
|
});
|
|
3522
3633
|
}
|
|
3523
|
-
/**
|
|
3634
|
+
/**
|
|
3635
|
+
* Arrow-key selection menu (slash menu, process menu, resume). Pauses input.
|
|
3636
|
+
* Boxed chrome matches the bottom input HUD: side margin + grey rounded
|
|
3637
|
+
* border, selected row with a brand-tinted pointer.
|
|
3638
|
+
*/
|
|
3524
3639
|
select(title, items) {
|
|
3525
3640
|
return new Promise((resolve) => {
|
|
3526
3641
|
let idx = 0;
|
|
3527
3642
|
this.beginTakeover();
|
|
3643
|
+
const cols2 = process.stdout.columns || 80;
|
|
3644
|
+
const geo = inputBoxGeometry(cols2);
|
|
3645
|
+
const border = inputBoxBorderColor();
|
|
3646
|
+
const pad = " ".repeat(geo.margin);
|
|
3647
|
+
const rowCap = Math.max(1, cols2 - 1);
|
|
3528
3648
|
if (title) process.stdout.write(`
|
|
3529
|
-
${title}
|
|
3530
|
-
|
|
3649
|
+
${pad}${title}
|
|
3531
3650
|
`);
|
|
3532
|
-
|
|
3533
|
-
|
|
3651
|
+
else process.stdout.write("\n");
|
|
3652
|
+
const renderItemContent = (i) => {
|
|
3534
3653
|
const it = items[i];
|
|
3535
3654
|
const sel = i === idx;
|
|
3536
3655
|
const hint = it.hint ?? "";
|
|
3537
3656
|
const hintW = hint.length;
|
|
3538
3657
|
const pointerW = 2;
|
|
3539
|
-
const labelMax = Math.max(
|
|
3540
|
-
let label = it.label;
|
|
3541
|
-
if (label.length > labelMax) label = label.slice(0, labelMax - 1) + "\u2026";
|
|
3658
|
+
const labelMax = Math.max(4, geo.contentW - pointerW - (hintW ? hintW + 2 : 0));
|
|
3659
|
+
let label = it.label.replace(/\s+/g, " ").trim();
|
|
3660
|
+
if (label.length > labelMax) label = label.slice(0, Math.max(0, labelMax - 1)) + "\u2026";
|
|
3542
3661
|
const pointer = sel ? `${C.magenta}\u276F${C.reset} ` : " ";
|
|
3543
|
-
const styledLabel = sel ? `${C.bold}${C.cyan}${label}${C.reset}` : label
|
|
3544
|
-
let
|
|
3662
|
+
const styledLabel = sel ? `${C.bold}${C.cyan}${label}${C.reset}` : `${C.dim}${label}${C.reset}`;
|
|
3663
|
+
let content = `${pointer}${styledLabel}`;
|
|
3545
3664
|
if (hintW) {
|
|
3546
|
-
const
|
|
3547
|
-
|
|
3665
|
+
const used = pointerW + label.length;
|
|
3666
|
+
const gap = Math.max(1, geo.contentW - used - hintW);
|
|
3667
|
+
content += `${" ".repeat(gap)}${sel ? C.gray : C.dim}${hint}${C.reset}`;
|
|
3548
3668
|
}
|
|
3549
|
-
return
|
|
3669
|
+
return content;
|
|
3550
3670
|
};
|
|
3551
|
-
const
|
|
3552
|
-
|
|
3553
|
-
|
|
3671
|
+
const writeRow = (line) => {
|
|
3672
|
+
const row = this.clampVisible(sanitizeHudRow(line), rowCap);
|
|
3673
|
+
process.stdout.write(`\r\x1B[K${row}
|
|
3554
3674
|
`);
|
|
3555
3675
|
};
|
|
3676
|
+
const draw = (moveUp) => {
|
|
3677
|
+
if (moveUp) process.stdout.write(`\x1B[${items.length + 1}A`);
|
|
3678
|
+
else writeRow(pad + buildBoxTopPlain(geo.boxW, border));
|
|
3679
|
+
for (let i = 0; i < items.length; i++) {
|
|
3680
|
+
writeRow(pad + buildBoxBody(renderItemContent(i), geo.contentW, border));
|
|
3681
|
+
}
|
|
3682
|
+
writeRow(pad + buildBoxBottom(geo.boxW, border));
|
|
3683
|
+
};
|
|
3556
3684
|
draw(false);
|
|
3557
|
-
const titleRows = title ?
|
|
3685
|
+
const titleRows = title ? 2 : 1;
|
|
3686
|
+
const boxRows = 1 + items.length + 1;
|
|
3558
3687
|
const erase = () => {
|
|
3559
3688
|
process.stdout.write("\r");
|
|
3560
|
-
const up = titleRows +
|
|
3689
|
+
const up = titleRows + boxRows;
|
|
3561
3690
|
if (up > 0) process.stdout.write(`\x1B[${up}A`);
|
|
3562
3691
|
process.stdout.write("\x1B[J");
|
|
3563
3692
|
};
|
|
@@ -4533,11 +4662,12 @@ function printAssistant(tui, text) {
|
|
|
4533
4662
|
}
|
|
4534
4663
|
function startLoader(label) {
|
|
4535
4664
|
const frames = ["\u28F7", "\u28EF", "\u28DF", "\u287F", "\u28BF", "\u28FB", "\u28FD", "\u28FE"];
|
|
4665
|
+
const pad = " ".repeat(INPUT_BOX_MARGIN);
|
|
4536
4666
|
stdout.write("\x1B[?25l");
|
|
4537
4667
|
const draw = () => {
|
|
4538
4668
|
const now = Date.now();
|
|
4539
4669
|
const f = frames[Math.floor(now / 70) % frames.length];
|
|
4540
|
-
stdout.write(`\r\x1B[K${brandCycleColor(now)}${f}${c.reset} ${c.dim}${label}\u2026${c.reset}`);
|
|
4670
|
+
stdout.write(`\r\x1B[K${pad}${brandCycleColor(now)}${f}${c.reset} ${c.dim}${label}\u2026${c.reset}`);
|
|
4541
4671
|
};
|
|
4542
4672
|
draw();
|
|
4543
4673
|
const timer = setInterval(draw, 70);
|
|
@@ -4860,7 +4990,7 @@ ${c.dim}Press Control-C again to exit${c.reset}
|
|
|
4860
4990
|
const tilde = projectDir.startsWith(home) ? "~" + projectDir.slice(home.length) : projectDir;
|
|
4861
4991
|
const shortDir = tilde.length > 38 ? "\u2026" + tilde.slice(-37) : tilde;
|
|
4862
4992
|
const picked = await tui.select(
|
|
4863
|
-
`${c.bold}${
|
|
4993
|
+
`${c.bold}${gradientText("Resume a session")}${c.reset} ${c.gray}${shortDir}${c.reset} ${c.dim}\u2191\u2193 \xB7 enter \xB7 esc${c.reset}`,
|
|
4864
4994
|
items
|
|
4865
4995
|
);
|
|
4866
4996
|
if (typeof picked === "string") {
|