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.
- package/README.md +302 -0
- package/README.zh-CN.md +304 -0
- package/cordis.patch.yml +11 -0
- package/core/dsh_pack/__init__.py +77 -0
- package/core/dsh_pack/__main__.py +4 -0
- package/core/dsh_pack/cli.py +372 -0
- package/core/dsh_pack/errors.py +167 -0
- package/core/dsh_pack/export.py +912 -0
- package/core/dsh_pack/importer.py +1347 -0
- package/core/dsh_pack/network.py +77 -0
- package/core/dsh_pack/pack.py +248 -0
- package/core/dsh_pack/preflight.py +811 -0
- package/core/dsh_pack/user_cli.py +462 -0
- package/core/dsh_pack/validation.py +272 -0
- package/core/dsh_pack/verify.py +1336 -0
- package/lib/client.js +1623 -0
- package/lib/index.d.ts +30 -0
- package/lib/index.js +1275 -0
- package/package.json +73 -0
- package/restart-helper.mjs +121 -0
- package/runtime/python/LICENSE.txt +676 -0
- package/runtime/python/__install__.json +82 -0
- package/runtime/python/_asyncio.pyd +0 -0
- package/runtime/python/_bz2.pyd +0 -0
- package/runtime/python/_ctypes.pyd +0 -0
- package/runtime/python/_decimal.pyd +0 -0
- package/runtime/python/_elementtree.pyd +0 -0
- package/runtime/python/_hashlib.pyd +0 -0
- package/runtime/python/_lzma.pyd +0 -0
- package/runtime/python/_multiprocessing.pyd +0 -0
- package/runtime/python/_overlapped.pyd +0 -0
- package/runtime/python/_queue.pyd +0 -0
- package/runtime/python/_remote_debugging.pyd +0 -0
- package/runtime/python/_socket.pyd +0 -0
- package/runtime/python/_sqlite3.pyd +0 -0
- package/runtime/python/_ssl.pyd +0 -0
- package/runtime/python/_uuid.pyd +0 -0
- package/runtime/python/_wmi.pyd +0 -0
- package/runtime/python/_zoneinfo.pyd +0 -0
- package/runtime/python/_zstd.pyd +0 -0
- package/runtime/python/libcrypto-3.dll +0 -0
- package/runtime/python/libffi-8.dll +0 -0
- package/runtime/python/libssl-3.dll +0 -0
- package/runtime/python/pyexpat.pyd +0 -0
- package/runtime/python/python.cat +0 -0
- package/runtime/python/python.exe +0 -0
- package/runtime/python/python3.dll +0 -0
- package/runtime/python/python314._pth +6 -0
- package/runtime/python/python314.dll +0 -0
- package/runtime/python/python314.zip +0 -0
- package/runtime/python/pythonw.exe +0 -0
- package/runtime/python/select.pyd +0 -0
- package/runtime/python/sqlite3.dll +0 -0
- package/runtime/python/unicodedata.pyd +0 -0
- package/runtime/python/vcruntime140.dll +0 -0
- package/runtime/python/vcruntime140_1.dll +0 -0
- 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
|
+
});
|