dsh-plugin-scout 0.0.0-stage → 0.1.1
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/LICENSE +21 -0
- package/README.md +137 -2
- package/README.zh.md +136 -0
- package/cordis.patch.yml +6 -0
- package/icon.svg +11 -0
- package/lib/client.js +1024 -0
- package/lib/host.js +2996 -0
- package/locale/en.json +6 -0
- package/locale/zh.json +6 -0
- package/package.json +84 -4
package/lib/client.js
ADDED
|
@@ -0,0 +1,1024 @@
|
|
|
1
|
+
window.__ModuleLoader__.load({
|
|
2
|
+
id: "dsh-plugin-scout",
|
|
3
|
+
factory: (require) => {
|
|
4
|
+
const module = { exports: {} };
|
|
5
|
+
const exports = module.exports;
|
|
6
|
+
var __create = Object.create;
|
|
7
|
+
var __defProp = Object.defineProperty;
|
|
8
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
9
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
10
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
11
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
12
|
+
var __export = (target, all) => {
|
|
13
|
+
for (var name in all)
|
|
14
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
15
|
+
};
|
|
16
|
+
var __copyProps = (to, from, except, desc) => {
|
|
17
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
18
|
+
for (let key of __getOwnPropNames(from))
|
|
19
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
20
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
21
|
+
}
|
|
22
|
+
return to;
|
|
23
|
+
};
|
|
24
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
25
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
26
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
27
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
28
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
29
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
30
|
+
mod
|
|
31
|
+
));
|
|
32
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
33
|
+
|
|
34
|
+
// src/client/index.jsx
|
|
35
|
+
var index_exports = {};
|
|
36
|
+
__export(index_exports, {
|
|
37
|
+
PANEL_ID: () => PANEL_ID,
|
|
38
|
+
PKG: () => PKG,
|
|
39
|
+
PanelIcon: () => PanelIcon,
|
|
40
|
+
apply: () => apply,
|
|
41
|
+
createInstalledStore: () => createInstalledStore,
|
|
42
|
+
inject: () => inject,
|
|
43
|
+
pluginManagerOf: () => pluginManagerOf
|
|
44
|
+
});
|
|
45
|
+
module.exports = __toCommonJS(index_exports);
|
|
46
|
+
var import_react3 = __toESM(require("react"), 1);
|
|
47
|
+
|
|
48
|
+
// src/client/Page.jsx
|
|
49
|
+
var import_react2 = __toESM(require("react"), 1);
|
|
50
|
+
|
|
51
|
+
// src/client/api.js
|
|
52
|
+
var PREFIX = "/dsh-plugin-scout";
|
|
53
|
+
async function call(path, init = {}) {
|
|
54
|
+
const res = await fetch(`${PREFIX}${path}`, {
|
|
55
|
+
...init,
|
|
56
|
+
credentials: "same-origin",
|
|
57
|
+
headers: { "x-dsh-plugin-scout": "1", ...init.body ? { "content-type": "application/json" } : {}, ...init.headers }
|
|
58
|
+
});
|
|
59
|
+
let body;
|
|
60
|
+
try {
|
|
61
|
+
body = await res.json();
|
|
62
|
+
} catch {
|
|
63
|
+
body = void 0;
|
|
64
|
+
}
|
|
65
|
+
if (!body || body.ok === false) {
|
|
66
|
+
const err = new Error(body?.error?.message ?? `HTTP ${res.status}`);
|
|
67
|
+
err.code = body?.error?.code ?? "http";
|
|
68
|
+
err.value = body?.value;
|
|
69
|
+
throw err;
|
|
70
|
+
}
|
|
71
|
+
return body.value;
|
|
72
|
+
}
|
|
73
|
+
var api = {
|
|
74
|
+
status: () => call("/api/status"),
|
|
75
|
+
list: (params) => call("/api/list", { method: "POST", body: JSON.stringify(params) }),
|
|
76
|
+
detail: (key) => call(`/api/detail?key=${encodeURIComponent(key)}`),
|
|
77
|
+
badges: (names) => call("/api/badges", { method: "POST", body: JSON.stringify({ names }) }),
|
|
78
|
+
refresh: (full) => call("/api/refresh", { method: "POST", body: JSON.stringify({ full: !!full }) })
|
|
79
|
+
};
|
|
80
|
+
function createBadgeCache(fetchBadges = api.badges) {
|
|
81
|
+
const cache = /* @__PURE__ */ new Map();
|
|
82
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
83
|
+
let pending = /* @__PURE__ */ new Set();
|
|
84
|
+
let timer;
|
|
85
|
+
const flush = async () => {
|
|
86
|
+
const names = [...pending];
|
|
87
|
+
pending = /* @__PURE__ */ new Set();
|
|
88
|
+
timer = void 0;
|
|
89
|
+
try {
|
|
90
|
+
const got = await fetchBadges(names);
|
|
91
|
+
for (const n of names) cache.set(n, got[n] ?? null);
|
|
92
|
+
} catch {
|
|
93
|
+
for (const n of names) cache.set(n, null);
|
|
94
|
+
}
|
|
95
|
+
for (const l of listeners) l();
|
|
96
|
+
};
|
|
97
|
+
return {
|
|
98
|
+
get(name) {
|
|
99
|
+
if (cache.has(name)) return cache.get(name);
|
|
100
|
+
pending.add(name);
|
|
101
|
+
timer ??= setTimeout(flush, 30);
|
|
102
|
+
return void 0;
|
|
103
|
+
},
|
|
104
|
+
subscribe(l) {
|
|
105
|
+
listeners.add(l);
|
|
106
|
+
return () => listeners.delete(l);
|
|
107
|
+
},
|
|
108
|
+
clear() {
|
|
109
|
+
cache.clear();
|
|
110
|
+
for (const l of listeners) l();
|
|
111
|
+
}
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// src/client/view.js
|
|
116
|
+
var band = (score) => score >= 70 ? "hi" : score >= 45 ? "mid" : "lo";
|
|
117
|
+
var ICON = { ok: "\u2713", warn: "!", bad: "\u2715", unk: "?" };
|
|
118
|
+
function rowTags(r, labels, runtime, installed) {
|
|
119
|
+
const t = [];
|
|
120
|
+
if (!r.cats.length) t.push(["mute", "\u672A\u5F52\u7C7B"]);
|
|
121
|
+
else r.cats.slice(0, 2).forEach((id, i) => t.push([i ? "cat2" : "cat", labels[id] ?? id]));
|
|
122
|
+
t.push(r.compat === "ok" ? ["ok", `\u517C\u5BB9 ${runtime}`] : r.compat === "incompatible" ? ["warn", "\u4E0D\u517C\u5BB9\u5F53\u524D\u7248\u672C"] : ["mute", "\u672A\u58F0\u660E\u517C\u5BB9"]);
|
|
123
|
+
if (r.hooks.length) t.push(["bad", `\u5B89\u88C5\u811A\u672C \xB7 ${r.hooks.join("/")}`]);
|
|
124
|
+
if (r.chain === "mismatch") t.push(["bad", "\u6765\u6E90\u94FE\u4E0D\u4E00\u81F4"]);
|
|
125
|
+
if (r.chain === "unpublished") t.push(["warn", "\u4EC5 Git"]);
|
|
126
|
+
if (r.provenance) t.push(["ok", "provenance"]);
|
|
127
|
+
if (r.archived) t.push(["bad", "\u5DF2\u5F52\u6863"]);
|
|
128
|
+
t.push(["info", r.kinds.includes("client") ? "\u5BBF\u4E3B + \u754C\u9762" : "\u4EC5\u5BBF\u4E3B"]);
|
|
129
|
+
if (r.copies > 1) t.push(["mute", r.shadowOf ? `\u526F\u672C \xB7 \u4EE3\u8868 ${r.shadowOf}` : `${r.copies - 1} \u4E2A\u540C\u540D\u526F\u672C`]);
|
|
130
|
+
if (installed) t.push(["mute", "\u5DF2\u5B89\u88C5"]);
|
|
131
|
+
return t;
|
|
132
|
+
}
|
|
133
|
+
function ago(iso, now = Date.now()) {
|
|
134
|
+
if (!iso) return "\u4ECE\u672A";
|
|
135
|
+
const s = Math.max(0, Math.round((now - Date.parse(iso)) / 1e3));
|
|
136
|
+
if (s < 90) return "\u521A\u521A";
|
|
137
|
+
if (s < 5400) return `${Math.round(s / 60)} \u5206\u949F\u524D`;
|
|
138
|
+
if (s < 129600) return `${Math.round(s / 3600)} \u5C0F\u65F6\u524D`;
|
|
139
|
+
return `${Math.round(s / 86400)} \u5929\u524D`;
|
|
140
|
+
}
|
|
141
|
+
function progressText(p) {
|
|
142
|
+
if (!p) return "\u51C6\u5907\u4E2D\u2026";
|
|
143
|
+
const parts = [];
|
|
144
|
+
if (p.phase === "github") parts.push(`GitHub ${(p.repos ?? 0).toLocaleString()} / ~${(p.expected ?? 0).toLocaleString()}`);
|
|
145
|
+
else parts.push("GitHub \u5B8C\u6210");
|
|
146
|
+
if (p.npm?.total) parts.push(`npm ${p.npm.done.toLocaleString()} / ${p.npm.total.toLocaleString()}`);
|
|
147
|
+
if (p.published) parts.push(`\u5DF2\u53EF\u6D4F\u89C8 ${p.published.toLocaleString()}`);
|
|
148
|
+
return parts.join(" \xB7 ");
|
|
149
|
+
}
|
|
150
|
+
function progressRatio(p) {
|
|
151
|
+
if (!p) return 0;
|
|
152
|
+
const gh = p.phase === "github" ? p.expected ? Math.min(1, (p.repos ?? 0) / p.expected) : 0.05 : 1;
|
|
153
|
+
const npm = p.npm?.total ? p.npm.done / p.npm.total : p.phase === "github" ? 0 : 1;
|
|
154
|
+
return Math.min(1, gh * 0.7 + npm * 0.3);
|
|
155
|
+
}
|
|
156
|
+
function inspectText(result) {
|
|
157
|
+
if (!result) return null;
|
|
158
|
+
if (result.error) return { tone: "bad", text: `\u8C03\u7528\u5931\u8D25\uFF1A${result.error}` };
|
|
159
|
+
if (result.status === "accepted") {
|
|
160
|
+
const v = [result.name, result.version].filter(Boolean).join("@");
|
|
161
|
+
if (result.bundle === false) return { tone: "bad", text: `\u80FD\u89E3\u6790\u5230 ${v}\uFF0C\u4F46\u5B83\u4E0D\u662F\u7EC4\u5408\u5305\uFF0C\u5B98\u65B9\u63D2\u4EF6\u9875\u4E0D\u4F1A\u63A5\u53D7` };
|
|
162
|
+
return { tone: "ok", text: `\u53EF\u4EE5\u6DFB\u52A0\uFF1A\u89E3\u6790\u5230 ${v || result.host || result.kind}${result.registry ? `\uFF08${result.registry}\uFF09` : ""}` };
|
|
163
|
+
}
|
|
164
|
+
const map = { "already-installed": "\u5DF2\u7ECF\u5B89\u88C5", "not-found": "registry \u4E0A\u627E\u4E0D\u5230", network: "\u7F51\u7EDC\u4E0D\u53EF\u8FBE", "invalid-spec": "\u89C4\u683C\u65E0\u6548", "not-a-package": "\u4E0D\u662F npm \u5305", "not-a-bundle": "\u4E0D\u662F DSH \u7EC4\u5408\u5305", unknown: "\u672A\u77E5\u539F\u56E0" };
|
|
165
|
+
return { tone: result.problem === "already-installed" ? "ok" : "warn", text: `${map[result.problem] ?? result.problem}\uFF1A${result.reason}` };
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// src/client/Drawer.jsx
|
|
169
|
+
var import_react = __toESM(require("react"), 1);
|
|
170
|
+
var import_jsx_runtime = require("react/jsx-runtime");
|
|
171
|
+
function Checklist({ checks }) {
|
|
172
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "scx-chk", children: checks.map((c) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "scx-ci", "data-check": c.id, "data-st": c.st, children: [
|
|
173
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "scx-ic", "data-st": c.st, children: ICON[c.st] }),
|
|
174
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
|
|
175
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
|
|
176
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "scx-id", children: c.id }),
|
|
177
|
+
c.text
|
|
178
|
+
] }),
|
|
179
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "scx-ev", children: c.ev })
|
|
180
|
+
] })
|
|
181
|
+
] }, c.id)) });
|
|
182
|
+
}
|
|
183
|
+
function Drawer({ repoKey, face, labels, runtime, installed, onClose, say }) {
|
|
184
|
+
const [d, setD] = (0, import_react.useState)(null);
|
|
185
|
+
const [err, setErr] = (0, import_react.useState)(null);
|
|
186
|
+
const [inspect, setInspect] = (0, import_react.useState)(null);
|
|
187
|
+
const [armed, setArmed] = (0, import_react.useState)(false);
|
|
188
|
+
const [armInstall, setArmInstall] = (0, import_react.useState)(false);
|
|
189
|
+
const [armBuilds, setArmBuilds] = (0, import_react.useState)(false);
|
|
190
|
+
const ins = face.useInstall?.(d?.name);
|
|
191
|
+
(0, import_react.useEffect)(() => {
|
|
192
|
+
let live = true;
|
|
193
|
+
setD(null);
|
|
194
|
+
setErr(null);
|
|
195
|
+
setInspect(null);
|
|
196
|
+
setArmed(false);
|
|
197
|
+
setArmInstall(false);
|
|
198
|
+
setArmBuilds(false);
|
|
199
|
+
api.detail(repoKey).then((v) => {
|
|
200
|
+
if (live) setD(v);
|
|
201
|
+
}, (e) => {
|
|
202
|
+
if (live) setErr(e.message);
|
|
203
|
+
});
|
|
204
|
+
return () => {
|
|
205
|
+
live = false;
|
|
206
|
+
};
|
|
207
|
+
}, [repoKey]);
|
|
208
|
+
(0, import_react.useEffect)(() => {
|
|
209
|
+
const onKey = (e) => {
|
|
210
|
+
if (e.key === "Escape") onClose();
|
|
211
|
+
};
|
|
212
|
+
document.addEventListener("keydown", onKey);
|
|
213
|
+
return () => document.removeEventListener("keydown", onKey);
|
|
214
|
+
}, [onClose]);
|
|
215
|
+
const runInspect = async () => {
|
|
216
|
+
if (!d || !face.inspect) return;
|
|
217
|
+
setInspect({ pending: true });
|
|
218
|
+
setInspect(await face.inspect(d.spec));
|
|
219
|
+
};
|
|
220
|
+
const copy = async () => {
|
|
221
|
+
if (d.risky && !armed) {
|
|
222
|
+
setArmed(true);
|
|
223
|
+
say("\u5B58\u5728 \u2715 \u7EA7\u6838\u9A8C\u9879\uFF0C\u5148\u770B\u6E05\u5355\uFF1B\u518D\u70B9\u4E00\u6B21\u786E\u8BA4\u590D\u5236");
|
|
224
|
+
return;
|
|
225
|
+
}
|
|
226
|
+
setArmed(false);
|
|
227
|
+
try {
|
|
228
|
+
await navigator.clipboard.writeText(d.spec);
|
|
229
|
+
say(`\u5DF2\u590D\u5236 ${d.spec}`);
|
|
230
|
+
} catch {
|
|
231
|
+
say("\u526A\u8D34\u677F\u4E0D\u53EF\u7528\uFF0C\u8BF7\u624B\u52A8\u9009\u4E2D\u89C4\u683C\u590D\u5236");
|
|
232
|
+
}
|
|
233
|
+
};
|
|
234
|
+
const needsConfirm = d && (d.risky || d.compat === "incompatible");
|
|
235
|
+
const install = async () => {
|
|
236
|
+
if (needsConfirm && !armInstall) {
|
|
237
|
+
setArmInstall(true);
|
|
238
|
+
say(d.risky ? "\u5B58\u5728 \u2715 \u7EA7\u6838\u9A8C\u9879\uFF0C\u5148\u770B\u6E05\u5355\uFF1B\u518D\u70B9\u4E00\u6B21\u786E\u8BA4\u5B89\u88C5" : "\u4E0E\u5F53\u524D DSH \u7248\u672C\u4E0D\u517C\u5BB9\uFF0C\u5B98\u65B9\u5927\u6982\u7387\u4F1A\u62D2\u88C5\uFF1B\u518D\u70B9\u4E00\u6B21\u4ECD\u7136\u5C1D\u8BD5");
|
|
239
|
+
return;
|
|
240
|
+
}
|
|
241
|
+
setArmInstall(false);
|
|
242
|
+
await face.installer.install(d.name, d.spec);
|
|
243
|
+
};
|
|
244
|
+
const allowBuilds = async () => {
|
|
245
|
+
if (!armBuilds) {
|
|
246
|
+
setArmBuilds(true);
|
|
247
|
+
return;
|
|
248
|
+
}
|
|
249
|
+
setArmBuilds(false);
|
|
250
|
+
await face.installer.install(d.name, d.spec, ins.pendingBuilds);
|
|
251
|
+
};
|
|
252
|
+
const working = ins && ["checking", "starting", "installing", "cancelling", "applying", "enabling"].includes(ins.phase);
|
|
253
|
+
const isInstalled = d && (installed.has(d.name) || ins?.phase === "installed" || ins?.phase === "enabled");
|
|
254
|
+
const open = () => {
|
|
255
|
+
const how = face.openPlugins(installed.has(d?.name) ? d.name : void 0);
|
|
256
|
+
say(how === "bundle" ? "\u5DF2\u6253\u5F00\u5B98\u65B9\u63D2\u4EF6\u9875\u4E2D\u7684\u8FD9\u4E2A\u63D2\u4EF6" : how === "panel" ? "\u5DF2\u5207\u5230\u5B98\u65B9\u300C\u63D2\u4EF6\u300D\u9875 \xB7 \u70B9\u300C\u6DFB\u52A0\u63D2\u4EF6\u300D\u7C98\u8D34\u89C4\u683C" : "\u627E\u4E0D\u5230\u5B98\u65B9\u63D2\u4EF6\u9875\u5165\u53E3");
|
|
257
|
+
};
|
|
258
|
+
const it = inspectText(inspect && !inspect.pending ? inspect : null);
|
|
259
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
|
|
260
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "scx-scrim", onClick: onClose }),
|
|
261
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("aside", { className: "scx-sheet scx-root", "aria-label": "\u63D2\u4EF6\u8BE6\u60C5", "data-scout-drawer": "", children: [
|
|
262
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "scx-dh", children: [
|
|
263
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("h2", { children: d?.name ?? repoKey }),
|
|
264
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: "scx-x", "aria-label": "\u5173\u95ED", onClick: onClose, children: "\xD7" }),
|
|
265
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "scx-pkg", children: [
|
|
266
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("a", { href: `https://github.com/${d?.repo ?? repoKey}`, target: "_blank", rel: "noreferrer", children: [
|
|
267
|
+
"github.com/",
|
|
268
|
+
d?.repo ?? repoKey
|
|
269
|
+
] }),
|
|
270
|
+
" \xB7 ",
|
|
271
|
+
d?.license ?? "\u65E0\u8BB8\u53EF\u8BC1",
|
|
272
|
+
d ? ` \xB7 \u2605 ${d.stars} \xB7 \u63A8\u9001\u4E8E ${ago(d.pushedAt)}` : ""
|
|
273
|
+
] })
|
|
274
|
+
] }),
|
|
275
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "scx-dbody", children: [
|
|
276
|
+
err ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "scx-note", "data-tone": "bad", children: err }) : null,
|
|
277
|
+
!d && !err ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "scx-empty", children: "\u52A0\u8F7D\u4E2D\u2026" }) : null,
|
|
278
|
+
d ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
|
|
279
|
+
d.description ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { style: { color: "var(--scx-ink2)", margin: "12px 0 0" }, children: d.description }) : null,
|
|
280
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "scx-sec", children: [
|
|
281
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("h3", { children: [
|
|
282
|
+
"\u6838\u9A8C\u6E05\u5355 \xB7 \u53EF\u4FE1\u5EA6 ",
|
|
283
|
+
d.score
|
|
284
|
+
] }),
|
|
285
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Checklist, { checks: d.checks })
|
|
286
|
+
] }),
|
|
287
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "scx-sec", children: [
|
|
288
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("h3", { children: "\u8BC4\u5206\u6784\u6210" }),
|
|
289
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "scx-parts", children: [
|
|
290
|
+
d.scoreParts.map(([k, n]) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_react.default.Fragment, { children: [
|
|
291
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: k }),
|
|
292
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { children: [
|
|
293
|
+
n > 0 ? "+" : "",
|
|
294
|
+
n
|
|
295
|
+
] })
|
|
296
|
+
] }, k)),
|
|
297
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("b", { children: "\u53EF\u4FE1\u5EA6" }),
|
|
298
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("b", { children: d.score })
|
|
299
|
+
] })
|
|
300
|
+
] }),
|
|
301
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "scx-sec", children: [
|
|
302
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("h3", { children: "\u7528\u9014\u5206\u7C7B" }),
|
|
303
|
+
d.cats.length ? d.cats.map((c, i) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "scx-catev", children: [
|
|
304
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
|
|
305
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "scx-tag", "data-tone": i ? "cat2" : "cat", children: labels[c.id] ?? c.id }),
|
|
306
|
+
i ? null : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: { fontSize: 11, color: "var(--scx-ink3)" }, children: "\u4E3B\u7C7B" }),
|
|
307
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "scx-s", children: [
|
|
308
|
+
"\u5F97\u5206 ",
|
|
309
|
+
c.score
|
|
310
|
+
] })
|
|
311
|
+
] }),
|
|
312
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("ul", { children: c.evidence.map((e) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("li", { children: e }, e)) })
|
|
313
|
+
] }, c.id)) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: { color: "var(--scx-ink3)" }, children: "\u6CA1\u6709\u8DB3\u591F\u4FE1\u53F7\uFF0C\u5F52\u5165\u300C\u672A\u5F52\u7C7B\u300D\u3002" })
|
|
314
|
+
] }),
|
|
315
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "scx-sec", children: [
|
|
316
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("h3", { children: "\u6765\u6E90\u94FE" }),
|
|
317
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "scx-chain", children: [
|
|
318
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "scx-node", children: [
|
|
319
|
+
"github.com/",
|
|
320
|
+
d.repo
|
|
321
|
+
] }),
|
|
322
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: "\u2192" }),
|
|
323
|
+
d.npmVersion ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
|
|
324
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "scx-node", children: [
|
|
325
|
+
"npm ",
|
|
326
|
+
d.name,
|
|
327
|
+
"@",
|
|
328
|
+
d.npmVersion
|
|
329
|
+
] }),
|
|
330
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: "\u2192" }),
|
|
331
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "scx-node", style: { color: d.provenance ? "var(--scx-ok)" : "var(--scx-ink4)" }, children: d.provenance ? "provenance \u2713" : "\u65E0 provenance" })
|
|
332
|
+
] }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "scx-node", style: { color: "var(--scx-warn)" }, children: d.chain === "unknown" ? "npm \u672A\u67E5\u8BE2" : "\u672A\u53D1\u5E03 npm" })
|
|
333
|
+
] }),
|
|
334
|
+
d.copiesList.length ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "scx-ev", style: { marginTop: 6 }, children: [
|
|
335
|
+
"\u540C\u540D\u4ED3\u5E93 ",
|
|
336
|
+
d.copiesList.length,
|
|
337
|
+
" \u4E2A\uFF1A",
|
|
338
|
+
d.copiesList.slice(0, 5).map((c) => c.repo).join("\u3001"),
|
|
339
|
+
d.copiesList.length > 5 ? " \u2026" : ""
|
|
340
|
+
] }) : null
|
|
341
|
+
] }),
|
|
342
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "scx-sec", children: [
|
|
343
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("h3", { children: "\u4F5C\u7528\u9762" }),
|
|
344
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("dl", { className: "scx-kv", children: [
|
|
345
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("dt", { children: "\u5F62\u6001" }),
|
|
346
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("dd", { children: d.kinds.join(" + ") }),
|
|
347
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("dt", { children: "cordis \u63D2\u5165" }),
|
|
348
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("dd", { children: d.inserts.length ? d.inserts.map((n) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "scx-mono", children: n }, n)) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: { color: "var(--scx-ink3)" }, children: "\u4ED3\u5E93\u65E0 cordis.patch.yml \u6216\u672A\u58F0\u660E" }) }),
|
|
349
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("dt", { children: "client.inject" }),
|
|
350
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("dd", { children: d.clientInject.length ? d.clientInject.map((m) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "scx-mono", children: m }, m)) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: { color: "var(--scx-ink3)" }, children: "\u65E0" }) }),
|
|
351
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("dt", { children: "DSH peer" }),
|
|
352
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("dd", { className: "scx-mono", children: Object.entries(d.compatPeers).map(([k, v]) => `${k}@${v}`).join(", ") || "\u65E0" }),
|
|
353
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("dt", { children: "\u8FD0\u884C\u65F6\u4F9D\u8D56" }),
|
|
354
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("dd", { children: [
|
|
355
|
+
d.depCount,
|
|
356
|
+
" \u4E2A"
|
|
357
|
+
] })
|
|
358
|
+
] })
|
|
359
|
+
] }),
|
|
360
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "scx-sec", children: [
|
|
361
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("h3", { children: "\u5B98\u65B9\u9884\u68C0\uFF08pluginManager.inspect\uFF0C\u4E0D\u6267\u884C\u4EE3\u7801\uFF09" }),
|
|
362
|
+
!face.inspect ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: { color: "var(--scx-ink3)" }, children: "\u5F53\u524D\u7A97\u53E3\u62FF\u4E0D\u5230\u5B98\u65B9\u63D2\u4EF6\u7BA1\u7406\u670D\u52A1" }) : inspect?.pending ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: { color: "var(--scx-ink3)" }, children: "\u67E5\u8BE2\u4E2D\u2026" }) : it ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "scx-note", "data-tone": it.tone === "bad" ? "bad" : void 0, style: { color: it.tone === "ok" ? "var(--scx-ok)" : void 0 }, children: it.text }) : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", { type: "button", className: "scx-btn", onClick: runInspect, children: [
|
|
363
|
+
"\u9884\u68C0 ",
|
|
364
|
+
d.spec
|
|
365
|
+
] }),
|
|
366
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "scx-ev", style: { marginTop: 6 }, children: "\u300C\u5B89\u88C5\u300D\u8D70\u5B98\u65B9\u5B89\u88C5\u5668\uFF1Apnpm \u8FD0\u884C\u524D\u6309 peer \u89C4\u5219\u5224\u517C\u5BB9\uFF08\u4E0D\u517C\u5BB9\u5373\u62D2\u88C5\uFF09\uFF0C\u4F9D\u8D56\u5E26\u5B89\u88C5\u811A\u672C\u4F1A\u5148\u62E6\u4E0B\u7B49\u4F60\u786E\u8BA4\uFF0C\u5931\u8D25\u81EA\u52A8\u56DE\u6EDA\u3002\u88C5\u597D\u9ED8\u8BA4\u4E0D\u542F\u7528\uFF0C\u786E\u8BA4\u65E0\u8BEF\u518D\u70B9\u300C\u542F\u7528\u300D\u3002" })
|
|
367
|
+
] })
|
|
368
|
+
] }) : null
|
|
369
|
+
] }),
|
|
370
|
+
d && ins ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "scx-inst", "data-phase": ins.phase, "aria-live": "polite", children: [
|
|
371
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "scx-inst-t", children: ins.text }),
|
|
372
|
+
working && ins.log ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "scx-ev", children: ins.log }) : null,
|
|
373
|
+
ins.phase === "failed" && ins.pendingBuilds?.length ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { marginTop: 6 }, children: [
|
|
374
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "scx-ev", children: [
|
|
375
|
+
"\u8FD9\u4E9B\u4F9D\u8D56\u8981\u5728\u5B89\u88C5\u65F6\u4EE5\u4F60\u7684\u7528\u6237\u6743\u9650\u6267\u884C\u811A\u672C\uFF1A",
|
|
376
|
+
ins.pendingBuilds.join("\u3001"),
|
|
377
|
+
"\u3002\u5141\u8BB8\u540E\u5B98\u65B9\u4F1A\u6309\u5305\u540D\u8BB0\u5165\u5F53\u524D profile\u3002"
|
|
378
|
+
] }),
|
|
379
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: "scx-btn", "data-tone": "bad", style: { marginTop: 6 }, onClick: allowBuilds, children: armBuilds ? "\u518D\u70B9\u4E00\u6B21\uFF1A\u5141\u8BB8\u8FD9\u4E9B\u811A\u672C\u5E76\u91CD\u8BD5" : "\u5141\u8BB8\u8FD9\u4E9B\u811A\u672C\u5E76\u91CD\u8BD5" })
|
|
380
|
+
] }) : null,
|
|
381
|
+
ins.logPath && ins.phase === "failed" ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "scx-ev", children: [
|
|
382
|
+
"\u65E5\u5FD7\uFF1A",
|
|
383
|
+
ins.logPath
|
|
384
|
+
] }) : null
|
|
385
|
+
] }) : null,
|
|
386
|
+
d ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "scx-df", children: [
|
|
387
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "scx-spec", title: d.spec, children: d.spec }),
|
|
388
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: "scx-btn", "data-tone": d.risky ? "bad" : void 0, onClick: copy, children: d.risky ? armed ? "\u518D\u70B9\u4E00\u6B21\u786E\u8BA4\u590D\u5236" : "\u4E86\u89E3\u98CE\u9669\u540E\u590D\u5236" : "\u590D\u5236\u9501\u5B9A\u89C4\u683C" }),
|
|
389
|
+
face.installer && !isInstalled ? working ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: "scx-btn", onClick: () => face.installer.cancel(d.name), disabled: ins.phase === "applying" || ins.phase === "cancelling" || ins.phase === "enabling", children: "\u53D6\u6D88\u5B89\u88C5" }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: "scx-btn", "data-tone": needsConfirm ? "bad" : "pri", onClick: install, children: armInstall ? "\u518D\u70B9\u4E00\u6B21\u786E\u8BA4\u5B89\u88C5" : ins?.phase === "failed" ? "\u91CD\u8BD5\u5B89\u88C5" : "\u5B89\u88C5" }) : null,
|
|
390
|
+
face.installer && ins?.phase === "installed" && !ins.restartRequired ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: "scx-btn", "data-tone": "pri", onClick: () => face.installer.enable(d.name), children: "\u542F\u7528" }) : null,
|
|
391
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: "scx-btn", "data-tone": face.installer && !isInstalled ? void 0 : "pri", onClick: open, children: isInstalled ? "\u5728\u63D2\u4EF6\u9875\u6253\u5F00" : "\u53BB\u63D2\u4EF6\u9875\u6DFB\u52A0" })
|
|
392
|
+
] }) : null
|
|
393
|
+
] })
|
|
394
|
+
] });
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
// src/client/Page.jsx
|
|
398
|
+
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
399
|
+
var FLAGS = [
|
|
400
|
+
["nohook", "\u65E0\u5B89\u88C5\u811A\u672C"],
|
|
401
|
+
["chain", "\u6765\u6E90\u94FE\u543B\u5408"],
|
|
402
|
+
["prov", "\u6709 provenance"],
|
|
403
|
+
["norisk", "\u65E0 \u2715 \u9879"],
|
|
404
|
+
["active", "30 \u5929\u5185\u6D3B\u8DC3"]
|
|
405
|
+
];
|
|
406
|
+
var KINDS = [["client", "\u5E26\u754C\u9762"], ["hostonly", "\u4EC5\u5BBF\u4E3B"]];
|
|
407
|
+
var COMPAT = [["ok", "\u517C\u5BB9\u5F53\u524D\u7248\u672C"], ["incompatible", "\u4E0D\u517C\u5BB9"], ["none", "\u672A\u58F0\u660E"]];
|
|
408
|
+
var PAGE = 60;
|
|
409
|
+
function ScoutPage({ face }) {
|
|
410
|
+
const [status, setStatus] = (0, import_react2.useState)(null);
|
|
411
|
+
const [error, setError] = (0, import_react2.useState)(null);
|
|
412
|
+
const [q, setQ] = (0, import_react2.useState)("");
|
|
413
|
+
const [cat, setCat] = (0, import_react2.useState)("all");
|
|
414
|
+
const [sort, setSort] = (0, import_react2.useState)("score");
|
|
415
|
+
const [compat, setCompat] = (0, import_react2.useState)([]);
|
|
416
|
+
const [flags, setFlags] = (0, import_react2.useState)([]);
|
|
417
|
+
const [inst, setInst] = (0, import_react2.useState)("all");
|
|
418
|
+
const [limit, setLimit] = (0, import_react2.useState)(PAGE);
|
|
419
|
+
const [data, setData] = (0, import_react2.useState)(null);
|
|
420
|
+
const [sel, setSel] = (0, import_react2.useState)(null);
|
|
421
|
+
const [toast, setToast] = (0, import_react2.useState)(null);
|
|
422
|
+
const installed = face.useInstalled();
|
|
423
|
+
const seq = (0, import_react2.useRef)(0);
|
|
424
|
+
const say = (0, import_react2.useCallback)((text) => {
|
|
425
|
+
setToast(text);
|
|
426
|
+
clearTimeout(say.h);
|
|
427
|
+
say.h = setTimeout(() => setToast(null), 2200);
|
|
428
|
+
}, []);
|
|
429
|
+
const loadStatus = (0, import_react2.useCallback)(async () => {
|
|
430
|
+
try {
|
|
431
|
+
setStatus(await api.status());
|
|
432
|
+
setError(null);
|
|
433
|
+
} catch (e) {
|
|
434
|
+
setError(e.message);
|
|
435
|
+
}
|
|
436
|
+
}, []);
|
|
437
|
+
(0, import_react2.useEffect)(() => {
|
|
438
|
+
void loadStatus();
|
|
439
|
+
}, [loadStatus]);
|
|
440
|
+
(0, import_react2.useEffect)(() => {
|
|
441
|
+
const take = () => {
|
|
442
|
+
const p = face.takePending?.();
|
|
443
|
+
if (p) setSel(p);
|
|
444
|
+
};
|
|
445
|
+
take();
|
|
446
|
+
return face.onPending?.(take);
|
|
447
|
+
}, [face]);
|
|
448
|
+
const busy = !!status && (status.running || !status.loaded);
|
|
449
|
+
(0, import_react2.useEffect)(() => {
|
|
450
|
+
if (!busy) return void 0;
|
|
451
|
+
const h = setInterval(loadStatus, 1500);
|
|
452
|
+
return () => clearInterval(h);
|
|
453
|
+
}, [busy, loadStatus]);
|
|
454
|
+
const installedKey = installed.join("\n");
|
|
455
|
+
(0, import_react2.useEffect)(() => {
|
|
456
|
+
const id = ++seq.current;
|
|
457
|
+
const h = setTimeout(async () => {
|
|
458
|
+
try {
|
|
459
|
+
const v = await api.list({ q, cat, sort, compat, flags, inst, installed, limit });
|
|
460
|
+
if (id === seq.current) {
|
|
461
|
+
setData(v);
|
|
462
|
+
setError(null);
|
|
463
|
+
}
|
|
464
|
+
} catch (e) {
|
|
465
|
+
if (id === seq.current) setError(e.message);
|
|
466
|
+
}
|
|
467
|
+
}, q ? 180 : 0);
|
|
468
|
+
return () => clearTimeout(h);
|
|
469
|
+
}, [q, cat, sort, compat.join(), flags.join(), inst, installedKey, limit, status?.rev]);
|
|
470
|
+
const toggle = (list, set, v) => {
|
|
471
|
+
set(list.includes(v) ? list.filter((x) => x !== v) : [...list, v]);
|
|
472
|
+
setLimit(PAGE);
|
|
473
|
+
};
|
|
474
|
+
const labels = status?.categories ?? {};
|
|
475
|
+
const facets = status?.facets;
|
|
476
|
+
const instSet = (0, import_react2.useMemo)(() => new Set(installed), [installedKey]);
|
|
477
|
+
const refresh = async (full) => {
|
|
478
|
+
try {
|
|
479
|
+
setStatus(await api.refresh(full));
|
|
480
|
+
} catch (e) {
|
|
481
|
+
say(e.message);
|
|
482
|
+
void loadStatus();
|
|
483
|
+
}
|
|
484
|
+
};
|
|
485
|
+
const empty = status && status.loaded && !status.crawledAt && !status.running && !status.repos;
|
|
486
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "scx-root scx-page", "data-scout-page": "", children: [
|
|
487
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "scx-top", children: [
|
|
488
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("h1", { children: "\u53D1\u73B0\u63D2\u4EF6" }),
|
|
489
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "scx-sub", children: "\u6765\u6E90\uFF1AGitHub topic:dsh-plugin + npm \xB7 \u53EA\u6838\u9A8C\uFF0C\u4E0D\u5B89\u88C5" }),
|
|
490
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "scx-search", children: [
|
|
491
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { "aria-hidden": "true", children: "\u2315" }),
|
|
492
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("input", { "aria-label": "\u641C\u7D22\u63D2\u4EF6", placeholder: "\u540D\u79F0\u3001\u63CF\u8FF0\u3001\u4ED3\u5E93\u3001\u5206\u7C7B", value: q, onChange: (e) => {
|
|
493
|
+
setQ(e.target.value);
|
|
494
|
+
setLimit(PAGE);
|
|
495
|
+
} })
|
|
496
|
+
] }),
|
|
497
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "scx-fresh", children: [
|
|
498
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { title: status?.runtimeSource, children: [
|
|
499
|
+
"\u8FD0\u884C\u65F6 ",
|
|
500
|
+
status?.runtime ?? "\u2026"
|
|
501
|
+
] }),
|
|
502
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { children: status?.running ? progressText(status.progress) : status && !status.loaded ? "\u8BFB\u53D6\u7D22\u5F15\u2026" : `\u66F4\u65B0\u4E8E ${ago(status?.crawledAt)}` }),
|
|
503
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { type: "button", className: "scx-btn", disabled: !status || status.running, onClick: () => refresh(false), children: status?.running ? "\u5237\u65B0\u4E2D\u2026" : "\u5237\u65B0" })
|
|
504
|
+
] })
|
|
505
|
+
] }),
|
|
506
|
+
status?.running ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "scx-progress", style: { margin: "0 16px" }, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("i", { style: { width: `${Math.round(progressRatio(status.progress) * 100)}%` } }) }) : null,
|
|
507
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "scx-body", children: [
|
|
508
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "scx-filters", children: [
|
|
509
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "scx-fg", children: [
|
|
510
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("h4", { children: "\u517C\u5BB9\uFF08\u5B98\u65B9 peer \u89C4\u5219\uFF09" }),
|
|
511
|
+
COMPAT.map(([v, l]) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("label", { children: [
|
|
512
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("input", { type: "checkbox", checked: compat.includes(v), onChange: () => toggle(compat, setCompat, v) }),
|
|
513
|
+
l,
|
|
514
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "scx-n", children: facets?.compat[v] ?? "" })
|
|
515
|
+
] }, v))
|
|
516
|
+
] }),
|
|
517
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "scx-fg", children: [
|
|
518
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("h4", { children: "\u98CE\u9669\u4E0E\u6765\u6E90" }),
|
|
519
|
+
FLAGS.map(([v, l]) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("label", { children: [
|
|
520
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("input", { type: "checkbox", checked: flags.includes(v), onChange: () => toggle(flags, setFlags, v) }),
|
|
521
|
+
l,
|
|
522
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "scx-n", children: facets?.flags[v] ?? "" })
|
|
523
|
+
] }, v))
|
|
524
|
+
] }),
|
|
525
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "scx-fg", children: [
|
|
526
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("h4", { children: "\u5F62\u6001" }),
|
|
527
|
+
KINDS.map(([v, l]) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("label", { children: [
|
|
528
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("input", { type: "checkbox", checked: flags.includes(v), onChange: () => toggle(flags, setFlags, v) }),
|
|
529
|
+
l,
|
|
530
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "scx-n", children: facets?.flags[v] ?? "" })
|
|
531
|
+
] }, v))
|
|
532
|
+
] }),
|
|
533
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "scx-fg", children: [
|
|
534
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("h4", { children: "\u672C\u673A" }),
|
|
535
|
+
[["all", "\u5168\u90E8"], ["hide", "\u9690\u85CF\u5DF2\u5B89\u88C5"], ["only", "\u53EA\u770B\u5DF2\u5B89\u88C5"]].map(([v, l]) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("label", { children: [
|
|
536
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("input", { type: "radio", name: "scx-inst", checked: inst === v, onChange: () => {
|
|
537
|
+
setInst(v);
|
|
538
|
+
setLimit(PAGE);
|
|
539
|
+
} }),
|
|
540
|
+
l
|
|
541
|
+
] }, v))
|
|
542
|
+
] }),
|
|
543
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "scx-fg", children: [
|
|
544
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("h4", { children: "\u540C\u540D\u526F\u672C" }),
|
|
545
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("label", { children: [
|
|
546
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("input", { type: "checkbox", checked: flags.includes("copies"), onChange: () => toggle(flags, setFlags, "copies") }),
|
|
547
|
+
"\u663E\u793A\u526F\u672C",
|
|
548
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "scx-n", children: facets?.copies ?? "" })
|
|
549
|
+
] })
|
|
550
|
+
] }),
|
|
551
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "scx-fg", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { type: "button", className: "scx-btn", disabled: !status || status.running, onClick: () => refresh(true), children: "\u5168\u91CF\u91CD\u5EFA\u7D22\u5F15" }) })
|
|
552
|
+
] }),
|
|
553
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "scx-list", children: [
|
|
554
|
+
error ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "scx-note", "data-tone": "bad", children: error }) : null,
|
|
555
|
+
status?.error ? /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "scx-note", "data-tone": "bad", children: [
|
|
556
|
+
"\u4E0A\u6B21\u5237\u65B0\u5931\u8D25\uFF1A",
|
|
557
|
+
status.error.message
|
|
558
|
+
] }) : null,
|
|
559
|
+
empty ? /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "scx-note", children: [
|
|
560
|
+
"\u8FD8\u6CA1\u6709\u7D22\u5F15\u3002\u6293\u53D6\u8FB9\u8FDB\u884C\u8FB9\u51FA\u7ED3\u679C\u2014\u2014\u6700\u8FD1\u6D3B\u8DC3\u7684\u63D2\u4EF6\u51E0\u79D2\u5185\u5C31\u4F1A\u51FA\u73B0\uFF0C\u5176\u4F59\u5728\u540E\u53F0\u8865\u9F50\uFF0C\u671F\u95F4\u53EF\u6B63\u5E38\u4F7F\u7528 DSH\u3002",
|
|
561
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { type: "button", className: "scx-btn", style: { marginLeft: 8 }, onClick: () => refresh(true), children: "\u5F00\u59CB\u6293\u53D6" })
|
|
562
|
+
] }) : null,
|
|
563
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "scx-cats", role: "tablist", "aria-label": "\u7528\u9014\u5206\u7C7B", children: ["all", ...Object.keys(labels), "other"].filter((id) => id !== "other" || (data?.cats.other ?? 0) > 0 || cat === "other").map((id) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("button", { type: "button", role: "tab", className: "scx-cat", "aria-selected": cat === id, onClick: () => {
|
|
564
|
+
setCat(id);
|
|
565
|
+
setLimit(PAGE);
|
|
566
|
+
}, children: [
|
|
567
|
+
id === "all" ? "\u5168\u90E8" : id === "other" ? "\u672A\u5F52\u7C7B" : labels[id],
|
|
568
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "scx-n", children: data?.cats[id] ?? "" })
|
|
569
|
+
] }, id)) }),
|
|
570
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "scx-lhead", children: [
|
|
571
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { children: data ? `${data.total.toLocaleString()} \u4E2A\u63D2\u4EF6${status?.running ? "\uFF08\u6293\u53D6\u4E2D\uFF0C\u6301\u7EED\u589E\u52A0\uFF09" : ""}` : "\u52A0\u8F7D\u4E2D\u2026" }),
|
|
572
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: { marginLeft: "auto" }, children: "\u6392\u5E8F" }),
|
|
573
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("select", { "aria-label": "\u6392\u5E8F", value: sort, onChange: (e) => setSort(e.target.value), children: [
|
|
574
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("option", { value: "score", children: "\u53EF\u4FE1\u5EA6" }),
|
|
575
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("option", { value: "stars", children: "star" }),
|
|
576
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("option", { value: "recent", children: "\u6700\u8FD1\u63A8\u9001" }),
|
|
577
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("option", { value: "name", children: "\u540D\u79F0" })
|
|
578
|
+
] })
|
|
579
|
+
] }),
|
|
580
|
+
data && !data.rows.length && !empty ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "scx-empty", children: "\u6CA1\u6709\u7B26\u5408\u6761\u4EF6\u7684\u63D2\u4EF6\u3002\u653E\u5BBD\u5DE6\u4FA7\u7B5B\u9009\uFF0C\u6216\u6362\u4E2A\u5173\u952E\u8BCD\u3002" }) : null,
|
|
581
|
+
data?.rows.map((r) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("button", { type: "button", className: "scx-item", "aria-current": sel === r.repo, "data-repo": r.repo, onClick: () => setSel(r.repo), children: [
|
|
582
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "scx-score", "data-band": band(r.score), title: r.scoreParts.map(([k, n]) => `${k} ${n > 0 ? "+" : ""}${n}`).join("\n"), children: r.score }),
|
|
583
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: { minWidth: 0 }, children: [
|
|
584
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "scx-rt", children: [
|
|
585
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("b", { children: r.name }),
|
|
586
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "scx-ver", children: r.npmVersion ?? r.repoVersion ?? "" })
|
|
587
|
+
] }),
|
|
588
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "scx-rd", children: r.description || "\uFF08\u65E0\u63CF\u8FF0\uFF09" }),
|
|
589
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "scx-tags", children: rowTags(r, labels, status?.runtime, instSet.has(r.name)).map(([tone, text], i) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "scx-tag", "data-tone": tone, children: text }, i)) })
|
|
590
|
+
] }),
|
|
591
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "scx-meta", children: [
|
|
592
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { children: [
|
|
593
|
+
"\u2605 ",
|
|
594
|
+
r.stars.toLocaleString()
|
|
595
|
+
] }),
|
|
596
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { children: r.repo.split("/")[0] }),
|
|
597
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { children: ago(r.pushedAt) })
|
|
598
|
+
] })
|
|
599
|
+
] }, r.repo)),
|
|
600
|
+
data && data.rows.length < data.total ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "scx-more", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("button", { type: "button", className: "scx-btn", onClick: () => setLimit(limit + PAGE), children: [
|
|
601
|
+
"\u518D\u663E\u793A ",
|
|
602
|
+
Math.min(PAGE, data.total - data.rows.length),
|
|
603
|
+
" \u4E2A"
|
|
604
|
+
] }) }) : null
|
|
605
|
+
] })
|
|
606
|
+
] }),
|
|
607
|
+
sel ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Drawer, { repoKey: sel, face, labels, runtime: status?.runtime, installed: instSet, onClose: () => setSel(null), say }) : null,
|
|
608
|
+
toast ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "scx-toast", role: "status", children: toast }) : null
|
|
609
|
+
] });
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
// src/client/installer.js
|
|
613
|
+
var PHASE_TEXT = {
|
|
614
|
+
checking: "\u5B98\u65B9\u9884\u68C0\u4E2D\u2026",
|
|
615
|
+
starting: "\u6B63\u5728\u63D0\u4EA4\u7ED9\u5B98\u65B9\u5B89\u88C5\u5668\u2026",
|
|
616
|
+
installing: "\u5B98\u65B9\u5B89\u88C5\u5668\u8FD0\u884C pnpm \u4E2D\u2026",
|
|
617
|
+
cancelling: "\u6B63\u5728\u53D6\u6D88\u2026",
|
|
618
|
+
applying: "\u6B63\u5728\u5199\u5165 profile\uFF08\u6B64\u9636\u6BB5\u4E0D\u53EF\u53D6\u6D88\uFF09\u2026",
|
|
619
|
+
enabling: "\u6B63\u5728\u542F\u7528\u2026"
|
|
620
|
+
};
|
|
621
|
+
var CODE_TEXT = {
|
|
622
|
+
"management-required": "\u5F53\u524D profile \u4E0D\u53D7\u63D2\u4EF6\u7BA1\u7406\u5668\u7BA1\u7406",
|
|
623
|
+
unaddressable: "\u65E0\u6CD5\u5B9A\u4F4D\u8FD9\u4E2A\u63D2\u4EF6",
|
|
624
|
+
"unknown-plugin": "\u5B98\u65B9\u4E0D\u8BA4\u8BC6\u8FD9\u4E2A\u63D2\u4EF6",
|
|
625
|
+
"invalid-spec": "\u89C4\u683C\u65E0\u6548",
|
|
626
|
+
"ambiguous-install": "\u89C4\u683C\u5BF9\u5E94\u591A\u4E2A\u5305\uFF0C\u65E0\u6CD5\u786E\u5B9A\u88C5\u54EA\u4E2A",
|
|
627
|
+
"not-bundle": "\u8FD9\u4E2A\u5305\u6CA1\u6709\u58F0\u660E DSH \u7EC4\u5408\u5305\uFF0C\u5B98\u65B9\u4E0D\u63A5\u53D7",
|
|
628
|
+
"not-removable": "\u4E0D\u53EF\u79FB\u9664",
|
|
629
|
+
"stop-profile": "\u9700\u8981\u5148\u505C\u6B62\u5F53\u524D profile",
|
|
630
|
+
"bundle-in-use": "\u7EC4\u5408\u5305\u6B63\u5728\u4F7F\u7528\u4E2D",
|
|
631
|
+
"stale-approval": "\u811A\u672C\u6279\u51C6\u5DF2\u8FC7\u671F\uFF0C\u8BF7\u91CD\u65B0\u786E\u8BA4",
|
|
632
|
+
"incompatible-version": "\u4E0E\u5F53\u524D DSH \u7248\u672C\u4E0D\u517C\u5BB9",
|
|
633
|
+
"operation-error": "\u5B89\u88C5\u5668\u5185\u90E8\u9519\u8BEF"
|
|
634
|
+
};
|
|
635
|
+
var KIND_TEXT = {
|
|
636
|
+
network: "\u7F51\u7EDC\u4E0D\u53EF\u8FBE",
|
|
637
|
+
timeout: "\u5B89\u88C5\u8D85\u65F6",
|
|
638
|
+
integrity: "\u5305\u6821\u9A8C\u5931\u8D25",
|
|
639
|
+
"pnpm-missing": "\u627E\u4E0D\u5230 pnpm",
|
|
640
|
+
"not-found": "registry \u4E0A\u627E\u4E0D\u5230",
|
|
641
|
+
"no-matching-version": "\u6CA1\u6709\u5339\u914D\u7684\u7248\u672C",
|
|
642
|
+
"disk-full": "\u78C1\u76D8\u5DF2\u6EE1",
|
|
643
|
+
permission: "\u6CA1\u6709\u5199\u6743\u9650",
|
|
644
|
+
"build-blocked": "\u4F9D\u8D56\u5E26\u5B89\u88C5\u811A\u672C\uFF0C\u5DF2\u88AB\u62E6\u4E0B"
|
|
645
|
+
};
|
|
646
|
+
var PROBLEM_TEXT = {
|
|
647
|
+
"already-installed": "\u5DF2\u7ECF\u5B89\u88C5",
|
|
648
|
+
"not-found": "registry \u4E0A\u627E\u4E0D\u5230",
|
|
649
|
+
network: "\u7F51\u7EDC\u4E0D\u53EF\u8FBE",
|
|
650
|
+
"invalid-spec": "\u89C4\u683C\u65E0\u6548",
|
|
651
|
+
"not-a-package": "\u4E0D\u662F npm \u5305",
|
|
652
|
+
"not-a-bundle": "\u4E0D\u662F DSH \u7EC4\u5408\u5305",
|
|
653
|
+
unknown: "\u672A\u77E5\u539F\u56E0"
|
|
654
|
+
};
|
|
655
|
+
function failureText(v) {
|
|
656
|
+
const e = v?.error;
|
|
657
|
+
if (e?.code === "incompatible-version") {
|
|
658
|
+
const named = (e.incompatible ?? v.packageResult?.incompatible ?? []).map((p) => `${p.name}@${p.version} \u8981\u6C42 ${Object.entries(p.peers).map(([k, r]) => `${k} ${r}`).join("\u3001")}\uFF0C\u5F53\u524D\u8FD0\u884C\u65F6 ${p.runtimeVersion}`);
|
|
659
|
+
return `${CODE_TEXT["incompatible-version"]}${named.length ? `\uFF1A${named.join("\uFF1B")}` : ""}\u3002\u5982\u786E\u9700\u4F7F\u7528\uFF0C\u53EF\u5728\u5B98\u65B9\u63D2\u4EF6\u9875\u6309\u7248\u672C\u8C41\u514D\u3002`;
|
|
660
|
+
}
|
|
661
|
+
if (v?.pendingBuilds?.length) return `${KIND_TEXT["build-blocked"]}\uFF1A${v.pendingBuilds.join("\u3001")}`;
|
|
662
|
+
if (v?.failedAt === "spec-host") return "\u8FDE\u4E0D\u4E0A\u6E90\u7801\u4E3B\u673A\uFF08GitHub\uFF09\uFF0C\u6362\u7F51\u7EDC\u6216\u7A0D\u540E\u91CD\u8BD5";
|
|
663
|
+
const kind = v?.packageResult?.kind;
|
|
664
|
+
const head = e?.code && CODE_TEXT[e.code] || kind && KIND_TEXT[kind] || "\u5B89\u88C5\u5931\u8D25";
|
|
665
|
+
return e?.diagnostic ? `${head}\uFF1A${e.diagnostic.slice(0, 300)}` : head;
|
|
666
|
+
}
|
|
667
|
+
var unwrap = (res) => res && typeof res === "object" && "ok" in res ? res : { ok: true, value: res };
|
|
668
|
+
var uuid = () => globalThis.crypto?.randomUUID?.() ?? `scout-${Date.now()}-${Math.random().toString(36).slice(2)}`;
|
|
669
|
+
function createInstaller(getPm, { onInstalled } = {}) {
|
|
670
|
+
let states = {};
|
|
671
|
+
let active;
|
|
672
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
673
|
+
const set = (name, patch) => {
|
|
674
|
+
states = { ...states, [name]: patch === null ? void 0 : { ...states[name], ...patch } };
|
|
675
|
+
for (const l of listeners) l();
|
|
676
|
+
};
|
|
677
|
+
const byRequest = (id) => active && active.requestId === id ? active.name : void 0;
|
|
678
|
+
async function settle(name, requestId, res) {
|
|
679
|
+
let r = unwrap(res);
|
|
680
|
+
if (!r.ok) {
|
|
681
|
+
try {
|
|
682
|
+
r = unwrap(await getPm()?.pm.waitForInstall?.(requestId));
|
|
683
|
+
} catch {
|
|
684
|
+
r = { ok: false };
|
|
685
|
+
}
|
|
686
|
+
if (!r.ok || r.value == null) {
|
|
687
|
+
set(name, { phase: "unconfirmed", text: "\u6CA1\u62FF\u5230\u5B89\u88C5\u7ED3\u679C\uFF1B\u8BF7\u5230\u5B98\u65B9\u63D2\u4EF6\u9875\u786E\u8BA4\u662F\u5426\u5DF2\u88C5\u4E0A\uFF0C\u518D\u51B3\u5B9A\u662F\u5426\u91CD\u8BD5" });
|
|
688
|
+
return;
|
|
689
|
+
}
|
|
690
|
+
}
|
|
691
|
+
const v = r.value;
|
|
692
|
+
if (v.application === "cancelled") set(name, { phase: "cancelled", text: "\u5DF2\u53D6\u6D88\uFF1B\u5B98\u65B9\u5DF2\u6062\u590D package.json \u4E0E pnpm-lock.yaml" });
|
|
693
|
+
else if (v.application === "failed") set(name, { phase: "failed", text: failureText(v), code: v.error?.code, pendingBuilds: v.pendingBuilds ?? [], logPath: v.packageResult?.logPath });
|
|
694
|
+
else {
|
|
695
|
+
set(name, { phase: "installed", bundle: v.bundle ?? name, restartRequired: v.application === "restart-required", text: v.application === "restart-required" ? "\u5DF2\u5B89\u88C5\uFF08\u9700\u91CD\u542F DSH \u751F\u6548\uFF09" : "\u5DF2\u5B89\u88C5\uFF0C\u5C1A\u672A\u542F\u7528" });
|
|
696
|
+
onInstalled?.();
|
|
697
|
+
}
|
|
698
|
+
}
|
|
699
|
+
return {
|
|
700
|
+
get: () => states,
|
|
701
|
+
subscribe(l) {
|
|
702
|
+
listeners.add(l);
|
|
703
|
+
return () => listeners.delete(l);
|
|
704
|
+
},
|
|
705
|
+
busy: () => !!active,
|
|
706
|
+
/** 官方 install-state / install-log 事件:只认自己发起的 requestId。 */
|
|
707
|
+
onState(p) {
|
|
708
|
+
const n = byRequest(p?.requestId);
|
|
709
|
+
if (n && PHASE_TEXT[p.phase]) set(n, { phase: p.phase, text: PHASE_TEXT[p.phase] });
|
|
710
|
+
},
|
|
711
|
+
onLog(c) {
|
|
712
|
+
const n = byRequest(c?.requestId);
|
|
713
|
+
if (!n || typeof c.text !== "string") return;
|
|
714
|
+
const line = c.text.split("\n").map((s) => s.trim()).filter(Boolean).at(-1);
|
|
715
|
+
if (line) set(n, { log: line.slice(0, 200) });
|
|
716
|
+
},
|
|
717
|
+
/**
|
|
718
|
+
* 先官方 inspect,再 installBundle(enabled:false)。approvedBuilds 只在用户看过名单、明确点了允许后才传。
|
|
719
|
+
* @returns {Promise<void>}
|
|
720
|
+
*/
|
|
721
|
+
async install(name, spec, approvedBuilds) {
|
|
722
|
+
const got = getPm();
|
|
723
|
+
if (!got || typeof got.pm.installBundle !== "function") {
|
|
724
|
+
set(name, { phase: "failed", text: "\u5F53\u524D\u7A97\u53E3\u62FF\u4E0D\u5230\u5B98\u65B9\u63D2\u4EF6\u7BA1\u7406\u670D\u52A1" });
|
|
725
|
+
return;
|
|
726
|
+
}
|
|
727
|
+
if (active) {
|
|
728
|
+
set(name, { phase: "failed", text: `\u6B63\u5728\u5B89\u88C5 ${active.name}\uFF0C\u8BF7\u7B49\u5B83\u7ED3\u675F` });
|
|
729
|
+
return;
|
|
730
|
+
}
|
|
731
|
+
const requestId = uuid();
|
|
732
|
+
active = { name, requestId };
|
|
733
|
+
set(name, { phase: "checking", text: PHASE_TEXT.checking, log: void 0, pendingBuilds: [], code: void 0, logPath: void 0, requestId });
|
|
734
|
+
try {
|
|
735
|
+
const insp = unwrap(await got.pm.inspect(spec, {}));
|
|
736
|
+
if (!insp.ok) {
|
|
737
|
+
set(name, { phase: "failed", text: `\u5B98\u65B9\u9884\u68C0\u5931\u8D25\uFF1A${insp.error?.message ?? "unknown"}` });
|
|
738
|
+
return;
|
|
739
|
+
}
|
|
740
|
+
const iv = insp.value ?? {};
|
|
741
|
+
if (iv.status !== "accepted") {
|
|
742
|
+
if (iv.problem === "already-installed") {
|
|
743
|
+
set(name, { phase: "installed", text: "\u5DF2\u7ECF\u5B89\u88C5" });
|
|
744
|
+
onInstalled?.();
|
|
745
|
+
return;
|
|
746
|
+
}
|
|
747
|
+
set(name, { phase: "failed", text: `\u5B98\u65B9\u9884\u68C0\u62D2\u7EDD\uFF1A${PROBLEM_TEXT[iv.problem] ?? iv.problem}${iv.reason ? `\uFF08${iv.reason}\uFF09` : ""}` });
|
|
748
|
+
return;
|
|
749
|
+
}
|
|
750
|
+
if (iv.bundle === false) {
|
|
751
|
+
set(name, { phase: "failed", text: "\u80FD\u89E3\u6790\u5230\u5305\uFF0C\u4F46\u5B83\u4E0D\u662F DSH \u7EC4\u5408\u5305\uFF0C\u5B98\u65B9\u4E0D\u63A5\u53D7" });
|
|
752
|
+
return;
|
|
753
|
+
}
|
|
754
|
+
set(name, { phase: "starting", text: PHASE_TEXT.starting });
|
|
755
|
+
const res = await got.pm.installBundle(spec, { enabled: false, requestId, ...iv.registry ? { registry: iv.registry } : {}, ...approvedBuilds?.length ? { approvedBuilds: [...approvedBuilds] } : {} });
|
|
756
|
+
await settle(name, requestId, res);
|
|
757
|
+
} catch (e) {
|
|
758
|
+
set(name, { phase: "failed", text: e?.message ?? String(e) });
|
|
759
|
+
} finally {
|
|
760
|
+
if (active?.requestId === requestId) active = void 0;
|
|
761
|
+
}
|
|
762
|
+
},
|
|
763
|
+
async cancel(name) {
|
|
764
|
+
const s = states[name];
|
|
765
|
+
if (!active || active.name !== name || !s?.requestId) return;
|
|
766
|
+
try {
|
|
767
|
+
const r = unwrap(await getPm()?.pm.cancelInstall?.(s.requestId));
|
|
768
|
+
if (r.ok && r.value?.status === "too-late") set(name, { text: "\u5DF2\u8FDB\u5165\u5199\u5165\u9636\u6BB5\uFF0C\u4E0D\u80FD\u53D6\u6D88\uFF1B\u7B49\u5B83\u5B8C\u6210" });
|
|
769
|
+
} catch {
|
|
770
|
+
}
|
|
771
|
+
},
|
|
772
|
+
async enable(name) {
|
|
773
|
+
const got = getPm();
|
|
774
|
+
if (!got || typeof got.pm.setBundleEnabled !== "function") {
|
|
775
|
+
set(name, { phase: "failed", text: "\u5F53\u524D\u7A97\u53E3\u62FF\u4E0D\u5230\u5B98\u65B9\u63D2\u4EF6\u7BA1\u7406\u670D\u52A1" });
|
|
776
|
+
return;
|
|
777
|
+
}
|
|
778
|
+
set(name, { phase: "enabling", text: PHASE_TEXT.enabling });
|
|
779
|
+
try {
|
|
780
|
+
const r = unwrap(await got.pm.setBundleEnabled(states[name]?.bundle ?? name, true));
|
|
781
|
+
if (!r.ok) {
|
|
782
|
+
set(name, { phase: "installed", text: `\u542F\u7528\u5931\u8D25\uFF1A${r.error?.message ?? "unknown"}` });
|
|
783
|
+
return;
|
|
784
|
+
}
|
|
785
|
+
const v = r.value;
|
|
786
|
+
if (v.application === "failed") set(name, { phase: "installed", text: `\u542F\u7528\u5931\u8D25\uFF1A${failureText(v)}\uFF1B\u63D2\u4EF6\u4ECD\u5DF2\u5B89\u88C5\uFF0C\u53EF\u5728\u5B98\u65B9\u63D2\u4EF6\u9875\u5904\u7406\u6216\u5378\u8F7D` });
|
|
787
|
+
else set(name, { phase: "enabled", text: v.application === "restart-required" ? "\u5DF2\u542F\u7528\uFF0C\u91CD\u542F DSH \u540E\u751F\u6548" : "\u5DF2\u542F\u7528" });
|
|
788
|
+
onInstalled?.();
|
|
789
|
+
} catch (e) {
|
|
790
|
+
set(name, { phase: "installed", text: `\u542F\u7528\u5931\u8D25\uFF1A${e?.message ?? e}` });
|
|
791
|
+
}
|
|
792
|
+
},
|
|
793
|
+
reset(name) {
|
|
794
|
+
if (!active || active.name !== name) set(name, null);
|
|
795
|
+
}
|
|
796
|
+
};
|
|
797
|
+
}
|
|
798
|
+
|
|
799
|
+
// src/client/style.css
|
|
800
|
+
var style_default = '/* DSH Plugin Scout. \u53EA\u7528\u5BBF\u4E3B --dsw-alias-* token\uFF1B\u6D6E\u5C42\u8868\u9762\u7528\u5B57\u9762\u8272\uFF08\u5BBF\u4E3B bg \u522B\u540D\u53EF\u80FD\u662F\u73BB\u7483\u534A\u900F\u660E\uFF09\u3002\n \u7C7B\u540D\u7EDF\u4E00 scx- \u524D\u7F00\uFF0C\u5E76\u907F\u5F00\u5BBF\u4E3B\u76AE\u80A4\u7684\u5B50\u4E32\u89C4\u5219\uFF1Acard bubble block pill banner button panel \u7B49\u3002 */\n.scx-root {\n --scx-ink1: var(--dsw-alias-label-primary, #16181d);\n --scx-ink2: var(--dsw-alias-label-secondary, #4a4f59);\n --scx-ink3: var(--dsw-alias-label-tertiary, #7a808b);\n --scx-ink4: var(--dsw-alias-label-caption, #a3a8b1);\n --scx-hair: var(--dsw-alias-border-l2, #e6e8ec);\n --scx-hair2: var(--dsw-alias-border-l3, #d6d9df);\n --scx-hover: var(--dsw-alias-interactive-bg-hover, rgba(15, 17, 21, 0.05));\n --scx-ok: var(--dsw-alias-state-success-primary, #1f9d55);\n --scx-warn: var(--dsw-alias-state-warn-primary, #c27c0e);\n --scx-bad: var(--dsw-alias-state-error-primary, #d14343);\n --scx-info: #3563e9;\n --scx-cat: #6a3fc8;\n --scx-tile: rgba(255, 255, 255, 0.72);\n --scx-sheet: #ffffff;\n --scx-shadow: 0 1px 2px rgba(15, 17, 21, 0.06);\n --scx-mono: var(--dsw-font-mono, ui-monospace, Menlo, monospace);\n font: 13px/1.5 var(--dsw-font-family, -apple-system, BlinkMacSystemFont, "PingFang SC", sans-serif);\n color: var(--scx-ink1);\n}\nbody[data-ds-dark-theme] .scx-root {\n --scx-info: #7d9bff;\n --scx-cat: #b79cff;\n --scx-tile: rgba(255, 255, 255, 0.05);\n --scx-sheet: #111318;\n --scx-shadow: 0 1px 2px rgba(0, 0, 0, 0.4);\n}\n.scx-root *, .scx-root *::before, .scx-root *::after { box-sizing: border-box; }\n\n/* \u2500\u2500 page \u2500\u2500 */\n.scx-page { height: 100%; display: flex; flex-direction: column; min-height: 0; overflow: hidden; }\n.scx-top { display: flex; align-items: center; gap: 12px; margin: 12px 16px 0; padding: 0 0 12px; border-bottom: 1px solid var(--scx-hair); flex-wrap: wrap; }\n.scx-top h1 { margin: 0; font-size: 15px; font-weight: 600; }\n.scx-sub { color: var(--scx-ink3); font-size: 12px; }\n.scx-search { flex: 1; min-width: 200px; max-width: 420px; display: flex; align-items: center; gap: 6px; border: 1px solid var(--scx-hair2); border-radius: 8px; padding: 4px 10px; background: var(--scx-tile); }\n.scx-search input { border: 0; outline: 0; flex: 1; font: inherit; background: transparent; color: inherit; min-width: 0; }\n.scx-fresh { margin-left: auto; display: flex; align-items: center; gap: 10px; color: var(--scx-ink3); font-size: 12px; }\n.scx-btn { border: 1px solid var(--scx-hair2); background: var(--scx-tile); border-radius: 7px; padding: 4px 11px; font: inherit; color: var(--scx-ink1); cursor: pointer; white-space: nowrap; }\n.scx-btn:hover:not(:disabled) { background: var(--scx-hover); }\n.scx-btn:disabled { opacity: 0.5; cursor: default; }\n.scx-btn[data-tone="bad"] { color: var(--scx-bad); border-color: var(--scx-bad); }\n.scx-btn[data-tone="pri"] { background: var(--scx-ink1); border-color: var(--scx-ink1); color: var(--scx-sheet); }\n.scx-body { flex: 1; display: grid; grid-template-columns: 220px 1fr; min-height: 0; }\n.scx-filters { border-right: 1px solid var(--scx-hair); padding: 14px 16px; overflow: auto; }\n.scx-fg { margin-bottom: 16px; }\n.scx-fg h4 { margin: 0 0 6px; font-size: 11px; font-weight: 600; color: var(--scx-ink3); letter-spacing: 0.04em; }\n.scx-fg label { display: flex; align-items: center; gap: 7px; padding: 2px 0; color: var(--scx-ink2); cursor: pointer; }\n.scx-fg label .scx-n { margin-left: auto; color: var(--scx-ink4); font-variant-numeric: tabular-nums; }\n.scx-list { overflow: auto; padding: 10px 16px 32px; min-width: 0; }\n.scx-cats { display: flex; gap: 6px; flex-wrap: wrap; padding: 2px 0 10px; border-bottom: 1px solid var(--scx-hair); margin-bottom: 8px; }\n.scx-cat { border: 1px solid var(--scx-hair2); background: var(--scx-tile); border-radius: 999px; padding: 3px 11px; font: inherit; font-size: 12.5px; color: var(--scx-ink2); cursor: pointer; display: inline-flex; gap: 6px; align-items: center; }\n.scx-cat:hover { background: var(--scx-hover); }\n.scx-cat .scx-n { color: var(--scx-ink4); font-variant-numeric: tabular-nums; font-size: 11.5px; }\n.scx-cat[aria-selected="true"] { background: var(--scx-ink1); border-color: var(--scx-ink1); color: var(--scx-sheet); }\n.scx-cat[aria-selected="true"] .scx-n { color: inherit; opacity: 0.7; }\n.scx-lhead { display: flex; align-items: center; gap: 10px; padding: 2px 0 8px; color: var(--scx-ink3); font-size: 12px; }\n.scx-lhead select { font: inherit; border: 1px solid var(--scx-hair2); border-radius: 6px; padding: 1px 6px; background: var(--scx-tile); color: inherit; }\n.scx-item { display: grid; grid-template-columns: 34px 1fr auto; gap: 12px; align-items: start; padding: 11px 14px; border: 1px solid var(--scx-hair); border-radius: 10px; background: var(--scx-tile); margin-bottom: 8px; cursor: pointer; box-shadow: var(--scx-shadow); width: 100%; text-align: left; font: inherit; color: inherit; }\n.scx-item:hover { border-color: var(--scx-hair2); }\n.scx-item[aria-current="true"] { border-color: var(--scx-ink1); }\n.scx-score { width: 34px; height: 34px; border-radius: 8px; display: grid; place-items: center; font-weight: 600; font-size: 12px; font-variant-numeric: tabular-nums; }\n.scx-score[data-band="hi"] { background: color-mix(in srgb, var(--scx-ok) 14%, transparent); color: var(--scx-ok); }\n.scx-score[data-band="mid"] { background: color-mix(in srgb, var(--scx-warn) 14%, transparent); color: var(--scx-warn); }\n.scx-score[data-band="lo"] { background: var(--scx-hover); color: var(--scx-ink3); }\n.scx-rt { display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap; }\n.scx-rt b { font-size: 13.5px; font-weight: 600; }\n.scx-ver { color: var(--scx-ink3); font-size: 12px; font-family: var(--scx-mono); }\n.scx-rd { color: var(--scx-ink2); margin: 2px 0 6px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n.scx-tags { display: flex; gap: 5px; flex-wrap: wrap; }\n.scx-tag { font-size: 11px; padding: 1px 7px; border-radius: 5px; white-space: nowrap; background: var(--scx-hover); color: var(--scx-ink3); }\n.scx-tag[data-tone="ok"] { background: color-mix(in srgb, var(--scx-ok) 12%, transparent); color: var(--scx-ok); }\n.scx-tag[data-tone="warn"] { background: color-mix(in srgb, var(--scx-warn) 12%, transparent); color: var(--scx-warn); }\n.scx-tag[data-tone="bad"] { background: color-mix(in srgb, var(--scx-bad) 12%, transparent); color: var(--scx-bad); }\n.scx-tag[data-tone="info"] { background: color-mix(in srgb, var(--scx-info) 12%, transparent); color: var(--scx-info); }\n.scx-tag[data-tone="cat"] { background: color-mix(in srgb, var(--scx-cat) 12%, transparent); color: var(--scx-cat); }\n.scx-tag[data-tone="cat2"] { background: transparent; color: var(--scx-cat); border: 1px solid color-mix(in srgb, var(--scx-cat) 30%, transparent); }\n.scx-meta { text-align: right; color: var(--scx-ink3); font-size: 12px; white-space: nowrap; }\n.scx-empty { padding: 56px 0; text-align: center; color: var(--scx-ink3); }\n.scx-more { display: flex; justify-content: center; padding: 8px 0; }\n.scx-note { border: 1px solid var(--scx-hair); border-radius: 9px; padding: 10px 12px; margin-bottom: 10px; color: var(--scx-ink2); font-size: 12px; }\n.scx-note[data-tone="bad"] { border-color: var(--scx-bad); color: var(--scx-bad); }\n.scx-progress { height: 3px; background: var(--scx-hair); border-radius: 2px; overflow: hidden; margin-top: 6px; }\n.scx-progress > i { display: block; height: 100%; background: var(--scx-info); transition: width 0.3s; }\n\n/* \u2500\u2500 drawer (non-modal side sheet; no role=dialog, the host skin repaints those) \u2500\u2500 */\n.scx-scrim { position: fixed; inset: 0; background: rgba(15, 17, 21, 0.18); z-index: 40; }\n.scx-sheet { position: fixed; top: 0; right: 0; bottom: 0; width: min(540px, 92vw); background: var(--scx-sheet); border-left: 1px solid var(--scx-hair); box-shadow: -12px 0 32px rgba(15, 17, 21, 0.12); display: flex; flex-direction: column; z-index: 41; }\n.scx-dh { padding: 16px 20px 12px; border-bottom: 1px solid var(--scx-hair); display: grid; grid-template-columns: 1fr auto; gap: 2px 12px; }\n.scx-dh h2 { margin: 0; font-size: 16px; font-weight: 600; word-break: break-all; }\n.scx-x { border: 0; background: none; font-size: 18px; color: var(--scx-ink3); cursor: pointer; grid-row: span 2; align-self: start; }\n.scx-pkg { font-family: var(--scx-mono); font-size: 12px; color: var(--scx-ink3); }\n.scx-pkg a { color: inherit; }\n.scx-dbody { flex: 1; overflow: auto; padding: 4px 20px 20px; }\n.scx-sec { margin-top: 16px; }\n.scx-sec h3 { font-size: 12px; font-weight: 600; color: var(--scx-ink3); margin: 0 0 8px; letter-spacing: 0.03em; }\n.scx-chk { border: 1px solid var(--scx-hair); border-radius: 9px; overflow: hidden; }\n.scx-ci { display: grid; grid-template-columns: 20px 1fr; gap: 8px; padding: 8px 12px; border-top: 1px solid var(--scx-hair); }\n.scx-ci:first-child { border-top: 0; }\n.scx-ic { font-weight: 700; text-align: center; }\n.scx-ic[data-st="ok"] { color: var(--scx-ok); }\n.scx-ic[data-st="warn"] { color: var(--scx-warn); }\n.scx-ic[data-st="bad"] { color: var(--scx-bad); }\n.scx-ic[data-st="unk"] { color: var(--scx-ink4); }\n.scx-id { font-family: var(--scx-mono); color: var(--scx-ink4); font-size: 11px; margin-right: 4px; }\n.scx-ev { color: var(--scx-ink3); font-size: 12px; font-family: var(--scx-mono); word-break: break-all; margin-top: 1px; }\n.scx-catev { border: 1px solid var(--scx-hair); border-radius: 9px; padding: 8px 12px; margin-bottom: 6px; }\n.scx-catev > div { display: flex; gap: 8px; align-items: center; }\n.scx-catev .scx-s { margin-left: auto; font-size: 11px; color: var(--scx-ink4); }\n.scx-catev ul { margin: 4px 0 0; padding-left: 16px; color: var(--scx-ink3); font-size: 12px; font-family: var(--scx-mono); }\n.scx-chain { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; font-size: 12px; }\n.scx-node { border: 1px solid var(--scx-hair2); border-radius: 6px; padding: 2px 8px; font-family: var(--scx-mono); }\n.scx-kv { display: grid; grid-template-columns: 130px 1fr; gap: 4px 10px; font-size: 12px; margin: 0; }\n.scx-kv dt { color: var(--scx-ink3); }\n.scx-kv dd { margin: 0; word-break: break-all; }\n.scx-mono { font-family: var(--scx-mono); font-size: 12px; }\n.scx-parts { display: grid; grid-template-columns: 1fr auto; gap: 2px 12px; font-size: 12px; color: var(--scx-ink2); max-width: 280px; }\n.scx-parts b { border-top: 1px solid var(--scx-hair); padding-top: 3px; }\n.scx-df { border-top: 1px solid var(--scx-hair); padding: 12px 20px; display: flex; gap: 8px; align-items: center; }\n.scx-spec { flex: 1; min-width: 0; font-family: var(--scx-mono); font-size: 12px; border: 1px solid var(--scx-hair2); border-radius: 7px; padding: 5px 9px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n.scx-inst { border-top: 1px solid var(--scx-hair); padding: 10px 20px; font-size: 12px; color: var(--scx-ink2); }\n.scx-inst[data-phase="failed"], .scx-inst[data-phase="unconfirmed"] { color: var(--scx-bad); }\n.scx-inst[data-phase="installed"], .scx-inst[data-phase="enabled"] { color: var(--scx-ok); }\n.scx-inst-t { font-weight: 500; }\n.scx-toast { position: fixed; bottom: 24px; left: 50%; transform: translateX(-50%); background: var(--scx-ink1); color: var(--scx-sheet); padding: 7px 14px; border-radius: 8px; font-size: 12px; z-index: 60; }\n\n/* \u2500\u2500 official plugin page contributions \u2500\u2500 */\n.scx-badge { font-size: 11px; padding: 1px 7px; border-radius: 5px; white-space: nowrap; margin-left: 6px; }\n.scx-badge[data-band="hi"] { background: color-mix(in srgb, var(--scx-ok) 14%, transparent); color: var(--scx-ok); }\n.scx-badge[data-band="mid"] { background: color-mix(in srgb, var(--scx-warn) 14%, transparent); color: var(--scx-warn); }\n.scx-badge[data-band="lo"], .scx-badge[data-risky="true"] { background: color-mix(in srgb, var(--scx-bad) 12%, transparent); color: var(--scx-bad); }\n.scx-section { margin-top: 16px; border: 1px solid var(--scx-hair); border-radius: 10px; padding: 12px 14px; }\n.scx-section h4 { margin: 0 0 8px; font-size: 13px; font-weight: 600; display: flex; gap: 8px; align-items: center; }\n.scx-section .scx-link { margin-left: auto; font-size: 12px; font-weight: 400; color: var(--scx-info); background: none; border: 0; cursor: pointer; padding: 0; }\n';
|
|
801
|
+
|
|
802
|
+
// src/client/index.jsx
|
|
803
|
+
var import_jsx_runtime3 = require("react/jsx-runtime");
|
|
804
|
+
var PKG = "dsh-plugin-scout";
|
|
805
|
+
var PANEL_ID = "dsh-plugin-scout";
|
|
806
|
+
var NS = "dshPluginScout";
|
|
807
|
+
var OFFICIAL_PANEL = "plugins";
|
|
808
|
+
var inject = ["slots", "locale"];
|
|
809
|
+
var zh = { panel: "\u53D1\u73B0\u63D2\u4EF6", badge: "\u53EF\u4FE1\u5EA6", section: "\u63D2\u4EF6\u53D1\u73B0 \xB7 \u6838\u9A8C" };
|
|
810
|
+
var en = { panel: "Discover", badge: "Trust", section: "Plugin Scout \xB7 checks" };
|
|
811
|
+
function optional(ctx, name) {
|
|
812
|
+
try {
|
|
813
|
+
return ctx.get?.(name);
|
|
814
|
+
} catch {
|
|
815
|
+
return void 0;
|
|
816
|
+
}
|
|
817
|
+
}
|
|
818
|
+
function pluginManagerOf(ctx) {
|
|
819
|
+
try {
|
|
820
|
+
const pm = optional(ctx, "remote.pluginManager");
|
|
821
|
+
if (!pm || typeof pm.listBundles !== "function") return void 0;
|
|
822
|
+
return { pm, remote: optional(ctx, "remote") };
|
|
823
|
+
} catch {
|
|
824
|
+
return void 0;
|
|
825
|
+
}
|
|
826
|
+
}
|
|
827
|
+
function createInstalledStore(ctx) {
|
|
828
|
+
let names = [];
|
|
829
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
830
|
+
const emit = () => {
|
|
831
|
+
for (const l of listeners) l();
|
|
832
|
+
};
|
|
833
|
+
const load = async () => {
|
|
834
|
+
const got = pluginManagerOf(ctx);
|
|
835
|
+
if (!got) return;
|
|
836
|
+
try {
|
|
837
|
+
const res = await got.pm.listBundles();
|
|
838
|
+
const list = res?.ok ? res.value : Array.isArray(res) ? res : [];
|
|
839
|
+
const next = list.filter((b) => b.installed).map((b) => b.name).sort();
|
|
840
|
+
if (next.join("\n") !== names.join("\n")) {
|
|
841
|
+
names = next;
|
|
842
|
+
emit();
|
|
843
|
+
}
|
|
844
|
+
} catch {
|
|
845
|
+
}
|
|
846
|
+
};
|
|
847
|
+
return {
|
|
848
|
+
load,
|
|
849
|
+
get: () => names,
|
|
850
|
+
subscribe: (l) => {
|
|
851
|
+
listeners.add(l);
|
|
852
|
+
if (listeners.size === 1) void load();
|
|
853
|
+
return () => listeners.delete(l);
|
|
854
|
+
},
|
|
855
|
+
watch(extra = {}) {
|
|
856
|
+
const got = pluginManagerOf(ctx);
|
|
857
|
+
const offs = [];
|
|
858
|
+
try {
|
|
859
|
+
const off = got?.remote?.$on?.("plugin-manager/changed", load);
|
|
860
|
+
if (typeof off === "function") offs.push(off);
|
|
861
|
+
} catch {
|
|
862
|
+
}
|
|
863
|
+
for (const [event, fn] of Object.entries(extra)) {
|
|
864
|
+
try {
|
|
865
|
+
const off = got?.remote?.$on?.(event, fn);
|
|
866
|
+
if (typeof off === "function") offs.push(off);
|
|
867
|
+
} catch {
|
|
868
|
+
}
|
|
869
|
+
}
|
|
870
|
+
try {
|
|
871
|
+
const off = ctx.on?.("connection/reset", load);
|
|
872
|
+
if (typeof off === "function") offs.push(off);
|
|
873
|
+
} catch {
|
|
874
|
+
}
|
|
875
|
+
return () => offs.forEach((o) => o());
|
|
876
|
+
}
|
|
877
|
+
};
|
|
878
|
+
}
|
|
879
|
+
function installStyle() {
|
|
880
|
+
if (typeof document === "undefined") return () => {
|
|
881
|
+
};
|
|
882
|
+
const tag = document.createElement("style");
|
|
883
|
+
tag.dataset.plugin = PKG;
|
|
884
|
+
tag.dataset.pluginCss = `${PKG}/style`;
|
|
885
|
+
tag.dataset.scout = PKG;
|
|
886
|
+
tag.textContent = style_default;
|
|
887
|
+
document.head.appendChild(tag);
|
|
888
|
+
return () => tag.remove();
|
|
889
|
+
}
|
|
890
|
+
function PanelIcon({ size = 20 }) {
|
|
891
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("svg", { width: size, height: size, viewBox: "0 0 24 24", fill: "none", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("g", { stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round", children: [
|
|
892
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("circle", { cx: "10.5", cy: "10.5", r: "6" }),
|
|
893
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("path", { d: "m15 15 5 5" }),
|
|
894
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("path", { d: "m8 10.5 1.8 1.8L13.2 9" })
|
|
895
|
+
] }) });
|
|
896
|
+
}
|
|
897
|
+
var subjectName = (subject) => subject?.kind === "bundle" || subject?.kind === "row" ? subject.pkg?.name : void 0;
|
|
898
|
+
function apply(ctx) {
|
|
899
|
+
ctx.effect(() => ctx.locale.register(NS, { zh, en }), "scout: dictionaries");
|
|
900
|
+
const t = ctx.locale.bind(NS);
|
|
901
|
+
ctx.effect(installStyle, "scout: stylesheet");
|
|
902
|
+
const installed = createInstalledStore(ctx);
|
|
903
|
+
const installer = createInstaller(() => pluginManagerOf(ctx), { onInstalled: () => void installed.load() });
|
|
904
|
+
ctx.effect(() => {
|
|
905
|
+
try {
|
|
906
|
+
return installed.watch({ "plugin-manager/install-state": (p) => installer.onState(p), "plugin-manager/install-log": (c) => installer.onLog(c) });
|
|
907
|
+
} catch {
|
|
908
|
+
return void 0;
|
|
909
|
+
}
|
|
910
|
+
}, "scout: installed watch");
|
|
911
|
+
const badges = createBadgeCache();
|
|
912
|
+
let pending;
|
|
913
|
+
const face = {
|
|
914
|
+
useInstalled: () => (0, import_react3.useSyncExternalStore)(installed.subscribe, installed.get, installed.get),
|
|
915
|
+
/** 一键安装:拿不到官方 installBundle 时为 undefined,界面退回「复制 + 去插件页」。 */
|
|
916
|
+
installer: (() => {
|
|
917
|
+
const got = pluginManagerOf(ctx);
|
|
918
|
+
return got && typeof got.pm.installBundle === "function" ? installer : void 0;
|
|
919
|
+
})(),
|
|
920
|
+
useInstall: (name) => (0, import_react3.useSyncExternalStore)(installer.subscribe, () => installer.get()[name], () => void 0),
|
|
921
|
+
inspect: pluginManagerOf(ctx) ? async (spec) => {
|
|
922
|
+
try {
|
|
923
|
+
const got = pluginManagerOf(ctx);
|
|
924
|
+
if (!got) return { error: "\u5B98\u65B9\u63D2\u4EF6\u7BA1\u7406\u670D\u52A1\u6682\u4E0D\u53EF\u7528" };
|
|
925
|
+
const res = await got.pm.inspect(spec, {});
|
|
926
|
+
if (res?.ok === false) return { error: res.error?.message ?? "failed" };
|
|
927
|
+
return res?.ok ? res.value : res;
|
|
928
|
+
} catch (e) {
|
|
929
|
+
return { error: e.message };
|
|
930
|
+
}
|
|
931
|
+
} : void 0,
|
|
932
|
+
/** 已安装 → 直达官方详情;否则切到官方插件页。返回实际走了哪条路。 */
|
|
933
|
+
openPlugins(name) {
|
|
934
|
+
const nav = optional(ctx, "pluginNavigation");
|
|
935
|
+
if (name && typeof nav?.openBundle === "function") {
|
|
936
|
+
try {
|
|
937
|
+
nav.openBundle(name);
|
|
938
|
+
return "bundle";
|
|
939
|
+
} catch {
|
|
940
|
+
}
|
|
941
|
+
}
|
|
942
|
+
const layout = optional(ctx, "layout");
|
|
943
|
+
if (typeof layout?.selectPanel === "function") {
|
|
944
|
+
try {
|
|
945
|
+
layout.selectPanel(OFFICIAL_PANEL);
|
|
946
|
+
return "panel";
|
|
947
|
+
} catch {
|
|
948
|
+
}
|
|
949
|
+
}
|
|
950
|
+
return "none";
|
|
951
|
+
},
|
|
952
|
+
takePending() {
|
|
953
|
+
const p = pending;
|
|
954
|
+
pending = void 0;
|
|
955
|
+
return p;
|
|
956
|
+
},
|
|
957
|
+
onPending(fn) {
|
|
958
|
+
pendingListeners.add(fn);
|
|
959
|
+
return () => pendingListeners.delete(fn);
|
|
960
|
+
}
|
|
961
|
+
};
|
|
962
|
+
const pendingListeners = /* @__PURE__ */ new Set();
|
|
963
|
+
const focus = (repo) => {
|
|
964
|
+
pending = repo;
|
|
965
|
+
for (const l of pendingListeners) l();
|
|
966
|
+
};
|
|
967
|
+
function Page() {
|
|
968
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(ScoutPage, { face });
|
|
969
|
+
}
|
|
970
|
+
ctx.effect(() => ctx.slots.inject("main", () => ctx.slots.register({ name: "main", key: PANEL_ID, locale: NS }, Page)), "scout: page");
|
|
971
|
+
ctx.effect(() => ctx.slots.inject("sidebar.panellist", () => ctx.slots.register({
|
|
972
|
+
name: "sidebar.panellist",
|
|
973
|
+
id: PANEL_ID,
|
|
974
|
+
order: 40,
|
|
975
|
+
label: () => t("panel"),
|
|
976
|
+
locale: NS
|
|
977
|
+
}, PanelIcon)), "scout: sidebar entry");
|
|
978
|
+
const useBadge = (name) => {
|
|
979
|
+
(0, import_react3.useSyncExternalStore)(badges.subscribe, () => name ? badges.get(name) : null, () => null);
|
|
980
|
+
return name ? badges.get(name) : null;
|
|
981
|
+
};
|
|
982
|
+
function Badge({ subject }) {
|
|
983
|
+
const b = useBadge(subject?.kind === "bundle" ? subjectName(subject) : void 0);
|
|
984
|
+
if (!b) return null;
|
|
985
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("span", { className: "scx-root scx-badge", "data-band": band(b.score), "data-risky": b.risky, title: `dsh-plugin-scout \xB7 ${b.repo}`, children: [
|
|
986
|
+
t("badge"),
|
|
987
|
+
" ",
|
|
988
|
+
b.score,
|
|
989
|
+
b.risky ? " \xB7 \u2715" : ""
|
|
990
|
+
] });
|
|
991
|
+
}
|
|
992
|
+
function Section({ subject }) {
|
|
993
|
+
const name = subject?.kind === "bundle" ? subjectName(subject) : void 0;
|
|
994
|
+
const b = useBadge(name);
|
|
995
|
+
if (!b) return null;
|
|
996
|
+
const goto = () => {
|
|
997
|
+
focus(b.repo);
|
|
998
|
+
const layout = optional(ctx, "layout");
|
|
999
|
+
try {
|
|
1000
|
+
layout?.selectPanel?.(PANEL_ID);
|
|
1001
|
+
} catch {
|
|
1002
|
+
}
|
|
1003
|
+
};
|
|
1004
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("section", { className: "scx-root scx-section", "data-scout-section": "", children: [
|
|
1005
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("h4", { children: [
|
|
1006
|
+
t("section"),
|
|
1007
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "scx-badge", "data-band": band(b.score), "data-risky": b.risky, children: b.score }),
|
|
1008
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { type: "button", className: "scx-link", onClick: goto, children: "\u5728\u53D1\u73B0\u9875\u67E5\u770B \u2192" })
|
|
1009
|
+
] }),
|
|
1010
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "scx-ev", style: { marginBottom: 8 }, children: [
|
|
1011
|
+
"\u6309\u5305\u540D\u5339\u914D\u7D22\u5F15\u4E2D\u7684\u4EE3\u8868\u4ED3\u5E93 github.com/",
|
|
1012
|
+
b.repo,
|
|
1013
|
+
b.npmVersion ? ` \xB7 npm \u6700\u65B0 ${b.npmVersion}` : ""
|
|
1014
|
+
] }),
|
|
1015
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Checklist, { checks: b.checks })
|
|
1016
|
+
] });
|
|
1017
|
+
}
|
|
1018
|
+
ctx.effect(() => ctx.slots.inject("plugins.detail.badge", () => ctx.slots.register({ name: "plugins.detail.badge", id: `${PKG}/badge`, order: 50, locale: NS }, Badge)), "scout: official badge");
|
|
1019
|
+
ctx.effect(() => ctx.slots.inject("plugins.detail.section", () => ctx.slots.register({ name: "plugins.detail.section", id: `${PKG}/section`, order: 50, locale: NS }, Section)), "scout: official section");
|
|
1020
|
+
}
|
|
1021
|
+
|
|
1022
|
+
return module.exports;
|
|
1023
|
+
}
|
|
1024
|
+
});
|