@vertaaux/cli 0.4.0 → 0.5.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/CHANGELOG.md +97 -0
- package/MIGRATION.md +239 -0
- package/README.md +34 -16
- package/dist/app/interactive-app.d.ts +101 -0
- package/dist/app/interactive-app.d.ts.map +1 -0
- package/dist/app/interactive-app.js +309 -0
- package/dist/app/layout/canvas.d.ts +23 -0
- package/dist/app/layout/canvas.d.ts.map +1 -0
- package/dist/app/layout/canvas.js +36 -0
- package/dist/app/layout/footer.d.ts +31 -0
- package/dist/app/layout/footer.d.ts.map +1 -0
- package/dist/app/layout/footer.js +41 -0
- package/dist/app/layout/header.d.ts +20 -0
- package/dist/app/layout/header.d.ts.map +1 -0
- package/dist/app/layout/header.js +27 -0
- package/dist/app/menu/categories.d.ts +20 -0
- package/dist/app/menu/categories.d.ts.map +1 -0
- package/dist/app/menu/categories.js +181 -0
- package/dist/app/menu/filter.d.ts +17 -0
- package/dist/app/menu/filter.d.ts.map +1 -0
- package/dist/app/menu/filter.js +33 -0
- package/dist/app/menu/menu-view.d.ts +35 -0
- package/dist/app/menu/menu-view.d.ts.map +1 -0
- package/dist/app/menu/menu-view.js +230 -0
- package/dist/app/menu/recent.d.ts +24 -0
- package/dist/app/menu/recent.d.ts.map +1 -0
- package/dist/app/menu/recent.js +49 -0
- package/dist/app/types.d.ts +43 -0
- package/dist/app/types.d.ts.map +1 -0
- package/dist/app/types.js +7 -0
- package/dist/app/views/command-runner.d.ts +36 -0
- package/dist/app/views/command-runner.d.ts.map +1 -0
- package/dist/app/views/command-runner.js +372 -0
- package/dist/app/views/help-overlay.d.ts +21 -0
- package/dist/app/views/help-overlay.d.ts.map +1 -0
- package/dist/app/views/help-overlay.js +45 -0
- package/dist/auth/ci-token.d.ts +8 -2
- package/dist/auth/ci-token.d.ts.map +1 -1
- package/dist/auth/ci-token.js +15 -30
- package/dist/auth/device-flow.d.ts +2 -1
- package/dist/auth/device-flow.d.ts.map +1 -1
- package/dist/auth/device-flow.js +13 -10
- package/dist/auth/token-store.d.ts.map +1 -1
- package/dist/auth/token-store.js +12 -2
- package/dist/baseline/diff.d.ts +2 -2
- package/dist/baseline/diff.d.ts.map +1 -1
- package/dist/baseline/diff.js +15 -34
- package/dist/commands/a11y.d.ts +9 -0
- package/dist/commands/a11y.d.ts.map +1 -0
- package/dist/commands/a11y.js +76 -0
- package/dist/commands/audit/artifacts.d.ts +27 -0
- package/dist/commands/audit/artifacts.d.ts.map +1 -0
- package/dist/commands/audit/artifacts.js +158 -0
- package/dist/commands/audit/ci-detection.d.ts +18 -0
- package/dist/commands/audit/ci-detection.d.ts.map +1 -0
- package/dist/commands/audit/ci-detection.js +71 -0
- package/dist/commands/audit/explain.d.ts +11 -0
- package/dist/commands/audit/explain.d.ts.map +1 -0
- package/dist/commands/audit/explain.js +45 -0
- package/dist/commands/audit/filters.d.ts +17 -0
- package/dist/commands/audit/filters.d.ts.map +1 -0
- package/dist/commands/audit/filters.js +40 -0
- package/dist/commands/audit/index.d.ts +18 -0
- package/dist/commands/audit/index.d.ts.map +1 -0
- package/dist/commands/audit/index.js +564 -0
- package/dist/commands/audit/output.d.ts +32 -0
- package/dist/commands/audit/output.d.ts.map +1 -0
- package/dist/commands/audit/output.js +130 -0
- package/dist/commands/audit/policy.d.ts +19 -0
- package/dist/commands/audit/policy.d.ts.map +1 -0
- package/dist/commands/audit/policy.js +102 -0
- package/dist/commands/audit/scoring.d.ts +23 -0
- package/dist/commands/audit/scoring.d.ts.map +1 -0
- package/dist/commands/audit/scoring.js +70 -0
- package/dist/commands/audit/types.d.ts +88 -0
- package/dist/commands/audit/types.d.ts.map +1 -0
- package/dist/commands/audit/types.js +8 -0
- package/dist/commands/audit.d.ts +2 -60
- package/dist/commands/audit.d.ts.map +1 -1
- package/dist/commands/audit.js +2 -1097
- package/dist/commands/baseline.d.ts +1 -0
- package/dist/commands/baseline.d.ts.map +1 -1
- package/dist/commands/baseline.js +205 -121
- package/dist/commands/comment.d.ts +22 -0
- package/dist/commands/comment.d.ts.map +1 -1
- package/dist/commands/comment.js +122 -58
- package/dist/commands/compare.d.ts +17 -0
- package/dist/commands/compare.d.ts.map +1 -1
- package/dist/commands/compare.js +287 -180
- package/dist/commands/diff.d.ts +5 -0
- package/dist/commands/diff.d.ts.map +1 -1
- package/dist/commands/diff.js +168 -141
- package/dist/commands/doc.d.ts +10 -0
- package/dist/commands/doc.d.ts.map +1 -1
- package/dist/commands/doc.js +134 -76
- package/dist/commands/doctor.d.ts +2 -0
- package/dist/commands/doctor.d.ts.map +1 -1
- package/dist/commands/doctor.js +164 -17
- package/dist/commands/download.d.ts +10 -0
- package/dist/commands/download.d.ts.map +1 -1
- package/dist/commands/download.js +169 -112
- package/dist/commands/explain.d.ts +5 -0
- package/dist/commands/explain.d.ts.map +1 -1
- package/dist/commands/explain.js +241 -155
- package/dist/commands/fix-all.d.ts +25 -0
- package/dist/commands/fix-all.d.ts.map +1 -0
- package/dist/commands/fix-all.js +206 -0
- package/dist/commands/fix-plan.d.ts +9 -0
- package/dist/commands/fix-plan.d.ts.map +1 -1
- package/dist/commands/fix-plan.js +152 -89
- package/dist/commands/fix.d.ts +17 -0
- package/dist/commands/fix.d.ts.map +1 -0
- package/dist/commands/fix.js +111 -0
- package/dist/commands/init.d.ts +11 -0
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +94 -42
- package/dist/commands/login.d.ts +18 -0
- package/dist/commands/login.d.ts.map +1 -1
- package/dist/commands/login.js +263 -92
- package/dist/commands/patch-review.d.ts +11 -0
- package/dist/commands/patch-review.d.ts.map +1 -1
- package/dist/commands/patch-review.js +159 -97
- package/dist/commands/policy.d.ts +31 -0
- package/dist/commands/policy.d.ts.map +1 -1
- package/dist/commands/policy.js +269 -124
- package/dist/commands/release-notes.d.ts +10 -0
- package/dist/commands/release-notes.d.ts.map +1 -1
- package/dist/commands/release-notes.js +127 -73
- package/dist/commands/scan.d.ts +13 -0
- package/dist/commands/scan.d.ts.map +1 -0
- package/dist/commands/scan.js +133 -0
- package/dist/commands/status.d.ts +9 -0
- package/dist/commands/status.d.ts.map +1 -0
- package/dist/commands/status.js +81 -0
- package/dist/commands/suggest.d.ts +10 -0
- package/dist/commands/suggest.d.ts.map +1 -1
- package/dist/commands/suggest.js +153 -82
- package/dist/commands/triage.d.ts +35 -0
- package/dist/commands/triage.d.ts.map +1 -1
- package/dist/commands/triage.js +206 -81
- package/dist/commands/upload.d.ts +9 -0
- package/dist/commands/upload.d.ts.map +1 -1
- package/dist/commands/upload.js +140 -101
- package/dist/commands/verify.d.ts +13 -0
- package/dist/commands/verify.d.ts.map +1 -0
- package/dist/commands/verify.js +118 -0
- package/dist/index.d.ts +3 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +125 -990
- package/dist/interactive/fix-wizard.d.ts +3 -0
- package/dist/interactive/fix-wizard.d.ts.map +1 -1
- package/dist/interactive/fix-wizard.js +130 -112
- package/dist/interactive/init-wizard.d.ts +3 -1
- package/dist/interactive/init-wizard.d.ts.map +1 -1
- package/dist/interactive/init-wizard.js +207 -138
- package/dist/interactive/prompts.d.ts +7 -3
- package/dist/interactive/prompts.d.ts.map +1 -1
- package/dist/interactive/prompts.js +44 -23
- package/dist/output/envelope.d.ts +2 -0
- package/dist/output/envelope.d.ts.map +1 -1
- package/dist/output/envelope.js +18 -2
- package/dist/output/factory.d.ts +2 -1
- package/dist/output/factory.d.ts.map +1 -1
- package/dist/output/html.d.ts +2 -1
- package/dist/output/html.d.ts.map +1 -1
- package/dist/output/html.js +3 -2
- package/dist/output/human.d.ts +2 -1
- package/dist/output/human.d.ts.map +1 -1
- package/dist/output/human.js +3 -2
- package/dist/output/json.d.ts +2 -1
- package/dist/output/json.d.ts.map +1 -1
- package/dist/output/junit.d.ts +2 -1
- package/dist/output/junit.d.ts.map +1 -1
- package/dist/output/sarif.d.ts +2 -1
- package/dist/output/sarif.d.ts.map +1 -1
- package/dist/types.d.ts +74 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +5 -0
- package/dist/ui/banner.d.ts +34 -0
- package/dist/ui/banner.d.ts.map +1 -1
- package/dist/ui/banner.js +97 -5
- package/dist/ui/diagnostics.d.ts +9 -4
- package/dist/ui/diagnostics.d.ts.map +1 -1
- package/dist/ui/diagnostics.js +32 -82
- package/dist/ui/strings.d.ts +373 -0
- package/dist/ui/strings.d.ts.map +1 -0
- package/dist/ui/strings.js +499 -0
- package/dist/ui/table.d.ts +0 -2
- package/dist/ui/table.d.ts.map +1 -1
- package/dist/ui/table.js +3 -4
- package/dist/utils/api-client.d.ts +46 -0
- package/dist/utils/api-client.d.ts.map +1 -0
- package/dist/utils/api-client.js +170 -0
- package/dist/utils/client.d.ts +29 -18
- package/dist/utils/client.d.ts.map +1 -1
- package/dist/utils/client.js +102 -12
- package/dist/utils/formatters.d.ts +38 -0
- package/dist/utils/formatters.d.ts.map +1 -0
- package/dist/utils/formatters.js +277 -0
- package/dist/utils/url-classify.d.ts.map +1 -1
- package/dist/utils/url-classify.js +24 -3
- package/node_modules/@vertaaux/tui/dist/index.cjs +713 -20
- package/node_modules/@vertaaux/tui/dist/index.cjs.map +1 -1
- package/node_modules/@vertaaux/tui/dist/index.d.cts +361 -4
- package/node_modules/@vertaaux/tui/dist/index.d.ts +361 -4
- package/node_modules/@vertaaux/tui/dist/index.js +689 -21
- package/node_modules/@vertaaux/tui/dist/index.js.map +1 -1
- package/package.json +13 -5
- package/dist/commands/client.d.ts +0 -14
- package/dist/commands/client.d.ts.map +0 -1
- package/dist/commands/client.js +0 -362
- package/dist/commands/drift.d.ts +0 -15
- package/dist/commands/drift.d.ts.map +0 -1
- package/dist/commands/drift.js +0 -309
- package/dist/commands/protect.d.ts +0 -16
- package/dist/commands/protect.d.ts.map +0 -1
- package/dist/commands/protect.js +0 -323
- package/dist/commands/report.d.ts +0 -15
- package/dist/commands/report.d.ts.map +0 -1
- package/dist/commands/report.js +0 -214
- package/dist/policy/sync.d.ts +0 -67
- package/dist/policy/sync.d.ts.map +0 -1
- package/dist/policy/sync.js +0 -147
|
@@ -202,6 +202,7 @@ var tokens = {
|
|
|
202
202
|
var _colorEnabled = true;
|
|
203
203
|
function setColorEnabled(enabled) {
|
|
204
204
|
_colorEnabled = enabled;
|
|
205
|
+
chalk__default.default.level = enabled ? 3 : 0;
|
|
205
206
|
}
|
|
206
207
|
function isColorEnabled() {
|
|
207
208
|
return _colorEnabled;
|
|
@@ -222,6 +223,17 @@ function bold(text) {
|
|
|
222
223
|
if (!_colorEnabled) return text;
|
|
223
224
|
return chalk__default.default.bold(text);
|
|
224
225
|
}
|
|
226
|
+
function severityColor(level) {
|
|
227
|
+
const normalized = (level || "info").toLowerCase();
|
|
228
|
+
const hex = severity[normalized] ?? severity.info;
|
|
229
|
+
const isBold = ["critical", "error", "serious", "warning"].includes(normalized);
|
|
230
|
+
const label2 = normalized.toUpperCase();
|
|
231
|
+
return isBold ? boldColor(label2, hex) : colorize(label2, hex);
|
|
232
|
+
}
|
|
233
|
+
function bgColor(text, hex) {
|
|
234
|
+
if (!_colorEnabled) return text;
|
|
235
|
+
return chalk__default.default.bgHex(hex)(text);
|
|
236
|
+
}
|
|
225
237
|
function applyGradient(text, colors = gradient) {
|
|
226
238
|
if (!_colorEnabled) return text;
|
|
227
239
|
const colorCount = colors.length;
|
|
@@ -260,7 +272,7 @@ function shouldUseColor() {
|
|
|
260
272
|
return isTTY();
|
|
261
273
|
}
|
|
262
274
|
function getTerminalWidth() {
|
|
263
|
-
return process.stderr.columns || 80;
|
|
275
|
+
return parseInt(process.env.COLUMNS ?? "", 10) || process.stderr.columns || 80;
|
|
264
276
|
}
|
|
265
277
|
function getTerminalHeight() {
|
|
266
278
|
return process.stderr.rows || 24;
|
|
@@ -273,6 +285,96 @@ function supportsUnicode() {
|
|
|
273
285
|
return true;
|
|
274
286
|
}
|
|
275
287
|
|
|
288
|
+
// src/symbols.ts
|
|
289
|
+
var SYMBOL_MAP = {
|
|
290
|
+
nerd: {
|
|
291
|
+
// Nerd Font Material Design icons (private use area U+F0000+)
|
|
292
|
+
success: "\u{F012C}",
|
|
293
|
+
// nf-md-check_circle
|
|
294
|
+
fail: "\u{F0156}",
|
|
295
|
+
// nf-md-close_circle
|
|
296
|
+
warning: "\u{F01A6}",
|
|
297
|
+
// nf-md-alert
|
|
298
|
+
info: "\u{F02FD}",
|
|
299
|
+
// nf-md-information
|
|
300
|
+
pending: "\u25EF",
|
|
301
|
+
// ◯ large circle (no NF required)
|
|
302
|
+
running: "\u25C9"
|
|
303
|
+
// ◉ fisheye (no NF required)
|
|
304
|
+
},
|
|
305
|
+
unicode: {
|
|
306
|
+
success: "\u2713",
|
|
307
|
+
fail: "\u2717",
|
|
308
|
+
warning: "\u25B2",
|
|
309
|
+
info: "\u2139",
|
|
310
|
+
pending: "\u25CB",
|
|
311
|
+
running: "\u25C6"
|
|
312
|
+
},
|
|
313
|
+
ascii: {
|
|
314
|
+
success: "+",
|
|
315
|
+
fail: "x",
|
|
316
|
+
warning: "!",
|
|
317
|
+
info: "i",
|
|
318
|
+
pending: "-",
|
|
319
|
+
running: "*"
|
|
320
|
+
}
|
|
321
|
+
};
|
|
322
|
+
var _tier = "unicode";
|
|
323
|
+
function setSymbolTier(tier) {
|
|
324
|
+
_tier = tier;
|
|
325
|
+
}
|
|
326
|
+
function sym(role) {
|
|
327
|
+
return SYMBOL_MAP[_tier][role];
|
|
328
|
+
}
|
|
329
|
+
function detectSymbolTier(options = {}) {
|
|
330
|
+
if (options.plainMode) return "ascii";
|
|
331
|
+
const envIcons = process.env.VERTAA_ICONS;
|
|
332
|
+
if (envIcons === "nerd" || envIcons === "unicode" || envIcons === "ascii") {
|
|
333
|
+
return envIcons;
|
|
334
|
+
}
|
|
335
|
+
const termProgram = process.env.TERM_PROGRAM;
|
|
336
|
+
if (termProgram === "iTerm.app" || termProgram === "WezTerm") {
|
|
337
|
+
return "nerd";
|
|
338
|
+
}
|
|
339
|
+
if (supportsUnicode()) return "unicode";
|
|
340
|
+
return "ascii";
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
// src/plain.ts
|
|
344
|
+
var _plainMode = false;
|
|
345
|
+
function setPlainMode(enabled) {
|
|
346
|
+
_plainMode = enabled;
|
|
347
|
+
if (enabled) {
|
|
348
|
+
setColorEnabled(false);
|
|
349
|
+
setSymbolTier("ascii");
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
function isPlainMode() {
|
|
353
|
+
return _plainMode;
|
|
354
|
+
}
|
|
355
|
+
function toPlainBox(str) {
|
|
356
|
+
if (!_plainMode) return str;
|
|
357
|
+
return str.replace(/[─━═]/g, "-").replace(/[│┃║]/g, "|").replace(/[╭╮╰╯]/g, "+").replace(/[┌┍┎┏╔]/g, "+").replace(/[┐┑┒┓╗]/g, "+").replace(/[└┕┖┗╚]/g, "+").replace(/[┘┙┚┛╝]/g, "+").replace(/[├┤┬┴┼]/g, "+");
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
// src/easing.ts
|
|
361
|
+
function linear(t) {
|
|
362
|
+
return t;
|
|
363
|
+
}
|
|
364
|
+
function easeIn(t) {
|
|
365
|
+
return t * t;
|
|
366
|
+
}
|
|
367
|
+
function easeOut(t) {
|
|
368
|
+
return t * (2 - t);
|
|
369
|
+
}
|
|
370
|
+
function easeInOut(t) {
|
|
371
|
+
return t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t;
|
|
372
|
+
}
|
|
373
|
+
function lerp(from, to, t, ease = linear) {
|
|
374
|
+
const clamped = Math.max(0, Math.min(1, t));
|
|
375
|
+
return from + (to - from) * ease(clamped);
|
|
376
|
+
}
|
|
377
|
+
|
|
276
378
|
// src/primitives/text.ts
|
|
277
379
|
var text_exports = {};
|
|
278
380
|
__export(text_exports, {
|
|
@@ -391,7 +493,8 @@ function box(content, opts = {}) {
|
|
|
391
493
|
result.push(bc(chars.vertical) + " ".repeat(termWidth - 2) + bc(chars.vertical));
|
|
392
494
|
}
|
|
393
495
|
result.push(renderBottomBorder(chars, termWidth, subtitle, borderColor));
|
|
394
|
-
|
|
496
|
+
const output = result.join("\n");
|
|
497
|
+
return toPlainBox(output);
|
|
395
498
|
}
|
|
396
499
|
function renderNoBorder(content, padding) {
|
|
397
500
|
const pad = " ".repeat(padding);
|
|
@@ -536,6 +639,9 @@ function alignCell(text, width, align) {
|
|
|
536
639
|
// src/primitives/spinner.ts
|
|
537
640
|
var BRAILLE_FRAMES = ["\u287F", "\u28DF", "\u28EF", "\u28F7", "\u28FE", "\u28FD", "\u28FB", "\u28BF"];
|
|
538
641
|
var FRAME_INTERVAL = 80;
|
|
642
|
+
function spinnerFrame(frameIndex) {
|
|
643
|
+
return BRAILLE_FRAMES[frameIndex % BRAILLE_FRAMES.length];
|
|
644
|
+
}
|
|
539
645
|
function createSpinner(text, output = process.stderr) {
|
|
540
646
|
let frameIndex = 0;
|
|
541
647
|
let currentText = text;
|
|
@@ -545,6 +651,11 @@ function createSpinner(text, output = process.stderr) {
|
|
|
545
651
|
const enabled = isTTY();
|
|
546
652
|
function render() {
|
|
547
653
|
if (!enabled) return;
|
|
654
|
+
if (isPlainMode()) {
|
|
655
|
+
output.write(`[running] ${currentText}
|
|
656
|
+
`);
|
|
657
|
+
return;
|
|
658
|
+
}
|
|
548
659
|
const frame = isColorEnabled() ? colorize(BRAILLE_FRAMES[frameIndex], brand.lime) : BRAILLE_FRAMES[frameIndex];
|
|
549
660
|
const phaseStr = currentPhase ? dim(` [${currentPhase}]`) : "";
|
|
550
661
|
const line = `${frame} ${currentText}${phaseStr}`;
|
|
@@ -569,8 +680,12 @@ function createSpinner(text, output = process.stderr) {
|
|
|
569
680
|
running = true;
|
|
570
681
|
frameIndex = 0;
|
|
571
682
|
if (enabled) {
|
|
572
|
-
|
|
573
|
-
|
|
683
|
+
if (isPlainMode()) {
|
|
684
|
+
render();
|
|
685
|
+
} else {
|
|
686
|
+
render();
|
|
687
|
+
timer = setInterval(render, FRAME_INTERVAL);
|
|
688
|
+
}
|
|
574
689
|
}
|
|
575
690
|
},
|
|
576
691
|
stop() {
|
|
@@ -589,23 +704,23 @@ function createSpinner(text, output = process.stderr) {
|
|
|
589
704
|
},
|
|
590
705
|
succeed(text2) {
|
|
591
706
|
instance.stop();
|
|
592
|
-
const
|
|
593
|
-
writeFinalLine(
|
|
707
|
+
const icon = isColorEnabled() ? colorize(sym("success"), "#4ADE80") : sym("success");
|
|
708
|
+
writeFinalLine(icon, text2);
|
|
594
709
|
},
|
|
595
710
|
fail(text2) {
|
|
596
711
|
instance.stop();
|
|
597
|
-
const
|
|
598
|
-
writeFinalLine(
|
|
712
|
+
const icon = isColorEnabled() ? colorize(sym("fail"), "#F87171") : sym("fail");
|
|
713
|
+
writeFinalLine(icon, text2);
|
|
599
714
|
},
|
|
600
715
|
warn(text2) {
|
|
601
716
|
instance.stop();
|
|
602
|
-
const
|
|
603
|
-
writeFinalLine(
|
|
717
|
+
const icon = isColorEnabled() ? colorize(sym("warning"), "#FACC15") : sym("warning");
|
|
718
|
+
writeFinalLine(icon, text2);
|
|
604
719
|
},
|
|
605
720
|
info(text2) {
|
|
606
721
|
instance.stop();
|
|
607
|
-
const
|
|
608
|
-
writeFinalLine(
|
|
722
|
+
const icon = isColorEnabled() ? colorize(sym("info"), "#6BCEFF") : sym("info");
|
|
723
|
+
writeFinalLine(icon, text2);
|
|
609
724
|
}
|
|
610
725
|
};
|
|
611
726
|
return instance;
|
|
@@ -623,7 +738,7 @@ function createProgressBar(opts) {
|
|
|
623
738
|
output = process.stderr
|
|
624
739
|
} = opts;
|
|
625
740
|
const enabled = isTTY();
|
|
626
|
-
|
|
741
|
+
const startTime = Date.now();
|
|
627
742
|
function renderBar(current, barWidth) {
|
|
628
743
|
const pct = total > 0 ? Math.min(current / total, 1) : 0;
|
|
629
744
|
const pctStr = `${Math.round(pct * 100)}%`;
|
|
@@ -645,8 +760,8 @@ function createProgressBar(opts) {
|
|
|
645
760
|
if (!enabled) return;
|
|
646
761
|
output.write(screen.clearLine + "\r");
|
|
647
762
|
if (message) {
|
|
648
|
-
const
|
|
649
|
-
output.write(`${
|
|
763
|
+
const sym2 = isColorEnabled() ? colorize("\u2713", "#4ADE80") : "\u2713";
|
|
764
|
+
output.write(`${sym2} ${message}
|
|
650
765
|
`);
|
|
651
766
|
}
|
|
652
767
|
},
|
|
@@ -654,8 +769,8 @@ function createProgressBar(opts) {
|
|
|
654
769
|
if (!enabled) return;
|
|
655
770
|
output.write(screen.clearLine + "\r");
|
|
656
771
|
if (message) {
|
|
657
|
-
const
|
|
658
|
-
output.write(`${
|
|
772
|
+
const sym2 = isColorEnabled() ? colorize("\u2717", "#F87171") : "\u2717";
|
|
773
|
+
output.write(`${sym2} ${message}
|
|
659
774
|
`);
|
|
660
775
|
}
|
|
661
776
|
},
|
|
@@ -862,6 +977,200 @@ function parseDiff(diffText) {
|
|
|
862
977
|
return lines;
|
|
863
978
|
}
|
|
864
979
|
|
|
980
|
+
// src/primitives/error-box.ts
|
|
981
|
+
function buildBoxWidth() {
|
|
982
|
+
return Math.min(getTerminalWidth() - 4, 88);
|
|
983
|
+
}
|
|
984
|
+
function buildErrorContent(opts) {
|
|
985
|
+
const parts = [];
|
|
986
|
+
parts.push(
|
|
987
|
+
`${sym("fail")} Error: ${bold(opts.message)}`
|
|
988
|
+
);
|
|
989
|
+
if (opts.context !== void 0 && opts.context !== "") {
|
|
990
|
+
parts.push("");
|
|
991
|
+
parts.push(dim(opts.context));
|
|
992
|
+
}
|
|
993
|
+
if (opts.suggestion !== void 0 && opts.suggestion !== "") {
|
|
994
|
+
parts.push("");
|
|
995
|
+
parts.push(colorize(`Try: ${opts.suggestion}`, brand.lime));
|
|
996
|
+
}
|
|
997
|
+
if (opts.exitCode !== void 0) {
|
|
998
|
+
parts.push("");
|
|
999
|
+
parts.push(dim(`Exit code: ${opts.exitCode}`));
|
|
1000
|
+
}
|
|
1001
|
+
return parts.join("\n");
|
|
1002
|
+
}
|
|
1003
|
+
function buildWarningContent(opts) {
|
|
1004
|
+
const parts = [];
|
|
1005
|
+
parts.push(
|
|
1006
|
+
`${sym("warning")} Warning: ${bold(opts.message)}`
|
|
1007
|
+
);
|
|
1008
|
+
if (opts.context !== void 0 && opts.context !== "") {
|
|
1009
|
+
parts.push("");
|
|
1010
|
+
parts.push(dim(opts.context));
|
|
1011
|
+
}
|
|
1012
|
+
if (opts.suggestion !== void 0 && opts.suggestion !== "") {
|
|
1013
|
+
parts.push("");
|
|
1014
|
+
parts.push(colorize(`Try: ${opts.suggestion}`, brand.lime));
|
|
1015
|
+
}
|
|
1016
|
+
return parts.join("\n");
|
|
1017
|
+
}
|
|
1018
|
+
function renderError(opts) {
|
|
1019
|
+
const content = buildErrorContent(opts);
|
|
1020
|
+
return box(content, {
|
|
1021
|
+
border: "rounded",
|
|
1022
|
+
borderColor: tokens.error,
|
|
1023
|
+
padding: 1,
|
|
1024
|
+
width: buildBoxWidth()
|
|
1025
|
+
});
|
|
1026
|
+
}
|
|
1027
|
+
function renderWarning(opts) {
|
|
1028
|
+
const content = buildWarningContent(opts);
|
|
1029
|
+
return box(content, {
|
|
1030
|
+
border: "rounded",
|
|
1031
|
+
borderColor: tokens.warning,
|
|
1032
|
+
padding: 1,
|
|
1033
|
+
width: buildBoxWidth()
|
|
1034
|
+
});
|
|
1035
|
+
}
|
|
1036
|
+
|
|
1037
|
+
// src/layout/step-list.ts
|
|
1038
|
+
function stepGlyph(state, frameIndex) {
|
|
1039
|
+
switch (state.status) {
|
|
1040
|
+
case "running":
|
|
1041
|
+
return colorize(spinnerFrame(frameIndex), tokens.success);
|
|
1042
|
+
case "done":
|
|
1043
|
+
return colorize(sym("success"), tokens.success);
|
|
1044
|
+
case "failed":
|
|
1045
|
+
return colorize(sym("fail"), tokens.error);
|
|
1046
|
+
case "warning":
|
|
1047
|
+
return colorize(sym("warning"), tokens.warning);
|
|
1048
|
+
case "skipped":
|
|
1049
|
+
return dim("\u2014");
|
|
1050
|
+
default:
|
|
1051
|
+
return dim(sym("pending"));
|
|
1052
|
+
}
|
|
1053
|
+
}
|
|
1054
|
+
function stepText(state) {
|
|
1055
|
+
switch (state.status) {
|
|
1056
|
+
case "done":
|
|
1057
|
+
return state.summaryText;
|
|
1058
|
+
case "failed":
|
|
1059
|
+
return state.failReason ? `${state.actionText} \u2014 ${state.failReason}` : state.actionText;
|
|
1060
|
+
case "skipped":
|
|
1061
|
+
return dim(state.actionText);
|
|
1062
|
+
default:
|
|
1063
|
+
return state.actionText;
|
|
1064
|
+
}
|
|
1065
|
+
}
|
|
1066
|
+
function stepDuration(state) {
|
|
1067
|
+
if (state.durationMs === void 0) return "";
|
|
1068
|
+
const secs = state.durationMs / 1e3;
|
|
1069
|
+
return `${secs.toFixed(1)}s`;
|
|
1070
|
+
}
|
|
1071
|
+
function renderStep(state, frameIndex, indent, termWidth) {
|
|
1072
|
+
if (isPlainMode()) {
|
|
1073
|
+
return renderStepPlain(state, indent);
|
|
1074
|
+
}
|
|
1075
|
+
const glyph = stepGlyph(state, frameIndex);
|
|
1076
|
+
const text = stepText(state);
|
|
1077
|
+
const duration = stepDuration(state);
|
|
1078
|
+
const glyphVisible = visibleLength(glyph);
|
|
1079
|
+
const textVisible = visibleLength(text);
|
|
1080
|
+
const durationVisible = visibleLength(duration);
|
|
1081
|
+
const indentLen = indent.length;
|
|
1082
|
+
const usedWidth = indentLen + glyphVisible + 1 + textVisible + durationVisible;
|
|
1083
|
+
const gap = Math.max(1, termWidth - usedWidth);
|
|
1084
|
+
let line;
|
|
1085
|
+
if (duration) {
|
|
1086
|
+
line = `${indent}${glyph} ${text}${" ".repeat(gap)}${dim(duration)}`;
|
|
1087
|
+
} else {
|
|
1088
|
+
line = `${indent}${glyph} ${text}`;
|
|
1089
|
+
}
|
|
1090
|
+
const lines = [line];
|
|
1091
|
+
if (state.subSteps && state.subSteps.length > 0) {
|
|
1092
|
+
const subIndent = indent + " | ";
|
|
1093
|
+
for (const sub of state.subSteps) {
|
|
1094
|
+
lines.push(renderStep(sub, frameIndex, subIndent, termWidth));
|
|
1095
|
+
}
|
|
1096
|
+
}
|
|
1097
|
+
return lines.join("\n");
|
|
1098
|
+
}
|
|
1099
|
+
function renderStepPlain(state, indent) {
|
|
1100
|
+
const text = stepText(state);
|
|
1101
|
+
const duration = stepDuration(state);
|
|
1102
|
+
const durationStr = duration ? ` (${duration})` : "";
|
|
1103
|
+
const line = `${indent}[${state.status}] ${text}${durationStr}`;
|
|
1104
|
+
const lines = [line];
|
|
1105
|
+
if (state.subSteps && state.subSteps.length > 0) {
|
|
1106
|
+
for (const sub of state.subSteps) {
|
|
1107
|
+
lines.push(renderStepPlain(sub, indent + " | "));
|
|
1108
|
+
}
|
|
1109
|
+
}
|
|
1110
|
+
return lines.join("\n");
|
|
1111
|
+
}
|
|
1112
|
+
function renderStepList(steps, frameIndex, termWidth = 80) {
|
|
1113
|
+
const lines = steps.map((step) => renderStep(step, frameIndex, "", termWidth));
|
|
1114
|
+
return lines.join("\n");
|
|
1115
|
+
}
|
|
1116
|
+
|
|
1117
|
+
// src/layout/run-steps.ts
|
|
1118
|
+
function defToState(def) {
|
|
1119
|
+
return {
|
|
1120
|
+
id: def.id,
|
|
1121
|
+
actionText: def.actionText,
|
|
1122
|
+
summaryText: def.summaryText,
|
|
1123
|
+
status: "pending",
|
|
1124
|
+
startedAt: void 0,
|
|
1125
|
+
finishedAt: void 0,
|
|
1126
|
+
durationMs: void 0,
|
|
1127
|
+
failReason: void 0,
|
|
1128
|
+
subSteps: def.subSteps ? def.subSteps.map(defToState) : void 0
|
|
1129
|
+
};
|
|
1130
|
+
}
|
|
1131
|
+
async function runSteps(defs, opts = {}) {
|
|
1132
|
+
const failFast = opts.failFast ?? (isCI() || !isTTY());
|
|
1133
|
+
const { onStateChange } = opts;
|
|
1134
|
+
const states = defs.map(defToState);
|
|
1135
|
+
let anyFailed = false;
|
|
1136
|
+
const notify = () => {
|
|
1137
|
+
onStateChange?.(states);
|
|
1138
|
+
};
|
|
1139
|
+
for (let i = 0; i < defs.length; i++) {
|
|
1140
|
+
const def = defs[i];
|
|
1141
|
+
const state = states[i];
|
|
1142
|
+
state.status = "running";
|
|
1143
|
+
state.startedAt = Date.now();
|
|
1144
|
+
notify();
|
|
1145
|
+
try {
|
|
1146
|
+
await def.run();
|
|
1147
|
+
state.finishedAt = Date.now();
|
|
1148
|
+
state.durationMs = state.finishedAt - (state.startedAt ?? state.finishedAt);
|
|
1149
|
+
state.status = "done";
|
|
1150
|
+
notify();
|
|
1151
|
+
} catch (err) {
|
|
1152
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
1153
|
+
state.finishedAt = Date.now();
|
|
1154
|
+
state.durationMs = state.finishedAt - (state.startedAt ?? state.finishedAt);
|
|
1155
|
+
state.failReason = reason;
|
|
1156
|
+
anyFailed = true;
|
|
1157
|
+
if (failFast) {
|
|
1158
|
+
state.status = "failed";
|
|
1159
|
+
notify();
|
|
1160
|
+
for (let j = i + 1; j < states.length; j++) {
|
|
1161
|
+
states[j].status = "skipped";
|
|
1162
|
+
}
|
|
1163
|
+
notify();
|
|
1164
|
+
return { success: false, states };
|
|
1165
|
+
} else {
|
|
1166
|
+
state.status = "warning";
|
|
1167
|
+
notify();
|
|
1168
|
+
}
|
|
1169
|
+
}
|
|
1170
|
+
}
|
|
1171
|
+
return { success: !anyFailed, states };
|
|
1172
|
+
}
|
|
1173
|
+
|
|
865
1174
|
// src/dashboard/state.ts
|
|
866
1175
|
var AuditPhase = {
|
|
867
1176
|
Connecting: "Connecting",
|
|
@@ -983,6 +1292,10 @@ var AlternateScreenRenderer = class {
|
|
|
983
1292
|
this.entered = false;
|
|
984
1293
|
}
|
|
985
1294
|
}
|
|
1295
|
+
suspend() {
|
|
1296
|
+
}
|
|
1297
|
+
resume() {
|
|
1298
|
+
}
|
|
986
1299
|
renderSummary(result) {
|
|
987
1300
|
const status = result.passed ? colorize("PASSED", "#4ADE80") : colorize("FAILED", "#F87171");
|
|
988
1301
|
const scoreStr = colorize(String(result.overallScore), result.overallScore >= 70 ? "#4ADE80" : "#F87171");
|
|
@@ -1004,6 +1317,132 @@ function formatElapsed2(ms) {
|
|
|
1004
1317
|
return `${Math.floor(secs / 60)}m ${secs % 60}s`;
|
|
1005
1318
|
}
|
|
1006
1319
|
|
|
1320
|
+
// src/layout/composed.ts
|
|
1321
|
+
var ComposedRenderer = class {
|
|
1322
|
+
output;
|
|
1323
|
+
tickTimer = null;
|
|
1324
|
+
frameIndex = 0;
|
|
1325
|
+
suspended = false;
|
|
1326
|
+
resizeTimer = null;
|
|
1327
|
+
lastState = null;
|
|
1328
|
+
entered = false;
|
|
1329
|
+
constructor(output = process.stderr) {
|
|
1330
|
+
this.output = output;
|
|
1331
|
+
process.stderr.on("resize", this.onResize);
|
|
1332
|
+
}
|
|
1333
|
+
update(state) {
|
|
1334
|
+
if (!this.entered) {
|
|
1335
|
+
this.output.write(screen.altEnter + cursor.hide);
|
|
1336
|
+
this.entered = true;
|
|
1337
|
+
}
|
|
1338
|
+
this.lastState = state;
|
|
1339
|
+
const content = this.buildContent(state, this.frameIndex);
|
|
1340
|
+
const capped = this.capContent(content);
|
|
1341
|
+
this.output.write(cursor.home + screen.clear + capped);
|
|
1342
|
+
if (!this.tickTimer && !this.suspended) {
|
|
1343
|
+
this.startTick();
|
|
1344
|
+
}
|
|
1345
|
+
}
|
|
1346
|
+
finish(result) {
|
|
1347
|
+
this.stopTick();
|
|
1348
|
+
const content = this.buildFinishContent(result);
|
|
1349
|
+
if (this.entered) {
|
|
1350
|
+
this.output.write(cursor.home + screen.clear + content);
|
|
1351
|
+
}
|
|
1352
|
+
if (this.entered) {
|
|
1353
|
+
this.output.write(cursor.show + screen.altExit);
|
|
1354
|
+
this.entered = false;
|
|
1355
|
+
}
|
|
1356
|
+
this.lastState = null;
|
|
1357
|
+
}
|
|
1358
|
+
dispose() {
|
|
1359
|
+
this.stopTick();
|
|
1360
|
+
if (this.resizeTimer !== null) {
|
|
1361
|
+
clearTimeout(this.resizeTimer);
|
|
1362
|
+
this.resizeTimer = null;
|
|
1363
|
+
}
|
|
1364
|
+
process.stderr.removeListener("resize", this.onResize);
|
|
1365
|
+
if (this.entered) {
|
|
1366
|
+
this.output.write(cursor.show + screen.altExit);
|
|
1367
|
+
this.entered = false;
|
|
1368
|
+
}
|
|
1369
|
+
}
|
|
1370
|
+
suspend() {
|
|
1371
|
+
this.suspended = true;
|
|
1372
|
+
this.stopTick();
|
|
1373
|
+
if (this.entered) {
|
|
1374
|
+
this.output.write(cursor.show + screen.altExit);
|
|
1375
|
+
this.entered = false;
|
|
1376
|
+
}
|
|
1377
|
+
}
|
|
1378
|
+
resume() {
|
|
1379
|
+
this.suspended = false;
|
|
1380
|
+
if (this.lastState) {
|
|
1381
|
+
this.output.write(screen.altEnter + cursor.hide);
|
|
1382
|
+
this.entered = true;
|
|
1383
|
+
this.startTick();
|
|
1384
|
+
}
|
|
1385
|
+
}
|
|
1386
|
+
// ── Private helpers ──────────────────────────────────────────────
|
|
1387
|
+
startTick() {
|
|
1388
|
+
this.tickTimer = setInterval(() => {
|
|
1389
|
+
this.frameIndex++;
|
|
1390
|
+
if (this.lastState && this.entered) {
|
|
1391
|
+
const content = this.buildContent(this.lastState, this.frameIndex);
|
|
1392
|
+
const capped = this.capContent(content);
|
|
1393
|
+
this.output.write(cursor.home + screen.clear + capped);
|
|
1394
|
+
}
|
|
1395
|
+
}, FRAME_INTERVAL);
|
|
1396
|
+
}
|
|
1397
|
+
stopTick() {
|
|
1398
|
+
if (this.tickTimer !== null) {
|
|
1399
|
+
clearInterval(this.tickTimer);
|
|
1400
|
+
this.tickTimer = null;
|
|
1401
|
+
}
|
|
1402
|
+
}
|
|
1403
|
+
onResize = () => {
|
|
1404
|
+
if (this.resizeTimer !== null) {
|
|
1405
|
+
clearTimeout(this.resizeTimer);
|
|
1406
|
+
}
|
|
1407
|
+
this.resizeTimer = setTimeout(() => {
|
|
1408
|
+
this.resizeTimer = null;
|
|
1409
|
+
if (this.lastState && this.entered) {
|
|
1410
|
+
const content = this.buildContent(this.lastState, this.frameIndex);
|
|
1411
|
+
const capped = this.capContent(content);
|
|
1412
|
+
this.output.write(cursor.home + screen.clear + capped);
|
|
1413
|
+
}
|
|
1414
|
+
}, 100);
|
|
1415
|
+
};
|
|
1416
|
+
capContent(content) {
|
|
1417
|
+
const maxLines = Math.max(1, getTerminalHeight() - 2);
|
|
1418
|
+
const lines = content.split("\n");
|
|
1419
|
+
if (lines.length <= maxLines) return content;
|
|
1420
|
+
const overflow = lines.length - (maxLines - 1);
|
|
1421
|
+
const truncated = lines.slice(0, maxLines - 1);
|
|
1422
|
+
truncated.push(dim(`... ${overflow} more lines`));
|
|
1423
|
+
return truncated.join("\n");
|
|
1424
|
+
}
|
|
1425
|
+
buildContent(state, frame) {
|
|
1426
|
+
if (state.stepStates && state.stepStates.length > 0) {
|
|
1427
|
+
return renderStepList(state.stepStates, frame, getTerminalWidth());
|
|
1428
|
+
}
|
|
1429
|
+
const spinner = spinnerFrame(frame);
|
|
1430
|
+
const progress = state.phaseTotal > 0 ? ` (${state.phaseIndex}/${state.phaseTotal})` : "";
|
|
1431
|
+
return `${spinner} ${state.phase}${dim(progress)}`;
|
|
1432
|
+
}
|
|
1433
|
+
buildFinishContent(result) {
|
|
1434
|
+
if (result.passed) {
|
|
1435
|
+
return `${sym("success")} ${bold("Complete")}${dim(" " + formatElapsed3(result.elapsed))}`;
|
|
1436
|
+
}
|
|
1437
|
+
return `${sym("fail")} ${bold("Failed")}${dim(" " + formatElapsed3(result.elapsed))}`;
|
|
1438
|
+
}
|
|
1439
|
+
};
|
|
1440
|
+
function formatElapsed3(ms) {
|
|
1441
|
+
const secs = Math.round(ms / 1e3);
|
|
1442
|
+
if (secs < 60) return `(${secs}s)`;
|
|
1443
|
+
return `(${Math.floor(secs / 60)}m ${secs % 60}s)`;
|
|
1444
|
+
}
|
|
1445
|
+
|
|
1007
1446
|
// src/layout/inline.ts
|
|
1008
1447
|
var InlineRenderer = class {
|
|
1009
1448
|
output;
|
|
@@ -1025,7 +1464,7 @@ var InlineRenderer = class {
|
|
|
1025
1464
|
finish(result) {
|
|
1026
1465
|
const ts = timestamp();
|
|
1027
1466
|
const status = result.passed ? isColorEnabled() ? colorize("PASSED", "#4ADE80") : "PASSED" : isColorEnabled() ? colorize("FAILED", "#F87171") : "FAILED";
|
|
1028
|
-
const elapsed =
|
|
1467
|
+
const elapsed = formatElapsed4(result.elapsed);
|
|
1029
1468
|
this.output.write(
|
|
1030
1469
|
`${dim(ts)} ${bold("Audit Complete")} ${status} score=${result.overallScore} issues=${result.issueCount} ${dim(elapsed)}
|
|
1031
1470
|
`
|
|
@@ -1033,6 +1472,10 @@ var InlineRenderer = class {
|
|
|
1033
1472
|
}
|
|
1034
1473
|
dispose() {
|
|
1035
1474
|
}
|
|
1475
|
+
suspend() {
|
|
1476
|
+
}
|
|
1477
|
+
resume() {
|
|
1478
|
+
}
|
|
1036
1479
|
};
|
|
1037
1480
|
function timestamp() {
|
|
1038
1481
|
const now = /* @__PURE__ */ new Date();
|
|
@@ -1041,23 +1484,248 @@ function timestamp() {
|
|
|
1041
1484
|
function pad2(n) {
|
|
1042
1485
|
return n < 10 ? `0${n}` : String(n);
|
|
1043
1486
|
}
|
|
1044
|
-
function
|
|
1487
|
+
function formatElapsed4(ms) {
|
|
1045
1488
|
const secs = Math.round(ms / 1e3);
|
|
1046
1489
|
if (secs < 60) return `(${secs}s)`;
|
|
1047
1490
|
return `(${Math.floor(secs / 60)}m ${secs % 60}s)`;
|
|
1048
1491
|
}
|
|
1049
1492
|
|
|
1050
1493
|
// src/layout/renderer.ts
|
|
1494
|
+
var _rendererOverride = null;
|
|
1495
|
+
function setRendererOverride(renderer) {
|
|
1496
|
+
_rendererOverride = renderer;
|
|
1497
|
+
}
|
|
1051
1498
|
function createRenderer(mode = "auto", output = process.stderr) {
|
|
1499
|
+
if (_rendererOverride) return _rendererOverride;
|
|
1052
1500
|
if (mode === "auto") {
|
|
1053
|
-
mode = isTTY() && !isCI() ? "
|
|
1501
|
+
mode = isTTY() && !isCI() ? "composed" : "inline";
|
|
1054
1502
|
}
|
|
1055
1503
|
if (mode === "alternate") {
|
|
1056
1504
|
return new AlternateScreenRenderer(output);
|
|
1057
1505
|
}
|
|
1506
|
+
if (mode === "composed") {
|
|
1507
|
+
if (!isTTY()) return new InlineRenderer(output);
|
|
1508
|
+
return new ComposedRenderer(output);
|
|
1509
|
+
}
|
|
1058
1510
|
return new InlineRenderer(output);
|
|
1059
1511
|
}
|
|
1512
|
+
|
|
1513
|
+
// src/layout/physicalRows.ts
|
|
1514
|
+
function physicalRows(content, terminalWidth) {
|
|
1515
|
+
if (terminalWidth <= 0) return 0;
|
|
1516
|
+
const lines = content.split("\n");
|
|
1517
|
+
return lines.reduce((total, line) => {
|
|
1518
|
+
const len = visibleLength(line);
|
|
1519
|
+
return total + Math.max(1, Math.ceil(len / terminalWidth));
|
|
1520
|
+
}, 0);
|
|
1521
|
+
}
|
|
1522
|
+
|
|
1523
|
+
// src/layout/viewport.ts
|
|
1524
|
+
var KEY_CTRL_C = "";
|
|
1525
|
+
var KEY_UP = "\x1B[A";
|
|
1526
|
+
var KEY_DOWN = "\x1B[B";
|
|
1527
|
+
var KEY_PAGE_UP = "\x1B[5~";
|
|
1528
|
+
var KEY_PAGE_DOWN = "\x1B[6~";
|
|
1529
|
+
var KEY_ENTER_CR = "\r";
|
|
1530
|
+
var KEY_ENTER_LF = "\n";
|
|
1531
|
+
var KEY_ESC = "\x1B";
|
|
1532
|
+
var KEY_J = "j";
|
|
1533
|
+
var KEY_K = "k";
|
|
1534
|
+
function parseKey(data) {
|
|
1535
|
+
const str = data.toString("binary");
|
|
1536
|
+
if (str === KEY_CTRL_C) return "quit";
|
|
1537
|
+
if (str === KEY_UP) return "up";
|
|
1538
|
+
if (str === KEY_DOWN) return "down";
|
|
1539
|
+
if (str === KEY_PAGE_UP) return "pageup";
|
|
1540
|
+
if (str === KEY_PAGE_DOWN) return "pagedown";
|
|
1541
|
+
if (str === KEY_ENTER_CR || str === KEY_ENTER_LF) return "enter";
|
|
1542
|
+
if (str === KEY_ESC) return "escape";
|
|
1543
|
+
if (str === KEY_J) return "down";
|
|
1544
|
+
if (str === KEY_K) return "up";
|
|
1545
|
+
return null;
|
|
1546
|
+
}
|
|
1547
|
+
var ScrollingViewport = class {
|
|
1548
|
+
items;
|
|
1549
|
+
maxRows;
|
|
1550
|
+
output;
|
|
1551
|
+
stdin;
|
|
1552
|
+
/** Index of the currently highlighted item (0-indexed) */
|
|
1553
|
+
cursorIdx = 0;
|
|
1554
|
+
/** Index of first visible item */
|
|
1555
|
+
scrollOffset = 0;
|
|
1556
|
+
/** Index of expanded item (-1 = none) */
|
|
1557
|
+
expandedIdx = -1;
|
|
1558
|
+
/** Calculated visible rows (updated on resize) */
|
|
1559
|
+
visibleRows;
|
|
1560
|
+
lastRows = 0;
|
|
1561
|
+
resizeTimer = null;
|
|
1562
|
+
constructor(options) {
|
|
1563
|
+
this.items = options.items;
|
|
1564
|
+
this.maxRows = options.maxRows ?? 10;
|
|
1565
|
+
this.output = options.output ?? process.stderr;
|
|
1566
|
+
this.stdin = options.stdin ?? process.stdin;
|
|
1567
|
+
this.visibleRows = this.calcVisibleRows();
|
|
1568
|
+
}
|
|
1569
|
+
// ── Public entry point ──────────────────────────────────────────────────────
|
|
1570
|
+
/**
|
|
1571
|
+
* Run the viewport interactively.
|
|
1572
|
+
*
|
|
1573
|
+
* Guard: if stdin is not a TTY or setRawMode is unavailable, returns
|
|
1574
|
+
* immediately (non-interactive fallback).
|
|
1575
|
+
*
|
|
1576
|
+
* Resolves when the user presses Ctrl+C.
|
|
1577
|
+
*/
|
|
1578
|
+
async run() {
|
|
1579
|
+
if (!this.stdin.isTTY || typeof this.stdin.setRawMode !== "function") {
|
|
1580
|
+
return;
|
|
1581
|
+
}
|
|
1582
|
+
return new Promise((resolve) => {
|
|
1583
|
+
this.stdin.setRawMode(true);
|
|
1584
|
+
this.stdin.resume();
|
|
1585
|
+
this.output.write(cursor.hide);
|
|
1586
|
+
this.redraw(this.buildFrame());
|
|
1587
|
+
const onResize = () => {
|
|
1588
|
+
if (this.resizeTimer !== null) clearTimeout(this.resizeTimer);
|
|
1589
|
+
this.resizeTimer = setTimeout(() => {
|
|
1590
|
+
this.resizeTimer = null;
|
|
1591
|
+
this.visibleRows = this.calcVisibleRows();
|
|
1592
|
+
this.clampScrollOffset();
|
|
1593
|
+
this.lastRows = 0;
|
|
1594
|
+
this.redraw(this.buildFrame());
|
|
1595
|
+
}, 100);
|
|
1596
|
+
};
|
|
1597
|
+
const onData = (data) => {
|
|
1598
|
+
const action = parseKey(data);
|
|
1599
|
+
if (action === "quit") {
|
|
1600
|
+
cleanup();
|
|
1601
|
+
resolve();
|
|
1602
|
+
return;
|
|
1603
|
+
}
|
|
1604
|
+
this.handleAction(action);
|
|
1605
|
+
this.redraw(this.buildFrame());
|
|
1606
|
+
};
|
|
1607
|
+
const cleanup = () => {
|
|
1608
|
+
this.stdin.removeListener("data", onData);
|
|
1609
|
+
process.stderr.removeListener("resize", onResize);
|
|
1610
|
+
if (this.resizeTimer !== null) {
|
|
1611
|
+
clearTimeout(this.resizeTimer);
|
|
1612
|
+
this.resizeTimer = null;
|
|
1613
|
+
}
|
|
1614
|
+
try {
|
|
1615
|
+
this.stdin.setRawMode(false);
|
|
1616
|
+
} catch {
|
|
1617
|
+
}
|
|
1618
|
+
this.stdin.pause();
|
|
1619
|
+
this.output.write(cursor.show);
|
|
1620
|
+
};
|
|
1621
|
+
this.stdin.on("data", onData);
|
|
1622
|
+
process.stderr.on("resize", onResize);
|
|
1623
|
+
});
|
|
1624
|
+
}
|
|
1625
|
+
// ── Private helpers ─────────────────────────────────────────────────────────
|
|
1626
|
+
calcVisibleRows() {
|
|
1627
|
+
return Math.min(this.maxRows, getTerminalHeight() - 4);
|
|
1628
|
+
}
|
|
1629
|
+
clampScrollOffset() {
|
|
1630
|
+
const maxOffset = Math.max(0, this.items.length - this.visibleRows);
|
|
1631
|
+
this.scrollOffset = Math.max(0, Math.min(this.scrollOffset, maxOffset));
|
|
1632
|
+
}
|
|
1633
|
+
handleAction(action) {
|
|
1634
|
+
const last = this.items.length - 1;
|
|
1635
|
+
switch (action) {
|
|
1636
|
+
case "up":
|
|
1637
|
+
this.cursorIdx = Math.max(0, this.cursorIdx - 1);
|
|
1638
|
+
break;
|
|
1639
|
+
case "down":
|
|
1640
|
+
this.cursorIdx = Math.min(last, this.cursorIdx + 1);
|
|
1641
|
+
break;
|
|
1642
|
+
case "pageup":
|
|
1643
|
+
this.cursorIdx = Math.max(0, this.cursorIdx - this.visibleRows);
|
|
1644
|
+
break;
|
|
1645
|
+
case "pagedown":
|
|
1646
|
+
this.cursorIdx = Math.min(last, this.cursorIdx + this.visibleRows);
|
|
1647
|
+
break;
|
|
1648
|
+
case "enter":
|
|
1649
|
+
this.expandedIdx = this.expandedIdx === this.cursorIdx ? -1 : this.cursorIdx;
|
|
1650
|
+
break;
|
|
1651
|
+
case "escape":
|
|
1652
|
+
this.expandedIdx = -1;
|
|
1653
|
+
break;
|
|
1654
|
+
}
|
|
1655
|
+
if (this.cursorIdx < this.scrollOffset) {
|
|
1656
|
+
this.scrollOffset = this.cursorIdx;
|
|
1657
|
+
}
|
|
1658
|
+
if (this.cursorIdx >= this.scrollOffset + this.visibleRows) {
|
|
1659
|
+
this.scrollOffset = this.cursorIdx - this.visibleRows + 1;
|
|
1660
|
+
}
|
|
1661
|
+
this.clampScrollOffset();
|
|
1662
|
+
}
|
|
1663
|
+
buildFrame() {
|
|
1664
|
+
const width = getTerminalWidth();
|
|
1665
|
+
const lines = [];
|
|
1666
|
+
const plain = isPlainMode();
|
|
1667
|
+
const colored = isColorEnabled();
|
|
1668
|
+
if (this.scrollOffset > 0) {
|
|
1669
|
+
const count = this.scrollOffset;
|
|
1670
|
+
const arrow = plain ? "^" : "\u25B2";
|
|
1671
|
+
lines.push(dim(` ${arrow} ${count} more`));
|
|
1672
|
+
}
|
|
1673
|
+
const end = Math.min(this.scrollOffset + this.visibleRows, this.items.length);
|
|
1674
|
+
for (let i = this.scrollOffset; i < end; i++) {
|
|
1675
|
+
const item = this.items[i];
|
|
1676
|
+
const isHighlighted = i === this.cursorIdx;
|
|
1677
|
+
const isExpanded = i === this.expandedIdx;
|
|
1678
|
+
const badge = severityColor(item.severity);
|
|
1679
|
+
const badgeVisibleWidth = item.severity.toUpperCase().length;
|
|
1680
|
+
const titleMaxWidth = Math.max(10, width - badgeVisibleWidth - 18);
|
|
1681
|
+
const title = truncate(item.title, titleMaxWidth);
|
|
1682
|
+
const effortTag = dim(`[${item.effort}]`);
|
|
1683
|
+
let row = ` ${badge} ${title} ${effortTag}`;
|
|
1684
|
+
if (isHighlighted) {
|
|
1685
|
+
if (colored && !plain) {
|
|
1686
|
+
row = bgColor(row, brand.teal);
|
|
1687
|
+
} else {
|
|
1688
|
+
row = `\x1B[7m${row}\x1B[0m`;
|
|
1689
|
+
}
|
|
1690
|
+
}
|
|
1691
|
+
lines.push(row);
|
|
1692
|
+
if (isExpanded && item.detail) {
|
|
1693
|
+
const detailLines = item.detail.split("\n");
|
|
1694
|
+
for (const dl of detailLines) {
|
|
1695
|
+
lines.push(` ${dim(truncate(dl, width - 6))}`);
|
|
1696
|
+
}
|
|
1697
|
+
}
|
|
1698
|
+
}
|
|
1699
|
+
if (this.scrollOffset + this.visibleRows < this.items.length) {
|
|
1700
|
+
const count = this.items.length - (this.scrollOffset + this.visibleRows);
|
|
1701
|
+
const arrow = plain ? "v" : "\u25BC";
|
|
1702
|
+
lines.push(dim(` ${arrow} ${count} more`));
|
|
1703
|
+
}
|
|
1704
|
+
return lines.join("\n");
|
|
1705
|
+
}
|
|
1706
|
+
redraw(content) {
|
|
1707
|
+
const rows = this.lastRows;
|
|
1708
|
+
let seq = rows > 0 ? cursor.up(rows) + screen.clearDown : "";
|
|
1709
|
+
seq += content;
|
|
1710
|
+
this.output.write(seq);
|
|
1711
|
+
this.lastRows = physicalRows(content, getTerminalWidth());
|
|
1712
|
+
}
|
|
1713
|
+
};
|
|
1714
|
+
var _isOverrideActive = false;
|
|
1715
|
+
function setKeyboardOverrideActive(active) {
|
|
1716
|
+
_isOverrideActive = active;
|
|
1717
|
+
}
|
|
1060
1718
|
function createKeyboardHandler(stdin = process.stdin) {
|
|
1719
|
+
if (_isOverrideActive) {
|
|
1720
|
+
return {
|
|
1721
|
+
start() {
|
|
1722
|
+
},
|
|
1723
|
+
dispose() {
|
|
1724
|
+
},
|
|
1725
|
+
on() {
|
|
1726
|
+
}
|
|
1727
|
+
};
|
|
1728
|
+
}
|
|
1061
1729
|
const emitter = new events.EventEmitter();
|
|
1062
1730
|
let started = false;
|
|
1063
1731
|
let wasRawMode = false;
|
|
@@ -1103,9 +1771,14 @@ function createKeyboardHandler(stdin = process.stdin) {
|
|
|
1103
1771
|
|
|
1104
1772
|
exports.AlternateScreenRenderer = AlternateScreenRenderer;
|
|
1105
1773
|
exports.AuditPhase = AuditPhase;
|
|
1774
|
+
exports.BRAILLE_FRAMES = BRAILLE_FRAMES;
|
|
1775
|
+
exports.ComposedRenderer = ComposedRenderer;
|
|
1776
|
+
exports.FRAME_INTERVAL = FRAME_INTERVAL;
|
|
1106
1777
|
exports.InlineRenderer = InlineRenderer;
|
|
1107
1778
|
exports.PHASE_ORDER = PHASE_ORDER;
|
|
1779
|
+
exports.ScrollingViewport = ScrollingViewport;
|
|
1108
1780
|
exports.applyGradient = applyGradient;
|
|
1781
|
+
exports.bgColor = bgColor;
|
|
1109
1782
|
exports.bold = bold;
|
|
1110
1783
|
exports.boldColor = boldColor;
|
|
1111
1784
|
exports.borders = borders;
|
|
@@ -1119,37 +1792,57 @@ exports.createProgressBar = createProgressBar;
|
|
|
1119
1792
|
exports.createRenderer = createRenderer;
|
|
1120
1793
|
exports.createSpinner = createSpinner;
|
|
1121
1794
|
exports.cursor = cursor;
|
|
1795
|
+
exports.detectSymbolTier = detectSymbolTier;
|
|
1122
1796
|
exports.dim = dim;
|
|
1797
|
+
exports.easeIn = easeIn;
|
|
1798
|
+
exports.easeInOut = easeInOut;
|
|
1799
|
+
exports.easeOut = easeOut;
|
|
1123
1800
|
exports.getTerminalHeight = getTerminalHeight;
|
|
1124
1801
|
exports.getTerminalWidth = getTerminalWidth;
|
|
1125
1802
|
exports.gradient = gradient;
|
|
1126
1803
|
exports.isCI = isCI;
|
|
1127
1804
|
exports.isColorEnabled = isColorEnabled;
|
|
1805
|
+
exports.isPlainMode = isPlainMode;
|
|
1128
1806
|
exports.isStdinTTY = isStdinTTY;
|
|
1129
1807
|
exports.isStdoutTTY = isStdoutTTY;
|
|
1130
1808
|
exports.isTTY = isTTY;
|
|
1809
|
+
exports.lerp = lerp;
|
|
1810
|
+
exports.linear = linear;
|
|
1131
1811
|
exports.padEnd = padEnd;
|
|
1132
1812
|
exports.padStart = padStart;
|
|
1133
1813
|
exports.parseDiff = parseDiff;
|
|
1134
1814
|
exports.phaseIndex = phaseIndex;
|
|
1135
1815
|
exports.phaseTotal = phaseTotal;
|
|
1816
|
+
exports.physicalRows = physicalRows;
|
|
1136
1817
|
exports.renderDiff = renderDiff;
|
|
1818
|
+
exports.renderError = renderError;
|
|
1137
1819
|
exports.renderFrame = renderFrame;
|
|
1138
1820
|
exports.renderGroupedList = renderGroupedList;
|
|
1139
1821
|
exports.renderList = renderList;
|
|
1822
|
+
exports.renderStepList = renderStepList;
|
|
1140
1823
|
exports.renderTable = renderTable;
|
|
1141
1824
|
exports.renderTree = renderTree;
|
|
1825
|
+
exports.renderWarning = renderWarning;
|
|
1826
|
+
exports.runSteps = runSteps;
|
|
1142
1827
|
exports.scoreColor = scoreColor;
|
|
1143
1828
|
exports.screen = screen;
|
|
1144
1829
|
exports.setColorEnabled = setColorEnabled;
|
|
1830
|
+
exports.setKeyboardOverrideActive = setKeyboardOverrideActive;
|
|
1831
|
+
exports.setPlainMode = setPlainMode;
|
|
1832
|
+
exports.setRendererOverride = setRendererOverride;
|
|
1833
|
+
exports.setSymbolTier = setSymbolTier;
|
|
1145
1834
|
exports.severity = severity;
|
|
1835
|
+
exports.severityColor = severityColor;
|
|
1146
1836
|
exports.severityLabels = severityLabels;
|
|
1147
1837
|
exports.severityOrder = severityOrder;
|
|
1148
1838
|
exports.shouldUseColor = shouldUseColor;
|
|
1839
|
+
exports.spinnerFrame = spinnerFrame;
|
|
1149
1840
|
exports.stripAnsi = stripAnsi;
|
|
1150
1841
|
exports.style = style;
|
|
1151
1842
|
exports.supportsUnicode = supportsUnicode;
|
|
1843
|
+
exports.sym = sym;
|
|
1152
1844
|
exports.text = text_exports;
|
|
1845
|
+
exports.toPlainBox = toPlainBox;
|
|
1153
1846
|
exports.tokens = tokens;
|
|
1154
1847
|
exports.truncate = truncate;
|
|
1155
1848
|
exports.visibleLength = visibleLength;
|