@westbayberry/dg 1.0.28 → 1.0.33

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.
Files changed (2) hide show
  1. package/dist/index.mjs +895 -395
  2. package/package.json +1 -1
package/dist/index.mjs CHANGED
@@ -1874,14 +1874,14 @@ var require_templates = __commonJS({
1874
1874
  }
1875
1875
  return results;
1876
1876
  }
1877
- function buildStyle(chalk8, styles8) {
1877
+ function buildStyle(chalk10, styles8) {
1878
1878
  const enabled = {};
1879
1879
  for (const layer of styles8) {
1880
1880
  for (const style of layer.styles) {
1881
1881
  enabled[style[0]] = layer.inverse ? null : style.slice(1);
1882
1882
  }
1883
1883
  }
1884
- let current = chalk8;
1884
+ let current = chalk10;
1885
1885
  for (const [styleName, styles9] of Object.entries(enabled)) {
1886
1886
  if (!Array.isArray(styles9)) {
1887
1887
  continue;
@@ -1893,7 +1893,7 @@ var require_templates = __commonJS({
1893
1893
  }
1894
1894
  return current;
1895
1895
  }
1896
- module.exports = (chalk8, temporary) => {
1896
+ module.exports = (chalk10, temporary) => {
1897
1897
  const styles8 = [];
1898
1898
  const chunks = [];
1899
1899
  let chunk = [];
@@ -1903,13 +1903,13 @@ var require_templates = __commonJS({
1903
1903
  } else if (style) {
1904
1904
  const string = chunk.join("");
1905
1905
  chunk = [];
1906
- chunks.push(styles8.length === 0 ? string : buildStyle(chalk8, styles8)(string));
1906
+ chunks.push(styles8.length === 0 ? string : buildStyle(chalk10, styles8)(string));
1907
1907
  styles8.push({ inverse, styles: parseStyle(style) });
1908
1908
  } else if (close) {
1909
1909
  if (styles8.length === 0) {
1910
1910
  throw new Error("Found extraneous } in Chalk template literal");
1911
1911
  }
1912
- chunks.push(buildStyle(chalk8, styles8)(chunk.join("")));
1912
+ chunks.push(buildStyle(chalk10, styles8)(chunk.join("")));
1913
1913
  chunk = [];
1914
1914
  styles8.pop();
1915
1915
  } else {
@@ -1957,16 +1957,16 @@ var require_source = __commonJS({
1957
1957
  }
1958
1958
  };
1959
1959
  var chalkFactory2 = (options) => {
1960
- const chalk9 = {};
1961
- applyOptions2(chalk9, options);
1962
- chalk9.template = (...arguments_) => chalkTag(chalk9.template, ...arguments_);
1963
- Object.setPrototypeOf(chalk9, Chalk.prototype);
1964
- Object.setPrototypeOf(chalk9.template, chalk9);
1965
- chalk9.template.constructor = () => {
1960
+ const chalk11 = {};
1961
+ applyOptions2(chalk11, options);
1962
+ chalk11.template = (...arguments_) => chalkTag(chalk11.template, ...arguments_);
1963
+ Object.setPrototypeOf(chalk11, Chalk.prototype);
1964
+ Object.setPrototypeOf(chalk11.template, chalk11);
1965
+ chalk11.template.constructor = () => {
1966
1966
  throw new Error("`chalk.constructor()` is deprecated. Use `new chalk.Instance()` instead.");
1967
1967
  };
1968
- chalk9.template.Instance = ChalkClass;
1969
- return chalk9.template;
1968
+ chalk11.template.Instance = ChalkClass;
1969
+ return chalk11.template;
1970
1970
  };
1971
1971
  function Chalk(options) {
1972
1972
  return chalkFactory2(options);
@@ -2077,7 +2077,7 @@ var require_source = __commonJS({
2077
2077
  return openAll + string + closeAll;
2078
2078
  };
2079
2079
  var template;
2080
- var chalkTag = (chalk9, ...strings) => {
2080
+ var chalkTag = (chalk11, ...strings) => {
2081
2081
  const [firstString] = strings;
2082
2082
  if (!isArray(firstString) || !isArray(firstString.raw)) {
2083
2083
  return strings.join(" ");
@@ -2093,14 +2093,14 @@ var require_source = __commonJS({
2093
2093
  if (template === void 0) {
2094
2094
  template = require_templates();
2095
2095
  }
2096
- return template(chalk9, parts.join(""));
2096
+ return template(chalk11, parts.join(""));
2097
2097
  };
2098
2098
  Object.defineProperties(Chalk.prototype, styles8);
2099
- var chalk8 = Chalk();
2100
- chalk8.supportsColor = stdoutColor2;
2101
- chalk8.stderr = Chalk({ level: stderrColor2 ? stderrColor2.level : 0 });
2102
- chalk8.stderr.supportsColor = stderrColor2;
2103
- module.exports = chalk8;
2099
+ var chalk10 = Chalk();
2100
+ chalk10.supportsColor = stdoutColor2;
2101
+ chalk10.stderr = Chalk({ level: stderrColor2 ? stderrColor2.level : 0 });
2102
+ chalk10.stderr.supportsColor = stderrColor2;
2103
+ module.exports = chalk10;
2104
2104
  }
2105
2105
  });
2106
2106
 
@@ -3436,7 +3436,7 @@ var require_react_development = __commonJS({
3436
3436
  }
3437
3437
  return dispatcher.useContext(Context);
3438
3438
  }
3439
- function useState7(initialState) {
3439
+ function useState9(initialState) {
3440
3440
  var dispatcher = resolveDispatcher();
3441
3441
  return dispatcher.useState(initialState);
3442
3442
  }
@@ -3444,11 +3444,11 @@ var require_react_development = __commonJS({
3444
3444
  var dispatcher = resolveDispatcher();
3445
3445
  return dispatcher.useReducer(reducer4, initialArg, init);
3446
3446
  }
3447
- function useRef7(initialValue) {
3447
+ function useRef8(initialValue) {
3448
3448
  var dispatcher = resolveDispatcher();
3449
3449
  return dispatcher.useRef(initialValue);
3450
3450
  }
3451
- function useEffect13(create2, deps) {
3451
+ function useEffect14(create2, deps) {
3452
3452
  var dispatcher = resolveDispatcher();
3453
3453
  return dispatcher.useEffect(create2, deps);
3454
3454
  }
@@ -4231,15 +4231,15 @@ var require_react_development = __commonJS({
4231
4231
  exports.useContext = useContext7;
4232
4232
  exports.useDebugValue = useDebugValue;
4233
4233
  exports.useDeferredValue = useDeferredValue;
4234
- exports.useEffect = useEffect13;
4234
+ exports.useEffect = useEffect14;
4235
4235
  exports.useId = useId;
4236
4236
  exports.useImperativeHandle = useImperativeHandle;
4237
4237
  exports.useInsertionEffect = useInsertionEffect;
4238
4238
  exports.useLayoutEffect = useLayoutEffect2;
4239
4239
  exports.useMemo = useMemo4;
4240
4240
  exports.useReducer = useReducer5;
4241
- exports.useRef = useRef7;
4242
- exports.useState = useState7;
4241
+ exports.useRef = useRef8;
4242
+ exports.useState = useState9;
4243
4243
  exports.useSyncExternalStore = useSyncExternalStore;
4244
4244
  exports.useTransition = useTransition;
4245
4245
  exports.version = ReactVersion;
@@ -11949,9 +11949,9 @@ var require_react_reconciler_development = __commonJS({
11949
11949
  module.exports = function $$$reconciler($$$hostConfig) {
11950
11950
  var exports2 = {};
11951
11951
  "use strict";
11952
- var React16 = require_react();
11952
+ var React17 = require_react();
11953
11953
  var Scheduler = require_scheduler();
11954
- var ReactSharedInternals = React16.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED;
11954
+ var ReactSharedInternals = React17.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED;
11955
11955
  var suppressWarning = false;
11956
11956
  function setSuppressWarning(newSuppressWarning) {
11957
11957
  {
@@ -33012,10 +33012,10 @@ var init_source = __esm({
33012
33012
  object.level = options.level === void 0 ? colorLevel : options.level;
33013
33013
  };
33014
33014
  chalkFactory = (options) => {
33015
- const chalk8 = (...strings) => strings.join(" ");
33016
- applyOptions(chalk8, options);
33017
- Object.setPrototypeOf(chalk8, createChalk.prototype);
33018
- return chalk8;
33015
+ const chalk10 = (...strings) => strings.join(" ");
33016
+ applyOptions(chalk10, options);
33017
+ Object.setPrototypeOf(chalk10, createChalk.prototype);
33018
+ return chalk10;
33019
33019
  };
33020
33020
  Object.setPrototypeOf(createChalk.prototype, Function.prototype);
33021
33021
  for (const [styleName, style] of Object.entries(ansi_styles_default3)) {
@@ -37902,7 +37902,7 @@ var require_react_jsx_runtime_development = __commonJS({
37902
37902
  if (process.env.NODE_ENV !== "production") {
37903
37903
  (function() {
37904
37904
  "use strict";
37905
- var React16 = require_react();
37905
+ var React17 = require_react();
37906
37906
  var REACT_ELEMENT_TYPE = Symbol.for("react.element");
37907
37907
  var REACT_PORTAL_TYPE = Symbol.for("react.portal");
37908
37908
  var REACT_FRAGMENT_TYPE = Symbol.for("react.fragment");
@@ -37928,7 +37928,7 @@ var require_react_jsx_runtime_development = __commonJS({
37928
37928
  }
37929
37929
  return null;
37930
37930
  }
37931
- var ReactSharedInternals = React16.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED;
37931
+ var ReactSharedInternals = React17.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED;
37932
37932
  function error(format) {
37933
37933
  {
37934
37934
  {
@@ -38778,10 +38778,10 @@ var require_react_jsx_runtime_development = __commonJS({
38778
38778
  return jsxWithValidation(type, props, key, false);
38779
38779
  }
38780
38780
  }
38781
- var jsx12 = jsxWithValidationDynamic;
38781
+ var jsx11 = jsxWithValidationDynamic;
38782
38782
  var jsxs13 = jsxWithValidationStatic;
38783
38783
  exports.Fragment = REACT_FRAGMENT_TYPE;
38784
- exports.jsx = jsx12;
38784
+ exports.jsx = jsx11;
38785
38785
  exports.jsxs = jsxs13;
38786
38786
  })();
38787
38787
  }
@@ -38956,14 +38956,19 @@ async function callAnalyzeAPI(packages, config, onProgress) {
38956
38956
  }
38957
38957
  const results = [];
38958
38958
  let completed = 0;
38959
- for (const batch of batches) {
38959
+ const tTotal = Date.now();
38960
+ for (let i = 0; i < batches.length; i++) {
38961
+ const batch = batches[i];
38962
+ const tBatch = Date.now();
38960
38963
  const result = await callBatchWithRetry(batch, config);
38964
+ if (process.env.DG_PERF) console.error(`[CLI-PERF] batch ${i + 1}/${batches.length}: ${batch.length} packages \u2192 ${Date.now() - tBatch}ms`);
38961
38965
  completed += batch.length;
38962
38966
  if (onProgress) {
38963
38967
  onProgress(completed, packages.length, batch.map((p) => p.name));
38964
38968
  }
38965
38969
  results.push(result);
38966
38970
  }
38971
+ if (process.env.DG_PERF) console.error(`[CLI-PERF] total: ${packages.length} packages \u2192 ${Date.now() - tTotal}ms`);
38967
38972
  return mergeResponses(results, config);
38968
38973
  }
38969
38974
  async function callBatchWithRetry(packages, config) {
@@ -39156,7 +39161,7 @@ var init_api = __esm({
39156
39161
  this.name = "TrialExhaustedError";
39157
39162
  }
39158
39163
  };
39159
- BATCH_SIZE = 75;
39164
+ BATCH_SIZE = 200;
39160
39165
  ANON_BATCH_SIZE = 50;
39161
39166
  MAX_RETRIES = 2;
39162
39167
  RETRY_DELAY_MS = 5e3;
@@ -40588,38 +40593,142 @@ var init_useNpmWrapper = __esm({
40588
40593
  });
40589
40594
 
40590
40595
  // src/ui/components/ScoreHeader.tsx
40591
- function actionBadge2(action) {
40592
- const label = ` ${action.toUpperCase()} `;
40593
- switch (action) {
40594
- case "block":
40595
- return import_chalk5.default.bgRed.white.bold(label);
40596
- case "warn":
40597
- return import_chalk5.default.bgYellow.black.bold(label);
40598
- case "pass":
40599
- return import_chalk5.default.bgGreen.black.bold(label);
40600
- }
40601
- }
40602
40596
  function scoreColor(score, action) {
40603
40597
  const colorFn = action === "block" ? import_chalk5.default.red.bold : action === "warn" ? import_chalk5.default.yellow.bold : import_chalk5.default.green.bold;
40604
40598
  return colorFn(String(score));
40605
40599
  }
40606
- var import_chalk5, import_jsx_runtime3, DG_LOGO, ScoreHeader;
40600
+ function severityBar(counts) {
40601
+ const levels = [5, 4, 3, 2];
40602
+ const total = levels.reduce((sum, lv) => sum + (counts[lv] ?? 0), 0);
40603
+ if (total === 0) return "";
40604
+ const BAR_WIDTH = 30;
40605
+ const parts = [];
40606
+ for (const lv of levels) {
40607
+ const count = counts[lv] ?? 0;
40608
+ if (count === 0) continue;
40609
+ const width = Math.max(2, Math.round(count / total * BAR_WIDTH));
40610
+ const bar = "\u2501".repeat(width);
40611
+ const colorFn = SEV_COLORS[lv] ?? import_chalk5.default.dim;
40612
+ parts.push(`${colorFn(bar)} ${count} ${SEV_LABELS[lv] ?? ""}`);
40613
+ }
40614
+ return parts.join(" ");
40615
+ }
40616
+ function plot(grid, x, y, type) {
40617
+ if (y >= 0 && y < grid.length && x >= 0 && x < grid[0].length)
40618
+ grid[y][x] = Math.max(grid[y][x], type);
40619
+ }
40620
+ function bresenham(grid, x0, y0, x1, y1, type) {
40621
+ let dx = Math.abs(x1 - x0), dy = Math.abs(y1 - y0);
40622
+ let sx = x0 < x1 ? 1 : -1, sy = y0 < y1 ? 1 : -1;
40623
+ let err = dx - dy, x = x0, y = y0;
40624
+ while (true) {
40625
+ plot(grid, x, y, type);
40626
+ if (x === x1 && y === y1) break;
40627
+ const e2 = 2 * err;
40628
+ if (e2 > -dy) {
40629
+ err -= dy;
40630
+ x += sx;
40631
+ }
40632
+ if (e2 < dx) {
40633
+ err += dx;
40634
+ y += sy;
40635
+ }
40636
+ }
40637
+ }
40638
+ function fillCircle(grid, cx, cy, r, type) {
40639
+ for (let y = Math.floor(cy - r); y <= Math.ceil(cy + r); y++)
40640
+ for (let x = Math.floor(cx - r); x <= Math.ceil(cx + r); x++)
40641
+ if ((x - cx) ** 2 + (y - cy) ** 2 <= r * r) plot(grid, x, y, type);
40642
+ }
40643
+ function renderLogo(action) {
40644
+ const colors = {
40645
+ 1: import_chalk5.default.dim,
40646
+ 2: import_chalk5.default.white,
40647
+ 3: (s) => import_chalk5.default.bold.white(s),
40648
+ 4: action === "block" ? import_chalk5.default.red : action === "warn" ? import_chalk5.default.yellow : import_chalk5.default.green
40649
+ };
40650
+ const result = [];
40651
+ const { chars, types, cols } = LOGO_DATA;
40652
+ for (let i = 0; i < chars.length; i += cols) {
40653
+ let row = "";
40654
+ for (let j = 0; j < cols; j++) {
40655
+ const ch = chars[i + j];
40656
+ const t = types[i + j];
40657
+ row += t > 0 ? (colors[t] ?? import_chalk5.default.dim)(ch) : ch;
40658
+ }
40659
+ result.push(row);
40660
+ }
40661
+ return result;
40662
+ }
40663
+ var import_chalk5, import_jsx_runtime3, SEV_COLORS, SEV_LABELS, LOGO_DATA, ScoreHeader;
40607
40664
  var init_ScoreHeader = __esm({
40608
40665
  async "src/ui/components/ScoreHeader.tsx"() {
40609
40666
  "use strict";
40610
40667
  await init_build2();
40611
40668
  import_chalk5 = __toESM(require_source());
40612
40669
  import_jsx_runtime3 = __toESM(require_jsx_runtime());
40613
- DG_LOGO = import_chalk5.default.bold.cyan(" DG ");
40670
+ SEV_COLORS = {
40671
+ 5: (s) => import_chalk5.default.red(s),
40672
+ 4: (s) => import_chalk5.default.magenta(s),
40673
+ 3: (s) => import_chalk5.default.yellow(s),
40674
+ 2: (s) => import_chalk5.default.dim(s)
40675
+ };
40676
+ SEV_LABELS = {
40677
+ 5: "crit",
40678
+ 4: "high",
40679
+ 3: "med",
40680
+ 2: "low"
40681
+ };
40682
+ LOGO_DATA = (() => {
40683
+ const W = 22, H = 28;
40684
+ const grid = Array.from({ length: H }, () => Array(W).fill(0));
40685
+ const cx = 11, cy = 14, R = 10;
40686
+ const angles = [270, 315, 0, 45, 90, 135, 180, 225];
40687
+ const nodes = angles.map((a) => {
40688
+ const rad = a * Math.PI / 180;
40689
+ return [Math.round(cx + R * Math.cos(rad)), Math.round(cy + R * Math.sin(rad))];
40690
+ });
40691
+ for (const [nx, ny] of nodes) bresenham(grid, cx, cy, nx, ny, 1);
40692
+ nodes.forEach(([nx, ny], i) => fillCircle(grid, nx, ny, 1.5, i === 1 ? 4 : 2));
40693
+ fillCircle(grid, cx, cy, 3, 3);
40694
+ const chars = [];
40695
+ const types = [];
40696
+ for (let row = 0; row < H; row += 4) {
40697
+ for (let col = 0; col < W; col += 2) {
40698
+ let bits = 0, maxType = 0;
40699
+ const offsets = [
40700
+ [0, 0, 1],
40701
+ [1, 0, 2],
40702
+ [2, 0, 4],
40703
+ [3, 0, 64],
40704
+ [0, 1, 8],
40705
+ [1, 1, 16],
40706
+ [2, 1, 32],
40707
+ [3, 1, 128]
40708
+ ];
40709
+ for (const [dy, dx, bit] of offsets) {
40710
+ const py = row + dy, px = col + dx;
40711
+ if (py < H && px < W && grid[py][px] > 0) {
40712
+ bits |= bit;
40713
+ maxType = Math.max(maxType, grid[py][px]);
40714
+ }
40715
+ }
40716
+ chars.push(String.fromCharCode(10240 + bits));
40717
+ types.push(maxType);
40718
+ }
40719
+ }
40720
+ return { chars, types, cols: W / 2 };
40721
+ })();
40614
40722
  ScoreHeader = ({
40615
40723
  score,
40616
40724
  action,
40617
40725
  total,
40618
40726
  flagged,
40619
- clean
40727
+ clean,
40728
+ severityCounts
40620
40729
  }) => {
40621
- const badge = actionBadge2(action);
40622
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
40730
+ const logo = renderLogo(action);
40731
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
40623
40732
  Box_default,
40624
40733
  {
40625
40734
  flexDirection: "column",
@@ -40628,37 +40737,38 @@ var init_ScoreHeader = __esm({
40628
40737
  paddingLeft: 1,
40629
40738
  paddingRight: 1,
40630
40739
  width: "100%",
40631
- children: [
40632
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(Box_default, { justifyContent: "space-between", children: [
40633
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Text, { bold: true, children: "Dependency Guardian" }),
40634
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Text, { children: import_chalk5.default.bgCyan.black.bold(DG_LOGO) })
40635
- ] }),
40636
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Box_default, { justifyContent: "space-between", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(Text, { children: [
40637
- "Score: ",
40638
- scoreColor(score, action),
40639
- " ",
40640
- badge
40641
- ] }) }),
40642
- total !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(Text, { children: [
40643
- total,
40644
- " package",
40645
- total !== 1 ? "s" : "",
40646
- " scanned",
40647
- flagged !== void 0 && flagged > 0 ? /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
40648
- " ",
40649
- import_chalk5.default.dim("\u2502"),
40650
- " ",
40651
- import_chalk5.default.yellow(`${flagged} flagged`),
40652
- " \u2502 ",
40653
- import_chalk5.default.green(`${clean ?? 0} clean`)
40654
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
40655
- " ",
40656
- import_chalk5.default.dim("\u2502"),
40657
- " ",
40658
- import_chalk5.default.green("all clean")
40740
+ children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(Box_default, { flexDirection: "row", children: [
40741
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(Box_default, { flexDirection: "column", flexGrow: 1, children: [
40742
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(Text, { bold: true, children: [
40743
+ import_chalk5.default.cyan("\u25C6"),
40744
+ " Dependency Guardian"
40745
+ ] }),
40746
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Text, { children: " " }),
40747
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(Text, { children: [
40748
+ import_chalk5.default.dim("Score"),
40749
+ " ",
40750
+ scoreColor(score, action)
40751
+ ] }),
40752
+ total !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
40753
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Text, { children: " " }),
40754
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(Text, { children: [
40755
+ import_chalk5.default.dim(`${total} package${total !== 1 ? "s" : ""} scanned`),
40756
+ flagged !== void 0 && flagged > 0 ? /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
40757
+ " ",
40758
+ import_chalk5.default.yellow(`${flagged} flagged`),
40759
+ " ",
40760
+ import_chalk5.default.green(`${clean ?? 0} clean`)
40761
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
40762
+ " ",
40763
+ import_chalk5.default.green("all clean")
40764
+ ] })
40765
+ ] }),
40766
+ severityCounts && Object.values(severityCounts).some((v) => v > 0) && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Text, { children: severityBar(severityCounts) }),
40767
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Text, { dimColor: true, children: `\x1B]8;;https://westbayberry.com\x07westbayberry.com\x1B]8;;\x07` })
40659
40768
  ] })
40660
- ] })
40661
- ]
40769
+ ] }),
40770
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Box_default, { flexDirection: "column", marginLeft: 2, children: logo.map((line, i) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Text, { children: line }, i)) })
40771
+ ] })
40662
40772
  }
40663
40773
  );
40664
40774
  };
@@ -40693,15 +40803,15 @@ function groupPackages2(packages) {
40693
40803
  }
40694
40804
  function severityColor2(sev) {
40695
40805
  if (sev >= 5) return (s) => import_chalk6.default.bold.red(s);
40696
- if (sev >= 4) return import_chalk6.default.red;
40806
+ if (sev >= 4) return import_chalk6.default.magenta;
40697
40807
  if (sev >= 3) return import_chalk6.default.yellow;
40698
- if (sev >= 2) return import_chalk6.default.cyan;
40808
+ if (sev >= 2) return import_chalk6.default.dim;
40699
40809
  return import_chalk6.default.dim;
40700
40810
  }
40701
- function actionBadge3(score, config) {
40811
+ function actionBadge2(score, config) {
40702
40812
  if (score >= config.blockThreshold) return { label: "BLOCK", color: import_chalk6.default.red };
40703
40813
  if (score >= config.warnThreshold) return { label: "WARN", color: import_chalk6.default.yellow };
40704
- return { label: "pass", color: import_chalk6.default.green };
40814
+ return { label: "PASS", color: import_chalk6.default.green };
40705
40815
  }
40706
40816
  function truncate2(s, max) {
40707
40817
  return s.length <= max ? s : s.slice(0, max - 1) + "\u2026";
@@ -40740,18 +40850,16 @@ var init_ResultsView = __esm({
40740
40850
  /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(ScoreHeader, { score: result.score, action: result.action }),
40741
40851
  /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Newline, {}),
40742
40852
  /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(Text, { children: [
40743
- total,
40744
- " package",
40745
- total !== 1 ? "s" : "",
40746
- " scanned",
40747
- " ",
40748
- import_chalk6.default.dim("\u2502"),
40749
- " ",
40853
+ import_chalk6.default.dim(`${total} package${total !== 1 ? "s" : ""} scanned`),
40750
40854
  flagged.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_jsx_runtime5.Fragment, { children: [
40855
+ " ",
40751
40856
  import_chalk6.default.yellow(`${flagged.length} flagged`),
40752
- " \u2502 ",
40857
+ " ",
40753
40858
  import_chalk6.default.green(`${clean.length} clean`)
40754
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_jsx_runtime5.Fragment, { children: import_chalk6.default.green("all clean") })
40859
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_jsx_runtime5.Fragment, { children: [
40860
+ " ",
40861
+ import_chalk6.default.green("all clean")
40862
+ ] })
40755
40863
  ] }),
40756
40864
  /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Newline, {}),
40757
40865
  flagged.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(Box_default, { flexDirection: "column", children: [
@@ -40759,7 +40867,7 @@ var init_ResultsView = __esm({
40759
40867
  /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Text, { dimColor: true, children: "\u2500".repeat(60) }),
40760
40868
  groups.map((group) => {
40761
40869
  const rep = group.packages[0];
40762
- const { label, color } = actionBadge3(rep.score, config);
40870
+ const { label, color } = actionBadge2(rep.score, config);
40763
40871
  const names = group.packages.length === 1 ? rep.name : group.packages.length <= 3 ? group.packages.map((p) => p.name).join(", ") : `${group.packages[0].name} + ${group.packages.length - 1} similar`;
40764
40872
  return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(Text, { children: [
40765
40873
  " ",
@@ -40770,13 +40878,12 @@ var init_ResultsView = __esm({
40770
40878
  }),
40771
40879
  /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Newline, {})
40772
40880
  ] }),
40773
- clean.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(Text, { dimColor: true, children: [
40881
+ clean.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(Text, { children: [
40774
40882
  import_chalk6.default.green("\u2713"),
40775
40883
  " ",
40776
- clean.length,
40777
- " package",
40778
- clean.length !== 1 ? "s" : "",
40779
- " passed with score 0"
40884
+ import_chalk6.default.green.bold(String(clean.length)),
40885
+ " ",
40886
+ import_chalk6.default.dim(`package${clean.length !== 1 ? "s" : ""} passed with score 0`)
40780
40887
  ] }),
40781
40888
  /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Newline, {}),
40782
40889
  groups.filter((g) => g.packages[0].score > 0).map((group) => {
@@ -40857,13 +40964,17 @@ var init_ConfirmPrompt = __esm({
40857
40964
  }
40858
40965
  });
40859
40966
  return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(Box_default, { flexDirection: "column", children: [
40860
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Text, { children: import_chalk7.default.bold(import_chalk7.default.red(message)) }),
40861
40967
  /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(Text, { children: [
40862
- "Install anyway? (",
40863
- import_chalk7.default.bold("y"),
40864
- "/",
40865
- import_chalk7.default.bold("N"),
40866
- ")"
40968
+ import_chalk7.default.red.bold("\u26A0"),
40969
+ " ",
40970
+ import_chalk7.default.bold(message)
40971
+ ] }),
40972
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(Text, { children: [
40973
+ "Install anyway? [",
40974
+ import_chalk7.default.bold.green("y"),
40975
+ "es / ",
40976
+ import_chalk7.default.bold.red("N"),
40977
+ "o]"
40867
40978
  ] })
40868
40979
  ] });
40869
40980
  };
@@ -40894,19 +41005,25 @@ var init_ErrorView = __esm({
40894
41005
  import_jsx_runtime7 = __toESM(require_jsx_runtime());
40895
41006
  ErrorView = ({ error }) => {
40896
41007
  const hint = getHint(error);
40897
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(Box_default, { flexDirection: "column", children: [
40898
- /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(Text, { children: [
40899
- import_chalk8.default.red("Error:"),
40900
- " ",
40901
- error.message
40902
- ] }),
40903
- hint && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(Text, { children: [
40904
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Newline, {}),
40905
- import_chalk8.default.yellow("Hint:"),
40906
- " ",
40907
- hint
40908
- ] })
40909
- ] });
41008
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
41009
+ Box_default,
41010
+ {
41011
+ flexDirection: "column",
41012
+ borderStyle: "round",
41013
+ borderColor: "red",
41014
+ paddingLeft: 1,
41015
+ paddingRight: 1,
41016
+ children: [
41017
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Text, { children: import_chalk8.default.red.bold("\u2718 Error") }),
41018
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Text, { children: error.message }),
41019
+ hint && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(Text, { children: [
41020
+ import_chalk8.default.yellow("\u2192"),
41021
+ " ",
41022
+ hint
41023
+ ] })
41024
+ ]
41025
+ }
41026
+ );
40910
41027
  };
40911
41028
  }
40912
41029
  });
@@ -41250,21 +41367,42 @@ var init_useScan = __esm({
41250
41367
  });
41251
41368
 
41252
41369
  // src/ui/components/ProgressBar.tsx
41253
- var import_jsx_runtime9, ProgressBar;
41370
+ function estimateScanSeconds(packageCount) {
41371
+ return Math.ceil(8 + packageCount * 0.12);
41372
+ }
41373
+ function formatTime(seconds) {
41374
+ if (seconds < 60) return `${seconds}s`;
41375
+ const m = Math.floor(seconds / 60);
41376
+ const s = seconds % 60;
41377
+ return s > 0 ? `${m}m ${s}s` : `${m}m`;
41378
+ }
41379
+ var import_react28, import_chalk9, import_jsx_runtime9, ProgressBar;
41254
41380
  var init_ProgressBar = __esm({
41255
41381
  async "src/ui/components/ProgressBar.tsx"() {
41256
41382
  "use strict";
41383
+ import_react28 = __toESM(require_react());
41257
41384
  await init_build2();
41258
41385
  await init_build3();
41386
+ import_chalk9 = __toESM(require_source());
41259
41387
  import_jsx_runtime9 = __toESM(require_jsx_runtime());
41260
41388
  ProgressBar = ({
41261
41389
  value,
41262
41390
  total,
41263
41391
  label
41264
41392
  }) => {
41393
+ const startRef = (0, import_react28.useRef)(Date.now());
41394
+ const [elapsed, setElapsed] = (0, import_react28.useState)(0);
41395
+ (0, import_react28.useEffect)(() => {
41396
+ const timer = setInterval(() => {
41397
+ setElapsed(Math.floor((Date.now() - startRef.current) / 1e3));
41398
+ }, 1e3);
41399
+ return () => clearInterval(timer);
41400
+ }, []);
41265
41401
  const termWidth = process.stdout.columns || 80;
41266
41402
  const percent = total > 0 ? Math.round(value / total * 100) : 0;
41267
- const counter = `${value}/${total} (${percent}%)`;
41403
+ const estimate = estimateScanSeconds(total);
41404
+ const timeInfo = `${formatTime(elapsed)} / ~${formatTime(estimate)}`;
41405
+ const counter = `${value}/${total} ${percent}%`;
41268
41406
  const barWidth = Math.max(10, termWidth - counter.length - 8);
41269
41407
  const fraction = total > 0 ? Math.min(1, value / total) : 0;
41270
41408
  const filled = Math.round(fraction * barWidth);
@@ -41272,9 +41410,20 @@ var init_ProgressBar = __esm({
41272
41410
  const filledBar = "\u2501".repeat(filled);
41273
41411
  const emptyBar = "\u2501".repeat(empty);
41274
41412
  return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(Box_default, { flexDirection: "column", paddingLeft: 2, children: [
41413
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(Text, { children: [
41414
+ import_chalk9.default.cyan("\u25C6"),
41415
+ " ",
41416
+ import_chalk9.default.bold("Dependency Guardian")
41417
+ ] }),
41418
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Text, { children: "" }),
41275
41419
  /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(Box_default, { children: [
41276
41420
  /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Text, { color: "cyan", children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(build_default, { type: "dots" }) }),
41277
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Text, { children: " Scanning packages..." })
41421
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(Text, { children: [
41422
+ " Scanning ",
41423
+ total,
41424
+ " packages... "
41425
+ ] }),
41426
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Text, { dimColor: true, children: timeInfo })
41278
41427
  ] }),
41279
41428
  /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(Box_default, { children: [
41280
41429
  /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Text, { children: " " }),
@@ -41287,6 +41436,8 @@ var init_ProgressBar = __esm({
41287
41436
  ] }),
41288
41437
  label && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Box_default, { children: /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(Text, { dimColor: true, children: [
41289
41438
  " ",
41439
+ import_chalk9.default.dim("\u203A"),
41440
+ " ",
41290
41441
  label
41291
41442
  ] }) })
41292
41443
  ] });
@@ -41296,9 +41447,9 @@ var init_ProgressBar = __esm({
41296
41447
 
41297
41448
  // src/ui/hooks/useExpandAnimation.ts
41298
41449
  function useExpandAnimation(targetHeight, active, durationMs = 180) {
41299
- const [visibleLines, setVisibleLines] = (0, import_react28.useState)(0);
41300
- const timerRef = (0, import_react28.useRef)(null);
41301
- (0, import_react28.useEffect)(() => {
41450
+ const [visibleLines, setVisibleLines] = (0, import_react29.useState)(0);
41451
+ const timerRef = (0, import_react29.useRef)(null);
41452
+ (0, import_react29.useEffect)(() => {
41302
41453
  if (timerRef.current) {
41303
41454
  clearInterval(timerRef.current);
41304
41455
  timerRef.current = null;
@@ -41332,22 +41483,22 @@ function useExpandAnimation(targetHeight, active, durationMs = 180) {
41332
41483
  isAnimating: active && visibleLines > 0 && visibleLines < targetHeight
41333
41484
  };
41334
41485
  }
41335
- var import_react28;
41486
+ var import_react29;
41336
41487
  var init_useExpandAnimation = __esm({
41337
41488
  "src/ui/hooks/useExpandAnimation.ts"() {
41338
41489
  "use strict";
41339
- import_react28 = __toESM(require_react());
41490
+ import_react29 = __toESM(require_react());
41340
41491
  }
41341
41492
  });
41342
41493
 
41343
41494
  // src/ui/hooks/useTerminalSize.ts
41344
41495
  function useTerminalSize() {
41345
41496
  const { stdout } = use_stdout_default();
41346
- const [size, setSize] = (0, import_react29.useState)({
41497
+ const [size, setSize] = (0, import_react30.useState)({
41347
41498
  rows: stdout?.rows ?? process.stdout.rows ?? 24,
41348
41499
  cols: stdout?.columns ?? process.stdout.columns ?? 80
41349
41500
  });
41350
- (0, import_react29.useEffect)(() => {
41501
+ (0, import_react30.useEffect)(() => {
41351
41502
  const handle = () => {
41352
41503
  const rows = process.stdout.rows ?? 24;
41353
41504
  const cols = process.stdout.columns ?? 80;
@@ -41365,11 +41516,11 @@ function useTerminalSize() {
41365
41516
  }, []);
41366
41517
  return size;
41367
41518
  }
41368
- var import_react29;
41519
+ var import_react30;
41369
41520
  var init_useTerminalSize = __esm({
41370
41521
  async "src/ui/hooks/useTerminalSize.ts"() {
41371
41522
  "use strict";
41372
- import_react29 = __toESM(require_react());
41523
+ import_react30 = __toESM(require_react());
41373
41524
  await init_build2();
41374
41525
  }
41375
41526
  });
@@ -41385,12 +41536,12 @@ function groupPackages3(packages) {
41385
41536
  }
41386
41537
  return [...map.entries()].map(([fingerprint, pkgs]) => ({ packages: pkgs, key: fingerprint })).sort((a, b) => b.packages[0].score - a.packages[0].score);
41387
41538
  }
41388
- function actionBadge4(score, config) {
41539
+ function actionBadge3(score, config) {
41389
41540
  if (score >= config.blockThreshold)
41390
- return { label: "BLOCK", color: import_chalk9.default.red };
41541
+ return { label: "BLOCK", color: import_chalk10.default.red };
41391
41542
  if (score >= config.warnThreshold)
41392
- return { label: "WARN", color: import_chalk9.default.yellow };
41393
- return { label: "pass", color: import_chalk9.default.green };
41543
+ return { label: "WARN", color: import_chalk10.default.yellow };
41544
+ return { label: "PASS", color: import_chalk10.default.green };
41394
41545
  }
41395
41546
  function truncate3(s, max) {
41396
41547
  return s.length <= max ? s : s.slice(0, max - 1) + "\u2026";
@@ -41447,6 +41598,108 @@ function affectsLine(group) {
41447
41598
  if (names.length <= 5) return names.join(", ");
41448
41599
  return names.slice(0, 5).join(", ") + ` + ${names.length - 5} more`;
41449
41600
  }
41601
+ function buildDetailLines(group, safeVersion, maxWidth) {
41602
+ const rep = group.packages[0];
41603
+ const visibleFindings = rep.findings.filter((f) => f.severity > 1 || f.critical).sort((a, b) => b.severity - a.severity);
41604
+ const lines = [];
41605
+ const evidenceWidth = Math.max(30, maxWidth - 12);
41606
+ if (group.packages.length > 3) {
41607
+ lines.push(
41608
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Text, { dimColor: true, children: [
41609
+ "Affects: ",
41610
+ affectsLine(group)
41611
+ ] }, "affects")
41612
+ );
41613
+ lines.push(/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Text, { children: "" }, "affects-gap"));
41614
+ }
41615
+ if (visibleFindings.length === 0 && rep.score > 0) {
41616
+ const reasons = rep.reasons ?? [];
41617
+ for (let i = 0; i < reasons.length; i++) {
41618
+ lines.push(
41619
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Text, { dimColor: true, children: truncate3(reasons[i], maxWidth - 4) }, `reason-${i}`)
41620
+ );
41621
+ }
41622
+ lines.push(
41623
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Text, { children: [
41624
+ import_chalk10.default.yellow("\u2192"),
41625
+ " ",
41626
+ import_chalk10.default.yellow("Upgrade to Pro"),
41627
+ " ",
41628
+ import_chalk10.default.dim("to see finding details \u2014 dg login")
41629
+ ] }, "upgrade")
41630
+ );
41631
+ return lines;
41632
+ }
41633
+ const hasEvidence = visibleFindings.some((f) => f.evidence && f.evidence.length > 0);
41634
+ for (let idx = 0; idx < visibleFindings.length; idx++) {
41635
+ const finding = visibleFindings[idx];
41636
+ const sevLabel = SEVERITY_LABELS3[finding.severity] ?? "INFO";
41637
+ const sevColor = SEVERITY_COLORS[finding.severity] ?? SEVERITY_COLORS[1];
41638
+ const evidence = finding.evidence ?? [];
41639
+ const findingId = finding.id ?? finding.category ?? "";
41640
+ if (idx > 0) lines.push(/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Text, { children: "" }, `gap-${idx}`));
41641
+ lines.push(
41642
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Text, { children: [
41643
+ sevColor(pad3(sevLabel, 5)),
41644
+ " ",
41645
+ findingId
41646
+ ] }, `${findingId}-${idx}-badge`)
41647
+ );
41648
+ lines.push(
41649
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Text, { dimColor: true, children: [
41650
+ " ",
41651
+ finding.title
41652
+ ] }, `${findingId}-${idx}-title`)
41653
+ );
41654
+ const evidenceCap = Math.min(evidence.length, DETAIL_EVIDENCE_LIMIT);
41655
+ for (let i = 0; i < evidenceCap; i++) {
41656
+ lines.push(
41657
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Text, { dimColor: true, children: [
41658
+ " ",
41659
+ import_chalk10.default.dim("\u203A"),
41660
+ " ",
41661
+ truncate3(evidence[i], evidenceWidth)
41662
+ ] }, `${findingId}-${idx}-ev-${i}`)
41663
+ );
41664
+ }
41665
+ if (evidence.length > DETAIL_EVIDENCE_LIMIT) {
41666
+ lines.push(
41667
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Text, { dimColor: true, children: [
41668
+ " ",
41669
+ import_chalk10.default.dim(`+${evidence.length - DETAIL_EVIDENCE_LIMIT} more`)
41670
+ ] }, `${findingId}-${idx}-overflow`)
41671
+ );
41672
+ }
41673
+ }
41674
+ if (visibleFindings.length > 0 && !hasEvidence) {
41675
+ lines.push(/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Text, { children: "" }, "upgrade-gap"));
41676
+ lines.push(
41677
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Text, { children: [
41678
+ import_chalk10.default.yellow("\u2192"),
41679
+ " ",
41680
+ import_chalk10.default.yellow("Upgrade to Team"),
41681
+ " ",
41682
+ import_chalk10.default.dim("for evidence and file locations")
41683
+ ] }, "upgrade-evidence")
41684
+ );
41685
+ }
41686
+ if (rep.recommendation) {
41687
+ lines.push(/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Text, { children: "" }, "rec-gap"));
41688
+ lines.push(
41689
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Text, { children: [
41690
+ import_chalk10.default.dim("Rec:"),
41691
+ " ",
41692
+ import_chalk10.default.cyan(truncate3(rep.recommendation, maxWidth - 8))
41693
+ ] }, "recommendation")
41694
+ );
41695
+ }
41696
+ if (safeVersion) {
41697
+ lines.push(
41698
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Text, { children: import_chalk10.default.green(`Safe: ${rep.name}@${safeVersion}`) }, "safe")
41699
+ );
41700
+ }
41701
+ return lines;
41702
+ }
41450
41703
  function viewReducer(_state, action) {
41451
41704
  switch (action.type) {
41452
41705
  case "MOVE":
@@ -41457,13 +41710,13 @@ function viewReducer(_state, action) {
41457
41710
  return { cursor: action.cursor, expandedIndex: action.expandedIndex, expandLevel: action.expandLevel, viewport: action.viewport };
41458
41711
  }
41459
41712
  }
41460
- var import_react30, import_chalk9, import_jsx_runtime10, SEVERITY_LABELS3, SEVERITY_COLORS, EVIDENCE_LIMIT2, FIXED_CHROME, InteractiveResultsView, T, FindingsSummary, FindingsDetail;
41713
+ var import_react31, import_chalk10, import_jsx_runtime10, SEVERITY_LABELS3, SEVERITY_COLORS, EVIDENCE_LIMIT2, FIXED_CHROME, DETAIL_PANE_CHROME, DETAIL_EVIDENCE_LIMIT, InteractiveResultsView, T, FindingsSummary;
41461
41714
  var init_InteractiveResultsView = __esm({
41462
41715
  async "src/ui/components/InteractiveResultsView.tsx"() {
41463
41716
  "use strict";
41464
- import_react30 = __toESM(require_react());
41717
+ import_react31 = __toESM(require_react());
41465
41718
  await init_build2();
41466
- import_chalk9 = __toESM(require_source());
41719
+ import_chalk10 = __toESM(require_source());
41467
41720
  await init_ScoreHeader();
41468
41721
  init_useExpandAnimation();
41469
41722
  await init_useTerminalSize();
@@ -41476,14 +41729,16 @@ var init_InteractiveResultsView = __esm({
41476
41729
  1: "INFO"
41477
41730
  };
41478
41731
  SEVERITY_COLORS = {
41479
- 5: (s) => import_chalk9.default.red.bold(s),
41480
- 4: (s) => import_chalk9.default.redBright(s),
41481
- 3: (s) => import_chalk9.default.yellow(s),
41482
- 2: (s) => import_chalk9.default.cyan(s),
41483
- 1: (s) => import_chalk9.default.gray(s)
41732
+ 5: (s) => import_chalk10.default.red.bold(s),
41733
+ 4: (s) => import_chalk10.default.magenta(s),
41734
+ 3: (s) => import_chalk10.default.yellow(s),
41735
+ 2: (s) => import_chalk10.default.dim(s),
41736
+ 1: (s) => import_chalk10.default.gray(s)
41484
41737
  };
41485
41738
  EVIDENCE_LIMIT2 = 2;
41486
- FIXED_CHROME = 16;
41739
+ FIXED_CHROME = 21;
41740
+ DETAIL_PANE_CHROME = 20;
41741
+ DETAIL_EVIDENCE_LIMIT = 10;
41487
41742
  InteractiveResultsView = ({
41488
41743
  result,
41489
41744
  config,
@@ -41492,31 +41747,63 @@ var init_InteractiveResultsView = __esm({
41492
41747
  onBack,
41493
41748
  discoveredTotal
41494
41749
  }) => {
41495
- const flagged = (0, import_react30.useMemo)(
41750
+ const flagged = (0, import_react31.useMemo)(
41496
41751
  () => result.packages.filter((p) => p.score > 0),
41497
41752
  [result.packages]
41498
41753
  );
41499
- const clean = (0, import_react30.useMemo)(
41754
+ const clean = (0, import_react31.useMemo)(
41500
41755
  () => result.packages.filter((p) => p.score === 0),
41501
41756
  [result.packages]
41502
41757
  );
41503
41758
  const total = result.packages.length;
41504
- const groups = (0, import_react30.useMemo)(() => groupPackages3(flagged), [flagged]);
41505
- const [view, dispatchView] = (0, import_react30.useReducer)(viewReducer, {
41759
+ const [searchQuery, setSearchQuery] = (0, import_react31.useState)("");
41760
+ const allGroups = (0, import_react31.useMemo)(() => groupPackages3(flagged), [flagged]);
41761
+ const allGroupCount = allGroups.length;
41762
+ const groups = (0, import_react31.useMemo)(() => {
41763
+ if (!searchQuery) return allGroups;
41764
+ const q = searchQuery.toLowerCase();
41765
+ return allGroups.filter((g) => g.packages.some((p) => p.name.toLowerCase().includes(q)));
41766
+ }, [allGroups, searchQuery]);
41767
+ const severityCounts = (0, import_react31.useMemo)(() => {
41768
+ const counts = {};
41769
+ for (const pkg of flagged) {
41770
+ const maxSev = pkg.findings.reduce((m, f) => Math.max(m, f.severity), 0);
41771
+ if (maxSev >= 2) counts[maxSev] = (counts[maxSev] ?? 0) + 1;
41772
+ }
41773
+ return counts;
41774
+ }, [flagged]);
41775
+ const [view, dispatchView] = (0, import_react31.useReducer)(viewReducer, {
41506
41776
  cursor: 0,
41507
41777
  expandLevel: null,
41508
41778
  expandedIndex: null,
41509
41779
  viewport: 0
41510
41780
  });
41511
- const viewRef = (0, import_react30.useRef)(view);
41781
+ const viewRef = (0, import_react31.useRef)(view);
41512
41782
  viewRef.current = view;
41783
+ const [detailPane, setDetailPane] = (0, import_react31.useState)(null);
41784
+ const detailPaneRef = (0, import_react31.useRef)(detailPane);
41785
+ detailPaneRef.current = detailPane;
41786
+ const [showHelp, setShowHelp] = (0, import_react31.useState)(false);
41787
+ const showHelpRef = (0, import_react31.useRef)(showHelp);
41788
+ showHelpRef.current = showHelp;
41789
+ const [searchMode, setSearchMode] = (0, import_react31.useState)(false);
41790
+ const searchModeRef = (0, import_react31.useRef)(searchMode);
41791
+ searchModeRef.current = searchMode;
41513
41792
  const { rows: termRows, cols: termCols } = useTerminalSize();
41514
41793
  const availableRows = Math.max(5, termRows - FIXED_CHROME);
41515
41794
  const innerWidth = Math.max(40, termCols - 6);
41795
+ const detailGroupIdx = detailPane?.groupIndex ?? -1;
41796
+ const detailLines = (0, import_react31.useMemo)(() => {
41797
+ if (detailGroupIdx < 0) return [];
41798
+ const group = groups[detailGroupIdx];
41799
+ if (!group) return [];
41800
+ return buildDetailLines(group, result.safeVersions[group.packages[0].name], innerWidth);
41801
+ }, [detailGroupIdx, groups, result.safeVersions, innerWidth]);
41802
+ const detailContentRows = Math.max(3, termRows - DETAIL_PANE_CHROME);
41516
41803
  const getLevel = (idx) => {
41517
41804
  return view.expandedIndex === idx ? view.expandLevel : null;
41518
41805
  };
41519
- const expandTargetHeight = (0, import_react30.useMemo)(() => {
41806
+ const expandTargetHeight = (0, import_react31.useMemo)(() => {
41520
41807
  if (view.expandedIndex === null || view.expandLevel === null) return 0;
41521
41808
  const group = groups[view.expandedIndex];
41522
41809
  if (!group) return 0;
@@ -41532,7 +41819,7 @@ var init_InteractiveResultsView = __esm({
41532
41819
  if (idx === view.expandedIndex) return 1 + animVisibleLines;
41533
41820
  return groupRowHeight(group, level, result.safeVersions);
41534
41821
  };
41535
- const visibleEnd = (0, import_react30.useMemo)(() => {
41822
+ const visibleEnd = (0, import_react31.useMemo)(() => {
41536
41823
  let consumed = 0;
41537
41824
  let end = view.viewport;
41538
41825
  while (end < groups.length) {
@@ -41564,7 +41851,7 @@ var init_InteractiveResultsView = __esm({
41564
41851
  }
41565
41852
  return newStart;
41566
41853
  };
41567
- (0, import_react30.useEffect)(() => {
41854
+ (0, import_react31.useEffect)(() => {
41568
41855
  if (groups.length === 0) return;
41569
41856
  const { cursor, expandedIndex, expandLevel, viewport } = viewRef.current;
41570
41857
  const clamped = Math.min(viewport, Math.max(0, groups.length - 1));
@@ -41576,15 +41863,73 @@ var init_InteractiveResultsView = __esm({
41576
41863
  if (input === "q" || key.return) onExit();
41577
41864
  return;
41578
41865
  }
41866
+ if (showHelpRef.current) {
41867
+ if (input === "?" || key.escape) setShowHelp(false);
41868
+ else if (input === "q") onExit();
41869
+ return;
41870
+ }
41871
+ if (input === "?") {
41872
+ setShowHelp(true);
41873
+ return;
41874
+ }
41875
+ if (searchModeRef.current) {
41876
+ if (key.escape) {
41877
+ setSearchMode(false);
41878
+ setSearchQuery("");
41879
+ dispatchView({ type: "MOVE", cursor: 0, viewport: 0 });
41880
+ } else if (key.return) {
41881
+ setSearchMode(false);
41882
+ } else if (key.backspace || key.delete) {
41883
+ setSearchQuery((prev) => prev.slice(0, -1));
41884
+ dispatchView({ type: "MOVE", cursor: 0, viewport: 0 });
41885
+ } else if (input && !key.upArrow && !key.downArrow && /^[\x20-\x7e]+$/.test(input)) {
41886
+ setSearchQuery((prev) => prev + input);
41887
+ dispatchView({ type: "MOVE", cursor: 0, viewport: 0 });
41888
+ }
41889
+ return;
41890
+ }
41891
+ const dp = detailPaneRef.current;
41892
+ if (dp !== null) {
41893
+ const maxScroll = Math.max(0, detailLines.length - detailContentRows);
41894
+ if (key.upArrow || input === "k") {
41895
+ setDetailPane({ groupIndex: dp.groupIndex, scroll: Math.max(0, dp.scroll - 1) });
41896
+ } else if (key.downArrow || input === "j") {
41897
+ setDetailPane({ groupIndex: dp.groupIndex, scroll: Math.min(maxScroll, dp.scroll + 1) });
41898
+ } else if (input === "g") {
41899
+ setDetailPane({ groupIndex: dp.groupIndex, scroll: 0 });
41900
+ } else if (input === "G") {
41901
+ setDetailPane({ groupIndex: dp.groupIndex, scroll: maxScroll });
41902
+ } else if (input === "e" || input === "b" || key.escape) {
41903
+ setDetailPane(null);
41904
+ } else if (input === "q") {
41905
+ onExit();
41906
+ }
41907
+ return;
41908
+ }
41579
41909
  const { cursor, expandLevel: expLvl, expandedIndex: expIdx, viewport: vpStart } = viewRef.current;
41580
- if (key.upArrow) {
41910
+ if (key.upArrow || input === "k") {
41581
41911
  const next = Math.max(0, cursor - 1);
41582
41912
  const newVp = adjustViewport(next, expIdx, expLvl, vpStart < next ? vpStart : next);
41583
41913
  dispatchView({ type: "MOVE", cursor: next, viewport: newVp });
41584
- } else if (key.downArrow) {
41914
+ } else if (key.downArrow || input === "j") {
41585
41915
  const next = Math.min(groups.length - 1, cursor + 1);
41586
41916
  const newVp = adjustViewport(next, expIdx, expLvl, vpStart);
41587
41917
  dispatchView({ type: "MOVE", cursor: next, viewport: newVp });
41918
+ } else if (input === "g") {
41919
+ const newVp = adjustViewport(0, expIdx, expLvl, 0);
41920
+ dispatchView({ type: "MOVE", cursor: 0, viewport: newVp });
41921
+ } else if (input === "G") {
41922
+ const last = groups.length - 1;
41923
+ const newVp = adjustViewport(last, expIdx, expLvl, vpStart);
41924
+ dispatchView({ type: "MOVE", cursor: last, viewport: newVp });
41925
+ } else if (key.pageDown) {
41926
+ const next = Math.min(groups.length - 1, cursor + availableRows);
41927
+ const newVp = adjustViewport(next, expIdx, expLvl, vpStart);
41928
+ dispatchView({ type: "MOVE", cursor: next, viewport: newVp });
41929
+ } else if (key.pageUp) {
41930
+ const next = Math.max(0, cursor - availableRows);
41931
+ const newVp = adjustViewport(next, expIdx, expLvl, next);
41932
+ dispatchView({ type: "MOVE", cursor: next, viewport: newVp });
41588
41933
  } else if (key.return) {
41589
41934
  let newExpIdx;
41590
41935
  let newExpLvl;
@@ -41598,13 +41943,9 @@ var init_InteractiveResultsView = __esm({
41598
41943
  const newVp = adjustViewport(cursor, newExpIdx, newExpLvl, vpStart);
41599
41944
  dispatchView({ type: "EXPAND", expandedIndex: newExpIdx, expandLevel: newExpLvl, viewport: newVp });
41600
41945
  } else if (input === "e") {
41601
- if (expIdx === cursor && expLvl === "detail") {
41602
- const newVp = adjustViewport(cursor, null, null, vpStart);
41603
- dispatchView({ type: "EXPAND", expandedIndex: null, expandLevel: null, viewport: newVp });
41604
- } else {
41605
- const newVp = adjustViewport(cursor, cursor, "detail", vpStart);
41606
- dispatchView({ type: "EXPAND", expandedIndex: cursor, expandLevel: "detail", viewport: newVp });
41607
- }
41946
+ setDetailPane({ groupIndex: view.cursor, scroll: 0 });
41947
+ } else if (input === "/") {
41948
+ setSearchMode(true);
41608
41949
  } else if (input === "b" && onBack) {
41609
41950
  onBack();
41610
41951
  } else if (input === "q") {
@@ -41615,6 +41956,220 @@ var init_InteractiveResultsView = __esm({
41615
41956
  const aboveCount = view.viewport;
41616
41957
  const belowCount = groups.length - visibleEnd;
41617
41958
  const nameCol = Math.max(20, innerWidth - 22);
41959
+ if (showHelp) {
41960
+ const isDetail = detailPane !== null;
41961
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Box_default, { flexDirection: "column", children: [
41962
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
41963
+ ScoreHeader,
41964
+ {
41965
+ score: result.score,
41966
+ action: result.action,
41967
+ total,
41968
+ flagged: flagged.length,
41969
+ clean: clean.length,
41970
+ severityCounts
41971
+ }
41972
+ ),
41973
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
41974
+ Box_default,
41975
+ {
41976
+ flexDirection: "column",
41977
+ borderStyle: "round",
41978
+ borderColor: "cyan",
41979
+ paddingLeft: 2,
41980
+ paddingRight: 2,
41981
+ width: "100%",
41982
+ children: [
41983
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Text, { bold: true, children: [
41984
+ import_chalk10.default.cyan("\u25C6"),
41985
+ " Keyboard Shortcuts"
41986
+ ] }),
41987
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Text, { children: "" }),
41988
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Text, { bold: true, children: " Navigation" }),
41989
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Text, { children: [
41990
+ " ",
41991
+ import_chalk10.default.cyan("\u2191 k"),
41992
+ " ",
41993
+ import_chalk10.default.dim("Move up")
41994
+ ] }),
41995
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Text, { children: [
41996
+ " ",
41997
+ import_chalk10.default.cyan("\u2193 j"),
41998
+ " ",
41999
+ import_chalk10.default.dim("Move down")
42000
+ ] }),
42001
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Text, { children: [
42002
+ " ",
42003
+ import_chalk10.default.cyan("g"),
42004
+ " ",
42005
+ import_chalk10.default.dim("Jump to top")
42006
+ ] }),
42007
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Text, { children: [
42008
+ " ",
42009
+ import_chalk10.default.cyan("G"),
42010
+ " ",
42011
+ import_chalk10.default.dim("Jump to bottom")
42012
+ ] }),
42013
+ !isDetail && /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Text, { children: [
42014
+ " ",
42015
+ import_chalk10.default.cyan("PgUp"),
42016
+ " ",
42017
+ import_chalk10.default.dim("Page up")
42018
+ ] }),
42019
+ !isDetail && /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Text, { children: [
42020
+ " ",
42021
+ import_chalk10.default.cyan("PgDn"),
42022
+ " ",
42023
+ import_chalk10.default.dim("Page down")
42024
+ ] }),
42025
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Text, { children: "" }),
42026
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Text, { bold: true, children: " Actions" }),
42027
+ !isDetail && /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Text, { children: [
42028
+ " ",
42029
+ import_chalk10.default.cyan("\u23CE"),
42030
+ " ",
42031
+ import_chalk10.default.dim("Toggle summary")
42032
+ ] }),
42033
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Text, { children: [
42034
+ " ",
42035
+ import_chalk10.default.cyan("e"),
42036
+ " ",
42037
+ import_chalk10.default.dim(isDetail ? "Back to list" : "Open detail view")
42038
+ ] }),
42039
+ !isDetail && /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Text, { children: [
42040
+ " ",
42041
+ import_chalk10.default.cyan("/"),
42042
+ " ",
42043
+ import_chalk10.default.dim("Search packages")
42044
+ ] }),
42045
+ !isDetail && onBack && /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Text, { children: [
42046
+ " ",
42047
+ import_chalk10.default.cyan("b"),
42048
+ " ",
42049
+ import_chalk10.default.dim("Back to project selector")
42050
+ ] }),
42051
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Text, { children: [
42052
+ " ",
42053
+ import_chalk10.default.cyan("q"),
42054
+ " ",
42055
+ import_chalk10.default.dim("Quit")
42056
+ ] }),
42057
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Text, { children: "" }),
42058
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Text, { dimColor: true, children: [
42059
+ " Press ",
42060
+ import_chalk10.default.bold.cyan("?"),
42061
+ " or ",
42062
+ import_chalk10.default.bold.cyan("Esc"),
42063
+ " to close"
42064
+ ] })
42065
+ ]
42066
+ }
42067
+ )
42068
+ ] });
42069
+ }
42070
+ if (detailPane !== null) {
42071
+ const dpGroup = groups[detailPane.groupIndex];
42072
+ if (!dpGroup) {
42073
+ setDetailPane(null);
42074
+ } else {
42075
+ const dpRep = dpGroup.packages[0];
42076
+ const { color: dpColor } = actionBadge3(dpRep.score, config);
42077
+ const dpScroll = detailPane.scroll;
42078
+ const dpAbove = dpScroll;
42079
+ const dpBelow = Math.max(0, detailLines.length - dpScroll - detailContentRows);
42080
+ const dpVisible = detailLines.slice(dpScroll, dpScroll + detailContentRows);
42081
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Box_default, { flexDirection: "column", children: [
42082
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
42083
+ ScoreHeader,
42084
+ {
42085
+ score: result.score,
42086
+ action: result.action,
42087
+ total,
42088
+ flagged: flagged.length,
42089
+ clean: clean.length,
42090
+ severityCounts
42091
+ }
42092
+ ),
42093
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
42094
+ Box_default,
42095
+ {
42096
+ flexDirection: "column",
42097
+ borderStyle: "round",
42098
+ borderColor: "gray",
42099
+ paddingLeft: 1,
42100
+ paddingRight: 1,
42101
+ width: "100%",
42102
+ children: [
42103
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Box_default, { justifyContent: "space-between", children: [
42104
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Text, { bold: true, children: groupNames(dpGroup) }),
42105
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Text, { children: dpColor(`score ${dpRep.score}`) })
42106
+ ] }),
42107
+ dpAbove > 0 && /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Text, { dimColor: true, children: [
42108
+ import_chalk10.default.cyan(" \u2191"),
42109
+ " ",
42110
+ dpAbove,
42111
+ " more above"
42112
+ ] }),
42113
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Box_default, { flexDirection: "column", marginLeft: 2, children: dpVisible }),
42114
+ dpBelow > 0 && /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Text, { dimColor: true, children: [
42115
+ import_chalk10.default.cyan(" \u2193"),
42116
+ " ",
42117
+ dpBelow,
42118
+ " more below"
42119
+ ] })
42120
+ ]
42121
+ }
42122
+ ),
42123
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
42124
+ Box_default,
42125
+ {
42126
+ flexDirection: "column",
42127
+ borderStyle: "round",
42128
+ borderColor: "gray",
42129
+ paddingLeft: 1,
42130
+ paddingRight: 1,
42131
+ width: "100%",
42132
+ children: [
42133
+ clean.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Text, { children: [
42134
+ import_chalk10.default.green("\u2713"),
42135
+ " ",
42136
+ import_chalk10.default.green.bold(String(clean.length)),
42137
+ " ",
42138
+ import_chalk10.default.dim(`package${clean.length !== 1 ? "s" : ""} passed with score 0`)
42139
+ ] }),
42140
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Box_default, { justifyContent: "space-between", children: [
42141
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Text, { dimColor: true, children: [
42142
+ (durationMs / 1e3).toFixed(1),
42143
+ "s"
42144
+ ] }),
42145
+ result.trialScansRemaining !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Text, { dimColor: true, children: [
42146
+ result.trialScansRemaining,
42147
+ " free scan",
42148
+ result.trialScansRemaining !== 1 ? "s" : "",
42149
+ " remaining"
42150
+ ] })
42151
+ ] })
42152
+ ]
42153
+ }
42154
+ ),
42155
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Text, { dimColor: true, children: import_chalk10.default.dim("\u2500".repeat(Math.min(60, termCols - 4))) }),
42156
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Text, { children: [
42157
+ " ",
42158
+ import_chalk10.default.bold.cyan("\u2191\u2193"),
42159
+ " ",
42160
+ import_chalk10.default.dim("scroll"),
42161
+ " ",
42162
+ import_chalk10.default.bold.cyan("e"),
42163
+ " ",
42164
+ import_chalk10.default.dim("back"),
42165
+ " ",
42166
+ import_chalk10.default.bold.cyan("q"),
42167
+ " ",
42168
+ import_chalk10.default.dim("quit")
42169
+ ] })
42170
+ ] });
42171
+ }
42172
+ }
41618
42173
  return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Box_default, { flexDirection: "column", children: [
41619
42174
  /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
41620
42175
  ScoreHeader,
@@ -41623,7 +42178,8 @@ var init_InteractiveResultsView = __esm({
41623
42178
  action: result.action,
41624
42179
  total,
41625
42180
  flagged: flagged.length,
41626
- clean: clean.length
42181
+ clean: clean.length,
42182
+ severityCounts
41627
42183
  }
41628
42184
  ),
41629
42185
  groups.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
@@ -41638,14 +42194,10 @@ var init_InteractiveResultsView = __esm({
41638
42194
  children: [
41639
42195
  /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Box_default, { justifyContent: "space-between", children: [
41640
42196
  /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Text, { bold: true, children: "Flagged Packages" }),
41641
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Text, { dimColor: true, children: [
41642
- groups.length,
41643
- " group",
41644
- groups.length !== 1 ? "s" : ""
41645
- ] })
42197
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Text, { dimColor: true, children: searchQuery ? `${groups.length} of ${allGroupCount}` : `${view.cursor + 1}/${groups.length}` })
41646
42198
  ] }),
41647
42199
  aboveCount > 0 && /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Text, { dimColor: true, children: [
41648
- import_chalk9.default.cyan(" \u2191"),
42200
+ import_chalk10.default.cyan(" \u2191"),
41649
42201
  " ",
41650
42202
  aboveCount,
41651
42203
  " more above"
@@ -41655,16 +42207,22 @@ var init_InteractiveResultsView = __esm({
41655
42207
  const isCursor = globalIdx === view.cursor;
41656
42208
  const level = getLevel(globalIdx);
41657
42209
  const rep = group.packages[0];
41658
- const { label, color } = actionBadge4(rep.score, config);
42210
+ const { label, color } = actionBadge3(rep.score, config);
41659
42211
  const names = groupNames(group);
41660
42212
  const chevron = level !== null ? "\u25BE" : "\u25B8";
41661
- const rowContent = ` ${chevron} ` + pad3(label, 7) + pad3(truncate3(names, nameCol - 2), nameCol) + `score ${rep.score}`;
42213
+ const scoreStr = String(rep.score);
41662
42214
  return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Box_default, { flexDirection: "column", children: [
41663
- isCursor ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Text, { children: import_chalk9.default.bgGray.white.bold(rowContent) }) : /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Text, { children: [
41664
- import_chalk9.default.dim(` ${chevron} `),
41665
- color(pad3(label, 7)),
42215
+ isCursor ? /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Text, { children: [
42216
+ import_chalk10.default.cyan("\u258C"),
42217
+ ` ${chevron} `,
42218
+ color(pad3(label, 6)),
42219
+ import_chalk10.default.bold(pad3(truncate3(names, nameCol - 2), nameCol)),
42220
+ color(scoreStr.padStart(3))
42221
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Text, { children: [
42222
+ ` ${import_chalk10.default.dim(chevron)} `,
42223
+ color(pad3(label, 6)),
41666
42224
  pad3(truncate3(names, nameCol - 2), nameCol),
41667
- color(`score ${rep.score}`)
42225
+ color(scoreStr.padStart(3))
41668
42226
  ] }),
41669
42227
  level === "summary" && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
41670
42228
  FindingsSummary,
@@ -41673,20 +42231,11 @@ var init_InteractiveResultsView = __esm({
41673
42231
  maxWidth: innerWidth - 8,
41674
42232
  maxLines: globalIdx === view.expandedIndex ? animVisibleLines : void 0
41675
42233
  }
41676
- ),
41677
- level === "detail" && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
41678
- FindingsDetail,
41679
- {
41680
- group,
41681
- safeVersion: result.safeVersions[rep.name],
41682
- maxWidth: innerWidth - 8,
41683
- maxLines: globalIdx === view.expandedIndex ? animVisibleLines : void 0
41684
- }
41685
42234
  )
41686
42235
  ] }, group.key);
41687
42236
  }),
41688
42237
  belowCount > 0 && /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Text, { dimColor: true, children: [
41689
- import_chalk9.default.cyan(" \u2193"),
42238
+ import_chalk10.default.cyan(" \u2193"),
41690
42239
  " ",
41691
42240
  belowCount,
41692
42241
  " more below"
@@ -41705,12 +42254,11 @@ var init_InteractiveResultsView = __esm({
41705
42254
  width: "100%",
41706
42255
  children: [
41707
42256
  clean.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Text, { children: [
41708
- import_chalk9.default.green("\u2713"),
42257
+ import_chalk10.default.green("\u2713"),
41709
42258
  " ",
41710
- clean.length,
41711
- " package",
41712
- clean.length !== 1 ? "s" : "",
41713
- " passed with score 0"
42259
+ import_chalk10.default.green.bold(String(clean.length)),
42260
+ " ",
42261
+ import_chalk10.default.dim(`package${clean.length !== 1 ? "s" : ""} passed with score 0`)
41714
42262
  ] }),
41715
42263
  discoveredTotal !== void 0 && discoveredTotal > total && /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Text, { dimColor: true, children: [
41716
42264
  "Scanned ",
@@ -41718,14 +42266,14 @@ var init_InteractiveResultsView = __esm({
41718
42266
  " of ",
41719
42267
  discoveredTotal,
41720
42268
  " packages ",
41721
- import_chalk9.default.dim("\u2014 dg login for full scans")
42269
+ import_chalk10.default.dim("\u2014 dg login for full scans")
41722
42270
  ] }),
41723
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Text, { dimColor: true, children: [
41724
- "Completed in ",
41725
- (durationMs / 1e3).toFixed(1),
41726
- "s",
41727
- result.trialScansRemaining !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_jsx_runtime10.Fragment, { children: [
41728
- import_chalk9.default.dim(" \u2502 "),
42271
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Box_default, { justifyContent: "space-between", children: [
42272
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Text, { dimColor: true, children: [
42273
+ (durationMs / 1e3).toFixed(1),
42274
+ "s"
42275
+ ] }),
42276
+ result.trialScansRemaining !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Text, { dimColor: true, children: [
41729
42277
  result.trialScansRemaining,
41730
42278
  " free scan",
41731
42279
  result.trialScansRemaining !== 1 ? "s" : "",
@@ -41735,39 +42283,62 @@ var init_InteractiveResultsView = __esm({
41735
42283
  ]
41736
42284
  }
41737
42285
  ),
41738
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Text, { dimColor: true, children: [
42286
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Text, { dimColor: true, children: import_chalk10.default.dim("\u2500".repeat(Math.min(60, termCols - 4))) }),
42287
+ searchMode ? /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Text, { children: [
42288
+ " ",
42289
+ import_chalk10.default.bold.cyan("/"),
42290
+ " ",
42291
+ searchQuery,
42292
+ import_chalk10.default.cyan("\u2588"),
42293
+ " ",
42294
+ import_chalk10.default.dim("Esc clear")
42295
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Text, { children: [
41739
42296
  " ",
41740
42297
  groups.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_jsx_runtime10.Fragment, { children: [
41741
- import_chalk9.default.cyan("\u2191\u2193"),
41742
- " navigate",
41743
- " ",
41744
- import_chalk9.default.cyan("\u23CE"),
41745
- " toggle",
41746
- " ",
41747
- import_chalk9.default.cyan("e"),
41748
- " expand/collapse",
41749
- " ",
42298
+ import_chalk10.default.bold.cyan("\u2191\u2193"),
42299
+ " ",
42300
+ import_chalk10.default.dim("navigate"),
42301
+ " ",
42302
+ import_chalk10.default.bold.cyan("\u23CE"),
42303
+ " ",
42304
+ import_chalk10.default.dim("toggle"),
42305
+ " ",
42306
+ import_chalk10.default.bold.cyan("e"),
42307
+ " ",
42308
+ import_chalk10.default.dim("detail"),
42309
+ " ",
42310
+ import_chalk10.default.bold.cyan("/"),
42311
+ " ",
42312
+ import_chalk10.default.dim("search"),
42313
+ " ",
42314
+ import_chalk10.default.bold.cyan("?"),
42315
+ " ",
42316
+ import_chalk10.default.dim("help"),
42317
+ " ",
41750
42318
  onBack && /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_jsx_runtime10.Fragment, { children: [
41751
- import_chalk9.default.cyan("b"),
41752
- " back",
41753
- " "
42319
+ import_chalk10.default.bold.cyan("b"),
42320
+ " ",
42321
+ import_chalk10.default.dim("back"),
42322
+ " "
41754
42323
  ] }),
41755
- import_chalk9.default.cyan("q"),
41756
- " quit"
42324
+ import_chalk10.default.bold.cyan("q"),
42325
+ " ",
42326
+ import_chalk10.default.dim("quit")
41757
42327
  ] }) : /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_jsx_runtime10.Fragment, { children: [
41758
42328
  "Press ",
41759
- import_chalk9.default.cyan("q"),
42329
+ import_chalk10.default.bold.cyan("q"),
41760
42330
  " or ",
41761
- import_chalk9.default.cyan("Enter"),
41762
- " to exit"
42331
+ import_chalk10.default.bold.cyan("Enter"),
42332
+ " ",
42333
+ import_chalk10.default.dim("to exit")
41763
42334
  ] })
41764
42335
  ] })
41765
42336
  ] });
41766
42337
  };
41767
42338
  T = {
41768
- branch: import_chalk9.default.dim("\u251C\u2500\u2500"),
41769
- last: import_chalk9.default.dim("\u2514\u2500\u2500"),
41770
- pipe: import_chalk9.default.dim("\u2502"),
42339
+ branch: import_chalk10.default.dim("\u251C\u2500\u2500"),
42340
+ last: import_chalk10.default.dim("\u2514\u2500\u2500"),
42341
+ pipe: import_chalk10.default.dim("\u2502"),
41771
42342
  blank: " "
41772
42343
  };
41773
42344
  FindingsSummary = ({ group, maxWidth, maxLines }) => {
@@ -41788,11 +42359,11 @@ var init_InteractiveResultsView = __esm({
41788
42359
  }
41789
42360
  allLines.push(
41790
42361
  /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Text, { dimColor: true, children: [
41791
- hasAffects ? T.branch : T.last,
42362
+ import_chalk10.default.yellow(" \u2192"),
41792
42363
  " ",
41793
- import_chalk9.default.yellow("Upgrade to Pro"),
42364
+ import_chalk10.default.yellow("Upgrade to Pro"),
41794
42365
  " to see finding details ",
41795
- import_chalk9.default.dim("\u2014 dg login")
42366
+ import_chalk10.default.dim("\u2014 dg login")
41796
42367
  ] }, "upgrade")
41797
42368
  );
41798
42369
  }
@@ -41806,9 +42377,9 @@ var init_InteractiveResultsView = __esm({
41806
42377
  /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Text, { children: [
41807
42378
  connector,
41808
42379
  " ",
41809
- sevColor(pad3(sevLabel, 4)),
42380
+ sevColor(pad3(sevLabel, 5)),
41810
42381
  " ",
41811
- import_chalk9.default.dim(f.id ?? f.category ?? "")
42382
+ import_chalk10.default.dim(f.id ?? f.category ?? "")
41812
42383
  ] }, `${f.id ?? f.category}-${idx}`)
41813
42384
  );
41814
42385
  }
@@ -41824,139 +42395,21 @@ var init_InteractiveResultsView = __esm({
41824
42395
  const linesToShow = maxLines !== void 0 ? allLines.slice(0, maxLines) : allLines;
41825
42396
  return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Box_default, { flexDirection: "column", marginLeft: 5, children: linesToShow });
41826
42397
  };
41827
- FindingsDetail = ({ group, safeVersion, maxWidth, maxLines }) => {
41828
- const rep = group.packages[0];
41829
- const visibleFindings = rep.findings.filter((f) => f.severity > 1 || f.critical).sort((a, b) => b.severity - a.severity);
41830
- const hasAffects = group.packages.length > 3;
41831
- const hasSafe = !!safeVersion;
41832
- const trailingItems = (hasAffects ? 1 : 0) + (hasSafe ? 1 : 0);
41833
- const evidenceWidth = Math.max(30, maxWidth - 12);
41834
- const allLines = [];
41835
- const hasRecommendation = !!rep.recommendation;
41836
- if (visibleFindings.length === 0 && rep.score > 0) {
41837
- const reasons = rep.reasons ?? [];
41838
- for (let i = 0; i < reasons.length; i++) {
41839
- allLines.push(
41840
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Text, { dimColor: true, children: [
41841
- T.branch,
41842
- " ",
41843
- truncate3(reasons[i], maxWidth - 8)
41844
- ] }, `reason-${i}`)
41845
- );
41846
- }
41847
- allLines.push(
41848
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Text, { dimColor: true, children: [
41849
- hasSafe || hasAffects || hasRecommendation ? T.branch : T.last,
41850
- " ",
41851
- import_chalk9.default.yellow("Upgrade to Pro"),
41852
- " to see finding details ",
41853
- import_chalk9.default.dim("\u2014 dg login")
41854
- ] }, "upgrade")
41855
- );
41856
- }
41857
- const hasEvidence = visibleFindings.some((f) => f.evidence && f.evidence.length > 0);
41858
- for (let idx = 0; idx < visibleFindings.length; idx++) {
41859
- const finding = visibleFindings[idx];
41860
- const isLastFinding = idx === visibleFindings.length - 1 && trailingItems === 0;
41861
- const connector = isLastFinding && !(!hasEvidence && visibleFindings.length > 0) ? T.last : T.branch;
41862
- const continuation = isLastFinding && !(!hasEvidence && visibleFindings.length > 0) ? `${T.blank} ` : `${T.pipe} `;
41863
- const sevLabel = SEVERITY_LABELS3[finding.severity] ?? "INFO";
41864
- const sevColor = SEVERITY_COLORS[finding.severity] ?? SEVERITY_COLORS[1];
41865
- const evidence = finding.evidence ?? [];
41866
- const evidenceSlice = evidence.slice(0, EVIDENCE_LIMIT2);
41867
- const overflow = evidence.length - EVIDENCE_LIMIT2;
41868
- const findingId = finding.id ?? finding.category ?? "";
41869
- allLines.push(
41870
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Text, { children: [
41871
- connector,
41872
- " ",
41873
- sevColor(pad3(sevLabel, 4)),
41874
- " ",
41875
- import_chalk9.default.bold(findingId)
41876
- ] }, `${findingId}-${idx}-badge`)
41877
- );
41878
- allLines.push(
41879
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Text, { dimColor: true, children: [
41880
- continuation,
41881
- " ",
41882
- finding.title
41883
- ] }, `${findingId}-${idx}-title`)
41884
- );
41885
- for (let i = 0; i < evidenceSlice.length; i++) {
41886
- allLines.push(
41887
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Text, { dimColor: true, children: [
41888
- continuation,
41889
- " ",
41890
- import_chalk9.default.dim("\u203A"),
41891
- " ",
41892
- truncate3(evidenceSlice[i], evidenceWidth)
41893
- ] }, `${findingId}-${idx}-ev-${i}`)
41894
- );
41895
- }
41896
- if (overflow > 0) {
41897
- allLines.push(
41898
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Text, { dimColor: true, children: [
41899
- continuation,
41900
- " ",
41901
- import_chalk9.default.dim(`+${overflow} more`)
41902
- ] }, `${findingId}-${idx}-overflow`)
41903
- );
41904
- }
41905
- }
41906
- if (visibleFindings.length > 0 && !hasEvidence) {
41907
- allLines.push(
41908
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Text, { dimColor: true, children: [
41909
- hasSafe || hasAffects ? T.branch : T.last,
41910
- " ",
41911
- import_chalk9.default.yellow("Upgrade to Team"),
41912
- " to see evidence and file locations"
41913
- ] }, "upgrade-evidence")
41914
- );
41915
- }
41916
- if (hasAffects) {
41917
- allLines.push(
41918
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Text, { dimColor: true, children: [
41919
- hasSafe || hasRecommendation ? T.branch : T.last,
41920
- " ",
41921
- truncate3(affectsLine(group), maxWidth - 8)
41922
- ] }, "affects")
41923
- );
41924
- }
41925
- if (hasRecommendation) {
41926
- allLines.push(
41927
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Text, { children: [
41928
- hasSafe ? T.branch : T.last,
41929
- " ",
41930
- import_chalk9.default.cyan(truncate3(rep.recommendation, maxWidth - 8))
41931
- ] }, "recommendation")
41932
- );
41933
- }
41934
- if (hasSafe) {
41935
- allLines.push(
41936
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Text, { children: [
41937
- T.last,
41938
- " ",
41939
- import_chalk9.default.green(`Safe: ${rep.name}@${safeVersion}`)
41940
- ] }, "safe")
41941
- );
41942
- }
41943
- const linesToShow = maxLines !== void 0 ? allLines.slice(0, maxLines) : allLines;
41944
- return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Box_default, { flexDirection: "column", marginLeft: 5, children: linesToShow });
41945
- };
41946
42398
  }
41947
42399
  });
41948
42400
 
41949
42401
  // src/ui/components/ProjectSelector.tsx
41950
- var import_react31, import_jsx_runtime11, ProjectSelector;
42402
+ var import_react32, import_chalk11, import_jsx_runtime11, ProjectSelector;
41951
42403
  var init_ProjectSelector = __esm({
41952
42404
  async "src/ui/components/ProjectSelector.tsx"() {
41953
42405
  "use strict";
41954
- import_react31 = __toESM(require_react());
42406
+ import_react32 = __toESM(require_react());
41955
42407
  await init_build2();
42408
+ import_chalk11 = __toESM(require_source());
41956
42409
  import_jsx_runtime11 = __toESM(require_jsx_runtime());
41957
42410
  ProjectSelector = ({ projects, onConfirm, onCancel }) => {
41958
- const [cursor, setCursor] = (0, import_react31.useState)(0);
41959
- const [selected, setSelected] = (0, import_react31.useState)(() => new Set(projects.map((_, i) => i)));
42411
+ const [cursor, setCursor] = (0, import_react32.useState)(0);
42412
+ const [selected, setSelected] = (0, import_react32.useState)(() => new Set(projects.map((_, i) => i)));
41960
42413
  use_input_default((input, key) => {
41961
42414
  if (key.upArrow) {
41962
42415
  setCursor((c) => Math.max(0, c - 1));
@@ -41981,25 +42434,58 @@ var init_ProjectSelector = __esm({
41981
42434
  onCancel();
41982
42435
  }
41983
42436
  });
41984
- const ecosystemLabel = (eco) => eco === "npm" ? "npm" : "pip";
42437
+ const ecosystemLabel = (eco) => eco === "npm" ? import_chalk11.default.magenta("npm") : import_chalk11.default.blue("pip");
41985
42438
  return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Box_default, { flexDirection: "column", paddingLeft: 1, children: [
42439
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Text, { children: [
42440
+ import_chalk11.default.cyan("\u25C6"),
42441
+ " ",
42442
+ import_chalk11.default.bold("Dependency Guardian")
42443
+ ] }),
42444
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Text, { children: "" }),
41986
42445
  /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Text, { bold: true, children: [
41987
42446
  "Found ",
41988
42447
  projects.length,
41989
42448
  " project",
41990
42449
  projects.length !== 1 ? "s" : ""
41991
42450
  ] }),
41992
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Text, { dimColor: true, children: "Space to toggle, a to select all, Enter to scan, q to quit" }),
41993
42451
  /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Text, { children: "" }),
41994
42452
  projects.map((proj, i) => {
41995
42453
  const isCursor = i === cursor;
41996
42454
  const isSelected = selected.has(i);
41997
- const check = isSelected ? "\u25C9" : "\u25CB";
41998
- const line = ` ${check} ${proj.relativePath.padEnd(40)} ${ecosystemLabel(proj.ecosystem).padEnd(5)} ${proj.packageCount} packages`;
41999
- return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Text, { bold: isCursor, inverse: isCursor, children: line }, i);
42455
+ const prefix = isCursor ? import_chalk11.default.cyan("\u258C") : " ";
42456
+ const check = isSelected ? import_chalk11.default.green("\u25C9") : import_chalk11.default.dim("\u25CB");
42457
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Text, { children: [
42458
+ prefix,
42459
+ check,
42460
+ " ",
42461
+ proj.relativePath.padEnd(40),
42462
+ " ",
42463
+ ecosystemLabel(proj.ecosystem).padEnd(5),
42464
+ " ",
42465
+ proj.packageCount,
42466
+ " packages"
42467
+ ] }, i);
42000
42468
  }),
42001
42469
  /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Text, { children: "" }),
42002
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Text, { dimColor: true, children: selected.size === 0 ? "Select at least 1 project to scan" : `${selected.size} of ${projects.length} selected` })
42470
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Text, { dimColor: true, children: selected.size === 0 ? import_chalk11.default.yellow("Select at least 1 project to scan") : `${selected.size} of ${projects.length} selected` }),
42471
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Text, { children: "" }),
42472
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Text, { children: [
42473
+ import_chalk11.default.bold.cyan("space"),
42474
+ " ",
42475
+ import_chalk11.default.dim("toggle"),
42476
+ " ",
42477
+ import_chalk11.default.bold.cyan("a"),
42478
+ " ",
42479
+ import_chalk11.default.dim("all"),
42480
+ " ",
42481
+ import_chalk11.default.bold.cyan("\u23CE"),
42482
+ " ",
42483
+ import_chalk11.default.dim("scan"),
42484
+ " ",
42485
+ import_chalk11.default.bold.cyan("q"),
42486
+ " ",
42487
+ import_chalk11.default.dim("quit")
42488
+ ] })
42003
42489
  ] });
42004
42490
  };
42005
42491
  }
@@ -42010,11 +42496,11 @@ var App_exports = {};
42010
42496
  __export(App_exports, {
42011
42497
  App: () => App2
42012
42498
  });
42013
- var import_react32, import_jsx_runtime12, App2;
42499
+ var import_react33, import_jsx_runtime12, App2;
42014
42500
  var init_App2 = __esm({
42015
42501
  async "src/ui/App.tsx"() {
42016
42502
  "use strict";
42017
- import_react32 = __toESM(require_react());
42503
+ import_react33 = __toESM(require_react());
42018
42504
  await init_build2();
42019
42505
  init_useScan();
42020
42506
  await init_Spinner();
@@ -42027,10 +42513,10 @@ var init_App2 = __esm({
42027
42513
  App2 = ({ config }) => {
42028
42514
  const { state, scanSelectedProjects, restartSelection } = useScan(config);
42029
42515
  const { exit } = use_app_default();
42030
- const { rows: termRows } = useTerminalSize();
42031
- const prevPhaseRef = (0, import_react32.useRef)(state.phase);
42032
- const altScreenActiveRef = (0, import_react32.useRef)(false);
42033
- (0, import_react32.useEffect)(() => {
42516
+ useTerminalSize();
42517
+ const prevPhaseRef = (0, import_react33.useRef)(state.phase);
42518
+ const altScreenActiveRef = (0, import_react33.useRef)(false);
42519
+ (0, import_react33.useEffect)(() => {
42034
42520
  if (!process.stdout.isTTY) return;
42035
42521
  process.stdout.write("\x1B[?1049h");
42036
42522
  process.stdout.write("\x1B[2J\x1B[H");
@@ -42043,13 +42529,20 @@ var init_App2 = __esm({
42043
42529
  process.stdout.write("\x1B[?25h");
42044
42530
  };
42045
42531
  }, []);
42046
- (0, import_react32.useEffect)(() => {
42532
+ (0, import_react33.useEffect)(() => {
42047
42533
  if (prevPhaseRef.current !== state.phase && process.stdout.isTTY) {
42048
42534
  process.stdout.write("\x1B[2J\x1B[H");
42049
42535
  }
42050
42536
  prevPhaseRef.current = state.phase;
42051
42537
  }, [state.phase]);
42052
- const handleResultsExit = (0, import_react32.useCallback)(() => {
42538
+ const leaveAltScreen = (0, import_react33.useCallback)(() => {
42539
+ if (altScreenActiveRef.current && process.stdout.isTTY) {
42540
+ process.stdout.write("\x1B[?1049l");
42541
+ altScreenActiveRef.current = false;
42542
+ }
42543
+ process.stdout.write("\x1B[?25h");
42544
+ }, []);
42545
+ const handleResultsExit = (0, import_react33.useCallback)(() => {
42053
42546
  if (state.phase === "results") {
42054
42547
  const { result } = state;
42055
42548
  if (result.action === "block" && config.mode === "block") {
@@ -42060,19 +42553,16 @@ var init_App2 = __esm({
42060
42553
  process.exitCode = 0;
42061
42554
  }
42062
42555
  }
42556
+ leaveAltScreen();
42063
42557
  exit();
42064
- }, [state, config, exit]);
42065
- const exitWithMessage = (0, import_react32.useCallback)((message, exitCode) => {
42558
+ }, [state, config, exit, leaveAltScreen]);
42559
+ const exitWithMessage = (0, import_react33.useCallback)((message, exitCode) => {
42066
42560
  process.exitCode = exitCode;
42067
- if (altScreenActiveRef.current && process.stdout.isTTY) {
42068
- process.stdout.write("\x1B[?1049l");
42069
- altScreenActiveRef.current = false;
42070
- }
42071
- process.stdout.write("\x1B[?25h");
42561
+ leaveAltScreen();
42072
42562
  process.stderr.write(message);
42073
42563
  return setTimeout(() => exit(), 0);
42074
- }, [exit]);
42075
- (0, import_react32.useEffect)(() => {
42564
+ }, [exit, leaveAltScreen]);
42565
+ (0, import_react33.useEffect)(() => {
42076
42566
  if (state.phase === "empty") {
42077
42567
  const timer = exitWithMessage(`${state.message}
42078
42568
  `, 0);
@@ -42091,10 +42581,19 @@ var init_App2 = __esm({
42091
42581
  return () => clearTimeout(timer);
42092
42582
  }
42093
42583
  }, [state, exitWithMessage]);
42584
+ use_input_default((input, key) => {
42585
+ if (state.phase === "discovering" || state.phase === "scanning") {
42586
+ if (input === "q" || key.escape) {
42587
+ process.exitCode = 0;
42588
+ leaveAltScreen();
42589
+ exit();
42590
+ }
42591
+ }
42592
+ });
42094
42593
  const content = (() => {
42095
42594
  switch (state.phase) {
42096
42595
  case "discovering":
42097
- return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Spinner2, { label: "Searching for dependencies..." });
42596
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Spinner2, { label: `Searching for dependencies in ${process.cwd()} ...` });
42098
42597
  case "selecting":
42099
42598
  return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
42100
42599
  ProjectSelector,
@@ -42103,6 +42602,7 @@ var init_App2 = __esm({
42103
42602
  onConfirm: scanSelectedProjects,
42104
42603
  onCancel: () => {
42105
42604
  process.exitCode = 0;
42605
+ leaveAltScreen();
42106
42606
  exit();
42107
42607
  }
42108
42608
  }
@@ -42143,7 +42643,7 @@ var init_App2 = __esm({
42143
42643
  ] });
42144
42644
  }
42145
42645
  })();
42146
- return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Box_default, { flexDirection: "column", height: termRows, children: content });
42646
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Box_default, { flexDirection: "column", children: content });
42147
42647
  };
42148
42648
  }
42149
42649
  });
@@ -42238,33 +42738,33 @@ async function checkForUpdate(currentVersion) {
42238
42738
  return null;
42239
42739
  }
42240
42740
  async function runUpdate(currentVersion) {
42241
- const chalk8 = (await Promise.resolve().then(() => __toESM(require_source()))).default;
42242
- process.stderr.write(chalk8.dim(" Checking for updates...\n"));
42741
+ const chalk10 = (await Promise.resolve().then(() => __toESM(require_source()))).default;
42742
+ process.stderr.write(chalk10.dim(" Checking for updates...\n"));
42243
42743
  const latest = await fetchLatestVersion();
42244
42744
  if (!latest) {
42245
- process.stderr.write(chalk8.red(" Could not reach npm registry.\n"));
42745
+ process.stderr.write(chalk10.red(" Could not reach npm registry.\n"));
42246
42746
  process.exit(1);
42247
42747
  }
42248
42748
  if (!isNewer(latest, currentVersion)) {
42249
42749
  process.stderr.write(
42250
- chalk8.green(` Already on latest version (${currentVersion}).
42750
+ chalk10.green(` Already on latest version (${currentVersion}).
42251
42751
  `)
42252
42752
  );
42253
42753
  return;
42254
42754
  }
42255
- process.stderr.write(chalk8.dim(` Installing ${PKG_NAME}@${latest}...
42755
+ process.stderr.write(chalk10.dim(` Installing ${PKG_NAME}@${latest}...
42256
42756
  `));
42257
42757
  try {
42258
42758
  execSync(`npm install -g ${PKG_NAME}@${latest}`, { stdio: "inherit" });
42259
42759
  writeCache({ latest, checkedAt: Date.now() });
42260
42760
  process.stderr.write(
42261
- chalk8.green(`
42761
+ chalk10.green(`
42262
42762
  Updated ${currentVersion} \u2192 ${latest}
42263
42763
  `)
42264
42764
  );
42265
42765
  } catch {
42266
42766
  process.stderr.write(
42267
- chalk8.red(`
42767
+ chalk10.red(`
42268
42768
  Update failed. Try manually: npm i -g ${PKG_NAME}@${latest}
42269
42769
  `)
42270
42770
  );
@@ -42294,9 +42794,9 @@ async function main() {
42294
42794
  if (rawCommand === "login") {
42295
42795
  if (isInteractive) {
42296
42796
  const { render: render3 } = await init_build2().then(() => build_exports);
42297
- const React17 = await Promise.resolve().then(() => __toESM(require_react()));
42797
+ const React18 = await Promise.resolve().then(() => __toESM(require_react()));
42298
42798
  const { LoginApp: LoginApp2 } = await init_LoginApp().then(() => LoginApp_exports);
42299
- const { waitUntilExit } = render3(React17.createElement(LoginApp2));
42799
+ const { waitUntilExit } = render3(React18.createElement(LoginApp2));
42300
42800
  await waitUntilExit();
42301
42801
  } else {
42302
42802
  const { runStaticLogin: runStaticLogin2 } = await Promise.resolve().then(() => (init_static_output(), static_output_exports));
@@ -42316,8 +42816,8 @@ async function main() {
42316
42816
  if (rawCommand === "logout") {
42317
42817
  const { clearCredentials: clearCredentials2 } = await Promise.resolve().then(() => (init_auth(), auth_exports));
42318
42818
  clearCredentials2();
42319
- const chalk8 = (await Promise.resolve().then(() => __toESM(require_source()))).default;
42320
- process.stderr.write(chalk8.green(" Logged out.\n"));
42819
+ const chalk10 = (await Promise.resolve().then(() => __toESM(require_source()))).default;
42820
+ process.stderr.write(chalk10.green(" Logged out.\n"));
42321
42821
  return;
42322
42822
  }
42323
42823
  const config = parseConfig(process.argv);
@@ -42332,42 +42832,42 @@ async function main() {
42332
42832
  }
42333
42833
  const updateMsg2 = await updatePromise;
42334
42834
  if (updateMsg2) {
42335
- const chalk8 = (await Promise.resolve().then(() => __toESM(require_source()))).default;
42336
- process.stderr.write(chalk8.dim(updateMsg2));
42835
+ const chalk10 = (await Promise.resolve().then(() => __toESM(require_source()))).default;
42836
+ process.stderr.write(chalk10.dim(updateMsg2));
42337
42837
  }
42338
42838
  return;
42339
42839
  }
42340
42840
  const { render: render2 } = await init_build2().then(() => build_exports);
42341
- const React16 = await Promise.resolve().then(() => __toESM(require_react()));
42841
+ const React17 = await Promise.resolve().then(() => __toESM(require_react()));
42342
42842
  if (rawCommand === "npm") {
42343
42843
  const { NpmWrapperApp: NpmWrapperApp2 } = await init_NpmWrapperApp().then(() => NpmWrapperApp_exports);
42344
42844
  const { waitUntilExit } = render2(
42345
- React16.createElement(NpmWrapperApp2, { config, npmArgs: process.argv.slice(3) })
42845
+ React17.createElement(NpmWrapperApp2, { config, npmArgs: process.argv.slice(3) })
42346
42846
  );
42347
42847
  await waitUntilExit();
42348
42848
  } else {
42349
42849
  if (config.mode === "off") {
42350
- const chalk8 = (await Promise.resolve().then(() => __toESM(require_source()))).default;
42351
- process.stderr.write(chalk8.dim(" Dependency Guardian: mode is off \u2014 skipping.\n"));
42850
+ const chalk10 = (await Promise.resolve().then(() => __toESM(require_source()))).default;
42851
+ process.stderr.write(chalk10.dim(" Dependency Guardian: mode is off \u2014 skipping.\n"));
42352
42852
  process.exit(0);
42353
42853
  }
42354
42854
  const { App: App3 } = await init_App2().then(() => App_exports);
42355
42855
  const { waitUntilExit } = render2(
42356
- React16.createElement(App3, { config })
42856
+ React17.createElement(App3, { config })
42357
42857
  );
42358
42858
  await waitUntilExit();
42359
42859
  }
42360
42860
  const updateMsg = await updatePromise;
42361
42861
  if (updateMsg) {
42362
- const chalk8 = (await Promise.resolve().then(() => __toESM(require_source()))).default;
42363
- process.stderr.write(chalk8.dim(updateMsg));
42862
+ const chalk10 = (await Promise.resolve().then(() => __toESM(require_source()))).default;
42863
+ process.stderr.write(chalk10.dim(updateMsg));
42364
42864
  }
42365
42865
  }
42366
42866
  main().catch((err) => {
42367
42867
  try {
42368
- const chalk8 = require_source();
42868
+ const chalk10 = require_source();
42369
42869
  process.stderr.write(`
42370
- ${chalk8.bold.red("Error:")} ${err.message}
42870
+ ${chalk10.bold.red("Error:")} ${err.message}
42371
42871
 
42372
42872
  `);
42373
42873
  } catch {