@ran-sh/dsh-crew 1.1.1 → 1.2.0-rc.4

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 (83) hide show
  1. package/.claude-plugin/marketplace.json +16 -17
  2. package/.claude-plugin/plugin.json +7 -1
  3. package/README.md +44 -15
  4. package/README.zh.md +37 -12
  5. package/agents/ds-flash.md +16 -18
  6. package/agents/ds-pro.md +16 -24
  7. package/agents/ds-reviewer.md +15 -15
  8. package/agents/ds-worker.md +16 -16
  9. package/bin/dsh-crew.mjs +16 -12
  10. package/codex/AGENTS.md +37 -85
  11. package/codex/agents/ds-flash.toml +18 -14
  12. package/codex/agents/ds-pro.toml +18 -15
  13. package/codex/agents/ds-reviewer.toml +17 -13
  14. package/codex/agents/ds-worker.toml +17 -13
  15. package/codex/prompts/dsh-config.md +17 -1
  16. package/codex/prompts/dsh-status.md +5 -1
  17. package/commands/config.md +16 -4
  18. package/commands/status.md +6 -2
  19. package/docs/installation.md +54 -9
  20. package/docs/readiness-matrix.md +14 -6
  21. package/docs/ui-surfaces.md +32 -17
  22. package/lib/client.js +5434 -4844
  23. package/official-web-bridge/lib/client.js +567 -479
  24. package/official-web-bridge/overlay-entry.mjs +59 -0
  25. package/package.json +6 -3
  26. package/scripts/build-client.mjs +2 -0
  27. package/scripts/setup.mjs +8 -7
  28. package/scripts/verify-crew-ui-polish.mjs +145 -0
  29. package/scripts/verify-history-ui.mjs +97 -0
  30. package/scripts/verify-npm-install.mjs +1 -1
  31. package/src/client/history-panel.tsx +108 -0
  32. package/src/client/host-readiness.mjs +15 -6
  33. package/src/client/index.tsx +86 -64
  34. package/src/client/model-callability-view.mjs +17 -0
  35. package/src/client/panel-chrome.tsx +40 -0
  36. package/src/client/quick-panel.tsx +88 -73
  37. package/src/client/readiness-envelope.mjs +34 -0
  38. package/src/config-readiness.mjs +181 -115
  39. package/src/extension-contract.mjs +44 -26
  40. package/src/history/admission-gate.mjs +51 -0
  41. package/src/history/archive-store.mjs +261 -0
  42. package/src/history/cleanup-plan.mjs +64 -0
  43. package/src/history/http.mjs +32 -0
  44. package/src/history/operation.mjs +86 -0
  45. package/src/history/runner.mjs +51 -0
  46. package/src/history/runtime.mjs +28 -0
  47. package/src/history/service.mjs +137 -0
  48. package/src/history/state.mjs +27 -0
  49. package/src/hub/index.mjs +141 -24
  50. package/src/hub-client.mjs +17 -15
  51. package/src/image-output.mjs +71 -0
  52. package/src/install/install-legacy.mjs +126 -23
  53. package/src/install/npx-lifecycle.mjs +373 -64
  54. package/src/install/official-frontend-assets.mjs +78 -0
  55. package/src/install/payload-content.mjs +88 -0
  56. package/src/install/windows-startup.mjs +107 -19
  57. package/src/install/windows-supervisor-adapter.mjs +443 -0
  58. package/src/install/windows-supervisor-lifecycle.mjs +782 -0
  59. package/src/install/zcode.mjs +53 -14
  60. package/src/job-contracts.mjs +37 -31
  61. package/src/jobs.mjs +2 -2
  62. package/src/mcp-runtime.mjs +74 -9
  63. package/src/model-callability-contract.mjs +79 -0
  64. package/src/multimodal.mjs +0 -0
  65. package/src/official-web-bridge.mjs +0 -2
  66. package/src/provider-health.mjs +130 -124
  67. package/src/readiness-matrix.mjs +20 -15
  68. package/src/runtime-identity-contract.mjs +34 -0
  69. package/src/runtime-identity.mjs +14 -4
  70. package/src/runtime-readiness-snapshot.mjs +285 -87
  71. package/src/server.mjs +86 -76
  72. package/src/standalone-sdk.mjs +23 -0
  73. package/src/supervisor/restart-request.mjs +64 -10
  74. package/src/workflow-runtime.mjs +28 -13
  75. package/windows/start-dsh-crew.cmd +57 -57
  76. package/windows/start-dsh-crew.ps1 +314 -51
  77. package/windows/supervisor-control.ps1 +467 -0
  78. package/zcode/AGENTS.md +4 -1
  79. package/zcode/agents/ds-reviewer.md +31 -36
  80. package/zcode/agents/ds-worker.md +31 -39
  81. package/zcode/commands/dsh-config.md +17 -6
  82. package/zcode/commands/dsh-status.md +5 -4
  83. package/.mcp.json +0 -8
@@ -1,479 +1,567 @@
1
- window.__ModuleLoader__.load({ id: "@ran-sh/dsh-crew-web-bridge", factory: (require) => {
2
- var module = { exports: {} }; var exports = module.exports;
3
- Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
4
- let react = require("react");
5
- let react_jsx_runtime = require("react/jsx-runtime");
6
- //#region src/client/quick-panel.tsx
7
- const API = "/_dsh/dsh-crew";
8
- const FULL = "http://127.0.0.1:3210/";
9
- const RESTART_KEYS = /* @__PURE__ */ new Set([
10
- "vision_enabled",
11
- "imagegen_enabled",
12
- "vision_provider",
13
- "imagegen_provider"
14
- ]);
15
- const T = {
16
- zh: {
17
- title: "DSH Crew 快捷控制",
18
- openFull: "打开完整设置 →",
19
- running: "运行中",
20
- unavailable: "Crew 后端不可用",
21
- openDiag: "打开诊断",
22
- crew: "Crew",
23
- enabled: "启用子 Agent",
24
- flash: "Flash 模型",
25
- pro: "Pro 模型",
26
- addModel: "+ 添加模型",
27
- multimodal: "多模态",
28
- vision: "视觉",
29
- imagegen: "生图",
30
- provider: "Provider",
31
- applyRestart: "应用并重启 Crew",
32
- savedNeedsRestart: "配置已保存 · 需要重启 Crew 才会生效",
33
- saved: "已保存",
34
- working: "处理中…",
35
- providerPlaceholder: "provider",
36
- modelPlaceholder: "model"
37
- },
38
- en: {
39
- title: "DSH Crew Quick Controls",
40
- openFull: "Open full settings →",
41
- running: "Running",
42
- unavailable: "Crew backend unavailable",
43
- openDiag: "Open diagnostics",
44
- crew: "Crew",
45
- enabled: "Enable sub-agents",
46
- flash: "Flash models",
47
- pro: "Pro models",
48
- addModel: "+ Add model",
49
- multimodal: "Multimodal",
50
- vision: "Vision",
51
- imagegen: "Imagegen",
52
- provider: "Provider",
53
- applyRestart: "Apply & restart Crew",
54
- savedNeedsRestart: "Saved · restart Crew to take effect",
55
- saved: "Saved",
56
- working: "Working…",
57
- providerPlaceholder: "provider",
58
- modelPlaceholder: "model"
59
- }
60
- };
61
- function LocalStyles() {
62
- return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("style", { children: `
63
- .crew-quick-card { border: 1px solid rgba(128,128,128,0.24); border-radius: 12px; padding: 14px 15px; font-size: 13px; line-height: 1.55; display: flex; flex-direction: column; gap: 10px; background: linear-gradient(135deg, rgba(74,158,255,0.10), rgba(128,128,128,0.025) 56%); }
64
- .crew-quick-row { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }
65
- .crew-quick-title { font-size: 16px; font-weight: 680; }
66
- .crew-quick-section { font-weight: 650; opacity: 0.85; }
67
- .crew-quick-chip { border: 1px solid rgba(128,128,128,0.35); border-radius: 999px; padding: 1px 10px; font-size: 12px; }
68
- .crew-quick-btn { border: 1px solid rgba(128,128,128,0.4); border-radius: 8px; padding: 2px 10px; cursor: pointer; background: transparent; font-size: 12.5px; }
69
- .crew-quick-btn.primary { border-color: #4a9eff; color: #4a9eff; font-weight: 650; }
70
- .crew-quick-input { border: 1px solid rgba(128,128,128,0.35); border-radius: 6px; padding: 2px 8px; font-size: 12.5px; width: 130px; background: transparent; color: inherit; }
71
- .crew-quick-notice { opacity: 0.75; font-size: 12.5px; }
72
- .crew-quick-model { display: flex; gap: 6px; align-items: center; font-size: 12.5px; }
73
- ` });
74
- }
75
- async function readJson(res) {
76
- if (!res.ok) return {
77
- ok: false,
78
- status: res.status
79
- };
80
- try {
81
- return await res.json();
82
- } catch {
83
- return { ok: false };
84
- }
85
- }
86
- function QuickPanel({ ctx }) {
87
- const locale = ctx?.locale?.getLocale?.().active === "zh" ? "zh" : "en";
88
- const t = T[locale] ?? T.zh;
89
- const [config, setConfig] = (0, react.useState)(null);
90
- const [ready, setReady] = (0, react.useState)(null);
91
- const [notice, setNotice] = (0, react.useState)("");
92
- const [busy, setBusy] = (0, react.useState)(false);
93
- const [restartPending, setRestartPending] = (0, react.useState)(false);
94
- const [drafts, setDrafts] = (0, react.useState)({});
95
- const load = (0, react.useCallback)(async () => {
96
- try {
97
- const status = await readJson(await fetch(`${API}/quick-status`, { cache: "no-store" }));
98
- if (!status.ok) {
99
- setReady(false);
100
- return;
101
- }
102
- setReady(true);
103
- setConfig(status.config ?? {});
104
- } catch {
105
- setReady(false);
106
- }
107
- }, []);
108
- (0, react.useEffect)(() => {
109
- load();
110
- }, [load]);
111
- const patch = (0, react.useCallback)(async (next) => {
112
- setBusy(true);
113
- setNotice(t.working);
114
- try {
115
- const res = await readJson(await fetch(`${API}/quick-config`, {
116
- method: "POST",
117
- headers: { "content-type": "application/json" },
118
- body: JSON.stringify(next)
119
- }));
120
- if (!res.ok) throw new Error(res.code ?? res.error ?? "save failed");
121
- setConfig(res.config ?? {});
122
- if (Object.keys(next).some((k) => RESTART_KEYS.has(k))) {
123
- setRestartPending(true);
124
- setNotice(t.savedNeedsRestart);
125
- } else {
126
- setNotice(t.saved);
127
- setTimeout(() => setNotice(""), 1500);
128
- }
129
- return true;
130
- } catch (e) {
131
- setNotice(String(e?.message ?? e));
132
- return false;
133
- } finally {
134
- setBusy(false);
135
- }
136
- }, [t]);
137
- const toggle = (key, value) => {
138
- setConfig((c) => ({
139
- ...c ?? {},
140
- [key]: value
141
- }));
142
- patch({ [key]: value });
143
- };
144
- const setProvider = (key, value) => {
145
- setConfig((c) => ({
146
- ...c ?? {},
147
- [key]: value
148
- }));
149
- patch({ [key]: value });
150
- };
151
- const moveModel = (listKey, index, dir) => {
152
- const list = [...config?.[listKey] ?? []];
153
- const to = index + dir;
154
- if (to < 0 || to >= list.length) return;
155
- [list[index], list[to]] = [list[to], list[index]];
156
- setConfig((c) => ({
157
- ...c ?? {},
158
- [listKey]: list
159
- }));
160
- patch({ [listKey]: list });
161
- };
162
- const removeModel = (listKey, index) => {
163
- const list = [...config?.[listKey] ?? []];
164
- list.splice(index, 1);
165
- setConfig((c) => ({
166
- ...c ?? {},
167
- [listKey]: list
168
- }));
169
- patch({ [listKey]: list });
170
- };
171
- const addModel = (listKey) => {
172
- const draft = drafts[listKey] ?? {
173
- provider: "",
174
- model: ""
175
- };
176
- if (!draft.provider.trim() || !draft.model.trim()) return;
177
- const list = [...config?.[listKey] ?? [], {
178
- provider: draft.provider.trim(),
179
- model: draft.model.trim()
180
- }];
181
- setConfig((c) => ({
182
- ...c ?? {},
183
- [listKey]: list
184
- }));
185
- setDrafts((d) => ({
186
- ...d,
187
- [listKey]: {
188
- provider: "",
189
- model: ""
190
- }
191
- }));
192
- patch({ [listKey]: list });
193
- };
194
- const applyRestart = (0, react.useCallback)(async () => {
195
- setBusy(true);
196
- setNotice(t.working);
197
- try {
198
- const created = await readJson(await fetch(`${API}/runtime/restart-request`, {
199
- method: "POST",
200
- headers: { "content-type": "application/json" },
201
- body: JSON.stringify({
202
- confirm: true,
203
- reason: "quick panel restart"
204
- })
205
- }));
206
- if (!created.ok) throw new Error(created.code ?? created.error ?? "restart failed");
207
- const requestId = created.request_id;
208
- const deadline = Date.now() + 9e4;
209
- for (;;) {
210
- await new Promise((r) => setTimeout(r, 1e3));
211
- if (Date.now() > deadline) throw new Error("restart timed out");
212
- const status = await readJson(await fetch(`${API}/runtime/restart-status?id=${encodeURIComponent(requestId)}`, { cache: "no-store" }));
213
- if (!status.ok) continue;
214
- if (status.state === "VERIFIED") {
215
- setRestartPending(false);
216
- setNotice(t.saved);
217
- return;
218
- }
219
- if (status.state !== "RESTART_REQUESTED") throw new Error(status.state ?? "restart failed");
220
- }
221
- } catch (e) {
222
- setNotice(String(e?.message ?? e));
223
- } finally {
224
- setBusy(false);
225
- }
226
- }, [t]);
227
- const modelList = (listKey, label) => {
228
- const list = config?.[listKey] ?? [];
229
- const draft = drafts[listKey] ?? {
230
- provider: "",
231
- model: ""
232
- };
233
- return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
234
- style: {
235
- display: "flex",
236
- flexDirection: "column",
237
- gap: 4
238
- },
239
- children: [
240
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
241
- className: "crew-quick-section",
242
- children: label
243
- }),
244
- list.map((entry, i) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
245
- className: "crew-quick-model",
246
- children: [
247
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: [
248
- i + 1,
249
- ". ",
250
- entry.provider,
251
- " / ",
252
- entry.model
253
- ] }),
254
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
255
- className: "crew-quick-btn",
256
- disabled: busy || i === 0,
257
- onClick: () => moveModel(listKey, i, -1),
258
- children: "↑"
259
- }),
260
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
261
- className: "crew-quick-btn",
262
- disabled: busy || i === list.length - 1,
263
- onClick: () => moveModel(listKey, i, 1),
264
- children: "↓"
265
- }),
266
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
267
- className: "crew-quick-btn",
268
- disabled: busy,
269
- onClick: () => removeModel(listKey, i),
270
- children: "×"
271
- })
272
- ]
273
- }, `${entry.provider}/${entry.model}/${i}`)),
274
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
275
- className: "crew-quick-row",
276
- children: [
277
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
278
- className: "crew-quick-input",
279
- placeholder: t.providerPlaceholder,
280
- disabled: busy,
281
- value: draft.provider,
282
- onChange: (e) => setDrafts((d) => ({
283
- ...d,
284
- [listKey]: {
285
- ...draft,
286
- provider: e.target.value
287
- }
288
- }))
289
- }),
290
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
291
- className: "crew-quick-input",
292
- placeholder: t.modelPlaceholder,
293
- disabled: busy,
294
- value: draft.model,
295
- onChange: (e) => setDrafts((d) => ({
296
- ...d,
297
- [listKey]: {
298
- ...draft,
299
- model: e.target.value
300
- }
301
- }))
302
- }),
303
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
304
- className: "crew-quick-btn",
305
- disabled: busy,
306
- onClick: () => addModel(listKey),
307
- children: t.addModel
308
- })
309
- ]
310
- })
311
- ]
312
- });
313
- };
314
- if (ready === false) return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
315
- className: "crew-quick-card",
316
- children: [
317
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(LocalStyles, {}),
318
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
319
- className: "crew-quick-title",
320
- children: t.title
321
- }),
322
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
323
- className: "crew-quick-notice",
324
- children: t.unavailable
325
- }),
326
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
327
- className: "crew-quick-row",
328
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("a", {
329
- href: FULL,
330
- target: "_blank",
331
- rel: "noreferrer",
332
- children: t.openDiag
333
- })
334
- })
335
- ]
336
- });
337
- if (config === null) return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
338
- className: "crew-quick-card",
339
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(LocalStyles, {}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
340
- className: "crew-quick-notice",
341
- children: t.working
342
- })]
343
- });
344
- return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
345
- className: "crew-quick-card",
346
- children: [
347
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(LocalStyles, {}),
348
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
349
- className: "crew-quick-row",
350
- style: { justifyContent: "space-between" },
351
- children: [
352
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
353
- className: "crew-quick-title",
354
- children: t.title
355
- }),
356
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
357
- className: "crew-quick-chip",
358
- children: [t.running, " · 3210"]
359
- }),
360
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("a", {
361
- href: FULL,
362
- target: "_blank",
363
- rel: "noreferrer",
364
- children: t.openFull
365
- })
366
- ]
367
- }),
368
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
369
- className: "crew-quick-row",
370
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
371
- className: "crew-quick-section",
372
- children: t.crew
373
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", { children: [
374
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
375
- type: "checkbox",
376
- disabled: busy,
377
- checked: config.subagents_enabled !== false,
378
- onChange: (e) => toggle("subagents_enabled", e.target.checked)
379
- }),
380
- " ",
381
- t.enabled
382
- ] })]
383
- }),
384
- modelList("flash_model_priority", t.flash),
385
- modelList("pro_model_priority", t.pro),
386
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
387
- style: {
388
- display: "flex",
389
- flexDirection: "column",
390
- gap: 4
391
- },
392
- children: [
393
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
394
- className: "crew-quick-section",
395
- children: t.multimodal
396
- }),
397
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
398
- className: "crew-quick-row",
399
- children: [
400
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", { children: [
401
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
402
- type: "checkbox",
403
- disabled: busy,
404
- checked: config.vision_enabled === true,
405
- onChange: (e) => toggle("vision_enabled", e.target.checked)
406
- }),
407
- " ",
408
- t.vision
409
- ] }),
410
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t.provider }),
411
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
412
- className: "crew-quick-input",
413
- disabled: busy,
414
- defaultValue: config.vision_provider ?? "",
415
- onBlur: (e) => setProvider("vision_provider", e.target.value)
416
- }, `vision-${config.vision_provider ?? ""}`)
417
- ]
418
- }),
419
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
420
- className: "crew-quick-row",
421
- children: [
422
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", { children: [
423
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
424
- type: "checkbox",
425
- disabled: busy,
426
- checked: config.imagegen_enabled === true,
427
- onChange: (e) => toggle("imagegen_enabled", e.target.checked)
428
- }),
429
- " ",
430
- t.imagegen
431
- ] }),
432
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t.provider }),
433
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
434
- className: "crew-quick-input",
435
- disabled: busy,
436
- defaultValue: config.imagegen_provider ?? "",
437
- onBlur: (e) => setProvider("imagegen_provider", e.target.value)
438
- })
439
- ]
440
- })
441
- ]
442
- }),
443
- restartPending && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
444
- className: "crew-quick-row",
445
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
446
- className: "crew-quick-btn primary",
447
- disabled: busy,
448
- onClick: () => void applyRestart(),
449
- children: t.applyRestart
450
- })
451
- }),
452
- notice && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
453
- className: "crew-quick-notice",
454
- children: notice
455
- })
456
- ]
457
- });
458
- }
459
- function applyQuick(ctx) {
460
- ctx.slots.inject("settings.section", () => {
461
- return ctx.slots.register({
462
- name: "settings.section",
463
- id: "dsh-crew-quick",
464
- order: 65,
465
- label: () => "DSH Crew"
466
- }, () => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(QuickPanel, { ctx }));
467
- });
468
- }
469
- //#endregion
470
- //#region src/client/quick-entry.tsx
471
- const inject = ["slots", "locale"];
472
- function apply(ctx) {
473
- applyQuick(ctx);
474
- }
475
- //#endregion
476
- exports.apply = apply;
477
- exports.inject = inject;
478
-
479
- return module.exports; } });
1
+ window.__ModuleLoader__.load({ id: "@ran-sh/dsh-crew-web-bridge", factory: (require) => {
2
+ var module = { exports: {} }; var exports = module.exports;
3
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
4
+ let react = require("react");
5
+ let react_jsx_runtime = require("react/jsx-runtime");
6
+ //#region src/client/panel-chrome.tsx
7
+ function PanelStyles() {
8
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("style", { children: `
9
+ .dsh-crew-ui { --crew-line: rgba(128,128,128,.24); --crew-soft: rgba(128,128,128,.045); --crew-accent: #2563eb; font-size: 13px; line-height: 1.55; min-width: 0; }
10
+ .dsh-crew-ui *, .dsh-crew-ui *::before, .dsh-crew-ui *::after { box-sizing: border-box; }
11
+ .dsh-crew-ui .crew-panel-header { border: 1px solid var(--crew-line); border-radius: 12px; padding: 16px; background: var(--crew-soft); display: flex; flex-wrap: wrap; align-items: flex-start; gap: 12px 16px; }
12
+ .dsh-crew-ui .crew-panel-heading { flex: 1 1 260px; min-width: 0; }
13
+ .dsh-crew-ui .crew-eyebrow { font-size: 10.5px; letter-spacing: .055em; opacity: .65; margin-bottom: 4px; }
14
+ .dsh-crew-ui .crew-panel-title { margin: 0; font-size: 19px; font-weight: 680; line-height: 1.3; letter-spacing: -.025em; }
15
+ .dsh-crew-ui .crew-panel-description { margin: 5px 0 0; font-size: 12px; opacity: .72; overflow-wrap: anywhere; }
16
+ .dsh-crew-ui .crew-panel-badges { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 10px; }
17
+ .dsh-crew-ui .crew-nav-link { display: inline-flex; align-items: center; justify-content: center; color: var(--crew-accent); background: transparent; border: 1px solid var(--crew-line); border-radius: 8px; padding: 7px 10px; font-size: 12px; font-weight: 600; text-decoration: none; flex-shrink: 0; }
18
+ .dsh-crew-ui button:focus-visible, .dsh-crew-ui a:focus-visible, .dsh-crew-ui input:focus-visible, .dsh-crew-ui summary:focus-visible { outline: 2px solid var(--crew-accent); outline-offset: 3px; }
19
+ .dsh-crew-ui button:disabled { cursor: not-allowed; opacity: .4; }
20
+ .dsh-crew-ui .crew-nav-link:hover, .dsh-crew-ui .crew-section-trigger:hover, .dsh-crew-ui summary:hover { background: rgba(128,128,128,.09); }
21
+ .dsh-crew-ui .crew-section-heading { display: flex; flex-direction: column; gap: 2px; min-width: 0; flex: 1; }
22
+ .dsh-crew-ui .crew-section-title { font-weight: 650; font-size: 13px; }
23
+ .dsh-crew-ui .crew-section-summary { font-size: 11.5px; opacity: .65; overflow-wrap: anywhere; }
24
+ .dsh-crew-ui .crew-config-toolbar { display: flex; flex-wrap: wrap; align-items: center; gap: 7px; margin: 6px 0 2px; }
25
+ @media (prefers-color-scheme: dark) { .dsh-crew-ui { --crew-accent: #8ab4ff; } }
26
+ @media (prefers-reduced-motion: reduce) { .dsh-crew-ui * { transition: none !important; } }
27
+ ` });
28
+ }
29
+ function PanelHeader({ title, eyebrow, description, href, linkText, children }) {
30
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("header", {
31
+ className: "crew-panel-header",
32
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
33
+ className: "crew-panel-heading",
34
+ children: [
35
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
36
+ className: "crew-eyebrow",
37
+ children: eyebrow
38
+ }),
39
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("h2", {
40
+ className: "crew-panel-title",
41
+ children: title
42
+ }),
43
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
44
+ className: "crew-panel-description",
45
+ children: description
46
+ }),
47
+ children && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
48
+ className: "crew-panel-badges",
49
+ children
50
+ })
51
+ ]
52
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("a", {
53
+ className: "crew-nav-link",
54
+ href,
55
+ target: "_blank",
56
+ rel: "noopener noreferrer",
57
+ children: linkText
58
+ })]
59
+ });
60
+ }
61
+ //#endregion
62
+ //#region src/client/quick-panel.tsx
63
+ const API = "/_dsh/dsh-crew";
64
+ const FULL = "http://127.0.0.1:3210/";
65
+ const RESTART_KEYS = /* @__PURE__ */ new Set([
66
+ "vision_enabled",
67
+ "imagegen_enabled",
68
+ "vision_provider",
69
+ "imagegen_provider"
70
+ ]);
71
+ const T = {
72
+ zh: {
73
+ title: "DSH Crew 快捷控制",
74
+ openFull: "打开 3210 后台 →",
75
+ running: "后台已连接",
76
+ surface: "3080 / 日常控制",
77
+ description: "常用开关与模型顺序在这里调整,更多设置进入后台。",
78
+ priorityHint: "从上到下按优先级排列;从列表移除不会删除 Provider。",
79
+ empty: "未配置优先模型,按现有后备策略选择。",
80
+ models: (n) => `${n} 个模型`,
81
+ moveUp: "上移",
82
+ moveDown: "下移",
83
+ remove: "从优先级移除",
84
+ unavailable: "Crew 后端不可用",
85
+ openDiag: "打开诊断",
86
+ crew: "Crew",
87
+ enabled: "启用子 Agent",
88
+ flash: "Worker / Flash",
89
+ pro: "Reviewer / Pro",
90
+ addModel: "+ 添加模型",
91
+ multimodal: "多模态",
92
+ vision: "视觉",
93
+ imagegen: "生图",
94
+ provider: "Provider",
95
+ applyRestart: "应用并重启 Crew",
96
+ savedNeedsRestart: "配置已保存 · 需要重启 Crew 才会生效",
97
+ saved: "已保存",
98
+ working: "处理中…",
99
+ providerPlaceholder: "provider",
100
+ modelPlaceholder: "model"
101
+ },
102
+ en: {
103
+ title: "DSH Crew Quick Controls",
104
+ openFull: "Open Crew backend (3210) →",
105
+ running: "Backend connected",
106
+ surface: "3080 / DAILY CONTROLS",
107
+ description: "Manage everyday switches and model order here. Open the backend for advanced settings.",
108
+ priorityHint: "Ordered by priority. Removing a model here does not delete its provider.",
109
+ empty: "No priority models. The existing fallback policy applies.",
110
+ models: (n) => `${n} models`,
111
+ moveUp: "Move up",
112
+ moveDown: "Move down",
113
+ remove: "Remove from priority",
114
+ unavailable: "Crew backend unavailable",
115
+ openDiag: "Open diagnostics",
116
+ crew: "Crew",
117
+ enabled: "Enable sub-agents",
118
+ flash: "Worker / Flash",
119
+ pro: "Reviewer / Pro",
120
+ addModel: "+ Add model",
121
+ multimodal: "Multimodal",
122
+ vision: "Vision",
123
+ imagegen: "Imagegen",
124
+ provider: "Provider",
125
+ applyRestart: "Apply & restart Crew",
126
+ savedNeedsRestart: "Saved · restart Crew to take effect",
127
+ saved: "Saved",
128
+ working: "Working…",
129
+ providerPlaceholder: "provider",
130
+ modelPlaceholder: "Model"
131
+ }
132
+ };
133
+ function LocalStyles() {
134
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("style", { children: `
135
+ .dsh-crew-ui.crew-quick-card { display: flex; flex-direction: column; gap: 12px; }
136
+ .crew-quick-row { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }
137
+ .crew-quick-title { font-size: 16px; font-weight: 680; }
138
+ .crew-quick-section { font-weight: 650; opacity: 0.85; }
139
+ .crew-quick-chip { border: 1px solid rgba(128,128,128,0.35); border-radius: 999px; padding: 1px 10px; font-size: 12px; }
140
+ .crew-quick-card .crew-quick-btn { border: 1px solid var(--crew-line); border-radius: 7px; min-height: 30px; padding: 3px 9px; cursor: pointer; background: transparent; color: inherit; font-size: 12px; }
141
+ .crew-quick-btn.primary { border-color: #4a9eff; color: #4a9eff; font-weight: 650; }
142
+ .crew-quick-card .crew-quick-input { border: 1px solid var(--crew-line); border-radius: 6px; padding: 6px 9px; font-size: 12px; min-width: 0; width: 100%; background: transparent; color: inherit; }
143
+ .crew-quick-notice { opacity: 0.75; font-size: 12.5px; }
144
+ .crew-quick-card .crew-quick-master { padding: 11px 14px; border: 1px solid var(--crew-line); border-radius: 10px; justify-content: space-between; }
145
+ .crew-quick-card input[type=checkbox] { accent-color: var(--crew-accent); width: 15px; height: 15px; }
146
+ .crew-quick-card label { display: inline-flex; gap: 7px; align-items: center; }
147
+ .crew-quick-card .crew-quick-group { border: 1px solid var(--crew-line); border-radius: 10px; overflow: hidden; }
148
+ .crew-quick-card .crew-quick-group > summary { cursor: pointer; padding: 12px 14px; }
149
+ .crew-quick-card .crew-quick-count { margin-left: 10px; font-size: 11px; opacity: .65; font-weight: 400; }
150
+ .crew-quick-card .crew-quick-content { padding: 0 14px 14px; }
151
+ .crew-quick-card .crew-quick-list { padding: 0; margin: 10px 0; list-style: none; }
152
+ .crew-quick-card .crew-quick-model { display: grid; grid-template-columns: 22px minmax(0, 1fr) auto; gap: 9px; align-items: center; border-top: 1px solid var(--crew-line); padding: 10px 0; }
153
+ .crew-quick-card .crew-quick-rank { opacity: .5; font-size: 11px; font-variant-numeric: tabular-nums; }
154
+ .crew-quick-card .crew-quick-model-name { font-size: 12.5px; font-weight: 600; overflow-wrap: anywhere; }
155
+ .crew-quick-card .crew-quick-provider { font-size: 11px; opacity: .65; overflow-wrap: anywhere; }
156
+ .crew-quick-card .crew-quick-actions { display: flex; gap: 4px; }
157
+ .crew-quick-card .crew-quick-add > summary { color: var(--crew-accent); cursor: pointer; font-size: 12px; padding: 4px 0; }
158
+ .crew-quick-card .crew-quick-form { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr) auto; gap: 7px; margin-top: 9px; }
159
+ .crew-quick-card .crew-quick-media { display: grid; grid-template-columns: 100px minmax(0, 1fr); gap: 10px; align-items: center; margin-top: 10px; }
160
+ @media (max-width: 640px) { .crew-quick-card .crew-quick-form { grid-template-columns: minmax(0, 1fr); } .crew-quick-card .crew-quick-model { grid-template-columns: 18px minmax(0, 1fr); } .crew-quick-card .crew-quick-actions { grid-column: 2; justify-content: flex-end; } }
161
+ ` });
162
+ }
163
+ async function readJson(res) {
164
+ if (!res.ok) return {
165
+ ok: false,
166
+ status: res.status
167
+ };
168
+ try {
169
+ return await res.json();
170
+ } catch {
171
+ return { ok: false };
172
+ }
173
+ }
174
+ function QuickPanel({ ctx }) {
175
+ const locale = ctx?.locale?.getLocale?.().active === "zh" ? "zh" : "en";
176
+ const t = T[locale] ?? T.zh;
177
+ const [config, setConfig] = (0, react.useState)(null);
178
+ const [ready, setReady] = (0, react.useState)(null);
179
+ const [notice, setNotice] = (0, react.useState)("");
180
+ const [busy, setBusy] = (0, react.useState)(false);
181
+ const [restartPending, setRestartPending] = (0, react.useState)(false);
182
+ const [drafts, setDrafts] = (0, react.useState)({});
183
+ const load = (0, react.useCallback)(async () => {
184
+ try {
185
+ const status = await readJson(await fetch(`${API}/quick-status`, { cache: "no-store" }));
186
+ if (!status.ok) {
187
+ setReady(false);
188
+ return;
189
+ }
190
+ setReady(true);
191
+ setConfig(status.config ?? {});
192
+ } catch {
193
+ setReady(false);
194
+ }
195
+ }, []);
196
+ (0, react.useEffect)(() => {
197
+ load();
198
+ }, [load]);
199
+ const patch = (0, react.useCallback)(async (next) => {
200
+ setBusy(true);
201
+ setNotice(t.working);
202
+ try {
203
+ const res = await readJson(await fetch(`${API}/quick-config`, {
204
+ method: "POST",
205
+ headers: { "content-type": "application/json" },
206
+ body: JSON.stringify(next)
207
+ }));
208
+ if (!res.ok) throw new Error(res.code ?? res.error ?? "save failed");
209
+ setConfig(res.config ?? {});
210
+ if (Object.keys(next).some((k) => RESTART_KEYS.has(k))) {
211
+ setRestartPending(true);
212
+ setNotice(t.savedNeedsRestart);
213
+ } else {
214
+ setNotice(t.saved);
215
+ setTimeout(() => setNotice(""), 1500);
216
+ }
217
+ return true;
218
+ } catch (e) {
219
+ setNotice(String(e?.message ?? e));
220
+ return false;
221
+ } finally {
222
+ setBusy(false);
223
+ }
224
+ }, [t]);
225
+ const toggle = (key, value) => {
226
+ setConfig((c) => ({
227
+ ...c ?? {},
228
+ [key]: value
229
+ }));
230
+ patch({ [key]: value });
231
+ };
232
+ const setProvider = (key, value) => {
233
+ setConfig((c) => ({
234
+ ...c ?? {},
235
+ [key]: value
236
+ }));
237
+ patch({ [key]: value });
238
+ };
239
+ const moveModel = (listKey, index, dir) => {
240
+ const list = [...config?.[listKey] ?? []];
241
+ const to = index + dir;
242
+ if (to < 0 || to >= list.length) return;
243
+ [list[index], list[to]] = [list[to], list[index]];
244
+ setConfig((c) => ({
245
+ ...c ?? {},
246
+ [listKey]: list
247
+ }));
248
+ patch({ [listKey]: list });
249
+ };
250
+ const removeModel = (listKey, index) => {
251
+ const list = [...config?.[listKey] ?? []];
252
+ list.splice(index, 1);
253
+ setConfig((c) => ({
254
+ ...c ?? {},
255
+ [listKey]: list
256
+ }));
257
+ patch({ [listKey]: list });
258
+ };
259
+ const addModel = (listKey) => {
260
+ const draft = drafts[listKey] ?? {
261
+ provider: "",
262
+ model: ""
263
+ };
264
+ if (!draft.provider.trim() || !draft.model.trim()) return;
265
+ const list = [...config?.[listKey] ?? [], {
266
+ provider: draft.provider.trim(),
267
+ model: draft.model.trim()
268
+ }];
269
+ setConfig((c) => ({
270
+ ...c ?? {},
271
+ [listKey]: list
272
+ }));
273
+ setDrafts((d) => ({
274
+ ...d,
275
+ [listKey]: {
276
+ provider: "",
277
+ model: ""
278
+ }
279
+ }));
280
+ patch({ [listKey]: list });
281
+ };
282
+ const modelList = (listKey, label) => {
283
+ const list = config?.[listKey] ?? [];
284
+ const draft = drafts[listKey] ?? {
285
+ provider: "",
286
+ model: ""
287
+ };
288
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("details", {
289
+ className: "crew-quick-group",
290
+ open: listKey === "flash_model_priority",
291
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("summary", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: label }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
292
+ className: "crew-quick-count",
293
+ children: t.models(list.length)
294
+ })] }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
295
+ className: "crew-quick-content",
296
+ children: [
297
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
298
+ className: "crew-quick-notice",
299
+ children: t.priorityHint
300
+ }),
301
+ list.length === 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
302
+ className: "crew-quick-notice",
303
+ children: t.empty
304
+ }),
305
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("ol", {
306
+ className: "crew-quick-list",
307
+ "aria-label": label,
308
+ children: list.map((entry, i) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("li", {
309
+ className: "crew-quick-model",
310
+ children: [
311
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
312
+ className: "crew-quick-rank",
313
+ "aria-hidden": "true",
314
+ children: [i + 1, "."]
315
+ }),
316
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
317
+ className: "crew-quick-model-name",
318
+ children: entry.model
319
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
320
+ className: "crew-quick-provider",
321
+ children: entry.provider
322
+ })] }),
323
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
324
+ className: "crew-quick-actions",
325
+ children: [
326
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
327
+ type: "button",
328
+ className: "crew-quick-btn",
329
+ "aria-label": `${t.moveUp} ${entry.model}`,
330
+ title: t.moveUp,
331
+ disabled: busy || i === 0,
332
+ onClick: () => moveModel(listKey, i, -1),
333
+ children: "↑"
334
+ }),
335
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
336
+ type: "button",
337
+ className: "crew-quick-btn",
338
+ "aria-label": `${t.moveDown} ${entry.model}`,
339
+ title: t.moveDown,
340
+ disabled: busy || i === list.length - 1,
341
+ onClick: () => moveModel(listKey, i, 1),
342
+ children: "↓"
343
+ }),
344
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
345
+ type: "button",
346
+ className: "crew-quick-btn",
347
+ "aria-label": `${t.remove} ${entry.model}`,
348
+ title: t.remove,
349
+ disabled: busy,
350
+ onClick: () => removeModel(listKey, i),
351
+ children: "×"
352
+ })
353
+ ]
354
+ })
355
+ ]
356
+ }, `${entry.provider}/${entry.model}/${i}`))
357
+ }),
358
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("details", {
359
+ className: "crew-quick-add",
360
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("summary", { children: t.addModel }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
361
+ className: "crew-quick-form",
362
+ children: [
363
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
364
+ className: "crew-quick-input",
365
+ "aria-label": `${label} Provider`,
366
+ placeholder: t.providerPlaceholder,
367
+ disabled: busy,
368
+ value: draft.provider,
369
+ onChange: (e) => setDrafts((d) => ({
370
+ ...d,
371
+ [listKey]: {
372
+ ...draft,
373
+ provider: e.target.value
374
+ }
375
+ }))
376
+ }),
377
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
378
+ className: "crew-quick-input",
379
+ "aria-label": `${label} ${locale === "zh" ? "模型" : "Model"}`,
380
+ placeholder: t.modelPlaceholder,
381
+ disabled: busy,
382
+ value: draft.model,
383
+ onChange: (e) => setDrafts((d) => ({
384
+ ...d,
385
+ [listKey]: {
386
+ ...draft,
387
+ model: e.target.value
388
+ }
389
+ }))
390
+ }),
391
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
392
+ type: "button",
393
+ className: "crew-quick-btn",
394
+ disabled: busy || !draft.provider.trim() || !draft.model.trim(),
395
+ onClick: () => addModel(listKey),
396
+ children: t.addModel
397
+ })
398
+ ]
399
+ })]
400
+ })
401
+ ]
402
+ })]
403
+ });
404
+ };
405
+ if (ready === false) return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
406
+ className: "dsh-crew-ui crew-quick-card",
407
+ children: [
408
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(PanelStyles, {}),
409
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(LocalStyles, {}),
410
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
411
+ className: "crew-quick-title",
412
+ children: t.title
413
+ }),
414
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
415
+ className: "crew-quick-notice",
416
+ children: t.unavailable
417
+ }),
418
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
419
+ className: "crew-quick-row",
420
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("a", {
421
+ className: "crew-nav-link",
422
+ href: FULL,
423
+ target: "_blank",
424
+ rel: "noopener noreferrer",
425
+ children: t.openDiag
426
+ })
427
+ })
428
+ ]
429
+ });
430
+ if (config === null) return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
431
+ className: "dsh-crew-ui crew-quick-card",
432
+ children: [
433
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(PanelStyles, {}),
434
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(LocalStyles, {}),
435
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
436
+ className: "crew-quick-notice",
437
+ role: "status",
438
+ children: t.working
439
+ })
440
+ ]
441
+ });
442
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
443
+ className: "dsh-crew-ui crew-quick-card",
444
+ children: [
445
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(PanelStyles, {}),
446
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(LocalStyles, {}),
447
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(PanelHeader, {
448
+ title: t.title,
449
+ eyebrow: t.surface,
450
+ description: t.description,
451
+ href: FULL,
452
+ linkText: t.openFull,
453
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
454
+ className: "crew-quick-chip",
455
+ children: [t.running, " · 3210"]
456
+ })
457
+ }),
458
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
459
+ className: "crew-quick-row crew-quick-master",
460
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
461
+ className: "crew-quick-section",
462
+ children: t.crew
463
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", { children: [
464
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
465
+ type: "checkbox",
466
+ disabled: busy,
467
+ checked: config.subagents_enabled !== false,
468
+ onChange: (e) => toggle("subagents_enabled", e.target.checked)
469
+ }),
470
+ " ",
471
+ t.enabled
472
+ ] })]
473
+ }),
474
+ modelList("flash_model_priority", t.flash),
475
+ modelList("pro_model_priority", t.pro),
476
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("details", {
477
+ className: "crew-quick-group",
478
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("summary", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: t.multimodal }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
479
+ className: "crew-quick-count",
480
+ children: [
481
+ t.vision,
482
+ " / ",
483
+ t.imagegen
484
+ ]
485
+ })] }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
486
+ className: "crew-quick-content",
487
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
488
+ className: "crew-quick-media",
489
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", { children: [
490
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
491
+ type: "checkbox",
492
+ disabled: busy,
493
+ checked: config.vision_enabled === true,
494
+ onChange: (e) => toggle("vision_enabled", e.target.checked)
495
+ }),
496
+ " ",
497
+ t.vision
498
+ ] }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
499
+ className: "crew-quick-input",
500
+ "aria-label": `${t.vision} Provider`,
501
+ placeholder: t.provider,
502
+ disabled: busy,
503
+ defaultValue: config.vision_provider ?? "",
504
+ onBlur: (e) => setProvider("vision_provider", e.target.value)
505
+ }, `vision-${config.vision_provider ?? ""}`)]
506
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
507
+ className: "crew-quick-media",
508
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", { children: [
509
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
510
+ type: "checkbox",
511
+ disabled: busy,
512
+ checked: config.imagegen_enabled === true,
513
+ onChange: (e) => toggle("imagegen_enabled", e.target.checked)
514
+ }),
515
+ " ",
516
+ t.imagegen
517
+ ] }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
518
+ className: "crew-quick-input",
519
+ "aria-label": `${t.imagegen} Provider`,
520
+ placeholder: t.provider,
521
+ disabled: busy,
522
+ defaultValue: config.imagegen_provider ?? "",
523
+ onBlur: (e) => setProvider("imagegen_provider", e.target.value)
524
+ })]
525
+ })]
526
+ })]
527
+ }),
528
+ restartPending && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
529
+ className: "crew-quick-row",
530
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("a", {
531
+ className: "crew-quick-btn primary",
532
+ href: FULL,
533
+ target: "_blank",
534
+ rel: "noopener noreferrer",
535
+ children: t.openFull
536
+ })
537
+ }),
538
+ notice && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
539
+ className: "crew-quick-notice",
540
+ role: "status",
541
+ "aria-live": "polite",
542
+ children: notice
543
+ })
544
+ ]
545
+ });
546
+ }
547
+ function applyQuick(ctx) {
548
+ ctx.slots.inject("settings.section", () => {
549
+ return ctx.slots.register({
550
+ name: "settings.section",
551
+ id: "dsh-crew-quick",
552
+ order: 65,
553
+ label: () => "DSH Crew"
554
+ }, () => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(QuickPanel, { ctx }));
555
+ });
556
+ }
557
+ //#endregion
558
+ //#region src/client/quick-entry.tsx
559
+ const inject = ["slots", "locale"];
560
+ function apply(ctx) {
561
+ applyQuick(ctx);
562
+ }
563
+ //#endregion
564
+ exports.apply = apply;
565
+ exports.inject = inject;
566
+
567
+ return module.exports; } });