@visulima/vis 1.0.0-alpha.11 → 1.0.0-alpha.13

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 (116) hide show
  1. package/CHANGELOG.md +101 -0
  2. package/LICENSE.md +559 -186
  3. package/README.md +18 -0
  4. package/dist/bin.js +1 -9
  5. package/dist/config/index.d.ts +477 -556
  6. package/dist/config/index.js +1 -2
  7. package/dist/generate/index.js +1 -3
  8. package/dist/packem_chunks/applyDefaults.js +2 -336
  9. package/dist/packem_chunks/bin.js +234 -9552
  10. package/dist/packem_chunks/doctor-probe.js +2 -112
  11. package/dist/packem_chunks/fix.js +11 -234
  12. package/dist/packem_chunks/handler.js +1 -99
  13. package/dist/packem_chunks/handler10.js +2 -53
  14. package/dist/packem_chunks/handler11.js +1 -32
  15. package/dist/packem_chunks/handler12.js +5 -100
  16. package/dist/packem_chunks/handler13.js +1 -25
  17. package/dist/packem_chunks/handler14.js +18 -916
  18. package/dist/packem_chunks/handler15.js +15 -201
  19. package/dist/packem_chunks/handler16.js +1 -124
  20. package/dist/packem_chunks/handler17.js +1 -13
  21. package/dist/packem_chunks/handler18.js +1 -106
  22. package/dist/packem_chunks/handler19.js +1 -19
  23. package/dist/packem_chunks/handler2.js +2 -75
  24. package/dist/packem_chunks/handler20.js +5 -29
  25. package/dist/packem_chunks/handler21.js +1 -222
  26. package/dist/packem_chunks/handler22.js +1 -237
  27. package/dist/packem_chunks/handler23.js +5 -101
  28. package/dist/packem_chunks/handler24.js +1 -110
  29. package/dist/packem_chunks/handler25.js +3 -402
  30. package/dist/packem_chunks/handler26.js +1 -13
  31. package/dist/packem_chunks/handler27.js +1 -63
  32. package/dist/packem_chunks/handler28.js +7 -34
  33. package/dist/packem_chunks/handler29.js +21 -456
  34. package/dist/packem_chunks/handler3.js +4 -95
  35. package/dist/packem_chunks/handler30.js +3 -170
  36. package/dist/packem_chunks/handler31.js +1 -530
  37. package/dist/packem_chunks/handler32.js +2 -214
  38. package/dist/packem_chunks/handler33.js +25 -119
  39. package/dist/packem_chunks/handler34.js +2 -630
  40. package/dist/packem_chunks/handler35.js +3 -283
  41. package/dist/packem_chunks/handler36.js +22 -542
  42. package/dist/packem_chunks/handler37.js +410 -744
  43. package/dist/packem_chunks/handler38.js +22 -989
  44. package/dist/packem_chunks/handler39.js +22 -574
  45. package/dist/packem_chunks/handler4.js +2 -90
  46. package/dist/packem_chunks/handler40.js +22 -1685
  47. package/dist/packem_chunks/handler41.js +6 -1088
  48. package/dist/packem_chunks/handler42.js +5 -797
  49. package/dist/packem_chunks/handler43.js +10 -2658
  50. package/dist/packem_chunks/handler44.js +51 -3784
  51. package/dist/packem_chunks/handler45.js +25 -2574
  52. package/dist/packem_chunks/handler46.js +3 -3769
  53. package/dist/packem_chunks/handler47.js +21 -1485
  54. package/dist/packem_chunks/handler48.js +42 -0
  55. package/dist/packem_chunks/handler5.js +8 -174
  56. package/dist/packem_chunks/handler6.js +1 -95
  57. package/dist/packem_chunks/handler7.js +1 -115
  58. package/dist/packem_chunks/handler8.js +1 -12
  59. package/dist/packem_chunks/handler9.js +1 -29
  60. package/dist/packem_chunks/heal-accept.js +10 -522
  61. package/dist/packem_chunks/heal.js +14 -673
  62. package/dist/packem_chunks/index.js +7 -873
  63. package/dist/packem_chunks/loader.js +1 -23
  64. package/dist/packem_chunks/tar.js +3 -0
  65. package/dist/packem_shared/ai-analysis-hm8d2W7z.js +67 -0
  66. package/dist/packem_shared/ai-cache-DoiF80AR.js +1 -0
  67. package/dist/packem_shared/ai-fix-nn4zOE95.js +43 -0
  68. package/dist/packem_shared/cache-directory-CwHlJhgx.js +1 -0
  69. package/dist/packem_shared/dependency-scan-COr5n63B.js +2 -0
  70. package/dist/packem_shared/docker-D6OGr5_S.js +2 -0
  71. package/dist/packem_shared/failure-log-iUVLf6ts.js +2 -0
  72. package/dist/packem_shared/flakiness-D9wf0t56.js +1 -0
  73. package/dist/packem_shared/giget-CcEy_Elm.js +2 -0
  74. package/dist/packem_shared/index-DH-5hsrC.js +1 -0
  75. package/dist/packem_shared/otel-DxDUPJJH.js +6 -0
  76. package/dist/packem_shared/otelPlugin-CQq6poq8.js +1 -0
  77. package/dist/packem_shared/registry-CkubDdiY.js +2 -0
  78. package/dist/packem_shared/run-summary-utils-BfBvjzhY.js +1 -0
  79. package/dist/packem_shared/runtime-check-BXZ43CBW.js +1 -0
  80. package/dist/packem_shared/selectors-BylODRiM.js +3 -0
  81. package/dist/packem_shared/symbols-CQmER5MT.js +1 -0
  82. package/dist/packem_shared/toolchain-BgBOUHII.js +5 -0
  83. package/dist/packem_shared/typosquats-CcZl99B1.js +1 -0
  84. package/dist/packem_shared/use-measured-height-DjYgUOKk.js +1 -0
  85. package/dist/packem_shared/utils-DrNg0XTR.js +1 -0
  86. package/dist/packem_shared/verify-Baj5mFJ7.js +1 -0
  87. package/dist/packem_shared/vis-update-app-D1jl0UZZ.js +1 -0
  88. package/dist/packem_shared/xxh3-DrAUNq4n.js +1 -0
  89. package/index.js +556 -727
  90. package/package.json +19 -29
  91. package/schemas/project.schema.json +739 -297
  92. package/schemas/vis-config.schema.json +3365 -384
  93. package/templates/buildkite-ci/template.yml +20 -20
  94. package/dist/packem_shared/VisUpdateApp-D-Yz_wvg.js +0 -1316
  95. package/dist/packem_shared/_commonjsHelpers-BqLXS_qQ.js +0 -5
  96. package/dist/packem_shared/ai-analysis-CHeB1joD.js +0 -367
  97. package/dist/packem_shared/ai-cache-Be_jexe4.js +0 -142
  98. package/dist/packem_shared/ai-fix-B9iQVcD2.js +0 -379
  99. package/dist/packem_shared/cache-directory-2qvs4goY.js +0 -98
  100. package/dist/packem_shared/catalog-BJTtyi-O.js +0 -1371
  101. package/dist/packem_shared/dependency-scan-A0KSklpG.js +0 -188
  102. package/dist/packem_shared/docker-2iZzc280.js +0 -181
  103. package/dist/packem_shared/failure-log-Cz3Z4SKL.js +0 -100
  104. package/dist/packem_shared/flakiness-goTxXuCX.js +0 -180
  105. package/dist/packem_shared/otel-DCvqCTz_.js +0 -158
  106. package/dist/packem_shared/otelPlugin-DFaLDvJf.js +0 -3
  107. package/dist/packem_shared/registry-CbqXI0rc.js +0 -272
  108. package/dist/packem_shared/run-summary-utils-PVMl4aIh.js +0 -130
  109. package/dist/packem_shared/runtime-check-Cobi3p6l.js +0 -127
  110. package/dist/packem_shared/selectors-SM69TfqC.js +0 -194
  111. package/dist/packem_shared/symbols-Ta7g2nU-.js +0 -14
  112. package/dist/packem_shared/toolchain-BdZd9eBi.js +0 -975
  113. package/dist/packem_shared/typosquats-C-bCh3PX.js +0 -1210
  114. package/dist/packem_shared/use-measured-height-CNP0vT4M.js +0 -20
  115. package/dist/packem_shared/utils-CthVdBPS.js +0 -40
  116. package/dist/packem_shared/xxh3-Ck8mXNg1.js +0 -239
@@ -1,1316 +0,0 @@
1
- import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
2
- import { useWindowSize, Box, Spinner, Text, ScrollView, Tabs, Tab, ScrollBar, useApp, useInput, Dialog } from '@visulima/tui';
3
- import { useSyncExternalStore, useState, useRef, useMemo, useCallback, useEffect } from 'react';
4
- import { j as scoreColor, _ as QuitDialog } from '../packem_chunks/bin.js';
5
- import { u as useMeasuredHeight } from './use-measured-height-CNP0vT4M.js';
6
-
7
- function CheckProgressApp({ current, total }) {
8
- const { columns: termColumns } = useWindowSize();
9
- const cols = termColumns || 80;
10
- const percent = total > 0 ? Math.min(1, current / total) : 0;
11
- const pctText = `${String(Math.round(percent * 100)).padStart(3)}%`;
12
- const counter = `${String(current)}/${String(total)}`;
13
- const barWidth = Math.max(10, cols - 2 - pctText.length - 1);
14
- const filled = Math.round(barWidth * percent);
15
- const empty = barWidth - filled;
16
- return jsxs(Box, { flexDirection: "column", paddingX: 1, children: [
17
- jsxs(Box, { children: [
18
- jsx(Spinner, { type: "dots" }),
19
- jsx(Text, { children: " Checking catalog dependencies " }),
20
- jsx(Text, { dimColor: true, children: counter })
21
- ] }),
22
- jsxs(Box, { children: [
23
- jsx(Text, { color: "cyan", children: "━".repeat(filled) }),
24
- jsx(Text, { dimColor: true, children: "─".repeat(empty) }),
25
- jsxs(Text, { dimColor: true, children: [
26
- " ",
27
- pctText
28
- ] })
29
- ] })
30
- ] });
31
- }
32
-
33
- const groupByCatalog = (entries) => {
34
- const map = /* @__PURE__ */ new Map();
35
- for (const entry of entries) {
36
- const group = map.get(entry.catalogName);
37
- if (group) {
38
- group.push(entry);
39
- } else {
40
- map.set(entry.catalogName, [entry]);
41
- }
42
- }
43
- return map;
44
- };
45
- const filterEntries = (entries, filterType, filterText) => {
46
- let filtered = entries;
47
- if (filterType !== "all") {
48
- filtered = filterType === "security" ? filtered.filter((e) => e.vulnerabilities && e.vulnerabilities.length > 0 || e.socketReport && e.socketReport.alerts.length > 0) : filtered.filter((e) => e.updateType === filterType);
49
- }
50
- if (filterText) {
51
- const lower = filterText.toLowerCase();
52
- filtered = filtered.filter((e) => e.packageName.toLowerCase().includes(lower));
53
- }
54
- return filtered;
55
- };
56
- class UpdateStore {
57
- #state;
58
- #listeners = /* @__PURE__ */ new Set();
59
- #allEntries;
60
- #recommendationMap = null;
61
- constructor(entries, aiResult = null) {
62
- this.#allEntries = entries;
63
- if (aiResult) {
64
- this.#recommendationMap = new Map(aiResult.recommendations.map((r) => [r.package, r]));
65
- }
66
- this.#state = {
67
- aiResult,
68
- allChecked: true,
69
- applyProgress: null,
70
- checkedEntries: new Set(entries.map((e) => e.packageName)),
71
- entries,
72
- error: null,
73
- filterActive: false,
74
- filterText: "",
75
- filterType: "all",
76
- focusedPanel: "list",
77
- groupedByCatalog: groupByCatalog(entries),
78
- phase: "browsing",
79
- selectedIndex: 0
80
- };
81
- }
82
- // ── React integration ───────────────────────────────────────────
83
- getSnapshot = () => this.#state;
84
- subscribe = (listener) => {
85
- this.#listeners.add(listener);
86
- return () => {
87
- this.#listeners.delete(listener);
88
- };
89
- };
90
- // ── Derived data ────────────────────────────────────────────────
91
- /** Get the currently filtered + visible entries. */
92
- getFilteredEntries() {
93
- return filterEntries(this.#allEntries, this.#state.filterType, this.#state.filterText);
94
- }
95
- /** Get AI recommendation for a specific package. */
96
- getRecommendation(packageName) {
97
- return this.#recommendationMap?.get(packageName);
98
- }
99
- /** Get the list of checked entries (for apply). */
100
- getCheckedEntries() {
101
- return this.#allEntries.filter((e) => this.#state.checkedEntries.has(e.packageName));
102
- }
103
- // ── Navigation ──────────────────────────────────────────────────
104
- setSelectedIndex(index) {
105
- const filtered = this.getFilteredEntries();
106
- const clamped = Math.max(0, Math.min(index, filtered.length - 1));
107
- if (clamped !== this.#state.selectedIndex) {
108
- this.#emit({ ...this.#state, selectedIndex: clamped });
109
- }
110
- }
111
- setFocusedPanel(panel) {
112
- if (panel !== this.#state.focusedPanel) {
113
- this.#emit({ ...this.#state, focusedPanel: panel });
114
- }
115
- }
116
- // ── Filtering ───────────────────────────────────────────────────
117
- setFilterType(type) {
118
- if (type !== this.#state.filterType) {
119
- const newEntries = filterEntries(this.#allEntries, type, this.#state.filterText);
120
- this.#emit({
121
- ...this.#state,
122
- entries: newEntries,
123
- filterType: type,
124
- groupedByCatalog: groupByCatalog(newEntries),
125
- selectedIndex: 0
126
- });
127
- }
128
- }
129
- setFilter(text) {
130
- const newEntries = filterEntries(this.#allEntries, this.#state.filterType, text);
131
- this.#emit({
132
- ...this.#state,
133
- entries: newEntries,
134
- filterText: text,
135
- groupedByCatalog: groupByCatalog(newEntries),
136
- selectedIndex: 0
137
- });
138
- }
139
- setFilterActive(active) {
140
- if (active !== this.#state.filterActive) {
141
- if (active) {
142
- this.#emit({ ...this.#state, filterActive: true });
143
- } else {
144
- const newEntries = filterEntries(this.#allEntries, this.#state.filterType, "");
145
- this.#emit({
146
- ...this.#state,
147
- entries: newEntries,
148
- filterActive: false,
149
- filterText: "",
150
- groupedByCatalog: groupByCatalog(newEntries),
151
- selectedIndex: 0
152
- });
153
- }
154
- }
155
- }
156
- // ── Selection ───────────────────────────────────────────────────
157
- toggleCheck(packageName) {
158
- const checked = new Set(this.#state.checkedEntries);
159
- if (checked.has(packageName)) {
160
- checked.delete(packageName);
161
- } else {
162
- checked.add(packageName);
163
- }
164
- this.#emit({
165
- ...this.#state,
166
- allChecked: checked.size === this.#allEntries.length,
167
- checkedEntries: checked
168
- });
169
- }
170
- checkAll() {
171
- this.#emit({
172
- ...this.#state,
173
- allChecked: true,
174
- checkedEntries: new Set(this.#allEntries.map((e) => e.packageName))
175
- });
176
- }
177
- uncheckAll() {
178
- this.#emit({
179
- ...this.#state,
180
- allChecked: false,
181
- checkedEntries: /* @__PURE__ */ new Set()
182
- });
183
- }
184
- toggleAll() {
185
- if (this.#state.allChecked) {
186
- this.uncheckAll();
187
- } else {
188
- this.checkAll();
189
- }
190
- }
191
- // ── Apply lifecycle ─────────────────────────────────────────────
192
- startApply() {
193
- const checked = this.getCheckedEntries();
194
- this.#emit({
195
- ...this.#state,
196
- applyProgress: { current: 0, total: checked.length },
197
- phase: "applying"
198
- });
199
- }
200
- updateApplyProgress(current) {
201
- if (this.#state.applyProgress) {
202
- this.#emit({
203
- ...this.#state,
204
- applyProgress: { ...this.#state.applyProgress, current }
205
- });
206
- }
207
- }
208
- markDone() {
209
- this.#emit({ ...this.#state, phase: "done" });
210
- }
211
- setError(error) {
212
- this.#emit({ ...this.#state, error, phase: "error" });
213
- }
214
- // ── Internal ────────────────────────────────────────────────────
215
- #emit(newState) {
216
- this.#state = newState;
217
- for (const listener of this.#listeners) {
218
- try {
219
- listener();
220
- } catch {
221
- }
222
- }
223
- }
224
- }
225
-
226
- const UPDATE_TYPE_COLORS$1 = {
227
- major: "red",
228
- minor: "yellow",
229
- patch: "green"
230
- };
231
- const SEVERITY_COLORS = {
232
- CRITICAL: "red",
233
- HIGH: "red",
234
- LOW: "gray",
235
- MODERATE: "yellow",
236
- UNKNOWN: "gray"
237
- };
238
- const SOCKET_SEVERITY_COLORS = {
239
- critical: "red",
240
- high: "red",
241
- low: "gray",
242
- medium: "yellow"
243
- };
244
- const RISK_COLORS = {
245
- critical: "red",
246
- high: "red",
247
- low: "green",
248
- medium: "yellow"
249
- };
250
- const ACTION_COLORS = {
251
- defer: "gray",
252
- review: "yellow",
253
- skip: "red",
254
- update: "green"
255
- };
256
- const PackageDetailPanel = ({ changelogUrl, entry, focused, recommendation, scrollRef }) => {
257
- const borderColor = focused ? "white" : "gray";
258
- if (!entry) {
259
- return jsx(Box, { alignItems: "center", borderColor: "gray", borderStyle: "single", flexDirection: "column", flexGrow: 1, justifyContent: "center", children: jsx(Text, { dimColor: true, children: "No package selected" }) });
260
- }
261
- const typeColor = UPDATE_TYPE_COLORS$1[entry.updateType] ?? "white";
262
- const hasVulnerabilities = entry.vulnerabilities && entry.vulnerabilities.length > 0;
263
- const socketScore = entry.socketReport?.score.overall ?? 0;
264
- const socketScoreColor = entry.socketReport ? scoreColor(socketScore) : "gray";
265
- return jsxs(Box, { borderColor, borderStyle: "single", flexDirection: "column", flexGrow: 1, children: [
266
- jsx(Box, { flexShrink: 0, paddingTop: 1, paddingX: 2, children: jsx(Text, { bold: true, color: "white", children: entry.packageName }) }),
267
- jsxs(ScrollView, { flexGrow: 1, flexShrink: 1, paddingX: 2, ref: scrollRef, scrollbar: true, scrollbarColor: "gray", scrollbarStyle: "block", children: [
268
- jsx(Text, {}),
269
- jsxs(Box, { children: [
270
- jsx(Box, { width: 12, children: jsx(Text, { dimColor: true, children: "Current:" }) }),
271
- jsx(Text, { children: entry.currentRange })
272
- ] }),
273
- jsxs(Box, { children: [
274
- jsx(Box, { width: 12, children: jsx(Text, { dimColor: true, children: "Target:" }) }),
275
- jsx(Text, { children: entry.newRange }),
276
- jsxs(Text, { bold: true, color: typeColor, children: [
277
- " ",
278
- "(",
279
- entry.updateType,
280
- ")"
281
- ] })
282
- ] }),
283
- jsxs(Box, { children: [
284
- jsx(Box, { width: 12, children: jsx(Text, { dimColor: true, children: "Version:" }) }),
285
- jsx(Text, { children: entry.targetVersion })
286
- ] }),
287
- jsxs(Box, { children: [
288
- jsx(Box, { width: 12, children: jsx(Text, { dimColor: true, children: "Catalog:" }) }),
289
- jsx(Text, { children: entry.catalogName })
290
- ] }),
291
- entry.acceptedRisk && jsxs(Box, { flexDirection: "column", marginTop: 1, children: [
292
- jsx(Text, { color: "gray", children: "── " }),
293
- jsx(Text, { bold: true, color: "gray", children: "ACKNOWLEDGED RISK" }),
294
- jsxs(Box, { flexDirection: "column", paddingLeft: 2, children: [
295
- jsxs(Box, { children: [
296
- jsx(Text, { dimColor: true, children: "Reason: " }),
297
- jsx(Text, { children: entry.acceptedRisk.reason })
298
- ] }),
299
- jsxs(Box, { children: [
300
- jsx(Text, { dimColor: true, children: "Accepted: " }),
301
- jsx(Text, { children: entry.acceptedRisk.acceptedAt.slice(0, 10) })
302
- ] })
303
- ] })
304
- ] }),
305
- hasVulnerabilities && jsxs(Box, { flexDirection: "column", marginTop: 1, children: [
306
- jsx(Text, { dimColor: true, children: "── " }),
307
- jsx(Text, { bold: true, color: "red", children: "SECURITY" }),
308
- jsx(Text, {}),
309
- entry.vulnerabilities.map((vuln) => jsxs(Box, { flexDirection: "column", marginBottom: 1, children: [
310
- jsxs(Box, { gap: 1, children: [
311
- jsxs(Text, { bold: true, color: SEVERITY_COLORS[vuln.severity] ?? "gray", children: [
312
- "⚠",
313
- " ",
314
- vuln.severity
315
- ] }),
316
- jsx(Text, { bold: true, children: vuln.id })
317
- ] }),
318
- jsx(Box, { paddingLeft: 2, children: jsx(Text, { children: vuln.summary }) }),
319
- jsxs(Box, { gap: 2, paddingLeft: 2, children: [
320
- vuln.cvssScore !== void 0 && jsxs(Text, { dimColor: true, children: [
321
- "CVSS:",
322
- String(vuln.cvssScore)
323
- ] }),
324
- vuln.fixedVersions.length > 0 && jsxs(Text, { dimColor: true, children: [
325
- "Fixed in:",
326
- vuln.fixedVersions.join(", ")
327
- ] })
328
- ] })
329
- ] }, vuln.id))
330
- ] }),
331
- entry.socketReport && jsxs(Box, { flexDirection: "column", marginTop: 1, children: [
332
- jsx(Text, { dimColor: true, children: "── " }),
333
- jsx(Text, { bold: true, color: "cyan", children: "SOCKET.DEV" }),
334
- jsx(Text, {}),
335
- jsxs(Box, { gap: 2, children: [
336
- jsxs(Box, { children: [
337
- jsx(Text, { dimColor: true, children: "Overall: " }),
338
- jsxs(Text, { bold: true, color: socketScoreColor, children: [
339
- String(Math.round(socketScore * 100)),
340
- "%"
341
- ] })
342
- ] }),
343
- jsxs(Box, { children: [
344
- jsx(Text, { dimColor: true, children: "Supply Chain: " }),
345
- jsxs(Text, { children: [
346
- String(Math.round(entry.socketReport.score.supplyChain * 100)),
347
- "%"
348
- ] })
349
- ] }),
350
- jsxs(Box, { children: [
351
- jsx(Text, { dimColor: true, children: "Quality: " }),
352
- jsxs(Text, { children: [
353
- String(Math.round(entry.socketReport.score.quality * 100)),
354
- "%"
355
- ] })
356
- ] })
357
- ] }),
358
- jsxs(Box, { gap: 2, children: [
359
- jsxs(Box, { children: [
360
- jsx(Text, { dimColor: true, children: "Maintenance: " }),
361
- jsxs(Text, { children: [
362
- String(Math.round(entry.socketReport.score.maintenance * 100)),
363
- "%"
364
- ] })
365
- ] }),
366
- jsxs(Box, { children: [
367
- jsx(Text, { dimColor: true, children: "Vulnerability: " }),
368
- jsxs(Text, { children: [
369
- String(Math.round(entry.socketReport.score.vulnerability * 100)),
370
- "%"
371
- ] })
372
- ] }),
373
- jsxs(Box, { children: [
374
- jsx(Text, { dimColor: true, children: "License: " }),
375
- jsxs(Text, { children: [
376
- entry.socketReport.license || "unknown",
377
- " (",
378
- String(Math.round(entry.socketReport.score.license * 100)),
379
- "%)"
380
- ] })
381
- ] })
382
- ] }),
383
- entry.socketReport.alerts.length > 0 && jsxs(Box, { flexDirection: "column", marginTop: 1, children: [
384
- jsxs(Text, { bold: true, color: "yellow", children: [
385
- "⚠",
386
- " ",
387
- String(entry.socketReport.alerts.length),
388
- " alert",
389
- entry.socketReport.alerts.length === 1 ? "" : "s",
390
- ":"
391
- ] }),
392
- entry.socketReport.alerts.map((alert) => jsxs(Box, { gap: 1, paddingLeft: 2, children: [
393
- jsxs(Text, { bold: true, color: SOCKET_SEVERITY_COLORS[alert.severity] ?? "gray", children: [
394
- "[",
395
- alert.severity.toUpperCase(),
396
- "]"
397
- ] }),
398
- jsx(Text, { children: alert.type }),
399
- jsxs(Text, { dimColor: true, children: [
400
- "(",
401
- alert.category,
402
- ")"
403
- ] })
404
- ] }, alert.key))
405
- ] })
406
- ] }),
407
- recommendation && jsxs(Box, { flexDirection: "column", marginTop: 1, children: [
408
- jsx(Text, { dimColor: true, children: "── " }),
409
- jsx(Text, { bold: true, color: "white", children: "AI ANALYSIS" }),
410
- jsx(Text, {}),
411
- jsxs(Box, { gap: 2, children: [
412
- jsxs(Box, { children: [
413
- jsx(Text, { dimColor: true, children: "Action: " }),
414
- jsx(Text, { bold: true, color: ACTION_COLORS[recommendation.action] ?? "white", children: recommendation.action })
415
- ] }),
416
- jsxs(Box, { children: [
417
- jsx(Text, { dimColor: true, children: "Risk: " }),
418
- jsx(Text, { bold: true, color: RISK_COLORS[recommendation.riskLevel] ?? "white", children: recommendation.riskLevel })
419
- ] }),
420
- jsxs(Box, { children: [
421
- jsx(Text, { dimColor: true, children: "Effort: " }),
422
- jsx(Text, { bold: true, children: recommendation.effort })
423
- ] })
424
- ] }),
425
- recommendation.reason && jsx(Box, { marginTop: 1, paddingLeft: 2, children: jsx(Text, { children: recommendation.reason }) }),
426
- recommendation.breakingChanges.length > 0 && jsxs(Box, { flexDirection: "column", marginTop: 1, paddingLeft: 2, children: [
427
- jsx(Text, { bold: true, color: "yellow", children: "Breaking changes:" }),
428
- recommendation.breakingChanges.map((change, i) => jsxs(Text, { children: [
429
- " ",
430
- "•",
431
- " ",
432
- change
433
- ] }, String(i)))
434
- ] })
435
- ] }),
436
- changelogUrl && jsxs(Box, { flexDirection: "column", marginTop: 1, children: [
437
- jsx(Text, { dimColor: true, children: "── " }),
438
- jsx(Text, { bold: true, color: "white", children: "CHANGELOG" }),
439
- jsx(Box, { marginTop: 1, paddingLeft: 2, children: jsx(Text, { color: "cyan", underline: true, children: changelogUrl }) })
440
- ] }),
441
- jsxs(Box, { flexDirection: "column", marginTop: 1, children: [
442
- jsx(Text, { dimColor: true, children: "── " }),
443
- jsx(Text, { bold: true, color: "white", children: "LINKS" }),
444
- jsx(Box, { flexDirection: "column", marginTop: 1, paddingLeft: 2, children: jsxs(Text, { color: "cyan", underline: true, children: [
445
- "https://npmx.dev/",
446
- entry.packageName
447
- ] }) })
448
- ] }),
449
- !recommendation && jsxs(Box, { flexDirection: "column", marginTop: 1, children: [
450
- jsx(Text, { dimColor: true, children: "── " }),
451
- jsx(Text, { bold: true, color: "white", children: "GUIDANCE" }),
452
- jsxs(Box, { flexDirection: "column", marginTop: 1, paddingLeft: 2, children: [
453
- entry.updateType === "major" && jsxs(Fragment, { children: [
454
- jsxs(Text, { color: "red", children: [
455
- "⚠",
456
- " Major update — likely contains breaking changes."
457
- ] }),
458
- jsx(Text, { dimColor: true, children: " Review the changelog before updating." }),
459
- jsx(Text, { dimColor: true, children: " Use --changelog to fetch release URLs." })
460
- ] }),
461
- entry.updateType === "minor" && jsxs(Fragment, { children: [
462
- jsxs(Text, { color: "yellow", children: [
463
- "ℹ",
464
- " Minor update — new features, backward compatible."
465
- ] }),
466
- jsx(Text, { dimColor: true, children: " Generally safe to update." })
467
- ] }),
468
- entry.updateType === "patch" && jsxs(Fragment, { children: [
469
- jsxs(Text, { color: "green", children: [
470
- "✓",
471
- " Patch update — bug fixes only."
472
- ] }),
473
- jsx(Text, { dimColor: true, children: " Safe to update." })
474
- ] }),
475
- !recommendation && jsx(Text, { dimColor: true, children: " Use --ai to get AI-powered analysis." })
476
- ] })
477
- ] })
478
- ] })
479
- ] });
480
- };
481
-
482
- const UPDATE_TYPE_COLORS = {
483
- major: "red",
484
- minor: "yellow",
485
- patch: "green"
486
- };
487
- const FILTER_TABS = [
488
- { id: "all", label: "All" },
489
- { id: "major", label: "Major" },
490
- { id: "minor", label: "Minor" },
491
- { id: "patch", label: "Patch" },
492
- { id: "security", label: "Security" }
493
- ];
494
- const PackageRow = ({ checked, entry, isSelected }) => {
495
- const typeColor = UPDATE_TYPE_COLORS[entry.updateType] ?? "white";
496
- const hasSecurity = entry.vulnerabilities && entry.vulnerabilities.length > 0;
497
- const hasSocketAlerts = entry.socketReport && entry.socketReport.alerts.length > 0;
498
- const isAcknowledged = Boolean(entry.acceptedRisk);
499
- const checkbox = checked ? "☑" : "☐";
500
- const scoreText = entry.socketReport ? `${String(Math.round(entry.socketReport.score.overall * 100))}%` : "";
501
- const scoreColorName = entry.socketReport ? scoreColor(entry.socketReport.score.overall) : "gray";
502
- return jsxs(Box, { flexShrink: 0, height: 1, children: [
503
- jsx(Text, { children: isSelected ? ">" : " " }),
504
- jsxs(Text, { color: checked ? "white" : "gray", children: [
505
- " ",
506
- checkbox,
507
- " "
508
- ] }),
509
- hasSecurity || hasSocketAlerts ? jsx(Text, { color: isAcknowledged ? "gray" : "red", children: isAcknowledged ? "✓ " : "⚠ " }) : jsx(Text, { children: " " }),
510
- jsx(Box, { flexGrow: 1, children: jsxs(Text, { bold: isSelected, inverse: isSelected, wrap: "truncate", children: [
511
- entry.packageName,
512
- isAcknowledged ? " [ack]" : ""
513
- ] }) }),
514
- scoreText && jsxs(Text, { color: scoreColorName, children: [
515
- " ",
516
- scoreText
517
- ] }),
518
- jsxs(Text, { dimColor: true, children: [
519
- " ",
520
- entry.currentRange
521
- ] }),
522
- jsxs(Text, { dimColor: true, children: [
523
- " ",
524
- "→",
525
- " "
526
- ] }),
527
- jsxs(Text, { children: [
528
- entry.newRange,
529
- " "
530
- ] }),
531
- jsx(Text, { bold: true, color: typeColor, children: entry.updateType })
532
- ] });
533
- };
534
- const CatalogHeader = ({ count, name }) => jsxs(Box, { flexShrink: 0, height: 1, marginTop: 1, children: [
535
- jsxs(Text, { dimColor: true, children: [
536
- "▼",
537
- " "
538
- ] }),
539
- jsx(Text, { bold: true, color: "white", children: name.toUpperCase() }),
540
- jsxs(Text, { dimColor: true, children: [
541
- " (",
542
- count,
543
- ")"
544
- ] })
545
- ] });
546
- const PackageListPanel = ({
547
- checkedEntries,
548
- entries,
549
- filterActive,
550
- filteredOutCount,
551
- filterText,
552
- filterType,
553
- focused,
554
- groupedByCatalog,
555
- isDryRun,
556
- onViewportHeightChange,
557
- scrollOffset,
558
- selectedIndex,
559
- totalCatalogEntries,
560
- totalChecked,
561
- totalEntries,
562
- viewportHeight
563
- }) => {
564
- const borderColor = focused ? "white" : "gray";
565
- const { measuredHeight: measuredViewportHeight, ref: contentRowRef } = useMeasuredHeight(viewportHeight, onViewportHeightChange);
566
- let majors = 0;
567
- let minors = 0;
568
- let patches = 0;
569
- let secCount = 0;
570
- for (const e of entries) {
571
- if (e.updateType === "major") {
572
- majors++;
573
- } else if (e.updateType === "minor") {
574
- minors++;
575
- } else {
576
- patches++;
577
- }
578
- if (e.vulnerabilities && e.vulnerabilities.length > 0 || e.socketReport && e.socketReport.alerts.length > 0) {
579
- secCount++;
580
- }
581
- }
582
- const summaryParts = [];
583
- if (majors > 0) {
584
- summaryParts.push(`${majors} major`);
585
- }
586
- if (minors > 0) {
587
- summaryParts.push(`${minors} minor`);
588
- }
589
- if (patches > 0) {
590
- summaryParts.push(`${patches} patch`);
591
- }
592
- if (secCount > 0) {
593
- summaryParts.push(`${secCount} vulnerable`);
594
- }
595
- const summaryText = summaryParts.length > 0 ? ` (${summaryParts.join(", ")})` : "";
596
- let checkedCount = 0;
597
- for (const e of entries) {
598
- if (checkedEntries.has(e.packageName)) {
599
- checkedCount++;
600
- }
601
- }
602
- const rows = [];
603
- let flatIndex = 0;
604
- for (const [catalogName, catalogEntries] of groupedByCatalog) {
605
- rows.push(jsx(CatalogHeader, { count: catalogEntries.length, name: catalogName }, `hdr-${catalogName}`));
606
- for (const entry of catalogEntries) {
607
- const currentFlatIndex = flatIndex;
608
- rows.push(
609
- jsx(
610
- PackageRow,
611
- {
612
- checked: checkedEntries.has(entry.packageName),
613
- entry,
614
- isSelected: currentFlatIndex === selectedIndex
615
- },
616
- entry.packageName
617
- )
618
- );
619
- flatIndex++;
620
- }
621
- }
622
- let contentHeight = 0;
623
- for (const [, catalogEntries] of groupedByCatalog) {
624
- contentHeight += 2 + catalogEntries.length;
625
- }
626
- const showScrollbar = contentHeight > measuredViewportHeight && measuredViewportHeight > 0;
627
- return jsxs(Box, { borderColor, borderStyle: "single", flexDirection: "column", flexGrow: 1, children: [
628
- jsxs(Box, { flexShrink: 0, gap: 1, paddingX: 1, children: [
629
- jsx(Text, { bold: true, inverse: true, children: " VIS " }),
630
- jsxs(Text, { wrap: "truncate", children: [
631
- totalEntries,
632
- totalChecked > 0 ? `/${totalChecked}` : "",
633
- " outdated",
634
- summaryText,
635
- totalCatalogEntries > totalChecked ? ` · ${totalCatalogEntries - totalChecked} dupes` : ""
636
- ] }),
637
- !isDryRun && checkedCount > 0 && jsxs(Text, { dimColor: true, children: [
638
- " —",
639
- checkedCount,
640
- " selected"
641
- ] })
642
- ] }),
643
- jsx(Box, { flexShrink: 0, paddingX: 1, paddingY: 1, children: jsx(
644
- Tabs,
645
- {
646
- isFocused: focused,
647
- keyMap: { next: [], previous: [], useNumbers: false, useTab: false },
648
- onChange: () => {
649
- },
650
- showIndex: false,
651
- value: filterType,
652
- children: FILTER_TABS.map(({ id, label }) => jsx(Tab, { name: id, children: label }, id))
653
- }
654
- ) }),
655
- filterActive && jsxs(Box, { flexShrink: 0, paddingX: 1, children: [
656
- jsx(Text, { bold: true, color: "white", children: "/ " }),
657
- jsx(Text, { children: filterText }),
658
- jsx(Text, { inverse: true, children: " " })
659
- ] }),
660
- filteredOutCount > 0 && jsx(Box, { flexShrink: 0, paddingX: 1, children: jsxs(Text, { color: "yellow", children: [
661
- "⚠",
662
- " ",
663
- filteredOutCount,
664
- " package",
665
- filteredOutCount === 1 ? "" : "s",
666
- " filtered out by target constraint — press",
667
- " ",
668
- jsx(Text, { bold: true, color: "white", children: "f" }),
669
- " ",
670
- "to view"
671
- ] }) }),
672
- jsxs(Box, { flexDirection: "row", flexGrow: 1, overflow: "hidden", ref: contentRowRef, children: [
673
- jsx(Box, { flexDirection: "column", flexGrow: 1, overflow: "hidden", paddingLeft: 1, children: jsx(Box, { flexDirection: "column", marginTop: -scrollOffset, children: rows }) }),
674
- showScrollbar && jsx(Box, { flexShrink: 0, marginLeft: 1, marginRight: 1, children: jsx(
675
- ScrollBar,
676
- {
677
- contentHeight,
678
- placement: "inset",
679
- scrollOffset,
680
- style: "block",
681
- viewportHeight: measuredViewportHeight
682
- }
683
- ) })
684
- ] }, `list-${filterType}-${filterText}`)
685
- ] });
686
- };
687
-
688
- const MIN_HORIZONTAL_WIDTH = 100;
689
- const MIN_VIEWPORT_WIDTH = 40;
690
- const MIN_VIEWPORT_HEIGHT = 10;
691
- const EMPTY_ENTRIES = [];
692
- const VisUpdateApp = ({
693
- autoExitSeconds = 0,
694
- changelogUrls,
695
- checkedCount = 0,
696
- filteredOutEntries = EMPTY_ENTRIES,
697
- isDryRun,
698
- store,
699
- totalCatalogEntries = 0
700
- }) => {
701
- const { exit } = useApp();
702
- const { columns, rows } = useWindowSize();
703
- const state = useSyncExternalStore(store.subscribe, store.getSnapshot);
704
- const [helpVisible, setHelpVisible] = useState(false);
705
- const [filteredOutVisible, setFilteredOutVisible] = useState(false);
706
- const helpScrollRef = useRef(null);
707
- const filteredOutScrollRef = useRef(null);
708
- const detailScrollRef = useRef(null);
709
- const confirmScrollRef = useRef(null);
710
- const [listScrollOffset, setListScrollOffset] = useState(0);
711
- const [confirmVisible, setConfirmVisible] = useState(false);
712
- const [quitDialogVisible, setQuitDialogVisible] = useState(false);
713
- const filteredEntries = useMemo(() => store.getFilteredEntries(), [state.entries, state.filterType, state.filterText]);
714
- const selectedEntry = filteredEntries[state.selectedIndex] ?? null;
715
- const selectedRecommendation = selectedEntry ? store.getRecommendation(selectedEntry.packageName) : void 0;
716
- const selectedChangelog = selectedEntry && changelogUrls ? changelogUrls.get(selectedEntry.packageName) : void 0;
717
- const getRowForIndex = useCallback(
718
- (index) => {
719
- let row = 0;
720
- let count = 0;
721
- for (const [, catalogEntries] of state.groupedByCatalog) {
722
- row += 2;
723
- for (let i = 0; i < catalogEntries.length; i++) {
724
- if (count === index) {
725
- return row;
726
- }
727
- row += 1;
728
- count++;
729
- }
730
- }
731
- return row;
732
- },
733
- [state.groupedByCatalog]
734
- );
735
- const estimatedViewportHeight = Math.max(1, rows - 8 - (state.filterActive ? 1 : 0));
736
- const [measuredViewportHeight, setMeasuredViewportHeight] = useState(estimatedViewportHeight);
737
- const listViewportHeight = measuredViewportHeight > 0 ? measuredViewportHeight : estimatedViewportHeight;
738
- const scrollToIndex = useCallback(
739
- (index) => {
740
- const targetRow = getRowForIndex(index);
741
- setListScrollOffset((current) => {
742
- if (targetRow > current + listViewportHeight - 2) {
743
- return Math.max(0, targetRow - listViewportHeight + 2);
744
- }
745
- if (targetRow < current + 1) {
746
- return Math.max(0, targetRow - 1);
747
- }
748
- return current;
749
- });
750
- },
751
- [getRowForIndex, listViewportHeight]
752
- );
753
- useEffect(() => {
754
- detailScrollRef.current?.scrollToTop();
755
- }, [selectedEntry?.packageName]);
756
- useInput(
757
- (input, key) => {
758
- if (input === "c" && key.ctrl) {
759
- exit();
760
- return;
761
- }
762
- if (quitDialogVisible) {
763
- return;
764
- }
765
- if (filteredOutVisible) {
766
- if (key.escape || input === "f" || input === "q") {
767
- setFilteredOutVisible(false);
768
- } else if (key.downArrow || input === "j") {
769
- filteredOutScrollRef.current?.scrollBy(1);
770
- } else if (key.upArrow || input === "k") {
771
- filteredOutScrollRef.current?.scrollBy(-1);
772
- }
773
- return;
774
- }
775
- if (confirmVisible) {
776
- if (input === "u" || key.return) {
777
- setConfirmVisible(false);
778
- store.startApply();
779
- exit(store.getCheckedEntries());
780
- } else if (key.escape || input === "q") {
781
- setConfirmVisible(false);
782
- } else if (key.downArrow || input === "j") {
783
- confirmScrollRef.current?.scrollBy(1);
784
- } else if (key.upArrow || input === "k") {
785
- confirmScrollRef.current?.scrollBy(-1);
786
- } else if (key.pageDown) {
787
- confirmScrollRef.current?.scrollBy(5);
788
- } else if (key.pageUp) {
789
- confirmScrollRef.current?.scrollBy(-5);
790
- }
791
- return;
792
- }
793
- if (helpVisible) {
794
- if (key.escape || input === "?") {
795
- setHelpVisible(false);
796
- } else if (input === "q") {
797
- setHelpVisible(false);
798
- setQuitDialogVisible(true);
799
- } else if (key.downArrow || input === "j") {
800
- helpScrollRef.current?.scrollBy(1);
801
- } else if (key.upArrow || input === "k") {
802
- helpScrollRef.current?.scrollBy(-1);
803
- }
804
- return;
805
- }
806
- if (input === "?") {
807
- setHelpVisible(true);
808
- return;
809
- }
810
- if (input === "q") {
811
- setQuitDialogVisible(true);
812
- return;
813
- }
814
- if (key.tab) {
815
- store.setFocusedPanel(state.focusedPanel === "list" ? "detail" : "list");
816
- return;
817
- }
818
- if (state.focusedPanel === "list" && (key.leftArrow || key.rightArrow)) {
819
- const tabs = ["all", "major", "minor", "patch", "security"];
820
- const currentIndex = tabs.indexOf(state.filterType);
821
- const nextIndex = key.rightArrow ? (currentIndex + 1) % tabs.length : (currentIndex - 1 + tabs.length) % tabs.length;
822
- setListScrollOffset(0);
823
- detailScrollRef.current?.scrollToTop();
824
- store.setFilterType(tabs[nextIndex]);
825
- return;
826
- }
827
- if (input === "f" && filteredOutEntries.length > 0) {
828
- setFilteredOutVisible((previous) => !previous);
829
- return;
830
- }
831
- if (state.filterActive) {
832
- if (key.escape) {
833
- store.setFilterActive(false);
834
- return;
835
- }
836
- if (key.return) {
837
- store.setFilterActive(false);
838
- return;
839
- }
840
- if (key.backspace) {
841
- setListScrollOffset(0);
842
- store.setFilter(state.filterText.slice(0, -1));
843
- return;
844
- }
845
- if (input && !key.ctrl && !key.meta) {
846
- setListScrollOffset(0);
847
- store.setFilter(state.filterText + input);
848
- return;
849
- }
850
- return;
851
- }
852
- if (state.focusedPanel === "list") {
853
- if (key.downArrow || input === "j") {
854
- const next = Math.min(state.selectedIndex + 1, filteredEntries.length - 1);
855
- store.setSelectedIndex(next);
856
- scrollToIndex(next);
857
- return;
858
- }
859
- if (key.upArrow || input === "k") {
860
- const next = Math.max(state.selectedIndex - 1, 0);
861
- store.setSelectedIndex(next);
862
- scrollToIndex(next);
863
- return;
864
- }
865
- if (key.pageDown) {
866
- const next = Math.min(state.selectedIndex + 10, filteredEntries.length - 1);
867
- store.setSelectedIndex(next);
868
- scrollToIndex(next);
869
- return;
870
- }
871
- if (key.pageUp) {
872
- const next = Math.max(state.selectedIndex - 10, 0);
873
- store.setSelectedIndex(next);
874
- scrollToIndex(next);
875
- return;
876
- }
877
- if (key.home) {
878
- store.setSelectedIndex(0);
879
- setListScrollOffset(0);
880
- return;
881
- }
882
- if (key.end) {
883
- const last = filteredEntries.length - 1;
884
- store.setSelectedIndex(last);
885
- scrollToIndex(last);
886
- return;
887
- }
888
- if (input === " " || key.return) {
889
- if (selectedEntry) {
890
- store.toggleCheck(selectedEntry.packageName);
891
- }
892
- return;
893
- }
894
- if (input === "a") {
895
- store.toggleAll();
896
- return;
897
- }
898
- if (input === "/") {
899
- store.setFilterActive(true);
900
- return;
901
- }
902
- if (input === "u" && !isDryRun && state.checkedEntries.size > 0) {
903
- setConfirmVisible(true);
904
- return;
905
- }
906
- if (key.rightArrow) {
907
- store.setFocusedPanel("detail");
908
- return;
909
- }
910
- return;
911
- }
912
- if (state.focusedPanel === "detail") {
913
- if (key.escape || key.leftArrow) {
914
- store.setFocusedPanel("list");
915
- return;
916
- }
917
- if (key.downArrow || input === "j") {
918
- detailScrollRef.current?.scrollBy(1);
919
- return;
920
- }
921
- if (key.upArrow || input === "k") {
922
- detailScrollRef.current?.scrollBy(-1);
923
- return;
924
- }
925
- if (key.pageDown) {
926
- detailScrollRef.current?.scrollBy(10);
927
- return;
928
- }
929
- if (key.pageUp) {
930
- detailScrollRef.current?.scrollBy(-10);
931
- return;
932
- }
933
- if (key.home) {
934
- detailScrollRef.current?.scrollToTop();
935
- return;
936
- }
937
- if (key.end) {
938
- detailScrollRef.current?.scrollToBottom();
939
- }
940
- }
941
- },
942
- { isActive: true }
943
- );
944
- if (columns < MIN_VIEWPORT_WIDTH || rows < MIN_VIEWPORT_HEIGHT) {
945
- return jsx(Box, { alignItems: "center", height: rows, justifyContent: "center", width: columns, children: jsxs(Text, { color: "yellow", children: [
946
- "Terminal too small (",
947
- columns,
948
- "x",
949
- rows,
950
- ")"
951
- ] }) });
952
- }
953
- const isHorizontal = columns >= MIN_HORIZONTAL_WIDTH;
954
- const footerItems = [
955
- jsxs(Box, { gap: 1, children: [
956
- jsx(Text, { bold: true, color: "white", children: "q" }),
957
- jsx(Text, { dimColor: true, children: "QUIT" })
958
- ] }, "q"),
959
- jsxs(Box, { gap: 1, children: [
960
- jsx(Text, { bold: true, color: "white", children: "?" }),
961
- jsx(Text, { dimColor: true, children: "HELP" })
962
- ] }, "?"),
963
- jsxs(Box, { gap: 1, children: [
964
- jsx(Text, { bold: true, color: "white", children: "↑↓" }),
965
- jsx(Text, { dimColor: true, children: "NAV" })
966
- ] }, "nav"),
967
- jsxs(Box, { gap: 1, children: [
968
- jsx(Text, { bold: true, color: "white", children: "Space" }),
969
- jsx(Text, { dimColor: true, children: "CHECK" })
970
- ] }, "sp"),
971
- jsxs(Box, { gap: 1, children: [
972
- jsx(Text, { bold: true, color: "white", children: "a" }),
973
- jsx(Text, { dimColor: true, children: "ALL" })
974
- ] }, "a")
975
- ];
976
- if (!isDryRun && state.checkedEntries.size > 0) {
977
- footerItems.push(
978
- jsxs(Box, { gap: 1, children: [
979
- jsx(Text, { bold: true, color: "green", children: "u" }),
980
- jsx(Text, { dimColor: true, children: "APPLY" })
981
- ] }, "u")
982
- );
983
- }
984
- if (filteredOutEntries.length > 0) {
985
- footerItems.push(
986
- jsxs(Box, { gap: 1, children: [
987
- jsx(Text, { bold: true, color: "yellow", children: "f" }),
988
- jsxs(Text, { dimColor: true, children: [
989
- "FILTERED (",
990
- filteredOutEntries.length,
991
- ")"
992
- ] })
993
- ] }, "fo")
994
- );
995
- }
996
- footerItems.push(
997
- jsxs(Box, { gap: 1, children: [
998
- jsx(Text, { bold: true, color: "white", children: "←→" }),
999
- jsx(Text, { dimColor: true, children: "FILTER" })
1000
- ] }, "lr"),
1001
- jsxs(Box, { gap: 1, children: [
1002
- jsx(Text, { bold: true, color: "white", children: "/" }),
1003
- jsx(Text, { dimColor: true, children: "SEARCH" })
1004
- ] }, "f"),
1005
- jsxs(Box, { gap: 1, children: [
1006
- jsx(Text, { bold: true, color: "white", children: "Tab" }),
1007
- jsx(Text, { dimColor: true, children: "PANEL" })
1008
- ] }, "t")
1009
- );
1010
- const footer = jsx(Box, { borderBottom: false, borderColor: "gray", borderLeft: false, borderRight: false, borderStyle: "single", flexShrink: 0, children: jsx(Box, { flexWrap: "wrap", gap: 2, paddingX: 1, children: footerItems }) });
1011
- const helpPopup = jsxs(
1012
- Dialog,
1013
- {
1014
- footer: jsxs(Text, { dimColor: true, children: [
1015
- jsx(Text, { bold: true, color: "white", children: "↑↓" }),
1016
- " ",
1017
- "scroll",
1018
- " ",
1019
- jsx(Text, { bold: true, color: "white", children: "?" }),
1020
- "/",
1021
- jsx(Text, { bold: true, color: "white", children: "Esc" }),
1022
- " ",
1023
- "close"
1024
- ] }),
1025
- scrollRef: helpScrollRef,
1026
- title: "KEYBOARD SHORTCUTS",
1027
- visible: helpVisible,
1028
- width: 52,
1029
- children: [
1030
- jsxs(Box, { flexDirection: "column", marginBottom: 1, children: [
1031
- jsxs(Box, { marginBottom: 1, children: [
1032
- jsx(Text, { dimColor: true, children: "── " }),
1033
- jsx(Text, { bold: true, color: "white", children: "NAVIGATION" })
1034
- ] }),
1035
- jsxs(Box, { children: [
1036
- jsx(Box, { width: 24, children: jsxs(Text, { children: [
1037
- jsxs(Text, { bold: true, color: "white", children: [
1038
- " ",
1039
- "↑",
1040
- "/k"
1041
- ] }),
1042
- jsx(Text, { dimColor: true, children: " Move up" })
1043
- ] }) }),
1044
- jsxs(Text, { children: [
1045
- jsxs(Text, { bold: true, color: "white", children: [
1046
- " ",
1047
- "↓",
1048
- "/j"
1049
- ] }),
1050
- jsx(Text, { dimColor: true, children: " Move down" })
1051
- ] })
1052
- ] }),
1053
- jsxs(Text, { children: [
1054
- jsxs(Text, { bold: true, color: "white", children: [
1055
- " ",
1056
- "Tab"
1057
- ] }),
1058
- jsx(Text, { dimColor: true, children: " Switch panel" })
1059
- ] }),
1060
- jsxs(Text, { children: [
1061
- jsxs(Text, { bold: true, color: "white", children: [
1062
- " ",
1063
- "→",
1064
- "/",
1065
- "←"
1066
- ] }),
1067
- jsx(Text, { dimColor: true, children: " Focus detail/list" })
1068
- ] })
1069
- ] }),
1070
- jsxs(Box, { flexDirection: "column", marginBottom: 1, children: [
1071
- jsxs(Box, { marginBottom: 1, children: [
1072
- jsx(Text, { dimColor: true, children: "── " }),
1073
- jsx(Text, { bold: true, color: "white", children: "SELECTION" })
1074
- ] }),
1075
- jsxs(Text, { children: [
1076
- jsxs(Text, { bold: true, color: "white", children: [
1077
- " ",
1078
- "Space"
1079
- ] }),
1080
- jsx(Text, { dimColor: true, children: " Toggle check on package" })
1081
- ] }),
1082
- jsxs(Text, { children: [
1083
- jsxs(Text, { bold: true, color: "white", children: [
1084
- " ",
1085
- "a"
1086
- ] }),
1087
- jsx(Text, { dimColor: true, children: " Toggle check all" })
1088
- ] })
1089
- ] }),
1090
- jsxs(Box, { flexDirection: "column", marginBottom: 1, children: [
1091
- jsxs(Box, { marginBottom: 1, children: [
1092
- jsx(Text, { dimColor: true, children: "── " }),
1093
- jsx(Text, { bold: true, color: "white", children: "FILTERS" })
1094
- ] }),
1095
- jsxs(Text, { children: [
1096
- jsxs(Text, { bold: true, color: "white", children: [
1097
- " ",
1098
- "←→"
1099
- ] }),
1100
- jsx(Text, { dimColor: true, children: " Switch filter tab" })
1101
- ] }),
1102
- jsxs(Text, { children: [
1103
- jsxs(Text, { bold: true, color: "white", children: [
1104
- " ",
1105
- "/"
1106
- ] }),
1107
- jsx(Text, { dimColor: true, children: " Text filter" })
1108
- ] }),
1109
- filteredOutEntries.length > 0 && jsxs(Text, { children: [
1110
- jsxs(Text, { bold: true, color: "white", children: [
1111
- " ",
1112
- "f"
1113
- ] }),
1114
- jsx(Text, { dimColor: true, children: " View filtered-out packages" })
1115
- ] })
1116
- ] }),
1117
- jsxs(Box, { flexDirection: "column", children: [
1118
- jsxs(Box, { marginBottom: 1, children: [
1119
- jsx(Text, { dimColor: true, children: "── " }),
1120
- jsx(Text, { bold: true, color: "white", children: "ACTIONS" })
1121
- ] }),
1122
- !isDryRun && jsxs(Text, { children: [
1123
- jsxs(Text, { bold: true, color: "white", children: [
1124
- " ",
1125
- "u"
1126
- ] }),
1127
- jsx(Text, { dimColor: true, children: " Apply selected updates" })
1128
- ] }),
1129
- jsxs(Text, { children: [
1130
- jsxs(Text, { bold: true, color: "white", children: [
1131
- " ",
1132
- "q"
1133
- ] }),
1134
- jsx(Text, { dimColor: true, children: " Quit" })
1135
- ] }),
1136
- jsxs(Text, { children: [
1137
- jsxs(Text, { bold: true, color: "white", children: [
1138
- " ",
1139
- "?"
1140
- ] }),
1141
- jsx(Text, { dimColor: true, children: " Toggle help" })
1142
- ] })
1143
- ] })
1144
- ]
1145
- }
1146
- );
1147
- const checkedList = store.getCheckedEntries();
1148
- const majorCount = checkedList.filter((e) => e.updateType === "major").length;
1149
- const confirmFooter = jsxs(Box, { alignItems: "center", flexDirection: "column", children: [
1150
- majorCount > 0 && jsx(Box, { marginBottom: 1, marginTop: 1, children: jsxs(Text, { color: "yellow", children: [
1151
- "⚠",
1152
- " ",
1153
- majorCount,
1154
- " major update",
1155
- majorCount === 1 ? "" : "s",
1156
- " — review breaking changes"
1157
- ] }) }),
1158
- jsxs(Text, { dimColor: true, children: [
1159
- "Press",
1160
- " ",
1161
- jsx(Text, { bold: true, color: "white", children: "u" }),
1162
- " ",
1163
- "or",
1164
- " ",
1165
- jsx(Text, { bold: true, color: "white", children: "Enter" }),
1166
- " ",
1167
- "to confirm,",
1168
- " ",
1169
- jsx(Text, { bold: true, color: "white", children: "Esc" }),
1170
- " ",
1171
- "to cancel"
1172
- ] })
1173
- ] });
1174
- const confirmDialog = jsx(
1175
- Dialog,
1176
- {
1177
- footer: confirmFooter,
1178
- scrollRef: confirmScrollRef,
1179
- title: `Apply ${checkedList.length} update${checkedList.length === 1 ? "" : "s"}?`,
1180
- visible: confirmVisible,
1181
- width: 70,
1182
- children: checkedList.map((e) => jsxs(Box, { gap: 1, children: [
1183
- jsxs(Text, { children: [
1184
- " ",
1185
- e.packageName
1186
- ] }),
1187
- jsxs(Text, { dimColor: true, children: [
1188
- e.currentRange,
1189
- " ",
1190
- "→",
1191
- " ",
1192
- e.newRange
1193
- ] }),
1194
- jsx(Text, { bold: true, color: e.updateType === "major" ? "red" : e.updateType === "minor" ? "yellow" : "green", children: e.updateType })
1195
- ] }, e.packageName))
1196
- }
1197
- );
1198
- const filteredOutDialog = filteredOutEntries.length > 0 ? jsx(
1199
- Dialog,
1200
- {
1201
- footer: jsxs(Text, { dimColor: true, children: [
1202
- jsx(Text, { bold: true, color: "white", children: "↑↓" }),
1203
- " ",
1204
- "scroll",
1205
- " ",
1206
- jsx(Text, { bold: true, color: "white", children: "f" }),
1207
- "/",
1208
- jsx(Text, { bold: true, color: "white", children: "Esc" }),
1209
- " ",
1210
- "close"
1211
- ] }),
1212
- scrollRef: filteredOutScrollRef,
1213
- title: `${filteredOutEntries.length} PACKAGE${filteredOutEntries.length === 1 ? "" : "S"} FILTERED BY TARGET`,
1214
- visible: filteredOutVisible,
1215
- width: 70,
1216
- children: jsxs(Box, { flexDirection: "column", children: [
1217
- jsx(Box, { marginBottom: 1, children: jsxs(Text, { dimColor: true, children: [
1218
- "These packages have newer versions available but are excluded by the current target constraint. Use",
1219
- " ",
1220
- jsx(Text, { bold: true, color: "white", children: "--target latest" }),
1221
- " ",
1222
- "to include them."
1223
- ] }) }),
1224
- filteredOutEntries.map((e) => jsxs(Box, { gap: 1, children: [
1225
- jsxs(Text, { children: [
1226
- " ",
1227
- e.packageName
1228
- ] }),
1229
- jsxs(Text, { dimColor: true, children: [
1230
- e.currentRange,
1231
- " ",
1232
- "→",
1233
- " ",
1234
- e.newRange
1235
- ] }),
1236
- jsx(Text, { bold: true, color: e.updateType === "major" ? "red" : e.updateType === "minor" ? "yellow" : "green", children: e.updateType })
1237
- ] }, e.packageName))
1238
- ] })
1239
- }
1240
- ) : null;
1241
- const listPanel = jsx(
1242
- PackageListPanel,
1243
- {
1244
- checkedEntries: state.checkedEntries,
1245
- entries: filteredEntries,
1246
- filterActive: state.filterActive,
1247
- filteredOutCount: filteredOutEntries.length,
1248
- filterText: state.filterText,
1249
- filterType: state.filterType,
1250
- focused: state.focusedPanel === "list",
1251
- groupedByCatalog: state.groupedByCatalog,
1252
- isDryRun,
1253
- onViewportHeightChange: setMeasuredViewportHeight,
1254
- scrollOffset: listScrollOffset,
1255
- selectedIndex: state.selectedIndex,
1256
- totalCatalogEntries,
1257
- totalChecked: checkedCount,
1258
- totalEntries: filteredEntries.length,
1259
- viewportHeight: listViewportHeight
1260
- }
1261
- );
1262
- const detailPanel = jsx(
1263
- PackageDetailPanel,
1264
- {
1265
- changelogUrl: selectedChangelog,
1266
- entry: selectedEntry,
1267
- focused: state.focusedPanel === "detail",
1268
- recommendation: selectedRecommendation,
1269
- scrollRef: detailScrollRef
1270
- }
1271
- );
1272
- if (isHorizontal) {
1273
- const detailWidth = Math.floor(columns * 0.35);
1274
- return jsxs(Box, { flexDirection: "column", height: rows, width: columns, children: [
1275
- jsxs(Box, { flexDirection: "row", flexGrow: 1, children: [
1276
- jsx(Box, { flexGrow: 1, children: listPanel }),
1277
- jsx(Box, { width: detailWidth, children: detailPanel })
1278
- ] }),
1279
- footer,
1280
- confirmDialog,
1281
- filteredOutDialog,
1282
- jsx(
1283
- QuitDialog,
1284
- {
1285
- autoExitSeconds: autoExitSeconds || 3,
1286
- onCancel: () => {
1287
- setQuitDialogVisible(false);
1288
- },
1289
- visible: quitDialogVisible
1290
- }
1291
- ),
1292
- helpPopup
1293
- ] });
1294
- }
1295
- const listHeight = Math.floor(rows * 0.55);
1296
- return jsxs(Box, { flexDirection: "column", height: rows, width: columns, children: [
1297
- jsx(Box, { height: listHeight, children: listPanel }),
1298
- jsx(Box, { flexGrow: 1, children: detailPanel }),
1299
- footer,
1300
- confirmDialog,
1301
- filteredOutDialog,
1302
- jsx(
1303
- QuitDialog,
1304
- {
1305
- autoExitSeconds: autoExitSeconds || 3,
1306
- onCancel: () => {
1307
- setQuitDialogVisible(false);
1308
- },
1309
- visible: quitDialogVisible
1310
- }
1311
- ),
1312
- helpPopup
1313
- ] });
1314
- };
1315
-
1316
- export { CheckProgressApp as C, UpdateStore as U, VisUpdateApp as V };