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

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 (84) 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 +769 -177
  23. package/official-web-bridge/lib/client.js +297 -209
  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 +91 -69
  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/client/task-telemetry.mjs +11 -4
  39. package/src/config-readiness.mjs +181 -115
  40. package/src/extension-contract.mjs +44 -26
  41. package/src/history/admission-gate.mjs +51 -0
  42. package/src/history/archive-store.mjs +261 -0
  43. package/src/history/cleanup-plan.mjs +64 -0
  44. package/src/history/http.mjs +32 -0
  45. package/src/history/operation.mjs +86 -0
  46. package/src/history/runner.mjs +51 -0
  47. package/src/history/runtime.mjs +28 -0
  48. package/src/history/service.mjs +137 -0
  49. package/src/history/state.mjs +27 -0
  50. package/src/hub/index.mjs +141 -24
  51. package/src/hub-client.mjs +17 -15
  52. package/src/image-output.mjs +71 -0
  53. package/src/install/install-legacy.mjs +126 -23
  54. package/src/install/npx-lifecycle.mjs +373 -64
  55. package/src/install/official-frontend-assets.mjs +78 -0
  56. package/src/install/payload-content.mjs +88 -0
  57. package/src/install/windows-startup.mjs +107 -19
  58. package/src/install/windows-supervisor-adapter.mjs +443 -0
  59. package/src/install/windows-supervisor-lifecycle.mjs +782 -0
  60. package/src/install/zcode.mjs +53 -14
  61. package/src/job-contracts.mjs +37 -31
  62. package/src/jobs.mjs +2 -2
  63. package/src/mcp-runtime.mjs +74 -9
  64. package/src/model-callability-contract.mjs +79 -0
  65. package/src/multimodal.mjs +0 -0
  66. package/src/official-web-bridge.mjs +0 -2
  67. package/src/provider-health.mjs +130 -124
  68. package/src/readiness-matrix.mjs +20 -15
  69. package/src/runtime-identity-contract.mjs +34 -0
  70. package/src/runtime-identity.mjs +14 -4
  71. package/src/runtime-readiness-snapshot.mjs +285 -87
  72. package/src/server.mjs +86 -76
  73. package/src/standalone-sdk.mjs +23 -0
  74. package/src/supervisor/restart-request.mjs +64 -10
  75. package/src/workflow-runtime.mjs +28 -13
  76. package/windows/start-dsh-crew.cmd +57 -57
  77. package/windows/start-dsh-crew.ps1 +327 -51
  78. package/windows/supervisor-control.ps1 +467 -0
  79. package/zcode/AGENTS.md +4 -1
  80. package/zcode/agents/ds-reviewer.md +31 -36
  81. package/zcode/agents/ds-worker.md +31 -39
  82. package/zcode/commands/dsh-config.md +17 -6
  83. package/zcode/commands/dsh-status.md +5 -4
  84. package/.mcp.json +0 -8
@@ -3,6 +3,62 @@ var module = { exports: {} }; var exports = module.exports;
3
3
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
4
4
  let react = require("react");
5
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
6
62
  //#region src/client/quick-panel.tsx
7
63
  const API = "/_dsh/dsh-crew";
8
64
  const FULL = "http://127.0.0.1:3210/";
@@ -15,14 +71,22 @@ const RESTART_KEYS = /* @__PURE__ */ new Set([
15
71
  const T = {
16
72
  zh: {
17
73
  title: "DSH Crew 快捷控制",
18
- openFull: "打开完整设置 →",
19
- running: "运行中",
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: "从优先级移除",
20
84
  unavailable: "Crew 后端不可用",
21
85
  openDiag: "打开诊断",
22
86
  crew: "Crew",
23
87
  enabled: "启用子 Agent",
24
- flash: "Flash 模型",
25
- pro: "Pro 模型",
88
+ flash: "Worker / Flash",
89
+ pro: "Reviewer / Pro",
26
90
  addModel: "+ 添加模型",
27
91
  multimodal: "多模态",
28
92
  vision: "视觉",
@@ -37,14 +101,22 @@ const T = {
37
101
  },
38
102
  en: {
39
103
  title: "DSH Crew Quick Controls",
40
- openFull: "Open full settings →",
41
- running: "Running",
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",
42
114
  unavailable: "Crew backend unavailable",
43
115
  openDiag: "Open diagnostics",
44
116
  crew: "Crew",
45
117
  enabled: "Enable sub-agents",
46
- flash: "Flash models",
47
- pro: "Pro models",
118
+ flash: "Worker / Flash",
119
+ pro: "Reviewer / Pro",
48
120
  addModel: "+ Add model",
49
121
  multimodal: "Multimodal",
50
122
  vision: "Vision",
@@ -55,21 +127,37 @@ const T = {
55
127
  saved: "Saved",
56
128
  working: "Working…",
57
129
  providerPlaceholder: "provider",
58
- modelPlaceholder: "model"
130
+ modelPlaceholder: "Model"
59
131
  }
60
132
  };
61
133
  function LocalStyles() {
62
134
  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%); }
135
+ .dsh-crew-ui.crew-quick-card { display: flex; flex-direction: column; gap: 12px; }
64
136
  .crew-quick-row { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }
65
137
  .crew-quick-title { font-size: 16px; font-weight: 680; }
66
138
  .crew-quick-section { font-weight: 650; opacity: 0.85; }
67
139
  .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; }
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; }
69
141
  .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; }
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; }
71
143
  .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; }
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; } }
73
161
  ` });
74
162
  }
75
163
  async function readJson(res) {
@@ -191,129 +279,133 @@ function QuickPanel({ ctx }) {
191
279
  }));
192
280
  patch({ [listKey]: list });
193
281
  };
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
282
  const modelList = (listKey, label) => {
228
283
  const list = config?.[listKey] ?? [];
229
284
  const draft = drafts[listKey] ?? {
230
285
  provider: "",
231
286
  model: ""
232
287
  };
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
- ]
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
+ })]
312
403
  });
313
404
  };
314
405
  if (ready === false) return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
315
- className: "crew-quick-card",
406
+ className: "dsh-crew-ui crew-quick-card",
316
407
  children: [
408
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(PanelStyles, {}),
317
409
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)(LocalStyles, {}),
318
410
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
319
411
  className: "crew-quick-title",
@@ -326,47 +418,45 @@ function QuickPanel({ ctx }) {
326
418
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
327
419
  className: "crew-quick-row",
328
420
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("a", {
421
+ className: "crew-nav-link",
329
422
  href: FULL,
330
423
  target: "_blank",
331
- rel: "noreferrer",
424
+ rel: "noopener noreferrer",
332
425
  children: t.openDiag
333
426
  })
334
427
  })
335
428
  ]
336
429
  });
337
430
  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
- })]
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
+ ]
343
441
  });
344
442
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
345
- className: "crew-quick-card",
443
+ className: "dsh-crew-ui crew-quick-card",
346
444
  children: [
445
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(PanelStyles, {}),
347
446
  /* @__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
- ]
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
+ })
367
457
  }),
368
458
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
369
- className: "crew-quick-row",
459
+ className: "crew-quick-row crew-quick-master",
370
460
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
371
461
  className: "crew-quick-section",
372
462
  children: t.crew
@@ -383,74 +473,72 @@ function QuickPanel({ ctx }) {
383
473
  }),
384
474
  modelList("flash_model_priority", t.flash),
385
475
  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 }),
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: [
411
490
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
412
- className: "crew-quick-input",
491
+ type: "checkbox",
413
492
  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 }),
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: [
433
509
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
434
- className: "crew-quick-input",
510
+ type: "checkbox",
435
511
  disabled: busy,
436
- defaultValue: config.imagegen_provider ?? "",
437
- onBlur: (e) => setProvider("imagegen_provider", e.target.value)
438
- })
439
- ]
440
- })
441
- ]
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
+ })]
442
527
  }),
443
528
  restartPending && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
444
529
  className: "crew-quick-row",
445
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
530
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("a", {
446
531
  className: "crew-quick-btn primary",
447
- disabled: busy,
448
- onClick: () => void applyRestart(),
449
- children: t.applyRestart
532
+ href: FULL,
533
+ target: "_blank",
534
+ rel: "noopener noreferrer",
535
+ children: t.openFull
450
536
  })
451
537
  }),
452
538
  notice && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
453
539
  className: "crew-quick-notice",
540
+ role: "status",
541
+ "aria-live": "polite",
454
542
  children: notice
455
543
  })
456
544
  ]