@deepseek-ai/dsh-client-ui-plugin-manager 0.1.6-alpha.2
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.i18n.yaml +6 -0
- package/README.md +129 -0
- package/README.zh.md +129 -0
- package/lib/client.js +2550 -0
- package/lib/index.js +6 -0
- package/lib/types/client/PluginManagerPage.d.ts +18 -0
- package/lib/types/client/PluginsPanelIcon.d.ts +10 -0
- package/lib/types/client/config-ledger.d.ts +38 -0
- package/lib/types/client/index.d.ts +35 -0
- package/lib/types/client/locales.d.ts +320 -0
- package/lib/types/client/manager-store.d.ts +330 -0
- package/lib/types/client/presentation.d.ts +42 -0
- package/lib/types/client/slot-contract.d.ts +56 -0
- package/lib/types/index.d.ts +4 -0
- package/package.json +71 -0
package/lib/client.js
ADDED
|
@@ -0,0 +1,2550 @@
|
|
|
1
|
+
window.__ModuleLoader__.load({
|
|
2
|
+
id: "@deepseek-ai/dsh-client-ui-plugin-manager",
|
|
3
|
+
factory: (require) => {
|
|
4
|
+
var module = { exports: {} };
|
|
5
|
+
var exports = module.exports;
|
|
6
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
7
|
+
let react_jsx_runtime = require("react/jsx-runtime");
|
|
8
|
+
let react = require("react");
|
|
9
|
+
let _deepseek_ai_dsh_client_ui_primitives = require("@deepseek-ai/dsh-client-ui-primitives");
|
|
10
|
+
let _deepseek_ai_dsh_client_ui_slots = require("@deepseek-ai/dsh-client-ui-slots");
|
|
11
|
+
let _deepseek_ai_dsh_client_store = require("@deepseek-ai/dsh-client-store");
|
|
12
|
+
//#region lib/types/client/config-ledger.js
|
|
13
|
+
/**
|
|
14
|
+
* Which plugins bring their own configuration to the Plugins page, read from
|
|
15
|
+
* the three slots the page declares: the official plugins listed beside the
|
|
16
|
+
* official bundles, the bundles with a form on their page, and the rows with a
|
|
17
|
+
* page of their own. The projection follows the slot ledgers and the active
|
|
18
|
+
* locale and keeps its snapshot until one of them moves.
|
|
19
|
+
*/
|
|
20
|
+
/**
|
|
21
|
+
* The key a row's configuration registers under.
|
|
22
|
+
* @param bundle - the bundle's package name.
|
|
23
|
+
* @param rowId - the row id the bundle's patch declares.
|
|
24
|
+
* @returns the `plugins.row.config` key.
|
|
25
|
+
*/
|
|
26
|
+
function rowConfigKey(bundle, rowId) {
|
|
27
|
+
return `${bundle}#${rowId}`;
|
|
28
|
+
}
|
|
29
|
+
const SLOTS = [
|
|
30
|
+
"plugins.item",
|
|
31
|
+
"plugins.bundle.config",
|
|
32
|
+
"plugins.row.config"
|
|
33
|
+
];
|
|
34
|
+
/**
|
|
35
|
+
* Project the configuration ledgers as one observable the page binds.
|
|
36
|
+
* @param ctx - the page plugin's context, whose slot registry and locale the projection follows.
|
|
37
|
+
* @returns the ledger source; its snapshot changes only when a ledger or the locale does.
|
|
38
|
+
*/
|
|
39
|
+
function configLedgerSource(ctx) {
|
|
40
|
+
let versions = [];
|
|
41
|
+
let revision = -1;
|
|
42
|
+
let ledger = {
|
|
43
|
+
items: [],
|
|
44
|
+
bundles: /* @__PURE__ */ new Set(),
|
|
45
|
+
rows: /* @__PURE__ */ new Set()
|
|
46
|
+
};
|
|
47
|
+
const keysOf = (name) => new Set(ctx.slots.entries(name).flatMap((entry) => entry.options.key === void 0 ? [] : [entry.options.key]));
|
|
48
|
+
return {
|
|
49
|
+
getSnapshot: () => {
|
|
50
|
+
const next = SLOTS.map((name) => ctx.slots.getVersion(name));
|
|
51
|
+
const current = ctx.locale.getSnapshot().revision;
|
|
52
|
+
if (current !== revision || next.some((version, index) => version !== versions[index])) {
|
|
53
|
+
versions = next;
|
|
54
|
+
revision = current;
|
|
55
|
+
ledger = {
|
|
56
|
+
items: ctx.slots.entries("plugins.item").map((entry) => ({
|
|
57
|
+
/* v8 ignore next -- list-slot registration requires id */
|
|
58
|
+
id: entry.options.id ?? "",
|
|
59
|
+
label: (0, _deepseek_ai_dsh_client_ui_slots.resolveSlotLabel)(entry.options.label) ?? ""
|
|
60
|
+
})),
|
|
61
|
+
bundles: keysOf("plugins.bundle.config"),
|
|
62
|
+
rows: keysOf("plugins.row.config")
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
return ledger;
|
|
66
|
+
},
|
|
67
|
+
subscribe: (listener) => {
|
|
68
|
+
const offs = [...SLOTS.map((name) => ctx.slots.subscribe(name, listener)), ctx.locale.subscribe(listener)];
|
|
69
|
+
return () => {
|
|
70
|
+
for (const off of offs) off();
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
//#endregion
|
|
76
|
+
//#region ../../util/crypto/src/index.ts
|
|
77
|
+
/**
|
|
78
|
+
* Random v4 UUID, minted from `crypto.getRandomValues`.
|
|
79
|
+
* @returns the UUID string.
|
|
80
|
+
*/
|
|
81
|
+
function randomUUID() {
|
|
82
|
+
const bytes = globalThis.crypto.getRandomValues(new Uint8Array(16));
|
|
83
|
+
const hex = Array.from(bytes, (byte, index) => {
|
|
84
|
+
return (index === 6 ? byte & 15 | 64 : index === 8 ? byte & 63 | 128 : byte).toString(16).padStart(2, "0");
|
|
85
|
+
}).join("");
|
|
86
|
+
return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
|
|
87
|
+
}
|
|
88
|
+
//#endregion
|
|
89
|
+
//#region lib/types/client/presentation.js
|
|
90
|
+
/** Display labels and toast sentences for global plugin management. */
|
|
91
|
+
/** The official packages with copy of their own, and whether each is a beta feature the page tags as such. */
|
|
92
|
+
const BUILTIN_COPY = new Map([
|
|
93
|
+
["@deepseek-ai/dsh-experimental-agent-team-profile", {
|
|
94
|
+
title: "builtinAgentTeamTitle",
|
|
95
|
+
description: "builtinAgentTeamDescription",
|
|
96
|
+
beta: true
|
|
97
|
+
}],
|
|
98
|
+
["@deepseek-ai/dsh-experimental-agent-team-web-profile", {
|
|
99
|
+
title: "builtinAgentTeamWebTitle",
|
|
100
|
+
description: "builtinAgentTeamWebDescription",
|
|
101
|
+
beta: true
|
|
102
|
+
}],
|
|
103
|
+
["@deepseek-ai/dsh-experimental-auto-review", {
|
|
104
|
+
title: "builtinAutoReviewTitle",
|
|
105
|
+
description: "builtinAutoReviewDescription",
|
|
106
|
+
beta: true
|
|
107
|
+
}]
|
|
108
|
+
]);
|
|
109
|
+
/** The sentence each of the Host's refusal codes reads as. */
|
|
110
|
+
const CODE_KEYS = {
|
|
111
|
+
"management-required": "reasonManagementRequired",
|
|
112
|
+
"unaddressable": "reasonUnaddressable",
|
|
113
|
+
"unknown-plugin": "reasonUnknownPlugin",
|
|
114
|
+
"invalid-spec": "reasonInvalidSpec",
|
|
115
|
+
"ambiguous-install": "reasonAmbiguousInstall",
|
|
116
|
+
"not-bundle": "reasonNotBundle",
|
|
117
|
+
"not-removable": "reasonNotRemovable",
|
|
118
|
+
"stop-profile": "reasonStopProfile",
|
|
119
|
+
"bundle-in-use": "reasonBundleInUse",
|
|
120
|
+
"stale-approval": "reasonStaleApproval",
|
|
121
|
+
"operation-error": "reasonOperationError"
|
|
122
|
+
};
|
|
123
|
+
/** The sentence a failed action opens with, by what was being done. */
|
|
124
|
+
const FAILED_KEYS = {
|
|
125
|
+
enable: "failedEnable",
|
|
126
|
+
disable: "failedDisable",
|
|
127
|
+
uninstall: "failedUninstall",
|
|
128
|
+
rowEnable: "failedRowEnable",
|
|
129
|
+
rowDisable: "failedRowDisable"
|
|
130
|
+
};
|
|
131
|
+
/**
|
|
132
|
+
* What a management error reads as: the code's sentence, or, for an
|
|
133
|
+
* operation error, the Host's diagnostic as it is.
|
|
134
|
+
* @param error - the Host's code and its diagnostic, when it has one.
|
|
135
|
+
* @param t - the manager's translate seat.
|
|
136
|
+
* @returns the sentence.
|
|
137
|
+
*/
|
|
138
|
+
function managementText(error, t) {
|
|
139
|
+
if (error.code !== "operation-error") return t(CODE_KEYS[error.code]);
|
|
140
|
+
return error.diagnostic === void 0 || error.diagnostic === "" ? t("reasonOperationError") : error.diagnostic;
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Compact a package name to what a person calls it.
|
|
144
|
+
* @param name - the package name.
|
|
145
|
+
* @returns the unscoped name without the harness prefixes.
|
|
146
|
+
*/
|
|
147
|
+
function shortName(name) {
|
|
148
|
+
return (name.startsWith("@") ? name.slice(name.indexOf("/") + 1) : name).replace(/^dsh-(?:host-|client-)?/, "");
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Localize known official packages by exact npm name at render time.
|
|
152
|
+
* @param pkg - original package identity and optional metadata description.
|
|
153
|
+
* @param t - the manager's current translate function.
|
|
154
|
+
* @returns localized copy and whether the package is a beta feature, or the package's short name and original description.
|
|
155
|
+
*/
|
|
156
|
+
function packageText(pkg, t) {
|
|
157
|
+
const keys = BUILTIN_COPY.get(pkg.name);
|
|
158
|
+
return keys === void 0 ? {
|
|
159
|
+
title: shortName(pkg.name),
|
|
160
|
+
description: pkg.description,
|
|
161
|
+
beta: false
|
|
162
|
+
} : {
|
|
163
|
+
title: t(keys.title),
|
|
164
|
+
description: t(keys.description),
|
|
165
|
+
beta: keys.beta
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* The sentence one notice shows.
|
|
170
|
+
* @param notice - the last action's outcome.
|
|
171
|
+
* @param t - the manager's translate seat.
|
|
172
|
+
* @returns the sentence.
|
|
173
|
+
*/
|
|
174
|
+
function noticeText(notice, t) {
|
|
175
|
+
switch (notice.kind) {
|
|
176
|
+
case "restart": return t("restartNotice");
|
|
177
|
+
case "overridden": return t("overriddenNotice", { name: notice.packageName });
|
|
178
|
+
case "cancelled": return t("installCancelled");
|
|
179
|
+
case "failed": {
|
|
180
|
+
const reason = notice.code === void 0 ? notice.reason : managementText({
|
|
181
|
+
code: notice.code,
|
|
182
|
+
diagnostic: notice.reason
|
|
183
|
+
}, t);
|
|
184
|
+
return t(FAILED_KEYS[notice.action], { reason: reason === "" ? t("reasonOperationError") : reason });
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
//#endregion
|
|
189
|
+
//#region lib/types/client/manager-store.js
|
|
190
|
+
/**
|
|
191
|
+
* The plugin manager's state: the Host's bundles joined with its plugin
|
|
192
|
+
* entries, the action in flight, the install run, and the confirmation an
|
|
193
|
+
* uninstall waits on. Every fact comes from the Host — the store re-reads
|
|
194
|
+
* after each action and after every `plugin-manager/changed` event, so a
|
|
195
|
+
* change made on another surface shows here without a manual refresh.
|
|
196
|
+
*/
|
|
197
|
+
/**
|
|
198
|
+
* Whether an installation is still owned by the Host.
|
|
199
|
+
* @param phase - the dialog's current installation phase.
|
|
200
|
+
* @returns true until an authoritative result settles the installation.
|
|
201
|
+
*/
|
|
202
|
+
function isInstallPending(phase) {
|
|
203
|
+
return phase === "starting" || phase === "running" || phase === "cancelling" || phase === "applying";
|
|
204
|
+
}
|
|
205
|
+
/** A refused answer or a change the Host could not apply, carrying what it said and, for a refusal, its code. */
|
|
206
|
+
var RemoteAnswerError = class extends Error {
|
|
207
|
+
reason;
|
|
208
|
+
code;
|
|
209
|
+
constructor(reason, code) {
|
|
210
|
+
super(reason);
|
|
211
|
+
this.reason = reason;
|
|
212
|
+
this.code = code;
|
|
213
|
+
this.name = "RemoteAnswerError";
|
|
214
|
+
}
|
|
215
|
+
};
|
|
216
|
+
/** The dialog's reading of a failed change: the Host's code and diagnostic, and the run's classified failure. */
|
|
217
|
+
function failureOf(error, kind, pendingBuilds) {
|
|
218
|
+
return {
|
|
219
|
+
reason: error?.diagnostic ?? "",
|
|
220
|
+
...error === void 0 ? {} : { code: error.code },
|
|
221
|
+
...kind === void 0 ? {} : { kind },
|
|
222
|
+
...pendingBuilds === void 0 || pendingBuilds.length === 0 ? {} : { pendingBuilds }
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
/** The notice a thrown failure becomes: a refusal keeps its code, anything else its words. */
|
|
226
|
+
function failedNotice(error, subject, seq) {
|
|
227
|
+
const code = error instanceof RemoteAnswerError ? error.code : void 0;
|
|
228
|
+
return {
|
|
229
|
+
kind: "failed",
|
|
230
|
+
reason: reasonOf(error),
|
|
231
|
+
...code === void 0 ? {} : { code },
|
|
232
|
+
...subject,
|
|
233
|
+
seq
|
|
234
|
+
};
|
|
235
|
+
}
|
|
236
|
+
/** The runs with every one still open settled at `exitCode`. */
|
|
237
|
+
function settledRuns(runs, exitCode) {
|
|
238
|
+
return runs.map((run) => run.exitCode === void 0 ? {
|
|
239
|
+
...run,
|
|
240
|
+
exitCode
|
|
241
|
+
} : run);
|
|
242
|
+
}
|
|
243
|
+
/**
|
|
244
|
+
* The key one row occupies in the busy list.
|
|
245
|
+
* @param entryId - the row's Loader entry id.
|
|
246
|
+
* @returns the busy key.
|
|
247
|
+
*/
|
|
248
|
+
function rowKey(entryId) {
|
|
249
|
+
return `row:${entryId}`;
|
|
250
|
+
}
|
|
251
|
+
/**
|
|
252
|
+
* One bundle as the page shows it: its rows joined with the Host's entries.
|
|
253
|
+
* @param bundle - the Host's bundle.
|
|
254
|
+
* @param plugins - the Host's plugin entries.
|
|
255
|
+
* @returns the package view.
|
|
256
|
+
*/
|
|
257
|
+
function packageView(bundle, plugins) {
|
|
258
|
+
const rows = bundle.rows.map((row) => {
|
|
259
|
+
const live = row.entryId === void 0 ? void 0 : plugins.find((plugin) => plugin.entryId === row.entryId);
|
|
260
|
+
return {
|
|
261
|
+
rowId: row.rowId,
|
|
262
|
+
moduleName: row.moduleName,
|
|
263
|
+
enabled: live?.enabled ?? false,
|
|
264
|
+
phase: live?.fiberPhase ?? null,
|
|
265
|
+
...row.entryId === void 0 ? {} : { entryId: row.entryId },
|
|
266
|
+
...live?.readOnlyReason === void 0 ? {} : { readOnlyReason: live.readOnlyReason }
|
|
267
|
+
};
|
|
268
|
+
});
|
|
269
|
+
return {
|
|
270
|
+
name: bundle.name,
|
|
271
|
+
installed: bundle.installed,
|
|
272
|
+
optional: bundle.optional,
|
|
273
|
+
enabled: bundle.enabled,
|
|
274
|
+
rows,
|
|
275
|
+
...bundle.version === void 0 ? {} : { version: bundle.version },
|
|
276
|
+
...bundle.description === void 0 ? {} : { description: bundle.description },
|
|
277
|
+
...bundle.readOnlyReason === void 0 ? {} : { readOnlyReason: bundle.readOnlyReason },
|
|
278
|
+
...bundle.error === void 0 ? {} : { error: bundle.error }
|
|
279
|
+
};
|
|
280
|
+
}
|
|
281
|
+
/**
|
|
282
|
+
* The order the list shows packages in: by the short name a person reads, so a
|
|
283
|
+
* card stays put when its bundle is switched, whatever order the Host answers in.
|
|
284
|
+
* @param packages - the Host's bundles as views.
|
|
285
|
+
* @returns the views sorted by short name.
|
|
286
|
+
*/
|
|
287
|
+
function sortPackages(packages) {
|
|
288
|
+
return [...packages].sort((a, b) => shortName(a.name).localeCompare(shortName(b.name)));
|
|
289
|
+
}
|
|
290
|
+
const IDLE_INSTALL = {
|
|
291
|
+
open: false,
|
|
292
|
+
spec: "",
|
|
293
|
+
phase: "idle",
|
|
294
|
+
inputError: null,
|
|
295
|
+
subject: null,
|
|
296
|
+
runs: [],
|
|
297
|
+
detailsOpen: false,
|
|
298
|
+
installed: null,
|
|
299
|
+
restartRequired: false,
|
|
300
|
+
failure: null,
|
|
301
|
+
approvedBuilds: [],
|
|
302
|
+
enabling: false
|
|
303
|
+
};
|
|
304
|
+
/** Reads and mutates the profile's plugins through the `pluginManager` Remote. */
|
|
305
|
+
var PluginManagerController = class {
|
|
306
|
+
ctx;
|
|
307
|
+
store;
|
|
308
|
+
inFlight;
|
|
309
|
+
rerun = false;
|
|
310
|
+
generation = 0;
|
|
311
|
+
disposed = false;
|
|
312
|
+
pendingConfirm;
|
|
313
|
+
/** Cancels the check the dialog has in flight. */
|
|
314
|
+
inspectAbort;
|
|
315
|
+
noticeSeq = 0;
|
|
316
|
+
/**
|
|
317
|
+
* @param ctx - the tab plugin's context, whose `remote.pluginManager` and `remote.pluginInventory` namespaces answer.
|
|
318
|
+
*/
|
|
319
|
+
constructor(ctx) {
|
|
320
|
+
this.ctx = ctx;
|
|
321
|
+
this.store = (0, _deepseek_ai_dsh_client_store.createSnapshotStore)({
|
|
322
|
+
status: "idle",
|
|
323
|
+
packages: [],
|
|
324
|
+
busy: [],
|
|
325
|
+
notice: null,
|
|
326
|
+
install: IDLE_INSTALL,
|
|
327
|
+
confirm: null,
|
|
328
|
+
highlight: null
|
|
329
|
+
});
|
|
330
|
+
}
|
|
331
|
+
/**
|
|
332
|
+
* Read the tab's state.
|
|
333
|
+
* @returns the current sync snapshot (stable reference until the next change).
|
|
334
|
+
*/
|
|
335
|
+
getSnapshot() {
|
|
336
|
+
return this.store.getSnapshot();
|
|
337
|
+
}
|
|
338
|
+
/** Stop publishing and drop every late settlement. */
|
|
339
|
+
dispose() {
|
|
340
|
+
this.disposed = true;
|
|
341
|
+
this.generation += 1;
|
|
342
|
+
}
|
|
343
|
+
/**
|
|
344
|
+
* Build the face the tab's slot registration injects.
|
|
345
|
+
* @param configLedger - the projection of the plugins carrying configuration, bound beside the tab's own state.
|
|
346
|
+
* @returns the tab's snapshot sources and its actions.
|
|
347
|
+
*/
|
|
348
|
+
inject(configLedger) {
|
|
349
|
+
return {
|
|
350
|
+
hooks: {
|
|
351
|
+
pluginManager: this.store,
|
|
352
|
+
configLedger
|
|
353
|
+
},
|
|
354
|
+
ensure: () => {
|
|
355
|
+
if (this.getSnapshot().status === "idle") this.load();
|
|
356
|
+
},
|
|
357
|
+
refresh: () => {
|
|
358
|
+
this.load();
|
|
359
|
+
},
|
|
360
|
+
openInstall: () => {
|
|
361
|
+
if (!isInstallPending(this.getSnapshot().install.phase)) this.patch({ install: {
|
|
362
|
+
...IDLE_INSTALL,
|
|
363
|
+
open: true
|
|
364
|
+
} });
|
|
365
|
+
},
|
|
366
|
+
closeInstall: () => {
|
|
367
|
+
if (isInstallPending(this.getSnapshot().install.phase)) return;
|
|
368
|
+
this.abortInspect();
|
|
369
|
+
this.patch({ install: IDLE_INSTALL });
|
|
370
|
+
},
|
|
371
|
+
editInstallSpec: (text) => {
|
|
372
|
+
const install = this.getSnapshot().install;
|
|
373
|
+
if (install.phase === "checking" || isInstallPending(install.phase)) return;
|
|
374
|
+
this.patchInstall(install.phase === "idle" ? {
|
|
375
|
+
spec: text,
|
|
376
|
+
inputError: null
|
|
377
|
+
} : {
|
|
378
|
+
...IDLE_INSTALL,
|
|
379
|
+
open: true,
|
|
380
|
+
spec: text
|
|
381
|
+
});
|
|
382
|
+
},
|
|
383
|
+
runInstall: () => {
|
|
384
|
+
this.runInstall();
|
|
385
|
+
},
|
|
386
|
+
approveBuildsAndRetry: () => {
|
|
387
|
+
this.approveBuildsAndRetry();
|
|
388
|
+
},
|
|
389
|
+
cancelInstall: () => {
|
|
390
|
+
this.cancelInstall();
|
|
391
|
+
},
|
|
392
|
+
cancelInstallAndClose: () => {
|
|
393
|
+
this.cancelInstall(true);
|
|
394
|
+
},
|
|
395
|
+
toggleInstallDetails: () => {
|
|
396
|
+
this.patchInstall({ detailsOpen: !this.getSnapshot().install.detailsOpen });
|
|
397
|
+
},
|
|
398
|
+
enableInstalled: () => {
|
|
399
|
+
this.enableInstalled();
|
|
400
|
+
},
|
|
401
|
+
clearHighlight: () => {
|
|
402
|
+
if (this.getSnapshot().highlight !== null) this.patch({ highlight: null });
|
|
403
|
+
},
|
|
404
|
+
setEnabled: (packageName, enabled) => {
|
|
405
|
+
this.run(packageName, {
|
|
406
|
+
packageName,
|
|
407
|
+
action: enabled ? "enable" : "disable"
|
|
408
|
+
}, async () => {
|
|
409
|
+
this.applied(await this.ctx.remote.pluginManager.setBundleEnabled(packageName, enabled), packageName);
|
|
410
|
+
});
|
|
411
|
+
},
|
|
412
|
+
uninstall: (packageName) => {
|
|
413
|
+
this.pendingConfirm = () => this.run(packageName, {
|
|
414
|
+
packageName,
|
|
415
|
+
action: "uninstall"
|
|
416
|
+
}, async () => {
|
|
417
|
+
this.applied(await this.ctx.remote.pluginManager.removeBundle(packageName), packageName);
|
|
418
|
+
});
|
|
419
|
+
this.patch({ confirm: {
|
|
420
|
+
action: "uninstall",
|
|
421
|
+
packageName
|
|
422
|
+
} });
|
|
423
|
+
},
|
|
424
|
+
confirm: () => {
|
|
425
|
+
this.confirm();
|
|
426
|
+
},
|
|
427
|
+
cancelConfirm: () => {
|
|
428
|
+
this.pendingConfirm = void 0;
|
|
429
|
+
this.patch({ confirm: null });
|
|
430
|
+
},
|
|
431
|
+
setRowEnabled: (entryId, enabled) => {
|
|
432
|
+
this.run(rowKey(entryId), {
|
|
433
|
+
packageName: entryId,
|
|
434
|
+
action: enabled ? "rowEnable" : "rowDisable"
|
|
435
|
+
}, async () => {
|
|
436
|
+
this.applied(await this.ctx.remote.pluginManager.setPluginEnabled(entryId, enabled), entryId);
|
|
437
|
+
});
|
|
438
|
+
},
|
|
439
|
+
dismissNotice: () => {
|
|
440
|
+
this.patch({ notice: null });
|
|
441
|
+
}
|
|
442
|
+
};
|
|
443
|
+
}
|
|
444
|
+
/**
|
|
445
|
+
* Follow the Host's cancellation window for this dialog's installation.
|
|
446
|
+
* @param progress - a request id and phase received from the Host.
|
|
447
|
+
*/
|
|
448
|
+
installProgress(progress) {
|
|
449
|
+
const install = this.getSnapshot().install;
|
|
450
|
+
if (install.requestId !== progress.requestId || !isInstallPending(install.phase)) return;
|
|
451
|
+
if (install.phase === "cancelling" && progress.phase === "installing") return;
|
|
452
|
+
this.patchInstall({ phase: progress.phase === "installing" ? "running" : progress.phase });
|
|
453
|
+
}
|
|
454
|
+
/**
|
|
455
|
+
* Fold a chunk belonging to this installation into its pnpm command.
|
|
456
|
+
* A final chunk may arrive after the install answer and still updates an existing run.
|
|
457
|
+
* @param chunk - the chunk the Host forwarded.
|
|
458
|
+
*/
|
|
459
|
+
appendLog(chunk) {
|
|
460
|
+
const install = this.getSnapshot().install;
|
|
461
|
+
if (chunk.requestId !== install.requestId) return;
|
|
462
|
+
const index = install.runs.findIndex((run) => run.jobId === chunk.jobId);
|
|
463
|
+
if (index === -1 && !isInstallPending(install.phase)) return;
|
|
464
|
+
const settled = chunk.exitCode === void 0 ? {} : { exitCode: chunk.exitCode };
|
|
465
|
+
const runs = index === -1 ? [...install.runs, {
|
|
466
|
+
jobId: chunk.jobId,
|
|
467
|
+
command: chunk.argv.join(" "),
|
|
468
|
+
cwd: chunk.cwd,
|
|
469
|
+
output: chunk.text,
|
|
470
|
+
...settled
|
|
471
|
+
}] : install.runs.map((run, at) => at === index ? {
|
|
472
|
+
...run,
|
|
473
|
+
output: run.output + chunk.text,
|
|
474
|
+
...settled
|
|
475
|
+
} : run);
|
|
476
|
+
this.patchInstall({ runs });
|
|
477
|
+
}
|
|
478
|
+
/**
|
|
479
|
+
* Read the bundles and the entries their rows run as. A call during an
|
|
480
|
+
* in-flight read marks one rerun after it settles.
|
|
481
|
+
* @returns settlement after this call's freshness is reflected.
|
|
482
|
+
*/
|
|
483
|
+
load() {
|
|
484
|
+
if (this.disposed) return Promise.resolve();
|
|
485
|
+
if (this.inFlight !== void 0) {
|
|
486
|
+
this.rerun = true;
|
|
487
|
+
return this.inFlight;
|
|
488
|
+
}
|
|
489
|
+
const run = Promise.resolve().then(() => this.read());
|
|
490
|
+
this.inFlight = run;
|
|
491
|
+
return run;
|
|
492
|
+
}
|
|
493
|
+
async read() {
|
|
494
|
+
try {
|
|
495
|
+
do {
|
|
496
|
+
this.rerun = false;
|
|
497
|
+
const generation = ++this.generation;
|
|
498
|
+
if (this.getSnapshot().status === "idle") this.patch({ status: "loading" });
|
|
499
|
+
const inventory = await this.ctx.remote.pluginInventory.list();
|
|
500
|
+
if (generation !== this.generation) return;
|
|
501
|
+
if (!inventory.ok) {
|
|
502
|
+
this.patch({ status: "error" });
|
|
503
|
+
continue;
|
|
504
|
+
}
|
|
505
|
+
if (inventory.value.managementAvailable !== true) {
|
|
506
|
+
this.patch({
|
|
507
|
+
status: "unavailable",
|
|
508
|
+
packages: []
|
|
509
|
+
});
|
|
510
|
+
continue;
|
|
511
|
+
}
|
|
512
|
+
const [bundles, plugins] = await Promise.all([this.ctx.remote.pluginManager.listBundles(), this.ctx.remote.pluginManager.listPlugins()]);
|
|
513
|
+
if (generation !== this.generation) return;
|
|
514
|
+
if (!bundles.ok || !plugins.ok) {
|
|
515
|
+
this.patch({ status: "error" });
|
|
516
|
+
continue;
|
|
517
|
+
}
|
|
518
|
+
this.patch({
|
|
519
|
+
status: "ready",
|
|
520
|
+
packages: sortPackages(bundles.value.map((bundle) => packageView(bundle, plugins.value)))
|
|
521
|
+
});
|
|
522
|
+
} while (this.shouldRerun());
|
|
523
|
+
} finally {
|
|
524
|
+
this.inFlight = void 0;
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
shouldRerun() {
|
|
528
|
+
return this.rerun;
|
|
529
|
+
}
|
|
530
|
+
async confirm() {
|
|
531
|
+
const pending = this.pendingConfirm;
|
|
532
|
+
this.pendingConfirm = void 0;
|
|
533
|
+
this.patch({ confirm: null });
|
|
534
|
+
if (pending !== void 0) await pending();
|
|
535
|
+
}
|
|
536
|
+
/** Drop the check in flight; its answer is ignored. */
|
|
537
|
+
abortInspect() {
|
|
538
|
+
this.inspectAbort?.abort();
|
|
539
|
+
this.inspectAbort = void 0;
|
|
540
|
+
}
|
|
541
|
+
/** Whether a settlement arrives too late to matter: the store is disposed, or the dialog moved on. */
|
|
542
|
+
gone(signal) {
|
|
543
|
+
return this.disposed || signal.aborted;
|
|
544
|
+
}
|
|
545
|
+
/**
|
|
546
|
+
* Check the typed spec, then install it. The Host reads what the spec
|
|
547
|
+
* names first; a refused spec returns to the field with the reason, an
|
|
548
|
+
* accepted one becomes the subject the next screens show while pnpm runs.
|
|
549
|
+
*/
|
|
550
|
+
async runInstall() {
|
|
551
|
+
const state = this.getSnapshot();
|
|
552
|
+
const install = state.install;
|
|
553
|
+
const spec = install.spec.trim();
|
|
554
|
+
if (install.phase === "checking" || isInstallPending(install.phase) || spec === "") return;
|
|
555
|
+
if (state.packages.some((pkg) => pkg.name === spec)) {
|
|
556
|
+
this.patchInstall({
|
|
557
|
+
phase: "idle",
|
|
558
|
+
inputError: {
|
|
559
|
+
problem: "already-installed",
|
|
560
|
+
reason: spec
|
|
561
|
+
}
|
|
562
|
+
});
|
|
563
|
+
return;
|
|
564
|
+
}
|
|
565
|
+
this.abortInspect();
|
|
566
|
+
const controller = new AbortController();
|
|
567
|
+
this.inspectAbort = controller;
|
|
568
|
+
this.patchInstall({
|
|
569
|
+
phase: "checking",
|
|
570
|
+
inputError: null,
|
|
571
|
+
subject: null,
|
|
572
|
+
runs: [],
|
|
573
|
+
detailsOpen: false,
|
|
574
|
+
installed: null,
|
|
575
|
+
restartRequired: false,
|
|
576
|
+
failure: null,
|
|
577
|
+
approvedBuilds: []
|
|
578
|
+
});
|
|
579
|
+
const inspected = await this.ctx.remote.pluginManager.inspect(spec, controller.signal);
|
|
580
|
+
if (this.gone(controller.signal)) return;
|
|
581
|
+
this.inspectAbort = void 0;
|
|
582
|
+
if (!inspected.ok) {
|
|
583
|
+
this.patchInstall({
|
|
584
|
+
phase: "idle",
|
|
585
|
+
inputError: {
|
|
586
|
+
problem: "unknown",
|
|
587
|
+
reason: inspected.error.message
|
|
588
|
+
}
|
|
589
|
+
});
|
|
590
|
+
return;
|
|
591
|
+
}
|
|
592
|
+
if (inspected.value.status === "refused") {
|
|
593
|
+
this.patchInstall({
|
|
594
|
+
phase: "idle",
|
|
595
|
+
inputError: {
|
|
596
|
+
problem: inspected.value.problem,
|
|
597
|
+
reason: inspected.value.reason
|
|
598
|
+
}
|
|
599
|
+
});
|
|
600
|
+
return;
|
|
601
|
+
}
|
|
602
|
+
await this.startInstall({
|
|
603
|
+
spec,
|
|
604
|
+
...inspected.value
|
|
605
|
+
});
|
|
606
|
+
}
|
|
607
|
+
/**
|
|
608
|
+
* Hand the checked spec to the Host and settle the dialog from its answer.
|
|
609
|
+
* `approvedBuilds` names the pending install scripts the person allowed;
|
|
610
|
+
* the Host saves that permission for this profile before pnpm runs.
|
|
611
|
+
*/
|
|
612
|
+
async startInstall(subject, approvedBuilds) {
|
|
613
|
+
const { spec } = subject;
|
|
614
|
+
const requestId = randomUUID();
|
|
615
|
+
this.patchInstall({
|
|
616
|
+
phase: "starting",
|
|
617
|
+
requestId,
|
|
618
|
+
subject,
|
|
619
|
+
runs: [],
|
|
620
|
+
failure: null,
|
|
621
|
+
installed: null,
|
|
622
|
+
approvedBuilds: []
|
|
623
|
+
});
|
|
624
|
+
const result = await this.ctx.remote.pluginManager.installBundle(spec, {
|
|
625
|
+
enabled: false,
|
|
626
|
+
requestId,
|
|
627
|
+
...approvedBuilds === void 0 ? {} : { approvedBuilds: [...approvedBuilds] }
|
|
628
|
+
});
|
|
629
|
+
if (this.disposed || this.getSnapshot().install.requestId !== requestId) return;
|
|
630
|
+
const runs = this.getSnapshot().install.runs;
|
|
631
|
+
if (!result.ok) this.patchInstall({
|
|
632
|
+
phase: "failed",
|
|
633
|
+
runs: settledRuns(runs, null),
|
|
634
|
+
failure: { reason: result.error.message }
|
|
635
|
+
});
|
|
636
|
+
else if (result.value.application === "cancelled") this.offerSpecAgain({
|
|
637
|
+
kind: "cancelled",
|
|
638
|
+
seq: ++this.noticeSeq
|
|
639
|
+
});
|
|
640
|
+
else if (result.value.application === "failed") {
|
|
641
|
+
const packages = result.value.packageResult;
|
|
642
|
+
this.patchInstall({
|
|
643
|
+
phase: "failed",
|
|
644
|
+
runs: settledRuns(runs, packages?.exitCode ?? null),
|
|
645
|
+
failure: failureOf(result.value.error, packages?.kind, result.value.pendingBuilds)
|
|
646
|
+
});
|
|
647
|
+
} else this.patchInstall({
|
|
648
|
+
phase: "done",
|
|
649
|
+
runs: settledRuns(runs, 0),
|
|
650
|
+
installed: result.value.bundle ?? null,
|
|
651
|
+
restartRequired: result.value.application === "restart-required",
|
|
652
|
+
approvedBuilds: result.value.approvedBuilds ?? []
|
|
653
|
+
});
|
|
654
|
+
this.load();
|
|
655
|
+
}
|
|
656
|
+
/**
|
|
657
|
+
* Allow the install scripts the failed run left pending and run the same
|
|
658
|
+
* spec again. Only the failed screen with pending names offers this.
|
|
659
|
+
*/
|
|
660
|
+
async approveBuildsAndRetry() {
|
|
661
|
+
const install = this.getSnapshot().install;
|
|
662
|
+
const pending = install.failure?.pendingBuilds;
|
|
663
|
+
if (install.phase !== "failed" || install.subject === null || pending === void 0 || pending.length === 0) return;
|
|
664
|
+
await this.startInstall(install.subject, pending);
|
|
665
|
+
}
|
|
666
|
+
/**
|
|
667
|
+
* Leave the check or the failed screen for the spec at once; a Host-owned
|
|
668
|
+
* run is asked to stop and the dialog waits for the Host's word, since
|
|
669
|
+
* neither a dropped RPC nor a closed connection means pnpm has stopped.
|
|
670
|
+
* @param closeAfter - stop only a running install, and close the dialog once the Host confirms the stop.
|
|
671
|
+
*/
|
|
672
|
+
async cancelInstall(closeAfter = false) {
|
|
673
|
+
const install = this.getSnapshot().install;
|
|
674
|
+
if (!closeAfter && (install.phase === "checking" || install.phase === "failed")) {
|
|
675
|
+
this.abortInspect();
|
|
676
|
+
this.offerSpecAgain();
|
|
677
|
+
return;
|
|
678
|
+
}
|
|
679
|
+
if (install.phase !== "running" || install.requestId === void 0) return;
|
|
680
|
+
const requestId = install.requestId;
|
|
681
|
+
this.patchInstall({
|
|
682
|
+
phase: "cancelling",
|
|
683
|
+
failure: null
|
|
684
|
+
});
|
|
685
|
+
const result = await this.ctx.remote.pluginManager.cancelInstall(requestId);
|
|
686
|
+
const current = this.getSnapshot().install;
|
|
687
|
+
if (this.disposed || current.requestId !== requestId || !isInstallPending(current.phase)) return;
|
|
688
|
+
if (!result.ok) {
|
|
689
|
+
this.patchInstall({
|
|
690
|
+
phase: "running",
|
|
691
|
+
failure: {
|
|
692
|
+
reason: result.error.message,
|
|
693
|
+
cancelUnconfirmed: true
|
|
694
|
+
}
|
|
695
|
+
});
|
|
696
|
+
return;
|
|
697
|
+
}
|
|
698
|
+
if (result.value.status === "cancelled") {
|
|
699
|
+
const notice = {
|
|
700
|
+
kind: "cancelled",
|
|
701
|
+
seq: ++this.noticeSeq
|
|
702
|
+
};
|
|
703
|
+
if (closeAfter) this.patch({
|
|
704
|
+
install: IDLE_INSTALL,
|
|
705
|
+
notice
|
|
706
|
+
});
|
|
707
|
+
else this.offerSpecAgain(notice);
|
|
708
|
+
this.load();
|
|
709
|
+
} else if (result.value.status === "too-late") this.patchInstall({ phase: "applying" });
|
|
710
|
+
else this.patchInstall({
|
|
711
|
+
phase: "running",
|
|
712
|
+
failure: {
|
|
713
|
+
reason: "",
|
|
714
|
+
cancelUnconfirmed: true
|
|
715
|
+
}
|
|
716
|
+
});
|
|
717
|
+
}
|
|
718
|
+
/**
|
|
719
|
+
* Back to the spec: the check, the run, and its request are forgotten and
|
|
720
|
+
* the spec is kept, with a toast when there is something to say — the Host
|
|
721
|
+
* stopped the run.
|
|
722
|
+
*/
|
|
723
|
+
offerSpecAgain(notice = null) {
|
|
724
|
+
const { open, spec } = this.getSnapshot().install;
|
|
725
|
+
this.patch({
|
|
726
|
+
install: {
|
|
727
|
+
...IDLE_INSTALL,
|
|
728
|
+
open,
|
|
729
|
+
spec
|
|
730
|
+
},
|
|
731
|
+
...notice === null ? {} : { notice }
|
|
732
|
+
});
|
|
733
|
+
}
|
|
734
|
+
/**
|
|
735
|
+
* Enable the bundle the finished install added, then close the dialog and
|
|
736
|
+
* mark it in the list. A refusal toasts and still closes: the list shows
|
|
737
|
+
* what did not switch on.
|
|
738
|
+
*/
|
|
739
|
+
async enableInstalled() {
|
|
740
|
+
const install = this.getSnapshot().install;
|
|
741
|
+
if (install.phase !== "done" || install.enabling) return;
|
|
742
|
+
const name = install.installed;
|
|
743
|
+
this.patchInstall({ enabling: true });
|
|
744
|
+
if (name !== null) {
|
|
745
|
+
const result = await this.ctx.remote.pluginManager.setBundleEnabled(name, true);
|
|
746
|
+
if (this.disposed) return;
|
|
747
|
+
try {
|
|
748
|
+
this.applied(result, name);
|
|
749
|
+
} catch (error) {
|
|
750
|
+
this.patch({ notice: failedNotice(error, {
|
|
751
|
+
packageName: name,
|
|
752
|
+
action: "enable"
|
|
753
|
+
}, ++this.noticeSeq) });
|
|
754
|
+
}
|
|
755
|
+
}
|
|
756
|
+
this.patch({
|
|
757
|
+
install: IDLE_INSTALL,
|
|
758
|
+
highlight: name
|
|
759
|
+
});
|
|
760
|
+
await this.load();
|
|
761
|
+
}
|
|
762
|
+
/**
|
|
763
|
+
* Run one action under a busy key, turn its failure into the notice, and
|
|
764
|
+
* re-read the Host afterwards whatever happened.
|
|
765
|
+
*/
|
|
766
|
+
async run(key, subject, action) {
|
|
767
|
+
if (this.disposed || this.getSnapshot().busy.includes(key)) return;
|
|
768
|
+
this.patch({
|
|
769
|
+
busy: [...this.getSnapshot().busy, key],
|
|
770
|
+
notice: null
|
|
771
|
+
});
|
|
772
|
+
try {
|
|
773
|
+
await action();
|
|
774
|
+
} catch (error) {
|
|
775
|
+
this.patch({ notice: failedNotice(error, subject, ++this.noticeSeq) });
|
|
776
|
+
} finally {
|
|
777
|
+
this.patch({ busy: this.getSnapshot().busy.filter((entry) => entry !== key) });
|
|
778
|
+
}
|
|
779
|
+
await this.load();
|
|
780
|
+
}
|
|
781
|
+
/**
|
|
782
|
+
* Publish a change's outcome: a refused answer or a change the Host could
|
|
783
|
+
* not apply throws for {@link run} to report; a change that waits for the
|
|
784
|
+
* next start, that a higher layer overrides, or that the Host stopped is
|
|
785
|
+
* said in passing.
|
|
786
|
+
*/
|
|
787
|
+
applied(answer, packageName) {
|
|
788
|
+
if (!answer.ok) throw new RemoteAnswerError(answer.error.message);
|
|
789
|
+
const result = answer.value;
|
|
790
|
+
switch (result.application) {
|
|
791
|
+
case "failed": throw new RemoteAnswerError(result.error?.diagnostic ?? "", result.error?.code);
|
|
792
|
+
case "cancelled":
|
|
793
|
+
this.patch({ notice: {
|
|
794
|
+
kind: "cancelled",
|
|
795
|
+
seq: ++this.noticeSeq
|
|
796
|
+
} });
|
|
797
|
+
return;
|
|
798
|
+
case "restart-required":
|
|
799
|
+
this.patch({ notice: {
|
|
800
|
+
kind: "restart",
|
|
801
|
+
packageName,
|
|
802
|
+
seq: ++this.noticeSeq
|
|
803
|
+
} });
|
|
804
|
+
return;
|
|
805
|
+
case "overridden":
|
|
806
|
+
this.patch({ notice: {
|
|
807
|
+
kind: "overridden",
|
|
808
|
+
packageName,
|
|
809
|
+
seq: ++this.noticeSeq
|
|
810
|
+
} });
|
|
811
|
+
return;
|
|
812
|
+
case "applied": return;
|
|
813
|
+
}
|
|
814
|
+
}
|
|
815
|
+
patch(next) {
|
|
816
|
+
if (this.disposed) return;
|
|
817
|
+
this.store.set({
|
|
818
|
+
...this.getSnapshot(),
|
|
819
|
+
...next
|
|
820
|
+
});
|
|
821
|
+
}
|
|
822
|
+
patchInstall(next) {
|
|
823
|
+
this.patch({ install: {
|
|
824
|
+
...this.getSnapshot().install,
|
|
825
|
+
...next
|
|
826
|
+
} });
|
|
827
|
+
}
|
|
828
|
+
};
|
|
829
|
+
/** What a thrown failure said: a refused answer's reason, else the error's message. */
|
|
830
|
+
function reasonOf(error) {
|
|
831
|
+
return error instanceof Error ? error.message : String(error);
|
|
832
|
+
}
|
|
833
|
+
//#endregion
|
|
834
|
+
//#region \0dsh-css:/home/runner/work/deepseek-harness/deepseek-harness/packages/client/ui-plugin-manager/src/client/PluginManagerPage.module.css.mjs
|
|
835
|
+
const css = ".X_2TxG_page{box-sizing:border-box;height:100%;color:var(--dsw-alias-label-primary);flex-direction:column;align-items:center;gap:32px;padding:28px clamp(24px,4vw,48px) 48px;display:flex;overflow:auto}.X_2TxG_page>*{width:100%;max-width:960px}.X_2TxG_pageHead{justify-content:space-between;align-items:flex-start;gap:16px;display:flex}.X_2TxG_pageTitle{margin:0;font-size:20px;font-weight:500;line-height:28px}.X_2TxG_pageIntro{color:var(--dsw-alias-label-secondary);margin:4px 0 0;font-size:13px;line-height:20px}.X_2TxG_toolbar{justify-content:flex-end;align-items:center;gap:16px;display:flex}.X_2TxG_status,.X_2TxG_failure p,.X_2TxG_empty{color:var(--dsw-alias-label-tertiary);margin:0;font-size:13px;line-height:20px}.X_2TxG_failure{color:var(--dsw-alias-state-error-primary);align-items:center;gap:10px;display:flex}.X_2TxG_banner{background:color-mix(in srgb, var(--dsw-alias-state-warning-primary,var(--dsw-alias-state-business-primary)) 12%, transparent);color:var(--dsw-alias-label-primary);border-radius:10px;margin:0;padding:8px 12px;font-size:12px;line-height:18px}.X_2TxG_group{flex-direction:column;gap:8px;display:flex}.X_2TxG_groupHead{align-items:baseline;gap:8px;display:flex}.X_2TxG_groupTitle{margin:0;font-size:14px;font-weight:500;line-height:22px}.X_2TxG_count{color:var(--dsw-alias-label-caption);font-variant-numeric:tabular-nums;font-size:14px}.X_2TxG_groupInfo{color:var(--dsw-alias-label-caption);align-self:center;align-items:center;display:inline-flex}.X_2TxG_groupInfo:hover,.X_2TxG_groupInfo:focus-visible{color:var(--dsw-alias-label-secondary)}.X_2TxG_statusTag{height:20px;padding:0 8px;line-height:1}.X_2TxG_card[data-plugin-highlight]{animation:2.4s ease-out X_2TxG_dsh-plugin-highlight}@keyframes X_2TxG_dsh-plugin-highlight{0%,55%{box-shadow:0 0 0 2px var(--dsw-alias-state-business-primary)}to{box-shadow:0 0 #0000}}@media (prefers-reduced-motion:reduce){.X_2TxG_card[data-plugin-highlight]{box-shadow:0 0 0 2px var(--dsw-alias-state-business-primary);animation:none}}.X_2TxG_iconButton:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:1px}.X_2TxG_cards{flex-direction:column;gap:2px;margin:0;padding:0;list-style:none;display:flex}.X_2TxG_card{--dsh-scrollbar-thumb:var(--dsw-alias-scrollbar-bg-l2);--dsh-scrollbar-thumb-hover:var(--dsw-alias-scrollbar-hover-l2);border-radius:12px;min-width:0;margin:0 -8px}.X_2TxG_cardHead{align-items:center;gap:14px;padding:8px;display:flex}.X_2TxG_cardIcon{border:.5px solid var(--dsw-alias-border-l3);width:48px;height:48px;color:var(--dsw-alias-label-secondary);border-radius:10px;flex:none;justify-content:center;align-items:center;display:inline-flex}.X_2TxG_cardMain{flex-direction:column;flex:1;gap:4px;min-width:0;display:flex}.X_2TxG_titleRow{flex-wrap:wrap;align-items:center;gap:8px;min-width:0;display:flex}.X_2TxG_cardTitle{text-overflow:ellipsis;white-space:nowrap;font-size:14px;font-weight:500;line-height:20px;overflow:hidden}.X_2TxG_cardLink{position:relative}.X_2TxG_cardLink:hover{background:var(--dsw-alias-interactive-bg-hover)}.X_2TxG_cardOpen{max-width:100%;color:inherit;font:inherit;text-align:left;cursor:pointer;background:0 0;border:0;padding:0;font-weight:500}.X_2TxG_cardOpen:after{content:\"\";border-radius:12px;position:absolute;inset:0}.X_2TxG_cardOpen:focus-visible{outline:none}.X_2TxG_cardOpen:focus-visible:after{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:2px}.X_2TxG_cardDesc{color:var(--dsw-alias-label-tertiary);-webkit-line-clamp:1;-webkit-box-orient:vertical;font-size:13px;line-height:18px;display:-webkit-box;overflow:hidden}.X_2TxG_cardEnd{z-index:1;flex:none;align-items:center;gap:8px;display:inline-flex;position:relative}.X_2TxG_iconButton{width:28px;height:28px;color:var(--dsw-alias-label-caption);cursor:pointer;background:0 0;border:0;border-radius:28px;justify-content:center;align-items:center;display:inline-flex}.X_2TxG_iconButton:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-secondary)}.X_2TxG_iconButton:disabled{opacity:.5;cursor:default}.X_2TxG_iconWrap{display:inline-flex}.X_2TxG_danger{color:var(--dsw-alias-state-error-primary);border-color:color-mix(in srgb, var(--dsw-alias-state-error-primary) 30%, transparent);--dsw-alias-interactive-bg-hover:color-mix(in srgb, var(--dsw-alias-state-error-primary) 8%, transparent)}.X_2TxG_detailActions{flex:none;align-items:center;gap:16px;display:flex}.X_2TxG_actions{align-items:center;gap:16px;display:flex}.X_2TxG_deleteButton{width:28px;height:28px;color:var(--dsw-alias-state-error-primary);cursor:pointer;background:0 0;border:0;border-radius:8px;justify-content:center;align-items:center;display:inline-flex}.X_2TxG_deleteButton:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover-danger,var(--dsw-alias-interactive-bg-hover))}.X_2TxG_deleteButton:disabled{opacity:.5;cursor:default}.X_2TxG_deleteButton:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:1px}.X_2TxG_reason{color:var(--dsw-alias-state-error-primary);overflow-wrap:anywhere;white-space:pre-wrap;margin:0;font-size:12px;line-height:18px}.X_2TxG_partsHead .X_2TxG_subLabel{margin:0}.X_2TxG_partsFilter{width:200px}.X_2TxG_partsFilter:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:1px}.X_2TxG_installDialog{width:min(600px,100%);max-height:calc(100vh - 48px)}.X_2TxG_installBody{flex-direction:column;gap:12px;min-width:0;display:flex}.X_2TxG_installLocation{color:var(--dsw-alias-label-tertiary);overflow-wrap:anywhere;margin:0;font-size:12px;line-height:18px}.X_2TxG_installField{flex-direction:column;gap:6px;font-size:13px;display:flex}.X_2TxG_installField input[type=text]{border:.5px solid var(--dsw-alias-border-l4);background:var(--dsw-alias-bg-layer-3);height:36px;font:inherit;color:var(--dsw-alias-label-primary);border-radius:10px;padding:0 12px;font-size:13px}.X_2TxG_installField input[aria-invalid=true]{border-color:var(--dsw-alias-state-error-primary)}.X_2TxG_guideToggle{font:inherit;color:var(--dsw-alias-label-secondary);cursor:pointer;background:0 0;border:0;align-self:flex-start;align-items:center;gap:4px;padding:0;font-size:12.5px;display:inline-flex}.X_2TxG_guideToggle:hover{color:var(--dsw-alias-label-primary)}.X_2TxG_guideChevron{transition:transform .16s}.X_2TxG_guideToggle[aria-expanded=true] .X_2TxG_guideChevron{transform:rotate(180deg)}.X_2TxG_guide{border:.5px solid var(--dsw-alias-border-l4);background:var(--dsw-alias-bg-layer-1);border-radius:12px;flex-direction:column;gap:10px;padding:12px 14px;display:flex}.X_2TxG_guideIntro,.X_2TxG_guideHint{color:var(--dsw-alias-label-tertiary);margin:0;font-size:12px;line-height:18px}.X_2TxG_guideSafety{background:color-mix(in srgb, var(--dsw-alias-state-warning-primary,var(--dsw-alias-state-business-primary)) 12%, transparent);color:var(--dsw-alias-state-warning-primary,var(--dsw-alias-state-business-primary));border-radius:8px;align-items:flex-start;gap:6px;margin:0;padding:8px 10px;font-size:12px;line-height:18px;display:flex}.X_2TxG_guideSafety>svg{flex:none;margin-top:2px}.X_2TxG_guideNote{background:var(--dsw-alias-bg-layer-3);color:var(--dsw-alias-label-secondary);border-radius:8px;margin:0;padding:8px 10px;font-size:12px;line-height:18px}.X_2TxG_guideList{flex-direction:column;margin:0;padding:0;list-style:none;display:flex}.X_2TxG_guideItem{border-top:.5px solid var(--dsw-alias-border-l4);align-items:flex-start;gap:10px;padding:8px 0;display:flex}.X_2TxG_guideIndex{corner-shape:round;background:var(--dsw-alias-bg-layer-3);text-align:center;width:20px;height:20px;color:var(--dsw-alias-label-tertiary);border-radius:999px;flex:none;font-size:11px;line-height:20px}.X_2TxG_guideMain{flex-direction:column;flex:1;gap:2px;min-width:0;display:flex}.X_2TxG_guideTitle{color:var(--dsw-alias-label-primary);font-size:12.5px;font-weight:600}.X_2TxG_guideExample{color:var(--dsw-alias-label-secondary);overflow-wrap:anywhere;font-size:12px}.X_2TxG_guideExample code{font-family:var(--dsw-font-mono,ui-monospace, SFMono-Regular, Menlo, monospace)}.X_2TxG_guideExampleLabel{color:var(--dsw-alias-label-tertiary)}.X_2TxG_inputError{color:var(--dsw-alias-state-error-primary);margin:-4px 0 0;font-size:12px;line-height:18px}.X_2TxG_wide{justify-content:center;width:100%}.X_2TxG_wizard{flex-direction:column;flex:auto;gap:16px;min-width:0;min-height:0;padding:16px 20px 20px;display:flex}.X_2TxG_wizardScroll{flex-direction:column;flex:auto;gap:16px;min-height:0;display:flex;overflow-y:auto}.X_2TxG_wizardHead{justify-content:space-between;align-items:center;min-height:24px;display:flex}.X_2TxG_wizardBack{font:inherit;color:var(--dsw-alias-label-primary);cursor:pointer;background:0 0;border:0;align-items:center;gap:4px;padding:0;font-size:15px;font-weight:600;display:inline-flex}.X_2TxG_wizardClose{width:24px;height:24px;color:var(--dsw-alias-label-tertiary);cursor:pointer;background:0 0;border:0;border-radius:6px;justify-content:center;align-items:center;padding:0;display:inline-flex}.X_2TxG_wizardClose:hover{background:var(--dsw-alias-bg-layer-3);color:var(--dsw-alias-label-primary)}.X_2TxG_wizardHero{text-align:center;flex-direction:column;align-items:center;gap:10px;padding:8px 0 4px;display:flex}.X_2TxG_wizardIcon{width:44px;height:44px;color:var(--dsw-alias-label-secondary);justify-content:center;align-items:center;display:inline-flex}.X_2TxG_wizardIcon[data-tone=done]{color:var(--dsw-alias-state-success-primary)}.X_2TxG_wizardIcon[data-tone=failed]{color:var(--dsw-alias-state-warning-primary,var(--dsw-alias-state-business-primary))}.X_2TxG_wizardTitle{margin:0;font-size:18px;font-weight:600;line-height:26px}.X_2TxG_wizardSub{color:var(--dsw-alias-label-secondary);overflow-wrap:anywhere;margin:0;font-size:13px;line-height:20px}.X_2TxG_subject{border:.5px solid var(--dsw-alias-border-l3);text-align:center;border-radius:12px;flex-direction:column;align-items:center;gap:6px;padding:16px;display:flex}.X_2TxG_subjectName{overflow-wrap:anywhere;margin:0;font-size:15px;font-weight:600;line-height:22px}.X_2TxG_subjectDesc,.X_2TxG_subjectMeta{color:var(--dsw-alias-label-secondary);overflow-wrap:anywhere;margin:0;font-size:13px;line-height:20px}.X_2TxG_wizardFoot{justify-content:space-between;align-items:center;gap:12px;display:flex}.X_2TxG_detailsToggle{font:inherit;color:var(--dsw-alias-label-secondary);cursor:pointer;background:0 0;border:0;align-items:center;gap:4px;padding:0;font-size:13px;display:inline-flex}.X_2TxG_detailsChevron{transition:transform .16s}.X_2TxG_detailsToggle[aria-expanded=true] .X_2TxG_detailsChevron{transform:rotate(180deg)}.X_2TxG_detailsBody{flex-direction:column;gap:8px;min-width:0;display:flex}.X_2TxG_spinnerLarge{border:3px solid color-mix(in srgb, var(--dsw-alias-label-primary) 18%, transparent);border-top-color:var(--dsw-alias-label-primary);corner-shape:round;border-radius:50%;width:28px;height:28px;animation:.9s linear infinite X_2TxG_spin}.X_2TxG_spinner{border:2px solid color-mix(in srgb, var(--dsw-alias-label-primary) 18%, transparent);border-top-color:var(--dsw-alias-label-primary);corner-shape:round;border-radius:50%;flex:none;width:14px;height:14px;animation:.9s linear infinite X_2TxG_spin}@keyframes X_2TxG_spin{to{transform:rotate(360deg)}}@media (prefers-reduced-motion:reduce){.X_2TxG_spinner,.X_2TxG_spinnerLarge{animation:none}.X_2TxG_detailsChevron{transition:none}}.X_2TxG_result,.X_2TxG_resultWarn{background:color-mix(in srgb, var(--dsw-alias-state-success-primary) 10%, transparent);color:var(--dsw-alias-label-primary);border-radius:10px;margin:0;padding:8px 12px;font-size:13px;line-height:20px}.X_2TxG_resultWarn{background:color-mix(in srgb, var(--dsw-alias-state-warning-primary,var(--dsw-alias-state-business-primary)) 12%, transparent)}.X_2TxG_approval{border:.5px solid color-mix(in srgb, var(--dsw-alias-state-warning-primary,var(--dsw-alias-state-business-primary)) 40%, transparent);background:color-mix(in srgb, var(--dsw-alias-state-warning-primary,var(--dsw-alias-state-business-primary)) 8%, transparent);border-radius:12px;flex-direction:column;gap:8px;padding:12px 14px;display:flex}.X_2TxG_approvalTitle{color:var(--dsw-alias-label-primary);margin:0;font-size:13px;font-weight:600}.X_2TxG_approvalText{color:var(--dsw-alias-label-secondary);margin:0;font-size:12.5px;line-height:18px}.X_2TxG_approvalCaution{color:var(--dsw-alias-state-warning-primary,var(--dsw-alias-state-business-primary));margin:0;font-size:12.5px;font-weight:600;line-height:18px}.X_2TxG_approvalList{flex-wrap:wrap;gap:6px;margin:0;padding:0;list-style:none;display:flex}.X_2TxG_approvalList code{background:var(--dsw-alias-bg-layer-3);font-family:var(--dsw-font-mono,ui-monospace, SFMono-Regular, Menlo, monospace);color:var(--dsw-alias-label-primary);border-radius:6px;padding:2px 8px;font-size:12px;display:inline-block}.X_2TxG_terminal{--dsl-terminal-font:var(--dsw-font-markdown-code-block-small);--dsl-terminal-line-height:18px;--dsl-terminal-output-max-height:240px;border:.5px solid var(--dsw-alias-border-l1);margin:4px 0 0}.X_2TxG_dependents{color:var(--dsw-alias-label-secondary);margin:8px 0 0;padding-left:18px;font-size:13px;line-height:20px}.X_2TxG_dangerButton{--dsw-alias-button-primary-fill:var(--dsw-alias-state-error-primary);--dsw-alias-button-primary-hover:var(--dsw-alias-state-error-primary)}.X_2TxG_detail{flex-direction:column;display:flex}.X_2TxG_crumb{color:var(--dsw-alias-label-tertiary);font:inherit;cursor:pointer;background:0 0;border:0;align-items:center;gap:6px;padding:0;font-size:12.5px;display:inline-flex}.X_2TxG_crumb:hover{color:var(--dsw-alias-label-primary)}.X_2TxG_crumb:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:2px}.X_2TxG_crumbIcon{transform:rotate(90deg)}.X_2TxG_detailHead{justify-content:space-between;align-items:center;gap:12px;margin:32px 0 0;display:flex}.X_2TxG_detailMain{flex-direction:column;gap:8px;min-width:0;margin-top:20px;display:flex}.X_2TxG_detailTitle{margin:0;font-size:20px;font-weight:500;line-height:28px}.X_2TxG_versionTag{font-variant-numeric:tabular-nums;flex:none}.X_2TxG_detailDesc{color:var(--dsw-alias-label-secondary);margin:0;font-size:14px;line-height:22px}.X_2TxG_detailName{color:var(--dsw-alias-label-caption);overflow-wrap:anywhere;margin:0;font-size:12px;line-height:18px}.X_2TxG_detailName code{font-family:var(--dsw-font-mono,ui-monospace, SFMono-Regular, Menlo, monospace)}.X_2TxG_detail>.X_2TxG_detailDesc{margin-top:12px}.X_2TxG_detailSections{flex-direction:column;gap:32px;margin-top:32px;display:flex}.X_2TxG_detailSection{flex-direction:column;gap:12px;display:flex}.X_2TxG_sectionHead{align-items:baseline;gap:10px;display:flex}.X_2TxG_sectionTitle{margin:0;font-size:14px;font-weight:500;line-height:20px}.X_2TxG_sectionCount{color:var(--dsw-alias-label-secondary);font-size:12px;line-height:18px}.X_2TxG_cardArrow{color:var(--dsw-alias-label-tertiary);flex:none}.X_2TxG_rows{flex-direction:column;margin:0;padding:0;list-style:none;display:flex}.X_2TxG_row{border-bottom:.5px solid var(--dsw-alias-border-l2);padding:12px 2px}.X_2TxG_row:last-child{border-bottom:0}.X_2TxG_rowLine{align-items:center;gap:16px;min-width:0;display:flex}.X_2TxG_rowIcon{border:.5px solid var(--dsw-alias-border-l3);width:40px;height:40px;color:var(--dsw-alias-label-secondary);border-radius:10px;flex:none;justify-content:center;align-items:center;display:inline-flex}.X_2TxG_rowMain{flex-direction:column;flex:1;gap:2px;min-width:0;display:flex}.X_2TxG_rowId{color:var(--dsw-alias-label-primary);overflow-wrap:anywhere;font-size:13.5px;font-weight:500;line-height:20px}.X_2TxG_row[data-state=off] .X_2TxG_rowId{color:var(--dsw-alias-label-secondary)}.X_2TxG_rowModule{font-family:var(--dsw-font-mono,ui-monospace, SFMono-Regular, Menlo, monospace);color:var(--dsw-alias-label-tertiary);overflow-wrap:anywhere;font-size:11.5px;line-height:16px}.X_2TxG_rowOpen{color:inherit;font:inherit;text-align:left;cursor:pointer;background:0 0;border:0;align-items:center;gap:2px;padding:0;display:inline-flex}.X_2TxG_rowOpen:hover .X_2TxG_rowId{text-underline-offset:3px;text-decoration:underline}.X_2TxG_rowOpenIcon{color:var(--dsw-alias-label-tertiary);flex:none}.X_2TxG_rowOpen:hover .X_2TxG_rowOpenIcon{color:var(--dsw-alias-label-primary)}.X_2TxG_rowState{color:var(--dsw-alias-label-secondary);white-space:nowrap;flex:none;align-items:center;gap:6px;font-size:12.5px;line-height:18px;display:inline-flex}.X_2TxG_row[data-state=failed] .X_2TxG_rowState{color:var(--dsw-alias-state-error-primary)}.X_2TxG_rowFailure{color:var(--dsw-alias-state-error-primary);overflow-wrap:anywhere;margin:4px 0 0 56px;font-size:12px;line-height:18px}";
|
|
836
|
+
const tagId = "@deepseek-ai/dsh-client-ui-plugin-manager/PluginManagerPage.module.css";
|
|
837
|
+
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId) + "]") === null) {
|
|
838
|
+
const tag = document.createElement("style");
|
|
839
|
+
tag.dataset.plugin = "@deepseek-ai/dsh-client-ui-plugin-manager";
|
|
840
|
+
tag.dataset.pluginCss = tagId;
|
|
841
|
+
tag.textContent = css;
|
|
842
|
+
document.head.appendChild(tag);
|
|
843
|
+
}
|
|
844
|
+
var PluginManagerPage_module_css_default = {
|
|
845
|
+
"actions": "X_2TxG_actions",
|
|
846
|
+
"approval": "X_2TxG_approval",
|
|
847
|
+
"approvalCaution": "X_2TxG_approvalCaution",
|
|
848
|
+
"approvalList": "X_2TxG_approvalList",
|
|
849
|
+
"approvalText": "X_2TxG_approvalText",
|
|
850
|
+
"approvalTitle": "X_2TxG_approvalTitle",
|
|
851
|
+
"banner": "X_2TxG_banner",
|
|
852
|
+
"card": "X_2TxG_card",
|
|
853
|
+
"cardArrow": "X_2TxG_cardArrow",
|
|
854
|
+
"cardDesc": "X_2TxG_cardDesc",
|
|
855
|
+
"cardEnd": "X_2TxG_cardEnd",
|
|
856
|
+
"cardHead": "X_2TxG_cardHead",
|
|
857
|
+
"cardIcon": "X_2TxG_cardIcon",
|
|
858
|
+
"cardLink": "X_2TxG_cardLink",
|
|
859
|
+
"cardMain": "X_2TxG_cardMain",
|
|
860
|
+
"cardOpen": "X_2TxG_cardOpen",
|
|
861
|
+
"cardTitle": "X_2TxG_cardTitle",
|
|
862
|
+
"cards": "X_2TxG_cards",
|
|
863
|
+
"count": "X_2TxG_count",
|
|
864
|
+
"crumb": "X_2TxG_crumb",
|
|
865
|
+
"crumbIcon": "X_2TxG_crumbIcon",
|
|
866
|
+
"danger": "X_2TxG_danger",
|
|
867
|
+
"dangerButton": "X_2TxG_dangerButton",
|
|
868
|
+
"deleteButton": "X_2TxG_deleteButton",
|
|
869
|
+
"dependents": "X_2TxG_dependents",
|
|
870
|
+
"detail": "X_2TxG_detail",
|
|
871
|
+
"detailActions": "X_2TxG_detailActions",
|
|
872
|
+
"detailDesc": "X_2TxG_detailDesc",
|
|
873
|
+
"detailHead": "X_2TxG_detailHead",
|
|
874
|
+
"detailMain": "X_2TxG_detailMain",
|
|
875
|
+
"detailName": "X_2TxG_detailName",
|
|
876
|
+
"detailSection": "X_2TxG_detailSection",
|
|
877
|
+
"detailSections": "X_2TxG_detailSections",
|
|
878
|
+
"detailTitle": "X_2TxG_detailTitle",
|
|
879
|
+
"detailsBody": "X_2TxG_detailsBody",
|
|
880
|
+
"detailsChevron": "X_2TxG_detailsChevron",
|
|
881
|
+
"detailsToggle": "X_2TxG_detailsToggle",
|
|
882
|
+
"dsh-plugin-highlight": "X_2TxG_dsh-plugin-highlight",
|
|
883
|
+
"empty": "X_2TxG_empty",
|
|
884
|
+
"failure": "X_2TxG_failure",
|
|
885
|
+
"group": "X_2TxG_group",
|
|
886
|
+
"groupHead": "X_2TxG_groupHead",
|
|
887
|
+
"groupInfo": "X_2TxG_groupInfo",
|
|
888
|
+
"groupTitle": "X_2TxG_groupTitle",
|
|
889
|
+
"guide": "X_2TxG_guide",
|
|
890
|
+
"guideChevron": "X_2TxG_guideChevron",
|
|
891
|
+
"guideExample": "X_2TxG_guideExample",
|
|
892
|
+
"guideExampleLabel": "X_2TxG_guideExampleLabel",
|
|
893
|
+
"guideHint": "X_2TxG_guideHint",
|
|
894
|
+
"guideIndex": "X_2TxG_guideIndex",
|
|
895
|
+
"guideIntro": "X_2TxG_guideIntro",
|
|
896
|
+
"guideItem": "X_2TxG_guideItem",
|
|
897
|
+
"guideList": "X_2TxG_guideList",
|
|
898
|
+
"guideMain": "X_2TxG_guideMain",
|
|
899
|
+
"guideNote": "X_2TxG_guideNote",
|
|
900
|
+
"guideSafety": "X_2TxG_guideSafety",
|
|
901
|
+
"guideTitle": "X_2TxG_guideTitle",
|
|
902
|
+
"guideToggle": "X_2TxG_guideToggle",
|
|
903
|
+
"iconButton": "X_2TxG_iconButton",
|
|
904
|
+
"iconWrap": "X_2TxG_iconWrap",
|
|
905
|
+
"inputError": "X_2TxG_inputError",
|
|
906
|
+
"installBody": "X_2TxG_installBody",
|
|
907
|
+
"installDialog": "X_2TxG_installDialog",
|
|
908
|
+
"installField": "X_2TxG_installField",
|
|
909
|
+
"installLocation": "X_2TxG_installLocation",
|
|
910
|
+
"page": "X_2TxG_page",
|
|
911
|
+
"pageHead": "X_2TxG_pageHead",
|
|
912
|
+
"pageIntro": "X_2TxG_pageIntro",
|
|
913
|
+
"pageTitle": "X_2TxG_pageTitle",
|
|
914
|
+
"partsFilter": "X_2TxG_partsFilter",
|
|
915
|
+
"partsHead": "X_2TxG_partsHead",
|
|
916
|
+
"reason": "X_2TxG_reason",
|
|
917
|
+
"result": "X_2TxG_result",
|
|
918
|
+
"resultWarn": "X_2TxG_resultWarn",
|
|
919
|
+
"row": "X_2TxG_row",
|
|
920
|
+
"rowFailure": "X_2TxG_rowFailure",
|
|
921
|
+
"rowIcon": "X_2TxG_rowIcon",
|
|
922
|
+
"rowId": "X_2TxG_rowId",
|
|
923
|
+
"rowLine": "X_2TxG_rowLine",
|
|
924
|
+
"rowMain": "X_2TxG_rowMain",
|
|
925
|
+
"rowModule": "X_2TxG_rowModule",
|
|
926
|
+
"rowOpen": "X_2TxG_rowOpen",
|
|
927
|
+
"rowOpenIcon": "X_2TxG_rowOpenIcon",
|
|
928
|
+
"rowState": "X_2TxG_rowState",
|
|
929
|
+
"rows": "X_2TxG_rows",
|
|
930
|
+
"sectionCount": "X_2TxG_sectionCount",
|
|
931
|
+
"sectionHead": "X_2TxG_sectionHead",
|
|
932
|
+
"sectionTitle": "X_2TxG_sectionTitle",
|
|
933
|
+
"spin": "X_2TxG_spin",
|
|
934
|
+
"spinner": "X_2TxG_spinner",
|
|
935
|
+
"spinnerLarge": "X_2TxG_spinnerLarge",
|
|
936
|
+
"status": "X_2TxG_status",
|
|
937
|
+
"statusTag": "X_2TxG_statusTag",
|
|
938
|
+
"subLabel": "X_2TxG_subLabel",
|
|
939
|
+
"subject": "X_2TxG_subject",
|
|
940
|
+
"subjectDesc": "X_2TxG_subjectDesc",
|
|
941
|
+
"subjectMeta": "X_2TxG_subjectMeta",
|
|
942
|
+
"subjectName": "X_2TxG_subjectName",
|
|
943
|
+
"terminal": "X_2TxG_terminal",
|
|
944
|
+
"titleRow": "X_2TxG_titleRow",
|
|
945
|
+
"toolbar": "X_2TxG_toolbar",
|
|
946
|
+
"versionTag": "X_2TxG_versionTag",
|
|
947
|
+
"wide": "X_2TxG_wide",
|
|
948
|
+
"wizard": "X_2TxG_wizard",
|
|
949
|
+
"wizardBack": "X_2TxG_wizardBack",
|
|
950
|
+
"wizardClose": "X_2TxG_wizardClose",
|
|
951
|
+
"wizardFoot": "X_2TxG_wizardFoot",
|
|
952
|
+
"wizardHead": "X_2TxG_wizardHead",
|
|
953
|
+
"wizardHero": "X_2TxG_wizardHero",
|
|
954
|
+
"wizardIcon": "X_2TxG_wizardIcon",
|
|
955
|
+
"wizardScroll": "X_2TxG_wizardScroll",
|
|
956
|
+
"wizardSub": "X_2TxG_wizardSub",
|
|
957
|
+
"wizardTitle": "X_2TxG_wizardTitle"
|
|
958
|
+
};
|
|
959
|
+
//#endregion
|
|
960
|
+
//#region lib/types/client/PluginManagerPage.js
|
|
961
|
+
/**
|
|
962
|
+
* Global plugin management: the Official group's cards for the bundles the
|
|
963
|
+
* installation ships switched off and for the official plugins that register
|
|
964
|
+
* their configuration, the Installed group's cards for the profile's bundles,
|
|
965
|
+
* their row switches, the install dialog with its guide and folded pnpm
|
|
966
|
+
* output, the uninstall confirmation, and the toasts an action's outcome
|
|
967
|
+
* becomes. A bundle's page lists the rows it contributes as the Host runs
|
|
968
|
+
* them; a plugin's configuration renders on its own page through the slots
|
|
969
|
+
* the page declares.
|
|
970
|
+
*/
|
|
971
|
+
/** How long the list marks a package an install just enabled. */
|
|
972
|
+
const HIGHLIGHT_MS = 2400;
|
|
973
|
+
/** Built-in profile bundles stay out of this page even when the profile declares them as dependencies. */
|
|
974
|
+
const BUILTIN_PROFILE_BUNDLES = new Set([
|
|
975
|
+
"@deepseek-ai/dsh-base",
|
|
976
|
+
"@deepseek-ai/dsh-web-app",
|
|
977
|
+
"@deepseek-ai/dsh-headless",
|
|
978
|
+
"@deepseek-ai/dsh-sdk-app",
|
|
979
|
+
"@deepseek-ai/dsh-acp-app",
|
|
980
|
+
"@deepseek-ai/dsh-sdk-minimal"
|
|
981
|
+
]);
|
|
982
|
+
/** How long a toast holds: long enough to read a failure that names what broke. */
|
|
983
|
+
function toastHoldMs(text) {
|
|
984
|
+
return Math.min(8e3, Math.max(3e3, text.length * 80));
|
|
985
|
+
}
|
|
986
|
+
const PHASE_KEYS = {
|
|
987
|
+
pending: "rowPhasePending",
|
|
988
|
+
loading: "rowPhaseLoading",
|
|
989
|
+
active: "rowPhaseActive",
|
|
990
|
+
failed: "rowPhaseFailed",
|
|
991
|
+
unloading: "rowPhaseUnloading"
|
|
992
|
+
};
|
|
993
|
+
/** Status dot naming a live root-fiber phase: pending and unloading fibers do nothing; only loading is in progress. */
|
|
994
|
+
const PHASE_STATES = {
|
|
995
|
+
pending: "idle",
|
|
996
|
+
loading: "ongoing",
|
|
997
|
+
active: "done",
|
|
998
|
+
failed: "error",
|
|
999
|
+
unloading: "idle"
|
|
1000
|
+
};
|
|
1001
|
+
/** The count line over a pack's components: the total, then only the states that occur. */
|
|
1002
|
+
function partsSummary(rows, t) {
|
|
1003
|
+
const failed = rows.filter((row) => row.phase === "failed").length;
|
|
1004
|
+
const off = rows.filter((row) => !row.enabled).length;
|
|
1005
|
+
const running = rows.filter((row) => row.enabled && row.phase === "active").length;
|
|
1006
|
+
return [
|
|
1007
|
+
t("partsCountTotal", { count: String(rows.length) }),
|
|
1008
|
+
...running > 0 ? [t("partsCountRunning", { count: String(running) })] : [],
|
|
1009
|
+
...off > 0 ? [t("partsCountOff", { count: String(off) })] : [],
|
|
1010
|
+
...failed > 0 ? [t("partsCountFailed", { count: String(failed) })] : []
|
|
1011
|
+
].join(" · ");
|
|
1012
|
+
}
|
|
1013
|
+
/** Rows beyond this count get a filter box above the list. */
|
|
1014
|
+
const ROW_FILTER_THRESHOLD = 10;
|
|
1015
|
+
/** A row's switch: locked, saying why, when the Host refuses to address the row through the profile patch. */
|
|
1016
|
+
function RowSwitch({ row, t, busy, onChange }) {
|
|
1017
|
+
const locked = row.readOnlyReason !== void 0 || row.entryId === void 0;
|
|
1018
|
+
return (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Switch, {
|
|
1019
|
+
checked: row.enabled,
|
|
1020
|
+
label: t("partToggle", { name: row.rowId }),
|
|
1021
|
+
disabled: busy || locked,
|
|
1022
|
+
...row.readOnlyReason === void 0 ? {} : { title: managementText({ code: row.readOnlyReason }, t) },
|
|
1023
|
+
onChange
|
|
1024
|
+
});
|
|
1025
|
+
}
|
|
1026
|
+
/** What a row's state line says: off, or the phase its fiber is in. */
|
|
1027
|
+
function rowStateText(row, t) {
|
|
1028
|
+
if (!row.enabled) return t("partOff");
|
|
1029
|
+
return row.phase === null ? t("rowStateIdle") : t(PHASE_KEYS[row.phase]);
|
|
1030
|
+
}
|
|
1031
|
+
/** The dot beside a row: its fiber phase, or idle. */
|
|
1032
|
+
function rowDotState(row) {
|
|
1033
|
+
if (!row.enabled || row.phase === null) return "idle";
|
|
1034
|
+
return PHASE_STATES[row.phase];
|
|
1035
|
+
}
|
|
1036
|
+
/**
|
|
1037
|
+
* A pack's rows as a list in the order the pack declares them: a state dot,
|
|
1038
|
+
* the row id, one line saying its state, a configure control for a row that
|
|
1039
|
+
* registered a page, and, when the pack is on, a switch. A pack like base
|
|
1040
|
+
* carries close to a hundred rows, so a long list gets a filter.
|
|
1041
|
+
*/
|
|
1042
|
+
function RowsSection({ rows, t, toggle, configure }) {
|
|
1043
|
+
const [filter, setFilter] = (0, react.useState)("");
|
|
1044
|
+
const query = filter.trim().toLowerCase();
|
|
1045
|
+
const shown = query === "" ? rows : rows.filter((row) => row.rowId.toLowerCase().includes(query));
|
|
1046
|
+
return (0, react_jsx_runtime.jsxs)("section", {
|
|
1047
|
+
className: PluginManagerPage_module_css_default.detailSection,
|
|
1048
|
+
"data-plugin-rows": true,
|
|
1049
|
+
children: [
|
|
1050
|
+
(0, react_jsx_runtime.jsxs)("div", {
|
|
1051
|
+
className: PluginManagerPage_module_css_default.sectionHead,
|
|
1052
|
+
children: [(0, react_jsx_runtime.jsx)("h4", {
|
|
1053
|
+
className: PluginManagerPage_module_css_default.sectionTitle,
|
|
1054
|
+
children: t("partsLabel")
|
|
1055
|
+
}), rows.length === 0 ? null : (0, react_jsx_runtime.jsx)("span", {
|
|
1056
|
+
className: PluginManagerPage_module_css_default.sectionCount,
|
|
1057
|
+
children: partsSummary(rows, t)
|
|
1058
|
+
})]
|
|
1059
|
+
}),
|
|
1060
|
+
rows.length === 0 ? (0, react_jsx_runtime.jsx)("p", {
|
|
1061
|
+
className: PluginManagerPage_module_css_default.status,
|
|
1062
|
+
children: t("partsEmpty")
|
|
1063
|
+
}) : null,
|
|
1064
|
+
rows.length > ROW_FILTER_THRESHOLD ? (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Input, {
|
|
1065
|
+
type: "search",
|
|
1066
|
+
className: PluginManagerPage_module_css_default.partsFilter,
|
|
1067
|
+
placeholder: t("partsFilter"),
|
|
1068
|
+
"aria-label": t("partsFilter"),
|
|
1069
|
+
value: filter,
|
|
1070
|
+
onChange: (event) => {
|
|
1071
|
+
setFilter(event.target.value);
|
|
1072
|
+
}
|
|
1073
|
+
}) : null,
|
|
1074
|
+
rows.length > 0 && shown.length === 0 ? (0, react_jsx_runtime.jsx)("p", {
|
|
1075
|
+
className: PluginManagerPage_module_css_default.status,
|
|
1076
|
+
children: t("partsFilterEmpty")
|
|
1077
|
+
}) : null,
|
|
1078
|
+
shown.length === 0 ? null : (0, react_jsx_runtime.jsx)("ul", {
|
|
1079
|
+
className: PluginManagerPage_module_css_default.rows,
|
|
1080
|
+
children: shown.map((row) => (0, react_jsx_runtime.jsx)("li", {
|
|
1081
|
+
className: PluginManagerPage_module_css_default.row,
|
|
1082
|
+
"data-plugin-row": row.entryId ?? row.rowId,
|
|
1083
|
+
...row.phase === "failed" ? { "data-state": "failed" } : row.enabled ? {} : { "data-state": "off" },
|
|
1084
|
+
children: (0, react_jsx_runtime.jsxs)("div", {
|
|
1085
|
+
className: PluginManagerPage_module_css_default.rowLine,
|
|
1086
|
+
children: [
|
|
1087
|
+
(0, react_jsx_runtime.jsx)("span", {
|
|
1088
|
+
className: PluginManagerPage_module_css_default.rowIcon,
|
|
1089
|
+
"aria-hidden": "true",
|
|
1090
|
+
children: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconCordisPluginOutline14, {})
|
|
1091
|
+
}),
|
|
1092
|
+
(0, react_jsx_runtime.jsxs)("div", {
|
|
1093
|
+
className: PluginManagerPage_module_css_default.rowMain,
|
|
1094
|
+
children: [configure?.has(row) === true ? (0, react_jsx_runtime.jsxs)("button", {
|
|
1095
|
+
type: "button",
|
|
1096
|
+
className: PluginManagerPage_module_css_default.rowOpen,
|
|
1097
|
+
"aria-label": t("configureRow", { name: row.rowId }),
|
|
1098
|
+
onClick: () => {
|
|
1099
|
+
configure.open(row);
|
|
1100
|
+
},
|
|
1101
|
+
children: [(0, react_jsx_runtime.jsx)("span", {
|
|
1102
|
+
className: PluginManagerPage_module_css_default.rowId,
|
|
1103
|
+
children: row.rowId
|
|
1104
|
+
}), (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronRightOutline14, {
|
|
1105
|
+
className: PluginManagerPage_module_css_default.rowOpenIcon,
|
|
1106
|
+
"aria-hidden": "true"
|
|
1107
|
+
})]
|
|
1108
|
+
}) : (0, react_jsx_runtime.jsx)("span", {
|
|
1109
|
+
className: PluginManagerPage_module_css_default.rowId,
|
|
1110
|
+
children: row.rowId
|
|
1111
|
+
}), (0, react_jsx_runtime.jsx)("span", {
|
|
1112
|
+
className: PluginManagerPage_module_css_default.rowModule,
|
|
1113
|
+
children: row.moduleName
|
|
1114
|
+
})]
|
|
1115
|
+
}),
|
|
1116
|
+
(0, react_jsx_runtime.jsxs)("span", {
|
|
1117
|
+
className: PluginManagerPage_module_css_default.rowState,
|
|
1118
|
+
children: [(0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.StateDot, {
|
|
1119
|
+
state: rowDotState(row),
|
|
1120
|
+
size: 8
|
|
1121
|
+
}), rowStateText(row, t)]
|
|
1122
|
+
}),
|
|
1123
|
+
toggle === void 0 ? null : (0, react_jsx_runtime.jsx)(RowSwitch, {
|
|
1124
|
+
row,
|
|
1125
|
+
t,
|
|
1126
|
+
busy: toggle.busy(row),
|
|
1127
|
+
onChange: (enabled) => {
|
|
1128
|
+
toggle.onSetEnabled(row, enabled);
|
|
1129
|
+
}
|
|
1130
|
+
})
|
|
1131
|
+
]
|
|
1132
|
+
})
|
|
1133
|
+
}, row.rowId))
|
|
1134
|
+
})
|
|
1135
|
+
]
|
|
1136
|
+
});
|
|
1137
|
+
}
|
|
1138
|
+
/**
|
|
1139
|
+
* A bundle's enable switch on its card and its page: locked, saying why, for
|
|
1140
|
+
* one the Host protects; off and locked for one it cannot read.
|
|
1141
|
+
*/
|
|
1142
|
+
function EnableSwitch({ pkg, title, t, busy, onSetEnabled }) {
|
|
1143
|
+
return (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Switch, {
|
|
1144
|
+
checked: pkg.enabled,
|
|
1145
|
+
label: t("enableToggle", { name: title }),
|
|
1146
|
+
disabled: busy || pkg.readOnlyReason !== void 0 || !pkg.enabled && pkg.error !== void 0,
|
|
1147
|
+
...pkg.readOnlyReason === void 0 ? {} : { title: managementText({ code: pkg.readOnlyReason }, t) },
|
|
1148
|
+
onChange: onSetEnabled
|
|
1149
|
+
});
|
|
1150
|
+
}
|
|
1151
|
+
/** The status one card carries: running, off, or a problem the Host reported. */
|
|
1152
|
+
function packageStatus(pkg) {
|
|
1153
|
+
if (pkg.error !== void 0) return "problem";
|
|
1154
|
+
return pkg.enabled ? "running" : "disabled";
|
|
1155
|
+
}
|
|
1156
|
+
/** The head every card shares: the pinwheel icon, the name that opens the page beside its tags, its one-liner, and what sits at the end. */
|
|
1157
|
+
function CardHead({ title, t, onOpen, tags, description, end }) {
|
|
1158
|
+
return (0, react_jsx_runtime.jsxs)("div", {
|
|
1159
|
+
className: PluginManagerPage_module_css_default.cardHead,
|
|
1160
|
+
children: [
|
|
1161
|
+
(0, react_jsx_runtime.jsx)("span", {
|
|
1162
|
+
className: PluginManagerPage_module_css_default.cardIcon,
|
|
1163
|
+
"aria-hidden": "true",
|
|
1164
|
+
children: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconPluginPinwheelOutline16, { size: 20 })
|
|
1165
|
+
}),
|
|
1166
|
+
(0, react_jsx_runtime.jsxs)("div", {
|
|
1167
|
+
className: PluginManagerPage_module_css_default.cardMain,
|
|
1168
|
+
children: [(0, react_jsx_runtime.jsxs)("div", {
|
|
1169
|
+
className: PluginManagerPage_module_css_default.titleRow,
|
|
1170
|
+
children: [(0, react_jsx_runtime.jsx)("button", {
|
|
1171
|
+
type: "button",
|
|
1172
|
+
className: `${PluginManagerPage_module_css_default.cardTitle} ${PluginManagerPage_module_css_default.cardOpen}`,
|
|
1173
|
+
"aria-label": t("openDetail", { name: title }),
|
|
1174
|
+
onClick: onOpen,
|
|
1175
|
+
children: title
|
|
1176
|
+
}), tags]
|
|
1177
|
+
}), description === void 0 ? null : (0, react_jsx_runtime.jsx)("span", {
|
|
1178
|
+
className: PluginManagerPage_module_css_default.cardDesc,
|
|
1179
|
+
children: description
|
|
1180
|
+
})]
|
|
1181
|
+
}),
|
|
1182
|
+
end === void 0 ? null : (0, react_jsx_runtime.jsx)("div", {
|
|
1183
|
+
className: PluginManagerPage_module_css_default.cardEnd,
|
|
1184
|
+
children: end
|
|
1185
|
+
})
|
|
1186
|
+
]
|
|
1187
|
+
});
|
|
1188
|
+
}
|
|
1189
|
+
/** The top every page shares: the crumb that leads back, then the icon with the page's actions at its right. */
|
|
1190
|
+
function DetailTop({ crumbLabel, crumbText, onBack, icon, actions }) {
|
|
1191
|
+
return (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [(0, react_jsx_runtime.jsxs)("button", {
|
|
1192
|
+
type: "button",
|
|
1193
|
+
className: PluginManagerPage_module_css_default.crumb,
|
|
1194
|
+
"aria-label": crumbLabel,
|
|
1195
|
+
onClick: onBack,
|
|
1196
|
+
children: [(0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronDownOutline14, {
|
|
1197
|
+
className: PluginManagerPage_module_css_default.crumbIcon,
|
|
1198
|
+
"aria-hidden": "true"
|
|
1199
|
+
}), (0, react_jsx_runtime.jsx)("span", { children: crumbText })]
|
|
1200
|
+
}), (0, react_jsx_runtime.jsxs)("div", {
|
|
1201
|
+
className: PluginManagerPage_module_css_default.detailHead,
|
|
1202
|
+
children: [(0, react_jsx_runtime.jsx)("span", {
|
|
1203
|
+
className: PluginManagerPage_module_css_default.cardIcon,
|
|
1204
|
+
"aria-hidden": "true",
|
|
1205
|
+
children: icon ?? (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconPluginPinwheelOutline16, { size: 20 })
|
|
1206
|
+
}), actions]
|
|
1207
|
+
})] });
|
|
1208
|
+
}
|
|
1209
|
+
/** One package as a card that opens its page: its name, its one-liner, its tags, and its bundle switch. */
|
|
1210
|
+
function PackageCard({ pkg, t, busy, highlighted, onOpen, onSetEnabled }) {
|
|
1211
|
+
const { title, description, beta } = packageText(pkg, t);
|
|
1212
|
+
const status = packageStatus(pkg);
|
|
1213
|
+
return (0, react_jsx_runtime.jsx)("li", {
|
|
1214
|
+
className: `${PluginManagerPage_module_css_default.card} ${PluginManagerPage_module_css_default.cardLink}`,
|
|
1215
|
+
"data-plugin-package": pkg.name,
|
|
1216
|
+
"data-plugin-status": status,
|
|
1217
|
+
...highlighted ? { "data-plugin-highlight": "" } : {},
|
|
1218
|
+
children: (0, react_jsx_runtime.jsx)(CardHead, {
|
|
1219
|
+
title,
|
|
1220
|
+
t,
|
|
1221
|
+
onOpen,
|
|
1222
|
+
tags: (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [beta ? (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tag, {
|
|
1223
|
+
className: PluginManagerPage_module_css_default.statusTag,
|
|
1224
|
+
tone: "info",
|
|
1225
|
+
children: t("statusBeta")
|
|
1226
|
+
}) : null, status === "problem" ? (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tag, {
|
|
1227
|
+
className: PluginManagerPage_module_css_default.statusTag,
|
|
1228
|
+
tone: "danger",
|
|
1229
|
+
children: t("statusProblem")
|
|
1230
|
+
}) : null] }),
|
|
1231
|
+
description,
|
|
1232
|
+
end: (0, react_jsx_runtime.jsx)(EnableSwitch, {
|
|
1233
|
+
pkg,
|
|
1234
|
+
title,
|
|
1235
|
+
t,
|
|
1236
|
+
busy,
|
|
1237
|
+
onSetEnabled
|
|
1238
|
+
})
|
|
1239
|
+
})
|
|
1240
|
+
});
|
|
1241
|
+
}
|
|
1242
|
+
/**
|
|
1243
|
+
* One official plugin as a card that opens its page: its icon, its title from
|
|
1244
|
+
* the registration, and the one-liner the entry renders in its summary view.
|
|
1245
|
+
*/
|
|
1246
|
+
function ItemCard({ item, t, onOpen, renderSlot }) {
|
|
1247
|
+
return (0, react_jsx_runtime.jsx)("li", {
|
|
1248
|
+
className: `${PluginManagerPage_module_css_default.card} ${PluginManagerPage_module_css_default.cardLink}`,
|
|
1249
|
+
"data-plugin-item": item.id,
|
|
1250
|
+
children: (0, react_jsx_runtime.jsx)(CardHead, {
|
|
1251
|
+
title: item.label,
|
|
1252
|
+
t,
|
|
1253
|
+
onOpen,
|
|
1254
|
+
description: renderSlot("plugins.item", { view: "summary" }, { only: item.id })
|
|
1255
|
+
})
|
|
1256
|
+
});
|
|
1257
|
+
}
|
|
1258
|
+
/** An official plugin's page: the crumb back to the cards, its icon, its title over its one-liner, and the form the entry renders. */
|
|
1259
|
+
function ItemDetail({ item, t, onBack, renderSlot }) {
|
|
1260
|
+
return (0, react_jsx_runtime.jsxs)("div", {
|
|
1261
|
+
className: PluginManagerPage_module_css_default.detail,
|
|
1262
|
+
"data-plugin-item-detail": item.id,
|
|
1263
|
+
children: [
|
|
1264
|
+
(0, react_jsx_runtime.jsx)(DetailTop, {
|
|
1265
|
+
crumbLabel: t("backToList"),
|
|
1266
|
+
crumbText: t("crumbRoot"),
|
|
1267
|
+
onBack
|
|
1268
|
+
}),
|
|
1269
|
+
(0, react_jsx_runtime.jsxs)("div", {
|
|
1270
|
+
className: PluginManagerPage_module_css_default.detailMain,
|
|
1271
|
+
children: [(0, react_jsx_runtime.jsx)("div", {
|
|
1272
|
+
className: PluginManagerPage_module_css_default.titleRow,
|
|
1273
|
+
children: (0, react_jsx_runtime.jsx)("h3", {
|
|
1274
|
+
className: PluginManagerPage_module_css_default.detailTitle,
|
|
1275
|
+
children: item.label
|
|
1276
|
+
})
|
|
1277
|
+
}), (0, react_jsx_runtime.jsx)("p", {
|
|
1278
|
+
className: PluginManagerPage_module_css_default.detailDesc,
|
|
1279
|
+
children: renderSlot("plugins.item", { view: "summary" }, { only: item.id })
|
|
1280
|
+
})]
|
|
1281
|
+
}),
|
|
1282
|
+
(0, react_jsx_runtime.jsx)("div", {
|
|
1283
|
+
className: PluginManagerPage_module_css_default.detailSections,
|
|
1284
|
+
"data-plugin-config": true,
|
|
1285
|
+
children: renderSlot("plugins.item", { view: "page" }, { only: item.id })
|
|
1286
|
+
})
|
|
1287
|
+
]
|
|
1288
|
+
});
|
|
1289
|
+
}
|
|
1290
|
+
/**
|
|
1291
|
+
* A row's configuration page: the crumb back to its bundle's page, the row id
|
|
1292
|
+
* over the module it names and the entry's one-liner, and the form the entry renders.
|
|
1293
|
+
*/
|
|
1294
|
+
function RowDetail({ pkg, row, t, onBack, renderSlot }) {
|
|
1295
|
+
const { title } = packageText(pkg, t);
|
|
1296
|
+
const key = rowConfigKey(pkg.name, row.rowId);
|
|
1297
|
+
return (0, react_jsx_runtime.jsxs)("div", {
|
|
1298
|
+
className: PluginManagerPage_module_css_default.detail,
|
|
1299
|
+
"data-plugin-row-detail": key,
|
|
1300
|
+
children: [
|
|
1301
|
+
(0, react_jsx_runtime.jsx)(DetailTop, {
|
|
1302
|
+
crumbLabel: t("backToPackage", { name: title }),
|
|
1303
|
+
crumbText: title,
|
|
1304
|
+
onBack,
|
|
1305
|
+
icon: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconCordisPluginOutline14, { size: 20 })
|
|
1306
|
+
}),
|
|
1307
|
+
(0, react_jsx_runtime.jsxs)("div", {
|
|
1308
|
+
className: PluginManagerPage_module_css_default.detailMain,
|
|
1309
|
+
children: [
|
|
1310
|
+
(0, react_jsx_runtime.jsx)("div", {
|
|
1311
|
+
className: PluginManagerPage_module_css_default.titleRow,
|
|
1312
|
+
children: (0, react_jsx_runtime.jsx)("h3", {
|
|
1313
|
+
className: PluginManagerPage_module_css_default.detailTitle,
|
|
1314
|
+
children: row.rowId
|
|
1315
|
+
})
|
|
1316
|
+
}),
|
|
1317
|
+
(0, react_jsx_runtime.jsx)("p", {
|
|
1318
|
+
className: PluginManagerPage_module_css_default.detailName,
|
|
1319
|
+
children: (0, react_jsx_runtime.jsx)("code", { children: row.moduleName })
|
|
1320
|
+
}),
|
|
1321
|
+
(0, react_jsx_runtime.jsx)("p", {
|
|
1322
|
+
className: PluginManagerPage_module_css_default.detailDesc,
|
|
1323
|
+
children: renderSlot("plugins.row.config", { view: "summary" }, { entryKey: key })
|
|
1324
|
+
})
|
|
1325
|
+
]
|
|
1326
|
+
}),
|
|
1327
|
+
(0, react_jsx_runtime.jsx)("div", {
|
|
1328
|
+
className: PluginManagerPage_module_css_default.detailSections,
|
|
1329
|
+
"data-plugin-config": true,
|
|
1330
|
+
children: renderSlot("plugins.row.config", { view: "page" }, { entryKey: key })
|
|
1331
|
+
})
|
|
1332
|
+
]
|
|
1333
|
+
});
|
|
1334
|
+
}
|
|
1335
|
+
/**
|
|
1336
|
+
* One package's page: the crumb back to the list; its icon with its switch
|
|
1337
|
+
* and, for a package the profile installed, uninstall; its title beside its
|
|
1338
|
+
* version tag, its beta tag, and its problem tag; the package name the title
|
|
1339
|
+
* stands for, which is what installs it elsewhere; its one-liner; the Host's
|
|
1340
|
+
* problem when it reports one; the configuration the bundle registered for
|
|
1341
|
+
* itself; and its rows with their switches and configure controls.
|
|
1342
|
+
*/
|
|
1343
|
+
function PackageDetail({ pkg, t, busy, rowBusy, configured, configure, renderSlot, onBack, onSetEnabled, onUninstall, onSetRowEnabled }) {
|
|
1344
|
+
const { title, description, beta } = packageText(pkg, t);
|
|
1345
|
+
const status = packageStatus(pkg);
|
|
1346
|
+
return (0, react_jsx_runtime.jsxs)("div", {
|
|
1347
|
+
className: PluginManagerPage_module_css_default.detail,
|
|
1348
|
+
"data-plugin-detail": pkg.name,
|
|
1349
|
+
children: [
|
|
1350
|
+
(0, react_jsx_runtime.jsx)(DetailTop, {
|
|
1351
|
+
crumbLabel: t("backToList"),
|
|
1352
|
+
crumbText: t("crumbRoot"),
|
|
1353
|
+
onBack,
|
|
1354
|
+
actions: (0, react_jsx_runtime.jsxs)("div", {
|
|
1355
|
+
className: PluginManagerPage_module_css_default.detailActions,
|
|
1356
|
+
children: [pkg.installed ? (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
1357
|
+
variant: "outline",
|
|
1358
|
+
size: "sm",
|
|
1359
|
+
className: PluginManagerPage_module_css_default.danger,
|
|
1360
|
+
icon: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconTrashOutline16, { size: 13 }),
|
|
1361
|
+
"aria-label": t("uninstallLabel", { name: title }),
|
|
1362
|
+
disabled: busy || pkg.readOnlyReason !== void 0,
|
|
1363
|
+
onClick: onUninstall,
|
|
1364
|
+
children: t("uninstall")
|
|
1365
|
+
}) : null, (0, react_jsx_runtime.jsx)(EnableSwitch, {
|
|
1366
|
+
pkg,
|
|
1367
|
+
title,
|
|
1368
|
+
t,
|
|
1369
|
+
busy,
|
|
1370
|
+
onSetEnabled
|
|
1371
|
+
})]
|
|
1372
|
+
})
|
|
1373
|
+
}),
|
|
1374
|
+
(0, react_jsx_runtime.jsxs)("div", {
|
|
1375
|
+
className: PluginManagerPage_module_css_default.detailMain,
|
|
1376
|
+
children: [
|
|
1377
|
+
(0, react_jsx_runtime.jsxs)("div", {
|
|
1378
|
+
className: PluginManagerPage_module_css_default.titleRow,
|
|
1379
|
+
children: [
|
|
1380
|
+
(0, react_jsx_runtime.jsx)("h3", {
|
|
1381
|
+
className: PluginManagerPage_module_css_default.detailTitle,
|
|
1382
|
+
children: title
|
|
1383
|
+
}),
|
|
1384
|
+
pkg.version === void 0 ? null : (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tag, {
|
|
1385
|
+
className: PluginManagerPage_module_css_default.versionTag,
|
|
1386
|
+
tone: "neutral",
|
|
1387
|
+
children: t("versionTag", { version: pkg.version })
|
|
1388
|
+
}),
|
|
1389
|
+
beta ? (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tag, {
|
|
1390
|
+
className: PluginManagerPage_module_css_default.statusTag,
|
|
1391
|
+
tone: "info",
|
|
1392
|
+
children: t("statusBeta")
|
|
1393
|
+
}) : null,
|
|
1394
|
+
status === "problem" ? (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tag, {
|
|
1395
|
+
className: PluginManagerPage_module_css_default.statusTag,
|
|
1396
|
+
tone: "danger",
|
|
1397
|
+
children: t("statusProblem")
|
|
1398
|
+
}) : null
|
|
1399
|
+
]
|
|
1400
|
+
}),
|
|
1401
|
+
(0, react_jsx_runtime.jsx)("p", {
|
|
1402
|
+
className: PluginManagerPage_module_css_default.detailName,
|
|
1403
|
+
children: (0, react_jsx_runtime.jsx)("code", {
|
|
1404
|
+
"data-plugin-name": true,
|
|
1405
|
+
children: pkg.name
|
|
1406
|
+
})
|
|
1407
|
+
}),
|
|
1408
|
+
(0, react_jsx_runtime.jsx)("p", {
|
|
1409
|
+
className: PluginManagerPage_module_css_default.detailDesc,
|
|
1410
|
+
children: description ?? t("noDescription")
|
|
1411
|
+
})
|
|
1412
|
+
]
|
|
1413
|
+
}),
|
|
1414
|
+
pkg.error === void 0 ? null : (0, react_jsx_runtime.jsxs)("p", {
|
|
1415
|
+
className: PluginManagerPage_module_css_default.reason,
|
|
1416
|
+
role: "status",
|
|
1417
|
+
children: [
|
|
1418
|
+
t("reasonLabel"),
|
|
1419
|
+
": ",
|
|
1420
|
+
managementText(pkg.error, t)
|
|
1421
|
+
]
|
|
1422
|
+
}),
|
|
1423
|
+
pkg.readOnlyReason === void 0 ? null : (0, react_jsx_runtime.jsx)("p", {
|
|
1424
|
+
className: PluginManagerPage_module_css_default.reason,
|
|
1425
|
+
role: "status",
|
|
1426
|
+
children: managementText({ code: pkg.readOnlyReason }, t)
|
|
1427
|
+
}),
|
|
1428
|
+
(0, react_jsx_runtime.jsxs)("div", {
|
|
1429
|
+
className: PluginManagerPage_module_css_default.detailSections,
|
|
1430
|
+
children: [configured ? (0, react_jsx_runtime.jsx)("section", {
|
|
1431
|
+
className: PluginManagerPage_module_css_default.detailSection,
|
|
1432
|
+
"data-plugin-config": true,
|
|
1433
|
+
children: renderSlot("plugins.bundle.config", { view: "page" }, { entryKey: pkg.name })
|
|
1434
|
+
}) : null, (0, react_jsx_runtime.jsx)(RowsSection, {
|
|
1435
|
+
rows: pkg.rows,
|
|
1436
|
+
t,
|
|
1437
|
+
toggle: pkg.enabled ? {
|
|
1438
|
+
busy: (row) => busy || rowBusy(row),
|
|
1439
|
+
onSetEnabled: onSetRowEnabled
|
|
1440
|
+
} : void 0,
|
|
1441
|
+
configure
|
|
1442
|
+
})]
|
|
1443
|
+
})
|
|
1444
|
+
]
|
|
1445
|
+
});
|
|
1446
|
+
}
|
|
1447
|
+
/** Output lines an install run's terminal shows before its middle folds: the first and last six of a long pnpm log. */
|
|
1448
|
+
const INSTALL_TERMINAL_LINES = 12;
|
|
1449
|
+
/** The install terminal's display copy, from the tab's dictionary. */
|
|
1450
|
+
function terminalLabels(t) {
|
|
1451
|
+
return {
|
|
1452
|
+
/* v8 ignore next -- the Host reports a killed pnpm as a null exit code, never a signal name; the label interface needs one */
|
|
1453
|
+
signal: (signal) => t("terminalSignal", { signal }),
|
|
1454
|
+
exitCode: (code) => t("terminalExitCode", { code: String(code) }),
|
|
1455
|
+
noExitCode: t("terminalNoExitCode"),
|
|
1456
|
+
running: t("terminalRunning"),
|
|
1457
|
+
failed: t("terminalFailed"),
|
|
1458
|
+
done: t("terminalDone"),
|
|
1459
|
+
copy: t("terminalCopy"),
|
|
1460
|
+
copied: t("terminalCopied"),
|
|
1461
|
+
noOutput: t("terminalNoOutput"),
|
|
1462
|
+
collapseAria: t("terminalCollapseAria"),
|
|
1463
|
+
collapse: t("terminalCollapse"),
|
|
1464
|
+
expandAria: (hidden) => t("terminalExpandAria", { n: String(hidden) }),
|
|
1465
|
+
expand: (hidden) => t("terminalExpand", { n: String(hidden) })
|
|
1466
|
+
};
|
|
1467
|
+
}
|
|
1468
|
+
/** The sentence under the field for a spec the check refused. */
|
|
1469
|
+
const INPUT_PROBLEM_KEYS = {
|
|
1470
|
+
"invalid-spec": "installProblemInvalid",
|
|
1471
|
+
"already-installed": "installProblemInstalled",
|
|
1472
|
+
"not-found": "installProblemNotFound",
|
|
1473
|
+
"not-a-package": "installProblemNotPackage",
|
|
1474
|
+
"not-a-bundle": "installProblemNotBundle",
|
|
1475
|
+
"network": "installProblemNetwork",
|
|
1476
|
+
"unknown": "installProblemUnknown"
|
|
1477
|
+
};
|
|
1478
|
+
/** The spec forms the install guide shows, each with an example the person can drop into the field. */
|
|
1479
|
+
const GUIDE_EXAMPLES = [
|
|
1480
|
+
{
|
|
1481
|
+
key: "id",
|
|
1482
|
+
titleKey: "installGuideIdTitle",
|
|
1483
|
+
exampleKey: "installGuideIdExample",
|
|
1484
|
+
hintKey: "installGuideIdHint"
|
|
1485
|
+
},
|
|
1486
|
+
{
|
|
1487
|
+
key: "git",
|
|
1488
|
+
titleKey: "installGuideGitTitle",
|
|
1489
|
+
exampleKey: "installGuideGitExample",
|
|
1490
|
+
hintKey: "installGuideGitHint"
|
|
1491
|
+
},
|
|
1492
|
+
{
|
|
1493
|
+
key: "path",
|
|
1494
|
+
titleKey: "installGuidePathTitle",
|
|
1495
|
+
exampleKey: "installGuidePathExample",
|
|
1496
|
+
hintKey: "installGuidePathHint"
|
|
1497
|
+
}
|
|
1498
|
+
];
|
|
1499
|
+
/** The one-line reading of a classified pnpm failure. */
|
|
1500
|
+
const FAILURE_KIND_KEYS = {
|
|
1501
|
+
"pnpm-missing": "installFailurePnpmMissing",
|
|
1502
|
+
"timeout": "installFailureTimeout",
|
|
1503
|
+
"not-found": "installFailureNotFound",
|
|
1504
|
+
"no-matching-version": "installFailureNoMatchingVersion",
|
|
1505
|
+
"network": "installFailureNetwork",
|
|
1506
|
+
"disk-full": "installFailureDiskFull",
|
|
1507
|
+
"permission": "installFailurePermission",
|
|
1508
|
+
"build-blocked": "installFailureBuildBlocked",
|
|
1509
|
+
"integrity": "installFailureIntegrity",
|
|
1510
|
+
"unknown": "installFailureGeneric"
|
|
1511
|
+
};
|
|
1512
|
+
/** The heading of each screen past the spec. */
|
|
1513
|
+
const SCREEN_TITLE_KEYS = {
|
|
1514
|
+
starting: "installStarting",
|
|
1515
|
+
running: "installingTitle",
|
|
1516
|
+
cancelling: "installCancelling",
|
|
1517
|
+
applying: "installApplying",
|
|
1518
|
+
done: "installedTitle",
|
|
1519
|
+
failed: "installFailedTitle"
|
|
1520
|
+
};
|
|
1521
|
+
/** What the spec's kind reads as when the package carries no description of its own. */
|
|
1522
|
+
const SUBJECT_KIND_KEYS = {
|
|
1523
|
+
registry: void 0,
|
|
1524
|
+
path: "installSubjectPath",
|
|
1525
|
+
git: "installSubjectGit",
|
|
1526
|
+
tarball: "installSubjectTarball"
|
|
1527
|
+
};
|
|
1528
|
+
/**
|
|
1529
|
+
* The failed screen's one line: a pnpm failure by its kind, a refusal by its
|
|
1530
|
+
* code, any other failure in the Host's words; the run's output stays behind the details.
|
|
1531
|
+
*/
|
|
1532
|
+
function failureText(failure, t) {
|
|
1533
|
+
if (failure === null) return t("installFailureGeneric");
|
|
1534
|
+
if (failure.kind === "build-blocked" && !failure.pendingBuilds?.length) return t("installFailureBuildBlockedManual");
|
|
1535
|
+
if (failure.kind !== void 0) return t(FAILURE_KIND_KEYS[failure.kind]);
|
|
1536
|
+
if (failure.code !== void 0) return managementText({
|
|
1537
|
+
code: failure.code,
|
|
1538
|
+
diagnostic: failure.reason
|
|
1539
|
+
}, t);
|
|
1540
|
+
return failure.reason === "" ? t("installFailureGeneric") : failure.reason;
|
|
1541
|
+
}
|
|
1542
|
+
/** The package the install is about: its name, one-liner, and version, as the Host read them before installing. */
|
|
1543
|
+
function SubjectCard({ subject, t }) {
|
|
1544
|
+
const title = subject.name ?? subject.spec;
|
|
1545
|
+
const kindKey = SUBJECT_KIND_KEYS[subject.kind];
|
|
1546
|
+
const description = subject.description ?? (kindKey === void 0 ? void 0 : t(kindKey));
|
|
1547
|
+
return (0, react_jsx_runtime.jsxs)("div", {
|
|
1548
|
+
className: PluginManagerPage_module_css_default.subject,
|
|
1549
|
+
"data-install-subject": subject.spec,
|
|
1550
|
+
children: [
|
|
1551
|
+
(0, react_jsx_runtime.jsx)("p", {
|
|
1552
|
+
className: PluginManagerPage_module_css_default.subjectName,
|
|
1553
|
+
children: title
|
|
1554
|
+
}),
|
|
1555
|
+
description === void 0 ? null : (0, react_jsx_runtime.jsx)("p", {
|
|
1556
|
+
className: PluginManagerPage_module_css_default.subjectDesc,
|
|
1557
|
+
children: description
|
|
1558
|
+
}),
|
|
1559
|
+
subject.version === void 0 ? null : (0, react_jsx_runtime.jsx)("p", {
|
|
1560
|
+
className: PluginManagerPage_module_css_default.subjectMeta,
|
|
1561
|
+
children: t("installVersion", { version: subject.version })
|
|
1562
|
+
})
|
|
1563
|
+
]
|
|
1564
|
+
});
|
|
1565
|
+
}
|
|
1566
|
+
/**
|
|
1567
|
+
* The install dialog: the spec and its check, then the installing, installed,
|
|
1568
|
+
* and failed screens over the same subject card. A failed run that left
|
|
1569
|
+
* install scripts undecided shows them for approval in place of plain retry.
|
|
1570
|
+
*/
|
|
1571
|
+
function InstallDialog({ install, t, onClose, onEditSpec, onRun, onCancel, onCancelAndClose, onToggleDetails, onEnableNow, onApproveBuilds }) {
|
|
1572
|
+
const errorId = (0, react.useId)();
|
|
1573
|
+
const guideId = (0, react.useId)();
|
|
1574
|
+
const approvalId = (0, react.useId)();
|
|
1575
|
+
const [guideOpen, setGuideOpen] = (0, react.useState)(false);
|
|
1576
|
+
const { phase } = install;
|
|
1577
|
+
if (phase === "idle" || phase === "checking") {
|
|
1578
|
+
const checking = phase === "checking";
|
|
1579
|
+
const empty = install.spec.trim() === "";
|
|
1580
|
+
return (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Modal, {
|
|
1581
|
+
open: install.open,
|
|
1582
|
+
onClose,
|
|
1583
|
+
title: t("installTitle"),
|
|
1584
|
+
closeLabel: t("close"),
|
|
1585
|
+
description: t("installDescription"),
|
|
1586
|
+
className: PluginManagerPage_module_css_default.installDialog,
|
|
1587
|
+
footer: (0, react_jsx_runtime.jsxs)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
1588
|
+
variant: "primary",
|
|
1589
|
+
className: PluginManagerPage_module_css_default.wide,
|
|
1590
|
+
disabled: checking || empty,
|
|
1591
|
+
"aria-busy": checking,
|
|
1592
|
+
onClick: onRun,
|
|
1593
|
+
children: [checking ? (0, react_jsx_runtime.jsx)("span", {
|
|
1594
|
+
className: PluginManagerPage_module_css_default.spinner,
|
|
1595
|
+
"aria-hidden": "true"
|
|
1596
|
+
}) : null, t(checking ? "installChecking" : "installRun")]
|
|
1597
|
+
}),
|
|
1598
|
+
children: (0, react_jsx_runtime.jsxs)("div", {
|
|
1599
|
+
className: PluginManagerPage_module_css_default.installBody,
|
|
1600
|
+
children: [
|
|
1601
|
+
(0, react_jsx_runtime.jsxs)("label", {
|
|
1602
|
+
className: PluginManagerPage_module_css_default.installField,
|
|
1603
|
+
children: [(0, react_jsx_runtime.jsx)("span", { children: t("installSpecLabel") }), (0, react_jsx_runtime.jsx)("input", {
|
|
1604
|
+
type: "text",
|
|
1605
|
+
value: install.spec,
|
|
1606
|
+
placeholder: t("installSpecPlaceholder"),
|
|
1607
|
+
disabled: checking,
|
|
1608
|
+
"aria-invalid": install.inputError !== null,
|
|
1609
|
+
"aria-describedby": install.inputError === null ? void 0 : errorId,
|
|
1610
|
+
onChange: (event) => {
|
|
1611
|
+
onEditSpec(event.currentTarget.value);
|
|
1612
|
+
},
|
|
1613
|
+
onKeyDown: (event) => {
|
|
1614
|
+
if (event.key === "Enter" && !empty && !checking) onRun();
|
|
1615
|
+
}
|
|
1616
|
+
})]
|
|
1617
|
+
}),
|
|
1618
|
+
install.inputError === null ? null : (0, react_jsx_runtime.jsx)("p", {
|
|
1619
|
+
id: errorId,
|
|
1620
|
+
className: PluginManagerPage_module_css_default.inputError,
|
|
1621
|
+
role: "alert",
|
|
1622
|
+
children: t(INPUT_PROBLEM_KEYS[install.inputError.problem], { reason: install.inputError.reason })
|
|
1623
|
+
}),
|
|
1624
|
+
(0, react_jsx_runtime.jsxs)("button", {
|
|
1625
|
+
type: "button",
|
|
1626
|
+
className: PluginManagerPage_module_css_default.guideToggle,
|
|
1627
|
+
"aria-expanded": guideOpen,
|
|
1628
|
+
"aria-controls": guideId,
|
|
1629
|
+
onClick: () => {
|
|
1630
|
+
setGuideOpen((open) => !open);
|
|
1631
|
+
},
|
|
1632
|
+
children: [(0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronDownOutline14, {
|
|
1633
|
+
className: PluginManagerPage_module_css_default.guideChevron,
|
|
1634
|
+
"aria-hidden": "true"
|
|
1635
|
+
}), (0, react_jsx_runtime.jsx)("span", { children: t(guideOpen ? "installGuideHide" : "installGuideToggle") })]
|
|
1636
|
+
}),
|
|
1637
|
+
guideOpen ? (0, react_jsx_runtime.jsxs)("div", {
|
|
1638
|
+
id: guideId,
|
|
1639
|
+
className: PluginManagerPage_module_css_default.guide,
|
|
1640
|
+
"data-install-guide": true,
|
|
1641
|
+
children: [
|
|
1642
|
+
(0, react_jsx_runtime.jsx)("p", {
|
|
1643
|
+
className: PluginManagerPage_module_css_default.guideIntro,
|
|
1644
|
+
children: t("installGuideIntro")
|
|
1645
|
+
}),
|
|
1646
|
+
(0, react_jsx_runtime.jsx)("p", {
|
|
1647
|
+
className: PluginManagerPage_module_css_default.guideNote,
|
|
1648
|
+
children: t("installGuideIdNote")
|
|
1649
|
+
}),
|
|
1650
|
+
(0, react_jsx_runtime.jsx)("ol", {
|
|
1651
|
+
className: PluginManagerPage_module_css_default.guideList,
|
|
1652
|
+
children: GUIDE_EXAMPLES.map(({ key, titleKey, exampleKey, hintKey }, index) => (0, react_jsx_runtime.jsxs)("li", {
|
|
1653
|
+
className: PluginManagerPage_module_css_default.guideItem,
|
|
1654
|
+
children: [
|
|
1655
|
+
(0, react_jsx_runtime.jsx)("span", {
|
|
1656
|
+
className: PluginManagerPage_module_css_default.guideIndex,
|
|
1657
|
+
"aria-hidden": "true",
|
|
1658
|
+
children: index + 1
|
|
1659
|
+
}),
|
|
1660
|
+
(0, react_jsx_runtime.jsxs)("div", {
|
|
1661
|
+
className: PluginManagerPage_module_css_default.guideMain,
|
|
1662
|
+
children: [
|
|
1663
|
+
(0, react_jsx_runtime.jsx)("span", {
|
|
1664
|
+
className: PluginManagerPage_module_css_default.guideTitle,
|
|
1665
|
+
children: t(titleKey)
|
|
1666
|
+
}),
|
|
1667
|
+
(0, react_jsx_runtime.jsxs)("span", {
|
|
1668
|
+
className: PluginManagerPage_module_css_default.guideExample,
|
|
1669
|
+
children: [(0, react_jsx_runtime.jsx)("span", {
|
|
1670
|
+
className: PluginManagerPage_module_css_default.guideExampleLabel,
|
|
1671
|
+
children: t("installGuideExampleLabel")
|
|
1672
|
+
}), (0, react_jsx_runtime.jsx)("code", { children: t(exampleKey) })]
|
|
1673
|
+
}),
|
|
1674
|
+
(0, react_jsx_runtime.jsx)("span", {
|
|
1675
|
+
className: PluginManagerPage_module_css_default.guideHint,
|
|
1676
|
+
children: t(hintKey)
|
|
1677
|
+
})
|
|
1678
|
+
]
|
|
1679
|
+
}),
|
|
1680
|
+
(0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
1681
|
+
variant: "outline",
|
|
1682
|
+
size: "sm",
|
|
1683
|
+
"aria-label": t("installGuideFillAria", { example: t(exampleKey) }),
|
|
1684
|
+
disabled: checking,
|
|
1685
|
+
onClick: () => {
|
|
1686
|
+
onEditSpec(t(exampleKey));
|
|
1687
|
+
},
|
|
1688
|
+
children: t("installGuideFill")
|
|
1689
|
+
})
|
|
1690
|
+
]
|
|
1691
|
+
}, key))
|
|
1692
|
+
}),
|
|
1693
|
+
(0, react_jsx_runtime.jsxs)("p", {
|
|
1694
|
+
className: PluginManagerPage_module_css_default.guideSafety,
|
|
1695
|
+
role: "note",
|
|
1696
|
+
children: [(0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconWarningOutline16, {
|
|
1697
|
+
size: 14,
|
|
1698
|
+
"aria-hidden": "true"
|
|
1699
|
+
}), (0, react_jsx_runtime.jsx)("span", { children: t("installGuideSafety") })]
|
|
1700
|
+
})
|
|
1701
|
+
]
|
|
1702
|
+
}) : null
|
|
1703
|
+
]
|
|
1704
|
+
})
|
|
1705
|
+
});
|
|
1706
|
+
}
|
|
1707
|
+
const heading = t(SCREEN_TITLE_KEYS[phase]);
|
|
1708
|
+
const pending = isInstallPending(phase);
|
|
1709
|
+
const stoppable = phase === "running" || phase === "failed";
|
|
1710
|
+
const unconfirmed = install.failure?.cancelUnconfirmed === true ? install.failure.reason : void 0;
|
|
1711
|
+
const pendingBuilds = phase === "failed" ? install.failure?.pendingBuilds ?? [] : [];
|
|
1712
|
+
const approvable = pendingBuilds.length > 0;
|
|
1713
|
+
const firstRun = install.runs[0];
|
|
1714
|
+
return (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Modal, {
|
|
1715
|
+
open: install.open,
|
|
1716
|
+
onClose,
|
|
1717
|
+
title: heading,
|
|
1718
|
+
headless: true,
|
|
1719
|
+
className: PluginManagerPage_module_css_default.installDialog,
|
|
1720
|
+
children: (0, react_jsx_runtime.jsxs)("div", {
|
|
1721
|
+
className: PluginManagerPage_module_css_default.wizard,
|
|
1722
|
+
"data-install-phase": phase,
|
|
1723
|
+
children: [(0, react_jsx_runtime.jsxs)("div", {
|
|
1724
|
+
className: PluginManagerPage_module_css_default.wizardHead,
|
|
1725
|
+
children: [phase === "done" ? (0, react_jsx_runtime.jsx)("span", {}) : (0, react_jsx_runtime.jsxs)("button", {
|
|
1726
|
+
type: "button",
|
|
1727
|
+
className: PluginManagerPage_module_css_default.wizardBack,
|
|
1728
|
+
"aria-label": t("installEditAria"),
|
|
1729
|
+
disabled: !stoppable,
|
|
1730
|
+
onClick: onCancel,
|
|
1731
|
+
children: [(0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronLeftOutline14, { "aria-hidden": "true" }), (0, react_jsx_runtime.jsx)("span", { children: t("installEdit") })]
|
|
1732
|
+
}), (0, react_jsx_runtime.jsx)("button", {
|
|
1733
|
+
type: "button",
|
|
1734
|
+
className: PluginManagerPage_module_css_default.wizardClose,
|
|
1735
|
+
"aria-label": t(phase === "running" ? "installCloseCancels" : "close"),
|
|
1736
|
+
disabled: pending && phase !== "running",
|
|
1737
|
+
onClick: phase === "running" ? onCancelAndClose : onClose,
|
|
1738
|
+
children: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconCloseOutline16, { size: 14 })
|
|
1739
|
+
})]
|
|
1740
|
+
}), (0, react_jsx_runtime.jsxs)("div", {
|
|
1741
|
+
className: PluginManagerPage_module_css_default.wizardScroll,
|
|
1742
|
+
children: [
|
|
1743
|
+
(0, react_jsx_runtime.jsxs)("div", {
|
|
1744
|
+
className: PluginManagerPage_module_css_default.wizardHero,
|
|
1745
|
+
children: [
|
|
1746
|
+
(0, react_jsx_runtime.jsx)("span", {
|
|
1747
|
+
className: PluginManagerPage_module_css_default.wizardIcon,
|
|
1748
|
+
"data-tone": pending ? "pending" : phase,
|
|
1749
|
+
"aria-hidden": "true",
|
|
1750
|
+
children: pending ? (0, react_jsx_runtime.jsx)("span", { className: PluginManagerPage_module_css_default.spinnerLarge }) : phase === "done" ? (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconCheckOutline16, { size: 28 }) : (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconWarningOutline16, { size: 28 })
|
|
1751
|
+
}),
|
|
1752
|
+
(0, react_jsx_runtime.jsx)("h2", {
|
|
1753
|
+
className: PluginManagerPage_module_css_default.wizardTitle,
|
|
1754
|
+
role: phase === "failed" ? "alert" : "status",
|
|
1755
|
+
children: heading
|
|
1756
|
+
}),
|
|
1757
|
+
phase === "failed" ? (0, react_jsx_runtime.jsx)("p", {
|
|
1758
|
+
className: PluginManagerPage_module_css_default.wizardSub,
|
|
1759
|
+
children: failureText(install.failure, t)
|
|
1760
|
+
}) : null,
|
|
1761
|
+
unconfirmed === void 0 ? null : (0, react_jsx_runtime.jsx)("p", {
|
|
1762
|
+
className: PluginManagerPage_module_css_default.wizardSub,
|
|
1763
|
+
role: "alert",
|
|
1764
|
+
children: t("installCancelUnconfirmed", { reason: unconfirmed })
|
|
1765
|
+
})
|
|
1766
|
+
]
|
|
1767
|
+
}),
|
|
1768
|
+
install.subject === null ? null : (0, react_jsx_runtime.jsx)(SubjectCard, {
|
|
1769
|
+
subject: install.subject,
|
|
1770
|
+
t
|
|
1771
|
+
}),
|
|
1772
|
+
approvable ? (0, react_jsx_runtime.jsxs)("section", {
|
|
1773
|
+
className: PluginManagerPage_module_css_default.approval,
|
|
1774
|
+
role: "group",
|
|
1775
|
+
"aria-labelledby": approvalId,
|
|
1776
|
+
"data-install-approval": true,
|
|
1777
|
+
children: [
|
|
1778
|
+
(0, react_jsx_runtime.jsx)("h3", {
|
|
1779
|
+
id: approvalId,
|
|
1780
|
+
className: PluginManagerPage_module_css_default.approvalTitle,
|
|
1781
|
+
children: t("installApprovalTitle")
|
|
1782
|
+
}),
|
|
1783
|
+
(0, react_jsx_runtime.jsx)("p", {
|
|
1784
|
+
className: PluginManagerPage_module_css_default.approvalText,
|
|
1785
|
+
children: t("installApprovalDescription")
|
|
1786
|
+
}),
|
|
1787
|
+
(0, react_jsx_runtime.jsx)("ul", {
|
|
1788
|
+
className: PluginManagerPage_module_css_default.approvalList,
|
|
1789
|
+
children: pendingBuilds.map((name) => (0, react_jsx_runtime.jsx)("li", { children: (0, react_jsx_runtime.jsx)("code", { children: name }) }, name))
|
|
1790
|
+
}),
|
|
1791
|
+
(0, react_jsx_runtime.jsx)("p", {
|
|
1792
|
+
className: PluginManagerPage_module_css_default.approvalText,
|
|
1793
|
+
children: t("installApprovalConsequence")
|
|
1794
|
+
}),
|
|
1795
|
+
(0, react_jsx_runtime.jsx)("p", {
|
|
1796
|
+
className: PluginManagerPage_module_css_default.approvalCaution,
|
|
1797
|
+
children: t("installApprovalCaution")
|
|
1798
|
+
}),
|
|
1799
|
+
(0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
1800
|
+
variant: "primary",
|
|
1801
|
+
className: PluginManagerPage_module_css_default.wide,
|
|
1802
|
+
onClick: onApproveBuilds,
|
|
1803
|
+
children: t("installApproveAndRetry")
|
|
1804
|
+
})
|
|
1805
|
+
]
|
|
1806
|
+
}) : null,
|
|
1807
|
+
phase === "done" && install.installed === null ? (0, react_jsx_runtime.jsx)("p", {
|
|
1808
|
+
className: PluginManagerPage_module_css_default.result,
|
|
1809
|
+
role: "status",
|
|
1810
|
+
children: t("installDoneNothing")
|
|
1811
|
+
}) : null,
|
|
1812
|
+
phase === "done" && install.restartRequired ? (0, react_jsx_runtime.jsx)("p", {
|
|
1813
|
+
className: PluginManagerPage_module_css_default.resultWarn,
|
|
1814
|
+
role: "status",
|
|
1815
|
+
children: t("installDoneRestart")
|
|
1816
|
+
}) : null,
|
|
1817
|
+
phase === "done" && install.approvedBuilds.length > 0 ? (0, react_jsx_runtime.jsx)("p", {
|
|
1818
|
+
className: PluginManagerPage_module_css_default.result,
|
|
1819
|
+
role: "status",
|
|
1820
|
+
children: t("installDoneApproved", { names: install.approvedBuilds.join(", ") })
|
|
1821
|
+
}) : null,
|
|
1822
|
+
(0, react_jsx_runtime.jsxs)("div", {
|
|
1823
|
+
className: PluginManagerPage_module_css_default.wizardFoot,
|
|
1824
|
+
children: [
|
|
1825
|
+
(0, react_jsx_runtime.jsxs)("button", {
|
|
1826
|
+
type: "button",
|
|
1827
|
+
className: PluginManagerPage_module_css_default.detailsToggle,
|
|
1828
|
+
"aria-expanded": install.detailsOpen,
|
|
1829
|
+
onClick: onToggleDetails,
|
|
1830
|
+
children: [(0, react_jsx_runtime.jsx)("span", { children: t(install.detailsOpen ? "installDetailsHide" : "installDetailsShow") }), (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronDownOutline14, {
|
|
1831
|
+
className: PluginManagerPage_module_css_default.detailsChevron,
|
|
1832
|
+
"aria-hidden": "true"
|
|
1833
|
+
})]
|
|
1834
|
+
}),
|
|
1835
|
+
pending ? (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
1836
|
+
variant: "outline",
|
|
1837
|
+
size: "sm",
|
|
1838
|
+
disabled: phase !== "running",
|
|
1839
|
+
onClick: onCancel,
|
|
1840
|
+
children: t(phase === "cancelling" ? "installCancelling" : "installCancel")
|
|
1841
|
+
}) : null,
|
|
1842
|
+
phase === "failed" && !approvable ? (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
1843
|
+
variant: "primary",
|
|
1844
|
+
size: "sm",
|
|
1845
|
+
onClick: onRun,
|
|
1846
|
+
children: t("installRetry")
|
|
1847
|
+
}) : null
|
|
1848
|
+
]
|
|
1849
|
+
}),
|
|
1850
|
+
install.detailsOpen ? (0, react_jsx_runtime.jsxs)("div", {
|
|
1851
|
+
className: PluginManagerPage_module_css_default.detailsBody,
|
|
1852
|
+
children: [(0, react_jsx_runtime.jsx)("p", {
|
|
1853
|
+
className: PluginManagerPage_module_css_default.installLocation,
|
|
1854
|
+
children: firstRun === void 0 ? t("terminalNoOutput") : t("installLocation", { dir: firstRun.cwd })
|
|
1855
|
+
}), install.runs.map((run) => (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.TerminalBlock, {
|
|
1856
|
+
command: run.command,
|
|
1857
|
+
output: run.output,
|
|
1858
|
+
running: run.exitCode === void 0,
|
|
1859
|
+
exitCode: run.exitCode,
|
|
1860
|
+
maxLines: INSTALL_TERMINAL_LINES,
|
|
1861
|
+
labels: {
|
|
1862
|
+
...terminalLabels(t),
|
|
1863
|
+
...phase === "cancelling" ? { failed: t("installCancelledShort") } : {}
|
|
1864
|
+
},
|
|
1865
|
+
className: PluginManagerPage_module_css_default.terminal
|
|
1866
|
+
}, run.jobId))]
|
|
1867
|
+
}) : null,
|
|
1868
|
+
phase !== "done" ? null : install.installed !== null ? (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
1869
|
+
variant: "primary",
|
|
1870
|
+
className: PluginManagerPage_module_css_default.wide,
|
|
1871
|
+
disabled: install.enabling,
|
|
1872
|
+
"aria-busy": install.enabling,
|
|
1873
|
+
onClick: onEnableNow,
|
|
1874
|
+
children: t("installEnableNow")
|
|
1875
|
+
}) : (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
1876
|
+
variant: "primary",
|
|
1877
|
+
className: PluginManagerPage_module_css_default.wide,
|
|
1878
|
+
onClick: onClose,
|
|
1879
|
+
children: t("installClose")
|
|
1880
|
+
})
|
|
1881
|
+
]
|
|
1882
|
+
})]
|
|
1883
|
+
})
|
|
1884
|
+
});
|
|
1885
|
+
}
|
|
1886
|
+
/** The confirmation an uninstall waits on. */
|
|
1887
|
+
function ConfirmDialog({ confirm, t, onConfirm, onCancel }) {
|
|
1888
|
+
const { title: name } = packageText({ name: confirm.packageName }, t);
|
|
1889
|
+
return (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Modal, {
|
|
1890
|
+
open: true,
|
|
1891
|
+
onClose: onCancel,
|
|
1892
|
+
title: t("confirmUninstallTitle", { name }),
|
|
1893
|
+
closeLabel: t("close"),
|
|
1894
|
+
description: t("confirmUninstallDescription"),
|
|
1895
|
+
footer: (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [(0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
1896
|
+
variant: "outline",
|
|
1897
|
+
onClick: onCancel,
|
|
1898
|
+
children: t("cancel")
|
|
1899
|
+
}), (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
1900
|
+
variant: "primary",
|
|
1901
|
+
className: PluginManagerPage_module_css_default.dangerButton,
|
|
1902
|
+
onClick: onConfirm,
|
|
1903
|
+
children: t("confirmUninstall")
|
|
1904
|
+
})] })
|
|
1905
|
+
});
|
|
1906
|
+
}
|
|
1907
|
+
/** Render the plugin manager: the official plugins and installed bundles, their pages, the install dialog, and the confirmation. */
|
|
1908
|
+
function PluginManagerPage(props) {
|
|
1909
|
+
const { t, ensure, renderSlot } = props;
|
|
1910
|
+
const state = props.usePluginManager((snapshot) => snapshot);
|
|
1911
|
+
const ledger = props.useConfigLedger((snapshot) => snapshot);
|
|
1912
|
+
const [view, setView] = (0, react.useState)({ kind: "list" });
|
|
1913
|
+
(0, react.useEffect)(() => {
|
|
1914
|
+
ensure();
|
|
1915
|
+
}, [ensure]);
|
|
1916
|
+
const { highlight, clearHighlight } = {
|
|
1917
|
+
highlight: state.highlight,
|
|
1918
|
+
clearHighlight: props.clearHighlight
|
|
1919
|
+
};
|
|
1920
|
+
(0, react.useEffect)(() => {
|
|
1921
|
+
if (highlight === null) return;
|
|
1922
|
+
const card = document.querySelector(`[data-plugin-package="${highlight}"]`);
|
|
1923
|
+
if (card !== null && typeof card.scrollIntoView === "function") card.scrollIntoView({
|
|
1924
|
+
block: "center",
|
|
1925
|
+
behavior: "smooth"
|
|
1926
|
+
});
|
|
1927
|
+
const timer = setTimeout(clearHighlight, HIGHLIGHT_MS);
|
|
1928
|
+
return () => {
|
|
1929
|
+
clearTimeout(timer);
|
|
1930
|
+
};
|
|
1931
|
+
}, [highlight, clearHighlight]);
|
|
1932
|
+
const noticeLine = state.notice === null ? null : noticeText(state.notice, t);
|
|
1933
|
+
const listed = state.packages.filter((pkg) => !BUILTIN_PROFILE_BUNDLES.has(pkg.name) && (pkg.installed || pkg.optional || pkg.error !== void 0));
|
|
1934
|
+
const mine = listed.filter((pkg) => pkg.installed || !pkg.optional);
|
|
1935
|
+
const official = listed.filter((pkg) => pkg.optional && !pkg.installed);
|
|
1936
|
+
const loaded = state.status === "ready" || state.status === "error";
|
|
1937
|
+
const openPkg = view.kind === "package" || view.kind === "row" ? listed.find((pkg) => pkg.name === view.name) : void 0;
|
|
1938
|
+
const openItem = view.kind === "item" ? ledger.items.find((item) => item.id === view.id) : void 0;
|
|
1939
|
+
const openRow = view.kind === "row" && openPkg !== void 0 ? openPkg.rows.find((row) => row.rowId === view.rowId) : void 0;
|
|
1940
|
+
const showsCards = openPkg === void 0 && openItem === void 0;
|
|
1941
|
+
const setRowEnabled = (row, enabled) => {
|
|
1942
|
+
/* v8 ignore next -- a row without a live entry has its switch disabled */
|
|
1943
|
+
if (row.entryId !== void 0) props.setRowEnabled(row.entryId, enabled);
|
|
1944
|
+
};
|
|
1945
|
+
const configure = (pkg) => ({
|
|
1946
|
+
has: (row) => ledger.rows.has(rowConfigKey(pkg.name, row.rowId)),
|
|
1947
|
+
open: (row) => {
|
|
1948
|
+
setView({
|
|
1949
|
+
kind: "row",
|
|
1950
|
+
name: pkg.name,
|
|
1951
|
+
rowId: row.rowId
|
|
1952
|
+
});
|
|
1953
|
+
}
|
|
1954
|
+
});
|
|
1955
|
+
const packageCard = (pkg) => (0, react_jsx_runtime.jsx)(PackageCard, {
|
|
1956
|
+
pkg,
|
|
1957
|
+
t,
|
|
1958
|
+
busy: state.busy.includes(pkg.name),
|
|
1959
|
+
highlighted: state.highlight === pkg.name,
|
|
1960
|
+
onOpen: () => {
|
|
1961
|
+
setView({
|
|
1962
|
+
kind: "package",
|
|
1963
|
+
name: pkg.name
|
|
1964
|
+
});
|
|
1965
|
+
},
|
|
1966
|
+
onSetEnabled: (enabled) => {
|
|
1967
|
+
props.setEnabled(pkg.name, enabled);
|
|
1968
|
+
}
|
|
1969
|
+
}, pkg.name);
|
|
1970
|
+
const officialCards = [...official.map(packageCard), ...ledger.items.map((item) => (0, react_jsx_runtime.jsx)(ItemCard, {
|
|
1971
|
+
item,
|
|
1972
|
+
t,
|
|
1973
|
+
renderSlot,
|
|
1974
|
+
onOpen: () => {
|
|
1975
|
+
setView({
|
|
1976
|
+
kind: "item",
|
|
1977
|
+
id: item.id
|
|
1978
|
+
});
|
|
1979
|
+
}
|
|
1980
|
+
}, `item:${item.id}`))];
|
|
1981
|
+
const renderGroup = (id, heading, cards) => cards.length === 0 ? null : (0, react_jsx_runtime.jsxs)("section", {
|
|
1982
|
+
className: PluginManagerPage_module_css_default.group,
|
|
1983
|
+
"data-plugin-scope": "global",
|
|
1984
|
+
"data-plugin-group": id,
|
|
1985
|
+
children: [(0, react_jsx_runtime.jsxs)("div", {
|
|
1986
|
+
className: PluginManagerPage_module_css_default.groupHead,
|
|
1987
|
+
children: [(0, react_jsx_runtime.jsx)("h3", {
|
|
1988
|
+
className: PluginManagerPage_module_css_default.groupTitle,
|
|
1989
|
+
children: heading
|
|
1990
|
+
}), (0, react_jsx_runtime.jsx)("span", {
|
|
1991
|
+
className: PluginManagerPage_module_css_default.count,
|
|
1992
|
+
"data-plugin-count": cards.length,
|
|
1993
|
+
children: cards.length
|
|
1994
|
+
})]
|
|
1995
|
+
}), (0, react_jsx_runtime.jsx)("ul", {
|
|
1996
|
+
className: PluginManagerPage_module_css_default.cards,
|
|
1997
|
+
children: cards
|
|
1998
|
+
})]
|
|
1999
|
+
});
|
|
2000
|
+
return (0, react_jsx_runtime.jsxs)("section", {
|
|
2001
|
+
className: PluginManagerPage_module_css_default.page,
|
|
2002
|
+
"data-plugin-panel": true,
|
|
2003
|
+
"aria-busy": state.status === "loading",
|
|
2004
|
+
children: [
|
|
2005
|
+
showsCards ? (0, react_jsx_runtime.jsxs)("header", {
|
|
2006
|
+
className: PluginManagerPage_module_css_default.pageHead,
|
|
2007
|
+
children: [(0, react_jsx_runtime.jsxs)("div", { children: [(0, react_jsx_runtime.jsx)("h1", {
|
|
2008
|
+
className: PluginManagerPage_module_css_default.pageTitle,
|
|
2009
|
+
children: t("title")
|
|
2010
|
+
}), (0, react_jsx_runtime.jsx)("p", {
|
|
2011
|
+
className: PluginManagerPage_module_css_default.pageIntro,
|
|
2012
|
+
children: t("intro")
|
|
2013
|
+
})] }), (0, react_jsx_runtime.jsxs)("div", {
|
|
2014
|
+
className: PluginManagerPage_module_css_default.toolbar,
|
|
2015
|
+
children: [(0, react_jsx_runtime.jsx)("button", {
|
|
2016
|
+
type: "button",
|
|
2017
|
+
className: PluginManagerPage_module_css_default.iconButton,
|
|
2018
|
+
"aria-label": t("refresh"),
|
|
2019
|
+
title: t("refresh"),
|
|
2020
|
+
disabled: !loaded,
|
|
2021
|
+
onClick: props.refresh,
|
|
2022
|
+
children: (0, react_jsx_runtime.jsx)("span", {
|
|
2023
|
+
className: PluginManagerPage_module_css_default.iconWrap,
|
|
2024
|
+
"aria-hidden": "true",
|
|
2025
|
+
children: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconRefreshOutline16, {})
|
|
2026
|
+
})
|
|
2027
|
+
}), (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
2028
|
+
variant: "primary",
|
|
2029
|
+
size: "sm",
|
|
2030
|
+
icon: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconPlusOutline16, { size: 13 }),
|
|
2031
|
+
disabled: !loaded,
|
|
2032
|
+
onClick: props.openInstall,
|
|
2033
|
+
children: t("addPlugin")
|
|
2034
|
+
})]
|
|
2035
|
+
})]
|
|
2036
|
+
}) : null,
|
|
2037
|
+
state.status === "loading" ? (0, react_jsx_runtime.jsx)("p", {
|
|
2038
|
+
className: PluginManagerPage_module_css_default.status,
|
|
2039
|
+
children: t("loading")
|
|
2040
|
+
}) : null,
|
|
2041
|
+
state.status === "unavailable" ? (0, react_jsx_runtime.jsx)("p", {
|
|
2042
|
+
className: PluginManagerPage_module_css_default.status,
|
|
2043
|
+
role: "status",
|
|
2044
|
+
children: t("unavailable")
|
|
2045
|
+
}) : null,
|
|
2046
|
+
state.status === "error" ? (0, react_jsx_runtime.jsxs)("div", {
|
|
2047
|
+
className: PluginManagerPage_module_css_default.failure,
|
|
2048
|
+
children: [(0, react_jsx_runtime.jsx)("p", {
|
|
2049
|
+
role: "alert",
|
|
2050
|
+
children: t("error")
|
|
2051
|
+
}), (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
2052
|
+
variant: "outline",
|
|
2053
|
+
size: "sm",
|
|
2054
|
+
onClick: props.refresh,
|
|
2055
|
+
children: t("retry")
|
|
2056
|
+
})]
|
|
2057
|
+
}) : null,
|
|
2058
|
+
state.notice === null || noticeLine === null ? null : (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Toast, {
|
|
2059
|
+
text: noticeLine,
|
|
2060
|
+
icon: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconWarningOutline16, {}),
|
|
2061
|
+
holdMs: toastHoldMs(noticeLine),
|
|
2062
|
+
onDone: props.dismissNotice
|
|
2063
|
+
}, state.notice.seq),
|
|
2064
|
+
loaded && openPkg !== void 0 && openRow !== void 0 ? (0, react_jsx_runtime.jsx)(RowDetail, {
|
|
2065
|
+
pkg: openPkg,
|
|
2066
|
+
row: openRow,
|
|
2067
|
+
t,
|
|
2068
|
+
renderSlot,
|
|
2069
|
+
onBack: () => {
|
|
2070
|
+
setView({
|
|
2071
|
+
kind: "package",
|
|
2072
|
+
name: openPkg.name
|
|
2073
|
+
});
|
|
2074
|
+
}
|
|
2075
|
+
}) : null,
|
|
2076
|
+
loaded && openPkg !== void 0 && openRow === void 0 ? (0, react_jsx_runtime.jsx)(PackageDetail, {
|
|
2077
|
+
pkg: openPkg,
|
|
2078
|
+
t,
|
|
2079
|
+
busy: state.busy.includes(openPkg.name),
|
|
2080
|
+
rowBusy: (row) => row.entryId !== void 0 && state.busy.includes(rowKey(row.entryId)),
|
|
2081
|
+
configured: ledger.bundles.has(openPkg.name),
|
|
2082
|
+
configure: configure(openPkg),
|
|
2083
|
+
renderSlot,
|
|
2084
|
+
onBack: () => {
|
|
2085
|
+
setView({ kind: "list" });
|
|
2086
|
+
},
|
|
2087
|
+
onSetEnabled: (enabled) => {
|
|
2088
|
+
props.setEnabled(openPkg.name, enabled);
|
|
2089
|
+
},
|
|
2090
|
+
onUninstall: () => {
|
|
2091
|
+
props.uninstall(openPkg.name);
|
|
2092
|
+
},
|
|
2093
|
+
onSetRowEnabled: setRowEnabled
|
|
2094
|
+
}) : null,
|
|
2095
|
+
loaded && openItem !== void 0 ? (0, react_jsx_runtime.jsx)(ItemDetail, {
|
|
2096
|
+
item: openItem,
|
|
2097
|
+
t,
|
|
2098
|
+
renderSlot,
|
|
2099
|
+
onBack: () => {
|
|
2100
|
+
setView({ kind: "list" });
|
|
2101
|
+
}
|
|
2102
|
+
}) : null,
|
|
2103
|
+
loaded && showsCards ? officialCards.length === 0 && mine.length === 0 ? (0, react_jsx_runtime.jsx)("p", {
|
|
2104
|
+
className: PluginManagerPage_module_css_default.empty,
|
|
2105
|
+
children: t("empty")
|
|
2106
|
+
}) : (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [renderGroup("official", t("officialTitle"), officialCards), renderGroup("bundles", t("bundlesTitle"), mine.map(packageCard))] }) : null,
|
|
2107
|
+
(0, react_jsx_runtime.jsx)(InstallDialog, {
|
|
2108
|
+
install: state.install,
|
|
2109
|
+
t,
|
|
2110
|
+
onClose: props.closeInstall,
|
|
2111
|
+
onEditSpec: props.editInstallSpec,
|
|
2112
|
+
onRun: props.runInstall,
|
|
2113
|
+
onCancel: props.cancelInstall,
|
|
2114
|
+
onCancelAndClose: props.cancelInstallAndClose,
|
|
2115
|
+
onToggleDetails: props.toggleInstallDetails,
|
|
2116
|
+
onEnableNow: props.enableInstalled,
|
|
2117
|
+
onApproveBuilds: props.approveBuildsAndRetry
|
|
2118
|
+
}),
|
|
2119
|
+
state.confirm === null ? null : (0, react_jsx_runtime.jsx)(ConfirmDialog, {
|
|
2120
|
+
confirm: state.confirm,
|
|
2121
|
+
t,
|
|
2122
|
+
onConfirm: props.confirm,
|
|
2123
|
+
onCancel: props.cancelConfirm
|
|
2124
|
+
})
|
|
2125
|
+
]
|
|
2126
|
+
});
|
|
2127
|
+
}
|
|
2128
|
+
//#endregion
|
|
2129
|
+
//#region lib/types/client/PluginsPanelIcon.js
|
|
2130
|
+
/**
|
|
2131
|
+
* Render the plugin glyph at the size the sidebar asks for.
|
|
2132
|
+
* @param props - the sidebar's icon share: the requested edge and whether the panel is selected.
|
|
2133
|
+
* @returns the icon element.
|
|
2134
|
+
*/
|
|
2135
|
+
function PluginsPanelIcon({ size }) {
|
|
2136
|
+
return (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconPluginPinwheelOutline16, { size });
|
|
2137
|
+
}
|
|
2138
|
+
//#endregion
|
|
2139
|
+
//#region lib/types/client/locales.js
|
|
2140
|
+
/** Plugin management copy and display names of shipped global rows. */
|
|
2141
|
+
/** Simplified Chinese dictionary and key source of truth. */
|
|
2142
|
+
const zh = {
|
|
2143
|
+
panel: "插件",
|
|
2144
|
+
title: "插件",
|
|
2145
|
+
intro: "添加和管理插件",
|
|
2146
|
+
loading: "正在读取插件…",
|
|
2147
|
+
error: "暂时无法读取插件。",
|
|
2148
|
+
unavailable: "本部署没有可管理的 profile,无法安装或启停插件。",
|
|
2149
|
+
retry: "重试",
|
|
2150
|
+
refresh: "刷新",
|
|
2151
|
+
empty: "还没有安装任何插件。",
|
|
2152
|
+
addPlugin: "添加插件",
|
|
2153
|
+
restartNotice: "更改将在下次启动生效",
|
|
2154
|
+
overriddenNotice: "{name} 已保存,但被更高优先级的配置覆盖,当前未生效",
|
|
2155
|
+
bundlesTitle: "已安装",
|
|
2156
|
+
officialTitle: "官方",
|
|
2157
|
+
builtinAgentTeamTitle: "智能体团队",
|
|
2158
|
+
builtinAgentTeamDescription: "启用智能体团队协作与团队工具。",
|
|
2159
|
+
builtinAgentTeamWebTitle: "智能体团队 Web 界面",
|
|
2160
|
+
builtinAgentTeamWebDescription: "在浏览器中查看团队成员、任务看板和成员会话。",
|
|
2161
|
+
builtinAutoReviewTitle: "自动授权审查",
|
|
2162
|
+
builtinAutoReviewDescription: "提供自动审查权限模式,由模型在每次工具调用前判断是否授权。",
|
|
2163
|
+
statusProblem: "异常",
|
|
2164
|
+
statusBeta: "Beta",
|
|
2165
|
+
reasonLabel: "原因",
|
|
2166
|
+
versionTag: "v{version}",
|
|
2167
|
+
noDescription: "暂无描述。",
|
|
2168
|
+
partsLabel: "包含的组件",
|
|
2169
|
+
partsEmpty: "这个插件包不包含任何组件。",
|
|
2170
|
+
partsCountTotal: "共 {count} 个",
|
|
2171
|
+
partsCountRunning: "{count} 运行中",
|
|
2172
|
+
partsCountOff: "{count} 已停用",
|
|
2173
|
+
partOff: "已关闭",
|
|
2174
|
+
partsCountFailed: "{count} 异常",
|
|
2175
|
+
partsFilter: "筛选组件",
|
|
2176
|
+
partsFilterEmpty: "没有匹配的组件。",
|
|
2177
|
+
partToggle: "启用组件 {name}",
|
|
2178
|
+
rowPhasePending: "等待依赖",
|
|
2179
|
+
rowPhaseLoading: "加载中",
|
|
2180
|
+
rowPhaseActive: "运行中",
|
|
2181
|
+
rowPhaseFailed: "异常",
|
|
2182
|
+
rowPhaseUnloading: "卸载中",
|
|
2183
|
+
enableToggle: "启用 {name}",
|
|
2184
|
+
openDetail: "查看 {name}",
|
|
2185
|
+
backToList: "返回插件列表",
|
|
2186
|
+
crumbRoot: "插件列表",
|
|
2187
|
+
backToPackage: "返回 {name}",
|
|
2188
|
+
configureRow: "配置 {name}",
|
|
2189
|
+
rowStateIdle: "未运行",
|
|
2190
|
+
uninstall: "卸载",
|
|
2191
|
+
uninstallLabel: "卸载 {name}",
|
|
2192
|
+
installTitle: "添加插件",
|
|
2193
|
+
installDescription: "输入插件的包名、GitHub 仓库地址或本地目录路径。",
|
|
2194
|
+
installSpecLabel: "包名或地址",
|
|
2195
|
+
installSpecPlaceholder: "例如 @deepseek-ai/dsh-experimental-auto-review",
|
|
2196
|
+
installGuideToggle: "不知道该填什么?",
|
|
2197
|
+
installGuideHide: "收起引导",
|
|
2198
|
+
installGuideIntro: "从插件的 README 或发布页面复制以下任意一项。",
|
|
2199
|
+
installGuideIdNote: "包名即 npm 包名,常见形式为 dsh-xxx 或 @作者/插件名;插件的显示名称不是包名。",
|
|
2200
|
+
installGuideIdTitle: "包名",
|
|
2201
|
+
installGuideIdExample: "@deepseek-ai/dsh-experimental-auto-review",
|
|
2202
|
+
installGuideIdHint: "README 安装命令中 dsh plugin add 或 pnpm add 之后的部分。",
|
|
2203
|
+
installGuideGitTitle: "GitHub 仓库地址",
|
|
2204
|
+
installGuideGitExample: "https://github.com/author/dsh-plugin",
|
|
2205
|
+
installGuideGitHint: "插件在 GitHub 上的开源仓库地址,也支持其他 Git 仓库。",
|
|
2206
|
+
installGuidePathTitle: "本地插件目录",
|
|
2207
|
+
installGuidePathExample: "/Users/name/my-plugin",
|
|
2208
|
+
installGuidePathHint: "本机上插件目录的绝对路径,适用于自行开发或已下载的插件。",
|
|
2209
|
+
installGuideExampleLabel: "示例:",
|
|
2210
|
+
installGuideFill: "填入示例",
|
|
2211
|
+
installGuideFillAria: "填入示例 {example}",
|
|
2212
|
+
installGuideSafety: "请确认插件来源可信。插件在本机以你的权限运行,来源不明的插件可能损坏 DeepSeek Harness,或读取和泄露你的数据。",
|
|
2213
|
+
installRun: "安装",
|
|
2214
|
+
installChecking: "正在检查…",
|
|
2215
|
+
installProblemInvalid: "无法识别这个包名或地址:{reason}",
|
|
2216
|
+
installProblemInstalled: "该插件已安装",
|
|
2217
|
+
installProblemNotFound: "未找到相关插件",
|
|
2218
|
+
installProblemNotPackage: "该路径不存在或不是有效的插件包",
|
|
2219
|
+
installProblemNotBundle: "这个包没有声明组合包,无法作为插件安装:{reason}",
|
|
2220
|
+
installProblemNetwork: "无法连接插件源,请检查网络后重试",
|
|
2221
|
+
installProblemUnknown: "无法获取插件信息:{reason}",
|
|
2222
|
+
installingTitle: "插件安装中…",
|
|
2223
|
+
installedTitle: "已安装",
|
|
2224
|
+
installFailedTitle: "插件安装失败",
|
|
2225
|
+
installEdit: "编辑",
|
|
2226
|
+
installEditAria: "返回编辑",
|
|
2227
|
+
installCancel: "取消安装",
|
|
2228
|
+
installCloseCancels: "取消安装并关闭",
|
|
2229
|
+
installStarting: "正在准备安装…",
|
|
2230
|
+
installCancelling: "正在停止安装…",
|
|
2231
|
+
installApplying: "正在应用配置,请稍候…",
|
|
2232
|
+
installCancelledShort: "已取消",
|
|
2233
|
+
installCancelled: "已取消安装,插件未启用,下载的文件可能保留",
|
|
2234
|
+
installCancelUnconfirmed: "尚未确认安装已停止,请重试取消或等待安装结果。{reason}",
|
|
2235
|
+
installEnableNow: "立即启用",
|
|
2236
|
+
installDetailsShow: "查看安装详情",
|
|
2237
|
+
installDetailsHide: "收起安装详情",
|
|
2238
|
+
installVersion: "版本 {version}",
|
|
2239
|
+
installSubjectPath: "本地目录",
|
|
2240
|
+
installSubjectGit: "Git 仓库",
|
|
2241
|
+
installSubjectTarball: "压缩包",
|
|
2242
|
+
installLocation: "安装位置:{dir}",
|
|
2243
|
+
installRetry: "重试",
|
|
2244
|
+
installFailureNetwork: "网络连接失败",
|
|
2245
|
+
installFailureNotFound: "未找到相关插件",
|
|
2246
|
+
installFailureNoMatchingVersion: "没有匹配的版本",
|
|
2247
|
+
installFailureDiskFull: "磁盘空间不足,安装已停止",
|
|
2248
|
+
installFailurePermission: "没有写入权限,无法安装",
|
|
2249
|
+
installFailureBuildBlocked: "有依赖的安装脚本需要你允许后才能继续",
|
|
2250
|
+
installFailureBuildBlockedManual: "有依赖的安装脚本被 pnpm 拦下,请在 profile 的 pnpm-workspace.yaml 的 allowBuilds 中放行后重试",
|
|
2251
|
+
installFailureIntegrity: "下载的安装包校验失败",
|
|
2252
|
+
installFailureTimeout: "安装超时",
|
|
2253
|
+
installFailurePnpmMissing: "没有找到 pnpm,无法安装",
|
|
2254
|
+
installFailureGeneric: "安装过程中出错,原因见安装详情",
|
|
2255
|
+
terminalRunning: "运行中",
|
|
2256
|
+
terminalFailed: "失败",
|
|
2257
|
+
terminalDone: "已完成",
|
|
2258
|
+
terminalCopy: "复制",
|
|
2259
|
+
terminalCopied: "复制成功",
|
|
2260
|
+
terminalNoOutput: "无输出",
|
|
2261
|
+
terminalCollapseAria: "收起输出",
|
|
2262
|
+
terminalCollapse: "收起",
|
|
2263
|
+
terminalExpandAria: "展开其余 {n} 行输出",
|
|
2264
|
+
terminalExpand: "… 其余 {n} 行",
|
|
2265
|
+
terminalExitCode: "退出码 {code}",
|
|
2266
|
+
terminalSignal: "信号 {signal}",
|
|
2267
|
+
terminalNoExitCode: "未正常退出",
|
|
2268
|
+
installDoneNothing: "安装完成,没有新增依赖。",
|
|
2269
|
+
installDoneRestart: "已安装,下次启动后加载。",
|
|
2270
|
+
installDoneApproved: "已允许运行安装脚本:{names}",
|
|
2271
|
+
installApprovalTitle: "需要允许安装脚本",
|
|
2272
|
+
installApprovalDescription: "以下包声明了安装脚本,pnpm 默认不运行。",
|
|
2273
|
+
installApprovalConsequence: "允许后,脚本会以你的权限在本机运行,授权保存在当前 profile,之后不再询问。",
|
|
2274
|
+
installApprovalCaution: "只在信任这些包时允许。",
|
|
2275
|
+
installApproveAndRetry: "允许这些脚本并重试",
|
|
2276
|
+
installClose: "完成",
|
|
2277
|
+
close: "关闭",
|
|
2278
|
+
cancel: "取消",
|
|
2279
|
+
confirmUninstallTitle: "卸载「{name}」?",
|
|
2280
|
+
confirmUninstallDescription: "卸载后它提供的功能会消失。",
|
|
2281
|
+
confirmUninstall: "卸载",
|
|
2282
|
+
failedEnable: "启用失败:{reason}",
|
|
2283
|
+
failedDisable: "停用失败:{reason}",
|
|
2284
|
+
failedUninstall: "卸载失败:{reason}",
|
|
2285
|
+
failedRowEnable: "组件启用失败:{reason}",
|
|
2286
|
+
failedRowDisable: "组件停用失败:{reason}",
|
|
2287
|
+
reasonManagementRequired: "插件管理所需,不能停用或卸载",
|
|
2288
|
+
reasonUnaddressable: "当前 profile 的 patch 无法唯一定位这一项",
|
|
2289
|
+
reasonUnknownPlugin: "找不到该插件",
|
|
2290
|
+
reasonInvalidSpec: "请输入有效的包名或地址",
|
|
2291
|
+
reasonAmbiguousInstall: "无法从依赖变更中确定安装了哪一个包",
|
|
2292
|
+
reasonNotBundle: "这个包没有声明组合包,不能作为插件管理",
|
|
2293
|
+
reasonNotRemovable: "这个包不属于当前 profile,或者是插件管理所需的组件",
|
|
2294
|
+
reasonStopProfile: "这个 profile 没有启用 HMR,正在使用的包要停止后用 dsh plugin 卸载",
|
|
2295
|
+
reasonBundleInUse: "其他配置仍在使用这个组合包的组件,请先停用它们",
|
|
2296
|
+
reasonStaleApproval: "待允许的安装脚本列表已变化,请重新安装以刷新",
|
|
2297
|
+
reasonOperationError: "Host 报告了一个错误"
|
|
2298
|
+
};
|
|
2299
|
+
/** English dictionary checked against the Chinese key set. */
|
|
2300
|
+
const en = {
|
|
2301
|
+
panel: "Plugins",
|
|
2302
|
+
title: "Plugins",
|
|
2303
|
+
intro: "Add and manage plugins",
|
|
2304
|
+
loading: "Reading plugins…",
|
|
2305
|
+
error: "Plugins are temporarily unavailable.",
|
|
2306
|
+
unavailable: "This deployment runs without a manageable profile, so plugins cannot be installed or switched here.",
|
|
2307
|
+
retry: "Retry",
|
|
2308
|
+
refresh: "Refresh",
|
|
2309
|
+
empty: "No plugins are installed yet.",
|
|
2310
|
+
addPlugin: "Add plugin",
|
|
2311
|
+
restartNotice: "The change takes effect at the next start",
|
|
2312
|
+
overriddenNotice: "{name} was saved, but a higher-priority configuration overrides it, so it is not in effect",
|
|
2313
|
+
bundlesTitle: "Installed",
|
|
2314
|
+
officialTitle: "Official",
|
|
2315
|
+
builtinAgentTeamTitle: "Agent Teams",
|
|
2316
|
+
builtinAgentTeamDescription: "Enable agent team collaboration and team tools.",
|
|
2317
|
+
builtinAgentTeamWebTitle: "Agent Teams Web UI",
|
|
2318
|
+
builtinAgentTeamWebDescription: "View team members, the task board, and teammate sessions in the browser.",
|
|
2319
|
+
builtinAutoReviewTitle: "Auto Authorization Review",
|
|
2320
|
+
builtinAutoReviewDescription: "Add an Auto review permission mode that uses the model to assess authorization before each tool call.",
|
|
2321
|
+
statusProblem: "Problem",
|
|
2322
|
+
statusBeta: "Beta",
|
|
2323
|
+
reasonLabel: "Reason",
|
|
2324
|
+
versionTag: "v{version}",
|
|
2325
|
+
noDescription: "No description.",
|
|
2326
|
+
partsLabel: "Components",
|
|
2327
|
+
partsEmpty: "This plugin pack contains no components.",
|
|
2328
|
+
partsCountTotal: "{count} total",
|
|
2329
|
+
partsCountRunning: "{count} running",
|
|
2330
|
+
partsCountOff: "{count} off",
|
|
2331
|
+
partOff: "Off",
|
|
2332
|
+
partsCountFailed: "{count} failed",
|
|
2333
|
+
partsFilter: "Filter components",
|
|
2334
|
+
partsFilterEmpty: "No component matches.",
|
|
2335
|
+
partToggle: "Enable component {name}",
|
|
2336
|
+
rowPhasePending: "Waiting for dependencies",
|
|
2337
|
+
rowPhaseLoading: "Loading",
|
|
2338
|
+
rowPhaseActive: "Running",
|
|
2339
|
+
rowPhaseFailed: "Problem",
|
|
2340
|
+
rowPhaseUnloading: "Unloading",
|
|
2341
|
+
enableToggle: "Enable {name}",
|
|
2342
|
+
openDetail: "View {name}",
|
|
2343
|
+
backToList: "Back to plugins",
|
|
2344
|
+
crumbRoot: "Plugins",
|
|
2345
|
+
backToPackage: "Back to {name}",
|
|
2346
|
+
configureRow: "Configure {name}",
|
|
2347
|
+
rowStateIdle: "Not running",
|
|
2348
|
+
uninstall: "Uninstall",
|
|
2349
|
+
uninstallLabel: "Uninstall {name}",
|
|
2350
|
+
installTitle: "Add plugin",
|
|
2351
|
+
installDescription: "Enter the plugin's package name, GitHub repository address, or local directory path.",
|
|
2352
|
+
installSpecLabel: "Package name or address",
|
|
2353
|
+
installSpecPlaceholder: "for example @deepseek-ai/dsh-experimental-auto-review",
|
|
2354
|
+
installGuideToggle: "Not sure what to enter?",
|
|
2355
|
+
installGuideHide: "Hide the guide",
|
|
2356
|
+
installGuideIntro: "Copy any one of these from the plugin's README or release page.",
|
|
2357
|
+
installGuideIdNote: "The package name is the npm package name, usually dsh-xxx or @author/plugin; the display name is not one.",
|
|
2358
|
+
installGuideIdTitle: "Package name",
|
|
2359
|
+
installGuideIdExample: "@deepseek-ai/dsh-experimental-auto-review",
|
|
2360
|
+
installGuideIdHint: "The part after dsh plugin add or pnpm add in the README's install command.",
|
|
2361
|
+
installGuideGitTitle: "GitHub repository address",
|
|
2362
|
+
installGuideGitExample: "https://github.com/author/dsh-plugin",
|
|
2363
|
+
installGuideGitHint: "The address of the plugin's open-source repository on GitHub; other Git hosts work too.",
|
|
2364
|
+
installGuidePathTitle: "Local plugin directory",
|
|
2365
|
+
installGuidePathExample: "/Users/name/my-plugin",
|
|
2366
|
+
installGuidePathHint: "The absolute path of a plugin directory on this machine, developed here or downloaded.",
|
|
2367
|
+
installGuideExampleLabel: "Example: ",
|
|
2368
|
+
installGuideFill: "Use example",
|
|
2369
|
+
installGuideFillAria: "Use the example {example}",
|
|
2370
|
+
installGuideSafety: "Install only plugins you trust: they run with your permissions and can damage DeepSeek Harness or leak your data.",
|
|
2371
|
+
installRun: "Install",
|
|
2372
|
+
installChecking: "Checking…",
|
|
2373
|
+
installProblemInvalid: "This is not a package name or address that can be installed: {reason}",
|
|
2374
|
+
installProblemInstalled: "This plugin is already installed",
|
|
2375
|
+
installProblemNotFound: "No such plugin was found",
|
|
2376
|
+
installProblemNotPackage: "The path does not exist or is not a valid plugin package",
|
|
2377
|
+
installProblemNotBundle: "This package declares no bundle, so it cannot be installed as a plugin: {reason}",
|
|
2378
|
+
installProblemNetwork: "The plugin registry could not be reached; check the network and try again",
|
|
2379
|
+
installProblemUnknown: "The plugin could not be looked up: {reason}",
|
|
2380
|
+
installingTitle: "Installing the plugin…",
|
|
2381
|
+
installedTitle: "Installed",
|
|
2382
|
+
installFailedTitle: "The plugin could not be installed",
|
|
2383
|
+
installEdit: "Edit",
|
|
2384
|
+
installEditAria: "Back to editing",
|
|
2385
|
+
installCancel: "Cancel install",
|
|
2386
|
+
installCloseCancels: "Cancel install and close",
|
|
2387
|
+
installStarting: "Preparing installation…",
|
|
2388
|
+
installCancelling: "Stopping installation…",
|
|
2389
|
+
installApplying: "Applying configuration, please wait…",
|
|
2390
|
+
installCancelledShort: "Cancelled",
|
|
2391
|
+
installCancelled: "Installation cancelled; the plugin is not enabled, and downloaded files may remain",
|
|
2392
|
+
installCancelUnconfirmed: "Installation has not been confirmed stopped. Retry cancellation or wait for the installation result. {reason}",
|
|
2393
|
+
installEnableNow: "Enable now",
|
|
2394
|
+
installDetailsShow: "Show install details",
|
|
2395
|
+
installDetailsHide: "Hide install details",
|
|
2396
|
+
installVersion: "Version {version}",
|
|
2397
|
+
installSubjectPath: "Local directory",
|
|
2398
|
+
installSubjectGit: "Git repository",
|
|
2399
|
+
installSubjectTarball: "Tarball",
|
|
2400
|
+
installLocation: "Installs into {dir}",
|
|
2401
|
+
installRetry: "Retry",
|
|
2402
|
+
installFailureNetwork: "The network connection failed",
|
|
2403
|
+
installFailureNotFound: "No such plugin was found",
|
|
2404
|
+
installFailureNoMatchingVersion: "No version matches the request",
|
|
2405
|
+
installFailureDiskFull: "The disk is full; the install stopped",
|
|
2406
|
+
installFailurePermission: "No write permission; the plugin cannot be installed",
|
|
2407
|
+
installFailureBuildBlocked: "A dependency's install scripts need your permission before the install can continue",
|
|
2408
|
+
installFailureBuildBlockedManual: "pnpm blocked install scripts; allow them under allowBuilds in pnpm-workspace.yaml and retry",
|
|
2409
|
+
installFailureIntegrity: "The downloaded package failed its integrity check",
|
|
2410
|
+
installFailureTimeout: "The install timed out",
|
|
2411
|
+
installFailurePnpmMissing: "pnpm was not found, so nothing can be installed",
|
|
2412
|
+
installFailureGeneric: "Something went wrong during the install; the details say what",
|
|
2413
|
+
terminalRunning: "Running",
|
|
2414
|
+
terminalFailed: "Failed",
|
|
2415
|
+
terminalDone: "Done",
|
|
2416
|
+
terminalCopy: "Copy",
|
|
2417
|
+
terminalCopied: "Copied",
|
|
2418
|
+
terminalNoOutput: "No output",
|
|
2419
|
+
terminalCollapseAria: "Collapse output",
|
|
2420
|
+
terminalCollapse: "Collapse",
|
|
2421
|
+
terminalExpandAria: "Expand the remaining {n} output lines",
|
|
2422
|
+
terminalExpand: "… {n} more lines",
|
|
2423
|
+
terminalExitCode: "exit code {code}",
|
|
2424
|
+
terminalSignal: "signal {signal}",
|
|
2425
|
+
terminalNoExitCode: "no exit code",
|
|
2426
|
+
installDoneNothing: "Install finished with no new dependency.",
|
|
2427
|
+
installDoneRestart: "Installed; it loads at the next start.",
|
|
2428
|
+
installDoneApproved: "Install scripts allowed for {names}",
|
|
2429
|
+
installApprovalTitle: "Install scripts need permission",
|
|
2430
|
+
installApprovalDescription: "These packages have install scripts that pnpm did not run.",
|
|
2431
|
+
installApprovalConsequence: "Once allowed, the scripts run here with your permissions, and the permission is saved in this profile.",
|
|
2432
|
+
installApprovalCaution: "Allow only packages you trust.",
|
|
2433
|
+
installApproveAndRetry: "Allow these scripts and retry",
|
|
2434
|
+
installClose: "Done",
|
|
2435
|
+
close: "Close",
|
|
2436
|
+
cancel: "Cancel",
|
|
2437
|
+
confirmUninstallTitle: "Uninstall \"{name}\"?",
|
|
2438
|
+
confirmUninstallDescription: "What it provides goes away once it is uninstalled.",
|
|
2439
|
+
confirmUninstall: "Uninstall",
|
|
2440
|
+
failedEnable: "Could not enable: {reason}",
|
|
2441
|
+
failedDisable: "Could not disable: {reason}",
|
|
2442
|
+
failedUninstall: "Could not uninstall: {reason}",
|
|
2443
|
+
failedRowEnable: "Could not enable the component: {reason}",
|
|
2444
|
+
failedRowDisable: "Could not disable the component: {reason}",
|
|
2445
|
+
reasonManagementRequired: "Plugin management needs it; it cannot be switched off or uninstalled.",
|
|
2446
|
+
reasonUnaddressable: "The profile patch cannot address this one uniquely.",
|
|
2447
|
+
reasonUnknownPlugin: "No such plugin.",
|
|
2448
|
+
reasonInvalidSpec: "Enter a valid package name or address.",
|
|
2449
|
+
reasonAmbiguousInstall: "Which package was installed cannot be told from the dependency change.",
|
|
2450
|
+
reasonNotBundle: "This package declares no bundle, so it cannot be managed as a plugin.",
|
|
2451
|
+
reasonNotRemovable: "This package is not owned by the profile, or plugin management needs it.",
|
|
2452
|
+
reasonStopProfile: "This profile runs without HMR; stop it and uninstall the package with dsh plugin.",
|
|
2453
|
+
reasonBundleInUse: "Other configuration still uses this bundle's components; switch them off first.",
|
|
2454
|
+
reasonStaleApproval: "The pending script approvals changed; install again to refresh them.",
|
|
2455
|
+
reasonOperationError: "The Host reported an error."
|
|
2456
|
+
};
|
|
2457
|
+
//#endregion
|
|
2458
|
+
//#region lib/types/client/index.js
|
|
2459
|
+
/**
|
|
2460
|
+
* Plugin manager, browser half: the **Plugins** entry of the sidebar and the
|
|
2461
|
+
* management page it opens in the main column. The page installs, enables,
|
|
2462
|
+
* disables, and removes the bundles of the Host's profile through the
|
|
2463
|
+
* `pluginManager` Remote and switches their rows in the profile's user layer.
|
|
2464
|
+
* A plugin that carries its own configuration renders it on this page through
|
|
2465
|
+
* the slots the page declares (`slot-contract.ts`).
|
|
2466
|
+
*/
|
|
2467
|
+
/** Dictionary namespace owned by this plugin. */
|
|
2468
|
+
const NS = "pluginManager";
|
|
2469
|
+
/** The id shared by the sidebar entry and the main panel it opens. */
|
|
2470
|
+
const PANEL_ID = "plugins";
|
|
2471
|
+
/** Services required by the sidebar registration and the Remote methods; the inventory says whether the Host manages a profile. */
|
|
2472
|
+
const inject = [
|
|
2473
|
+
"slots",
|
|
2474
|
+
"locale",
|
|
2475
|
+
"remote",
|
|
2476
|
+
"remote.pluginManager",
|
|
2477
|
+
"remote.pluginInventory"
|
|
2478
|
+
];
|
|
2479
|
+
/**
|
|
2480
|
+
* Contribute the Plugins entry to the sidebar with the management page it
|
|
2481
|
+
* opens, and keep it current on the Host's change events.
|
|
2482
|
+
* @param ctx - the browser plugin context.
|
|
2483
|
+
*/
|
|
2484
|
+
function apply(ctx) {
|
|
2485
|
+
ctx.effect(() => ctx.locale.register(NS, {
|
|
2486
|
+
zh,
|
|
2487
|
+
en
|
|
2488
|
+
}), "ui-plugin-manager: dictionaries");
|
|
2489
|
+
const t = ctx.locale.bind(NS);
|
|
2490
|
+
const controller = new PluginManagerController(ctx);
|
|
2491
|
+
ctx.effect(() => () => {
|
|
2492
|
+
controller.dispose();
|
|
2493
|
+
}, "ui-plugin-manager: controller");
|
|
2494
|
+
ctx.effect(() => {
|
|
2495
|
+
const refresh = () => {
|
|
2496
|
+
if (controller.getSnapshot().status !== "idle") controller.load();
|
|
2497
|
+
};
|
|
2498
|
+
const disposers = [
|
|
2499
|
+
ctx.remote.$on("plugin-manager/changed", refresh),
|
|
2500
|
+
ctx.remote.$on("plugin-manager/install-log", (chunk) => {
|
|
2501
|
+
controller.appendLog(chunk);
|
|
2502
|
+
}),
|
|
2503
|
+
ctx.remote.$on("plugin-manager/install-state", (progress) => {
|
|
2504
|
+
controller.installProgress(progress);
|
|
2505
|
+
}),
|
|
2506
|
+
ctx.on("connection/reset", refresh)
|
|
2507
|
+
];
|
|
2508
|
+
return () => {
|
|
2509
|
+
for (const dispose of disposers) dispose();
|
|
2510
|
+
};
|
|
2511
|
+
}, "ui-plugin-manager: host invalidations");
|
|
2512
|
+
const configLedger = configLedgerSource(ctx);
|
|
2513
|
+
ctx.slots.inject("main", () => ctx.slots.register({
|
|
2514
|
+
name: "main",
|
|
2515
|
+
key: PANEL_ID,
|
|
2516
|
+
locale: NS,
|
|
2517
|
+
inject: () => controller.inject(configLedger),
|
|
2518
|
+
children: {
|
|
2519
|
+
"plugins.item": {
|
|
2520
|
+
kind: "list",
|
|
2521
|
+
scope: "root"
|
|
2522
|
+
},
|
|
2523
|
+
"plugins.bundle.config": {
|
|
2524
|
+
kind: "keyed",
|
|
2525
|
+
scope: "root"
|
|
2526
|
+
},
|
|
2527
|
+
"plugins.row.config": {
|
|
2528
|
+
kind: "keyed",
|
|
2529
|
+
scope: "root"
|
|
2530
|
+
}
|
|
2531
|
+
}
|
|
2532
|
+
}, PluginManagerPage));
|
|
2533
|
+
ctx.slots.inject("sidebar.panellist", () => ctx.slots.register({
|
|
2534
|
+
name: "sidebar.panellist",
|
|
2535
|
+
id: PANEL_ID,
|
|
2536
|
+
order: 0,
|
|
2537
|
+
label: () => t("panel"),
|
|
2538
|
+
locale: NS
|
|
2539
|
+
}, PluginsPanelIcon));
|
|
2540
|
+
}
|
|
2541
|
+
//#endregion
|
|
2542
|
+
exports.NS = NS;
|
|
2543
|
+
exports.PANEL_ID = PANEL_ID;
|
|
2544
|
+
exports.apply = apply;
|
|
2545
|
+
exports.inject = inject;
|
|
2546
|
+
return module.exports;
|
|
2547
|
+
}
|
|
2548
|
+
});
|
|
2549
|
+
|
|
2550
|
+
//# sourceMappingURL=client.js.map
|