@max-null/dsh-node-appearance 0.3.5 → 0.4.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/lib/client.js CHANGED
@@ -26,6 +26,7 @@ window.__ModuleLoader__.load({
26
26
  execute: "#f59e0b",
27
27
  file: "#22c55e",
28
28
  task: "#ec4899",
29
+ ask: "#65a30d",
29
30
  command: "#f97316",
30
31
  thinking: "#c4b5fd",
31
32
  context: "#8a9bb5",
@@ -78,6 +79,7 @@ window.__ModuleLoader__.load({
78
79
  "schedule_list",
79
80
  "exit_plan_mode"
80
81
  ],
82
+ ask: ["ask_user_question"],
81
83
  other: []
82
84
  };
83
85
  /** Selector matching every tool row inside a tool-call node. */
@@ -192,51 +194,60 @@ window.__ModuleLoader__.load({
192
194
  }
193
195
  //#endregion
194
196
  //#region \0dsh-css:H:\MaxNull\WorkStation\max-null-plugins\dsh-node-appearance\src\client\card.module.css.mjs
195
- const css = ".CDqThG_group{border-bottom:1px solid var(--dsw-alias-border-l2);flex-direction:column;gap:10px;padding:12px 0;display:flex}.CDqThG_header{appearance:none;width:100%;font:inherit;text-align:left;cursor:pointer;background:0 0;border:0;border-radius:6px;align-items:center;gap:8px;padding:0;display:flex}.CDqThG_title{min-width:0;color:var(--dsw-alias-label-primary);flex:1;font-size:13px;font-weight:500;line-height:1.5}.CDqThG_chevron{color:var(--dsw-alias-label-tertiary);flex:none;transition:transform .16s}.CDqThG_chevronOpen{color:var(--dsw-alias-label-tertiary);flex:none;transition:transform .16s;transform:rotate(180deg)}.CDqThG_body{flex-direction:column;display:flex}.CDqThG_row{align-items:center;gap:10px;padding:6px 0;font-size:13px;display:flex}.CDqThG_rowLabel{color:var(--dsw-alias-label-primary);flex:none;font-size:13px;font-weight:500;line-height:1.5}.CDqThG_rowHint{color:var(--dsw-alias-label-tertiary);white-space:nowrap;flex:none;font-size:12px;line-height:1.5}.CDqThG_checkbox{accent-color:var(--dsw-alias-brand-primary);cursor:pointer;width:15px;height:15px}.CDqThG_switch{cursor:pointer;background:var(--dsw-alias-border-l4,#00000029);border:none;border-radius:11px;flex:none;width:40px;height:22px;margin-left:auto;padding:0;transition:background .15s}.CDqThG_switch.CDqThG_on{background:var(--dsw-alias-state-business-primary,#4fc3f7)}.CDqThG_switch .CDqThG_knob{background:#fff;border-radius:8px;width:16px;height:16px;margin-left:2px;transition:margin-left .15s;display:block}.CDqThG_switch.CDqThG_on .CDqThG_knob{margin-left:22px}.CDqThG_colorInput{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-3);cursor:pointer;border-radius:6px;width:44px;height:26px;margin-left:auto;padding:2px}.CDqThG_colorInput:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:1px}.CDqThG_colorValue{color:var(--dsw-alias-label-secondary);font-family:ui-monospace,monospace;font-size:12px}.CDqThG_sectionTitle{letter-spacing:.06em;text-transform:uppercase;color:var(--dsw-alias-label-tertiary);border-top:1px solid var(--dsw-alias-border-l2);margin-top:10px;padding:10px 0 4px;font-size:12px;font-weight:600;line-height:1.5}.CDqThG_toolRow{align-items:center;gap:10px;padding:6px 0;font-size:13px;display:flex}.CDqThG_toolName{text-overflow:ellipsis;white-space:nowrap;min-width:0;color:var(--dsw-alias-label-secondary);flex:1;font-family:ui-monospace,monospace;font-size:12px;line-height:1.5;overflow:hidden}.CDqThG_removeButton,.CDqThG_addButton{appearance:none;font:inherit;cursor:pointer;color:var(--dsw-alias-label-secondary);background:0 0;border:none;border-radius:7px;padding:5px 8px;font-size:12.5px;line-height:1.5}.CDqThG_removeButton:hover:not(:disabled),.CDqThG_addButton:hover:not(:disabled){color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-layer-1)}.CDqThG_removeButton:disabled,.CDqThG_addButton:disabled{opacity:.4;cursor:default}.CDqThG_removeButton:focus-visible,.CDqThG_addButton:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:1px}.CDqThG_addRow{align-items:center;gap:10px;padding:10px 0 4px;display:flex}.CDqThG_toolInput{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-3);min-width:0;height:34px;font:inherit;color:var(--dsw-alias-label-primary);border-radius:8px;flex:1;padding:0 12px;font-size:13px;line-height:1.5}.CDqThG_toolInput:focus-visible{border-color:var(--dsw-alias-brand-primary);outline:none}.CDqThG_statusLine{color:var(--dsw-alias-label-tertiary);margin:0 0 8px;font-size:12px;line-height:1.5}.CDqThG_disabled{opacity:.5;pointer-events:none}";
196
- const tagId = "@max-null/dsh-node-appearance/card.module.css";
197
- if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId) + "]") === null) {
197
+ const css$2 = ".CDqThG_card{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-3);border-radius:12px;list-style:none;transition:border-color .16s,background .16s}.CDqThG_card:hover{border-color:var(--dsw-alias-label-dimmed)}.CDqThG_cardOpen{background:var(--dsw-alias-bg-layer-2);border-color:var(--dsw-alias-label-dimmed)}.CDqThG_header{appearance:none;width:100%;font:inherit;color:inherit;text-align:left;cursor:pointer;background:0 0;border:0;border-radius:12px;align-items:center;gap:12px;padding:14px 16px;display:flex}.CDqThG_header:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:-2px}.CDqThG_headText{flex-direction:column;flex:1;gap:4px;min-width:0;display:flex}.CDqThG_name{color:var(--dsw-alias-label-primary);font-size:15px;font-weight:600;line-height:1.4}.CDqThG_description{color:var(--dsw-alias-label-tertiary);font-size:13px;line-height:1.5}.CDqThG_chevron{color:var(--dsw-alias-label-tertiary);flex:none;transition:transform .16s}.CDqThG_chevronOpen{transform:rotate(180deg)}.CDqThG_body{border-top:1px solid var(--dsw-alias-border-l2);margin:0 16px;padding-bottom:8px}.CDqThG_readOnly,.CDqThG_statusLine{color:var(--dsw-alias-label-tertiary);margin:12px 0 0;font-size:12px;line-height:1.5}.CDqThG_disabled{opacity:.55;pointer-events:none}.CDqThG_footer{border-top:1px solid var(--dsw-alias-border-l2);justify-content:flex-end;align-items:center;gap:8px;padding:16px 0 4px;display:flex}.CDqThG_reset{appearance:none;border:1px solid var(--dsw-alias-border-l2);font:inherit;cursor:pointer;color:var(--dsw-alias-label-secondary);background:0 0;border-radius:8px;padding:5px 14px;font-size:13px;line-height:1.5}.CDqThG_reset:hover:not(:disabled){color:var(--dsw-alias-label-primary);border-color:var(--dsw-alias-label-dimmed)}.CDqThG_resetConfirm{border-color:var(--dsw-alias-state-error-primary);color:var(--dsw-alias-state-error-primary)}.CDqThG_reset:disabled{opacity:.4;cursor:default}.CDqThG_reset:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:1px}.CDqThG_row{border-top:1px solid var(--dsw-alias-border-l2);align-items:center;gap:10px;margin-top:10px;padding:10px 0 4px;display:flex;position:relative}.CDqThG_row:first-child{border-top:none;margin-top:4px}.CDqThG_rowLabel{min-width:0;color:var(--dsw-alias-label-primary);flex-direction:column;flex:1;gap:2px;font-size:13px;font-weight:500;line-height:1.5;display:flex}.CDqThG_rowHint{color:var(--dsw-alias-label-tertiary);font-size:12px;font-weight:400;line-height:1.5}.CDqThG_colorInput{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-3);cursor:pointer;border-radius:8px;flex:none;width:34px;height:28px;padding:2px}.CDqThG_colorValue{width:64px;color:var(--dsw-alias-label-tertiary);font-variant-numeric:tabular-nums;flex:none;font-size:12px;line-height:1.5}.CDqThG_sectionTitle{color:var(--dsw-alias-label-primary);margin:14px 0 0;font-size:13px;font-weight:600;line-height:1.5}.CDqThG_toolName{min-width:0;color:var(--dsw-alias-label-primary);text-overflow:ellipsis;white-space:nowrap;flex:1;font-size:13px;line-height:1.5;overflow:hidden}.CDqThG_removeButton{appearance:none;border:1px solid var(--dsw-alias-border-l2);font:inherit;cursor:pointer;color:var(--dsw-alias-label-secondary);background:0 0;border-radius:8px;flex:none;padding:3px 10px;font-size:12px;line-height:1.5}.CDqThG_removeButton:hover{color:var(--dsw-alias-label-primary);border-color:var(--dsw-alias-label-dimmed)}.CDqThG_addRow{align-items:center;gap:8px;margin-top:10px;margin-bottom:12px;display:flex}.CDqThG_toolInput{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-3);min-width:0;height:34px;font:inherit;color:var(--dsw-alias-label-primary);border-radius:8px;flex:1;padding:0 12px;font-size:13px;line-height:1.5}.CDqThG_toolInput:focus-visible{border-color:var(--dsw-alias-brand-primary);outline:none}.CDqThG_toolInput::placeholder{color:var(--dsw-alias-label-tertiary)}.CDqThG_addButton{appearance:none;font:inherit;cursor:pointer;background:var(--dsw-alias-label-primary);color:var(--dsw-alias-bg-layer-3);border:1px solid #0000;border-radius:8px;flex:none;padding:5px 14px;font-size:13px;line-height:1.5}.CDqThG_addButton:disabled{opacity:.4;cursor:default}.CDqThG_switch{cursor:pointer;background:var(--dsw-alias-border-l4,#00000029);border:none;border-radius:11px;flex:none;width:40px;height:22px;padding:0;transition:background .15s}.CDqThG_switch.CDqThG_on{background:var(--dsw-alias-state-business-primary,#4fc3f7)}.CDqThG_knob{pointer-events:none;background:#fff;border-radius:8px;width:16px;height:16px;margin-left:2px;transition:margin-left .15s;display:block}.CDqThG_switch.CDqThG_on .CDqThG_knob{margin-left:22px}";
198
+ const tagId$2 = "@max-null/dsh-node-appearance/card.module.css";
199
+ if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$2) + "]") === null) {
198
200
  const tag = document.createElement("style");
199
201
  tag.dataset.plugin = "@max-null/dsh-node-appearance";
200
- tag.dataset.pluginCss = tagId;
201
- tag.textContent = css;
202
+ tag.dataset.pluginCss = tagId$2;
203
+ tag.textContent = css$2;
202
204
  document.head.appendChild(tag);
203
205
  }
204
206
  var card_module_css_default = {
205
- "checkbox": "CDqThG_checkbox",
206
- "addRow": "CDqThG_addRow",
207
- "toolInput": "CDqThG_toolInput",
208
- "row": "CDqThG_row",
207
+ "resetConfirm": "CDqThG_resetConfirm",
209
208
  "body": "CDqThG_body",
210
- "switch": "CDqThG_switch",
211
- "on": "CDqThG_on",
212
- "group": "CDqThG_group",
213
- "sectionTitle": "CDqThG_sectionTitle",
214
- "removeButton": "CDqThG_removeButton",
215
- "disabled": "CDqThG_disabled",
216
- "addButton": "CDqThG_addButton",
217
- "title": "CDqThG_title",
218
- "header": "CDqThG_header",
219
- "rowLabel": "CDqThG_rowLabel",
220
- "toolName": "CDqThG_toolName",
221
209
  "statusLine": "CDqThG_statusLine",
210
+ "header": "CDqThG_header",
211
+ "cardOpen": "CDqThG_cardOpen",
212
+ "switch": "CDqThG_switch",
222
213
  "rowHint": "CDqThG_rowHint",
223
- "chevron": "CDqThG_chevron",
214
+ "toolInput": "CDqThG_toolInput",
215
+ "headText": "CDqThG_headText",
216
+ "addButton": "CDqThG_addButton",
217
+ "disabled": "CDqThG_disabled",
224
218
  "chevronOpen": "CDqThG_chevronOpen",
219
+ "row": "CDqThG_row",
225
220
  "knob": "CDqThG_knob",
226
- "colorValue": "CDqThG_colorValue",
221
+ "chevron": "CDqThG_chevron",
227
222
  "colorInput": "CDqThG_colorInput",
228
- "toolRow": "CDqThG_toolRow"
223
+ "name": "CDqThG_name",
224
+ "sectionTitle": "CDqThG_sectionTitle",
225
+ "description": "CDqThG_description",
226
+ "addRow": "CDqThG_addRow",
227
+ "removeButton": "CDqThG_removeButton",
228
+ "card": "CDqThG_card",
229
+ "footer": "CDqThG_footer",
230
+ "on": "CDqThG_on",
231
+ "readOnly": "CDqThG_readOnly",
232
+ "reset": "CDqThG_reset",
233
+ "rowLabel": "CDqThG_rowLabel",
234
+ "colorValue": "CDqThG_colorValue",
235
+ "toolName": "CDqThG_toolName"
229
236
  };
230
237
  //#endregion
231
238
  //#region src/client/settings-card.tsx
232
239
  /**
233
- * The plugin's General-settings row: a "节点外观" title with the standard
234
- * chevron, expanding to palette rows, tool overrides, and the Think
235
- * visibility switch. Reads the namespace scope through the injected hooks
236
- * face and writes through the injected actions; every change applies
237
- * immediately. Drawn by hand (no value import from ui-settings-plugins — the
238
- * client bundle purity gate forbids cross-plugin value imports), styled with
239
- * the DSH design tokens like the shipped Appearance row.
240
+ * The node-appearance plugin config card. Mirrors the official
241
+ * `settings.plugin.item` card chrome (PluginCard: card shell, name-over-
242
+ * description header, chevron disclosure) with the DSH design tokens —
243
+ * drawn by hand because a feature plugin must not runtime-import another
244
+ * feature plugin's values (bundle purity gate).
245
+ *
246
+ * Writes are immediate: picking a color or flipping the Think switch applies
247
+ * it right away (a palette is a live preview, not a staged form). The footer
248
+ * carries one「恢复初始设置」button that resets the whole namespace to the
249
+ * shipped defaults — two-click confirmation so a miss cannot wipe a custom
250
+ * palette.
240
251
  */
241
252
  /** Chinese labels per category (product copy is Chinese). */
242
253
  const CATEGORY_LABELS = {
@@ -245,6 +256,7 @@ window.__ModuleLoader__.load({
245
256
  execute: "代码 / 指令执行",
246
257
  file: "文件操作",
247
258
  task: "任务 / 目标",
259
+ ask: "提问卡片",
248
260
  command: "指令节点",
249
261
  thinking: "思考过程",
250
262
  context: "上下文注入",
@@ -253,20 +265,59 @@ window.__ModuleLoader__.load({
253
265
  };
254
266
  /** Tools shown in the add-override autocomplete-free text input placeholder. */
255
267
  const TOOL_SUGGESTIONS = Object.values(TOOL_CATEGORIES).flat().join("、");
268
+ /** The card copy (one locale). */
269
+ const COPY = {
270
+ title: "节点外观",
271
+ description: "消息节点外观与颜色的偏好设置",
272
+ reset: "恢复初始设置",
273
+ resetConfirm: "再次点击确认恢复",
274
+ resetting: "恢复中…",
275
+ readOnly: "配置只读:插件主机侧未挂载或连接非本机。",
276
+ loading: "配置加载中…",
277
+ showThinking: "显示思考过程",
278
+ showThinkingHint: "关闭后会话中的 Think 思考行隐藏",
279
+ toolColors: "工具颜色覆盖(可选)",
280
+ addTool: "添加",
281
+ removeTool: "删除"
282
+ };
283
+ /** The shipped defaults, one face call away from any custom palette. */
284
+ const INITIAL_SETTINGS = {
285
+ showThinking: true,
286
+ colors: DEFAULT_COLORS,
287
+ toolColors: {}
288
+ };
256
289
  /**
257
- * Render the node-appearance General row.
258
- * @param props - the injected scope snapshot hook and write actions.
259
- * @returns the row, always visible once its namespace is served.
290
+ * Render the node-appearance config card.
291
+ * @param props - the injected scope snapshot hook, its write actions.
292
+ * @returns the card, matching the official plugin-card chrome.
260
293
  */
261
- function NodeAppearanceRow({ useNodeAppearance, setShowThinking, setCategoryColor, setToolColor, removeToolColor }) {
294
+ function NodeAppearanceRow({ useNodeAppearance, apply, setShowThinking, setCategoryColor, setToolColor, removeToolColor }) {
262
295
  const [open, setOpen] = (0, react.useState)(false);
296
+ const [confirmReset, setConfirmReset] = (0, react.useState)(false);
297
+ const [resetting, setResetting] = (0, react.useState)(false);
263
298
  const [toolName, setToolName] = (0, react.useState)("");
264
299
  const [toolColor, setToolColorDraft] = (0, react.useState)("#64748b");
265
300
  const snapshot = useNodeAppearance((snapshot) => snapshot);
266
- const colors = resolveColors(snapshot.value ?? {});
267
- const toolColors = snapshot.value?.toolColors ?? {};
268
- const showThinking = snapshot.value?.showThinking ?? true;
301
+ const served = snapshot.value ?? {};
302
+ const colors = resolveColors(served);
303
+ const toolColors = served.toolColors ?? {};
304
+ const showThinking = served.showThinking ?? true;
269
305
  const disabled = snapshot.writable === false;
306
+ const unavailable = snapshot.status !== "ready";
307
+ const resetInitial = async () => {
308
+ if (resetting) return;
309
+ setResetting(true);
310
+ try {
311
+ await apply(INITIAL_SETTINGS);
312
+ setConfirmReset(false);
313
+ } catch {} finally {
314
+ setResetting(false);
315
+ }
316
+ };
317
+ const handleReset = () => {
318
+ if (confirmReset) resetInitial();
319
+ else setConfirmReset(true);
320
+ };
270
321
  const addTool = (event) => {
271
322
  event.preventDefault();
272
323
  const name = toolName.trim();
@@ -274,136 +325,647 @@ window.__ModuleLoader__.load({
274
325
  setToolColor(name, toolColor);
275
326
  setToolName("");
276
327
  };
277
- return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
278
- className: card_module_css_default.group,
328
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("li", {
329
+ className: card_module_css_default.card + (open ? " " + card_module_css_default.cardOpen : ""),
279
330
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
280
331
  type: "button",
281
332
  className: card_module_css_default.header,
282
333
  "aria-expanded": open,
334
+ "aria-label": `${open ? "收起" : "展开"}:${COPY.title}`,
283
335
  onClick: () => {
284
336
  setOpen(!open);
285
337
  },
286
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
287
- className: card_module_css_default.title,
288
- children: "节点外观"
289
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronDownOutline14, { className: open ? card_module_css_default.chevronOpen : card_module_css_default.chevron })]
338
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
339
+ className: card_module_css_default.headText,
340
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
341
+ className: card_module_css_default.name,
342
+ children: COPY.title
343
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
344
+ className: card_module_css_default.description,
345
+ children: COPY.description
346
+ })]
347
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronDownOutline14, { className: card_module_css_default.chevron + (open ? " " + card_module_css_default.chevronOpen : "") })]
290
348
  }), open && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
291
349
  className: card_module_css_default.body,
292
- children: [snapshot.status !== "ready" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
293
- className: card_module_css_default.statusLine,
294
- children: snapshot.status === "unavailable" ? "配置暂不可用:插件主机侧未挂载或连接非本机。" : "配置加载中…"
295
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
296
- className: `${disabled ? card_module_css_default.disabled : ""}`,
350
+ children: [
351
+ unavailable && !disabled && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
352
+ className: card_module_css_default.statusLine,
353
+ children: COPY.loading
354
+ }),
355
+ disabled && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
356
+ className: card_module_css_default.readOnly,
357
+ role: "status",
358
+ children: COPY.readOnly
359
+ }),
360
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
361
+ className: disabled ? card_module_css_default.disabled : "",
362
+ children: [
363
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
364
+ className: card_module_css_default.row,
365
+ children: [
366
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("label", {
367
+ className: card_module_css_default.rowLabel,
368
+ htmlFor: "node-appearance-show-thinking",
369
+ children: COPY.showThinking
370
+ }),
371
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
372
+ className: card_module_css_default.rowHint,
373
+ children: COPY.showThinkingHint
374
+ }),
375
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
376
+ id: "node-appearance-show-thinking",
377
+ className: card_module_css_default.switch + (showThinking ? " " + card_module_css_default.on : ""),
378
+ type: "button",
379
+ "aria-pressed": showThinking,
380
+ onClick: () => {
381
+ setShowThinking(!showThinking);
382
+ },
383
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: card_module_css_default.knob })
384
+ })
385
+ ]
386
+ }),
387
+ Object.keys(CATEGORY_LABELS).map((category) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
388
+ className: card_module_css_default.row,
389
+ children: [
390
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("label", {
391
+ className: card_module_css_default.rowLabel,
392
+ htmlFor: `node-appearance-color-${category}`,
393
+ children: CATEGORY_LABELS[category]
394
+ }),
395
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
396
+ id: `node-appearance-color-${category}`,
397
+ className: card_module_css_default.colorInput,
398
+ type: "color",
399
+ value: colors[category],
400
+ onChange: (event) => {
401
+ setCategoryColor(category, event.target.value);
402
+ }
403
+ }),
404
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
405
+ className: card_module_css_default.colorValue,
406
+ children: colors[category]
407
+ })
408
+ ]
409
+ }, category)),
410
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
411
+ className: card_module_css_default.sectionTitle,
412
+ children: COPY.toolColors
413
+ }),
414
+ Object.entries(toolColors).map(([tool, color]) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
415
+ className: card_module_css_default.row,
416
+ children: [
417
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
418
+ className: card_module_css_default.toolName,
419
+ title: tool,
420
+ children: tool
421
+ }),
422
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
423
+ className: card_module_css_default.colorInput,
424
+ type: "color",
425
+ value: isCssColor(color) ? color : DEFAULT_COLORS.other,
426
+ onChange: (event) => {
427
+ setToolColor(tool, event.target.value);
428
+ }
429
+ }),
430
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
431
+ type: "button",
432
+ className: card_module_css_default.removeButton,
433
+ onClick: () => {
434
+ removeToolColor(tool);
435
+ },
436
+ children: COPY.removeTool
437
+ })
438
+ ]
439
+ }, tool)),
440
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("form", {
441
+ className: card_module_css_default.addRow,
442
+ onSubmit: addTool,
443
+ children: [
444
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
445
+ className: card_module_css_default.toolInput,
446
+ type: "text",
447
+ placeholder: `工具名,如 ${TOOL_SUGGESTIONS}`,
448
+ value: toolName,
449
+ onChange: (event) => {
450
+ setToolName(event.target.value);
451
+ }
452
+ }),
453
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
454
+ className: card_module_css_default.colorInput,
455
+ type: "color",
456
+ value: toolColor,
457
+ onChange: (event) => {
458
+ setToolColorDraft(event.target.value);
459
+ }
460
+ }),
461
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
462
+ type: "submit",
463
+ className: card_module_css_default.addButton,
464
+ disabled: toolName.trim() === "",
465
+ children: COPY.addTool
466
+ })
467
+ ]
468
+ })
469
+ ]
470
+ }),
471
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
472
+ className: card_module_css_default.footer,
473
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
474
+ type: "button",
475
+ className: card_module_css_default.reset + (confirmReset ? " " + card_module_css_default.resetConfirm : ""),
476
+ disabled: resetting,
477
+ onClick: handleReset,
478
+ children: resetting ? COPY.resetting : confirmReset ? COPY.resetConfirm : COPY.reset
479
+ })
480
+ })
481
+ ]
482
+ })]
483
+ });
484
+ }
485
+ //#endregion
486
+ //#region src/client/ask-model.ts
487
+ function isRecord(value) {
488
+ return typeof value === "object" && value !== null && !Array.isArray(value);
489
+ }
490
+ /** 读一个字符串字段,非字符串一律当空串。 */
491
+ function text(value) {
492
+ return typeof value === "string" ? value : "";
493
+ }
494
+ function parseJson(source) {
495
+ try {
496
+ return JSON.parse(source);
497
+ } catch {
498
+ return;
499
+ }
500
+ }
501
+ /** 从结果内容块拼出展示文本(文本块原样、其余块转 JSON)。 */
502
+ function resultTextOf(block) {
503
+ const content = block.content;
504
+ if (!Array.isArray(content) || content.length === 0) return null;
505
+ const parts = [];
506
+ for (const part of content) if (isRecord(part) && part.type === "text" && typeof part.text === "string") parts.push(part.text);
507
+ else parts.push(JSON.stringify(part, null, 2));
508
+ return parts.length === 0 ? null : parts.join("\n");
509
+ }
510
+ function readOptions(value) {
511
+ if (!Array.isArray(value)) return [];
512
+ const options = [];
513
+ for (const item of value) {
514
+ if (typeof item === "string") {
515
+ if (item !== "") options.push({
516
+ label: item,
517
+ description: ""
518
+ });
519
+ continue;
520
+ }
521
+ if (!isRecord(item)) continue;
522
+ const label = text(item.label);
523
+ if (label === "") continue;
524
+ options.push({
525
+ label,
526
+ description: text(item.description)
527
+ });
528
+ }
529
+ return options;
530
+ }
531
+ /** 读调用参数里的问题列表;拿不到合法问题时返回 null(由调用方兜底)。 */
532
+ function readQuestions(argsRaw) {
533
+ const parsed = parseJson(argsRaw);
534
+ if (!isRecord(parsed) || !Array.isArray(parsed.questions)) return null;
535
+ const questions = [];
536
+ for (const item of parsed.questions) {
537
+ if (!isRecord(item)) continue;
538
+ const question = text(item.question);
539
+ if (question === "") continue;
540
+ questions.push({
541
+ id: text(item.id),
542
+ question,
543
+ header: text(item.header),
544
+ detail: text(item.detail),
545
+ multiSelect: item.multiSelect === true,
546
+ options: readOptions(item.options)
547
+ });
548
+ }
549
+ return questions.length === 0 ? null : questions;
550
+ }
551
+ function readAnswers(resultText) {
552
+ if (resultText === null) return null;
553
+ const parsed = parseJson(resultText);
554
+ if (!isRecord(parsed) || !Array.isArray(parsed.answers)) return null;
555
+ const answers = [];
556
+ for (const item of parsed.answers) {
557
+ if (!isRecord(item)) continue;
558
+ const selected = Array.isArray(item.selected) ? item.selected.filter((v) => typeof v === "string") : [];
559
+ answers.push({
560
+ id: text(item.id),
561
+ selected,
562
+ custom: text(item.custom)
563
+ });
564
+ }
565
+ return answers;
566
+ }
567
+ /** 调用参数:运行中取 block 自身,结算后取结算节点的调用头。 */
568
+ function argsRawOf(block) {
569
+ return "kind" in block ? block.call?.argsRaw ?? "" : block.argsRaw ?? "";
570
+ }
571
+ /**
572
+ * 从一次提问调用的冻结切片派生卡片模型。
573
+ * @param block - 运行中的调用或已结算的结果节点。
574
+ * @returns 卡片模型;参数或结果畸形时 `questions` 为空并带兜底文本。
575
+ */
576
+ function askCardModel(block) {
577
+ const settled = "kind" in block;
578
+ const code = settled ? block.error?.code : void 0;
579
+ const state = !settled ? "running" : code === "ASK_ABORTED" || code === "interrupted" ? "stopped" : code === "ASK_CANCELLED" ? "ok" : block.isError === true ? "error" : "ok";
580
+ const argsRaw = argsRawOf(block);
581
+ const resultText = settled ? resultTextOf(block) : null;
582
+ let verdict;
583
+ let phase;
584
+ if (code === "ASK_CANCELLED") {
585
+ verdict = "cancelled";
586
+ phase = "unanswered";
587
+ } else if (code === "ASK_ABORTED") {
588
+ verdict = "aborted";
589
+ phase = "unanswered";
590
+ } else if (!settled) phase = "pending";
591
+ else phase = resultText === null ? "unanswered" : "answered";
592
+ const raw = readQuestions(argsRaw);
593
+ if (raw === null) return {
594
+ state,
595
+ phase,
596
+ verdict,
597
+ questions: [],
598
+ answeredCount: 0,
599
+ fallbackText: resultText ?? (argsRaw === "" ? null : argsRaw)
600
+ };
601
+ const byId = /* @__PURE__ */ new Map();
602
+ for (const answer of readAnswers(resultText) ?? []) if (!byId.has(answer.id)) byId.set(answer.id, answer);
603
+ const questions = raw.map((question) => {
604
+ const answer = byId.get(question.id);
605
+ return {
606
+ ...question,
607
+ selected: answer?.selected ?? [],
608
+ custom: answer?.custom ?? ""
609
+ };
610
+ });
611
+ const answeredCount = questions.filter((question) => question.selected.length > 0 || question.custom !== "").length;
612
+ return {
613
+ state,
614
+ phase,
615
+ verdict,
616
+ questions,
617
+ answeredCount,
618
+ fallbackText: resultText
619
+ };
620
+ }
621
+ /** 折叠行右侧的进度计数(文案由组件用 locale 组装)。 */
622
+ function askProgress(model) {
623
+ return {
624
+ answered: model.answeredCount,
625
+ total: model.questions.length
626
+ };
627
+ }
628
+ //#endregion
629
+ //#region \0dsh-css:H:\MaxNull\WorkStation\max-null-plugins\dsh-node-appearance\src\client\ask-card.module.css.mjs
630
+ const css$1 = ".ylzLDa_root{--ask-accent:var(--ncolor-accent,#65a30d);flex-direction:column;display:flex}.ylzLDa_row{position:relative;overflow:hidden}.ylzLDa_root[data-state=running] .ylzLDa_row:after{content:\"\";background:linear-gradient(90deg, transparent 0%, color-mix(in srgb, var(--dsw-alias-bg-base) 60%, transparent) 55%, transparent 100%);pointer-events:none;width:300px;animation:2.6s ease-out infinite ylzLDa_ask-row-sweep;position:absolute;top:0;bottom:0;left:0}@keyframes ylzLDa_ask-row-sweep{0%{left:-300px}90%,to{left:100%}}.ylzLDa_leading{flex-shrink:0}.ylzLDa_chevron{color:var(--dsw-alias-label-secondary)}.ylzLDa_title{font-weight:400}.ylzLDa_sep{background:var(--dsw-alias-label-caption);border-radius:1px;flex:none;width:2px;height:2px;margin:0 8px}.ylzLDa_summary{text-overflow:ellipsis;white-space:nowrap;min-width:0;font-size:var(--dsh-content-font-size-secondary,13px);line-height:calc(24px + var(--dsh-content-font-delta,0px));color:var(--dsw-alias-label-tertiary);flex:auto;overflow:hidden}.ylzLDa_bodyWrap{flex-direction:column;display:flex}.ylzLDa_card{border:1px solid color-mix(in srgb, var(--ask-accent) 34%, transparent);background:color-mix(in srgb, var(--ask-accent) 6%, var(--dsw-alias-bg-base));border-radius:12px;flex-direction:column;gap:14px;margin:4px 0 4px 4px;padding:14px 16px;display:flex}.ylzLDa_item{flex-direction:column;gap:8px;min-width:0;display:flex}.ylzLDa_header{color:var(--ask-accent);font-size:var(--dsh-content-font-size-secondary,13px);font-weight:500;line-height:calc(20px + var(--dsh-content-font-delta,0px))}.ylzLDa_question{color:var(--dsw-alias-label-primary);font-size:var(--dsh-content-font-size,14px);line-height:calc(22px + var(--dsh-content-font-delta,0px));white-space:pre-wrap;overflow-wrap:anywhere}.ylzLDa_detail{color:var(--dsw-alias-label-tertiary);font-size:var(--dsh-content-font-size-secondary,13px);line-height:calc(20px + var(--dsh-content-font-delta,0px));white-space:pre-wrap;overflow-wrap:anywhere}.ylzLDa_verdict{color:var(--dsw-alias-label-tertiary);font-size:var(--dsh-content-font-size-secondary,13px);line-height:calc(20px + var(--dsh-content-font-delta,0px));margin:0}.ylzLDa_options{flex-direction:column;gap:6px;display:flex}.ylzLDa_option{border:1px solid var(--dsw-alias-border-l3);background:var(--dsw-alias-bg-layer-1);color:var(--dsw-alias-label-secondary);font-size:var(--dsh-content-font-size,14px);line-height:calc(22px + var(--dsh-content-font-delta,0px));border-radius:8px;align-items:flex-start;gap:8px;padding:9px 12px;display:flex}.ylzLDa_option[data-chosen]{border-color:var(--ask-accent);background:color-mix(in srgb, var(--ask-accent) 14%, transparent);color:var(--ask-accent);font-weight:500}.ylzLDa_marker{width:16px;height:calc(22px + var(--dsh-content-font-delta,0px));flex:none;justify-content:center;align-items:center;display:inline-flex}.ylzLDa_optionText{flex-direction:column;gap:2px;min-width:0;display:flex}.ylzLDa_label{overflow-wrap:anywhere}.ylzLDa_desc{color:inherit;opacity:.72;font-size:var(--dsh-content-font-size-secondary,13px);font-weight:400;line-height:calc(20px + var(--dsh-content-font-delta,0px));overflow-wrap:anywhere}.ylzLDa_answers{flex-direction:column;gap:2px;display:flex}.ylzLDa_answerLine{color:var(--dsw-alias-label-primary);font-size:var(--dsh-content-font-size,14px);line-height:calc(22px + var(--dsh-content-font-delta,0px));white-space:pre-wrap;overflow-wrap:anywhere}.ylzLDa_answersEmpty{color:var(--dsw-alias-label-tertiary);font-size:var(--dsh-content-font-size,14px);line-height:calc(22px + var(--dsh-content-font-delta,0px))}.ylzLDa_fallback{white-space:pre-wrap;overflow-wrap:anywhere;font:var(--dsw-font-xs-13);color:var(--dsw-alias-label-tertiary);margin:4px 0 4px 4px}.ylzLDa_inspectButton{border:.5px solid var(--dsw-alias-border-l3);corner-shape:round;background:var(--dsw-alias-bg-base);color:var(--dsw-alias-label-secondary);cursor:pointer;opacity:0;border-radius:999px;align-self:flex-start;align-items:center;gap:4px;margin:4px 0 2px 4px;padding:2px 8px;font-size:11px;line-height:16px;transition:opacity .1s;display:inline-flex}.ylzLDa_root:hover .ylzLDa_inspectButton,.ylzLDa_inspectButton:focus-visible{opacity:1}.ylzLDa_inspectButton:hover{background:var(--dsw-alias-interactive-bg-hover-solid);color:var(--dsw-alias-label-primary)}";
631
+ const tagId$1 = "@max-null/dsh-node-appearance/ask-card.module.css";
632
+ if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$1) + "]") === null) {
633
+ const tag = document.createElement("style");
634
+ tag.dataset.plugin = "@max-null/dsh-node-appearance";
635
+ tag.dataset.pluginCss = tagId$1;
636
+ tag.textContent = css$1;
637
+ document.head.appendChild(tag);
638
+ }
639
+ var ask_card_module_css_default = {
640
+ "chevron": "ylzLDa_chevron",
641
+ "summary": "ylzLDa_summary",
642
+ "detail": "ylzLDa_detail",
643
+ "optionText": "ylzLDa_optionText",
644
+ "answersEmpty": "ylzLDa_answersEmpty",
645
+ "sep": "ylzLDa_sep",
646
+ "inspectButton": "ylzLDa_inspectButton",
647
+ "answers": "ylzLDa_answers",
648
+ "answerLine": "ylzLDa_answerLine",
649
+ "leading": "ylzLDa_leading",
650
+ "title": "ylzLDa_title",
651
+ "ask-row-sweep": "ylzLDa_ask-row-sweep",
652
+ "root": "ylzLDa_root",
653
+ "label": "ylzLDa_label",
654
+ "bodyWrap": "ylzLDa_bodyWrap",
655
+ "row": "ylzLDa_row",
656
+ "verdict": "ylzLDa_verdict",
657
+ "desc": "ylzLDa_desc",
658
+ "card": "ylzLDa_card",
659
+ "options": "ylzLDa_options",
660
+ "option": "ylzLDa_option",
661
+ "header": "ylzLDa_header",
662
+ "item": "ylzLDa_item",
663
+ "question": "ylzLDa_question",
664
+ "marker": "ylzLDa_marker",
665
+ "fallback": "ylzLDa_fallback"
666
+ };
667
+ //#endregion
668
+ //#region src/client/ask-card.tsx
669
+ /**
670
+ * 提问卡:接管 `ask_user_question` 的工具行。
671
+ *
672
+ * 官方转录卡只列**已选答案**,结算后就再也看不到当时还有哪些选项。本卡片
673
+ * 用调用参数里的 `options` 把每个选项都渲染成一块,被选中的那块用插件色板
674
+ * 里 ask 类别的强调色(默认蓝)反显并打勾。
675
+ *
676
+ * 接管是 DSH 支持的机制(keyed slot 用不同 priority 遮蔽既有占用者,
677
+ * 见 ui-slots 的 `register()`),代价是本文件要自己承担官方 ToolRow 的
678
+ * 折叠行外观 —— 因此行外壳逐项对齐 ToolRow.module.css 的既有视觉
679
+ * (24px 行高、2px 分隔点、13px 次级摘要、running 扫光、hover 才现的
680
+ * Inspect 胶囊),组件则全部来自共享的 ui-primitives。
681
+ */
682
+ /** 折叠行右侧的摘要:等待 / 已取消 / 已中断 / 未回答 / N-M 已回答。 */
683
+ function summaryText(model, t) {
684
+ if (model.phase === "pending") return t("ask.waiting");
685
+ if (model.phase === "unanswered") {
686
+ if (model.verdict === "cancelled") return t("ask.cancelled");
687
+ if (model.verdict === "aborted") return t("ask.interrupted");
688
+ return t("ask.skipped");
689
+ }
690
+ const { answered, total } = askProgress(model);
691
+ return t("ask.answered", {
692
+ answered,
693
+ total
694
+ });
695
+ }
696
+ /** 一行「未选/已选」选项块。 */
697
+ function OptionRow({ label, description, chosen }) {
698
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
699
+ className: ask_card_module_css_default.option,
700
+ "data-chosen": chosen ? "" : void 0,
701
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
702
+ className: ask_card_module_css_default.marker,
703
+ "aria-hidden": "true",
704
+ children: chosen && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconCheckOutline14, {})
705
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
706
+ className: ask_card_module_css_default.optionText,
707
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
708
+ className: ask_card_module_css_default.label,
709
+ children: label
710
+ }), description !== void 0 && description !== "" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
711
+ className: ask_card_module_css_default.desc,
712
+ children: description
713
+ })]
714
+ })]
715
+ });
716
+ }
717
+ /** 展开后的卡片体:逐问题列出全部选项并标出选中项。 */
718
+ function QuestionList({ model, t }) {
719
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
720
+ className: ask_card_module_css_default.card,
721
+ children: [model.verdict !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
722
+ className: ask_card_module_css_default.verdict,
723
+ children: model.verdict === "cancelled" ? t("ask.cancelledDetail") : t("ask.interruptedDetail")
724
+ }), model.questions.map((question, index) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
725
+ className: ask_card_module_css_default.item,
726
+ children: [
727
+ question.header !== "" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
728
+ className: ask_card_module_css_default.header,
729
+ children: question.header
730
+ }),
731
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
732
+ className: ask_card_module_css_default.question,
733
+ children: question.question
734
+ }),
735
+ question.detail !== "" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
736
+ className: ask_card_module_css_default.detail,
737
+ children: question.detail
738
+ }),
739
+ question.options.length > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
740
+ className: ask_card_module_css_default.options,
741
+ children: [question.options.map((option, optionIndex) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(OptionRow, {
742
+ label: option.label,
743
+ description: option.description,
744
+ chosen: question.selected.includes(option.label)
745
+ }, `${String(optionIndex)}-${option.label}`)), question.custom !== "" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(OptionRow, {
746
+ label: question.custom,
747
+ chosen: true
748
+ })]
749
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
750
+ className: ask_card_module_css_default.answers,
751
+ children: [
752
+ question.selected.map((answer, answerIndex) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
753
+ className: ask_card_module_css_default.answerLine,
754
+ children: answer
755
+ }, `${String(answerIndex)}-${answer}`)),
756
+ question.custom !== "" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
757
+ className: ask_card_module_css_default.answerLine,
758
+ children: question.custom
759
+ }),
760
+ question.selected.length === 0 && question.custom === "" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
761
+ className: ask_card_module_css_default.answersEmpty,
762
+ children: t("ask.skipped")
763
+ })
764
+ ]
765
+ })
766
+ ]
767
+ }, question.id !== "" ? question.id : `q${String(index)}`))]
768
+ });
769
+ }
770
+ /**
771
+ * 渲染一次提问的完整记录。
772
+ * @param props - locale 翻译函数、冻结的调用切片与可选的轨迹入口。
773
+ * @returns 折叠行 + 展开后的选项卡片。
774
+ */
775
+ function AskQuestionRow({ t, block, inspect }) {
776
+ const model = (0, react.useMemo)(() => askCardModel(block), [block]);
777
+ const [expanded, setExpanded] = (0, react.useState)(false);
778
+ const hasBody = model.questions.length > 0 || model.fallbackText !== null;
779
+ const open = expanded && hasBody;
780
+ const leading = model.state === "error" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.StateDot, { state: "error" }) : model.state === "stopped" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.StateDot, { state: "warning" }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconQuestionOutline14, {});
781
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
782
+ className: ask_card_module_css_default.root,
783
+ "data-variant": "others",
784
+ "data-tool": "ask_user_question",
785
+ "data-state": model.state,
786
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.DisclosureRow, {
787
+ rowClassName: ask_card_module_css_default.row,
788
+ leadingClassName: ask_card_module_css_default.leading,
789
+ titleClassName: ask_card_module_css_default.title,
790
+ chevronClassName: ask_card_module_css_default.chevron,
791
+ icon: leading,
792
+ title: t("ask.rowTitle"),
793
+ open,
794
+ expandable: hasBody,
795
+ expandOnRowClick: true,
796
+ keepContentWhenOpen: true,
797
+ onToggle: () => {
798
+ setExpanded((value) => !value);
799
+ },
800
+ collapsedContent: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
801
+ className: ask_card_module_css_default.sep,
802
+ "aria-hidden": true
803
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
804
+ className: ask_card_module_css_default.summary,
805
+ children: summaryText(model, t)
806
+ })] }),
807
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
808
+ className: ask_card_module_css_default.bodyWrap,
809
+ children: [model.questions.length > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(QuestionList, {
810
+ model,
811
+ t
812
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("pre", {
813
+ className: ask_card_module_css_default.fallback,
814
+ children: model.fallbackText
815
+ }), inspect !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
816
+ type: "button",
817
+ className: ask_card_module_css_default.inspectButton,
818
+ onClick: inspect,
819
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconInspectOutline12, {}), t("row.inspect")]
820
+ })]
821
+ })
822
+ })
823
+ });
824
+ }
825
+ //#endregion
826
+ //#region \0dsh-css:H:\MaxNull\WorkStation\max-null-plugins\dsh-node-appearance\src\client\goal-detail.module.css.mjs
827
+ const css = ".WyWU3a_root{box-sizing:border-box;width:calc(100% - var(--dsh-composer-side-clearance) - var(--dsh-composer-side-clearance) - var(--dsh-composer-dock-inset) - var(--dsh-composer-dock-inset) - var(--dsh-composer-dock-inset) - var(--dsh-composer-dock-inset));max-width:calc(var(--dsh-composer-card-max-width) - var(--dsh-composer-dock-inset) - var(--dsh-composer-dock-inset) - var(--dsh-composer-dock-inset) - var(--dsh-composer-dock-inset));border:.5px solid var(--dsw-alias-border-l1);background:var(--dsw-specific-tip);--dsh-scrollbar-thumb:var(--dsw-alias-scrollbar-bg-l2);--dsh-scrollbar-thumb-hover:var(--dsw-alias-scrollbar-hover-l2);border-top-color:#0000;border-radius:0 0 12px 12px;flex:none;margin:-6px auto 0;overflow:hidden}[data-goal-bar]>div:first-child{border-bottom-color:#0000;border-bottom-right-radius:0;border-bottom-left-radius:0}.WyWU3a_body{flex-direction:column;gap:8px;padding:6px 12px;display:flex}.WyWU3a_header{text-align:left;cursor:pointer;background:0 0;border:none;align-items:center;gap:10px;width:100%;padding:0;display:flex}.WyWU3a_lead{color:var(--dsw-alias-label-tertiary);flex:none;place-items:center;display:grid}.WyWU3a_title{color:var(--dsw-alias-label-primary);flex:none;font-size:13px;font-weight:500;line-height:24px}.WyWU3a_summary{min-width:0;color:var(--dsw-alias-label-tertiary);text-overflow:ellipsis;white-space:nowrap;flex:auto;font-size:13px;line-height:20px;overflow:hidden}.WyWU3a_chevron{color:var(--dsw-alias-label-tertiary);flex:none;place-items:center;display:grid}.WyWU3a_detail{flex-direction:column;gap:6px;max-height:180px;margin:0;display:flex;overflow-y:auto}.WyWU3a_row{grid-template-columns:max-content 1fr;align-items:baseline;column-gap:10px;margin:0;display:grid}.WyWU3a_key{color:var(--dsw-alias-label-caption);font-size:13px;line-height:20px}.WyWU3a_value{min-width:0;color:var(--dsw-alias-label-secondary);white-space:pre-wrap;overflow-wrap:anywhere;margin:0;font-size:13px;line-height:20px}.WyWU3a_code{font-family:var(--ds-font-family-code);color:var(--dsw-alias-label-caption);font-size:11px}.WyWU3a_value>.WyWU3a_code:not(:first-child){margin-left:8px}";
828
+ const tagId = "@max-null/dsh-node-appearance/goal-detail.module.css";
829
+ if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId) + "]") === null) {
830
+ const tag = document.createElement("style");
831
+ tag.dataset.plugin = "@max-null/dsh-node-appearance";
832
+ tag.dataset.pluginCss = tagId;
833
+ tag.textContent = css;
834
+ document.head.appendChild(tag);
835
+ }
836
+ var goal_detail_module_css_default = {
837
+ "header": "WyWU3a_header",
838
+ "summary": "WyWU3a_summary",
839
+ "row": "WyWU3a_row",
840
+ "lead": "WyWU3a_lead",
841
+ "value": "WyWU3a_value",
842
+ "chevron": "WyWU3a_chevron",
843
+ "key": "WyWU3a_key",
844
+ "detail": "WyWU3a_detail",
845
+ "code": "WyWU3a_code",
846
+ "root": "WyWU3a_root",
847
+ "body": "WyWU3a_body",
848
+ "title": "WyWU3a_title"
849
+ };
850
+ //#endregion
851
+ //#region src/client/goal-detail.tsx
852
+ /**
853
+ * 目标详情折叠条:接在官方 GoalBar 下面(`conversation.input.dock` 里
854
+ * `order: 11`,官方那条是 10),把官方条上被 ellipsis 截断的目标展开成可读
855
+ * 详情。
856
+ *
857
+ * 卡片形态照同槽的待办面板(ui-conversation 的 TodoPanel):同一套 dock 宽度
858
+ * 公式、同一张 `--dsw-specific-tip` 卡片、同一个「标题 + 右侧摘要 + chevron」
859
+ * 折叠头,默认收起。折叠头只放官方条**没有**的元信息(版本号、自主轮次),
860
+ * 目标原文放在展开区 —— 这样两块不会互相重复。
861
+ *
862
+ * 为什么是「另加一条」而不是接管官方条:`conversation.input.dock` 是 list
863
+ * 槽,不同 id 各自成格并列渲染;而官方 ui-goal 的注入面(edit/pause/resume/
864
+ * clear 四个 Remote 动词 + 一个带竞态防护的 activation 订阅源)是**注册者
865
+ * 私有**的,接管就得整套复刻 —— 那会把这个「只读地多看一行」的需求变成
866
+ * 插件里多一处会写会话数据的 RPC 客户端。这里只读 `useProjection('goal')`。
867
+ */
868
+ /** 阶段文案:与官方 goal 字典同义,按本插件既有做法直写中文。 */
869
+ const PHASE_LABEL = {
870
+ active: "进行中",
871
+ paused: "已暂停",
872
+ blocked: "已阻塞",
873
+ complete: "已完成"
874
+ };
875
+ /** 折叠头右侧摘要:只放官方条没有的元信息。 */
876
+ function summaryLabel(goal) {
877
+ const parts = [`rev ${String(goal.revision)}`];
878
+ if (goal.roundsStarted !== void 0) parts.push(goal.maxGoalRounds === void 0 ? `${String(goal.roundsStarted)} 轮` : `${String(goal.roundsStarted)} / ${String(goal.maxGoalRounds)} 轮`);
879
+ return parts.join(" · ");
880
+ }
881
+ /** 展开区里的一组「键 / 值」。 */
882
+ function Row({ label, children }) {
883
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
884
+ className: goal_detail_module_css_default.row,
885
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("dt", {
886
+ className: goal_detail_module_css_default.key,
887
+ children: label
888
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("dd", {
889
+ className: goal_detail_module_css_default.value,
890
+ children
891
+ })]
892
+ });
893
+ }
894
+ /**
895
+ * 渲染目标详情折叠条。
896
+ * @param props - 框架标准投影席位。
897
+ * @returns 折叠条;加载中、无目标、或目标已完成时不渲染(与官方条一致)。
898
+ */
899
+ function GoalDetail({ useProjection }) {
900
+ const projection = useProjection("goal");
901
+ const [collapsed, setCollapsed] = (0, react.useState)(true);
902
+ const goalId = projection?.goal.id;
903
+ (0, react.useEffect)(() => {
904
+ setCollapsed(true);
905
+ }, [goalId]);
906
+ const goal = projection?.goal;
907
+ if (goal === void 0 || goal === null || goal.phase === "complete") return null;
908
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("section", {
909
+ className: goal_detail_module_css_default.root,
910
+ "data-goal-detail": true,
911
+ "aria-label": "目标详情",
912
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
913
+ className: goal_detail_module_css_default.body,
914
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
915
+ type: "button",
916
+ className: goal_detail_module_css_default.header,
917
+ "aria-expanded": !collapsed,
918
+ onClick: () => {
919
+ setCollapsed((value) => !value);
920
+ },
921
+ children: [
922
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
923
+ className: goal_detail_module_css_default.lead,
924
+ "aria-hidden": true,
925
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconGoalOutline16, { size: 14 })
926
+ }),
927
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
928
+ className: goal_detail_module_css_default.title,
929
+ children: "目标详情"
930
+ }),
931
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
932
+ className: goal_detail_module_css_default.summary,
933
+ children: summaryLabel(goal)
934
+ }),
935
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
936
+ className: goal_detail_module_css_default.chevron,
937
+ "aria-hidden": true,
938
+ children: collapsed ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronUpOutline14, {}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronDownOutline14, {})
939
+ })
940
+ ]
941
+ }), !collapsed && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("dl", {
942
+ className: goal_detail_module_css_default.detail,
297
943
  children: [
298
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
299
- className: card_module_css_default.row,
300
- children: [
301
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("label", {
302
- className: card_module_css_default.rowLabel,
303
- htmlFor: "node-appearance-show-thinking",
304
- children: "显示思考过程"
305
- }),
306
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
307
- className: card_module_css_default.rowHint,
308
- children: "关闭后会话中的 Think 思考行隐藏"
309
- }),
310
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
311
- id: "node-appearance-show-thinking",
312
- className: `${card_module_css_default.switch}${showThinking ? ` ${card_module_css_default.on}` : ""}`,
313
- type: "button",
314
- "aria-pressed": showThinking,
315
- onClick: () => {
316
- setShowThinking(!showThinking);
317
- },
318
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: card_module_css_default.knob })
319
- })
320
- ]
944
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Row, {
945
+ label: "目标",
946
+ children: goal.objective
321
947
  }),
322
- Object.keys(CATEGORY_LABELS).map((category) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
323
- className: card_module_css_default.row,
324
- children: [
325
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("label", {
326
- className: card_module_css_default.rowLabel,
327
- htmlFor: `node-appearance-color-${category}`,
328
- children: CATEGORY_LABELS[category]
329
- }),
330
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
331
- id: `node-appearance-color-${category}`,
332
- className: card_module_css_default.colorInput,
333
- type: "color",
334
- value: colors[category],
335
- onChange: (event) => {
336
- setCategoryColor(category, event.target.value);
337
- }
338
- }),
339
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
340
- className: card_module_css_default.colorValue,
341
- children: colors[category]
342
- })
343
- ]
344
- }, category)),
345
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
346
- className: card_module_css_default.sectionTitle,
347
- children: "工具颜色覆盖(可选)"
948
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Row, {
949
+ label: "阶段",
950
+ children: PHASE_LABEL[goal.phase]
348
951
  }),
349
- Object.entries(toolColors).map(([tool, color]) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
350
- className: card_module_css_default.toolRow,
351
- children: [
352
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
353
- className: card_module_css_default.toolName,
354
- title: tool,
355
- children: tool
356
- }),
357
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
358
- className: card_module_css_default.colorInput,
359
- type: "color",
360
- value: isCssColor(color) ? color : DEFAULT_COLORS.other,
361
- onChange: (event) => {
362
- setToolColor(tool, event.target.value);
363
- }
364
- }),
365
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
366
- type: "button",
367
- className: card_module_css_default.removeButton,
368
- onClick: () => {
369
- removeToolColor(tool);
370
- },
371
- children: "删除"
372
- })
373
- ]
374
- }, tool)),
375
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("form", {
376
- className: card_module_css_default.addRow,
377
- onSubmit: addTool,
378
- children: [
379
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
380
- className: card_module_css_default.toolInput,
381
- type: "text",
382
- placeholder: `工具名,如 ${TOOL_SUGGESTIONS}`,
383
- value: toolName,
384
- onChange: (event) => {
385
- setToolName(event.target.value);
386
- }
387
- }),
388
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
389
- className: card_module_css_default.colorInput,
390
- type: "color",
391
- value: toolColor,
392
- onChange: (event) => {
393
- setToolColorDraft(event.target.value);
394
- }
395
- }),
396
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
397
- type: "submit",
398
- className: card_module_css_default.addButton,
399
- disabled: toolName.trim() === "",
400
- children: "添加"
401
- })
402
- ]
952
+ goal.blockedReason !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(Row, {
953
+ label: "阻塞",
954
+ children: [goal.blockedReason.message, /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
955
+ className: goal_detail_module_css_default.code,
956
+ children: goal.blockedReason.code
957
+ })]
958
+ }),
959
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Row, {
960
+ label: "标识",
961
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
962
+ className: goal_detail_module_css_default.code,
963
+ children: goal.id
964
+ })
403
965
  })
404
966
  ]
405
967
  })]
406
- })]
968
+ })
407
969
  });
408
970
  }
409
971
  //#endregion
@@ -437,6 +999,11 @@ window.__ModuleLoader__.load({
437
999
  }), "dsh-node-appearance: repaint on settings change");
438
1000
  const face = {
439
1001
  hooks: { nodeAppearance: scope },
1002
+ apply: async (value) => {
1003
+ await scope.set("showThinking", value.showThinking ?? true);
1004
+ await scope.set("colors", value.colors ?? {});
1005
+ await scope.set("toolColors", value.toolColors ?? {});
1006
+ },
440
1007
  setShowThinking: (show) => {
441
1008
  scope.set("showThinking", show);
442
1009
  },
@@ -465,6 +1032,17 @@ window.__ModuleLoader__.load({
465
1032
  key: NODE_APPEARANCE_NS,
466
1033
  inject: () => face
467
1034
  }, NodeAppearanceRow));
1035
+ ctx.slots.inject("tool.call.toolview", () => ctx.slots.register({
1036
+ name: "tool.call.toolview",
1037
+ key: "ask_user_question",
1038
+ priority: -1,
1039
+ locale: "conversation"
1040
+ }, AskQuestionRow));
1041
+ ctx.slots.inject("conversation.input.dock", () => ctx.slots.register({
1042
+ name: "conversation.input.dock",
1043
+ id: "max-null/goal-detail",
1044
+ order: 11
1045
+ }, GoalDetail));
468
1046
  }
469
1047
  //#endregion
470
1048
  exports.apply = apply;