ripencli 0.3.3 → 0.3.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +588 -599
- package/package.json +12 -3
package/dist/cli.js
CHANGED
|
@@ -3,12 +3,12 @@ import { Box, Text, render, useApp, useInput, useStdout } from "ink";
|
|
|
3
3
|
import { createRequire } from "module";
|
|
4
4
|
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
|
|
5
5
|
import { join } from "path";
|
|
6
|
-
import { useEffect, useMemo, useRef, useState } from "react";
|
|
6
|
+
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
7
7
|
import { execa } from "execa";
|
|
8
8
|
import { homedir } from "os";
|
|
9
9
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
10
|
-
import { exec } from "child_process";
|
|
11
10
|
import { ScrollView } from "ink-scroll-view";
|
|
11
|
+
import { exec } from "child_process";
|
|
12
12
|
//#region src/detector.ts
|
|
13
13
|
function detectPackageManager(cwd) {
|
|
14
14
|
if (existsSync(join(cwd, "bun.lock"))) return "bun";
|
|
@@ -47,6 +47,64 @@ function getProjectInfo(cwd) {
|
|
|
47
47
|
};
|
|
48
48
|
}
|
|
49
49
|
//#endregion
|
|
50
|
+
//#region src/lib/versions.ts
|
|
51
|
+
/**
|
|
52
|
+
* Semver version parsing, comparison, and range utilities.
|
|
53
|
+
*
|
|
54
|
+
* Centralises logic that was previously duplicated across registry.ts and fetcher.ts.
|
|
55
|
+
*/
|
|
56
|
+
/**
|
|
57
|
+
* Parse a version string into [major, minor, patch].
|
|
58
|
+
* Strips any non-numeric prefix ("v", "next@", "package@v", etc.)
|
|
59
|
+
* and any pre-release suffix ("-beta.1", etc.).
|
|
60
|
+
*/
|
|
61
|
+
function parseVersion(v) {
|
|
62
|
+
return v.replace(/^[^0-9]*/, "").replace(/-.*$/, "").split(".").map(Number);
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Compare two parsed version arrays.
|
|
66
|
+
* Returns negative if a < b, positive if a > b, 0 if equal.
|
|
67
|
+
*/
|
|
68
|
+
function compareVersions(a, b) {
|
|
69
|
+
const pa = parseVersion(a);
|
|
70
|
+
const pb = parseVersion(b);
|
|
71
|
+
for (let i = 0; i < 3; i++) {
|
|
72
|
+
const diff = (pa[i] ?? 0) - (pb[i] ?? 0);
|
|
73
|
+
if (diff !== 0) return diff;
|
|
74
|
+
}
|
|
75
|
+
return 0;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Returns true when `latest` is a newer version than `current`.
|
|
79
|
+
*
|
|
80
|
+
* Pre-release suffixes are stripped for the base comparison.
|
|
81
|
+
* When base versions are equal, a pre-release current is considered
|
|
82
|
+
* older than a stable latest (e.g. "3.0.0-beta.8" < "3.0.0").
|
|
83
|
+
*/
|
|
84
|
+
function isNewerVersion(current, latest) {
|
|
85
|
+
const cmp = compareVersions(latest, current);
|
|
86
|
+
if (cmp > 0) return true;
|
|
87
|
+
if (cmp < 0) return false;
|
|
88
|
+
if (current.includes("-") && !latest.includes("-")) return true;
|
|
89
|
+
return false;
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Strip semver range prefixes to extract the base version and prefix.
|
|
93
|
+
* e.g. "^9.3.0" → { version: "9.3.0", prefix: "^" }
|
|
94
|
+
* Returns null for unparseable ranges (*, latest, git URLs, file: paths).
|
|
95
|
+
*/
|
|
96
|
+
function parseBaseVersion(range) {
|
|
97
|
+
let v = range.replace(/^workspace:/, "");
|
|
98
|
+
const prefixMatch = v.match(/^([~^>=<]+)/);
|
|
99
|
+
const prefix = prefixMatch ? prefixMatch[1] : "";
|
|
100
|
+
v = v.replace(/^[~^>=<]+/, "").trim();
|
|
101
|
+
if (/^\d+\.\d+\.\d+/.test(v)) return {
|
|
102
|
+
version: v,
|
|
103
|
+
prefix
|
|
104
|
+
};
|
|
105
|
+
return null;
|
|
106
|
+
}
|
|
107
|
+
//#endregion
|
|
50
108
|
//#region src/registry.ts
|
|
51
109
|
async function fetchVersions(packageName) {
|
|
52
110
|
try {
|
|
@@ -62,9 +120,8 @@ async function fetchVersions(packageName) {
|
|
|
62
120
|
date: times[v] ? new Date(times[v]).toISOString().split("T")[0] : "",
|
|
63
121
|
tag: tagByVersion[v]
|
|
64
122
|
})).sort((a, b) => {
|
|
65
|
-
const
|
|
66
|
-
|
|
67
|
-
for (let i = 0; i < 3; i++) if ((pb[i] ?? 0) !== (pa[i] ?? 0)) return (pb[i] ?? 0) - (pa[i] ?? 0);
|
|
123
|
+
const cmp = compareVersions(b.version, a.version);
|
|
124
|
+
if (cmp !== 0) return cmp;
|
|
68
125
|
const aHas = a.version.includes("-");
|
|
69
126
|
const bHas = b.version.includes("-");
|
|
70
127
|
if (aHas && !bHas) return 1;
|
|
@@ -79,27 +136,14 @@ async function fetchChangelog(packageName, fromVersion, toVersion) {
|
|
|
79
136
|
try {
|
|
80
137
|
const res = await fetch(`https://registry.npmjs.org/${encodeURIComponent(packageName)}/latest`);
|
|
81
138
|
if (!res.ok) return [];
|
|
82
|
-
const
|
|
83
|
-
|
|
84
|
-
if (!match) return [];
|
|
85
|
-
const repo = match[1].replace(/\.git$/, "");
|
|
139
|
+
const repo = extractGitHubRepo(await res.json());
|
|
140
|
+
if (!repo) return [];
|
|
86
141
|
const ghRes = await fetch(`https://api.github.com/repos/${repo}/releases?per_page=30`, { headers: { Accept: "application/vnd.github+json" } });
|
|
87
142
|
if (!ghRes.ok) return [];
|
|
88
143
|
const releases = await ghRes.json();
|
|
89
|
-
const parseVer = (v) => v.replace(/^[^0-9]*/, "").split(".").map(Number);
|
|
90
|
-
const cmpVer = (a, b) => {
|
|
91
|
-
for (let i = 0; i < 3; i++) {
|
|
92
|
-
const diff = (a[i] ?? 0) - (b[i] ?? 0);
|
|
93
|
-
if (diff !== 0) return diff;
|
|
94
|
-
}
|
|
95
|
-
return 0;
|
|
96
|
-
};
|
|
97
|
-
const from = parseVer(fromVersion);
|
|
98
|
-
const to = parseVer(toVersion);
|
|
99
144
|
const filtered = releases.filter((r) => {
|
|
100
145
|
if (r.draft || r.prerelease) return false;
|
|
101
|
-
|
|
102
|
-
return cmpVer(ver, from) > 0 && cmpVer(ver, to) <= 0;
|
|
146
|
+
return compareVersions(r.tag_name, fromVersion) > 0 && compareVersions(r.tag_name, toVersion) <= 0;
|
|
103
147
|
}).map((r) => ({
|
|
104
148
|
version: r.tag_name,
|
|
105
149
|
body: r.body?.trim() ?? "No release notes.",
|
|
@@ -127,50 +171,27 @@ async function fetchLatestVersion(packageName) {
|
|
|
127
171
|
return null;
|
|
128
172
|
}
|
|
129
173
|
}
|
|
130
|
-
function isNewerVersion(current, latest) {
|
|
131
|
-
const baseA = current.replace(/-.*$/, "");
|
|
132
|
-
const baseB = latest.replace(/-.*$/, "");
|
|
133
|
-
const a = baseA.split(".").map(Number);
|
|
134
|
-
const b = baseB.split(".").map(Number);
|
|
135
|
-
for (let i = 0; i < 3; i++) {
|
|
136
|
-
if ((b[i] ?? 0) > (a[i] ?? 0)) return true;
|
|
137
|
-
if ((b[i] ?? 0) < (a[i] ?? 0)) return false;
|
|
138
|
-
}
|
|
139
|
-
const currentHasPrerelease = current.includes("-");
|
|
140
|
-
const latestHasPrerelease = latest.includes("-");
|
|
141
|
-
if (currentHasPrerelease && !latestHasPrerelease) return true;
|
|
142
|
-
return false;
|
|
143
|
-
}
|
|
144
174
|
async function fetchRepoUrl(packageName) {
|
|
145
175
|
try {
|
|
146
176
|
const res = await fetch(`https://registry.npmjs.org/${encodeURIComponent(packageName)}/latest`);
|
|
147
177
|
if (!res.ok) return "";
|
|
148
|
-
const
|
|
149
|
-
|
|
150
|
-
if (!match) return "";
|
|
151
|
-
return `https://github.com/${match[1].replace(/\.git$/, "")}`;
|
|
178
|
+
const repo = extractGitHubRepo(await res.json());
|
|
179
|
+
return repo ? `https://github.com/${repo}` : "";
|
|
152
180
|
} catch {
|
|
153
181
|
return "";
|
|
154
182
|
}
|
|
155
183
|
}
|
|
156
|
-
//#endregion
|
|
157
|
-
//#region src/fetcher.ts
|
|
158
184
|
/**
|
|
159
|
-
*
|
|
160
|
-
*
|
|
161
|
-
* Returns null for unparseable ranges (*, latest, git URLs, file: paths).
|
|
185
|
+
* Extract "owner/repo" from npm registry package data.
|
|
186
|
+
* Handles both string and object repository fields.
|
|
162
187
|
*/
|
|
163
|
-
function
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
v = v.replace(/^[~^>=<]+/, "").trim();
|
|
168
|
-
if (/^\d+\.\d+\.\d+/.test(v)) return {
|
|
169
|
-
version: v,
|
|
170
|
-
prefix
|
|
171
|
-
};
|
|
172
|
-
return null;
|
|
188
|
+
function extractGitHubRepo(data) {
|
|
189
|
+
const match = (typeof data.repository === "string" ? data.repository : data.repository?.url ?? "").match(/github\.com[/:]([^/]+\/[^/]+)/);
|
|
190
|
+
if (!match) return null;
|
|
191
|
+
return match[1].replace(/\.git$/, "");
|
|
173
192
|
}
|
|
193
|
+
//#endregion
|
|
194
|
+
//#region src/fetcher.ts
|
|
174
195
|
function readPackageJsonDeps(cwd) {
|
|
175
196
|
const raw = readFileSync(join(cwd, "package.json"), "utf-8");
|
|
176
197
|
const pkg = JSON.parse(raw);
|
|
@@ -576,7 +597,7 @@ function incrementFrequency(packageNames) {
|
|
|
576
597
|
} catch {}
|
|
577
598
|
}
|
|
578
599
|
//#endregion
|
|
579
|
-
//#region src/ui/
|
|
600
|
+
//#region src/ui/package-list/types.ts
|
|
580
601
|
const TYPE_COLORS = {
|
|
581
602
|
dependencies: "cyan",
|
|
582
603
|
devDependencies: "magenta",
|
|
@@ -592,8 +613,8 @@ const GROUP_ORDER = [
|
|
|
592
613
|
"devDependencies",
|
|
593
614
|
"global"
|
|
594
615
|
];
|
|
595
|
-
|
|
596
|
-
|
|
616
|
+
//#endregion
|
|
617
|
+
//#region src/ui/package-list/build-rows.ts
|
|
597
618
|
function getScope(name) {
|
|
598
619
|
return name.match(/^(@[^/]+)\//)?.[1] ?? null;
|
|
599
620
|
}
|
|
@@ -628,7 +649,8 @@ function buildDisplayRows(packages, groupByScope = false, groupScopes = [], grou
|
|
|
628
649
|
kind: "header",
|
|
629
650
|
groupType: type,
|
|
630
651
|
label,
|
|
631
|
-
packages: allPkgs
|
|
652
|
+
packages: allPkgs,
|
|
653
|
+
packageIndices: items.map((i) => i.index)
|
|
632
654
|
});
|
|
633
655
|
if (groupByScope && groupScopes.length > 0) {
|
|
634
656
|
const scopeMap = /* @__PURE__ */ new Map();
|
|
@@ -825,11 +847,13 @@ function groupCheckbox(packages) {
|
|
|
825
847
|
};
|
|
826
848
|
}
|
|
827
849
|
function computeMaxPerGroup(terminalRows, groupCount) {
|
|
828
|
-
const available = terminalRows -
|
|
850
|
+
const available = terminalRows - 8 - groupCount * 7 - 2;
|
|
829
851
|
const perGroup = Math.floor(available / groupCount);
|
|
830
852
|
return Math.max(3, perGroup);
|
|
831
853
|
}
|
|
832
|
-
|
|
854
|
+
//#endregion
|
|
855
|
+
//#region src/ui/package-list/PackageList.tsx
|
|
856
|
+
function PackageList({ packages, onToggle, onToggleMany, onSelectVersion, onViewChangelog, onConfirm, onOpenSettings, groupByScope = false, groupScopes, groupsOnTop = false, frequencySort = false, frequency = {}, separateDevDeps = true, isActive = true }) {
|
|
833
857
|
const [focusedIndex, setFocusedIndex] = useState(0);
|
|
834
858
|
const allRows = useMemo(() => buildDisplayRows(packages, groupByScope, groupScopes, groupsOnTop, frequencySort, frequency, separateDevDeps), [
|
|
835
859
|
packages,
|
|
@@ -853,7 +877,8 @@ function PackageList({ packages, onToggle, onToggleGroup, onToggleMany, onSelect
|
|
|
853
877
|
setInitialized(true);
|
|
854
878
|
}
|
|
855
879
|
}, [allScopeKeys, initialized]);
|
|
856
|
-
const
|
|
880
|
+
const effectiveCollapsed = !initialized && allScopeKeys.size > 0 ? allScopeKeys : collapsedScopes;
|
|
881
|
+
const visibleRows = useMemo(() => filterCollapsed(allRows, effectiveCollapsed), [allRows, effectiveCollapsed]);
|
|
857
882
|
const groups = useMemo(() => buildGroups(visibleRows), [visibleRows]);
|
|
858
883
|
const { stdout } = useStdout();
|
|
859
884
|
const terminalRows = stdout?.rows ?? 24;
|
|
@@ -872,6 +897,16 @@ function PackageList({ packages, onToggle, onToggleGroup, onToggleMany, onSelect
|
|
|
872
897
|
if (focusedIndex >= visibleRows.length) setFocusedIndex(Math.max(0, visibleRows.length - 1));
|
|
873
898
|
}, [visibleRows.length, focusedIndex]);
|
|
874
899
|
const toggleCollapse = (scopeKey) => {
|
|
900
|
+
if (!initialized && allScopeKeys.size > 0) {
|
|
901
|
+
setCollapsedScopes(() => {
|
|
902
|
+
const next = new Set(allScopeKeys);
|
|
903
|
+
if (next.has(scopeKey)) next.delete(scopeKey);
|
|
904
|
+
else next.add(scopeKey);
|
|
905
|
+
return next;
|
|
906
|
+
});
|
|
907
|
+
setInitialized(true);
|
|
908
|
+
return;
|
|
909
|
+
}
|
|
875
910
|
setCollapsedScopes((prev) => {
|
|
876
911
|
const next = new Set(prev);
|
|
877
912
|
if (next.has(scopeKey)) next.delete(scopeKey);
|
|
@@ -896,16 +931,16 @@ function PackageList({ packages, onToggle, onToggleGroup, onToggleMany, onSelect
|
|
|
896
931
|
if (!focused) return;
|
|
897
932
|
if (focused.kind === "scope-header") {
|
|
898
933
|
const scopeKey = `${focused.groupType}::${focused.scope}`;
|
|
899
|
-
if (key.leftArrow && !
|
|
934
|
+
if (key.leftArrow && !effectiveCollapsed.has(scopeKey)) {
|
|
900
935
|
toggleCollapse(scopeKey);
|
|
901
936
|
return;
|
|
902
937
|
}
|
|
903
|
-
if (key.rightArrow &&
|
|
938
|
+
if (key.rightArrow && effectiveCollapsed.has(scopeKey)) {
|
|
904
939
|
toggleCollapse(scopeKey);
|
|
905
940
|
return;
|
|
906
941
|
}
|
|
907
942
|
}
|
|
908
|
-
if (input === " ") if (focused.kind === "header")
|
|
943
|
+
if (input === " ") if (focused.kind === "header") onToggleMany(focused.packageIndices);
|
|
909
944
|
else if (focused.kind === "scope-header") onToggleMany(focused.packageIndices);
|
|
910
945
|
else onToggle(focused.packageIndex);
|
|
911
946
|
if (input === "v" && !key.ctrl && focused.kind === "package") onSelectVersion(focused.packageIndex);
|
|
@@ -987,199 +1022,13 @@ function PackageList({ packages, onToggle, onToggleGroup, onToggleMany, onSelect
|
|
|
987
1022
|
})
|
|
988
1023
|
})]
|
|
989
1024
|
}),
|
|
990
|
-
groups.map((group) => {
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
const needsScroll = totalItems > maxVisible;
|
|
998
|
-
const focusedLocalIndex = group.items.findIndex((item) => item.visibleIndex === focusedIndex);
|
|
999
|
-
return /* @__PURE__ */ jsxs(Box, {
|
|
1000
|
-
flexDirection: "column",
|
|
1001
|
-
marginBottom: 1,
|
|
1002
|
-
children: [/* @__PURE__ */ jsxs(Box, {
|
|
1003
|
-
gap: 1,
|
|
1004
|
-
children: [
|
|
1005
|
-
/* @__PURE__ */ jsx(Text, {
|
|
1006
|
-
color: "greenBright",
|
|
1007
|
-
children: headerFocused ? "❯" : " "
|
|
1008
|
-
}),
|
|
1009
|
-
/* @__PURE__ */ jsx(Text, {
|
|
1010
|
-
color: check.color,
|
|
1011
|
-
children: check.symbol
|
|
1012
|
-
}),
|
|
1013
|
-
/* @__PURE__ */ jsx(Text, {
|
|
1014
|
-
bold: headerFocused,
|
|
1015
|
-
color: typeColor,
|
|
1016
|
-
children: group.label
|
|
1017
|
-
}),
|
|
1018
|
-
/* @__PURE__ */ jsxs(Text, {
|
|
1019
|
-
color: "gray",
|
|
1020
|
-
children: [
|
|
1021
|
-
"(",
|
|
1022
|
-
group.allPackages.length,
|
|
1023
|
-
")"
|
|
1024
|
-
]
|
|
1025
|
-
}),
|
|
1026
|
-
needsScroll && focusedLocalIndex >= 0 && /* @__PURE__ */ jsxs(Text, {
|
|
1027
|
-
color: "gray",
|
|
1028
|
-
children: [
|
|
1029
|
-
focusedLocalIndex + 1,
|
|
1030
|
-
"/",
|
|
1031
|
-
totalItems
|
|
1032
|
-
]
|
|
1033
|
-
})
|
|
1034
|
-
]
|
|
1035
|
-
}), /* @__PURE__ */ jsxs(Box, {
|
|
1036
|
-
flexDirection: "column",
|
|
1037
|
-
borderStyle: "round",
|
|
1038
|
-
borderColor: headerFocused ? typeColor : "gray",
|
|
1039
|
-
paddingX: 1,
|
|
1040
|
-
children: [
|
|
1041
|
-
/* @__PURE__ */ jsxs(Box, {
|
|
1042
|
-
gap: 2,
|
|
1043
|
-
marginBottom: 0,
|
|
1044
|
-
children: [
|
|
1045
|
-
/* @__PURE__ */ jsx(Text, {
|
|
1046
|
-
color: "gray",
|
|
1047
|
-
children: " "
|
|
1048
|
-
}),
|
|
1049
|
-
/* @__PURE__ */ jsx(Box, {
|
|
1050
|
-
width: 28,
|
|
1051
|
-
children: /* @__PURE__ */ jsx(Text, {
|
|
1052
|
-
color: "gray",
|
|
1053
|
-
children: "package"
|
|
1054
|
-
})
|
|
1055
|
-
}),
|
|
1056
|
-
/* @__PURE__ */ jsx(Box, {
|
|
1057
|
-
width: 14,
|
|
1058
|
-
children: /* @__PURE__ */ jsx(Text, {
|
|
1059
|
-
color: "gray",
|
|
1060
|
-
children: "current"
|
|
1061
|
-
})
|
|
1062
|
-
}),
|
|
1063
|
-
/* @__PURE__ */ jsx(Box, {
|
|
1064
|
-
width: 14,
|
|
1065
|
-
children: /* @__PURE__ */ jsx(Text, {
|
|
1066
|
-
color: "gray",
|
|
1067
|
-
children: "target"
|
|
1068
|
-
})
|
|
1069
|
-
}),
|
|
1070
|
-
/* @__PURE__ */ jsx(Box, {
|
|
1071
|
-
width: 14,
|
|
1072
|
-
children: /* @__PURE__ */ jsx(Text, {
|
|
1073
|
-
color: "gray",
|
|
1074
|
-
children: "latest"
|
|
1075
|
-
})
|
|
1076
|
-
})
|
|
1077
|
-
]
|
|
1078
|
-
}),
|
|
1079
|
-
needsScroll && /* @__PURE__ */ jsx(Text, {
|
|
1080
|
-
color: "gray",
|
|
1081
|
-
children: offset > 0 ? ` ↑ ${offset} more above` : " "
|
|
1082
|
-
}),
|
|
1083
|
-
visibleItems.map((item) => {
|
|
1084
|
-
const { row } = item;
|
|
1085
|
-
const isFocused = item.visibleIndex === focusedIndex;
|
|
1086
|
-
if (row.kind === "scope-header") {
|
|
1087
|
-
const scopeKey = `${row.groupType}::${row.scope}`;
|
|
1088
|
-
const isCollapsed = collapsedScopes.has(scopeKey);
|
|
1089
|
-
const scopeCheck = groupCheckbox(row.packages);
|
|
1090
|
-
return /* @__PURE__ */ jsxs(Box, {
|
|
1091
|
-
gap: 2,
|
|
1092
|
-
children: [
|
|
1093
|
-
/* @__PURE__ */ jsx(Text, {
|
|
1094
|
-
color: "greenBright",
|
|
1095
|
-
children: isFocused ? "❯" : " "
|
|
1096
|
-
}),
|
|
1097
|
-
/* @__PURE__ */ jsx(Text, {
|
|
1098
|
-
color: "gray",
|
|
1099
|
-
children: isCollapsed ? "▶" : "▼"
|
|
1100
|
-
}),
|
|
1101
|
-
/* @__PURE__ */ jsx(Text, {
|
|
1102
|
-
color: scopeCheck.color,
|
|
1103
|
-
children: scopeCheck.symbol
|
|
1104
|
-
}),
|
|
1105
|
-
/* @__PURE__ */ jsxs(Text, {
|
|
1106
|
-
bold: isFocused,
|
|
1107
|
-
color: isFocused ? "whiteBright" : "white",
|
|
1108
|
-
children: [
|
|
1109
|
-
row.scope,
|
|
1110
|
-
" (",
|
|
1111
|
-
row.packages.length,
|
|
1112
|
-
")"
|
|
1113
|
-
]
|
|
1114
|
-
})
|
|
1115
|
-
]
|
|
1116
|
-
}, scopeKey);
|
|
1117
|
-
}
|
|
1118
|
-
if (row.kind !== "package") return null;
|
|
1119
|
-
const pkg = row.pkg;
|
|
1120
|
-
const isMajorBump = parseInt(pkg.latest) > parseInt(pkg.current);
|
|
1121
|
-
return /* @__PURE__ */ jsxs(Box, {
|
|
1122
|
-
gap: 2,
|
|
1123
|
-
children: [
|
|
1124
|
-
/* @__PURE__ */ jsx(Text, {
|
|
1125
|
-
color: "greenBright",
|
|
1126
|
-
children: isFocused ? "❯" : " "
|
|
1127
|
-
}),
|
|
1128
|
-
row.indented && /* @__PURE__ */ jsx(Text, { children: " " }),
|
|
1129
|
-
/* @__PURE__ */ jsx(Text, {
|
|
1130
|
-
color: pkg.selected ? "greenBright" : "gray",
|
|
1131
|
-
children: pkg.selected ? "◉" : "○"
|
|
1132
|
-
}),
|
|
1133
|
-
/* @__PURE__ */ jsx(Box, {
|
|
1134
|
-
width: row.indented ? 24 : 26,
|
|
1135
|
-
children: /* @__PURE__ */ jsx(Text, {
|
|
1136
|
-
bold: isFocused,
|
|
1137
|
-
color: isFocused ? "whiteBright" : "white",
|
|
1138
|
-
children: (() => {
|
|
1139
|
-
const maxLen = row.indented ? 24 : 26;
|
|
1140
|
-
return pkg.name.length > maxLen ? pkg.name.slice(0, maxLen - 2) + "…" : pkg.name;
|
|
1141
|
-
})()
|
|
1142
|
-
})
|
|
1143
|
-
}),
|
|
1144
|
-
/* @__PURE__ */ jsx(Box, {
|
|
1145
|
-
width: 14,
|
|
1146
|
-
children: /* @__PURE__ */ jsx(Text, {
|
|
1147
|
-
color: "red",
|
|
1148
|
-
children: pkg.current
|
|
1149
|
-
})
|
|
1150
|
-
}),
|
|
1151
|
-
/* @__PURE__ */ jsx(Box, {
|
|
1152
|
-
width: 14,
|
|
1153
|
-
children: /* @__PURE__ */ jsx(Text, {
|
|
1154
|
-
color: "greenBright",
|
|
1155
|
-
children: pkg.targetVersion ?? pkg.latest
|
|
1156
|
-
})
|
|
1157
|
-
}),
|
|
1158
|
-
/* @__PURE__ */ jsx(Box, {
|
|
1159
|
-
width: 14,
|
|
1160
|
-
children: /* @__PURE__ */ jsx(Text, {
|
|
1161
|
-
color: "gray",
|
|
1162
|
-
children: pkg.latest
|
|
1163
|
-
})
|
|
1164
|
-
}),
|
|
1165
|
-
/* @__PURE__ */ jsx(Box, {
|
|
1166
|
-
width: 9,
|
|
1167
|
-
children: isMajorBump ? /* @__PURE__ */ jsx(Text, {
|
|
1168
|
-
color: "yellow",
|
|
1169
|
-
children: "⚠ major"
|
|
1170
|
-
}) : /* @__PURE__ */ jsx(Text, { children: " " })
|
|
1171
|
-
})
|
|
1172
|
-
]
|
|
1173
|
-
}, pkg.name);
|
|
1174
|
-
}),
|
|
1175
|
-
needsScroll && /* @__PURE__ */ jsx(Text, {
|
|
1176
|
-
color: "gray",
|
|
1177
|
-
children: offset + maxVisible < totalItems ? ` ↓ ${totalItems - offset - maxVisible} more below` : " "
|
|
1178
|
-
})
|
|
1179
|
-
]
|
|
1180
|
-
})]
|
|
1181
|
-
}, group.type);
|
|
1182
|
-
}),
|
|
1025
|
+
groups.map((group) => /* @__PURE__ */ jsx(PackageGroupBox, {
|
|
1026
|
+
group,
|
|
1027
|
+
focusedIndex,
|
|
1028
|
+
collapsedScopes,
|
|
1029
|
+
scrollOffset: scrollOffsetsRef.current[group.type] ?? 0,
|
|
1030
|
+
maxVisible
|
|
1031
|
+
}, group.type)),
|
|
1183
1032
|
/* @__PURE__ */ jsx(Box, {
|
|
1184
1033
|
flexDirection: "column",
|
|
1185
1034
|
children: /* @__PURE__ */ jsxs(Box, {
|
|
@@ -1208,6 +1057,198 @@ function PackageList({ packages, onToggle, onToggleGroup, onToggleMany, onSelect
|
|
|
1208
1057
|
]
|
|
1209
1058
|
});
|
|
1210
1059
|
}
|
|
1060
|
+
function PackageGroupBox({ group, focusedIndex, collapsedScopes, scrollOffset, maxVisible }) {
|
|
1061
|
+
const check = groupCheckbox(group.allPackages);
|
|
1062
|
+
const headerFocused = focusedIndex === group.headerVisibleIndex;
|
|
1063
|
+
const typeColor = TYPE_COLORS[group.type] ?? "white";
|
|
1064
|
+
const visibleItems = group.items.slice(scrollOffset, scrollOffset + maxVisible);
|
|
1065
|
+
const totalItems = group.items.length;
|
|
1066
|
+
const needsScroll = totalItems > maxVisible;
|
|
1067
|
+
const focusedLocalIndex = group.items.findIndex((item) => item.visibleIndex === focusedIndex);
|
|
1068
|
+
return /* @__PURE__ */ jsxs(Box, {
|
|
1069
|
+
flexDirection: "column",
|
|
1070
|
+
marginBottom: 1,
|
|
1071
|
+
children: [/* @__PURE__ */ jsxs(Box, {
|
|
1072
|
+
gap: 1,
|
|
1073
|
+
children: [
|
|
1074
|
+
/* @__PURE__ */ jsx(Text, {
|
|
1075
|
+
color: "greenBright",
|
|
1076
|
+
children: headerFocused ? "❯" : " "
|
|
1077
|
+
}),
|
|
1078
|
+
/* @__PURE__ */ jsx(Text, {
|
|
1079
|
+
color: check.color,
|
|
1080
|
+
children: check.symbol
|
|
1081
|
+
}),
|
|
1082
|
+
/* @__PURE__ */ jsx(Text, {
|
|
1083
|
+
bold: headerFocused,
|
|
1084
|
+
color: typeColor,
|
|
1085
|
+
children: group.label
|
|
1086
|
+
}),
|
|
1087
|
+
/* @__PURE__ */ jsxs(Text, {
|
|
1088
|
+
color: "gray",
|
|
1089
|
+
children: [
|
|
1090
|
+
"(",
|
|
1091
|
+
group.allPackages.length,
|
|
1092
|
+
")"
|
|
1093
|
+
]
|
|
1094
|
+
}),
|
|
1095
|
+
needsScroll && focusedLocalIndex >= 0 && /* @__PURE__ */ jsxs(Text, {
|
|
1096
|
+
color: "gray",
|
|
1097
|
+
children: [
|
|
1098
|
+
focusedLocalIndex + 1,
|
|
1099
|
+
"/",
|
|
1100
|
+
totalItems
|
|
1101
|
+
]
|
|
1102
|
+
})
|
|
1103
|
+
]
|
|
1104
|
+
}), /* @__PURE__ */ jsxs(Box, {
|
|
1105
|
+
flexDirection: "column",
|
|
1106
|
+
borderStyle: "round",
|
|
1107
|
+
borderColor: headerFocused ? typeColor : "gray",
|
|
1108
|
+
paddingX: 1,
|
|
1109
|
+
children: [
|
|
1110
|
+
/* @__PURE__ */ jsxs(Box, {
|
|
1111
|
+
gap: 2,
|
|
1112
|
+
marginBottom: 0,
|
|
1113
|
+
children: [
|
|
1114
|
+
/* @__PURE__ */ jsx(Text, {
|
|
1115
|
+
color: "gray",
|
|
1116
|
+
children: " "
|
|
1117
|
+
}),
|
|
1118
|
+
/* @__PURE__ */ jsx(Box, {
|
|
1119
|
+
width: 28,
|
|
1120
|
+
children: /* @__PURE__ */ jsx(Text, {
|
|
1121
|
+
color: "gray",
|
|
1122
|
+
children: "package"
|
|
1123
|
+
})
|
|
1124
|
+
}),
|
|
1125
|
+
/* @__PURE__ */ jsx(Box, {
|
|
1126
|
+
width: 14,
|
|
1127
|
+
children: /* @__PURE__ */ jsx(Text, {
|
|
1128
|
+
color: "gray",
|
|
1129
|
+
children: "current"
|
|
1130
|
+
})
|
|
1131
|
+
}),
|
|
1132
|
+
/* @__PURE__ */ jsx(Box, {
|
|
1133
|
+
width: 14,
|
|
1134
|
+
children: /* @__PURE__ */ jsx(Text, {
|
|
1135
|
+
color: "gray",
|
|
1136
|
+
children: "target"
|
|
1137
|
+
})
|
|
1138
|
+
}),
|
|
1139
|
+
/* @__PURE__ */ jsx(Box, {
|
|
1140
|
+
width: 14,
|
|
1141
|
+
children: /* @__PURE__ */ jsx(Text, {
|
|
1142
|
+
color: "gray",
|
|
1143
|
+
children: "latest"
|
|
1144
|
+
})
|
|
1145
|
+
})
|
|
1146
|
+
]
|
|
1147
|
+
}),
|
|
1148
|
+
needsScroll && /* @__PURE__ */ jsx(Text, {
|
|
1149
|
+
color: "gray",
|
|
1150
|
+
children: scrollOffset > 0 ? ` ↑ ${scrollOffset} more above` : " "
|
|
1151
|
+
}),
|
|
1152
|
+
visibleItems.map((item) => {
|
|
1153
|
+
const { row } = item;
|
|
1154
|
+
const isFocused = item.visibleIndex === focusedIndex;
|
|
1155
|
+
if (row.kind === "scope-header") {
|
|
1156
|
+
const scopeKey = `${row.groupType}::${row.scope}`;
|
|
1157
|
+
const isCollapsed = collapsedScopes.has(scopeKey);
|
|
1158
|
+
const scopeCheck = groupCheckbox(row.packages);
|
|
1159
|
+
return /* @__PURE__ */ jsxs(Box, {
|
|
1160
|
+
gap: 2,
|
|
1161
|
+
children: [
|
|
1162
|
+
/* @__PURE__ */ jsx(Text, {
|
|
1163
|
+
color: "greenBright",
|
|
1164
|
+
children: isFocused ? "❯" : " "
|
|
1165
|
+
}),
|
|
1166
|
+
/* @__PURE__ */ jsx(Text, {
|
|
1167
|
+
color: "gray",
|
|
1168
|
+
children: isCollapsed ? "▶" : "▼"
|
|
1169
|
+
}),
|
|
1170
|
+
/* @__PURE__ */ jsx(Text, {
|
|
1171
|
+
color: scopeCheck.color,
|
|
1172
|
+
children: scopeCheck.symbol
|
|
1173
|
+
}),
|
|
1174
|
+
/* @__PURE__ */ jsxs(Text, {
|
|
1175
|
+
bold: isFocused,
|
|
1176
|
+
color: isFocused ? "whiteBright" : "white",
|
|
1177
|
+
children: [
|
|
1178
|
+
row.scope,
|
|
1179
|
+
" (",
|
|
1180
|
+
row.packages.length,
|
|
1181
|
+
")"
|
|
1182
|
+
]
|
|
1183
|
+
})
|
|
1184
|
+
]
|
|
1185
|
+
}, scopeKey);
|
|
1186
|
+
}
|
|
1187
|
+
if (row.kind !== "package") return null;
|
|
1188
|
+
const pkg = row.pkg;
|
|
1189
|
+
const isMajorBump = parseInt(pkg.latest) > parseInt(pkg.current);
|
|
1190
|
+
return /* @__PURE__ */ jsxs(Box, {
|
|
1191
|
+
gap: 2,
|
|
1192
|
+
children: [
|
|
1193
|
+
/* @__PURE__ */ jsx(Text, {
|
|
1194
|
+
color: "greenBright",
|
|
1195
|
+
children: isFocused ? "❯" : " "
|
|
1196
|
+
}),
|
|
1197
|
+
row.indented && /* @__PURE__ */ jsx(Text, { children: " " }),
|
|
1198
|
+
/* @__PURE__ */ jsx(Text, {
|
|
1199
|
+
color: pkg.selected ? "greenBright" : "gray",
|
|
1200
|
+
children: pkg.selected ? "◉" : "○"
|
|
1201
|
+
}),
|
|
1202
|
+
/* @__PURE__ */ jsx(Box, {
|
|
1203
|
+
width: row.indented ? 24 : 26,
|
|
1204
|
+
children: /* @__PURE__ */ jsx(Text, {
|
|
1205
|
+
bold: isFocused,
|
|
1206
|
+
color: isFocused ? "whiteBright" : "white",
|
|
1207
|
+
children: (() => {
|
|
1208
|
+
const maxLen = row.indented ? 24 : 26;
|
|
1209
|
+
return pkg.name.length > maxLen ? pkg.name.slice(0, maxLen - 2) + "…" : pkg.name;
|
|
1210
|
+
})()
|
|
1211
|
+
})
|
|
1212
|
+
}),
|
|
1213
|
+
/* @__PURE__ */ jsx(Box, {
|
|
1214
|
+
width: 14,
|
|
1215
|
+
children: /* @__PURE__ */ jsx(Text, {
|
|
1216
|
+
color: "red",
|
|
1217
|
+
children: pkg.current
|
|
1218
|
+
})
|
|
1219
|
+
}),
|
|
1220
|
+
/* @__PURE__ */ jsx(Box, {
|
|
1221
|
+
width: 14,
|
|
1222
|
+
children: /* @__PURE__ */ jsx(Text, {
|
|
1223
|
+
color: "greenBright",
|
|
1224
|
+
children: pkg.targetVersion ?? pkg.latest
|
|
1225
|
+
})
|
|
1226
|
+
}),
|
|
1227
|
+
/* @__PURE__ */ jsx(Box, {
|
|
1228
|
+
width: 14,
|
|
1229
|
+
children: /* @__PURE__ */ jsx(Text, {
|
|
1230
|
+
color: "gray",
|
|
1231
|
+
children: pkg.latest
|
|
1232
|
+
})
|
|
1233
|
+
}),
|
|
1234
|
+
/* @__PURE__ */ jsx(Box, {
|
|
1235
|
+
width: 9,
|
|
1236
|
+
children: isMajorBump ? /* @__PURE__ */ jsx(Text, {
|
|
1237
|
+
color: "yellow",
|
|
1238
|
+
children: "⚠ major"
|
|
1239
|
+
}) : /* @__PURE__ */ jsx(Text, { children: " " })
|
|
1240
|
+
})
|
|
1241
|
+
]
|
|
1242
|
+
}, pkg.name);
|
|
1243
|
+
}),
|
|
1244
|
+
needsScroll && /* @__PURE__ */ jsx(Text, {
|
|
1245
|
+
color: "gray",
|
|
1246
|
+
children: scrollOffset + maxVisible < totalItems ? ` ↓ ${totalItems - scrollOffset - maxVisible} more below` : " "
|
|
1247
|
+
})
|
|
1248
|
+
]
|
|
1249
|
+
})]
|
|
1250
|
+
}, group.type);
|
|
1251
|
+
}
|
|
1211
1252
|
//#endregion
|
|
1212
1253
|
//#region src/ui/VersionPicker.tsx
|
|
1213
1254
|
function VersionPicker({ pkg, onSelect, onCancel }) {
|
|
@@ -1371,6 +1412,12 @@ function VersionPicker({ pkg, onSelect, onCancel }) {
|
|
|
1371
1412
|
});
|
|
1372
1413
|
}
|
|
1373
1414
|
//#endregion
|
|
1415
|
+
//#region src/lib/utils.ts
|
|
1416
|
+
/** Open a URL in the user's default browser (cross-platform). */
|
|
1417
|
+
function openInBrowser(url) {
|
|
1418
|
+
exec(process.platform === "win32" ? `start "" "${url}"` : process.platform === "darwin" ? `open "${url}"` : `xdg-open "${url}"`);
|
|
1419
|
+
}
|
|
1420
|
+
//#endregion
|
|
1374
1421
|
//#region src/ui/MarkdownLine.tsx
|
|
1375
1422
|
function parseInline(raw) {
|
|
1376
1423
|
const segments = [];
|
|
@@ -1439,9 +1486,6 @@ function MarkdownLine({ line, baseColor = "white", baseDim = false }) {
|
|
|
1439
1486
|
}
|
|
1440
1487
|
//#endregion
|
|
1441
1488
|
//#region src/ui/ChangelogPanel.tsx
|
|
1442
|
-
function openInBrowser(url) {
|
|
1443
|
-
exec(process.platform === "win32" ? `start "" "${url}"` : process.platform === "darwin" ? `open "${url}"` : `xdg-open "${url}"`);
|
|
1444
|
-
}
|
|
1445
1489
|
function ChangelogPanel({ pkg, onClose }) {
|
|
1446
1490
|
const [entries, setEntries] = useState([]);
|
|
1447
1491
|
const [repoUrl, setRepoUrl] = useState("");
|
|
@@ -1472,6 +1516,14 @@ function ChangelogPanel({ pkg, onClose }) {
|
|
|
1472
1516
|
setTimeout(() => setOpened(false), 2e3);
|
|
1473
1517
|
};
|
|
1474
1518
|
const currentEntry = entries[activeEntry];
|
|
1519
|
+
const clampedScrollBy = (delta) => {
|
|
1520
|
+
const sv = scrollRef.current;
|
|
1521
|
+
if (!sv) return;
|
|
1522
|
+
const maxOffset = sv.getBottomOffset();
|
|
1523
|
+
const current = sv.getScrollOffset();
|
|
1524
|
+
const target = Math.max(0, Math.min(maxOffset, current + delta));
|
|
1525
|
+
sv.scrollTo(target);
|
|
1526
|
+
};
|
|
1475
1527
|
useInput((input, key) => {
|
|
1476
1528
|
if (key.escape || input === "q" || input === "c") {
|
|
1477
1529
|
onClose();
|
|
@@ -1487,16 +1539,10 @@ function ChangelogPanel({ pkg, onClose }) {
|
|
|
1487
1539
|
setActiveEntry((prev) => Math.min(entries.length - 1, prev + 1));
|
|
1488
1540
|
return;
|
|
1489
1541
|
}
|
|
1490
|
-
if (key.upArrow)
|
|
1491
|
-
if (key.downArrow)
|
|
1492
|
-
if (key.pageUp)
|
|
1493
|
-
|
|
1494
|
-
scrollRef.current?.scrollBy(-h);
|
|
1495
|
-
}
|
|
1496
|
-
if (key.pageDown) {
|
|
1497
|
-
const h = scrollRef.current?.getViewportHeight() ?? 10;
|
|
1498
|
-
scrollRef.current?.scrollBy(h);
|
|
1499
|
-
}
|
|
1542
|
+
if (key.upArrow) clampedScrollBy(-1);
|
|
1543
|
+
if (key.downArrow) clampedScrollBy(1);
|
|
1544
|
+
if (key.pageUp) clampedScrollBy(-(scrollRef.current?.getViewportHeight() ?? 10));
|
|
1545
|
+
if (key.pageDown) clampedScrollBy(scrollRef.current?.getViewportHeight() ?? 10);
|
|
1500
1546
|
if (input === "r" && releasesPageUrl) triggerOpen(releasesPageUrl);
|
|
1501
1547
|
if (input === "o" && currentEntry?.url) triggerOpen(currentEntry.url);
|
|
1502
1548
|
});
|
|
@@ -1609,7 +1655,7 @@ function ChangelogPanel({ pkg, onClose }) {
|
|
|
1609
1655
|
children: " Check the package repository manually."
|
|
1610
1656
|
})]
|
|
1611
1657
|
}) : currentEntry ? /* @__PURE__ */ jsx(Box, {
|
|
1612
|
-
height: 18,
|
|
1658
|
+
height: Math.min(currentEntry.body.split("\n").length, 18),
|
|
1613
1659
|
flexDirection: "column",
|
|
1614
1660
|
children: /* @__PURE__ */ jsx(ScrollView, {
|
|
1615
1661
|
ref: scrollRef,
|
|
@@ -1763,6 +1809,46 @@ function UpdateResults({ results, onDone }) {
|
|
|
1763
1809
|
});
|
|
1764
1810
|
}
|
|
1765
1811
|
//#endregion
|
|
1812
|
+
//#region src/ui/SettingsToggle.tsx
|
|
1813
|
+
function SettingsToggle({ label, description, checked, focused, disabled = false }) {
|
|
1814
|
+
return /* @__PURE__ */ jsxs(Box, {
|
|
1815
|
+
flexDirection: "column",
|
|
1816
|
+
marginBottom: 1,
|
|
1817
|
+
children: [/* @__PURE__ */ jsxs(Box, {
|
|
1818
|
+
gap: 1,
|
|
1819
|
+
children: [
|
|
1820
|
+
/* @__PURE__ */ jsx(Text, {
|
|
1821
|
+
dimColor: disabled,
|
|
1822
|
+
color: "greenBright",
|
|
1823
|
+
children: focused ? ">" : " "
|
|
1824
|
+
}),
|
|
1825
|
+
/* @__PURE__ */ jsxs(Text, {
|
|
1826
|
+
dimColor: disabled,
|
|
1827
|
+
color: checked && !disabled ? "greenBright" : "gray",
|
|
1828
|
+
children: [
|
|
1829
|
+
"[",
|
|
1830
|
+
checked ? "x" : " ",
|
|
1831
|
+
"]"
|
|
1832
|
+
]
|
|
1833
|
+
}),
|
|
1834
|
+
/* @__PURE__ */ jsx(Text, {
|
|
1835
|
+
dimColor: disabled,
|
|
1836
|
+
bold: focused,
|
|
1837
|
+
color: disabled ? "gray" : focused ? "whiteBright" : "white",
|
|
1838
|
+
children: label
|
|
1839
|
+
})
|
|
1840
|
+
]
|
|
1841
|
+
}), /* @__PURE__ */ jsx(Box, {
|
|
1842
|
+
marginLeft: 6,
|
|
1843
|
+
children: /* @__PURE__ */ jsx(Text, {
|
|
1844
|
+
dimColor: disabled,
|
|
1845
|
+
color: "gray",
|
|
1846
|
+
children: description
|
|
1847
|
+
})
|
|
1848
|
+
})]
|
|
1849
|
+
});
|
|
1850
|
+
}
|
|
1851
|
+
//#endregion
|
|
1766
1852
|
//#region src/ui/Settings.tsx
|
|
1767
1853
|
function Settings({ config, onConfigChange, onClose }) {
|
|
1768
1854
|
const [inputMode, setInputMode] = useState(false);
|
|
@@ -1851,10 +1937,6 @@ function Settings({ config, onConfigChange, onClose }) {
|
|
|
1851
1937
|
}
|
|
1852
1938
|
if (key.escape || input === "s") onClose();
|
|
1853
1939
|
});
|
|
1854
|
-
const freqToggleFocused = currentRow?.type === "toggle-frequency";
|
|
1855
|
-
const separateDevFocused = currentRow?.type === "toggle-separate-dev";
|
|
1856
|
-
const groupToggleFocused = currentRow?.type === "toggle-group";
|
|
1857
|
-
const groupsTopFocused = currentRow?.type === "toggle-groups-top";
|
|
1858
1940
|
const listHeaderFocused = currentRow?.type === "list-header";
|
|
1859
1941
|
return /* @__PURE__ */ jsxs(Box, {
|
|
1860
1942
|
flexDirection: "column",
|
|
@@ -1875,137 +1957,30 @@ function Settings({ config, onConfigChange, onClose }) {
|
|
|
1875
1957
|
]
|
|
1876
1958
|
})
|
|
1877
1959
|
}),
|
|
1878
|
-
/* @__PURE__ */
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
children: [
|
|
1884
|
-
/* @__PURE__ */ jsx(Text, {
|
|
1885
|
-
color: "greenBright",
|
|
1886
|
-
children: freqToggleFocused ? ">" : " "
|
|
1887
|
-
}),
|
|
1888
|
-
/* @__PURE__ */ jsxs(Text, {
|
|
1889
|
-
color: config.frequencySort ? "greenBright" : "gray",
|
|
1890
|
-
children: [
|
|
1891
|
-
"[",
|
|
1892
|
-
config.frequencySort ? "x" : " ",
|
|
1893
|
-
"]"
|
|
1894
|
-
]
|
|
1895
|
-
}),
|
|
1896
|
-
/* @__PURE__ */ jsx(Text, {
|
|
1897
|
-
bold: freqToggleFocused,
|
|
1898
|
-
color: freqToggleFocused ? "whiteBright" : "white",
|
|
1899
|
-
children: "Sort by update frequency"
|
|
1900
|
-
})
|
|
1901
|
-
]
|
|
1902
|
-
}), /* @__PURE__ */ jsx(Box, {
|
|
1903
|
-
marginLeft: 6,
|
|
1904
|
-
children: /* @__PURE__ */ jsx(Text, {
|
|
1905
|
-
color: "gray",
|
|
1906
|
-
children: "Packages you update often appear at the top"
|
|
1907
|
-
})
|
|
1908
|
-
})]
|
|
1960
|
+
/* @__PURE__ */ jsx(SettingsToggle, {
|
|
1961
|
+
label: "Sort by update frequency",
|
|
1962
|
+
description: "Packages you update often appear at the top",
|
|
1963
|
+
checked: config.frequencySort,
|
|
1964
|
+
focused: currentRow?.type === "toggle-frequency"
|
|
1909
1965
|
}),
|
|
1910
|
-
/* @__PURE__ */
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
children: [
|
|
1916
|
-
/* @__PURE__ */ jsx(Text, {
|
|
1917
|
-
color: "greenBright",
|
|
1918
|
-
children: separateDevFocused ? ">" : " "
|
|
1919
|
-
}),
|
|
1920
|
-
/* @__PURE__ */ jsxs(Text, {
|
|
1921
|
-
color: config.separateDevDeps ? "greenBright" : "gray",
|
|
1922
|
-
children: [
|
|
1923
|
-
"[",
|
|
1924
|
-
config.separateDevDeps ? "x" : " ",
|
|
1925
|
-
"]"
|
|
1926
|
-
]
|
|
1927
|
-
}),
|
|
1928
|
-
/* @__PURE__ */ jsx(Text, {
|
|
1929
|
-
bold: separateDevFocused,
|
|
1930
|
-
color: separateDevFocused ? "whiteBright" : "white",
|
|
1931
|
-
children: "Separate dev dependencies"
|
|
1932
|
-
})
|
|
1933
|
-
]
|
|
1934
|
-
}), /* @__PURE__ */ jsx(Box, {
|
|
1935
|
-
marginLeft: 6,
|
|
1936
|
-
children: /* @__PURE__ */ jsx(Text, {
|
|
1937
|
-
color: "gray",
|
|
1938
|
-
children: "Show dependencies and devDependencies in separate groups"
|
|
1939
|
-
})
|
|
1940
|
-
})]
|
|
1966
|
+
/* @__PURE__ */ jsx(SettingsToggle, {
|
|
1967
|
+
label: "Separate dev dependencies",
|
|
1968
|
+
description: "Show dependencies and devDependencies in separate groups",
|
|
1969
|
+
checked: config.separateDevDeps,
|
|
1970
|
+
focused: currentRow?.type === "toggle-separate-dev"
|
|
1941
1971
|
}),
|
|
1942
|
-
/* @__PURE__ */
|
|
1943
|
-
|
|
1944
|
-
|
|
1945
|
-
|
|
1946
|
-
|
|
1947
|
-
children: [
|
|
1948
|
-
/* @__PURE__ */ jsx(Text, {
|
|
1949
|
-
color: "greenBright",
|
|
1950
|
-
children: groupToggleFocused ? ">" : " "
|
|
1951
|
-
}),
|
|
1952
|
-
/* @__PURE__ */ jsxs(Text, {
|
|
1953
|
-
color: config.groupByScope ? "greenBright" : "gray",
|
|
1954
|
-
children: [
|
|
1955
|
-
"[",
|
|
1956
|
-
config.groupByScope ? "x" : " ",
|
|
1957
|
-
"]"
|
|
1958
|
-
]
|
|
1959
|
-
}),
|
|
1960
|
-
/* @__PURE__ */ jsx(Text, {
|
|
1961
|
-
bold: groupToggleFocused,
|
|
1962
|
-
color: groupToggleFocused ? "whiteBright" : "white",
|
|
1963
|
-
children: "Enable scope grouping"
|
|
1964
|
-
})
|
|
1965
|
-
]
|
|
1966
|
-
}), /* @__PURE__ */ jsx(Box, {
|
|
1967
|
-
marginLeft: 6,
|
|
1968
|
-
children: /* @__PURE__ */ jsx(Text, {
|
|
1969
|
-
color: "gray",
|
|
1970
|
-
children: "Group scoped packages listed below under their scope prefix"
|
|
1971
|
-
})
|
|
1972
|
-
})]
|
|
1972
|
+
/* @__PURE__ */ jsx(SettingsToggle, {
|
|
1973
|
+
label: "Enable scope grouping",
|
|
1974
|
+
description: "Group scoped packages listed below under their scope prefix",
|
|
1975
|
+
checked: config.groupByScope,
|
|
1976
|
+
focused: currentRow?.type === "toggle-group"
|
|
1973
1977
|
}),
|
|
1974
|
-
/* @__PURE__ */
|
|
1975
|
-
|
|
1976
|
-
|
|
1977
|
-
|
|
1978
|
-
|
|
1979
|
-
|
|
1980
|
-
/* @__PURE__ */ jsx(Text, {
|
|
1981
|
-
dimColor: !config.groupByScope,
|
|
1982
|
-
color: "greenBright",
|
|
1983
|
-
children: groupsTopFocused ? ">" : " "
|
|
1984
|
-
}),
|
|
1985
|
-
/* @__PURE__ */ jsxs(Text, {
|
|
1986
|
-
dimColor: !config.groupByScope,
|
|
1987
|
-
color: config.groupsOnTop && config.groupByScope ? "greenBright" : "gray",
|
|
1988
|
-
children: [
|
|
1989
|
-
"[",
|
|
1990
|
-
config.groupsOnTop ? "x" : " ",
|
|
1991
|
-
"]"
|
|
1992
|
-
]
|
|
1993
|
-
}),
|
|
1994
|
-
/* @__PURE__ */ jsx(Text, {
|
|
1995
|
-
dimColor: !config.groupByScope,
|
|
1996
|
-
bold: groupsTopFocused,
|
|
1997
|
-
color: !config.groupByScope ? "gray" : groupsTopFocused ? "whiteBright" : "white",
|
|
1998
|
-
children: "Show grouped scopes on top"
|
|
1999
|
-
})
|
|
2000
|
-
]
|
|
2001
|
-
}), /* @__PURE__ */ jsx(Box, {
|
|
2002
|
-
marginLeft: 6,
|
|
2003
|
-
children: /* @__PURE__ */ jsx(Text, {
|
|
2004
|
-
dimColor: !config.groupByScope,
|
|
2005
|
-
color: "gray",
|
|
2006
|
-
children: "Grouped scope packages appear before ungrouped ones"
|
|
2007
|
-
})
|
|
2008
|
-
})]
|
|
1978
|
+
/* @__PURE__ */ jsx(SettingsToggle, {
|
|
1979
|
+
label: "Show grouped scopes on top",
|
|
1980
|
+
description: "Grouped scope packages appear before ungrouped ones",
|
|
1981
|
+
checked: config.groupsOnTop,
|
|
1982
|
+
focused: currentRow?.type === "toggle-groups-top",
|
|
1983
|
+
disabled: !config.groupByScope
|
|
2009
1984
|
}),
|
|
2010
1985
|
/* @__PURE__ */ jsxs(Box, {
|
|
2011
1986
|
flexDirection: "column",
|
|
@@ -2197,78 +2172,213 @@ function SelfUpdatePrompt({ currentVersion, latestVersion, updating, error, onUp
|
|
|
2197
2172
|
});
|
|
2198
2173
|
}
|
|
2199
2174
|
//#endregion
|
|
2200
|
-
//#region src/ui/
|
|
2201
|
-
function
|
|
2202
|
-
|
|
2203
|
-
|
|
2204
|
-
|
|
2175
|
+
//#region src/ui/TerminalOutputBox.tsx
|
|
2176
|
+
function TerminalOutputBox({ message, command, outputLines, maxLines }) {
|
|
2177
|
+
return /* @__PURE__ */ jsxs(Box, {
|
|
2178
|
+
flexDirection: "column",
|
|
2179
|
+
padding: 1,
|
|
2180
|
+
children: [
|
|
2181
|
+
/* @__PURE__ */ jsxs(Text, {
|
|
2182
|
+
color: "greenBright",
|
|
2183
|
+
bold: true,
|
|
2184
|
+
children: [" ", "ripen"]
|
|
2185
|
+
}),
|
|
2186
|
+
/* @__PURE__ */ jsx(Box, {
|
|
2187
|
+
marginTop: 1,
|
|
2188
|
+
children: /* @__PURE__ */ jsx(Text, {
|
|
2189
|
+
color: "gray",
|
|
2190
|
+
children: message
|
|
2191
|
+
})
|
|
2192
|
+
}),
|
|
2193
|
+
/* @__PURE__ */ jsxs(Box, {
|
|
2194
|
+
flexDirection: "column",
|
|
2195
|
+
marginTop: 1,
|
|
2196
|
+
borderStyle: "round",
|
|
2197
|
+
borderColor: "gray",
|
|
2198
|
+
paddingX: 1,
|
|
2199
|
+
width: 64,
|
|
2200
|
+
height: maxLines + 3,
|
|
2201
|
+
overflow: "hidden",
|
|
2202
|
+
children: [command !== "" && /* @__PURE__ */ jsxs(Box, { children: [/* @__PURE__ */ jsx(Text, {
|
|
2203
|
+
color: "gray",
|
|
2204
|
+
children: "$ "
|
|
2205
|
+
}), /* @__PURE__ */ jsx(Text, {
|
|
2206
|
+
color: "gray",
|
|
2207
|
+
children: command
|
|
2208
|
+
})] }), outputLines.map((line, i) => /* @__PURE__ */ jsx(Text, {
|
|
2209
|
+
color: line.includes("WARN") || line.includes("ERR") ? "yellow" : "gray",
|
|
2210
|
+
wrap: "truncate",
|
|
2211
|
+
children: line
|
|
2212
|
+
}, i))]
|
|
2213
|
+
})
|
|
2214
|
+
]
|
|
2205
2215
|
});
|
|
2206
|
-
|
|
2216
|
+
}
|
|
2217
|
+
//#endregion
|
|
2218
|
+
//#region src/ui/hooks/use-self-update.ts
|
|
2219
|
+
function useSelfUpdate(currentVersion, installManager) {
|
|
2207
2220
|
const [latestVersion, setLatestVersion] = useState(null);
|
|
2208
|
-
const [selfUpdateError, setSelfUpdateError] = useState(null);
|
|
2209
2221
|
const [selfUpdating, setSelfUpdating] = useState(false);
|
|
2210
|
-
const [
|
|
2211
|
-
const [
|
|
2212
|
-
const [
|
|
2213
|
-
const [activeIndex, setActiveIndex] = useState(0);
|
|
2214
|
-
const [results, setResults] = useState([]);
|
|
2215
|
-
const [errorMsg, setErrorMsg] = useState("");
|
|
2216
|
-
const [loadingMsg, setLoadingMsg] = useState("Checking for outdated packages…");
|
|
2217
|
-
const MAX_TERMINAL_LINES = 3;
|
|
2218
|
-
const [outputLines, setOutputLines] = useState([]);
|
|
2219
|
-
const [terminalCmd, setTerminalCmd] = useState(global ? "Checking all package managers…" : "Checking npm registry…");
|
|
2222
|
+
const [selfUpdateError, setSelfUpdateError] = useState(null);
|
|
2223
|
+
const [checkComplete, setCheckComplete] = useState(false);
|
|
2224
|
+
const [hasUpdate, setHasUpdate] = useState(false);
|
|
2220
2225
|
useEffect(() => {
|
|
2221
2226
|
let cancelled = false;
|
|
2222
2227
|
fetchLatestVersion("ripencli").then((latest) => {
|
|
2223
2228
|
if (cancelled) return;
|
|
2224
|
-
if (latest && isNewerVersion(
|
|
2229
|
+
if (latest && isNewerVersion(currentVersion, latest)) {
|
|
2225
2230
|
setLatestVersion(latest);
|
|
2226
|
-
|
|
2227
|
-
}
|
|
2231
|
+
setHasUpdate(true);
|
|
2232
|
+
}
|
|
2233
|
+
setCheckComplete(true);
|
|
2228
2234
|
});
|
|
2229
2235
|
return () => {
|
|
2230
2236
|
cancelled = true;
|
|
2231
2237
|
};
|
|
2232
2238
|
}, []);
|
|
2239
|
+
const performUpdate = async () => {
|
|
2240
|
+
setSelfUpdating(true);
|
|
2241
|
+
try {
|
|
2242
|
+
await execa(installManager, installManager === "yarn" ? [
|
|
2243
|
+
"global",
|
|
2244
|
+
"add",
|
|
2245
|
+
`ripencli@${latestVersion}`
|
|
2246
|
+
] : [
|
|
2247
|
+
"add",
|
|
2248
|
+
"--global",
|
|
2249
|
+
`ripencli@${latestVersion}`
|
|
2250
|
+
]);
|
|
2251
|
+
setSelfUpdating(false);
|
|
2252
|
+
return true;
|
|
2253
|
+
} catch (err) {
|
|
2254
|
+
setSelfUpdateError(err.message ?? "Unknown error");
|
|
2255
|
+
setSelfUpdating(false);
|
|
2256
|
+
return false;
|
|
2257
|
+
}
|
|
2258
|
+
};
|
|
2259
|
+
return {
|
|
2260
|
+
latestVersion,
|
|
2261
|
+
selfUpdating,
|
|
2262
|
+
selfUpdateError,
|
|
2263
|
+
checkComplete,
|
|
2264
|
+
hasUpdate,
|
|
2265
|
+
performUpdate
|
|
2266
|
+
};
|
|
2267
|
+
}
|
|
2268
|
+
//#endregion
|
|
2269
|
+
//#region src/ui/hooks/use-packages.ts
|
|
2270
|
+
function usePackages() {
|
|
2271
|
+
const [packages, setPackages] = useState([]);
|
|
2272
|
+
return {
|
|
2273
|
+
packages,
|
|
2274
|
+
setPackages,
|
|
2275
|
+
toggleOne: useCallback((index) => {
|
|
2276
|
+
setPackages((prev) => prev.map((p, i) => i === index ? {
|
|
2277
|
+
...p,
|
|
2278
|
+
selected: !p.selected
|
|
2279
|
+
} : p));
|
|
2280
|
+
}, []),
|
|
2281
|
+
toggleMany: useCallback((indices) => {
|
|
2282
|
+
setPackages((prev) => {
|
|
2283
|
+
const allSelected = indices.every((i) => prev[i]?.selected);
|
|
2284
|
+
return prev.map((p, i) => indices.includes(i) ? {
|
|
2285
|
+
...p,
|
|
2286
|
+
selected: !allSelected
|
|
2287
|
+
} : p);
|
|
2288
|
+
});
|
|
2289
|
+
}, []),
|
|
2290
|
+
chooseVersion: useCallback((activeIndex, version) => {
|
|
2291
|
+
setPackages((prev) => prev.map((p, i) => i === activeIndex ? {
|
|
2292
|
+
...p,
|
|
2293
|
+
targetVersion: version,
|
|
2294
|
+
selected: true
|
|
2295
|
+
} : p));
|
|
2296
|
+
}, [])
|
|
2297
|
+
};
|
|
2298
|
+
}
|
|
2299
|
+
//#endregion
|
|
2300
|
+
//#region src/ui/hooks/use-terminal-output.ts
|
|
2301
|
+
const DEFAULT_MAX_LINES = 3;
|
|
2302
|
+
function useTerminalOutput(maxLines = DEFAULT_MAX_LINES) {
|
|
2303
|
+
const [outputLines, setOutputLines] = useState([]);
|
|
2304
|
+
const [terminalCmd, setTerminalCmd] = useState("");
|
|
2305
|
+
const [loadingMsg, setLoadingMsg] = useState("");
|
|
2306
|
+
return {
|
|
2307
|
+
outputLines,
|
|
2308
|
+
terminalCmd,
|
|
2309
|
+
loadingMsg,
|
|
2310
|
+
setTerminalCmd,
|
|
2311
|
+
setLoadingMsg,
|
|
2312
|
+
onLine: useCallback((line) => {
|
|
2313
|
+
setOutputLines((prev) => [...prev.slice(-(maxLines - 1)), line]);
|
|
2314
|
+
}, [maxLines]),
|
|
2315
|
+
reset: useCallback(() => {
|
|
2316
|
+
setOutputLines([]);
|
|
2317
|
+
setTerminalCmd("");
|
|
2318
|
+
setLoadingMsg("");
|
|
2319
|
+
}, []),
|
|
2320
|
+
maxLines
|
|
2321
|
+
};
|
|
2322
|
+
}
|
|
2323
|
+
//#endregion
|
|
2324
|
+
//#region src/ui/hooks/use-exit-on-screen.ts
|
|
2325
|
+
/**
|
|
2326
|
+
* Exit the Ink app after a delay when the screen matches one of the target screens.
|
|
2327
|
+
*/
|
|
2328
|
+
function useExitOnScreen(screen, targetScreens, exit, options) {
|
|
2329
|
+
const { delay = 300, beforeExit } = options ?? {};
|
|
2233
2330
|
useEffect(() => {
|
|
2234
|
-
if (screen
|
|
2235
|
-
const timer = setTimeout(() => {
|
|
2236
|
-
exit();
|
|
2237
|
-
process.exit(0);
|
|
2238
|
-
}, 300);
|
|
2239
|
-
return () => clearTimeout(timer);
|
|
2240
|
-
}, [screen]);
|
|
2241
|
-
useEffect(() => {
|
|
2242
|
-
if (screen !== "empty") return;
|
|
2331
|
+
if (!targetScreens.includes(screen)) return;
|
|
2243
2332
|
const timer = setTimeout(() => {
|
|
2244
2333
|
exit();
|
|
2334
|
+
beforeExit?.();
|
|
2245
2335
|
process.exit(0);
|
|
2246
|
-
},
|
|
2336
|
+
}, delay);
|
|
2247
2337
|
return () => clearTimeout(timer);
|
|
2248
2338
|
}, [screen]);
|
|
2339
|
+
}
|
|
2340
|
+
//#endregion
|
|
2341
|
+
//#region src/ui/App.tsx
|
|
2342
|
+
function App({ project, global, version, installManager }) {
|
|
2343
|
+
const { exit } = useApp();
|
|
2344
|
+
const [screen, setScreen] = useState("self-update-check");
|
|
2345
|
+
const [config, setConfig] = useState(() => loadConfig());
|
|
2346
|
+
const [frequency, setFrequency] = useState(() => loadFrequency());
|
|
2347
|
+
const [activeIndex, setActiveIndex] = useState(0);
|
|
2348
|
+
const [errorMsg, setErrorMsg] = useState("");
|
|
2349
|
+
const selfUpdate = useSelfUpdate(version, installManager);
|
|
2350
|
+
const { packages, setPackages, toggleOne, toggleMany, chooseVersion } = usePackages();
|
|
2351
|
+
const terminal = useTerminalOutput();
|
|
2352
|
+
useInput((_input, key) => {
|
|
2353
|
+
if (key.ctrl && _input === "c") setScreen("cancelled");
|
|
2354
|
+
});
|
|
2355
|
+
useExitOnScreen(screen, ["self-update-done", "empty"], exit);
|
|
2356
|
+
useExitOnScreen(screen, ["cancelled"], exit, {
|
|
2357
|
+
delay: 200,
|
|
2358
|
+
beforeExit: () => console.log(" \x1B[32mCancelled.\x1B[0m\n")
|
|
2359
|
+
});
|
|
2249
2360
|
useEffect(() => {
|
|
2250
|
-
if (
|
|
2251
|
-
|
|
2252
|
-
|
|
2253
|
-
|
|
2254
|
-
|
|
2255
|
-
|
|
2256
|
-
|
|
2257
|
-
}
|
|
2361
|
+
if (!selfUpdate.checkComplete) return;
|
|
2362
|
+
if (screen !== "self-update-check") return;
|
|
2363
|
+
setScreen(selfUpdate.hasUpdate ? "self-update" : "loading");
|
|
2364
|
+
}, [selfUpdate.checkComplete]);
|
|
2365
|
+
const handleSelfUpdate = async () => {
|
|
2366
|
+
if (await selfUpdate.performUpdate()) setScreen("self-update-done");
|
|
2367
|
+
else setTimeout(() => setScreen("loading"), 2e3);
|
|
2368
|
+
};
|
|
2258
2369
|
const [fetchStarted, setFetchStarted] = useState(false);
|
|
2259
2370
|
useEffect(() => {
|
|
2260
2371
|
if (screen !== "loading" || fetchStarted) return;
|
|
2261
2372
|
setFetchStarted(true);
|
|
2262
|
-
|
|
2263
|
-
|
|
2264
|
-
|
|
2265
|
-
(global ? getAllGlobalOutdated(project.cwd, onLine) : getOutdatedPackages(project.manager, project.cwd, false, onLine)).then((result) => {
|
|
2373
|
+
terminal.setLoadingMsg("Checking for outdated packages…");
|
|
2374
|
+
terminal.setTerminalCmd(global ? "Checking all package managers…" : "Checking npm registry…");
|
|
2375
|
+
(global ? getAllGlobalOutdated(project.cwd, terminal.onLine) : getOutdatedPackages(project.manager, project.cwd, false, terminal.onLine)).then((result) => {
|
|
2266
2376
|
if (!result.ok) {
|
|
2267
2377
|
setErrorMsg(result.error);
|
|
2268
2378
|
setScreen("error");
|
|
2269
2379
|
return;
|
|
2270
2380
|
}
|
|
2271
|
-
|
|
2381
|
+
terminal.reset();
|
|
2272
2382
|
if (result.packages.length === 0) setScreen("empty");
|
|
2273
2383
|
else {
|
|
2274
2384
|
setPackages(result.packages);
|
|
@@ -2276,81 +2386,18 @@ function App({ project, global, version, installManager }) {
|
|
|
2276
2386
|
}
|
|
2277
2387
|
});
|
|
2278
2388
|
}, [screen]);
|
|
2279
|
-
const handleToggle = (index) => {
|
|
2280
|
-
setPackages((prev) => prev.map((p, i) => i === index ? {
|
|
2281
|
-
...p,
|
|
2282
|
-
selected: !p.selected
|
|
2283
|
-
} : p));
|
|
2284
|
-
};
|
|
2285
|
-
const handleToggleGroup = (groupType) => {
|
|
2286
|
-
setPackages((prev) => {
|
|
2287
|
-
const allSelected = prev.filter((p) => p.type === groupType).every((p) => p.selected);
|
|
2288
|
-
return prev.map((p) => p.type === groupType ? {
|
|
2289
|
-
...p,
|
|
2290
|
-
selected: !allSelected
|
|
2291
|
-
} : p);
|
|
2292
|
-
});
|
|
2293
|
-
};
|
|
2294
|
-
const handleToggleMany = (indices) => {
|
|
2295
|
-
setPackages((prev) => {
|
|
2296
|
-
const allSelected = indices.every((i) => prev[i]?.selected);
|
|
2297
|
-
return prev.map((p, i) => indices.includes(i) ? {
|
|
2298
|
-
...p,
|
|
2299
|
-
selected: !allSelected
|
|
2300
|
-
} : p);
|
|
2301
|
-
});
|
|
2302
|
-
};
|
|
2303
2389
|
const handleConfigChange = (newConfig) => {
|
|
2304
2390
|
setConfig(newConfig);
|
|
2305
2391
|
saveConfig(newConfig);
|
|
2306
2392
|
};
|
|
2307
|
-
const
|
|
2308
|
-
setActiveIndex(index);
|
|
2309
|
-
setScreen("version-picker");
|
|
2310
|
-
};
|
|
2311
|
-
const handleViewChangelog = (index) => {
|
|
2312
|
-
setActiveIndex(index);
|
|
2313
|
-
setScreen("changelog");
|
|
2314
|
-
};
|
|
2315
|
-
const handleVersionChosen = (version) => {
|
|
2316
|
-
setPackages((prev) => prev.map((p, i) => i === activeIndex ? {
|
|
2317
|
-
...p,
|
|
2318
|
-
targetVersion: version,
|
|
2319
|
-
selected: true
|
|
2320
|
-
} : p));
|
|
2321
|
-
setScreen("list");
|
|
2322
|
-
};
|
|
2323
|
-
const handleSelfUpdate = async () => {
|
|
2324
|
-
setSelfUpdating(true);
|
|
2325
|
-
try {
|
|
2326
|
-
await execa(installManager, installManager === "yarn" ? [
|
|
2327
|
-
"global",
|
|
2328
|
-
"add",
|
|
2329
|
-
`ripencli@${latestVersion}`
|
|
2330
|
-
] : [
|
|
2331
|
-
"add",
|
|
2332
|
-
"--global",
|
|
2333
|
-
`ripencli@${latestVersion}`
|
|
2334
|
-
]);
|
|
2335
|
-
setSelfUpdating(false);
|
|
2336
|
-
setScreen("self-update-done");
|
|
2337
|
-
} catch (err) {
|
|
2338
|
-
setSelfUpdateError(err.message ?? "Unknown error");
|
|
2339
|
-
setSelfUpdating(false);
|
|
2340
|
-
setTimeout(() => setScreen("loading"), 2e3);
|
|
2341
|
-
}
|
|
2342
|
-
};
|
|
2393
|
+
const [results, setResults] = useState([]);
|
|
2343
2394
|
const handleConfirm = async () => {
|
|
2344
2395
|
const selected = packages.filter((p) => p.selected);
|
|
2345
2396
|
if (selected.length === 0) return;
|
|
2346
|
-
setLoadingMsg(`Updating ${selected.length} package${selected.length > 1 ? "s" : ""}…`);
|
|
2347
|
-
setTerminalCmd("");
|
|
2348
|
-
setOutputLines([]);
|
|
2397
|
+
terminal.setLoadingMsg(`Updating ${selected.length} package${selected.length > 1 ? "s" : ""}…`);
|
|
2398
|
+
terminal.setTerminalCmd("");
|
|
2349
2399
|
setScreen("updating");
|
|
2350
|
-
const
|
|
2351
|
-
setOutputLines((prev) => [...prev.slice(-(MAX_TERMINAL_LINES - 1)), line]);
|
|
2352
|
-
};
|
|
2353
|
-
const res = await updatePackages(project.manager, selected, project.cwd, global, onLine);
|
|
2400
|
+
const res = await updatePackages(project.manager, selected, project.cwd, global, terminal.onLine);
|
|
2354
2401
|
setResults(res);
|
|
2355
2402
|
setScreen("results");
|
|
2356
2403
|
const successNames = res.filter((r) => r.success).map((r) => r.name);
|
|
@@ -2376,9 +2423,9 @@ function App({ project, global, version, installManager }) {
|
|
|
2376
2423
|
});
|
|
2377
2424
|
if (screen === "self-update") return /* @__PURE__ */ jsx(SelfUpdatePrompt, {
|
|
2378
2425
|
currentVersion: version,
|
|
2379
|
-
latestVersion,
|
|
2380
|
-
updating: selfUpdating,
|
|
2381
|
-
error: selfUpdateError,
|
|
2426
|
+
latestVersion: selfUpdate.latestVersion,
|
|
2427
|
+
updating: selfUpdate.selfUpdating,
|
|
2428
|
+
error: selfUpdate.selfUpdateError,
|
|
2382
2429
|
onUpdate: handleSelfUpdate,
|
|
2383
2430
|
onSkip: () => setScreen("loading")
|
|
2384
2431
|
});
|
|
@@ -2395,50 +2442,17 @@ function App({ project, global, version, installManager }) {
|
|
|
2395
2442
|
color: "green",
|
|
2396
2443
|
children: [
|
|
2397
2444
|
"✓ Updated to v",
|
|
2398
|
-
latestVersion,
|
|
2445
|
+
selfUpdate.latestVersion,
|
|
2399
2446
|
". Run ripen again to use the new version."
|
|
2400
2447
|
]
|
|
2401
2448
|
})
|
|
2402
2449
|
})]
|
|
2403
2450
|
});
|
|
2404
|
-
if (screen === "loading") return /* @__PURE__ */
|
|
2405
|
-
|
|
2406
|
-
|
|
2407
|
-
|
|
2408
|
-
|
|
2409
|
-
color: "greenBright",
|
|
2410
|
-
bold: true,
|
|
2411
|
-
children: [" ", "ripen"]
|
|
2412
|
-
}),
|
|
2413
|
-
/* @__PURE__ */ jsx(Box, {
|
|
2414
|
-
marginTop: 1,
|
|
2415
|
-
children: /* @__PURE__ */ jsx(Text, {
|
|
2416
|
-
color: "gray",
|
|
2417
|
-
children: loadingMsg
|
|
2418
|
-
})
|
|
2419
|
-
}),
|
|
2420
|
-
/* @__PURE__ */ jsxs(Box, {
|
|
2421
|
-
flexDirection: "column",
|
|
2422
|
-
marginTop: 1,
|
|
2423
|
-
borderStyle: "round",
|
|
2424
|
-
borderColor: "gray",
|
|
2425
|
-
paddingX: 1,
|
|
2426
|
-
width: 64,
|
|
2427
|
-
height: MAX_TERMINAL_LINES + 3,
|
|
2428
|
-
overflow: "hidden",
|
|
2429
|
-
children: [terminalCmd !== "" && /* @__PURE__ */ jsxs(Box, { children: [/* @__PURE__ */ jsx(Text, {
|
|
2430
|
-
color: "gray",
|
|
2431
|
-
children: "$ "
|
|
2432
|
-
}), /* @__PURE__ */ jsx(Text, {
|
|
2433
|
-
color: "gray",
|
|
2434
|
-
children: terminalCmd
|
|
2435
|
-
})] }), outputLines.map((line, i) => /* @__PURE__ */ jsx(Text, {
|
|
2436
|
-
color: line.includes("WARN") || line.includes("ERR") ? "yellow" : "gray",
|
|
2437
|
-
wrap: "truncate",
|
|
2438
|
-
children: line
|
|
2439
|
-
}, i))]
|
|
2440
|
-
})
|
|
2441
|
-
]
|
|
2451
|
+
if (screen === "loading") return /* @__PURE__ */ jsx(TerminalOutputBox, {
|
|
2452
|
+
message: terminal.loadingMsg,
|
|
2453
|
+
command: terminal.terminalCmd,
|
|
2454
|
+
outputLines: terminal.outputLines,
|
|
2455
|
+
maxLines: terminal.maxLines
|
|
2442
2456
|
});
|
|
2443
2457
|
if (screen === "error") return /* @__PURE__ */ jsxs(Box, {
|
|
2444
2458
|
flexDirection: "column",
|
|
@@ -2491,44 +2505,11 @@ function App({ project, global, version, installManager }) {
|
|
|
2491
2505
|
});
|
|
2492
2506
|
const isListActive = screen === "list";
|
|
2493
2507
|
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
2494
|
-
screen === "updating" && /* @__PURE__ */
|
|
2495
|
-
|
|
2496
|
-
|
|
2497
|
-
|
|
2498
|
-
|
|
2499
|
-
color: "greenBright",
|
|
2500
|
-
bold: true,
|
|
2501
|
-
children: [" ", "ripen"]
|
|
2502
|
-
}),
|
|
2503
|
-
/* @__PURE__ */ jsx(Box, {
|
|
2504
|
-
marginTop: 1,
|
|
2505
|
-
children: /* @__PURE__ */ jsx(Text, {
|
|
2506
|
-
color: "gray",
|
|
2507
|
-
children: loadingMsg
|
|
2508
|
-
})
|
|
2509
|
-
}),
|
|
2510
|
-
/* @__PURE__ */ jsxs(Box, {
|
|
2511
|
-
flexDirection: "column",
|
|
2512
|
-
marginTop: 1,
|
|
2513
|
-
borderStyle: "round",
|
|
2514
|
-
borderColor: "gray",
|
|
2515
|
-
paddingX: 1,
|
|
2516
|
-
width: 64,
|
|
2517
|
-
height: MAX_TERMINAL_LINES + 3,
|
|
2518
|
-
overflow: "hidden",
|
|
2519
|
-
children: [terminalCmd !== "" && /* @__PURE__ */ jsxs(Box, { children: [/* @__PURE__ */ jsx(Text, {
|
|
2520
|
-
color: "gray",
|
|
2521
|
-
children: "$ "
|
|
2522
|
-
}), /* @__PURE__ */ jsx(Text, {
|
|
2523
|
-
color: "gray",
|
|
2524
|
-
children: terminalCmd
|
|
2525
|
-
})] }), outputLines.map((line, i) => /* @__PURE__ */ jsx(Text, {
|
|
2526
|
-
color: line.includes("WARN") || line.includes("ERR") ? "yellow" : "gray",
|
|
2527
|
-
wrap: "truncate",
|
|
2528
|
-
children: line
|
|
2529
|
-
}, i))]
|
|
2530
|
-
})
|
|
2531
|
-
]
|
|
2508
|
+
screen === "updating" && /* @__PURE__ */ jsx(TerminalOutputBox, {
|
|
2509
|
+
message: terminal.loadingMsg,
|
|
2510
|
+
command: terminal.terminalCmd,
|
|
2511
|
+
outputLines: terminal.outputLines,
|
|
2512
|
+
maxLines: terminal.maxLines
|
|
2532
2513
|
}),
|
|
2533
2514
|
screen === "results" && /* @__PURE__ */ jsx(Box, {
|
|
2534
2515
|
padding: 1,
|
|
@@ -2552,7 +2533,10 @@ function App({ project, global, version, installManager }) {
|
|
|
2552
2533
|
padding: 1,
|
|
2553
2534
|
children: /* @__PURE__ */ jsx(VersionPicker, {
|
|
2554
2535
|
pkg: packages[activeIndex],
|
|
2555
|
-
onSelect:
|
|
2536
|
+
onSelect: (v) => {
|
|
2537
|
+
chooseVersion(activeIndex, v);
|
|
2538
|
+
setScreen("list");
|
|
2539
|
+
},
|
|
2556
2540
|
onCancel: () => setScreen("list")
|
|
2557
2541
|
})
|
|
2558
2542
|
}),
|
|
@@ -2568,11 +2552,16 @@ function App({ project, global, version, installManager }) {
|
|
|
2568
2552
|
display: isListActive ? "flex" : "none",
|
|
2569
2553
|
children: /* @__PURE__ */ jsx(PackageList, {
|
|
2570
2554
|
packages,
|
|
2571
|
-
onToggle:
|
|
2572
|
-
|
|
2573
|
-
|
|
2574
|
-
|
|
2575
|
-
|
|
2555
|
+
onToggle: toggleOne,
|
|
2556
|
+
onToggleMany: toggleMany,
|
|
2557
|
+
onSelectVersion: (i) => {
|
|
2558
|
+
setActiveIndex(i);
|
|
2559
|
+
setScreen("version-picker");
|
|
2560
|
+
},
|
|
2561
|
+
onViewChangelog: (i) => {
|
|
2562
|
+
setActiveIndex(i);
|
|
2563
|
+
setScreen("changelog");
|
|
2564
|
+
},
|
|
2576
2565
|
onConfirm: handleConfirm,
|
|
2577
2566
|
onOpenSettings: () => setScreen("settings"),
|
|
2578
2567
|
groupByScope: config.groupByScope,
|