brew-tui 1.0.0 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/build/index.js CHANGED
@@ -66,7 +66,7 @@ import { rm as rm2 } from "fs/promises";
66
66
  import { render } from "ink";
67
67
 
68
68
  // src/app.tsx
69
- import { useEffect as useEffect23, useState as useState20 } from "react";
69
+ import { useEffect as useEffect23, useState as useState21 } from "react";
70
70
  import { useApp } from "ink";
71
71
 
72
72
  // src/components/layout/app-layout.tsx
@@ -74,7 +74,32 @@ import { useRef } from "react";
74
74
  import { Box as Box3 } from "ink";
75
75
 
76
76
  // src/components/layout/header.tsx
77
- import { Box, Text as Text3, useStdout } from "ink";
77
+ import { Box, Text as Text3 } from "ink";
78
+
79
+ // src/hooks/use-terminal-size.ts
80
+ import { useEffect, useState } from "react";
81
+ import { useStdout } from "ink";
82
+ function useTerminalSize() {
83
+ const { stdout } = useStdout();
84
+ const [size, setSize] = useState(() => ({
85
+ columns: stdout?.columns ?? 80,
86
+ rows: stdout?.rows ?? 24
87
+ }));
88
+ useEffect(() => {
89
+ if (!stdout) return;
90
+ const onResize = () => {
91
+ setSize({
92
+ columns: stdout.columns ?? 80,
93
+ rows: stdout.rows ?? 24
94
+ });
95
+ };
96
+ stdout.on("resize", onResize);
97
+ return () => {
98
+ stdout.off("resize", onResize);
99
+ };
100
+ }, [stdout]);
101
+ return size;
102
+ }
78
103
 
79
104
  // src/stores/navigation-store.ts
80
105
  import { create } from "zustand";
@@ -297,7 +322,7 @@ var GRADIENTS = {
297
322
  };
298
323
 
299
324
  // src/components/common/blinking-text.tsx
300
- import { useEffect, useState } from "react";
325
+ import { useEffect as useEffect2, useState as useState2 } from "react";
301
326
  import { Text as Text2 } from "ink";
302
327
  import { jsx as jsx2 } from "react/jsx-runtime";
303
328
  function BlinkingText({
@@ -306,8 +331,8 @@ function BlinkingText({
306
331
  bold = true,
307
332
  children
308
333
  }) {
309
- const [bright, setBright] = useState(true);
310
- useEffect(() => {
334
+ const [bright, setBright] = useState2(true);
335
+ useEffect2(() => {
311
336
  const id = setInterval(() => setBright((b) => !b), intervalMs);
312
337
  return () => clearInterval(id);
313
338
  }, [intervalMs]);
@@ -389,9 +414,11 @@ function Header() {
389
414
  const menuMode = useNavigationStore((s) => s.menuMode);
390
415
  const menuCursor = useNavigationStore((s) => s.menuCursor);
391
416
  useLocaleStore((s) => s.locale);
392
- const { stdout } = useStdout();
393
- const cols = stdout?.columns ?? 80;
394
- const isNarrow = cols < 95;
417
+ const { columns, rows } = useTerminalSize();
418
+ const isNarrow = columns < 95;
419
+ const hideLogoByWidth = columns < 45;
420
+ const hideLogoByHeight = rows < 32;
421
+ const collapseMenu = rows < 22 && !menuMode;
395
422
  const cursorView = menuMode ? MENU_VIEWS[menuCursor] ?? null : null;
396
423
  const logoBlock = /* @__PURE__ */ jsx3(Box, { flexDirection: "column", flexShrink: 0, children: LOGO_BREW.map((brew, i) => /* @__PURE__ */ jsxs(Box, { children: [
397
424
  /* @__PURE__ */ jsx3(GradientText, { colors: GRADIENTS.gold, children: brew }),
@@ -412,12 +439,30 @@ function Header() {
412
439
  t("hint_menuOpen_suffix")
413
440
  ] }) })
414
441
  ] });
442
+ if (collapseMenu) {
443
+ const currentLabel = t(VIEW_LABEL_KEYS[currentView]);
444
+ const currentIsPro = isProView(currentView) || isTeamView(currentView);
445
+ return /* @__PURE__ */ jsxs(Box, { paddingX: SPACING.xs, children: [
446
+ /* @__PURE__ */ jsx3(Text3, { color: COLORS.success, bold: true, children: "\u25B6 " }),
447
+ /* @__PURE__ */ jsx3(Text3, { color: COLORS.success, bold: true, children: currentLabel }),
448
+ currentIsPro && /* @__PURE__ */ jsxs(Text3, { color: COLORS.brand, bold: true, children: [
449
+ " ",
450
+ t("pro_badge")
451
+ ] }),
452
+ /* @__PURE__ */ jsx3(Text3, { color: COLORS.textSecondary, children: " " }),
453
+ /* @__PURE__ */ jsx3(BlinkingText, { color: COLORS.brand, children: "M" }),
454
+ /* @__PURE__ */ jsx3(Text3, { color: COLORS.textSecondary, children: t("hint_menuOpen_suffix") })
455
+ ] });
456
+ }
415
457
  if (isNarrow) {
416
458
  return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", paddingX: SPACING.xs, children: [
417
- logoBlock,
418
- /* @__PURE__ */ jsx3(Box, { marginTop: SPACING.xs, children: menuBlock })
459
+ !hideLogoByWidth && !hideLogoByHeight && logoBlock,
460
+ /* @__PURE__ */ jsx3(Box, { marginTop: hideLogoByWidth || hideLogoByHeight ? SPACING.none : SPACING.xs, children: menuBlock })
419
461
  ] });
420
462
  }
463
+ if (hideLogoByHeight) {
464
+ return /* @__PURE__ */ jsx3(Box, { flexDirection: "column", paddingX: SPACING.xs, children: menuBlock });
465
+ }
421
466
  return /* @__PURE__ */ jsxs(Box, { flexDirection: "row", paddingX: SPACING.xs, alignItems: "center", children: [
422
467
  logoBlock,
423
468
  /* @__PURE__ */ jsx3(Box, { marginLeft: SPACING.sm, children: menuBlock })
@@ -460,8 +505,9 @@ function Footer() {
460
505
  const currentView = useNavigationStore((s) => s.currentView);
461
506
  const menuMode = useNavigationStore((s) => s.menuMode);
462
507
  const locale = useLocaleStore((s) => s.locale);
508
+ const { rows } = useTerminalSize();
463
509
  const defs = VIEW_HINT_DEFS[currentView] ?? [];
464
- const showChoose = hasNumberedActions(defs) && !menuMode;
510
+ const showChoose = hasNumberedActions(defs) && !menuMode && rows >= 26;
465
511
  return /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", children: [
466
512
  showChoose && /* @__PURE__ */ jsx4(Box2, { paddingX: SPACING.xs, children: /* @__PURE__ */ jsx4(Text4, { color: COLORS.text, children: t("hint_chooseNumber") }) }),
467
513
  /* @__PURE__ */ jsxs2(Box2, { borderStyle: "single", borderTop: true, borderBottom: false, borderLeft: false, borderRight: false, borderColor: COLORS.gold, paddingX: SPACING.xs, flexWrap: "wrap", children: [
@@ -506,31 +552,6 @@ function Footer() {
506
552
  ] });
507
553
  }
508
554
 
509
- // src/hooks/use-terminal-size.ts
510
- import { useEffect as useEffect2, useState as useState2 } from "react";
511
- import { useStdout as useStdout2 } from "ink";
512
- function useTerminalSize() {
513
- const { stdout } = useStdout2();
514
- const [size, setSize] = useState2(() => ({
515
- columns: stdout?.columns ?? 80,
516
- rows: stdout?.rows ?? 24
517
- }));
518
- useEffect2(() => {
519
- if (!stdout) return;
520
- const onResize = () => {
521
- setSize({
522
- columns: stdout.columns ?? 80,
523
- rows: stdout.rows ?? 24
524
- });
525
- };
526
- stdout.on("resize", onResize);
527
- return () => {
528
- stdout.off("resize", onResize);
529
- };
530
- }, [stdout]);
531
- return size;
532
- }
533
-
534
555
  // src/hooks/use-container-size.ts
535
556
  import { useEffect as useEffect3, useState as useState3 } from "react";
536
557
  import { measureElement } from "ink";
@@ -837,12 +858,12 @@ function WelcomeView({ onContinue }) {
837
858
  void markOnboardingComplete().finally(onContinue);
838
859
  }
839
860
  });
840
- return /* @__PURE__ */ jsxs4(Box4, { flexDirection: "column", paddingY: SPACING.md, paddingX: SPACING.lg, children: [
861
+ return /* @__PURE__ */ jsxs4(Box4, { flexDirection: "column", paddingY: SPACING.xs, paddingX: SPACING.sm, flexShrink: 1, children: [
841
862
  /* @__PURE__ */ jsx7(Box4, { children: /* @__PURE__ */ jsx7(GradientText, { colors: GRADIENTS.gold, bold: true, children: t("welcome_title") }) }),
842
- /* @__PURE__ */ jsx7(Box4, { marginTop: SPACING.sm, children: /* @__PURE__ */ jsx7(Text5, { color: COLORS.text, children: t("welcome_intro") }) }),
843
- /* @__PURE__ */ jsxs4(Box4, { flexDirection: "column", marginTop: SPACING.sm, children: [
863
+ /* @__PURE__ */ jsx7(Box4, { marginTop: SPACING.xs, children: /* @__PURE__ */ jsx7(Text5, { color: COLORS.text, wrap: "wrap", children: t("welcome_intro") }) }),
864
+ /* @__PURE__ */ jsxs4(Box4, { flexDirection: "column", marginTop: SPACING.xs, children: [
844
865
  /* @__PURE__ */ jsx7(Text5, { color: COLORS.muted, children: t("welcome_keysHeader") }),
845
- /* @__PURE__ */ jsxs4(Box4, { flexDirection: "column", paddingLeft: SPACING.sm, marginTop: SPACING.xs, children: [
866
+ /* @__PURE__ */ jsxs4(Box4, { flexDirection: "column", paddingLeft: SPACING.sm, children: [
846
867
  /* @__PURE__ */ jsxs4(Text5, { children: [
847
868
  /* @__PURE__ */ jsx7(Text5, { color: COLORS.gold, bold: true, children: "m" }),
848
869
  " ",
@@ -885,11 +906,11 @@ function WelcomeView({ onContinue }) {
885
906
  ] })
886
907
  ] })
887
908
  ] }),
888
- /* @__PURE__ */ jsxs4(Box4, { flexDirection: "column", marginTop: SPACING.sm, children: [
909
+ /* @__PURE__ */ jsxs4(Box4, { flexDirection: "column", marginTop: SPACING.xs, children: [
889
910
  /* @__PURE__ */ jsx7(Text5, { color: COLORS.muted, children: t("welcome_proHeader") }),
890
- /* @__PURE__ */ jsx7(Box4, { paddingLeft: SPACING.sm, children: /* @__PURE__ */ jsx7(Text5, { color: COLORS.textSecondary, children: t("welcome_proIntro") }) })
911
+ /* @__PURE__ */ jsx7(Box4, { paddingLeft: SPACING.sm, children: /* @__PURE__ */ jsx7(Text5, { color: COLORS.textSecondary, wrap: "wrap", children: t("welcome_proIntro") }) })
891
912
  ] }),
892
- /* @__PURE__ */ jsx7(Box4, { marginTop: SPACING.md, children: /* @__PURE__ */ jsx7(Text5, { color: COLORS.success, bold: true, children: t("welcome_continueHint") }) })
913
+ /* @__PURE__ */ jsx7(Box4, { marginTop: SPACING.xs, children: /* @__PURE__ */ jsx7(Text5, { color: COLORS.success, bold: true, children: t("welcome_continueHint") }) })
893
914
  ] });
894
915
  }
895
916
 
@@ -915,15 +936,16 @@ function UpgradePrompt({ viewId }) {
915
936
  const pricingKey = team ? "upgrade_teamPricing" : "upgrade_pricing";
916
937
  const buyUrlKey = team ? "upgrade_buyUrlTeam" : "upgrade_buyUrl";
917
938
  const labelKey = team ? "upgrade_teamLabel" : "upgrade_proLabel";
918
- return /* @__PURE__ */ jsx8(Box5, { flexDirection: "column", alignItems: "center", paddingY: SPACING.sm, children: /* @__PURE__ */ jsxs5(
939
+ return /* @__PURE__ */ jsx8(Box5, { flexDirection: "column", alignItems: "center", paddingY: SPACING.xs, children: /* @__PURE__ */ jsxs5(
919
940
  Box5,
920
941
  {
921
942
  borderStyle: "double",
922
943
  borderColor: COLORS.brand,
923
- paddingX: SPACING.md,
924
- paddingY: SPACING.sm,
944
+ paddingX: SPACING.sm,
945
+ paddingY: SPACING.none,
925
946
  flexDirection: "column",
926
947
  alignItems: "center",
948
+ flexShrink: 1,
927
949
  width: "80%",
928
950
  children: [
929
951
  /* @__PURE__ */ jsxs5(Text6, { bold: true, color: COLORS.brand, children: [
@@ -931,24 +953,13 @@ function UpgradePrompt({ viewId }) {
931
953
  " ",
932
954
  t(headerKey, { title })
933
955
  ] }),
934
- /* @__PURE__ */ jsx8(Text6, { children: " " }),
935
956
  /* @__PURE__ */ jsx8(Text6, { color: COLORS.text, wrap: "wrap", children: t(keys.desc) }),
936
- /* @__PURE__ */ jsx8(Text6, { children: " " }),
937
- /* @__PURE__ */ jsxs5(Box5, { flexDirection: "column", alignItems: "center", children: [
957
+ /* @__PURE__ */ jsxs5(Box5, { flexDirection: "column", alignItems: "center", marginTop: SPACING.xs, children: [
938
958
  /* @__PURE__ */ jsx8(Text6, { color: COLORS.info, bold: true, children: t(pricingKey) }),
939
- /* @__PURE__ */ jsx8(Text6, { children: " " }),
940
959
  /* @__PURE__ */ jsx8(Text6, { color: COLORS.muted, children: t("upgrade_buyAt") }),
941
- /* @__PURE__ */ jsxs5(Text6, { color: COLORS.sky, bold: true, children: [
942
- " ",
943
- t(buyUrlKey)
944
- ] }),
945
- /* @__PURE__ */ jsx8(Text6, { children: " " }),
960
+ /* @__PURE__ */ jsx8(Text6, { color: COLORS.sky, bold: true, children: t(buyUrlKey) }),
946
961
  /* @__PURE__ */ jsx8(Text6, { color: COLORS.muted, children: t("upgrade_activateWith") }),
947
- /* @__PURE__ */ jsxs5(Text6, { color: COLORS.success, bold: true, children: [
948
- " ",
949
- t("upgrade_activateCmd")
950
- ] }),
951
- /* @__PURE__ */ jsx8(Text6, { children: " " }),
962
+ /* @__PURE__ */ jsx8(Text6, { color: COLORS.success, bold: true, children: t("upgrade_activateCmd") }),
952
963
  /* @__PURE__ */ jsx8(Text6, { color: COLORS.brand, children: t(labelKey) })
953
964
  ] })
954
965
  ]
@@ -958,7 +969,19 @@ function UpgradePrompt({ viewId }) {
958
969
 
959
970
  // src/views/dashboard.tsx
960
971
  import { useEffect as useEffect5, useMemo as useMemo2 } from "react";
961
- import { Box as Box9, Text as Text12, useStdout as useStdout4 } from "ink";
972
+ import { Box as Box9, Text as Text12 } from "ink";
973
+
974
+ // src/hooks/use-visible-rows.ts
975
+ function useVisibleRows({
976
+ reservedRows,
977
+ fallbackReservedRows = reservedRows,
978
+ minRows = 3
979
+ }) {
980
+ const { height: contentHeight } = useContentSize();
981
+ const { rows: terminalRows } = useTerminalSize();
982
+ const availableRows = contentHeight > 0 ? contentHeight - reservedRows : terminalRows - fallbackReservedRows;
983
+ return Math.max(minRows, Math.floor(availableRows));
984
+ }
962
985
 
963
986
  // src/stores/brew-store.ts
964
987
  import { create as create4 } from "zustand";
@@ -1924,12 +1947,11 @@ var useComplianceStore = create8((set, get) => ({
1924
1947
  }));
1925
1948
 
1926
1949
  // src/components/common/stat-card.tsx
1927
- import { Box as Box6, Text as Text7, useStdout as useStdout3 } from "ink";
1950
+ import { Box as Box6, Text as Text7 } from "ink";
1928
1951
  import { jsx as jsx9, jsxs as jsxs6 } from "react/jsx-runtime";
1929
1952
  function StatCard({ label, value, color = COLORS.white }) {
1930
- const { stdout } = useStdout3();
1931
- const cols = stdout?.columns ?? 80;
1932
- const minW = cols < 60 ? 12 : cols < 100 ? 14 : 16;
1953
+ const { columns } = useTerminalSize();
1954
+ const minW = columns < 60 ? 12 : columns < 100 ? 14 : 16;
1933
1955
  return /* @__PURE__ */ jsxs6(
1934
1956
  Box6,
1935
1957
  {
@@ -1939,6 +1961,7 @@ function StatCard({ label, value, color = COLORS.white }) {
1939
1961
  paddingY: SPACING.none,
1940
1962
  flexDirection: "column",
1941
1963
  alignItems: "center",
1964
+ flexShrink: 1,
1942
1965
  minWidth: minW,
1943
1966
  children: [
1944
1967
  /* @__PURE__ */ jsx9(Text7, { bold: true, color, children: value }),
@@ -2103,8 +2126,13 @@ function DashboardView() {
2103
2126
  const lastFetchedAt = useBrewStore((s) => s.lastFetchedAt);
2104
2127
  const fetchAll = useBrewStore((s) => s.fetchAll);
2105
2128
  const isPro = useLicenseStore((s) => s.isPro);
2106
- const { stdout } = useStdout4();
2107
- const columns = stdout?.columns ?? 80;
2129
+ const { columns } = useTerminalSize();
2130
+ const splitRows = useVisibleRows({
2131
+ reservedRows: 18,
2132
+ fallbackReservedRows: 22,
2133
+ minRows: 2
2134
+ });
2135
+ const halfRows = Math.max(1, Math.floor(splitRows / 2));
2108
2136
  useEffect5(() => {
2109
2137
  fetchAll();
2110
2138
  }, []);
@@ -2195,20 +2223,23 @@ function DashboardView() {
2195
2223
  !errors.outdated && outdated.formulae.length > 0 && /* @__PURE__ */ jsxs11(Box9, { flexDirection: "column", marginTop: SPACING.xs, children: [
2196
2224
  /* @__PURE__ */ jsx13(SectionHeader, { emoji: "\u{1F4E6}", title: t("dashboard_outdatedPackages"), gradient: GRADIENTS.fire }),
2197
2225
  /* @__PURE__ */ jsxs11(Box9, { paddingLeft: SPACING.sm, flexDirection: "column", children: [
2198
- outdated.formulae.slice(0, 10).map((pkg) => /* @__PURE__ */ jsxs11(Box9, { gap: SPACING.xs, children: [
2226
+ outdated.formulae.slice(0, halfRows).map((pkg) => /* @__PURE__ */ jsxs11(Box9, { gap: SPACING.xs, children: [
2199
2227
  /* @__PURE__ */ jsx13(Text12, { color: COLORS.text, children: pkg.name }),
2200
2228
  /* @__PURE__ */ jsx13(VersionArrow, { current: pkg.installed_versions[0] ?? "", latest: pkg.current_version })
2201
2229
  ] }, pkg.name)),
2202
- outdated.formulae.length > 10 && /* @__PURE__ */ jsx13(Text12, { color: COLORS.textSecondary, italic: true, children: t("common_andMore", { count: outdated.formulae.length - 10 }) })
2230
+ outdated.formulae.length > halfRows && /* @__PURE__ */ jsx13(Text12, { color: COLORS.textSecondary, italic: true, children: t("common_andMore", { count: outdated.formulae.length - halfRows }) })
2203
2231
  ] })
2204
2232
  ] }),
2205
2233
  !errors.services && errorServices > 0 && /* @__PURE__ */ jsxs11(Box9, { flexDirection: "column", marginTop: SPACING.xs, children: [
2206
2234
  /* @__PURE__ */ jsx13(SectionHeader, { emoji: "\u26A0\uFE0F", title: t("dashboard_serviceErrors"), color: COLORS.error }),
2207
- /* @__PURE__ */ jsx13(Box9, { paddingLeft: SPACING.sm, flexDirection: "column", children: errorServiceList.map((s) => /* @__PURE__ */ jsxs11(Box9, { gap: SPACING.xs, children: [
2208
- /* @__PURE__ */ jsx13(StatusBadge, { label: t("badge_error"), variant: "error" }),
2209
- /* @__PURE__ */ jsx13(Text12, { children: s.name }),
2210
- s.exit_code != null && /* @__PURE__ */ jsx13(Text12, { color: COLORS.muted, children: t("common_exit", { code: s.exit_code }) })
2211
- ] }, s.name)) })
2235
+ /* @__PURE__ */ jsxs11(Box9, { paddingLeft: SPACING.sm, flexDirection: "column", children: [
2236
+ errorServiceList.slice(0, halfRows).map((s) => /* @__PURE__ */ jsxs11(Box9, { gap: SPACING.xs, children: [
2237
+ /* @__PURE__ */ jsx13(StatusBadge, { label: t("badge_error"), variant: "error" }),
2238
+ /* @__PURE__ */ jsx13(Text12, { children: s.name }),
2239
+ s.exit_code != null && /* @__PURE__ */ jsx13(Text12, { color: COLORS.muted, children: t("common_exit", { code: s.exit_code }) })
2240
+ ] }, s.name)),
2241
+ errorServiceList.length > halfRows && /* @__PURE__ */ jsx13(Text12, { color: COLORS.textSecondary, italic: true, children: t("common_andMore", { count: errorServiceList.length - halfRows }) })
2242
+ ] })
2212
2243
  ] }),
2213
2244
  isPro() && /* @__PURE__ */ jsx13(ProStatusPanel, {})
2214
2245
  ] });
@@ -2347,8 +2378,8 @@ function ConfirmDialog({ message, onConfirm, onCancel }) {
2347
2378
  else if (input === "n" || input === "N") onCancel();
2348
2379
  else if (key.escape) onCancel();
2349
2380
  });
2350
- return /* @__PURE__ */ jsxs13(Box11, { borderStyle: "double", borderColor: COLORS.purple, paddingX: SPACING.sm, paddingY: SPACING.xs, flexDirection: "column", children: [
2351
- /* @__PURE__ */ jsx15(Text14, { bold: true, color: COLORS.text, children: message }),
2381
+ return /* @__PURE__ */ jsxs13(Box11, { borderStyle: "double", borderColor: COLORS.purple, paddingX: SPACING.sm, paddingY: SPACING.xs, flexDirection: "column", flexShrink: 1, children: [
2382
+ /* @__PURE__ */ jsx15(Text14, { bold: true, color: COLORS.text, wrap: "wrap", children: message }),
2352
2383
  /* @__PURE__ */ jsxs13(Box11, { marginTop: SPACING.xs, children: [
2353
2384
  /* @__PURE__ */ jsx15(Text14, { color: COLORS.success, children: t("confirm_yes") }),
2354
2385
  /* @__PURE__ */ jsx15(Text14, { children: " / " }),
@@ -2405,18 +2436,6 @@ function SelectableRow({ isCurrent, children, gap = 1 }) {
2405
2436
  ] });
2406
2437
  }
2407
2438
 
2408
- // src/hooks/use-visible-rows.ts
2409
- function useVisibleRows({
2410
- reservedRows,
2411
- fallbackReservedRows = reservedRows,
2412
- minRows = 3
2413
- }) {
2414
- const { height: contentHeight } = useContentSize();
2415
- const { rows: terminalRows } = useTerminalSize();
2416
- const availableRows = contentHeight > 0 ? contentHeight - reservedRows : terminalRows - fallbackReservedRows;
2417
- return Math.max(minRows, Math.floor(availableRows));
2418
- }
2419
-
2420
2439
  // src/views/installed.tsx
2421
2440
  import { jsx as jsx19, jsxs as jsxs16 } from "react/jsx-runtime";
2422
2441
  function InstalledView() {
@@ -2431,8 +2450,9 @@ function InstalledView() {
2431
2450
  const containerRef = useRef3(null);
2432
2451
  const { width: containerWidth } = useContainerSize(containerRef);
2433
2452
  const columns = containerWidth > 0 ? containerWidth : 80;
2434
- const nameWidth = Math.floor(columns * 0.35);
2435
- const versionWidth = Math.floor(columns * 0.15);
2453
+ const nameWidth = Math.max(12, Math.floor(columns * 0.35));
2454
+ const versionWidth = Math.max(8, Math.floor(columns * 0.15));
2455
+ const descWidth = Math.max(10, columns - nameWidth - versionWidth - 8);
2436
2456
  const [filter, setFilter] = useState6("");
2437
2457
  const [cursor, setCursor] = useState6(0);
2438
2458
  const [tab, setTab] = useState6("formulae");
@@ -2617,7 +2637,7 @@ function InstalledView() {
2617
2637
  item.pinned && /* @__PURE__ */ jsx19(StatusBadge, { label: t("badge_pinned"), variant: "info" }),
2618
2638
  item.kegOnly && /* @__PURE__ */ jsx19(StatusBadge, { label: t("badge_kegOnly"), variant: "muted" }),
2619
2639
  item.installedAsDependency && /* @__PURE__ */ jsx19(StatusBadge, { label: t("badge_dep"), variant: "muted" }),
2620
- !item.outdated && !item.pinned && !item.kegOnly && !item.installedAsDependency && /* @__PURE__ */ jsx19(Text18, { color: COLORS.textSecondary, dimColor: true, children: truncate(item.desc, 30) })
2640
+ !item.outdated && !item.pinned && !item.kegOnly && !item.installedAsDependency && /* @__PURE__ */ jsx19(Text18, { color: COLORS.textSecondary, dimColor: true, children: truncate(item.desc, descWidth) })
2621
2641
  ] }, item.name);
2622
2642
  }),
2623
2643
  start + MAX_VISIBLE_ROWS < allItems.length && /* @__PURE__ */ jsxs16(Text18, { color: COLORS.textSecondary, dimColor: true, children: [
@@ -3259,11 +3279,14 @@ function PackageInfoView() {
3259
3279
  ] }),
3260
3280
  formula.dependencies.length > 0 && /* @__PURE__ */ jsxs19(Box18, { flexDirection: "column", children: [
3261
3281
  /* @__PURE__ */ jsx22(SectionHeader, { emoji: "\u{1F517}", title: t("pkgInfo_dependencies", { count: formula.dependencies.length }), gradient: GRADIENTS.ocean }),
3262
- /* @__PURE__ */ jsx22(Box18, { paddingLeft: SPACING.sm, flexWrap: "wrap", columnGap: 2, children: formula.dependencies.map((dep) => /* @__PURE__ */ jsx22(Text21, { color: COLORS.muted, children: dep }, dep)) })
3282
+ /* @__PURE__ */ jsxs19(Box18, { paddingLeft: SPACING.sm, flexWrap: "wrap", columnGap: 2, children: [
3283
+ formula.dependencies.slice(0, 30).map((dep) => /* @__PURE__ */ jsx22(Text21, { color: COLORS.muted, children: dep }, dep)),
3284
+ formula.dependencies.length > 30 && /* @__PURE__ */ jsx22(Text21, { color: COLORS.textSecondary, italic: true, children: t("common_andMore", { count: formula.dependencies.length - 30 }) })
3285
+ ] })
3263
3286
  ] }),
3264
3287
  formula.caveats && /* @__PURE__ */ jsxs19(Box18, { flexDirection: "column", children: [
3265
3288
  /* @__PURE__ */ jsx22(SectionHeader, { emoji: "\u26A0\uFE0F", title: t("pkgInfo_caveats"), color: COLORS.warning }),
3266
- /* @__PURE__ */ jsx22(Box18, { borderStyle: "round", borderColor: COLORS.warning, paddingX: SPACING.sm, children: /* @__PURE__ */ jsx22(Text21, { color: COLORS.warning, children: formula.caveats }) })
3289
+ /* @__PURE__ */ jsx22(Box18, { borderStyle: "round", borderColor: COLORS.warning, paddingX: SPACING.sm, children: /* @__PURE__ */ jsx22(Text21, { color: COLORS.warning, wrap: "wrap", children: formula.caveats }) })
3267
3290
  ] })
3268
3291
  ] }),
3269
3292
  /* @__PURE__ */ jsx22(Box18, { marginTop: SPACING.xs, children: /* @__PURE__ */ jsxs19(Text21, { color: COLORS.textSecondary, children: [
@@ -3275,8 +3298,8 @@ function PackageInfoView() {
3275
3298
  }
3276
3299
 
3277
3300
  // src/views/services.tsx
3278
- import { useEffect as useEffect13, useState as useState10 } from "react";
3279
- import { Box as Box19, Text as Text22, useStdout as useStdout5 } from "ink";
3301
+ import { useEffect as useEffect13, useRef as useRef7, useState as useState10 } from "react";
3302
+ import { Box as Box19, Text as Text22 } from "ink";
3280
3303
  import { jsx as jsx23, jsxs as jsxs20 } from "react/jsx-runtime";
3281
3304
  var STATUS_VARIANTS = {
3282
3305
  started: "success",
@@ -3296,10 +3319,11 @@ function ServicesView() {
3296
3319
  const [actionInProgress, setActionInProgress] = useState10(false);
3297
3320
  const [confirmAction, setConfirmAction] = useState10(null);
3298
3321
  const [lastError, setLastError] = useState10(null);
3299
- const { stdout } = useStdout5();
3300
- const cols = stdout?.columns ?? 80;
3301
- const svcNameWidth = Math.floor(cols * 0.35);
3302
- const svcStatusWidth = Math.floor(cols * 0.15);
3322
+ const containerRef = useRef7(null);
3323
+ const { width: containerWidth } = useContainerSize(containerRef);
3324
+ const cols = containerWidth > 0 ? containerWidth : 80;
3325
+ const svcNameWidth = Math.max(12, Math.floor(cols * 0.35));
3326
+ const svcStatusWidth = Math.max(8, Math.floor(cols * 0.15));
3303
3327
  const MAX_VISIBLE_ROWS = useVisibleRows({
3304
3328
  reservedRows: lastError || actionInProgress ? 8 : 6,
3305
3329
  fallbackReservedRows: lastError || actionInProgress ? 16 : 14,
@@ -3347,7 +3371,7 @@ function ServicesView() {
3347
3371
  }
3348
3372
  const start = Math.max(0, cursor - Math.floor(MAX_VISIBLE_ROWS / 2));
3349
3373
  const visible = services.slice(start, start + MAX_VISIBLE_ROWS);
3350
- return /* @__PURE__ */ jsxs20(Box19, { flexDirection: "column", children: [
3374
+ return /* @__PURE__ */ jsxs20(Box19, { flexDirection: "column", ref: containerRef, children: [
3351
3375
  /* @__PURE__ */ jsx23(SectionHeader, { emoji: "\u2699\uFE0F", title: t("services_titleCount", { count: services.length }), gradient: GRADIENTS.ocean }),
3352
3376
  confirmAction && /* @__PURE__ */ jsx23(Box19, { marginY: SPACING.xs, children: /* @__PURE__ */ jsx23(
3353
3377
  ConfirmDialog,
@@ -3385,7 +3409,7 @@ function ServicesView() {
3385
3409
  const idx = start + i;
3386
3410
  const isCurrent = idx === cursor;
3387
3411
  return /* @__PURE__ */ jsxs20(SelectableRow, { isCurrent, children: [
3388
- /* @__PURE__ */ jsx23(Text22, { bold: isCurrent, inverse: isCurrent, color: isCurrent ? COLORS.text : COLORS.muted, children: svc.name.padEnd(svcNameWidth - 2) }),
3412
+ /* @__PURE__ */ jsx23(Text22, { bold: isCurrent, inverse: isCurrent, color: isCurrent ? COLORS.text : COLORS.muted, children: truncate(svc.name, svcNameWidth - 2).padEnd(svcNameWidth - 2) }),
3389
3413
  /* @__PURE__ */ jsx23(StatusBadge, { label: svc.status, variant: STATUS_VARIANTS[svc.status] }),
3390
3414
  /* @__PURE__ */ jsx23(Text22, { color: COLORS.muted, children: svc.user ?? "-" }),
3391
3415
  svc.exit_code != null && svc.exit_code !== 0 && /* @__PURE__ */ jsx23(Text22, { color: COLORS.error, children: t("common_exit", { code: svc.exit_code }) })
@@ -3407,12 +3431,18 @@ function ServicesView() {
3407
3431
  }
3408
3432
 
3409
3433
  // src/views/doctor.tsx
3410
- import { useEffect as useEffect14, useRef as useRef7 } from "react";
3434
+ import { useEffect as useEffect14, useRef as useRef8, useState as useState11 } from "react";
3411
3435
  import { Box as Box20, Text as Text23 } from "ink";
3412
3436
  import { jsx as jsx24, jsxs as jsxs21 } from "react/jsx-runtime";
3413
3437
  function DoctorView() {
3414
3438
  const { doctorWarnings, doctorClean, loading, errors, fetchDoctor } = useBrewStore();
3415
- const mountedRef = useRef7(true);
3439
+ const [cursor, setCursor] = useState11(0);
3440
+ const visibleWarnings = useVisibleRows({
3441
+ reservedRows: 6,
3442
+ fallbackReservedRows: 14,
3443
+ minRows: 1
3444
+ });
3445
+ const mountedRef = useRef8(true);
3416
3446
  useEffect14(() => {
3417
3447
  mountedRef.current = true;
3418
3448
  return () => {
@@ -3422,27 +3452,42 @@ function DoctorView() {
3422
3452
  useEffect14(() => {
3423
3453
  fetchDoctor();
3424
3454
  }, []);
3425
- useViewInput((input) => {
3426
- if (input === "r" || input === "1") void fetchDoctor();
3455
+ useViewInput((input, key) => {
3456
+ if (input === "r" || input === "1") {
3457
+ void fetchDoctor();
3458
+ return;
3459
+ }
3460
+ if (input === "j" || key.downArrow) setCursor((c) => Math.min(c + 1, Math.max(0, doctorWarnings.length - 1)));
3461
+ else if (input === "k" || key.upArrow) setCursor((c) => Math.max(c - 1, 0));
3427
3462
  });
3428
3463
  if (loading.doctor) return /* @__PURE__ */ jsx24(Loading, { message: t("loading_doctor") });
3429
3464
  if (errors.doctor) return /* @__PURE__ */ jsx24(ErrorMessage, { message: errors.doctor });
3465
+ const start = Math.max(0, cursor - Math.floor(visibleWarnings / 2));
3466
+ const visible = doctorWarnings.slice(start, start + visibleWarnings);
3430
3467
  return /* @__PURE__ */ jsxs21(Box20, { flexDirection: "column", children: [
3431
3468
  /* @__PURE__ */ jsx24(SectionHeader, { emoji: "\u{1FA7A}", title: t("doctor_title"), gradient: GRADIENTS.emerald }),
3432
3469
  /* @__PURE__ */ jsxs21(Box20, { flexDirection: "column", marginTop: SPACING.xs, children: [
3433
3470
  doctorClean && /* @__PURE__ */ jsx24(ResultBanner, { status: "success", message: `\u2714 ${t("doctor_clean")}` }),
3434
3471
  doctorClean === false && doctorWarnings.length === 0 && /* @__PURE__ */ jsx24(Text23, { color: COLORS.warning, children: t("doctor_warningsNotCaptured") }),
3435
- doctorWarnings.map((warning, i) => (
3436
- // FE-004: Improved React key
3437
- /* @__PURE__ */ jsx24(Box20, { flexDirection: "column", marginBottom: SPACING.xs, borderStyle: "single", borderColor: COLORS.warning, paddingX: SPACING.xs, children: warning.split("\n").map((line, j) => /* @__PURE__ */ jsx24(Text23, { color: j === 0 ? COLORS.warning : COLORS.muted, children: line }, `warning-${i}-${j}-${line.slice(0, 20)}`)) }, `warning-${i}-${warning.slice(0, 20)}`)
3438
- ))
3472
+ start > 0 && /* @__PURE__ */ jsxs21(Text23, { color: COLORS.textSecondary, dimColor: true, children: [
3473
+ " ",
3474
+ t("scroll_moreAbove", { count: start })
3475
+ ] }),
3476
+ visible.map((warning, i) => {
3477
+ const idx = start + i;
3478
+ return /* @__PURE__ */ jsx24(Box20, { flexDirection: "column", marginBottom: SPACING.xs, borderStyle: "single", borderColor: idx === cursor ? COLORS.gold : COLORS.warning, paddingX: SPACING.xs, children: warning.split("\n").map((line, j) => /* @__PURE__ */ jsx24(Text23, { color: j === 0 ? COLORS.warning : COLORS.muted, wrap: "wrap", children: line }, `warning-${idx}-${j}-${line.slice(0, 20)}`)) }, `warning-${idx}-${warning.slice(0, 20)}`);
3479
+ }),
3480
+ start + visibleWarnings < doctorWarnings.length && /* @__PURE__ */ jsxs21(Text23, { color: COLORS.textSecondary, dimColor: true, children: [
3481
+ " ",
3482
+ t("scroll_moreBelow", { count: doctorWarnings.length - start - visibleWarnings })
3483
+ ] })
3439
3484
  ] }),
3440
3485
  /* @__PURE__ */ jsx24(Box20, { marginTop: SPACING.xs, children: /* @__PURE__ */ jsx24(Text23, { color: COLORS.text, bold: true, children: doctorWarnings.length > 0 ? tp("plural_warnings", doctorWarnings.length) : "" }) })
3441
3486
  ] });
3442
3487
  }
3443
3488
 
3444
3489
  // src/views/profiles.tsx
3445
- import { useEffect as useEffect15, useRef as useRef8, useState as useState11 } from "react";
3490
+ import { useEffect as useEffect15, useRef as useRef9, useState as useState12 } from "react";
3446
3491
  import { Box as Box25 } from "ink";
3447
3492
 
3448
3493
  // src/stores/profile-store.ts
@@ -3705,18 +3750,33 @@ function ProfileListMode({ profileNames, cursor, confirmDelete, loadError, onCon
3705
3750
  import { Box as Box22, Text as Text25 } from "ink";
3706
3751
  import { jsx as jsx26, jsxs as jsxs23 } from "react/jsx-runtime";
3707
3752
  function ProfileDetailMode({ profile }) {
3753
+ const totalRows = useVisibleRows({
3754
+ reservedRows: 7,
3755
+ fallbackReservedRows: 14,
3756
+ minRows: 2
3757
+ });
3758
+ const total = profile.formulae.length + profile.casks.length;
3759
+ const formulaeBudget = total === 0 ? 0 : Math.min(profile.formulae.length, Math.max(1, Math.round(profile.formulae.length / total * totalRows)));
3760
+ const casksBudget = Math.max(0, totalRows - formulaeBudget);
3761
+ const visibleFormulae = profile.formulae.slice(0, formulaeBudget);
3762
+ const visibleCasks = profile.casks.slice(0, casksBudget);
3763
+ const formulaeHidden = profile.formulae.length - visibleFormulae.length;
3764
+ const casksHidden = profile.casks.length - visibleCasks.length;
3708
3765
  return /* @__PURE__ */ jsxs23(Box22, { flexDirection: "column", children: [
3709
3766
  /* @__PURE__ */ jsx26(Text25, { bold: true, color: COLORS.gold, children: profile.name }),
3710
- /* @__PURE__ */ jsx26(Text25, { color: COLORS.muted, children: profile.description }),
3767
+ /* @__PURE__ */ jsx26(Text25, { color: COLORS.muted, wrap: "wrap", children: profile.description }),
3711
3768
  /* @__PURE__ */ jsx26(Text25, { color: COLORS.muted, children: t("profiles_created", { date: formatDate(profile.createdAt) }) }),
3712
3769
  /* @__PURE__ */ jsxs23(Box22, { marginTop: SPACING.xs, flexDirection: "column", children: [
3713
3770
  /* @__PURE__ */ jsx26(Text25, { bold: true, children: t("profiles_formulaeCount", { count: profile.formulae.length }) }),
3714
3771
  /* @__PURE__ */ jsxs23(Box22, { paddingLeft: SPACING.sm, flexDirection: "column", children: [
3715
- profile.formulae.slice(0, 30).map((f) => /* @__PURE__ */ jsx26(Text25, { color: COLORS.muted, children: f }, f)),
3716
- profile.formulae.length > 30 && /* @__PURE__ */ jsx26(Text25, { color: COLORS.textSecondary, italic: true, children: t("common_andMore", { count: profile.formulae.length - 30 }) })
3772
+ visibleFormulae.map((f) => /* @__PURE__ */ jsx26(Text25, { color: COLORS.muted, children: f }, f)),
3773
+ formulaeHidden > 0 && /* @__PURE__ */ jsx26(Text25, { color: COLORS.textSecondary, italic: true, children: t("common_andMore", { count: formulaeHidden }) })
3717
3774
  ] }),
3718
3775
  /* @__PURE__ */ jsx26(Text25, { bold: true, children: t("profiles_casksCount", { count: profile.casks.length }) }),
3719
- /* @__PURE__ */ jsx26(Box22, { paddingLeft: SPACING.sm, flexDirection: "column", children: profile.casks.map((c) => /* @__PURE__ */ jsx26(Text25, { color: COLORS.muted, children: c }, c)) })
3776
+ /* @__PURE__ */ jsxs23(Box22, { paddingLeft: SPACING.sm, flexDirection: "column", children: [
3777
+ visibleCasks.map((c) => /* @__PURE__ */ jsx26(Text25, { color: COLORS.muted, children: c }, c)),
3778
+ casksHidden > 0 && /* @__PURE__ */ jsx26(Text25, { color: COLORS.textSecondary, italic: true, children: t("common_andMore", { count: casksHidden }) })
3779
+ ] })
3720
3780
  ] }),
3721
3781
  /* @__PURE__ */ jsx26(Box22, { marginTop: SPACING.xs, children: /* @__PURE__ */ jsxs23(Text25, { color: COLORS.textSecondary, children: [
3722
3782
  "esc:",
@@ -3799,19 +3859,19 @@ function ProfileEditDesc({ name, defaultDesc, loadError, onSubmit }) {
3799
3859
  import { jsx as jsx29, jsxs as jsxs26 } from "react/jsx-runtime";
3800
3860
  function ProfilesView() {
3801
3861
  const { profileNames, selectedProfile, loading, loadError, fetchProfiles, loadProfile: loadProfile2, exportCurrent, deleteProfile: deleteProfile2, updateProfile: updateProfile2 } = useProfileStore();
3802
- const [cursor, setCursor] = useState11(0);
3803
- const [mode, setMode] = useState11("list");
3804
- const [newName, setNewName] = useState11("");
3805
- const [confirmDelete, setConfirmDelete] = useState11(false);
3806
- const [editName, setEditName] = useState11("");
3807
- const [editDesc, setEditDesc] = useState11("");
3808
- const [importLines, setImportLines] = useState11([]);
3809
- const [importRunning, setImportRunning] = useState11(false);
3810
- const [importHadError, setImportHadError] = useState11(false);
3811
- const [importProfile2, setImportProfile] = useState11(null);
3862
+ const [cursor, setCursor] = useState12(0);
3863
+ const [mode, setMode] = useState12("list");
3864
+ const [newName, setNewName] = useState12("");
3865
+ const [confirmDelete, setConfirmDelete] = useState12(false);
3866
+ const [editName, setEditName] = useState12("");
3867
+ const [editDesc, setEditDesc] = useState12("");
3868
+ const [importLines, setImportLines] = useState12([]);
3869
+ const [importRunning, setImportRunning] = useState12(false);
3870
+ const [importHadError, setImportHadError] = useState12(false);
3871
+ const [importProfile2, setImportProfile] = useState12(null);
3812
3872
  const { openModal, closeModal } = useModalStore();
3813
- const importGenRef = useRef8(null);
3814
- const mountedRef = useRef8(true);
3873
+ const importGenRef = useRef9(null);
3874
+ const mountedRef = useRef9(true);
3815
3875
  useEffect15(() => {
3816
3876
  fetchProfiles();
3817
3877
  }, []);
@@ -3999,7 +4059,7 @@ function ProfilesView() {
3999
4059
  }
4000
4060
 
4001
4061
  // src/views/smart-cleanup.tsx
4002
- import { useEffect as useEffect16, useRef as useRef9, useState as useState12 } from "react";
4062
+ import { useEffect as useEffect16, useRef as useRef10, useState as useState13 } from "react";
4003
4063
  import { Box as Box26, Text as Text28 } from "ink";
4004
4064
 
4005
4065
  // src/stores/cleanup-store.ts
@@ -4123,12 +4183,17 @@ var useCleanupStore = create10((set, get) => ({
4123
4183
  import { jsx as jsx30, jsxs as jsxs27 } from "react/jsx-runtime";
4124
4184
  function SmartCleanupView() {
4125
4185
  const { summary, selected, loading, error, analyze, toggleSelect, selectAll } = useCleanupStore();
4126
- const [cursor, setCursor] = useState12(0);
4127
- const [confirmClean, setConfirmClean] = useState12(false);
4128
- const [confirmForce, setConfirmForce] = useState12(false);
4129
- const [failedNames, setFailedNames] = useState12([]);
4186
+ const [cursor, setCursor] = useState13(0);
4187
+ const [confirmClean, setConfirmClean] = useState13(false);
4188
+ const [confirmForce, setConfirmForce] = useState13(false);
4189
+ const [failedNames, setFailedNames] = useState13([]);
4130
4190
  const stream = useBrewStream();
4131
- const hasRefreshed = useRef9(false);
4191
+ const hasRefreshed = useRef10(false);
4192
+ const listRows = useVisibleRows({
4193
+ reservedRows: confirmClean || confirmForce ? 12 : 9,
4194
+ fallbackReservedRows: confirmClean || confirmForce ? 18 : 14,
4195
+ minRows: 1
4196
+ });
4132
4197
  useEffect16(() => {
4133
4198
  analyze();
4134
4199
  }, []);
@@ -4230,32 +4295,45 @@ function SmartCleanupView() {
4230
4295
  )
4231
4296
  ] }),
4232
4297
  candidates.length === 0 && !confirmClean && /* @__PURE__ */ jsx30(ResultBanner, { status: "success", message: `\u2714 ${t("cleanup_systemClean")}` }),
4233
- candidates.length > 0 && !confirmClean && /* @__PURE__ */ jsxs27(Box26, { flexDirection: "column", children: [
4234
- candidates.map((c, i) => {
4235
- const isCurrent = i === cursor;
4236
- const isSelected = selected.has(c.name);
4237
- return /* @__PURE__ */ jsxs27(SelectableRow, { isCurrent, children: [
4238
- /* @__PURE__ */ jsx30(Text28, { color: isSelected ? COLORS.success : COLORS.muted, children: isSelected ? "\u2611" : "\u2610" }),
4239
- /* @__PURE__ */ jsx30(Text28, { bold: isCurrent, inverse: isCurrent, color: isCurrent ? COLORS.text : COLORS.muted, children: c.name }),
4240
- /* @__PURE__ */ jsx30(Text28, { color: COLORS.warning, children: c.diskUsageFormatted }),
4241
- /* @__PURE__ */ jsxs27(Text28, { color: COLORS.textSecondary, children: [
4242
- "[",
4243
- c.reason,
4244
- "]"
4245
- ] })
4246
- ] }, c.name);
4247
- }),
4248
- /* @__PURE__ */ jsx30(Box26, { marginTop: SPACING.xs, children: /* @__PURE__ */ jsxs27(Text28, { color: COLORS.text, bold: true, children: [
4249
- cursor + 1,
4250
- "/",
4251
- candidates.length
4252
- ] }) })
4253
- ] })
4298
+ candidates.length > 0 && !confirmClean && (() => {
4299
+ const start = Math.max(0, cursor - Math.floor(listRows / 2));
4300
+ const visible = candidates.slice(start, start + listRows);
4301
+ return /* @__PURE__ */ jsxs27(Box26, { flexDirection: "column", children: [
4302
+ start > 0 && /* @__PURE__ */ jsxs27(Text28, { color: COLORS.textSecondary, dimColor: true, children: [
4303
+ " ",
4304
+ t("scroll_moreAbove", { count: start })
4305
+ ] }),
4306
+ visible.map((c, i) => {
4307
+ const idx = start + i;
4308
+ const isCurrent = idx === cursor;
4309
+ const isSelected = selected.has(c.name);
4310
+ return /* @__PURE__ */ jsxs27(SelectableRow, { isCurrent, children: [
4311
+ /* @__PURE__ */ jsx30(Text28, { color: isSelected ? COLORS.success : COLORS.muted, children: isSelected ? "\u2611" : "\u2610" }),
4312
+ /* @__PURE__ */ jsx30(Text28, { bold: isCurrent, inverse: isCurrent, color: isCurrent ? COLORS.text : COLORS.muted, children: c.name }),
4313
+ /* @__PURE__ */ jsx30(Text28, { color: COLORS.warning, children: c.diskUsageFormatted }),
4314
+ /* @__PURE__ */ jsxs27(Text28, { color: COLORS.textSecondary, children: [
4315
+ "[",
4316
+ c.reason,
4317
+ "]"
4318
+ ] })
4319
+ ] }, c.name);
4320
+ }),
4321
+ start + listRows < candidates.length && /* @__PURE__ */ jsxs27(Text28, { color: COLORS.textSecondary, dimColor: true, children: [
4322
+ " ",
4323
+ t("scroll_moreBelow", { count: candidates.length - start - listRows })
4324
+ ] }),
4325
+ /* @__PURE__ */ jsx30(Box26, { marginTop: SPACING.xs, children: /* @__PURE__ */ jsxs27(Text28, { color: COLORS.text, bold: true, children: [
4326
+ cursor + 1,
4327
+ "/",
4328
+ candidates.length
4329
+ ] }) })
4330
+ ] });
4331
+ })()
4254
4332
  ] });
4255
4333
  }
4256
4334
 
4257
4335
  // src/views/history.tsx
4258
- import { useEffect as useEffect17, useState as useState13, useMemo as useMemo5 } from "react";
4336
+ import { useEffect as useEffect17, useState as useState14, useMemo as useMemo5 } from "react";
4259
4337
  import { Box as Box27, Text as Text29 } from "ink";
4260
4338
 
4261
4339
  // src/stores/history-store.ts
@@ -4308,12 +4386,12 @@ var ACTION_LABEL_KEYS = {
4308
4386
  var FILTERS = ["all", "install", "uninstall", "upgrade", "upgrade-all"];
4309
4387
  function HistoryView() {
4310
4388
  const { entries, loading, error, fetchHistory, clearHistory: clearHistory2 } = useHistoryStore();
4311
- const [cursor, setCursor] = useState13(0);
4312
- const [filter, setFilter] = useState13("all");
4313
- const [searchQuery, setSearchQuery] = useState13("");
4314
- const [isSearching, setIsSearching] = useState13(false);
4315
- const [confirmClear, setConfirmClear] = useState13(false);
4316
- const [confirmReplay, setConfirmReplay] = useState13(null);
4389
+ const [cursor, setCursor] = useState14(0);
4390
+ const [filter, setFilter] = useState14("all");
4391
+ const [searchQuery, setSearchQuery] = useState14("");
4392
+ const [isSearching, setIsSearching] = useState14(false);
4393
+ const [confirmClear, setConfirmClear] = useState14(false);
4394
+ const [confirmReplay, setConfirmReplay] = useState14(null);
4317
4395
  const stream = useBrewStream();
4318
4396
  const debouncedQuery = useDebounce(searchQuery, 200);
4319
4397
  const { openModal, closeModal } = useModalStore();
@@ -4471,7 +4549,7 @@ function HistoryView() {
4471
4549
  }
4472
4550
 
4473
4551
  // src/views/security-audit.tsx
4474
- import { useEffect as useEffect18, useState as useState14 } from "react";
4552
+ import { useEffect as useEffect18, useState as useState15 } from "react";
4475
4553
  import { Box as Box28, Text as Text30 } from "ink";
4476
4554
  import { jsx as jsx32, jsxs as jsxs29 } from "react/jsx-runtime";
4477
4555
  var SEVERITY_COLORS = {
@@ -4494,9 +4572,9 @@ function isNetworkError(msg) {
4494
4572
  function SecurityAuditView() {
4495
4573
  const { summary, loading, error, scan, cachedAt } = useSecurityStore();
4496
4574
  const navigate = useNavigationStore((s) => s.navigate);
4497
- const [cursor, setCursor] = useState14(0);
4498
- const [expandedPkg, setExpandedPkg] = useState14(null);
4499
- const [confirmUpgrade, setConfirmUpgrade] = useState14(null);
4575
+ const [cursor, setCursor] = useState15(0);
4576
+ const [expandedPkg, setExpandedPkg] = useState15(null);
4577
+ const [confirmUpgrade, setConfirmUpgrade] = useState15(null);
4500
4578
  const stream = useBrewStream();
4501
4579
  useEffect18(() => {
4502
4580
  scan();
@@ -4603,7 +4681,7 @@ function SecurityAuditView() {
4603
4681
  }
4604
4682
 
4605
4683
  // src/views/account.tsx
4606
- import { useState as useState15 } from "react";
4684
+ import { useState as useState16 } from "react";
4607
4685
  import { Box as Box29, Text as Text31 } from "ink";
4608
4686
  import { TextInput as TextInput5 } from "@inkjs/ui";
4609
4687
 
@@ -4685,13 +4763,13 @@ async function redeemPromoCode(code) {
4685
4763
  import { Fragment as Fragment5, jsx as jsx33, jsxs as jsxs30 } from "react/jsx-runtime";
4686
4764
  function AccountView() {
4687
4765
  const { status, license, deactivate: deactivate2, revalidate: revalidate2, degradation } = useLicenseStore();
4688
- const [confirmDeactivate, setConfirmDeactivate] = useState15(false);
4689
- const [deactivating, setDeactivating] = useState15(false);
4690
- const [deactivateError, setDeactivateError] = useState15(null);
4691
- const [promoMode, setPromoMode] = useState15(false);
4692
- const [promoLoading, setPromoLoading] = useState15(false);
4693
- const [promoResult, setPromoResult] = useState15(null);
4694
- const [revalidating, setRevalidating] = useState15(false);
4766
+ const [confirmDeactivate, setConfirmDeactivate] = useState16(false);
4767
+ const [deactivating, setDeactivating] = useState16(false);
4768
+ const [deactivateError, setDeactivateError] = useState16(null);
4769
+ const [promoMode, setPromoMode] = useState16(false);
4770
+ const [promoLoading, setPromoLoading] = useState16(false);
4771
+ const [promoResult, setPromoResult] = useState16(null);
4772
+ const [revalidating, setRevalidating] = useState16(false);
4695
4773
  useViewInput((input, key) => {
4696
4774
  if (confirmDeactivate || deactivating || promoMode || revalidating) {
4697
4775
  if (key.escape && promoMode) {
@@ -4776,16 +4854,14 @@ function AccountView() {
4776
4854
  /* @__PURE__ */ jsx33(Text31, { children: formatDate(license.activatedAt) })
4777
4855
  ] })
4778
4856
  ] }),
4779
- status === "free" && /* @__PURE__ */ jsxs30(Box29, { flexDirection: "column", marginTop: SPACING.sm, borderStyle: "round", borderColor: COLORS.brand, paddingX: SPACING.sm, paddingY: SPACING.xs, children: [
4857
+ status === "free" && /* @__PURE__ */ jsxs30(Box29, { flexDirection: "column", marginTop: SPACING.xs, borderStyle: "round", borderColor: COLORS.brand, paddingX: SPACING.sm, paddingY: SPACING.none, flexShrink: 1, children: [
4780
4858
  /* @__PURE__ */ jsxs30(Text31, { bold: true, color: COLORS.brand, children: [
4781
4859
  "\u2B50",
4782
4860
  " ",
4783
4861
  t("account_upgradeTitle")
4784
4862
  ] }),
4785
- /* @__PURE__ */ jsx33(Text31, { children: " " }),
4786
- /* @__PURE__ */ jsx33(Text31, { children: t("account_unlockDesc") }),
4863
+ /* @__PURE__ */ jsx33(Text31, { wrap: "wrap", children: t("account_unlockDesc") }),
4787
4864
  /* @__PURE__ */ jsx33(Text31, { color: COLORS.info, bold: true, children: t("account_pricing") }),
4788
- /* @__PURE__ */ jsx33(Text31, { children: " " }),
4789
4865
  /* @__PURE__ */ jsxs30(Text31, { color: COLORS.muted, children: [
4790
4866
  t("upgrade_buyAt"),
4791
4867
  " ",
@@ -4837,13 +4913,13 @@ function AccountView() {
4837
4913
  status === "pro" || status === "team" || status === "expired" ? `v ${t("hint_revalidate")} ` : "",
4838
4914
  revalidating ? t("account_revalidating") : "",
4839
4915
  " ",
4840
- t("app_version", { version: "1.0.0" })
4916
+ t("app_version", { version: "1.1.0" })
4841
4917
  ] }) })
4842
4918
  ] });
4843
4919
  }
4844
4920
 
4845
4921
  // src/views/rollback.tsx
4846
- import { useCallback as useCallback3, useEffect as useEffect19, useRef as useRef10, useState as useState16 } from "react";
4922
+ import { useCallback as useCallback3, useEffect as useEffect19, useRef as useRef11, useState as useState17 } from "react";
4847
4923
  import { Box as Box30, Text as Text32 } from "ink";
4848
4924
 
4849
4925
  // src/stores/rollback-store.ts
@@ -5082,6 +5158,14 @@ function actionPrefix(action) {
5082
5158
  }
5083
5159
  function PlanView({ plan }) {
5084
5160
  const executableCount = plan.actions.filter((a) => a.strategy !== "unavailable" && a.action !== "remove").length;
5161
+ const reservedForWarnings = Math.min(plan.warnings.length, 3) * 2;
5162
+ const actionsBudget = useVisibleRows({
5163
+ reservedRows: 6 + reservedForWarnings,
5164
+ fallbackReservedRows: 14 + reservedForWarnings,
5165
+ minRows: 2
5166
+ });
5167
+ const visibleActions = plan.actions.slice(0, actionsBudget);
5168
+ const hiddenActions = plan.actions.length - visibleActions.length;
5085
5169
  return /* @__PURE__ */ jsxs31(Box30, { flexDirection: "column", marginTop: SPACING.xs, children: [
5086
5170
  /* @__PURE__ */ jsxs31(Box30, { marginBottom: SPACING.xs, children: [
5087
5171
  /* @__PURE__ */ jsxs31(Text32, { color: COLORS.text, bold: true, children: [
@@ -5091,7 +5175,7 @@ function PlanView({ plan }) {
5091
5175
  /* @__PURE__ */ jsx34(Text32, { color: COLORS.textSecondary, children: plan.snapshotDate })
5092
5176
  ] }),
5093
5177
  plan.actions.length === 0 && /* @__PURE__ */ jsx34(ResultBanner, { status: "success", message: t("rollback_diff_empty") }),
5094
- plan.actions.map((a) => /* @__PURE__ */ jsxs31(Box30, { children: [
5178
+ visibleActions.map((a) => /* @__PURE__ */ jsxs31(Box30, { children: [
5095
5179
  /* @__PURE__ */ jsxs31(Text32, { color: actionColor(a), children: [
5096
5180
  actionPrefix(a),
5097
5181
  " "
@@ -5114,10 +5198,18 @@ function PlanView({ plan }) {
5114
5198
  "]"
5115
5199
  ] })
5116
5200
  ] }, a.packageName + a.action)),
5117
- plan.warnings.map((w) => /* @__PURE__ */ jsx34(Box30, { marginTop: SPACING.xs, children: /* @__PURE__ */ jsxs31(Text32, { color: COLORS.warning, children: [
5201
+ hiddenActions > 0 && /* @__PURE__ */ jsxs31(Text32, { color: COLORS.textSecondary, dimColor: true, children: [
5202
+ " ",
5203
+ t("scroll_moreBelow", { count: hiddenActions })
5204
+ ] }),
5205
+ plan.warnings.slice(0, 3).map((w) => /* @__PURE__ */ jsx34(Box30, { marginTop: SPACING.xs, children: /* @__PURE__ */ jsxs31(Text32, { color: COLORS.warning, wrap: "wrap", children: [
5118
5206
  "\u26A0 ",
5119
5207
  w
5120
5208
  ] }) }, w)),
5209
+ plan.warnings.length > 3 && /* @__PURE__ */ jsxs31(Text32, { color: COLORS.textSecondary, dimColor: true, children: [
5210
+ " ",
5211
+ t("common_andMore", { count: plan.warnings.length - 3 })
5212
+ ] }),
5121
5213
  /* @__PURE__ */ jsx34(Box30, { marginTop: SPACING.xs, children: plan.canExecute ? /* @__PURE__ */ jsxs31(Text32, { color: COLORS.textSecondary, children: [
5122
5214
  "enter:",
5123
5215
  t("rollback_confirm", { count: String(executableCount) }),
@@ -5133,13 +5225,18 @@ function PlanView({ plan }) {
5133
5225
  function RollbackView() {
5134
5226
  const isPro = useLicenseStore((s) => s.isPro);
5135
5227
  const { snapshots, loading, error, plan, planLoading, planError, fetchSnapshots, selectSnapshot, clearPlan } = useRollbackStore();
5136
- const [cursor, setCursor] = useState16(0);
5137
- const [phase, setPhase] = useState16("list");
5138
- const [streamLines, setStreamLines] = useState16([]);
5139
- const [streamRunning, setStreamRunning] = useState16(false);
5140
- const [streamError, setStreamError] = useState16(null);
5141
- const generatorRef = useRef10(null);
5142
- const mountedRef = useRef10(true);
5228
+ const [cursor, setCursor] = useState17(0);
5229
+ const [phase, setPhase] = useState17("list");
5230
+ const [streamLines, setStreamLines] = useState17([]);
5231
+ const [streamRunning, setStreamRunning] = useState17(false);
5232
+ const [streamError, setStreamError] = useState17(null);
5233
+ const generatorRef = useRef11(null);
5234
+ const mountedRef = useRef11(true);
5235
+ const snapshotRows = useVisibleRows({
5236
+ reservedRows: 6,
5237
+ fallbackReservedRows: 14,
5238
+ minRows: 1
5239
+ });
5143
5240
  useEffect19(() => {
5144
5241
  mountedRef.current = true;
5145
5242
  return () => {
@@ -5232,22 +5329,39 @@ function RollbackView() {
5232
5329
  return /* @__PURE__ */ jsxs31(Box30, { flexDirection: "column", children: [
5233
5330
  /* @__PURE__ */ jsx34(SectionHeader, { emoji: "\u23EA", title: t("rollback_title"), gradient: GRADIENTS.gold }),
5234
5331
  snapshots.length === 0 && /* @__PURE__ */ jsx34(Box30, { marginTop: SPACING.xs, children: /* @__PURE__ */ jsx34(ResultBanner, { status: "info", message: t("rollback_no_snapshots") }) }),
5235
- phase === "list" && snapshots.length > 0 && /* @__PURE__ */ jsxs31(Box30, { flexDirection: "column", marginTop: SPACING.xs, children: [
5236
- /* @__PURE__ */ jsx34(Text32, { color: COLORS.textSecondary, dimColor: true, children: t("rollback_select_snapshot") }),
5237
- /* @__PURE__ */ jsx34(Box30, { flexDirection: "column", marginTop: SPACING.xs, children: snapshots.map((s, i) => /* @__PURE__ */ jsxs31(SelectableRow, { isCurrent: i === cursor, children: [
5238
- /* @__PURE__ */ jsx34(Text32, { bold: i === cursor, color: i === cursor ? COLORS.text : COLORS.muted, children: s.label ?? t("rollback_snapshot_auto") }),
5239
- /* @__PURE__ */ jsxs31(Text32, { color: COLORS.textSecondary, children: [
5240
- " \u2014 ",
5241
- new Date(s.capturedAt).toLocaleString()
5242
- ] }),
5243
- /* @__PURE__ */ jsxs31(Text32, { color: COLORS.muted, dimColor: true, children: [
5244
- " ",
5245
- "(",
5246
- tp("packages", s.formulae.length + s.casks.length),
5247
- ")"
5332
+ phase === "list" && snapshots.length > 0 && (() => {
5333
+ const start = Math.max(0, cursor - Math.floor(snapshotRows / 2));
5334
+ const visible = snapshots.slice(start, start + snapshotRows);
5335
+ return /* @__PURE__ */ jsxs31(Box30, { flexDirection: "column", marginTop: SPACING.xs, children: [
5336
+ /* @__PURE__ */ jsx34(Text32, { color: COLORS.textSecondary, dimColor: true, children: t("rollback_select_snapshot") }),
5337
+ /* @__PURE__ */ jsxs31(Box30, { flexDirection: "column", marginTop: SPACING.xs, children: [
5338
+ start > 0 && /* @__PURE__ */ jsxs31(Text32, { color: COLORS.textSecondary, dimColor: true, children: [
5339
+ " ",
5340
+ t("scroll_moreAbove", { count: start })
5341
+ ] }),
5342
+ visible.map((s, vi) => {
5343
+ const i = start + vi;
5344
+ return /* @__PURE__ */ jsxs31(SelectableRow, { isCurrent: i === cursor, children: [
5345
+ /* @__PURE__ */ jsx34(Text32, { bold: i === cursor, color: i === cursor ? COLORS.text : COLORS.muted, children: s.label ?? t("rollback_snapshot_auto") }),
5346
+ /* @__PURE__ */ jsxs31(Text32, { color: COLORS.textSecondary, children: [
5347
+ " \u2014 ",
5348
+ new Date(s.capturedAt).toLocaleString()
5349
+ ] }),
5350
+ /* @__PURE__ */ jsxs31(Text32, { color: COLORS.muted, dimColor: true, children: [
5351
+ " ",
5352
+ "(",
5353
+ tp("packages", s.formulae.length + s.casks.length),
5354
+ ")"
5355
+ ] })
5356
+ ] }, s.capturedAt);
5357
+ }),
5358
+ start + snapshotRows < snapshots.length && /* @__PURE__ */ jsxs31(Text32, { color: COLORS.textSecondary, dimColor: true, children: [
5359
+ " ",
5360
+ t("scroll_moreBelow", { count: snapshots.length - start - snapshotRows })
5361
+ ] })
5248
5362
  ] })
5249
- ] }, s.capturedAt)) })
5250
- ] }),
5363
+ ] });
5364
+ })(),
5251
5365
  phase === "plan" && /* @__PURE__ */ jsxs31(Box30, { flexDirection: "column", children: [
5252
5366
  planLoading && /* @__PURE__ */ jsx34(Loading, { message: t("rollback_capturing") }),
5253
5367
  planError && /* @__PURE__ */ jsx34(ErrorMessage, { message: planError }),
@@ -5267,7 +5381,7 @@ function RollbackView() {
5267
5381
  }
5268
5382
 
5269
5383
  // src/views/brewfile.tsx
5270
- import { useCallback as useCallback4, useEffect as useEffect20, useRef as useRef11, useState as useState17 } from "react";
5384
+ import { useCallback as useCallback4, useEffect as useEffect20, useRef as useRef12, useState as useState18 } from "react";
5271
5385
  import { Box as Box31, Text as Text33 } from "ink";
5272
5386
  import { TextInput as TextInput6 } from "@inkjs/ui";
5273
5387
  import { jsx as jsx35, jsxs as jsxs32 } from "react/jsx-runtime";
@@ -5313,13 +5427,13 @@ function DriftSummary({ drift }) {
5313
5427
  function BrewfileView() {
5314
5428
  const isPro = useLicenseStore((s) => s.isPro);
5315
5429
  const { schema, drift, loading, driftLoading, error, load, createFromCurrent } = useBrewfileStore();
5316
- const [phase, setPhase] = useState17("overview");
5317
- const [streamLines, setStreamLines] = useState17([]);
5318
- const [streamRunning, setStreamRunning] = useState17(false);
5319
- const [streamError, setStreamError] = useState17(null);
5320
- const [resultMessage, setResultMessage] = useState17("");
5321
- const generatorRef = useRef11(null);
5322
- const mountedRef = useRef11(true);
5430
+ const [phase, setPhase] = useState18("overview");
5431
+ const [streamLines, setStreamLines] = useState18([]);
5432
+ const [streamRunning, setStreamRunning] = useState18(false);
5433
+ const [streamError, setStreamError] = useState18(null);
5434
+ const [resultMessage, setResultMessage] = useState18("");
5435
+ const generatorRef = useRef12(null);
5436
+ const mountedRef = useRef12(true);
5323
5437
  useEffect20(() => {
5324
5438
  mountedRef.current = true;
5325
5439
  void load();
@@ -5496,7 +5610,7 @@ function BrewfileView() {
5496
5610
  }
5497
5611
 
5498
5612
  // src/views/sync.tsx
5499
- import { useCallback as useCallback5, useEffect as useEffect21, useState as useState18 } from "react";
5613
+ import { useCallback as useCallback5, useEffect as useEffect21, useState as useState19 } from "react";
5500
5614
  import { Box as Box32, Text as Text34 } from "ink";
5501
5615
  import { Fragment as Fragment6, jsx as jsx36, jsxs as jsxs33 } from "react/jsx-runtime";
5502
5616
  function OverviewSection({
@@ -5547,8 +5661,22 @@ function ConflictsList({
5547
5661
  entries,
5548
5662
  cursor
5549
5663
  }) {
5664
+ const availableRows = useVisibleRows({
5665
+ reservedRows: 8,
5666
+ fallbackReservedRows: 14,
5667
+ minRows: 4
5668
+ });
5669
+ const perEntryRows = 4;
5670
+ const maxEntries = Math.max(1, Math.floor(availableRows / perEntryRows));
5671
+ const start = Math.max(0, cursor - Math.floor(maxEntries / 2));
5672
+ const visible = entries.slice(start, start + maxEntries);
5550
5673
  return /* @__PURE__ */ jsxs33(Box32, { flexDirection: "column", marginTop: SPACING.xs, children: [
5551
- entries.map((entry, i) => {
5674
+ start > 0 && /* @__PURE__ */ jsxs33(Text34, { color: COLORS.textSecondary, dimColor: true, children: [
5675
+ " ",
5676
+ t("scroll_moreAbove", { count: start })
5677
+ ] }),
5678
+ visible.map((entry, vi) => {
5679
+ const i = start + vi;
5552
5680
  const { conflict, resolution } = entry;
5553
5681
  const isActive = i === cursor;
5554
5682
  return /* @__PURE__ */ jsxs33(Box32, { flexDirection: "column", marginBottom: SPACING.xs, children: [
@@ -5586,6 +5714,10 @@ function ConflictsList({
5586
5714
  ] })
5587
5715
  ] }, `${conflict.packageName}-${conflict.remoteMachine}`);
5588
5716
  }),
5717
+ start + maxEntries < entries.length && /* @__PURE__ */ jsxs33(Text34, { color: COLORS.textSecondary, dimColor: true, children: [
5718
+ " ",
5719
+ t("scroll_moreBelow", { count: entries.length - start - maxEntries })
5720
+ ] }),
5589
5721
  /* @__PURE__ */ jsx36(Box32, { marginTop: SPACING.xs, children: /* @__PURE__ */ jsxs33(Text34, { color: COLORS.textSecondary, children: [
5590
5722
  "j/k:",
5591
5723
  t("hint_navigate"),
@@ -5604,10 +5736,10 @@ function SyncView() {
5604
5736
  const isPro = useLicenseStore((s) => s.isPro);
5605
5737
  const navigate = useNavigationStore((s) => s.navigate);
5606
5738
  const { config, lastResult, conflicts, loading, error, initialize, syncNow, resolveConflicts } = useSyncStore();
5607
- const [phase, setPhase] = useState18("overview");
5608
- const [syncError, setSyncError] = useState18(null);
5609
- const [conflictEntries, setConflictEntries] = useState18([]);
5610
- const [cursor, setCursor] = useState18(0);
5739
+ const [phase, setPhase] = useState19("overview");
5740
+ const [syncError, setSyncError] = useState19(null);
5741
+ const [conflictEntries, setConflictEntries] = useState19([]);
5742
+ const [cursor, setCursor] = useState19(0);
5611
5743
  useEffect21(() => {
5612
5744
  void initialize(isPro());
5613
5745
  }, []);
@@ -5776,7 +5908,7 @@ function SyncView() {
5776
5908
  }
5777
5909
 
5778
5910
  // src/views/compliance.tsx
5779
- import { useCallback as useCallback6, useEffect as useEffect22, useRef as useRef12, useState as useState19 } from "react";
5911
+ import { useCallback as useCallback6, useEffect as useEffect22, useRef as useRef13, useState as useState20 } from "react";
5780
5912
  import { Box as Box33, Text as Text35 } from "ink";
5781
5913
  import { TextInput as TextInput7 } from "@inkjs/ui";
5782
5914
 
@@ -5857,38 +5989,58 @@ function ViolationItem({ violation }) {
5857
5989
  prefix,
5858
5990
  " "
5859
5991
  ] }),
5860
- /* @__PURE__ */ jsx37(Text35, { color, children: violation.detail })
5992
+ /* @__PURE__ */ jsx37(Text35, { color, wrap: "wrap", children: violation.detail })
5861
5993
  ] });
5862
5994
  }
5863
5995
  function ViolationList({ violations }) {
5864
5996
  const errors = violations.filter((v) => v.severity === "error");
5865
5997
  const warnings = violations.filter((v) => v.severity === "warning");
5998
+ const totalRows = useVisibleRows({
5999
+ reservedRows: 11,
6000
+ fallbackReservedRows: 18,
6001
+ minRows: 2
6002
+ });
6003
+ const total = errors.length + warnings.length;
6004
+ const errorBudget = total === 0 ? 0 : Math.min(errors.length, Math.max(1, Math.round(errors.length / total * totalRows)));
6005
+ const warningBudget = Math.max(0, totalRows - errorBudget);
6006
+ const visibleErrors = errors.slice(0, errorBudget);
6007
+ const visibleWarnings = warnings.slice(0, warningBudget);
6008
+ const errorsHidden = errors.length - visibleErrors.length;
6009
+ const warningsHidden = warnings.length - visibleWarnings.length;
5866
6010
  return /* @__PURE__ */ jsxs34(Box33, { flexDirection: "column", marginTop: SPACING.xs, children: [
5867
6011
  errors.length > 0 && /* @__PURE__ */ jsxs34(Box33, { flexDirection: "column", marginBottom: SPACING.xs, children: [
5868
6012
  /* @__PURE__ */ jsxs34(Text35, { color: COLORS.error, bold: true, children: [
5869
6013
  t("compliance_violations", { count: String(errors.length) }),
5870
6014
  " (errors)"
5871
6015
  ] }),
5872
- errors.map((v) => /* @__PURE__ */ jsx37(ViolationItem, { violation: v }, `${v.type}-${v.packageName}`))
6016
+ visibleErrors.map((v) => /* @__PURE__ */ jsx37(ViolationItem, { violation: v }, `${v.type}-${v.packageName}`)),
6017
+ errorsHidden > 0 && /* @__PURE__ */ jsxs34(Text35, { color: COLORS.textSecondary, dimColor: true, children: [
6018
+ " ",
6019
+ t("scroll_moreBelow", { count: errorsHidden })
6020
+ ] })
5873
6021
  ] }),
5874
6022
  warnings.length > 0 && /* @__PURE__ */ jsxs34(Box33, { flexDirection: "column", children: [
5875
6023
  /* @__PURE__ */ jsxs34(Text35, { color: COLORS.warning, bold: true, children: [
5876
6024
  t("compliance_violations", { count: String(warnings.length) }),
5877
6025
  " (warnings)"
5878
6026
  ] }),
5879
- warnings.map((v) => /* @__PURE__ */ jsx37(ViolationItem, { violation: v }, `${v.type}-${v.packageName}`))
6027
+ visibleWarnings.map((v) => /* @__PURE__ */ jsx37(ViolationItem, { violation: v }, `${v.type}-${v.packageName}`)),
6028
+ warningsHidden > 0 && /* @__PURE__ */ jsxs34(Text35, { color: COLORS.textSecondary, dimColor: true, children: [
6029
+ " ",
6030
+ t("scroll_moreBelow", { count: warningsHidden })
6031
+ ] })
5880
6032
  ] })
5881
6033
  ] });
5882
6034
  }
5883
6035
  function ComplianceView() {
5884
6036
  const isPro = useLicenseStore((s) => s.isPro);
5885
6037
  const { policy, report, loading, error, importPolicy, runCheck } = useComplianceStore();
5886
- const [phase, setPhase] = useState19("overview");
5887
- const [resultMessage, setResultMessage] = useState19(null);
5888
- const [streamLines, setStreamLines] = useState19([]);
5889
- const [streamRunning, setStreamRunning] = useState19(false);
5890
- const generatorRef = useRef12(null);
5891
- const mountedRef = useRef12(true);
6038
+ const [phase, setPhase] = useState20("overview");
6039
+ const [resultMessage, setResultMessage] = useState20(null);
6040
+ const [streamLines, setStreamLines] = useState20([]);
6041
+ const [streamRunning, setStreamRunning] = useState20(false);
6042
+ const generatorRef = useRef13(null);
6043
+ const mountedRef = useRef13(true);
5892
6044
  useEffect22(() => {
5893
6045
  mountedRef.current = true;
5894
6046
  return () => {
@@ -6147,7 +6299,7 @@ function App() {
6147
6299
  const { exit } = useApp();
6148
6300
  const currentView = useNavigationStore((s) => s.currentView);
6149
6301
  const isTestEnv = typeof process !== "undefined" && false;
6150
- const [showWelcome, setShowWelcome] = useState20(isTestEnv ? false : null);
6302
+ const [showWelcome, setShowWelcome] = useState21(isTestEnv ? false : null);
6151
6303
  useEffect23(() => {
6152
6304
  if (isTestEnv) return;
6153
6305
  void hasCompletedOnboarding().then((done) => setShowWelcome(!done));
@@ -6245,7 +6397,7 @@ async function reportError(err, context = {}) {
6245
6397
  const config = await resolveConfig();
6246
6398
  if (!config.enabled || !config.endpoint) return;
6247
6399
  const machineId = await getMachineId();
6248
- const version = true ? "1.0.0" : "unknown";
6400
+ const version = true ? "1.1.0" : "unknown";
6249
6401
  await postReport(buildReport("error", err, context, machineId, version), config);
6250
6402
  }
6251
6403
  async function installCrashReporter() {
@@ -6254,7 +6406,7 @@ async function installCrashReporter() {
6254
6406
  if (!config.enabled || !config.endpoint) return;
6255
6407
  _installed = true;
6256
6408
  const machineId = await getMachineId();
6257
- const version = true ? "1.0.0" : "unknown";
6409
+ const version = true ? "1.1.0" : "unknown";
6258
6410
  process.on("uncaughtException", (err) => {
6259
6411
  void postReport(buildReport("fatal", err, { kind: "uncaughtException" }, machineId, version), config);
6260
6412
  });
@@ -6269,7 +6421,7 @@ import { jsx as jsx39 } from "react/jsx-runtime";
6269
6421
  var [, , command, arg] = process.argv;
6270
6422
  async function runCli() {
6271
6423
  if (command === "--version" || command === "-v" || command === "version") {
6272
- process.stdout.write("1.0.0\n");
6424
+ process.stdout.write("1.1.0\n");
6273
6425
  return;
6274
6426
  }
6275
6427
  await ensureDataDirs();
@@ -6452,7 +6604,7 @@ async function ensureBrewBarRunning() {
6452
6604
  await useLicenseStore.getState().initialize();
6453
6605
  if (!useLicenseStore.getState().isPro()) return;
6454
6606
  const { isBrewBarInstalled, installBrewBar, launchBrewBar } = await import("./brewbar-installer-GWJ76J6G.js");
6455
- const { checkBrewBarVersion } = await import("./version-check-J6PTTQ7M.js");
6607
+ const { checkBrewBarVersion } = await import("./version-check-LVSQFXZU.js");
6456
6608
  try {
6457
6609
  if (!await isBrewBarInstalled()) {
6458
6610
  console.log(t("cli_brewbarInstalling"));