ripencli 0.3.3 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -1
- package/dist/cli.js +608 -602
- package/package.json +18 -6
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);
|
|
@@ -218,7 +239,7 @@ async function fetchAllLatest(names, concurrency, onLine) {
|
|
|
218
239
|
await Promise.all(Array.from({ length: Math.min(concurrency, names.length) }, () => worker()));
|
|
219
240
|
return results;
|
|
220
241
|
}
|
|
221
|
-
async function getOutdatedPackages(manager, cwd, global = false, onLine) {
|
|
242
|
+
async function getOutdatedPackages(manager, cwd, global = false, onLine, showAll = false) {
|
|
222
243
|
if (global) return getGlobalOutdatedPackages(manager, cwd, onLine);
|
|
223
244
|
let deps;
|
|
224
245
|
try {
|
|
@@ -242,7 +263,7 @@ async function getOutdatedPackages(manager, cwd, global = false, onLine) {
|
|
|
242
263
|
for (const dep of deps) {
|
|
243
264
|
const latest = latestVersions.get(dep.name);
|
|
244
265
|
if (!latest) continue;
|
|
245
|
-
if (!isNewerVersion(dep.current, latest)) continue;
|
|
266
|
+
if (!showAll && !isNewerVersion(dep.current, latest)) continue;
|
|
246
267
|
packages.push({
|
|
247
268
|
name: dep.name,
|
|
248
269
|
current: dep.current,
|
|
@@ -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, showAll = false, 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);
|
|
@@ -914,6 +949,7 @@ function PackageList({ packages, onToggle, onToggleGroup, onToggleMany, onSelect
|
|
|
914
949
|
if (key.return) onConfirm();
|
|
915
950
|
}, { isActive });
|
|
916
951
|
const selectedCount = packages.filter((p) => p.selected).length;
|
|
952
|
+
const outdatedCount = packages.filter((p) => p.current !== p.latest).length;
|
|
917
953
|
return /* @__PURE__ */ jsxs(Box, {
|
|
918
954
|
flexDirection: "column",
|
|
919
955
|
children: [
|
|
@@ -987,199 +1023,13 @@ function PackageList({ packages, onToggle, onToggleGroup, onToggleMany, onSelect
|
|
|
987
1023
|
})
|
|
988
1024
|
})]
|
|
989
1025
|
}),
|
|
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
|
-
}),
|
|
1026
|
+
groups.map((group) => /* @__PURE__ */ jsx(PackageGroupBox, {
|
|
1027
|
+
group,
|
|
1028
|
+
focusedIndex,
|
|
1029
|
+
collapsedScopes,
|
|
1030
|
+
scrollOffset: scrollOffsetsRef.current[group.type] ?? 0,
|
|
1031
|
+
maxVisible
|
|
1032
|
+
}, group.type)),
|
|
1183
1033
|
/* @__PURE__ */ jsx(Box, {
|
|
1184
1034
|
flexDirection: "column",
|
|
1185
1035
|
children: /* @__PURE__ */ jsxs(Box, {
|
|
@@ -1195,7 +1045,18 @@ function PackageList({ packages, onToggle, onToggleGroup, onToggleMany, onSelect
|
|
|
1195
1045
|
children: " selected"
|
|
1196
1046
|
}),
|
|
1197
1047
|
" ",
|
|
1198
|
-
/* @__PURE__ */ jsxs(Text, {
|
|
1048
|
+
showAll ? /* @__PURE__ */ jsxs(Text, {
|
|
1049
|
+
color: "gray",
|
|
1050
|
+
children: [
|
|
1051
|
+
packages.length,
|
|
1052
|
+
" packages",
|
|
1053
|
+
" ",
|
|
1054
|
+
/* @__PURE__ */ jsxs(Text, {
|
|
1055
|
+
color: outdatedCount > 0 ? "yellow" : "green",
|
|
1056
|
+
children: [outdatedCount, " outdated"]
|
|
1057
|
+
})
|
|
1058
|
+
]
|
|
1059
|
+
}) : /* @__PURE__ */ jsxs(Text, {
|
|
1199
1060
|
color: "gray",
|
|
1200
1061
|
children: [packages.length, " outdated"]
|
|
1201
1062
|
})
|
|
@@ -1208,6 +1069,199 @@ function PackageList({ packages, onToggle, onToggleGroup, onToggleMany, onSelect
|
|
|
1208
1069
|
]
|
|
1209
1070
|
});
|
|
1210
1071
|
}
|
|
1072
|
+
function PackageGroupBox({ group, focusedIndex, collapsedScopes, scrollOffset, maxVisible }) {
|
|
1073
|
+
const check = groupCheckbox(group.allPackages);
|
|
1074
|
+
const headerFocused = focusedIndex === group.headerVisibleIndex;
|
|
1075
|
+
const typeColor = TYPE_COLORS[group.type] ?? "white";
|
|
1076
|
+
const visibleItems = group.items.slice(scrollOffset, scrollOffset + maxVisible);
|
|
1077
|
+
const totalItems = group.items.length;
|
|
1078
|
+
const needsScroll = totalItems > maxVisible;
|
|
1079
|
+
const focusedLocalIndex = group.items.findIndex((item) => item.visibleIndex === focusedIndex);
|
|
1080
|
+
return /* @__PURE__ */ jsxs(Box, {
|
|
1081
|
+
flexDirection: "column",
|
|
1082
|
+
marginBottom: 1,
|
|
1083
|
+
children: [/* @__PURE__ */ jsxs(Box, {
|
|
1084
|
+
gap: 1,
|
|
1085
|
+
children: [
|
|
1086
|
+
/* @__PURE__ */ jsx(Text, {
|
|
1087
|
+
color: "greenBright",
|
|
1088
|
+
children: headerFocused ? "❯" : " "
|
|
1089
|
+
}),
|
|
1090
|
+
/* @__PURE__ */ jsx(Text, {
|
|
1091
|
+
color: check.color,
|
|
1092
|
+
children: check.symbol
|
|
1093
|
+
}),
|
|
1094
|
+
/* @__PURE__ */ jsx(Text, {
|
|
1095
|
+
bold: headerFocused,
|
|
1096
|
+
color: typeColor,
|
|
1097
|
+
children: group.label
|
|
1098
|
+
}),
|
|
1099
|
+
/* @__PURE__ */ jsxs(Text, {
|
|
1100
|
+
color: "gray",
|
|
1101
|
+
children: [
|
|
1102
|
+
"(",
|
|
1103
|
+
group.allPackages.length,
|
|
1104
|
+
")"
|
|
1105
|
+
]
|
|
1106
|
+
}),
|
|
1107
|
+
needsScroll && focusedLocalIndex >= 0 && /* @__PURE__ */ jsxs(Text, {
|
|
1108
|
+
color: "gray",
|
|
1109
|
+
children: [
|
|
1110
|
+
focusedLocalIndex + 1,
|
|
1111
|
+
"/",
|
|
1112
|
+
totalItems
|
|
1113
|
+
]
|
|
1114
|
+
})
|
|
1115
|
+
]
|
|
1116
|
+
}), /* @__PURE__ */ jsxs(Box, {
|
|
1117
|
+
flexDirection: "column",
|
|
1118
|
+
borderStyle: "round",
|
|
1119
|
+
borderColor: headerFocused ? typeColor : "gray",
|
|
1120
|
+
paddingX: 1,
|
|
1121
|
+
children: [
|
|
1122
|
+
/* @__PURE__ */ jsxs(Box, {
|
|
1123
|
+
gap: 2,
|
|
1124
|
+
marginBottom: 0,
|
|
1125
|
+
children: [
|
|
1126
|
+
/* @__PURE__ */ jsx(Text, {
|
|
1127
|
+
color: "gray",
|
|
1128
|
+
children: " "
|
|
1129
|
+
}),
|
|
1130
|
+
/* @__PURE__ */ jsx(Box, {
|
|
1131
|
+
width: 28,
|
|
1132
|
+
children: /* @__PURE__ */ jsx(Text, {
|
|
1133
|
+
color: "gray",
|
|
1134
|
+
children: "package"
|
|
1135
|
+
})
|
|
1136
|
+
}),
|
|
1137
|
+
/* @__PURE__ */ jsx(Box, {
|
|
1138
|
+
width: 14,
|
|
1139
|
+
children: /* @__PURE__ */ jsx(Text, {
|
|
1140
|
+
color: "gray",
|
|
1141
|
+
children: "current"
|
|
1142
|
+
})
|
|
1143
|
+
}),
|
|
1144
|
+
/* @__PURE__ */ jsx(Box, {
|
|
1145
|
+
width: 14,
|
|
1146
|
+
children: /* @__PURE__ */ jsx(Text, {
|
|
1147
|
+
color: "gray",
|
|
1148
|
+
children: "target"
|
|
1149
|
+
})
|
|
1150
|
+
}),
|
|
1151
|
+
/* @__PURE__ */ jsx(Box, {
|
|
1152
|
+
width: 14,
|
|
1153
|
+
children: /* @__PURE__ */ jsx(Text, {
|
|
1154
|
+
color: "gray",
|
|
1155
|
+
children: "latest"
|
|
1156
|
+
})
|
|
1157
|
+
})
|
|
1158
|
+
]
|
|
1159
|
+
}),
|
|
1160
|
+
needsScroll && /* @__PURE__ */ jsx(Text, {
|
|
1161
|
+
color: "gray",
|
|
1162
|
+
children: scrollOffset > 0 ? ` ↑ ${scrollOffset} more above` : " "
|
|
1163
|
+
}),
|
|
1164
|
+
visibleItems.map((item) => {
|
|
1165
|
+
const { row } = item;
|
|
1166
|
+
const isFocused = item.visibleIndex === focusedIndex;
|
|
1167
|
+
if (row.kind === "scope-header") {
|
|
1168
|
+
const scopeKey = `${row.groupType}::${row.scope}`;
|
|
1169
|
+
const isCollapsed = collapsedScopes.has(scopeKey);
|
|
1170
|
+
const scopeCheck = groupCheckbox(row.packages);
|
|
1171
|
+
return /* @__PURE__ */ jsxs(Box, {
|
|
1172
|
+
gap: 2,
|
|
1173
|
+
children: [
|
|
1174
|
+
/* @__PURE__ */ jsx(Text, {
|
|
1175
|
+
color: "greenBright",
|
|
1176
|
+
children: isFocused ? "❯" : " "
|
|
1177
|
+
}),
|
|
1178
|
+
/* @__PURE__ */ jsx(Text, {
|
|
1179
|
+
color: "gray",
|
|
1180
|
+
children: isCollapsed ? "▶" : "▼"
|
|
1181
|
+
}),
|
|
1182
|
+
/* @__PURE__ */ jsx(Text, {
|
|
1183
|
+
color: scopeCheck.color,
|
|
1184
|
+
children: scopeCheck.symbol
|
|
1185
|
+
}),
|
|
1186
|
+
/* @__PURE__ */ jsxs(Text, {
|
|
1187
|
+
bold: isFocused,
|
|
1188
|
+
color: isFocused ? "whiteBright" : "white",
|
|
1189
|
+
children: [
|
|
1190
|
+
row.scope,
|
|
1191
|
+
" (",
|
|
1192
|
+
row.packages.length,
|
|
1193
|
+
")"
|
|
1194
|
+
]
|
|
1195
|
+
})
|
|
1196
|
+
]
|
|
1197
|
+
}, scopeKey);
|
|
1198
|
+
}
|
|
1199
|
+
if (row.kind !== "package") return null;
|
|
1200
|
+
const pkg = row.pkg;
|
|
1201
|
+
const isUpToDate = pkg.current === pkg.latest;
|
|
1202
|
+
const isMajorBump = !isUpToDate && parseInt(pkg.latest) > parseInt(pkg.current);
|
|
1203
|
+
return /* @__PURE__ */ jsxs(Box, {
|
|
1204
|
+
gap: 2,
|
|
1205
|
+
children: [
|
|
1206
|
+
/* @__PURE__ */ jsx(Text, {
|
|
1207
|
+
color: "greenBright",
|
|
1208
|
+
children: isFocused ? "❯" : " "
|
|
1209
|
+
}),
|
|
1210
|
+
row.indented && /* @__PURE__ */ jsx(Text, { children: " " }),
|
|
1211
|
+
/* @__PURE__ */ jsx(Text, {
|
|
1212
|
+
color: pkg.selected ? "greenBright" : "gray",
|
|
1213
|
+
children: pkg.selected ? "◉" : "○"
|
|
1214
|
+
}),
|
|
1215
|
+
/* @__PURE__ */ jsx(Box, {
|
|
1216
|
+
width: row.indented ? 24 : 26,
|
|
1217
|
+
children: /* @__PURE__ */ jsx(Text, {
|
|
1218
|
+
bold: isFocused,
|
|
1219
|
+
color: isFocused ? "whiteBright" : "white",
|
|
1220
|
+
children: (() => {
|
|
1221
|
+
const maxLen = row.indented ? 24 : 26;
|
|
1222
|
+
return pkg.name.length > maxLen ? pkg.name.slice(0, maxLen - 2) + "…" : pkg.name;
|
|
1223
|
+
})()
|
|
1224
|
+
})
|
|
1225
|
+
}),
|
|
1226
|
+
/* @__PURE__ */ jsx(Box, {
|
|
1227
|
+
width: 14,
|
|
1228
|
+
children: /* @__PURE__ */ jsx(Text, {
|
|
1229
|
+
color: isUpToDate ? "green" : "red",
|
|
1230
|
+
children: pkg.current
|
|
1231
|
+
})
|
|
1232
|
+
}),
|
|
1233
|
+
/* @__PURE__ */ jsx(Box, {
|
|
1234
|
+
width: 14,
|
|
1235
|
+
children: /* @__PURE__ */ jsx(Text, {
|
|
1236
|
+
color: "greenBright",
|
|
1237
|
+
children: pkg.targetVersion ?? pkg.latest
|
|
1238
|
+
})
|
|
1239
|
+
}),
|
|
1240
|
+
/* @__PURE__ */ jsx(Box, {
|
|
1241
|
+
width: 14,
|
|
1242
|
+
children: /* @__PURE__ */ jsx(Text, {
|
|
1243
|
+
color: "gray",
|
|
1244
|
+
children: pkg.latest
|
|
1245
|
+
})
|
|
1246
|
+
}),
|
|
1247
|
+
/* @__PURE__ */ jsx(Box, {
|
|
1248
|
+
width: 9,
|
|
1249
|
+
children: isMajorBump ? /* @__PURE__ */ jsx(Text, {
|
|
1250
|
+
color: "yellow",
|
|
1251
|
+
children: "⚠ major"
|
|
1252
|
+
}) : /* @__PURE__ */ jsx(Text, { children: " " })
|
|
1253
|
+
})
|
|
1254
|
+
]
|
|
1255
|
+
}, pkg.name);
|
|
1256
|
+
}),
|
|
1257
|
+
needsScroll && /* @__PURE__ */ jsx(Text, {
|
|
1258
|
+
color: "gray",
|
|
1259
|
+
children: scrollOffset + maxVisible < totalItems ? ` ↓ ${totalItems - scrollOffset - maxVisible} more below` : " "
|
|
1260
|
+
})
|
|
1261
|
+
]
|
|
1262
|
+
})]
|
|
1263
|
+
}, group.type);
|
|
1264
|
+
}
|
|
1211
1265
|
//#endregion
|
|
1212
1266
|
//#region src/ui/VersionPicker.tsx
|
|
1213
1267
|
function VersionPicker({ pkg, onSelect, onCancel }) {
|
|
@@ -1371,6 +1425,12 @@ function VersionPicker({ pkg, onSelect, onCancel }) {
|
|
|
1371
1425
|
});
|
|
1372
1426
|
}
|
|
1373
1427
|
//#endregion
|
|
1428
|
+
//#region src/lib/utils.ts
|
|
1429
|
+
/** Open a URL in the user's default browser (cross-platform). */
|
|
1430
|
+
function openInBrowser(url) {
|
|
1431
|
+
exec(process.platform === "win32" ? `start "" "${url}"` : process.platform === "darwin" ? `open "${url}"` : `xdg-open "${url}"`);
|
|
1432
|
+
}
|
|
1433
|
+
//#endregion
|
|
1374
1434
|
//#region src/ui/MarkdownLine.tsx
|
|
1375
1435
|
function parseInline(raw) {
|
|
1376
1436
|
const segments = [];
|
|
@@ -1439,9 +1499,6 @@ function MarkdownLine({ line, baseColor = "white", baseDim = false }) {
|
|
|
1439
1499
|
}
|
|
1440
1500
|
//#endregion
|
|
1441
1501
|
//#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
1502
|
function ChangelogPanel({ pkg, onClose }) {
|
|
1446
1503
|
const [entries, setEntries] = useState([]);
|
|
1447
1504
|
const [repoUrl, setRepoUrl] = useState("");
|
|
@@ -1472,6 +1529,14 @@ function ChangelogPanel({ pkg, onClose }) {
|
|
|
1472
1529
|
setTimeout(() => setOpened(false), 2e3);
|
|
1473
1530
|
};
|
|
1474
1531
|
const currentEntry = entries[activeEntry];
|
|
1532
|
+
const clampedScrollBy = (delta) => {
|
|
1533
|
+
const sv = scrollRef.current;
|
|
1534
|
+
if (!sv) return;
|
|
1535
|
+
const maxOffset = sv.getBottomOffset();
|
|
1536
|
+
const current = sv.getScrollOffset();
|
|
1537
|
+
const target = Math.max(0, Math.min(maxOffset, current + delta));
|
|
1538
|
+
sv.scrollTo(target);
|
|
1539
|
+
};
|
|
1475
1540
|
useInput((input, key) => {
|
|
1476
1541
|
if (key.escape || input === "q" || input === "c") {
|
|
1477
1542
|
onClose();
|
|
@@ -1487,16 +1552,10 @@ function ChangelogPanel({ pkg, onClose }) {
|
|
|
1487
1552
|
setActiveEntry((prev) => Math.min(entries.length - 1, prev + 1));
|
|
1488
1553
|
return;
|
|
1489
1554
|
}
|
|
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
|
-
}
|
|
1555
|
+
if (key.upArrow) clampedScrollBy(-1);
|
|
1556
|
+
if (key.downArrow) clampedScrollBy(1);
|
|
1557
|
+
if (key.pageUp) clampedScrollBy(-(scrollRef.current?.getViewportHeight() ?? 10));
|
|
1558
|
+
if (key.pageDown) clampedScrollBy(scrollRef.current?.getViewportHeight() ?? 10);
|
|
1500
1559
|
if (input === "r" && releasesPageUrl) triggerOpen(releasesPageUrl);
|
|
1501
1560
|
if (input === "o" && currentEntry?.url) triggerOpen(currentEntry.url);
|
|
1502
1561
|
});
|
|
@@ -1609,7 +1668,7 @@ function ChangelogPanel({ pkg, onClose }) {
|
|
|
1609
1668
|
children: " Check the package repository manually."
|
|
1610
1669
|
})]
|
|
1611
1670
|
}) : currentEntry ? /* @__PURE__ */ jsx(Box, {
|
|
1612
|
-
height: 18,
|
|
1671
|
+
height: Math.min(currentEntry.body.split("\n").length, 18),
|
|
1613
1672
|
flexDirection: "column",
|
|
1614
1673
|
children: /* @__PURE__ */ jsx(ScrollView, {
|
|
1615
1674
|
ref: scrollRef,
|
|
@@ -1763,6 +1822,46 @@ function UpdateResults({ results, onDone }) {
|
|
|
1763
1822
|
});
|
|
1764
1823
|
}
|
|
1765
1824
|
//#endregion
|
|
1825
|
+
//#region src/ui/SettingsToggle.tsx
|
|
1826
|
+
function SettingsToggle({ label, description, checked, focused, disabled = false }) {
|
|
1827
|
+
return /* @__PURE__ */ jsxs(Box, {
|
|
1828
|
+
flexDirection: "column",
|
|
1829
|
+
marginBottom: 1,
|
|
1830
|
+
children: [/* @__PURE__ */ jsxs(Box, {
|
|
1831
|
+
gap: 1,
|
|
1832
|
+
children: [
|
|
1833
|
+
/* @__PURE__ */ jsx(Text, {
|
|
1834
|
+
dimColor: disabled,
|
|
1835
|
+
color: "greenBright",
|
|
1836
|
+
children: focused ? ">" : " "
|
|
1837
|
+
}),
|
|
1838
|
+
/* @__PURE__ */ jsxs(Text, {
|
|
1839
|
+
dimColor: disabled,
|
|
1840
|
+
color: checked && !disabled ? "greenBright" : "gray",
|
|
1841
|
+
children: [
|
|
1842
|
+
"[",
|
|
1843
|
+
checked ? "x" : " ",
|
|
1844
|
+
"]"
|
|
1845
|
+
]
|
|
1846
|
+
}),
|
|
1847
|
+
/* @__PURE__ */ jsx(Text, {
|
|
1848
|
+
dimColor: disabled,
|
|
1849
|
+
bold: focused,
|
|
1850
|
+
color: disabled ? "gray" : focused ? "whiteBright" : "white",
|
|
1851
|
+
children: label
|
|
1852
|
+
})
|
|
1853
|
+
]
|
|
1854
|
+
}), /* @__PURE__ */ jsx(Box, {
|
|
1855
|
+
marginLeft: 6,
|
|
1856
|
+
children: /* @__PURE__ */ jsx(Text, {
|
|
1857
|
+
dimColor: disabled,
|
|
1858
|
+
color: "gray",
|
|
1859
|
+
children: description
|
|
1860
|
+
})
|
|
1861
|
+
})]
|
|
1862
|
+
});
|
|
1863
|
+
}
|
|
1864
|
+
//#endregion
|
|
1766
1865
|
//#region src/ui/Settings.tsx
|
|
1767
1866
|
function Settings({ config, onConfigChange, onClose }) {
|
|
1768
1867
|
const [inputMode, setInputMode] = useState(false);
|
|
@@ -1851,10 +1950,6 @@ function Settings({ config, onConfigChange, onClose }) {
|
|
|
1851
1950
|
}
|
|
1852
1951
|
if (key.escape || input === "s") onClose();
|
|
1853
1952
|
});
|
|
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
1953
|
const listHeaderFocused = currentRow?.type === "list-header";
|
|
1859
1954
|
return /* @__PURE__ */ jsxs(Box, {
|
|
1860
1955
|
flexDirection: "column",
|
|
@@ -1875,137 +1970,30 @@ function Settings({ config, onConfigChange, onClose }) {
|
|
|
1875
1970
|
]
|
|
1876
1971
|
})
|
|
1877
1972
|
}),
|
|
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
|
-
})]
|
|
1973
|
+
/* @__PURE__ */ jsx(SettingsToggle, {
|
|
1974
|
+
label: "Sort by update frequency",
|
|
1975
|
+
description: "Packages you update often appear at the top",
|
|
1976
|
+
checked: config.frequencySort,
|
|
1977
|
+
focused: currentRow?.type === "toggle-frequency"
|
|
1909
1978
|
}),
|
|
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
|
-
})]
|
|
1979
|
+
/* @__PURE__ */ jsx(SettingsToggle, {
|
|
1980
|
+
label: "Separate dev dependencies",
|
|
1981
|
+
description: "Show dependencies and devDependencies in separate groups",
|
|
1982
|
+
checked: config.separateDevDeps,
|
|
1983
|
+
focused: currentRow?.type === "toggle-separate-dev"
|
|
1941
1984
|
}),
|
|
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
|
-
})]
|
|
1985
|
+
/* @__PURE__ */ jsx(SettingsToggle, {
|
|
1986
|
+
label: "Enable scope grouping",
|
|
1987
|
+
description: "Group scoped packages listed below under their scope prefix",
|
|
1988
|
+
checked: config.groupByScope,
|
|
1989
|
+
focused: currentRow?.type === "toggle-group"
|
|
1973
1990
|
}),
|
|
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
|
-
})]
|
|
1991
|
+
/* @__PURE__ */ jsx(SettingsToggle, {
|
|
1992
|
+
label: "Show grouped scopes on top",
|
|
1993
|
+
description: "Grouped scope packages appear before ungrouped ones",
|
|
1994
|
+
checked: config.groupsOnTop,
|
|
1995
|
+
focused: currentRow?.type === "toggle-groups-top",
|
|
1996
|
+
disabled: !config.groupByScope
|
|
2009
1997
|
}),
|
|
2010
1998
|
/* @__PURE__ */ jsxs(Box, {
|
|
2011
1999
|
flexDirection: "column",
|
|
@@ -2197,78 +2185,213 @@ function SelfUpdatePrompt({ currentVersion, latestVersion, updating, error, onUp
|
|
|
2197
2185
|
});
|
|
2198
2186
|
}
|
|
2199
2187
|
//#endregion
|
|
2200
|
-
//#region src/ui/
|
|
2201
|
-
function
|
|
2202
|
-
|
|
2203
|
-
|
|
2204
|
-
|
|
2188
|
+
//#region src/ui/TerminalOutputBox.tsx
|
|
2189
|
+
function TerminalOutputBox({ message, command, outputLines, maxLines }) {
|
|
2190
|
+
return /* @__PURE__ */ jsxs(Box, {
|
|
2191
|
+
flexDirection: "column",
|
|
2192
|
+
padding: 1,
|
|
2193
|
+
children: [
|
|
2194
|
+
/* @__PURE__ */ jsxs(Text, {
|
|
2195
|
+
color: "greenBright",
|
|
2196
|
+
bold: true,
|
|
2197
|
+
children: [" ", "ripen"]
|
|
2198
|
+
}),
|
|
2199
|
+
/* @__PURE__ */ jsx(Box, {
|
|
2200
|
+
marginTop: 1,
|
|
2201
|
+
children: /* @__PURE__ */ jsx(Text, {
|
|
2202
|
+
color: "gray",
|
|
2203
|
+
children: message
|
|
2204
|
+
})
|
|
2205
|
+
}),
|
|
2206
|
+
/* @__PURE__ */ jsxs(Box, {
|
|
2207
|
+
flexDirection: "column",
|
|
2208
|
+
marginTop: 1,
|
|
2209
|
+
borderStyle: "round",
|
|
2210
|
+
borderColor: "gray",
|
|
2211
|
+
paddingX: 1,
|
|
2212
|
+
width: 64,
|
|
2213
|
+
height: maxLines + 3,
|
|
2214
|
+
overflow: "hidden",
|
|
2215
|
+
children: [command !== "" && /* @__PURE__ */ jsxs(Box, { children: [/* @__PURE__ */ jsx(Text, {
|
|
2216
|
+
color: "gray",
|
|
2217
|
+
children: "$ "
|
|
2218
|
+
}), /* @__PURE__ */ jsx(Text, {
|
|
2219
|
+
color: "gray",
|
|
2220
|
+
children: command
|
|
2221
|
+
})] }), outputLines.map((line, i) => /* @__PURE__ */ jsx(Text, {
|
|
2222
|
+
color: line.includes("WARN") || line.includes("ERR") ? "yellow" : "gray",
|
|
2223
|
+
wrap: "truncate",
|
|
2224
|
+
children: line
|
|
2225
|
+
}, i))]
|
|
2226
|
+
})
|
|
2227
|
+
]
|
|
2205
2228
|
});
|
|
2206
|
-
|
|
2229
|
+
}
|
|
2230
|
+
//#endregion
|
|
2231
|
+
//#region src/ui/hooks/use-self-update.ts
|
|
2232
|
+
function useSelfUpdate(currentVersion, installManager) {
|
|
2207
2233
|
const [latestVersion, setLatestVersion] = useState(null);
|
|
2208
|
-
const [selfUpdateError, setSelfUpdateError] = useState(null);
|
|
2209
2234
|
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…");
|
|
2235
|
+
const [selfUpdateError, setSelfUpdateError] = useState(null);
|
|
2236
|
+
const [checkComplete, setCheckComplete] = useState(false);
|
|
2237
|
+
const [hasUpdate, setHasUpdate] = useState(false);
|
|
2220
2238
|
useEffect(() => {
|
|
2221
2239
|
let cancelled = false;
|
|
2222
2240
|
fetchLatestVersion("ripencli").then((latest) => {
|
|
2223
2241
|
if (cancelled) return;
|
|
2224
|
-
if (latest && isNewerVersion(
|
|
2242
|
+
if (latest && isNewerVersion(currentVersion, latest)) {
|
|
2225
2243
|
setLatestVersion(latest);
|
|
2226
|
-
|
|
2227
|
-
}
|
|
2244
|
+
setHasUpdate(true);
|
|
2245
|
+
}
|
|
2246
|
+
setCheckComplete(true);
|
|
2228
2247
|
});
|
|
2229
2248
|
return () => {
|
|
2230
2249
|
cancelled = true;
|
|
2231
2250
|
};
|
|
2232
2251
|
}, []);
|
|
2252
|
+
const performUpdate = async () => {
|
|
2253
|
+
setSelfUpdating(true);
|
|
2254
|
+
try {
|
|
2255
|
+
await execa(installManager, installManager === "yarn" ? [
|
|
2256
|
+
"global",
|
|
2257
|
+
"add",
|
|
2258
|
+
`ripencli@${latestVersion}`
|
|
2259
|
+
] : [
|
|
2260
|
+
"add",
|
|
2261
|
+
"--global",
|
|
2262
|
+
`ripencli@${latestVersion}`
|
|
2263
|
+
]);
|
|
2264
|
+
setSelfUpdating(false);
|
|
2265
|
+
return true;
|
|
2266
|
+
} catch (err) {
|
|
2267
|
+
setSelfUpdateError(err.message ?? "Unknown error");
|
|
2268
|
+
setSelfUpdating(false);
|
|
2269
|
+
return false;
|
|
2270
|
+
}
|
|
2271
|
+
};
|
|
2272
|
+
return {
|
|
2273
|
+
latestVersion,
|
|
2274
|
+
selfUpdating,
|
|
2275
|
+
selfUpdateError,
|
|
2276
|
+
checkComplete,
|
|
2277
|
+
hasUpdate,
|
|
2278
|
+
performUpdate
|
|
2279
|
+
};
|
|
2280
|
+
}
|
|
2281
|
+
//#endregion
|
|
2282
|
+
//#region src/ui/hooks/use-packages.ts
|
|
2283
|
+
function usePackages() {
|
|
2284
|
+
const [packages, setPackages] = useState([]);
|
|
2285
|
+
return {
|
|
2286
|
+
packages,
|
|
2287
|
+
setPackages,
|
|
2288
|
+
toggleOne: useCallback((index) => {
|
|
2289
|
+
setPackages((prev) => prev.map((p, i) => i === index ? {
|
|
2290
|
+
...p,
|
|
2291
|
+
selected: !p.selected
|
|
2292
|
+
} : p));
|
|
2293
|
+
}, []),
|
|
2294
|
+
toggleMany: useCallback((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
|
+
chooseVersion: useCallback((activeIndex, version) => {
|
|
2304
|
+
setPackages((prev) => prev.map((p, i) => i === activeIndex ? {
|
|
2305
|
+
...p,
|
|
2306
|
+
targetVersion: version,
|
|
2307
|
+
selected: true
|
|
2308
|
+
} : p));
|
|
2309
|
+
}, [])
|
|
2310
|
+
};
|
|
2311
|
+
}
|
|
2312
|
+
//#endregion
|
|
2313
|
+
//#region src/ui/hooks/use-terminal-output.ts
|
|
2314
|
+
const DEFAULT_MAX_LINES = 3;
|
|
2315
|
+
function useTerminalOutput(maxLines = DEFAULT_MAX_LINES) {
|
|
2316
|
+
const [outputLines, setOutputLines] = useState([]);
|
|
2317
|
+
const [terminalCmd, setTerminalCmd] = useState("");
|
|
2318
|
+
const [loadingMsg, setLoadingMsg] = useState("");
|
|
2319
|
+
return {
|
|
2320
|
+
outputLines,
|
|
2321
|
+
terminalCmd,
|
|
2322
|
+
loadingMsg,
|
|
2323
|
+
setTerminalCmd,
|
|
2324
|
+
setLoadingMsg,
|
|
2325
|
+
onLine: useCallback((line) => {
|
|
2326
|
+
setOutputLines((prev) => [...prev.slice(-(maxLines - 1)), line]);
|
|
2327
|
+
}, [maxLines]),
|
|
2328
|
+
reset: useCallback(() => {
|
|
2329
|
+
setOutputLines([]);
|
|
2330
|
+
setTerminalCmd("");
|
|
2331
|
+
setLoadingMsg("");
|
|
2332
|
+
}, []),
|
|
2333
|
+
maxLines
|
|
2334
|
+
};
|
|
2335
|
+
}
|
|
2336
|
+
//#endregion
|
|
2337
|
+
//#region src/ui/hooks/use-exit-on-screen.ts
|
|
2338
|
+
/**
|
|
2339
|
+
* Exit the Ink app after a delay when the screen matches one of the target screens.
|
|
2340
|
+
*/
|
|
2341
|
+
function useExitOnScreen(screen, targetScreens, exit, options) {
|
|
2342
|
+
const { delay = 300, beforeExit } = options ?? {};
|
|
2233
2343
|
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;
|
|
2344
|
+
if (!targetScreens.includes(screen)) return;
|
|
2243
2345
|
const timer = setTimeout(() => {
|
|
2244
2346
|
exit();
|
|
2347
|
+
beforeExit?.();
|
|
2245
2348
|
process.exit(0);
|
|
2246
|
-
},
|
|
2349
|
+
}, delay);
|
|
2247
2350
|
return () => clearTimeout(timer);
|
|
2248
2351
|
}, [screen]);
|
|
2352
|
+
}
|
|
2353
|
+
//#endregion
|
|
2354
|
+
//#region src/ui/App.tsx
|
|
2355
|
+
function App({ project, global, showAll, version, installManager }) {
|
|
2356
|
+
const { exit } = useApp();
|
|
2357
|
+
const [screen, setScreen] = useState("self-update-check");
|
|
2358
|
+
const [config, setConfig] = useState(() => loadConfig());
|
|
2359
|
+
const [frequency, setFrequency] = useState(() => loadFrequency());
|
|
2360
|
+
const [activeIndex, setActiveIndex] = useState(0);
|
|
2361
|
+
const [errorMsg, setErrorMsg] = useState("");
|
|
2362
|
+
const selfUpdate = useSelfUpdate(version, installManager);
|
|
2363
|
+
const { packages, setPackages, toggleOne, toggleMany, chooseVersion } = usePackages();
|
|
2364
|
+
const terminal = useTerminalOutput();
|
|
2365
|
+
useInput((_input, key) => {
|
|
2366
|
+
if (key.ctrl && _input === "c") setScreen("cancelled");
|
|
2367
|
+
});
|
|
2368
|
+
useExitOnScreen(screen, ["self-update-done", "empty"], exit);
|
|
2369
|
+
useExitOnScreen(screen, ["cancelled"], exit, {
|
|
2370
|
+
delay: 200,
|
|
2371
|
+
beforeExit: () => console.log(" \x1B[32mCancelled.\x1B[0m\n")
|
|
2372
|
+
});
|
|
2249
2373
|
useEffect(() => {
|
|
2250
|
-
if (
|
|
2251
|
-
|
|
2252
|
-
|
|
2253
|
-
|
|
2254
|
-
|
|
2255
|
-
|
|
2256
|
-
|
|
2257
|
-
}
|
|
2374
|
+
if (!selfUpdate.checkComplete) return;
|
|
2375
|
+
if (screen !== "self-update-check") return;
|
|
2376
|
+
setScreen(selfUpdate.hasUpdate ? "self-update" : "loading");
|
|
2377
|
+
}, [selfUpdate.checkComplete]);
|
|
2378
|
+
const handleSelfUpdate = async () => {
|
|
2379
|
+
if (await selfUpdate.performUpdate()) setScreen("self-update-done");
|
|
2380
|
+
else setTimeout(() => setScreen("loading"), 2e3);
|
|
2381
|
+
};
|
|
2258
2382
|
const [fetchStarted, setFetchStarted] = useState(false);
|
|
2259
2383
|
useEffect(() => {
|
|
2260
2384
|
if (screen !== "loading" || fetchStarted) return;
|
|
2261
2385
|
setFetchStarted(true);
|
|
2262
|
-
|
|
2263
|
-
|
|
2264
|
-
|
|
2265
|
-
(global ? getAllGlobalOutdated(project.cwd, onLine) : getOutdatedPackages(project.manager, project.cwd, false, onLine)).then((result) => {
|
|
2386
|
+
terminal.setLoadingMsg("Checking for outdated packages…");
|
|
2387
|
+
terminal.setTerminalCmd(global ? "Checking all package managers…" : "Checking npm registry…");
|
|
2388
|
+
(global ? getAllGlobalOutdated(project.cwd, terminal.onLine) : getOutdatedPackages(project.manager, project.cwd, false, terminal.onLine, showAll)).then((result) => {
|
|
2266
2389
|
if (!result.ok) {
|
|
2267
2390
|
setErrorMsg(result.error);
|
|
2268
2391
|
setScreen("error");
|
|
2269
2392
|
return;
|
|
2270
2393
|
}
|
|
2271
|
-
|
|
2394
|
+
terminal.reset();
|
|
2272
2395
|
if (result.packages.length === 0) setScreen("empty");
|
|
2273
2396
|
else {
|
|
2274
2397
|
setPackages(result.packages);
|
|
@@ -2276,81 +2399,18 @@ function App({ project, global, version, installManager }) {
|
|
|
2276
2399
|
}
|
|
2277
2400
|
});
|
|
2278
2401
|
}, [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
2402
|
const handleConfigChange = (newConfig) => {
|
|
2304
2403
|
setConfig(newConfig);
|
|
2305
2404
|
saveConfig(newConfig);
|
|
2306
2405
|
};
|
|
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
|
-
};
|
|
2406
|
+
const [results, setResults] = useState([]);
|
|
2343
2407
|
const handleConfirm = async () => {
|
|
2344
2408
|
const selected = packages.filter((p) => p.selected);
|
|
2345
2409
|
if (selected.length === 0) return;
|
|
2346
|
-
setLoadingMsg(`Updating ${selected.length} package${selected.length > 1 ? "s" : ""}…`);
|
|
2347
|
-
setTerminalCmd("");
|
|
2348
|
-
setOutputLines([]);
|
|
2410
|
+
terminal.setLoadingMsg(`Updating ${selected.length} package${selected.length > 1 ? "s" : ""}…`);
|
|
2411
|
+
terminal.setTerminalCmd("");
|
|
2349
2412
|
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);
|
|
2413
|
+
const res = await updatePackages(project.manager, selected, project.cwd, global, terminal.onLine);
|
|
2354
2414
|
setResults(res);
|
|
2355
2415
|
setScreen("results");
|
|
2356
2416
|
const successNames = res.filter((r) => r.success).map((r) => r.name);
|
|
@@ -2376,9 +2436,9 @@ function App({ project, global, version, installManager }) {
|
|
|
2376
2436
|
});
|
|
2377
2437
|
if (screen === "self-update") return /* @__PURE__ */ jsx(SelfUpdatePrompt, {
|
|
2378
2438
|
currentVersion: version,
|
|
2379
|
-
latestVersion,
|
|
2380
|
-
updating: selfUpdating,
|
|
2381
|
-
error: selfUpdateError,
|
|
2439
|
+
latestVersion: selfUpdate.latestVersion,
|
|
2440
|
+
updating: selfUpdate.selfUpdating,
|
|
2441
|
+
error: selfUpdate.selfUpdateError,
|
|
2382
2442
|
onUpdate: handleSelfUpdate,
|
|
2383
2443
|
onSkip: () => setScreen("loading")
|
|
2384
2444
|
});
|
|
@@ -2395,50 +2455,17 @@ function App({ project, global, version, installManager }) {
|
|
|
2395
2455
|
color: "green",
|
|
2396
2456
|
children: [
|
|
2397
2457
|
"✓ Updated to v",
|
|
2398
|
-
latestVersion,
|
|
2458
|
+
selfUpdate.latestVersion,
|
|
2399
2459
|
". Run ripen again to use the new version."
|
|
2400
2460
|
]
|
|
2401
2461
|
})
|
|
2402
2462
|
})]
|
|
2403
2463
|
});
|
|
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
|
-
]
|
|
2464
|
+
if (screen === "loading") return /* @__PURE__ */ jsx(TerminalOutputBox, {
|
|
2465
|
+
message: terminal.loadingMsg,
|
|
2466
|
+
command: terminal.terminalCmd,
|
|
2467
|
+
outputLines: terminal.outputLines,
|
|
2468
|
+
maxLines: terminal.maxLines
|
|
2442
2469
|
});
|
|
2443
2470
|
if (screen === "error") return /* @__PURE__ */ jsxs(Box, {
|
|
2444
2471
|
flexDirection: "column",
|
|
@@ -2491,44 +2518,11 @@ function App({ project, global, version, installManager }) {
|
|
|
2491
2518
|
});
|
|
2492
2519
|
const isListActive = screen === "list";
|
|
2493
2520
|
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
|
-
]
|
|
2521
|
+
screen === "updating" && /* @__PURE__ */ jsx(TerminalOutputBox, {
|
|
2522
|
+
message: terminal.loadingMsg,
|
|
2523
|
+
command: terminal.terminalCmd,
|
|
2524
|
+
outputLines: terminal.outputLines,
|
|
2525
|
+
maxLines: terminal.maxLines
|
|
2532
2526
|
}),
|
|
2533
2527
|
screen === "results" && /* @__PURE__ */ jsx(Box, {
|
|
2534
2528
|
padding: 1,
|
|
@@ -2552,7 +2546,10 @@ function App({ project, global, version, installManager }) {
|
|
|
2552
2546
|
padding: 1,
|
|
2553
2547
|
children: /* @__PURE__ */ jsx(VersionPicker, {
|
|
2554
2548
|
pkg: packages[activeIndex],
|
|
2555
|
-
onSelect:
|
|
2549
|
+
onSelect: (v) => {
|
|
2550
|
+
chooseVersion(activeIndex, v);
|
|
2551
|
+
setScreen("list");
|
|
2552
|
+
},
|
|
2556
2553
|
onCancel: () => setScreen("list")
|
|
2557
2554
|
})
|
|
2558
2555
|
}),
|
|
@@ -2568,11 +2565,16 @@ function App({ project, global, version, installManager }) {
|
|
|
2568
2565
|
display: isListActive ? "flex" : "none",
|
|
2569
2566
|
children: /* @__PURE__ */ jsx(PackageList, {
|
|
2570
2567
|
packages,
|
|
2571
|
-
onToggle:
|
|
2572
|
-
|
|
2573
|
-
|
|
2574
|
-
|
|
2575
|
-
|
|
2568
|
+
onToggle: toggleOne,
|
|
2569
|
+
onToggleMany: toggleMany,
|
|
2570
|
+
onSelectVersion: (i) => {
|
|
2571
|
+
setActiveIndex(i);
|
|
2572
|
+
setScreen("version-picker");
|
|
2573
|
+
},
|
|
2574
|
+
onViewChangelog: (i) => {
|
|
2575
|
+
setActiveIndex(i);
|
|
2576
|
+
setScreen("changelog");
|
|
2577
|
+
},
|
|
2576
2578
|
onConfirm: handleConfirm,
|
|
2577
2579
|
onOpenSettings: () => setScreen("settings"),
|
|
2578
2580
|
groupByScope: config.groupByScope,
|
|
@@ -2581,6 +2583,7 @@ function App({ project, global, version, installManager }) {
|
|
|
2581
2583
|
frequencySort: config.frequencySort,
|
|
2582
2584
|
frequency,
|
|
2583
2585
|
separateDevDeps: config.separateDevDeps,
|
|
2586
|
+
showAll,
|
|
2584
2587
|
isActive: isListActive
|
|
2585
2588
|
})
|
|
2586
2589
|
})
|
|
@@ -2591,6 +2594,7 @@ function App({ project, global, version, installManager }) {
|
|
|
2591
2594
|
const { version: VERSION } = createRequire(import.meta.url)("../package.json");
|
|
2592
2595
|
const args = process.argv.slice(2);
|
|
2593
2596
|
const isGlobal = args.includes("--global") || args.includes("-g");
|
|
2597
|
+
const showAll = args.includes("--all") || args.includes("-a");
|
|
2594
2598
|
const showHelp = args.includes("--help") || args.includes("-h");
|
|
2595
2599
|
if (args.includes("--version") || args.includes("-V")) {
|
|
2596
2600
|
console.log(VERSION);
|
|
@@ -2603,6 +2607,7 @@ if (showHelp) {
|
|
|
2603
2607
|
Usage:
|
|
2604
2608
|
ripen check current project
|
|
2605
2609
|
ripen -g check global packages
|
|
2610
|
+
ripen -a show all packages, not just outdated ones
|
|
2606
2611
|
ripen --help show this help
|
|
2607
2612
|
ripen --version show version
|
|
2608
2613
|
|
|
@@ -2625,6 +2630,7 @@ if (!isGlobal && !hasPackageJson(cwd)) {
|
|
|
2625
2630
|
const { waitUntilExit } = render(/* @__PURE__ */ jsx(App, {
|
|
2626
2631
|
project: getProjectInfo(cwd),
|
|
2627
2632
|
global: isGlobal,
|
|
2633
|
+
showAll,
|
|
2628
2634
|
version: VERSION,
|
|
2629
2635
|
installManager: detectGlobalInstallManager()
|
|
2630
2636
|
}), { exitOnCtrlC: false });
|