dsh-crate-web 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. package/README.md +302 -0
  2. package/README.zh-CN.md +304 -0
  3. package/cordis.patch.yml +11 -0
  4. package/core/dsh_pack/__init__.py +77 -0
  5. package/core/dsh_pack/__main__.py +4 -0
  6. package/core/dsh_pack/cli.py +372 -0
  7. package/core/dsh_pack/errors.py +167 -0
  8. package/core/dsh_pack/export.py +912 -0
  9. package/core/dsh_pack/importer.py +1347 -0
  10. package/core/dsh_pack/network.py +77 -0
  11. package/core/dsh_pack/pack.py +248 -0
  12. package/core/dsh_pack/preflight.py +811 -0
  13. package/core/dsh_pack/user_cli.py +462 -0
  14. package/core/dsh_pack/validation.py +272 -0
  15. package/core/dsh_pack/verify.py +1336 -0
  16. package/lib/client.js +1623 -0
  17. package/lib/index.d.ts +30 -0
  18. package/lib/index.js +1275 -0
  19. package/package.json +73 -0
  20. package/restart-helper.mjs +121 -0
  21. package/runtime/python/LICENSE.txt +676 -0
  22. package/runtime/python/__install__.json +82 -0
  23. package/runtime/python/_asyncio.pyd +0 -0
  24. package/runtime/python/_bz2.pyd +0 -0
  25. package/runtime/python/_ctypes.pyd +0 -0
  26. package/runtime/python/_decimal.pyd +0 -0
  27. package/runtime/python/_elementtree.pyd +0 -0
  28. package/runtime/python/_hashlib.pyd +0 -0
  29. package/runtime/python/_lzma.pyd +0 -0
  30. package/runtime/python/_multiprocessing.pyd +0 -0
  31. package/runtime/python/_overlapped.pyd +0 -0
  32. package/runtime/python/_queue.pyd +0 -0
  33. package/runtime/python/_remote_debugging.pyd +0 -0
  34. package/runtime/python/_socket.pyd +0 -0
  35. package/runtime/python/_sqlite3.pyd +0 -0
  36. package/runtime/python/_ssl.pyd +0 -0
  37. package/runtime/python/_uuid.pyd +0 -0
  38. package/runtime/python/_wmi.pyd +0 -0
  39. package/runtime/python/_zoneinfo.pyd +0 -0
  40. package/runtime/python/_zstd.pyd +0 -0
  41. package/runtime/python/libcrypto-3.dll +0 -0
  42. package/runtime/python/libffi-8.dll +0 -0
  43. package/runtime/python/libssl-3.dll +0 -0
  44. package/runtime/python/pyexpat.pyd +0 -0
  45. package/runtime/python/python.cat +0 -0
  46. package/runtime/python/python.exe +0 -0
  47. package/runtime/python/python3.dll +0 -0
  48. package/runtime/python/python314._pth +6 -0
  49. package/runtime/python/python314.dll +0 -0
  50. package/runtime/python/python314.zip +0 -0
  51. package/runtime/python/pythonw.exe +0 -0
  52. package/runtime/python/select.pyd +0 -0
  53. package/runtime/python/sqlite3.dll +0 -0
  54. package/runtime/python/unicodedata.pyd +0 -0
  55. package/runtime/python/vcruntime140.dll +0 -0
  56. package/runtime/python/vcruntime140_1.dll +0 -0
  57. package/runtime/python/winsound.pyd +0 -0
package/lib/client.js ADDED
@@ -0,0 +1,1623 @@
1
+ window.__ModuleLoader__.load({
2
+ id: "dsh-crate-web",
3
+ factory: (require) => {
4
+ var module = { exports: {} };
5
+ var exports = module.exports;
6
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
7
+ let react = require("react");
8
+ let _deepseek_ai_dsh_client_ui_primitives = require("@deepseek-ai/dsh-client-ui-primitives");
9
+ let react_jsx_runtime = require("react/jsx-runtime");
10
+ //#region src/client/DshCratePage.tsx
11
+ const css = {
12
+ section: "dsh-crate-section",
13
+ heading: "dsh-crate-heading",
14
+ intro: "dsh-crate-intro",
15
+ muted: "dsh-crate-muted",
16
+ tabs: "dsh-crate-tabs",
17
+ tab: "dsh-crate-tab",
18
+ form: "dsh-crate-form",
19
+ label: "dsh-crate-label",
20
+ select: "dsh-crate-select",
21
+ file: "dsh-crate-file",
22
+ plugin: "dsh-crate-plugin",
23
+ button: "dsh-crate-button",
24
+ report: "dsh-crate-report",
25
+ finding: "dsh-crate-finding",
26
+ history: "dsh-crate-history",
27
+ historyItem: "dsh-crate-history-item",
28
+ error: "dsh-crate-error",
29
+ preview: "dsh-crate-preview",
30
+ previewRow: "dsh-crate-preview-row",
31
+ diagnostic: "dsh-crate-diagnostic",
32
+ diagnosticGrid: "dsh-crate-diagnostic-grid",
33
+ step: "dsh-crate-step",
34
+ stepStatus: "dsh-crate-step-status",
35
+ inventory: "dsh-crate-inventory",
36
+ inventoryItem: "dsh-crate-inventory-item",
37
+ success: "dsh-crate-success",
38
+ modalBackdrop: "dsh-crate-modal-backdrop",
39
+ modal: "dsh-crate-modal",
40
+ modalActions: "dsh-crate-modal-actions",
41
+ group: "dsh-crate-group",
42
+ groupHead: "dsh-crate-group-head",
43
+ groupToggle: "dsh-crate-group-toggle",
44
+ groupBody: "dsh-crate-group-body",
45
+ badge: "dsh-crate-badge",
46
+ conflict: "dsh-crate-conflict",
47
+ toneOk: "dsh-crate-tone-ok",
48
+ toneBad: "dsh-crate-tone-bad",
49
+ toneNeutral: "dsh-crate-tone-neutral"
50
+ };
51
+ const STYLE = `
52
+ .dsh-crate-section{display:flex;flex-direction:column;gap:12px;max-width:760px;color:var(--dsw-alias-label-primary)}
53
+ .dsh-crate-heading{margin:0;font-size:18px;font-weight:600}.dsh-crate-intro,.dsh-crate-muted{margin:0;font-size:13px;color:var(--dsw-alias-label-tertiary)}
54
+ .dsh-crate-tabs{display:flex;gap:20px;border-bottom:1px solid var(--dsw-alias-border-l2);overflow:auto}.dsh-crate-tab{border:0;padding:7px 1px 9px;background:transparent;color:var(--dsw-alias-label-tertiary);font:inherit;cursor:pointer;white-space:nowrap}
55
+ .dsh-crate-tab[data-active=true]{color:var(--dsw-alias-label-primary);border-bottom:2px solid var(--dsw-alias-label-primary)}.dsh-crate-form{display:flex;flex-direction:column;gap:10px}
56
+ .dsh-crate-label{display:flex;flex-direction:column;gap:5px;font-size:13px}.dsh-crate-select,.dsh-crate-file{max-width:100%;padding:7px 8px;border:1px solid var(--dsw-alias-border-l2,#3a3a40);border-radius:6px;background-color:var(--dsw-alias-fill-secondary,rgba(255,255,255,.08));color:var(--dsw-alias-label-primary,#f9fafb);color-scheme:dark}.dsh-crate-select option{background-color:#242428;color:#f9fafb}@media (prefers-color-scheme:light){.dsh-crate-select,.dsh-crate-file{background-color:var(--dsw-alias-fill-secondary,#fff);color:var(--dsw-alias-label-primary,#111827);color-scheme:light}.dsh-crate-select option{background-color:#fff;color:#111827}}
57
+ .dsh-crate-plugin{display:grid;grid-template-columns:minmax(0,1fr) 150px;gap:8px;align-items:center}.dsh-crate-button{align-self:flex-start;border:0;border-radius:6px;padding:8px 13px;background:var(--dsw-alias-state-business-primary);color:white;cursor:pointer}.dsh-crate-button:disabled{cursor:default;opacity:.5}
58
+ .dsh-crate-report,.dsh-crate-preview,.dsh-crate-diagnostic{display:flex;flex-direction:column;gap:8px;padding:10px;border:1px solid var(--dsw-alias-border-l2);border-radius:8px;overflow-wrap:anywhere;font-size:12px}.dsh-crate-finding,.dsh-crate-step{padding:7px;border-radius:5px;background:var(--dsw-alias-fill-tertiary)}
59
+ .dsh-crate-preview-row{display:grid;grid-template-columns:170px minmax(0,1fr);gap:8px}.dsh-crate-preview-row pre{margin:0;white-space:pre-wrap}.dsh-crate-diagnostic-grid{display:grid;grid-template-columns:150px minmax(0,1fr);gap:6px}.dsh-crate-diagnostic-grid dt{font-weight:600}.dsh-crate-diagnostic-grid dd{margin:0;white-space:pre-wrap}.dsh-crate-step{display:flex;gap:8px;align-items:flex-start}.dsh-crate-step-status{min-width:80px;font-weight:600}
60
+ .dsh-crate-history{list-style:none;display:flex;flex-direction:column;gap:8px;padding:0;margin:0}.dsh-crate-history-item{display:flex;justify-content:space-between;gap:10px;padding:8px;border-bottom:1px solid var(--dsw-alias-border-l2);font-size:12px}.dsh-crate-error{color:var(--dsw-alias-label-negative)}
61
+ .dsh-crate-inventory{display:flex;flex-direction:column;gap:6px;padding:8px;border:1px solid var(--dsw-alias-border-l2);border-radius:8px;font-size:12px}.dsh-crate-inventory-item{display:flex;justify-content:space-between;gap:8px;padding:4px 0;border-bottom:1px solid var(--dsw-alias-border-l2)}.dsh-crate-inventory-item:last-child{border-bottom:0}
62
+ .dsh-crate-success{display:flex;flex-direction:column;gap:8px;padding:10px;border:1px solid var(--dsw-alias-label-positive);border-radius:8px;background:var(--dsw-alias-fill-positive);font-size:12px}
63
+ .dsh-crate-group{display:flex;flex-direction:column;gap:6px}.dsh-crate-group-head{display:flex;align-items:center;justify-content:space-between;gap:8px;padding:2px 0}.dsh-crate-group-toggle{border:0;background:transparent;color:var(--dsw-alias-state-business-primary,#4f8cff);font:inherit;font-size:12px;cursor:pointer;padding:2px 4px}.dsh-crate-group-body{display:flex;flex-direction:column;gap:6px}
64
+ .dsh-crate-badge{margin-left:6px;padding:1px 5px;border-radius:4px;background:var(--dsw-alias-fill-tertiary);color:var(--dsw-alias-label-tertiary);font-size:11px;border:1px solid var(--dsw-alias-border-l2)}.dsh-crate-conflict{color:var(--dsw-alias-label-negative)}
65
+ .dsh-crate-modal-backdrop{position:fixed;inset:0;z-index:1000;display:flex;align-items:center;justify-content:center;padding:24px;background:rgba(0,0,0,.58)}.dsh-crate-modal{display:flex;flex-direction:column;gap:12px;width:min(420px,calc(100vw - 48px));padding:18px;border:1px solid var(--dsw-alias-border-l2,#3a3a40);border-radius:10px;background:var(--dsw-alias-fill-primary,#1d1d20);color:var(--dsw-alias-label-primary,#f9fafb);box-shadow:0 18px 50px rgba(0,0,0,.45)}.dsh-crate-modal h3{margin:0;font-size:16px}.dsh-crate-modal p{margin:0;font-size:13px;color:var(--dsw-alias-label-tertiary,#b6b6bd)}.dsh-crate-modal-actions{display:flex;justify-content:flex-end;gap:8px}.dsh-crate-modal-actions button:last-child{background:var(--dsw-alias-state-business-primary,#4b7bec);color:#fff}
66
+ .dsh-crate-tone-ok{color:var(--dsw-alias-label-positive)}.dsh-crate-tone-bad{color:var(--dsw-alias-label-negative)}.dsh-crate-tone-neutral{color:var(--dsw-alias-label-tertiary)}
67
+ `;
68
+ function isObject(value) {
69
+ return value !== null && typeof value === "object" && !Array.isArray(value);
70
+ }
71
+ function formatTime(value) {
72
+ if (typeof value !== "string" || !value) return "";
73
+ const date = new Date(value);
74
+ return Number.isNaN(date.getTime()) ? value : date.toLocaleString();
75
+ }
76
+ function toneOf(status) {
77
+ const text = String(status ?? "").toUpperCase();
78
+ if (text.includes("FAIL") || text.includes("ERROR") || text === "BLOCKER" || text === "UNKNOWN") return css.toneBad;
79
+ if (text.includes("PASS") || text === "READY" || text === "PREPARED" || text === "OK") return css.toneOk;
80
+ return css.toneNeutral;
81
+ }
82
+ const FIELD_LABELS = {
83
+ code: "diagCode",
84
+ stage: "diagStage",
85
+ item: "diagItem",
86
+ expected: "diagExpected",
87
+ observed: "diagObserved",
88
+ evidence: "diagEvidence",
89
+ impact: "diagImpact",
90
+ originalProfileStatus: "diagOriginalProfile",
91
+ failedProfileStatus: "diagFailedProfile",
92
+ temporaryProfileStatus: "diagTemporaryProfile",
93
+ canContinue: "diagCanContinue",
94
+ suggestedNextStep: "diagSuggestedNext"
95
+ };
96
+ function resultOf(value) {
97
+ return isObject(value.result) ? value.result : value;
98
+ }
99
+ function objects(value) {
100
+ return Array.isArray(value) ? value.filter(isObject) : [];
101
+ }
102
+ function failMessage(value) {
103
+ const error = diagnosticOf(value);
104
+ if (error !== void 0 && typeof error.message === "string" && error.message) return error.message;
105
+ const result = resultOf(value);
106
+ if (typeof result.message === "string" && result.message) return result.message;
107
+ return "";
108
+ }
109
+ function display(value) {
110
+ if (typeof value === "string") return value;
111
+ const encoded = JSON.stringify(value, null, 2);
112
+ return encoded === void 0 ? String(value) : encoded;
113
+ }
114
+ function pluginLabel(plugin) {
115
+ const resolved = isObject(plugin.resolved) ? plugin.resolved : {};
116
+ return `${String(plugin.name ?? "unknown")}@${String(resolved.version ?? "unknown")}`;
117
+ }
118
+ function upsertProfileRow(list, row) {
119
+ const name = typeof row.name === "string" ? row.name : "";
120
+ if (!name) return list;
121
+ const entry = {
122
+ name,
123
+ installedBundles: objects(row.installedBundles)
124
+ };
125
+ return [...list.filter((item) => item.name !== name), entry].sort((a, b) => a.name.localeCompare(b.name));
126
+ }
127
+ function installedBundleLabel(bundle, t) {
128
+ return `${String(bundle.name ?? "unknown")}@${String(bundle.version ?? "unknown")} · ${bundle.active === true ? t("active") : t("installationAnchor")}${bundle.official === true ? ` · ${t("officialBadge")}` : ""}`;
129
+ }
130
+ function splitOfficial(items) {
131
+ const official = [];
132
+ const user = [];
133
+ for (const item of items) (item.official === true ? official : user).push(item);
134
+ return {
135
+ official,
136
+ user
137
+ };
138
+ }
139
+ function PluginName({ name, official, t }) {
140
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: [name, official ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
141
+ className: css.badge,
142
+ "data-kind": "official",
143
+ children: t("officialBadge")
144
+ }) : null] });
145
+ }
146
+ function PluginGroup({ label, count, defaultOpen = true, t, children }) {
147
+ const [open, setOpen] = (0, react.useState)(defaultOpen);
148
+ if (count === 0) return null;
149
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
150
+ className: css.group,
151
+ "data-open": open,
152
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
153
+ className: css.groupHead,
154
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("strong", { children: [
155
+ label,
156
+ " · ",
157
+ count
158
+ ] }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
159
+ className: css.groupToggle,
160
+ type: "button",
161
+ "aria-expanded": open,
162
+ onClick: () => setOpen((value) => !value),
163
+ children: open ? t("collapse") : t("expand")
164
+ })]
165
+ }), open ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
166
+ className: css.groupBody,
167
+ children
168
+ }) : null]
169
+ });
170
+ }
171
+ function ExportPlugins({ value, t }) {
172
+ if (value === void 0) return null;
173
+ const plugins = objects(resultOf(value).plugins);
174
+ if (plugins.length === 0) return null;
175
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
176
+ className: css.inventory,
177
+ "data-field": "exportPlugins",
178
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: t("exportPlugins") }), plugins.map((plugin, index) => {
179
+ const bundle = isObject(plugin.bundle) ? plugin.bundle : {};
180
+ const artifact = isObject(plugin.artifact) ? plugin.artifact : {};
181
+ const runtime = isObject(plugin.runtime) ? plugin.runtime : {};
182
+ const bundleLabel = bundle.enabled === true ? `${t("bundle")} #${String(bundle.order ?? "?")}` : t("notBundle");
183
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
184
+ className: css.inventoryItem,
185
+ "data-field": "exportPlugin",
186
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: pluginLabel(plugin) }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: [
187
+ bundleLabel,
188
+ " · ",
189
+ String(artifact.mode ?? "unknown"),
190
+ " · ",
191
+ String(runtime.source ?? "unknown")
192
+ ] })]
193
+ }, `${String(plugin.name)}-${index}`);
194
+ })]
195
+ });
196
+ }
197
+ async function request(action, body = {}) {
198
+ const value = await (await fetch("/dsh-crate/api", {
199
+ method: "POST",
200
+ headers: { "content-type": "application/json" },
201
+ body: JSON.stringify({
202
+ action,
203
+ ...body
204
+ })
205
+ })).json();
206
+ if (!isObject(value)) throw new Error("DSH Crate returned a malformed response");
207
+ return value;
208
+ }
209
+ async function toBase64(file) {
210
+ const bytes = new Uint8Array(await file.arrayBuffer());
211
+ let binary = "";
212
+ const chunk = 32768;
213
+ for (let index = 0; index < bytes.length; index += chunk) binary += String.fromCharCode(...bytes.subarray(index, index + chunk));
214
+ return btoa(binary);
215
+ }
216
+ function diagnosticOf(value) {
217
+ if (isObject(value.error)) return value.error;
218
+ const result = resultOf(value);
219
+ return isObject(result.error) ? result.error : void 0;
220
+ }
221
+ function Diagnostic({ value, t }) {
222
+ const [copied, setCopied] = (0, react.useState)(false);
223
+ const diagnostic = diagnosticOf(value);
224
+ if (diagnostic === void 0) return null;
225
+ const copy = async () => {
226
+ try {
227
+ await navigator.clipboard.writeText(JSON.stringify(diagnostic, null, 2));
228
+ setCopied(true);
229
+ } catch {
230
+ setCopied(false);
231
+ }
232
+ };
233
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
234
+ className: css.diagnostic,
235
+ role: "alert",
236
+ children: [
237
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: t("diagnostic") }),
238
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("dl", {
239
+ className: css.diagnosticGrid,
240
+ children: [
241
+ "code",
242
+ "stage",
243
+ "item",
244
+ "expected",
245
+ "observed",
246
+ "evidence",
247
+ "impact",
248
+ "originalProfileStatus",
249
+ "failedProfileStatus",
250
+ "temporaryProfileStatus",
251
+ "canContinue",
252
+ "suggestedNextStep"
253
+ ].filter((field) => diagnostic[field] !== void 0).map((field) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("dt", { children: t(FIELD_LABELS[field] ?? field) }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("dd", { children: display(diagnostic[field]) })] }, field))
254
+ }),
255
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
256
+ className: css.button,
257
+ type: "button",
258
+ onClick: () => void copy(),
259
+ children: copied ? t("diagnosticCopied") : t("copyDiagnostic")
260
+ }) }),
261
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("details", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("summary", { children: t("fullDiagnostic") }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("pre", { children: JSON.stringify(diagnostic, null, 2) })] })
262
+ ]
263
+ });
264
+ }
265
+ function Report({ value, t }) {
266
+ if (value === void 0) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
267
+ className: css.muted,
268
+ children: t("noReport")
269
+ });
270
+ const result = resultOf(value);
271
+ const findings = objects(result.findings);
272
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
273
+ className: css.report,
274
+ "data-status": String(result.status ?? "UNKNOWN"),
275
+ children: [
276
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [
277
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: t("coreStatus") }),
278
+ " · ",
279
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
280
+ className: toneOf(result.status),
281
+ children: String(result.status ?? "UNKNOWN")
282
+ })
283
+ ] }),
284
+ findings.length > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: t("findings") }), findings.map((finding, index) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
285
+ className: css.finding,
286
+ children: [
287
+ String(finding.severity ?? ""),
288
+ " / ",
289
+ String(finding.code ?? ""),
290
+ ": ",
291
+ String(finding.message ?? "")
292
+ ]
293
+ }, `${String(finding.code)}-${index}`))] }) : null,
294
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Diagnostic, {
295
+ value,
296
+ t
297
+ }),
298
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("details", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("summary", { children: t("rawJson") }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("pre", { children: JSON.stringify(result, null, 2) })] })
299
+ ]
300
+ });
301
+ }
302
+ function ImportPreview({ value, t, targetProfile, overwrite }) {
303
+ const result = resultOf(value);
304
+ const pack = isObject(result.pack) ? result.pack : {};
305
+ const profile = isObject(pack.profile) ? pack.profile : {};
306
+ const requiredPlugins = objects(result.requiredPlugins);
307
+ const optionalPlugins = objects(result.optionalPlugins);
308
+ const warningCount = typeof result.warningCount === "number" ? result.warningCount : 0;
309
+ const environment = isObject(result.environment) ? result.environment : {};
310
+ const target = targetProfile?.trim() || String(result.targetProfile ?? profile.name ?? "UNKNOWN");
311
+ const safety = overwrite ? t("overwritePreview") : t("willNotOverwrite");
312
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
313
+ className: css.preview,
314
+ children: [
315
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: t("importPreview") }),
316
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
317
+ className: css.previewRow,
318
+ "data-field": "targetProfile",
319
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: t("targetProfile") }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: target })]
320
+ }),
321
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
322
+ className: css.previewRow,
323
+ "data-field": "warningCount",
324
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: t("warningCount") }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: warningCount })]
325
+ }),
326
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
327
+ className: css.previewRow,
328
+ "data-field": "requiredPlugins",
329
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: t("requiredPlugins") }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: requiredPlugins.map(pluginLabel).join(", ") || t("none") })]
330
+ }),
331
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
332
+ className: css.previewRow,
333
+ "data-field": "optionalPlugins",
334
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: t("optionalPlugins") }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: optionalPlugins.map(pluginLabel).join(", ") || t("none") })]
335
+ }),
336
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
337
+ className: css.previewRow,
338
+ "data-field": "environment",
339
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: t("environment") }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("pre", { children: display(environment) })]
340
+ }),
341
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
342
+ className: css.previewRow,
343
+ "data-field": "coreDecision",
344
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: t("coreDecision") }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: display(result.canContinue) })]
345
+ }),
346
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
347
+ className: css.previewRow,
348
+ "data-field": "safety",
349
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: t("safety") }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: safety })]
350
+ })
351
+ ]
352
+ });
353
+ }
354
+ function ImportSuccess({ value, t }) {
355
+ if (value === void 0) return null;
356
+ const result = resultOf(value);
357
+ if (result.status !== "prepared") return null;
358
+ const plan = isObject(result.plan) ? result.plan : {};
359
+ const installed = objects(result.installedPlugins);
360
+ const profileName = String(plan.profileName ?? "UNKNOWN");
361
+ const overwritten = plan.overwrite === true;
362
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
363
+ className: css.success,
364
+ "data-field": "importSuccess",
365
+ role: "status",
366
+ children: [
367
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: t("importSuccess") }),
368
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
369
+ "data-field": "preparedProfile",
370
+ children: [
371
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: t("preparedProfile") }),
372
+ " · ",
373
+ profileName
374
+ ]
375
+ }),
376
+ overwritten ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
377
+ "data-field": "overwriteConfirmed",
378
+ children: t("overwritePreview")
379
+ }) : null,
380
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
381
+ "data-field": "installedPlugins",
382
+ children: [
383
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: t("installedPlugins") }),
384
+ " · ",
385
+ installed.map((plugin) => `${String(plugin.name ?? "unknown")}@${String(plugin.version ?? "unknown")}`).join(", ") || t("none")
386
+ ]
387
+ })
388
+ ]
389
+ });
390
+ }
391
+ function VerifyReport({ value, t }) {
392
+ if (value === void 0) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
393
+ className: css.muted,
394
+ children: t("noReport")
395
+ });
396
+ const result = resultOf(value);
397
+ const steps = objects(result.steps);
398
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
399
+ className: css.report,
400
+ "data-status": String(result.status ?? "UNKNOWN"),
401
+ children: [
402
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [
403
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: t("coreStatus") }),
404
+ " · ",
405
+ String(result.status ?? "UNKNOWN")
406
+ ] }),
407
+ String(result.status ?? "") === "UNTESTED" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
408
+ className: css.muted,
409
+ children: t("verifyUntestedNote")
410
+ }) : null,
411
+ steps.map((step, index) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
412
+ className: css.step,
413
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
414
+ className: `${css.stepStatus} ${toneOf(step.status)}`,
415
+ children: String(step.status ?? "UNKNOWN")
416
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: [
417
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: String(step.name ?? "unknown") }),
418
+ " — ",
419
+ String(step.message ?? ""),
420
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("br", {}),
421
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("small", { children: display(step.evidence ?? {}) })
422
+ ] })]
423
+ }, `${String(step.name)}-${index}`)),
424
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Diagnostic, {
425
+ value,
426
+ t
427
+ }),
428
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("details", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("summary", { children: t("rawJson") }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("pre", { children: JSON.stringify(result, null, 2) })] })
429
+ ]
430
+ });
431
+ }
432
+ function DshCratePage({ t }) {
433
+ const [tab, setTab] = (0, react.useState)("export");
434
+ const [profiles, setProfiles] = (0, react.useState)([]);
435
+ const [profile, setProfile] = (0, react.useState)("");
436
+ const [modes, setModes] = (0, react.useState)({});
437
+ const [file, setFile] = (0, react.useState)();
438
+ const [report, setReport] = (0, react.useState)();
439
+ const [history, setHistory] = (0, react.useState)([]);
440
+ const [busy, setBusy] = (0, react.useState)(false);
441
+ const busyRef = (0, react.useRef)(false);
442
+ const [error, setError] = (0, react.useState)("");
443
+ const [importMode, setImportMode] = (0, react.useState)("new");
444
+ const [importTarget, setImportTarget] = (0, react.useState)("");
445
+ const [deleteTarget, setDeleteTarget] = (0, react.useState)("");
446
+ const [deleteReport, setDeleteReport] = (0, react.useState)();
447
+ const [switchTarget, setSwitchTarget] = (0, react.useState)("");
448
+ const [switchReport, setSwitchReport] = (0, react.useState)();
449
+ const [runtime, setRuntime] = (0, react.useState)();
450
+ const [runtimePlugins, setRuntimePlugins] = (0, react.useState)([]);
451
+ const [updatedAt, setUpdatedAt] = (0, react.useState)("");
452
+ const [toast, setToast] = (0, react.useState)();
453
+ const toastSeq = (0, react.useRef)(0);
454
+ const showToast = (text) => {
455
+ toastSeq.current += 1;
456
+ setToast({
457
+ key: toastSeq.current,
458
+ text
459
+ });
460
+ };
461
+ const [createTarget, setCreateTarget] = (0, react.useState)("");
462
+ const [newProfileDialogOpen, setNewProfileDialogOpen] = (0, react.useState)(false);
463
+ const [newProfileDraft, setNewProfileDraft] = (0, react.useState)("");
464
+ const [newProfileDialogError, setNewProfileDialogError] = (0, react.useState)("");
465
+ const selectedProfile = (0, react.useMemo)(() => profiles.find((item) => item.name === profile), [profiles, profile]);
466
+ const installedBundles = selectedProfile?.installedBundles ?? [];
467
+ const exportBundles = installedBundles.filter((bundle) => bundle.active === true);
468
+ const conflictBundles = installedBundles.filter((bundle) => bundle.conflict === true);
469
+ const exportNames = exportBundles.map((bundle) => String(bundle.name ?? "")).filter(Boolean);
470
+ const canImport = isObject(report?.result) && report.result.canContinue === true;
471
+ const currentRuntimeProfile = typeof runtime?.currentProfile === "string" ? runtime.currentProfile : "";
472
+ const loadProfiles = async () => {
473
+ const value = await request("profiles");
474
+ const rows = objects(value.profiles).map((item) => ({
475
+ name: String(item.name ?? ""),
476
+ installedBundles: objects(item.installedBundles)
477
+ }));
478
+ const runtimeValue = isObject(value.runtime) ? value.runtime : void 0;
479
+ const activeName = typeof runtimeValue?.currentProfile === "string" ? runtimeValue.currentProfile : "";
480
+ const switchable = rows.filter((item) => item.name !== activeName);
481
+ setProfiles(rows);
482
+ setProfile((current) => rows.some((item) => item.name === current) ? current : rows[0]?.name || "");
483
+ setDeleteTarget((current) => rows.some((item) => item.name === current) ? current : rows[0]?.name || "");
484
+ setSwitchTarget((current) => switchable.some((item) => item.name === current) ? current : switchable[0]?.name || "");
485
+ if (runtimeValue) setRuntime(runtimeValue);
486
+ setRuntimePlugins(objects(value.runtimePlugins));
487
+ setUpdatedAt((/* @__PURE__ */ new Date()).toLocaleTimeString());
488
+ };
489
+ (0, react.useEffect)(() => {
490
+ loadProfiles().catch((caught) => setError(caught instanceof Error ? caught.message : String(caught)));
491
+ }, []);
492
+ (0, react.useEffect)(() => {
493
+ busyRef.current = busy;
494
+ }, [busy]);
495
+ const loadProfilesRef = (0, react.useRef)(() => Promise.resolve());
496
+ (0, react.useEffect)(() => {
497
+ loadProfilesRef.current = loadProfiles;
498
+ });
499
+ (0, react.useEffect)(() => {
500
+ let disposed = false;
501
+ const refresh = () => {
502
+ if (disposed || busyRef.current) return;
503
+ loadProfilesRef.current().catch((caught) => setError(caught instanceof Error ? caught.message : String(caught)));
504
+ };
505
+ const onVisibility = () => {
506
+ if (document.visibilityState === "visible") refresh();
507
+ };
508
+ const timer = window.setInterval(refresh, 1e4);
509
+ window.addEventListener("focus", refresh);
510
+ document.addEventListener("visibilitychange", onVisibility);
511
+ return () => {
512
+ disposed = true;
513
+ window.clearInterval(timer);
514
+ window.removeEventListener("focus", refresh);
515
+ document.removeEventListener("visibilitychange", onVisibility);
516
+ };
517
+ }, []);
518
+ (0, react.useEffect)(() => {
519
+ if (tab !== "history") return;
520
+ request("history").then((value) => {
521
+ setHistory(objects(value.history));
522
+ }).catch((caught) => setError(caught instanceof Error ? caught.message : String(caught)));
523
+ }, [tab]);
524
+ const run = async (action, body) => {
525
+ setBusy(true);
526
+ setError("");
527
+ try {
528
+ const value = await request(action, body);
529
+ setReport(value);
530
+ const result = resultOf(value);
531
+ const profileLabel = String(body.profileName ?? "");
532
+ if (value.status === "failed" || result.status === "failed" || result.status === "FAIL") showToast(`${t("error")}: ${failMessage(value) || t("error")}`);
533
+ else if (action === "export" && typeof value.downloadName === "string") showToast(`${t("exportSuccess")} ${profileLabel}`.trim());
534
+ else if (action === "inspect") showToast(t("inspectSuccess"));
535
+ else if (action === "verify") {
536
+ if (value.success === true) showToast(`${t("verifySuccess")} ${profileLabel}`.trim());
537
+ else if (result.status === "FAIL") showToast(`${t("error")}: ${failMessage(value) || t("verifyFailed")}`);
538
+ else showToast(`${t("verifyFinished")} ${profileLabel} · ${String(result.status ?? "")}`.trim());
539
+ }
540
+ } catch (caught) {
541
+ const message = caught instanceof Error ? caught.message : String(caught);
542
+ setError(message);
543
+ showToast(`${t("error")}: ${message}`);
544
+ } finally {
545
+ setBusy(false);
546
+ }
547
+ };
548
+ const createSelectedProfile = async () => {
549
+ const target = createTarget.trim();
550
+ if (!target) {
551
+ showToast(t("profileNameRequired"));
552
+ return;
553
+ }
554
+ if (profiles.some((item) => item.name === target)) {
555
+ showToast(t("profileNameExists"));
556
+ return;
557
+ }
558
+ setBusy(true);
559
+ setError("");
560
+ try {
561
+ const value = await request("create-profile", { profileName: target });
562
+ if (value.status === "failed") showToast(`${t("error")}: ${failMessage(value) || t("error")}`);
563
+ else {
564
+ setCreateTarget("");
565
+ await loadProfiles();
566
+ showToast(`${t("createSuccess")} ${target}`);
567
+ }
568
+ } catch (caught) {
569
+ const message = caught instanceof Error ? caught.message : String(caught);
570
+ setError(message);
571
+ showToast(`${t("error")}: ${message}`);
572
+ } finally {
573
+ setBusy(false);
574
+ }
575
+ };
576
+ const deleteSelectedProfile = async () => {
577
+ const target = deleteTarget.trim();
578
+ if (!target) return;
579
+ if (!window.confirm(`${t("deleteProfileWarning")}\n\n${target}`)) return;
580
+ setBusy(true);
581
+ setError("");
582
+ try {
583
+ const value = await request("delete-profile", {
584
+ profileName: target,
585
+ confirmDelete: true
586
+ });
587
+ setDeleteReport(value);
588
+ if (value.status === "failed") showToast(`${t("error")}: ${failMessage(value) || t("error")}`);
589
+ else {
590
+ await loadProfiles();
591
+ showToast(`${t("deleteSuccess")} ${target}`);
592
+ }
593
+ } catch (caught) {
594
+ const message = caught instanceof Error ? caught.message : String(caught);
595
+ setError(message);
596
+ showToast(`${t("error")}: ${message}`);
597
+ } finally {
598
+ setBusy(false);
599
+ }
600
+ };
601
+ const switchSelectedProfile = async () => {
602
+ const target = switchTarget.trim();
603
+ if (!target) return;
604
+ if (target === currentRuntimeProfile) {
605
+ setSwitchReport({
606
+ status: "ok",
607
+ result: {
608
+ status: "already-active",
609
+ profileName: target,
610
+ message: `Profile is already active: ${target}`,
611
+ impact: "The current DSH process was not changed; the requested Profile is already running.",
612
+ canContinue: true
613
+ }
614
+ });
615
+ showToast(`${t("switchAlreadyActive")} ${target}`);
616
+ return;
617
+ }
618
+ if (!window.confirm(`${t("switchProfileWarning")}\n\n${target}`)) {
619
+ setSwitchReport({
620
+ status: "failed",
621
+ error: {
622
+ code: "SWITCH_CANCELED",
623
+ stage: "planning",
624
+ item: target,
625
+ message: t("switchCanceled")
626
+ }
627
+ });
628
+ showToast(t("switchCanceled"));
629
+ return;
630
+ }
631
+ setBusy(true);
632
+ setError("");
633
+ setSwitchReport(void 0);
634
+ try {
635
+ const scheduled = await request("switch-profile", {
636
+ profileName: target,
637
+ confirmSwitch: true
638
+ });
639
+ if (scheduled.status === "failed") {
640
+ setSwitchReport(scheduled);
641
+ showToast(`${t("error")}: ${failMessage(scheduled) || t("error")}`);
642
+ return;
643
+ }
644
+ const operationId = typeof scheduled.operationId === "string" ? scheduled.operationId : "";
645
+ let last = scheduled;
646
+ if (!operationId) {
647
+ setSwitchReport({
648
+ status: "failed",
649
+ error: {
650
+ code: "SWITCH_REPORT_MISSING",
651
+ stage: "scheduling",
652
+ message: "Switch operation did not return an operation ID."
653
+ }
654
+ });
655
+ showToast(`${t("error")}: ${t("error")}`);
656
+ return;
657
+ }
658
+ for (let attempt = 0; attempt < 130; attempt += 1) {
659
+ await new Promise((resolve) => setTimeout(resolve, 500));
660
+ try {
661
+ const value = await request("switch-status", { operationId });
662
+ last = value;
663
+ const result = resultOf(value);
664
+ if (result.status === "ready" || result.status === "failed") break;
665
+ } catch {}
666
+ }
667
+ setSwitchReport(last);
668
+ const result = resultOf(last);
669
+ if (result.status === "ready") {
670
+ await loadProfiles();
671
+ showToast(`${t("switchSuccess")} ${target}`);
672
+ } else if (result.status === "failed") showToast(`${t("error")}: ${failMessage(last) || t("error")}`);
673
+ } catch (caught) {
674
+ const message = caught instanceof Error ? caught.message : String(caught);
675
+ setError(message);
676
+ showToast(`${t("error")}: ${message}`);
677
+ } finally {
678
+ setBusy(false);
679
+ }
680
+ };
681
+ const importPack = async () => {
682
+ if (!file) return;
683
+ if (importMode === "new") {
684
+ const packResult = resultOf(report ?? {});
685
+ const pack = isObject(packResult.pack) ? packResult.pack : {};
686
+ const packProfile = isObject(pack.profile) ? pack.profile : {};
687
+ setNewProfileDraft(importTarget.trim() || String(packProfile.name ?? ""));
688
+ setNewProfileDialogError("");
689
+ setNewProfileDialogOpen(true);
690
+ return;
691
+ }
692
+ const target = importTarget.trim() || profiles[0]?.name || "";
693
+ if (!target) {
694
+ setError(t("noProfiles"));
695
+ return;
696
+ }
697
+ if (!window.confirm(`${t("overwriteWarning")}\n\n${target}`)) {
698
+ showToast(t("overwriteCanceled"));
699
+ setError("");
700
+ return;
701
+ }
702
+ setBusy(true);
703
+ setError("");
704
+ try {
705
+ const value = await request("import", {
706
+ packBase64: await toBase64(file),
707
+ targetProfile: target,
708
+ overwrite: true,
709
+ confirmOverwrite: true
710
+ });
711
+ setReport(value);
712
+ if (value.status === "failed" || !(isObject(value.result) && value.result.status === "prepared")) showToast(`${t("error")}: ${failMessage(value) || t("error")}`);
713
+ else {
714
+ await loadProfiles();
715
+ setProfiles((current) => isObject(value.profile) ? upsertProfileRow(current, value.profile) : current);
716
+ showToast(`${t("importOverwritten")} ${target}`);
717
+ }
718
+ } catch (caught) {
719
+ const message = caught instanceof Error ? caught.message : String(caught);
720
+ setError(message);
721
+ showToast(`${t("error")}: ${message}`);
722
+ } finally {
723
+ setBusy(false);
724
+ }
725
+ };
726
+ const confirmNewProfileImport = async () => {
727
+ if (!file) return;
728
+ const target = newProfileDraft.trim();
729
+ if (!target) {
730
+ setNewProfileDialogError(t("profileNameRequired"));
731
+ return;
732
+ }
733
+ if (profiles.some((item) => item.name === target)) {
734
+ setNewProfileDialogError(t("profileNameExists"));
735
+ return;
736
+ }
737
+ setImportTarget(target);
738
+ setNewProfileDialogOpen(false);
739
+ setNewProfileDialogError("");
740
+ setBusy(true);
741
+ setError("");
742
+ try {
743
+ const value = await request("import", {
744
+ packBase64: await toBase64(file),
745
+ targetProfile: target,
746
+ overwrite: false,
747
+ confirmOverwrite: false
748
+ });
749
+ setReport(value);
750
+ if (value.status === "failed" || !(isObject(value.result) && value.result.status === "prepared")) showToast(`${t("error")}: ${failMessage(value) || t("error")}`);
751
+ else {
752
+ await loadProfiles();
753
+ setProfiles((current) => isObject(value.profile) ? upsertProfileRow(current, value.profile) : current);
754
+ setProfile(target);
755
+ showToast(`${t("importSuccess")} ${target}`);
756
+ }
757
+ } catch (caught) {
758
+ const message = caught instanceof Error ? caught.message : String(caught);
759
+ setError(message);
760
+ showToast(`${t("error")}: ${message}`);
761
+ } finally {
762
+ setBusy(false);
763
+ }
764
+ };
765
+ const inspectFile = async (nextFile) => {
766
+ setFile(nextFile);
767
+ setReport(void 0);
768
+ setError("");
769
+ if (nextFile === void 0) return;
770
+ await run("inspect", { packBase64: await toBase64(nextFile) });
771
+ };
772
+ const input = /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
773
+ className: css.label,
774
+ children: [t("choosePack"), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
775
+ className: css.file,
776
+ type: "file",
777
+ accept: ".dshcrate,application/zip",
778
+ onChange: (event) => {
779
+ inspectFile(event.target.files?.[0]);
780
+ }
781
+ })]
782
+ });
783
+ const profileSelect = /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
784
+ className: css.label,
785
+ children: [t("profile"), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("select", {
786
+ className: css.select,
787
+ value: profile,
788
+ onChange: (event) => setProfile(event.target.value),
789
+ children: profiles.map((item) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
790
+ value: item.name,
791
+ children: item.name
792
+ }, item.name))
793
+ })]
794
+ });
795
+ const profileManager = /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
796
+ className: css.inventory,
797
+ "data-field": "profileManagement",
798
+ children: [
799
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: t("profileManagement") }),
800
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
801
+ className: css.muted,
802
+ children: [
803
+ t("currentRunningProfile"),
804
+ ": ",
805
+ currentRuntimeProfile || t("none")
806
+ ]
807
+ }),
808
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
809
+ className: css.label,
810
+ children: [t("createProfile"), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
811
+ className: css.file,
812
+ value: createTarget,
813
+ placeholder: t("createProfilePlaceholder"),
814
+ onChange: (event) => setCreateTarget(event.target.value)
815
+ })]
816
+ }),
817
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
818
+ className: css.muted,
819
+ children: t("createProfileHint")
820
+ }),
821
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
822
+ className: css.button,
823
+ type: "button",
824
+ disabled: busy || !createTarget.trim(),
825
+ onClick: () => void createSelectedProfile(),
826
+ children: busy ? t("createPending") : t("createProfile")
827
+ }),
828
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
829
+ className: css.label,
830
+ children: [t("switchProfile"), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("select", {
831
+ className: css.select,
832
+ value: switchTarget && switchTarget !== currentRuntimeProfile ? switchTarget : profiles.find((item) => item.name !== currentRuntimeProfile)?.name || "",
833
+ onChange: (event) => setSwitchTarget(event.target.value),
834
+ children: profiles.filter((item) => item.name !== currentRuntimeProfile).map((item) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
835
+ value: item.name,
836
+ children: item.name
837
+ }, item.name))
838
+ })]
839
+ }),
840
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
841
+ className: css.button,
842
+ type: "button",
843
+ disabled: busy || !switchTarget,
844
+ onClick: () => void switchSelectedProfile(),
845
+ children: busy ? t("switchPending") : t("switchProfile")
846
+ }),
847
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
848
+ className: css.label,
849
+ children: [t("deleteProfile"), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("select", {
850
+ className: css.select,
851
+ value: deleteTarget || profiles[0]?.name || "",
852
+ onChange: (event) => setDeleteTarget(event.target.value),
853
+ children: profiles.map((item) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
854
+ value: item.name,
855
+ children: item.name
856
+ }, item.name))
857
+ })]
858
+ }),
859
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
860
+ className: css.muted,
861
+ children: t("deleteProfileWarning")
862
+ }),
863
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
864
+ className: css.button,
865
+ type: "button",
866
+ disabled: busy || !deleteTarget,
867
+ onClick: () => void deleteSelectedProfile(),
868
+ children: t("confirmDelete")
869
+ }),
870
+ switchReport ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Report, {
871
+ value: switchReport,
872
+ t
873
+ }) : null,
874
+ deleteReport ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Report, {
875
+ value: deleteReport,
876
+ t
877
+ }) : null
878
+ ]
879
+ });
880
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
881
+ className: css.section,
882
+ children: [
883
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("style", { children: STYLE }),
884
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("h2", {
885
+ className: css.heading,
886
+ children: t("title")
887
+ }),
888
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
889
+ className: css.intro,
890
+ children: t("intro")
891
+ }),
892
+ profiles.length > 0 ? profileManager : null,
893
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
894
+ className: css.tabs,
895
+ role: "tablist",
896
+ children: [
897
+ "export",
898
+ "import",
899
+ "inspect",
900
+ "verify",
901
+ "history"
902
+ ].map((item) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
903
+ className: css.tab,
904
+ "data-active": tab === item,
905
+ type: "button",
906
+ role: "tab",
907
+ onClick: () => setTab(item),
908
+ children: t(item)
909
+ }, item))
910
+ }),
911
+ error ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("p", {
912
+ className: css.error,
913
+ role: "alert",
914
+ children: [
915
+ t("error"),
916
+ ": ",
917
+ error
918
+ ]
919
+ }) : null,
920
+ tab === "export" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
921
+ className: css.form,
922
+ children: profiles.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
923
+ className: css.muted,
924
+ children: t("noProfiles")
925
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
926
+ profileSelect,
927
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
928
+ className: css.inventory,
929
+ "data-field": "runtimePlugins",
930
+ children: [
931
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("strong", { children: [t("runtimePlugins"), runtimePlugins.length > 0 ? ` · ${runtimePlugins.length}` : ""] }),
932
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
933
+ className: css.muted,
934
+ children: [
935
+ t("updatedAt"),
936
+ ": ",
937
+ updatedAt || t("none")
938
+ ]
939
+ }),
940
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
941
+ className: css.button,
942
+ type: "button",
943
+ disabled: busy,
944
+ onClick: () => {
945
+ loadProfiles().catch((caught) => setError(caught instanceof Error ? caught.message : String(caught)));
946
+ },
947
+ children: t("refresh")
948
+ }),
949
+ currentRuntimeProfile && selectedProfile?.name !== currentRuntimeProfile ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
950
+ className: css.muted,
951
+ children: t("runtimePluginsInactive")
952
+ }) : runtimePlugins.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
953
+ className: css.muted,
954
+ children: t("none")
955
+ }) : (() => {
956
+ const groups = splitOfficial(runtimePlugins);
957
+ const row = (plugin) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
958
+ className: css.inventoryItem,
959
+ "data-field": "runtimePlugin",
960
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(PluginName, {
961
+ name: String(plugin.name ?? "unknown"),
962
+ official: plugin.official === true,
963
+ t
964
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: [
965
+ plugin.enabled === true ? t("runtimeEnabled") : t("runtimeDisabled"),
966
+ " · ",
967
+ String(plugin.phase ?? "—")
968
+ ] })]
969
+ }, `${String(plugin.id ?? plugin.name)}`);
970
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(PluginGroup, {
971
+ label: t("userPlugins"),
972
+ count: groups.user.length,
973
+ defaultOpen: true,
974
+ t,
975
+ children: groups.user.map(row)
976
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(PluginGroup, {
977
+ label: t("officialPlugins"),
978
+ count: groups.official.length,
979
+ defaultOpen: false,
980
+ t,
981
+ children: groups.official.map(row)
982
+ })] });
983
+ })()
984
+ ]
985
+ }),
986
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
987
+ className: css.inventory,
988
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: t("declaredBundles") }), exportBundles.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
989
+ className: css.muted,
990
+ children: t("none")
991
+ }) : (() => {
992
+ const groups = splitOfficial(exportBundles);
993
+ const row = (bundle) => {
994
+ const name = String(bundle.name ?? "");
995
+ const conflict = bundle.conflict === true;
996
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
997
+ className: css.inventoryItem,
998
+ children: [
999
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: [installedBundleLabel(bundle, t), conflict ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
1000
+ className: css.conflict,
1001
+ children: [" · ", t("conflictWarning")]
1002
+ }) : null] }),
1003
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: String(bundle.patch ?? "") }),
1004
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
1005
+ "aria-label": `${name} ${t("artifactMode")}`,
1006
+ className: css.select,
1007
+ disabled: conflict,
1008
+ value: modes[name] ?? "embedded",
1009
+ onChange: (event) => setModes((current) => ({
1010
+ ...current,
1011
+ [name]: event.target.value
1012
+ })),
1013
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
1014
+ value: "embedded",
1015
+ children: t("embedded")
1016
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
1017
+ value: "reference-only",
1018
+ children: t("referenceOnly")
1019
+ })]
1020
+ })
1021
+ ]
1022
+ }, `${name}-${String(bundle.version)}`);
1023
+ };
1024
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(PluginGroup, {
1025
+ label: t("userPlugins"),
1026
+ count: groups.user.length,
1027
+ defaultOpen: true,
1028
+ t,
1029
+ children: groups.user.map(row)
1030
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(PluginGroup, {
1031
+ label: t("officialPlugins"),
1032
+ count: groups.official.length,
1033
+ defaultOpen: false,
1034
+ t,
1035
+ children: groups.official.map(row)
1036
+ })] });
1037
+ })()]
1038
+ }),
1039
+ conflictBundles.length > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1040
+ className: css.inventory,
1041
+ "data-field": "conflictBundles",
1042
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: t("conflictBundles") }), conflictBundles.map((bundle) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1043
+ className: css.inventoryItem,
1044
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: installedBundleLabel(bundle, t) }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1045
+ className: css.conflict,
1046
+ children: t("conflictWarning")
1047
+ })]
1048
+ }, `conflict-${String(bundle.name)}`))]
1049
+ }) : null,
1050
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
1051
+ className: css.button,
1052
+ type: "button",
1053
+ disabled: busy || !profile,
1054
+ onClick: () => void run("export", {
1055
+ profileName: profile,
1056
+ includeInstalledBundles: true,
1057
+ embed: exportNames.filter((name) => (modes[name] ?? "embedded") === "embedded"),
1058
+ referenceOnly: exportNames.filter((name) => (modes[name] ?? "embedded") === "reference-only")
1059
+ }),
1060
+ children: busy ? t("exporting") : t("exportButton")
1061
+ }),
1062
+ isObject(report) && typeof report.downloadName === "string" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("a", {
1063
+ href: `/dsh-crate/download?name=${encodeURIComponent(report.downloadName)}`,
1064
+ children: t("download")
1065
+ }) : null,
1066
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ExportPlugins, {
1067
+ value: report,
1068
+ t
1069
+ }),
1070
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Report, {
1071
+ value: report,
1072
+ t
1073
+ })
1074
+ ] })
1075
+ }) : null,
1076
+ tab === "import" ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1077
+ className: css.form,
1078
+ children: [
1079
+ input,
1080
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
1081
+ className: css.label,
1082
+ children: [t("importMode"), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
1083
+ className: css.select,
1084
+ value: importMode,
1085
+ onChange: (event) => setImportMode(event.target.value),
1086
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
1087
+ value: "new",
1088
+ children: t("newProfile")
1089
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
1090
+ value: "overwrite",
1091
+ children: t("overwriteProfile")
1092
+ })]
1093
+ })]
1094
+ }),
1095
+ importMode === "new" ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
1096
+ className: css.label,
1097
+ children: [t("newProfileName"), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
1098
+ className: css.file,
1099
+ value: importTarget,
1100
+ placeholder: t("newProfileName"),
1101
+ onChange: (event) => setImportTarget(event.target.value)
1102
+ })]
1103
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
1104
+ className: css.label,
1105
+ children: [t("targetProfile"), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("select", {
1106
+ className: css.select,
1107
+ value: importTarget || profiles[0]?.name || "",
1108
+ onChange: (event) => setImportTarget(event.target.value),
1109
+ children: profiles.map((item) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
1110
+ value: item.name,
1111
+ children: item.name
1112
+ }, item.name))
1113
+ })]
1114
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1115
+ className: css.error,
1116
+ children: t("overwriteWarning")
1117
+ })] }),
1118
+ report ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ImportPreview, {
1119
+ value: report,
1120
+ t,
1121
+ targetProfile: (importMode === "overwrite" ? importTarget || profiles[0]?.name : importTarget) || void 0,
1122
+ overwrite: importMode === "overwrite"
1123
+ }) : null,
1124
+ report ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ImportSuccess, {
1125
+ value: report,
1126
+ t
1127
+ }) : null,
1128
+ report ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Report, {
1129
+ value: report,
1130
+ t
1131
+ }) : null,
1132
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
1133
+ className: css.button,
1134
+ type: "button",
1135
+ disabled: busy || !file || !canImport || importMode === "overwrite" && !importTarget && profiles.length === 0,
1136
+ onClick: () => void importPack(),
1137
+ children: busy ? t("working") : t("importButton")
1138
+ })
1139
+ ]
1140
+ }) : null,
1141
+ tab === "inspect" ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1142
+ className: css.form,
1143
+ children: [
1144
+ input,
1145
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Report, {
1146
+ value: report,
1147
+ t
1148
+ }),
1149
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
1150
+ className: css.button,
1151
+ type: "button",
1152
+ disabled: busy || !file,
1153
+ onClick: async () => {
1154
+ if (file) await run("inspect", { packBase64: await toBase64(file) });
1155
+ },
1156
+ children: busy ? t("working") : t("inspectButton")
1157
+ })
1158
+ ]
1159
+ }) : null,
1160
+ tab === "verify" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1161
+ className: css.form,
1162
+ children: profiles.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1163
+ className: css.muted,
1164
+ children: t("noProfiles")
1165
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
1166
+ profileSelect,
1167
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
1168
+ className: css.button,
1169
+ type: "button",
1170
+ disabled: busy || !profile,
1171
+ onClick: () => void run("verify", {
1172
+ profileName: profile,
1173
+ mode: "web"
1174
+ }),
1175
+ children: busy ? t("working") : t("verifyButton")
1176
+ }),
1177
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(VerifyReport, {
1178
+ value: report,
1179
+ t
1180
+ })
1181
+ ] })
1182
+ }) : null,
1183
+ tab === "history" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("ul", {
1184
+ className: css.history,
1185
+ children: history.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("li", {
1186
+ className: css.muted,
1187
+ children: t("noHistory")
1188
+ }) : history.map((item, index) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("li", {
1189
+ className: css.historyItem,
1190
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: [
1191
+ formatTime(item.time),
1192
+ " · ",
1193
+ item.action,
1194
+ " · ",
1195
+ item.profile ?? item.pack ?? ""
1196
+ ] }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", {
1197
+ className: toneOf(item.status),
1198
+ children: item.status
1199
+ })]
1200
+ }, `${item.time}-${index}`))
1201
+ }) : null,
1202
+ newProfileDialogOpen ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1203
+ className: css.modalBackdrop,
1204
+ role: "presentation",
1205
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1206
+ className: css.modal,
1207
+ role: "dialog",
1208
+ "aria-modal": "true",
1209
+ "aria-labelledby": "dsh-crate-new-profile-title",
1210
+ children: [
1211
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", {
1212
+ id: "dsh-crate-new-profile-title",
1213
+ children: t("newProfileName")
1214
+ }),
1215
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", { children: t("confirmImport") }),
1216
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
1217
+ className: css.label,
1218
+ children: [t("newProfileName"), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
1219
+ autoFocus: true,
1220
+ className: css.file,
1221
+ value: newProfileDraft,
1222
+ onChange: (event) => {
1223
+ setNewProfileDraft(event.target.value);
1224
+ setNewProfileDialogError("");
1225
+ }
1226
+ })]
1227
+ }),
1228
+ newProfileDialogError ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1229
+ className: css.error,
1230
+ role: "alert",
1231
+ children: newProfileDialogError
1232
+ }) : null,
1233
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1234
+ className: css.modalActions,
1235
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
1236
+ className: css.button,
1237
+ type: "button",
1238
+ onClick: () => setNewProfileDialogOpen(false),
1239
+ children: t("cancel")
1240
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
1241
+ className: css.button,
1242
+ type: "button",
1243
+ onClick: () => void confirmNewProfileImport(),
1244
+ children: t("confirmImportAction")
1245
+ })]
1246
+ })
1247
+ ]
1248
+ })
1249
+ }) : null,
1250
+ toast ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Toast, {
1251
+ text: toast.text,
1252
+ onDone: () => setToast(void 0)
1253
+ }, toast.key) : null
1254
+ ]
1255
+ });
1256
+ }
1257
+ //#endregion
1258
+ //#region src/client/PluginListTab.tsx
1259
+ /**
1260
+ * Plugins settings tab replacing the official read-only inventory list.
1261
+ * Official DSH built-ins are collapsed into their own group while plugins
1262
+ * installed or authored by the user stay expanded, so the two kinds are
1263
+ * visually separated instead of mixed into one flat list.
1264
+ */
1265
+ function PluginListTab({ t }) {
1266
+ const [plugins, setPlugins] = (0, react.useState)([]);
1267
+ const [status, setStatus] = (0, react.useState)("loading");
1268
+ const [tick, setTick] = (0, react.useState)(0);
1269
+ (0, react.useEffect)(() => {
1270
+ let current = true;
1271
+ setStatus("loading");
1272
+ request("profiles").then((value) => {
1273
+ if (!current) return;
1274
+ setPlugins(objects(value.runtimePlugins));
1275
+ setStatus("ready");
1276
+ }).catch(() => {
1277
+ if (current) setStatus("error");
1278
+ });
1279
+ return () => {
1280
+ current = false;
1281
+ };
1282
+ }, [tick]);
1283
+ const retry = () => setTick((value) => value + 1);
1284
+ const row = (plugin) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1285
+ className: css.inventoryItem,
1286
+ "data-field": "runtimePlugin",
1287
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(PluginName, {
1288
+ name: String(plugin.name ?? "unknown"),
1289
+ official: plugin.official === true,
1290
+ t
1291
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: [
1292
+ plugin.enabled === true ? t("runtimeEnabled") : t("runtimeDisabled"),
1293
+ " · ",
1294
+ String(plugin.phase ?? "—")
1295
+ ] })]
1296
+ }, `${String(plugin.id ?? plugin.name)}`);
1297
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1298
+ className: css.section,
1299
+ "data-field": "pluginListTab",
1300
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("style", { children: STYLE }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1301
+ className: css.inventory,
1302
+ "data-field": "runtimePlugins",
1303
+ children: [
1304
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("strong", { children: [t("runtimePlugins"), plugins.length > 0 ? ` · ${plugins.length}` : ""] }),
1305
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
1306
+ className: css.muted,
1307
+ children: [
1308
+ t("updatedAt"),
1309
+ ": ",
1310
+ status === "ready" ? (/* @__PURE__ */ new Date()).toLocaleTimeString() : t("none")
1311
+ ]
1312
+ }),
1313
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
1314
+ className: css.button,
1315
+ type: "button",
1316
+ disabled: status === "loading",
1317
+ onClick: retry,
1318
+ children: t("refresh")
1319
+ }),
1320
+ status === "loading" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1321
+ className: css.muted,
1322
+ children: t("pluginListLoading")
1323
+ }) : null,
1324
+ status === "error" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1325
+ className: css.error,
1326
+ role: "alert",
1327
+ children: t("pluginListError")
1328
+ }) : null,
1329
+ status === "ready" ? (() => {
1330
+ const groups = splitOfficial(plugins);
1331
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(PluginGroup, {
1332
+ label: t("userPlugins"),
1333
+ count: groups.user.length,
1334
+ defaultOpen: true,
1335
+ t,
1336
+ children: groups.user.map(row)
1337
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(PluginGroup, {
1338
+ label: t("officialPlugins"),
1339
+ count: groups.official.length,
1340
+ defaultOpen: false,
1341
+ t,
1342
+ children: groups.official.map(row)
1343
+ })] });
1344
+ })() : null
1345
+ ]
1346
+ })]
1347
+ });
1348
+ }
1349
+ //#endregion
1350
+ //#region src/client/locales.ts
1351
+ const en = {
1352
+ nav: "DSH Crate",
1353
+ title: "DSH Crate",
1354
+ intro: "Export, inspect, and import a tested DSH environment Crate.",
1355
+ export: "Export",
1356
+ import: "Import",
1357
+ inspect: "Inspect",
1358
+ history: "History",
1359
+ profile: "Profile",
1360
+ noProfiles: "No selectable Profiles were found in this DSH_HOME.",
1361
+ artifactMode: "Artifact mode",
1362
+ embedded: "Embedded",
1363
+ referenceOnly: "Reference-only",
1364
+ exportButton: "Export Crate",
1365
+ exporting: "Exporting…",
1366
+ choosePack: "Choose a .dshcrate file",
1367
+ inspectButton: "Run Preflight",
1368
+ importButton: "Import Crate",
1369
+ confirmImportAction: "Confirm Import",
1370
+ confirmImport: "Imports as a new Profile after a Preflight check. Continue?",
1371
+ working: "Working…",
1372
+ noReport: "No report yet.",
1373
+ noHistory: "No DSH Crate operations have been recorded.",
1374
+ download: "Download Crate",
1375
+ plugins: "Plugins",
1376
+ findings: "Findings",
1377
+ coreStatus: "Core status",
1378
+ rawJson: "Raw JSON",
1379
+ diagnostic: "Failure diagnostic",
1380
+ copyDiagnostic: "Copy full diagnostic",
1381
+ diagnosticCopied: "Copied",
1382
+ fullDiagnostic: "View full diagnostic",
1383
+ importPreview: "Import preview",
1384
+ targetProfile: "Target Profile",
1385
+ warningCount: "Warnings",
1386
+ requiredPlugins: "Required packages",
1387
+ optionalPlugins: "Optional packages",
1388
+ environment: "Environment required/current",
1389
+ coreDecision: "Core canContinue",
1390
+ safety: "Import safety",
1391
+ willNotOverwrite: "Creates a new Profile only; it will not overwrite the original Profile.",
1392
+ none: "None",
1393
+ verify: "Verify",
1394
+ verifyButton: "Run Verify",
1395
+ error: "Operation failed",
1396
+ installedBundles: "Installed Bundles",
1397
+ active: "Active in this Profile",
1398
+ installationAnchor: "Installation anchor only",
1399
+ anchorOnlyNote: "Installed but not composed in this Profile (installation anchor only; not exported): ",
1400
+ exportPlugins: "Exported plugins",
1401
+ bundle: "Bundle",
1402
+ notBundle: "Not a Bundle",
1403
+ importSuccess: "Import successful",
1404
+ preparedProfile: "Prepared Profile",
1405
+ installedPlugins: "Installed plugins",
1406
+ importMode: "Import mode",
1407
+ newProfile: "Add new Profile",
1408
+ overwriteProfile: "Overwrite existing Profile",
1409
+ newProfileName: "New Profile name",
1410
+ overwriteWarning: "This replaces all files in the selected Profile.",
1411
+ overwritePreview: "Existing Profile will be replaced after confirmation.",
1412
+ confirmOverwrite: "Confirm overwrite",
1413
+ overwriteCanceled: "Overwrite canceled.",
1414
+ deleteProfile: "Delete Profile",
1415
+ deleteProfileWarning: "This permanently deletes the selected Profile.",
1416
+ confirmDelete: "Confirm deletion",
1417
+ deleteSuccess: "Profile deleted.",
1418
+ profileNameRequired: "Profile name is required.",
1419
+ profileNameExists: "That Profile name already exists.",
1420
+ cancel: "Cancel",
1421
+ createProfile: "New Profile",
1422
+ createProfilePlaceholder: "new profile name",
1423
+ createProfileHint: "A new Profile includes the official DSH bundles and the DSH Crate plugin by default.",
1424
+ createPending: "Creating…",
1425
+ createSuccess: "Profile created.",
1426
+ profileManagement: "Profile management",
1427
+ currentRunningProfile: "Currently running",
1428
+ switchProfile: "Switch and restart Profile",
1429
+ switchProfileWarning: "DSH will stop and restart using the selected Profile.",
1430
+ switchCanceled: "Profile switch canceled.",
1431
+ switchSuccess: "Profile switched and DSH restarted.",
1432
+ switchAlreadyActive: "Profile is already running.",
1433
+ declaredBundles: "Declared Bundles (Profile)",
1434
+ runtimePlugins: "Runtime plugins (currently running Profile)",
1435
+ runtimeEnabled: "enabled",
1436
+ runtimeDisabled: "disabled",
1437
+ runtimePluginsInactive: "Select the currently running Profile to see its live runtime plugin list.",
1438
+ updatedAt: "Last refreshed",
1439
+ refresh: "Refresh",
1440
+ pluginListTab: "Plugin list",
1441
+ pluginListLoading: "Reading plugins…",
1442
+ pluginListError: "Plugins are temporarily unavailable.",
1443
+ retry: "Retry",
1444
+ officialPlugins: "Official built-in plugins",
1445
+ userPlugins: "User-installed & written plugins",
1446
+ officialBadge: "Official",
1447
+ conflictWarning: "Conflicts with an official built-in; excluded from export",
1448
+ conflictBundles: "Conflicted Bundles (not exported)",
1449
+ collapse: "Collapse",
1450
+ expand: "Expand",
1451
+ exportSuccess: "Crate exported successfully.",
1452
+ inspectSuccess: "Preflight finished.",
1453
+ verifySuccess: "Verification passed.",
1454
+ verifyFailed: "Verification failed.",
1455
+ verifyFinished: "Verification finished.",
1456
+ verifyUntestedNote: "UNTESTED means composition was checked only; runtime, model, session, and plugin behavior were NOT actually verified.",
1457
+ importOverwritten: "Profile overwritten.",
1458
+ switchPending: "Switching Profile and waiting for DSH ready…",
1459
+ diagCode: "Code",
1460
+ diagStage: "Stage",
1461
+ diagItem: "Item",
1462
+ diagExpected: "Expected",
1463
+ diagObserved: "Observed",
1464
+ diagEvidence: "Evidence",
1465
+ diagImpact: "Impact",
1466
+ diagOriginalProfile: "Original Profile",
1467
+ diagFailedProfile: "Failed Profile",
1468
+ diagTemporaryProfile: "Temporary Profile",
1469
+ diagCanContinue: "Can continue",
1470
+ diagSuggestedNext: "Suggested next step"
1471
+ };
1472
+ const zh = {
1473
+ nav: "DSH Crate",
1474
+ title: "DSH Crate",
1475
+ intro: "导出、检查和导入经过测试的 DSH 环境 Crate。",
1476
+ export: "导出",
1477
+ import: "导入",
1478
+ inspect: "检查",
1479
+ history: "历史",
1480
+ profile: "Profile",
1481
+ noProfiles: "当前 DSH_HOME 没有可选择的 Profile。",
1482
+ artifactMode: "Artifact 模式",
1483
+ embedded: "内嵌",
1484
+ referenceOnly: "仅引用",
1485
+ exportButton: "导出 Crate",
1486
+ exporting: "导出中…",
1487
+ choosePack: "选择 .dshcrate 文件",
1488
+ inspectButton: "运行 Preflight",
1489
+ importButton: "导入 Crate",
1490
+ confirmImportAction: "确认导入",
1491
+ confirmImport: "将作为新 Profile 导入,导入前会先执行 Preflight 检查。是否继续?",
1492
+ working: "处理中…",
1493
+ noReport: "暂无报告。",
1494
+ noHistory: "还没有 DSH Crate 操作记录。",
1495
+ download: "下载 Crate",
1496
+ plugins: "插件",
1497
+ findings: "诊断项",
1498
+ coreStatus: "Core 状态",
1499
+ rawJson: "原始 JSON",
1500
+ diagnostic: "失败诊断",
1501
+ copyDiagnostic: "复制完整诊断",
1502
+ diagnosticCopied: "已复制",
1503
+ fullDiagnostic: "查看完整诊断",
1504
+ importPreview: "导入预览",
1505
+ targetProfile: "目标 Profile",
1506
+ warningCount: "WARNING 数量",
1507
+ requiredPlugins: "required 包",
1508
+ optionalPlugins: "optional 包",
1509
+ environment: "环境要求/当前值",
1510
+ coreDecision: "Core canContinue",
1511
+ safety: "导入安全",
1512
+ willNotOverwrite: "只创建新 Profile;不会覆盖原 Profile。",
1513
+ none: "无",
1514
+ verify: "验证",
1515
+ verifyButton: "运行 Verify",
1516
+ error: "操作失败",
1517
+ installedBundles: "已安装 Bundle",
1518
+ active: "当前 Profile 已组合",
1519
+ installationAnchor: "仅安装锚点",
1520
+ anchorOnlyNote: "已安装但未组合进当前 Profile(仅安装锚点,不导出):",
1521
+ exportPlugins: "导出插件",
1522
+ bundle: "Bundle",
1523
+ notBundle: "普通依赖",
1524
+ importSuccess: "导入成功",
1525
+ preparedProfile: "已准备 Profile",
1526
+ installedPlugins: "已安装插件",
1527
+ importMode: "导入模式",
1528
+ newProfile: "添加新 Profile",
1529
+ overwriteProfile: "覆盖已有 Profile",
1530
+ newProfileName: "新 Profile 名称",
1531
+ overwriteWarning: "这会替换所选 Profile 中的全部文件。",
1532
+ overwritePreview: "确认后将替换已有 Profile。",
1533
+ confirmOverwrite: "确认覆盖",
1534
+ overwriteCanceled: "已取消覆盖。",
1535
+ deleteProfile: "删除 Profile",
1536
+ deleteProfileWarning: "这会永久删除所选 Profile。",
1537
+ confirmDelete: "确认删除",
1538
+ deleteSuccess: "Profile 已删除。",
1539
+ profileNameRequired: "必须填写 Profile 名称。",
1540
+ profileNameExists: "该 Profile 名称已存在。",
1541
+ cancel: "取消",
1542
+ createProfile: "新建 Profile",
1543
+ createProfilePlaceholder: "新 Profile 名称",
1544
+ createProfileHint: "新 Profile 默认包含官方插件与本插件(DSH Crate)。",
1545
+ createPending: "创建中…",
1546
+ createSuccess: "Profile 已创建。",
1547
+ profileManagement: "Profile 管理",
1548
+ currentRunningProfile: "当前运行 Profile",
1549
+ switchProfile: "切换并重启 Profile",
1550
+ switchProfileWarning: "DSH 将停止当前进程,并使用所选 Profile 重新启动。",
1551
+ switchCanceled: "已取消 Profile 切换。",
1552
+ switchSuccess: "Profile 已切换并完成 DSH 重启。",
1553
+ switchAlreadyActive: "该 Profile 已在运行。",
1554
+ declaredBundles: "声明 Bundle(Profile)",
1555
+ runtimePlugins: "运行时插件(当前运行 Profile)",
1556
+ runtimeEnabled: "已启用",
1557
+ runtimeDisabled: "已禁用",
1558
+ runtimePluginsInactive: "选择当前运行的 Profile 以查看其实时运行的插件列表。",
1559
+ updatedAt: "最近刷新",
1560
+ refresh: "刷新",
1561
+ pluginListTab: "插件列表",
1562
+ pluginListLoading: "正在读取插件…",
1563
+ pluginListError: "暂时无法读取插件。",
1564
+ retry: "重试",
1565
+ officialPlugins: "官方内置插件",
1566
+ userPlugins: "用户安装与编写的插件",
1567
+ officialBadge: "官方",
1568
+ conflictWarning: "与官方内置插件冲突,已排除导出",
1569
+ conflictBundles: "冲突 Bundle(未导出)",
1570
+ collapse: "折叠",
1571
+ expand: "展开",
1572
+ exportSuccess: "Crate 导出成功。",
1573
+ inspectSuccess: "Preflight 检查完成。",
1574
+ verifySuccess: "验证通过。",
1575
+ verifyFailed: "验证未通过。",
1576
+ verifyFinished: "验证完成。",
1577
+ verifyUntestedNote: "UNTESTED 表示仅验证了组合(composition),运行时、模型、会话、插件业务行为均未实际验证。",
1578
+ importOverwritten: "Profile 已覆盖。",
1579
+ switchPending: "正在切换 Profile,等待 DSH ready…",
1580
+ diagCode: "诊断代码",
1581
+ diagStage: "失败阶段",
1582
+ diagItem: "对象",
1583
+ diagExpected: "预期",
1584
+ diagObserved: "实际",
1585
+ diagEvidence: "证据",
1586
+ diagImpact: "影响",
1587
+ diagOriginalProfile: "原 Profile 状态",
1588
+ diagFailedProfile: "失败 Profile 状态",
1589
+ diagTemporaryProfile: "临时 Profile 状态",
1590
+ diagCanContinue: "是否可继续",
1591
+ diagSuggestedNext: "建议下一步"
1592
+ };
1593
+ //#endregion
1594
+ //#region src/client/index.tsx
1595
+ const NS = "dsh.crate";
1596
+ const inject = ["slots", "locale"];
1597
+ function apply(ctx) {
1598
+ ctx.effect(() => ctx.locale.register(NS, {
1599
+ en,
1600
+ zh
1601
+ }), "dsh-crate-web: dictionaries");
1602
+ const t = ctx.locale.bind(NS);
1603
+ ctx.slots.inject("settings.section", () => ctx.slots.register({
1604
+ name: "settings.section",
1605
+ id: "dsh-crate",
1606
+ order: 30,
1607
+ label: () => t("nav"),
1608
+ locale: NS
1609
+ }, DshCratePage));
1610
+ ctx.slots.inject("settings.plugins.tab", () => ctx.slots.register({
1611
+ name: "settings.plugins.tab",
1612
+ id: "crate",
1613
+ order: 10,
1614
+ label: () => t("pluginListTab"),
1615
+ locale: NS
1616
+ }, PluginListTab));
1617
+ }
1618
+ //#endregion
1619
+ exports.apply = apply;
1620
+ exports.inject = inject;
1621
+ return module.exports;
1622
+ }
1623
+ });