dsh-context 0.54.3 → 0.55.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
@@ -3,10 +3,33 @@ window.__ModuleLoader__.load({
3
3
  factory: (require) => {
4
4
  var module = { exports: {} };
5
5
  module.exports;
6
+ //#region \0rolldown/runtime.js
7
+ var __create = Object.create;
8
+ var __defProp = Object.defineProperty;
9
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
10
+ var __getOwnPropNames = Object.getOwnPropertyNames;
11
+ var __getProtoOf = Object.getPrototypeOf;
12
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
13
+ var __copyProps = (to, from, except, desc) => {
14
+ if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
15
+ key = keys[i];
16
+ if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
17
+ get: ((k) => from[k]).bind(null, key),
18
+ enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
19
+ });
20
+ }
21
+ return to;
22
+ };
23
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule || !__hasOwnProp.call(mod, "default") ? __defProp(target, "default", {
24
+ value: mod,
25
+ enumerable: true
26
+ }) : target, mod));
27
+ //#endregion
6
28
  let react = require("react");
7
29
  let react_jsx_runtime = require("react/jsx-runtime");
8
30
  let react_dom = require("react-dom");
9
31
  let _deepseek_ai_dsh_client_ui_primitives = require("@deepseek-ai/dsh-client-ui-primitives");
32
+ _deepseek_ai_dsh_client_ui_primitives = __toESM(_deepseek_ai_dsh_client_ui_primitives, 1);
10
33
  //#region src/client/i18n.ts
11
34
  const DICT_ZH = {
12
35
  "tab": "上下文",
@@ -55,6 +78,7 @@ window.__ModuleLoader__.load({
55
78
  "timing.other": "其他开销",
56
79
  "timing.callTimes": "{n}次",
57
80
  "timing.toolTimes": "{n}次",
81
+ "timing.blockTimes": "{n}次",
58
82
  "timing.empty": "暂无耗时数据 · 对话开始后这里会显示时长分布",
59
83
  "timing.tps": "{tps} tok/s",
60
84
  "timing.tpsTip": "输出速度(TPS):累计解码输出 tokens ÷ 累计解码时长,与 DeepSeek Harness 会话统计同口径。",
@@ -67,7 +91,7 @@ window.__ModuleLoader__.load({
67
91
  "plugin.name": "插件",
68
92
  "plugin.github": "GitHub",
69
93
  "plugin.settings": "设置",
70
- "plugin.settingsOpen": "打开设置",
94
+ "plugin.settingsOpen": "打开偏好设置",
71
95
  "trend.title": "上下文趋势",
72
96
  "gran.step": "步骤",
73
97
  "gran.turn": "轮次",
@@ -427,6 +451,7 @@ window.__ModuleLoader__.load({
427
451
  "timing.other": "Overhead",
428
452
  "timing.callTimes": "{n} calls",
429
453
  "timing.toolTimes": "{n} runs",
454
+ "timing.blockTimes": "{n} blocks",
430
455
  "timing.empty": "No timing data yet — durations appear as the conversation runs",
431
456
  "timing.tps": "{tps} tok/s",
432
457
  "timing.tpsTip": "Output speed (TPS): summed decoded output tokens over summed decode time — the same pairing as the harness session stats.",
@@ -439,7 +464,7 @@ window.__ModuleLoader__.load({
439
464
  "plugin.name": "Plugin",
440
465
  "plugin.github": "GitHub",
441
466
  "plugin.settings": "Settings",
442
- "plugin.settingsOpen": "Open in Settings",
467
+ "plugin.settingsOpen": "Open preferences",
443
468
  "trend.title": "Context Trend",
444
469
  "gran.step": "Step",
445
470
  "gran.turn": "Turn",
@@ -753,20 +778,23 @@ window.__ModuleLoader__.load({
753
778
  "ov.time.d": "{n}d ago"
754
779
  };
755
780
  //#endregion
781
+ //#region icon.svg?raw
782
+ var icon_default = "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 1024 1024\">\n <path d=\"M899.984 19.873h-3.452c-26.123 0-47.296 21.172-47.296 47.296v888.508c0 26.127 21.173 47.298 47.296 47.298h3.452c26.119 0 47.297-21.171 47.297-47.298V67.169c0-26.124-21.177-47.296-47.297-47.296z\" fill=\"#4A5699\"/>\n <path d=\"M132.643 19.873h-3.449c-26.12 0-47.296 21.172-47.296 47.296v888.508c0 26.127 21.177 47.298 47.296 47.298h3.449c26.123 0 47.299-21.171 47.299-47.298V67.169c0-26.124-21.176-47.296-47.299-47.296z\" fill=\"#C45FA0\"/>\n <path d=\"M899.463 19.873H129.194c-26.12 0-47.296 21.172-47.296 47.296v3.377c0 26.12 21.177 47.299 47.296 47.299h770.269c26.123 0 47.296-21.179 47.296-47.299v-3.377c0-26.124-21.173-47.296-47.296-47.296z\" fill=\"#6277BA\"/>\n <path d=\"M899.463 905.006H129.194c-26.12 0-47.296 21.17-47.296 47.29v3.381c0 26.127 21.177 47.298 47.296 47.298h770.269c26.123 0 47.296-21.171 47.296-47.298v-3.381c0-26.12-21.173-47.29-47.296-47.29z\" fill=\"#C45FA0\"/>\n <path d=\"M717.962 543.153H542.047c-26.121 0-47.298 21.175-47.298 47.297v3.724c0 26.123 21.177 47.293 47.298 47.293h175.915c26.121 0 47.297-21.17 47.297-47.293v-3.724c0-26.122-21.176-47.297-47.297-47.297z\" fill=\"#E5594F\"/>\n <path d=\"M689.268 198.849H513.355c-26.122 0-47.298 21.175-47.298 47.297v3.722c0 26.12 21.176 47.297 47.298 47.297h175.912c26.122 0 47.298-21.177 47.298-47.297v-3.722c0-26.122-21.175-47.297-47.297-47.297z\" fill=\"#F0D043\"/>\n <path d=\"M757.789 353.081H261.17c-26.121 0-47.297 21.172-47.297 47.296v3.377c0 26.121 21.177 47.299 47.297 47.299h496.619c26.121 0 47.296-21.178 47.296-47.299v-3.377c0-26.125-21.175-47.296-47.296-47.296z\" fill=\"#E5594F\"/>\n <path d=\"M762.638 726.225h-496.62c-26.12 0-47.294 21.18-47.294 47.301v3.377c0 26.12 21.174 47.3 47.294 47.3h496.62c26.122 0 47.296-21.18 47.296-47.3v-3.377c0-26.122-21.174-47.301-47.296-47.301z\" fill=\"#6277BA\"/>\n <path d=\"M355.734 543.328H281.41c-26.122 0-47.297 21.17-47.297 47.293v3.378c0 26.118 21.175 47.297 47.297 47.297h74.324c26.123 0 47.296-21.179 47.296-47.297v-3.378c0-26.123-21.174-47.293-47.296-47.293z\" fill=\"#F39A2B\"/>\n <path d=\"M334.85 248.006m-48.986 0a48.986 48.986 0 1 0 97.972 0 48.986 48.986 0 1 0-97.972 0Z\" fill=\"#F39A2B\"/>\n</svg>\n";
783
+ //#endregion
756
784
  //#region src/client/icon.tsx
757
- /** The sheet's strokes, in paint order, as `[path, fill]` pairs. */
758
- const SHEET_PATHS = [
759
- ["M899.984 19.873h-3.452c-26.123 0-47.296 21.172-47.296 47.296v888.508c0 26.127 21.173 47.298 47.296 47.298h3.452c26.119 0 47.297-21.171 47.297-47.298V67.169c0-26.124-21.177-47.296-47.297-47.296z", "#4A5699"],
760
- ["M132.643 19.873h-3.449c-26.12 0-47.296 21.172-47.296 47.296v888.508c0 26.127 21.177 47.298 47.296 47.298h3.449c26.123 0 47.299-21.171 47.299-47.298V67.169c0-26.124-21.176-47.296-47.299-47.296z", "#C45FA0"],
761
- ["M899.463 19.873H129.194c-26.12 0-47.296 21.172-47.296 47.296v3.377c0 26.12 21.177 47.299 47.296 47.299h770.269c26.123 0 47.296-21.179 47.296-47.299v-3.377c0-26.124-21.173-47.296-47.296-47.296z", "#6277BA"],
762
- ["M899.463 905.006H129.194c-26.12 0-47.296 21.17-47.296 47.29v3.381c0 26.127 21.177 47.298 47.296 47.298h770.269c26.123 0 47.296-21.171 47.296-47.298v-3.381c0-26.12-21.173-47.29-47.296-47.29z", "#C45FA0"],
763
- ["M717.962 543.153H542.047c-26.121 0-47.298 21.175-47.298 47.297v3.724c0 26.123 21.177 47.293 47.298 47.293h175.915c26.121 0 47.297-21.17 47.297-47.293v-3.724c0-26.122-21.176-47.297-47.297-47.297z", "#E5594F"],
764
- ["M689.268 198.849H513.355c-26.122 0-47.298 21.175-47.298 47.297v3.722c0 26.12 21.176 47.297 47.298 47.297h175.912c26.122 0 47.298-21.177 47.298-47.297v-3.722c0-26.122-21.175-47.297-47.297-47.297z", "#F0D043"],
765
- ["M757.789 353.081H261.17c-26.121 0-47.297 21.172-47.297 47.296v3.377c0 26.121 21.177 47.299 47.297 47.299h496.619c26.121 0 47.296-21.178 47.296-47.299v-3.377c0-26.125-21.175-47.296-47.296-47.296z", "#E5594F"],
766
- ["M762.638 726.225h-496.62c-26.12 0-47.294 21.18-47.294 47.301v3.377c0 26.12 21.174 47.3 47.294 47.3h496.62c26.122 0 47.296-21.18 47.296-47.3v-3.377c0-26.122-21.174-47.301-47.296-47.301z", "#6277BA"],
767
- ["M355.734 543.328H281.41c-26.122 0-47.297 21.17-47.297 47.293v3.378c0 26.118 21.175 47.297 47.297 47.297h74.324c26.123 0 47.296-21.179 47.296-47.297v-3.378c0-26.123-21.174-47.293-47.296-47.293z", "#F39A2B"],
768
- ["M334.85 248.006m-48.986 0a48.986 48.986 0 1 0 97.972 0 48.986 48.986 0 1 0-97.972 0Z", "#F39A2B"]
769
- ];
785
+ /**
786
+ * The Context emblem: the colourful document sheet, bundled rather than read
787
+ * off the harness primitives so the plugin's identity is self-contained.
788
+ *
789
+ * The single graphic source is the package-root `icon.svg` — the same file
790
+ * package.json `icon` hands to the Host's package-meta reader, which serves
791
+ * it to the Plugins page's package cards. The client bundle inlines the
792
+ * file's markup at build time (the `?raw` channel in tsdown.config.ts) and
793
+ * re-renders it at every requested size. Fixed fills, never `currentColor` —
794
+ * the sheet is deliberately polychrome on both light and dark chrome.
795
+ */
796
+ /** Everything between the file's `<svg>` tags: the sheet's strokes in paint order, whitespace-folded. */
797
+ const SHEET_MARKUP = icon_default.slice(icon_default.indexOf(">") + 1, icon_default.lastIndexOf("</svg>")).trim().replace(/>\s+</g, "><");
770
798
  /** The colourful document sheet at the requested square edge. */
771
799
  function ContextIcon({ size = 20, className }) {
772
800
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("svg", {
@@ -776,10 +804,7 @@ window.__ModuleLoader__.load({
776
804
  className,
777
805
  "aria-hidden": "true",
778
806
  xmlns: "http://www.w3.org/2000/svg",
779
- children: SHEET_PATHS.map(([d, fill]) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
780
- d,
781
- fill
782
- }, d))
807
+ dangerouslySetInnerHTML: { __html: SHEET_MARKUP }
783
808
  });
784
809
  }
785
810
  /**
@@ -1439,11 +1464,12 @@ window.__ModuleLoader__.load({
1439
1464
  return typeof value === "number" && Number.isFinite(value) && value >= 0 ? value : 0;
1440
1465
  }
1441
1466
  /**
1442
- * The OPTIONAL timing scalars (the generation split): a real non-negative
1443
- * number passes, anything else — including absence — reads as undefined so the
1444
- * field stays absent on the narrowed value (see `timingOf`).
1467
+ * The OPTIONAL timing scalars (the generation split's spans and block counts):
1468
+ * a real non-negative number passes, anything else — including absence — reads
1469
+ * as undefined so the field stays absent on the narrowed value (see
1470
+ * `timingOf`).
1445
1471
  */
1446
- function optMsNumOf(value) {
1472
+ function optNumOf(value) {
1447
1473
  return typeof value === "number" && Number.isFinite(value) && value >= 0 ? value : void 0;
1448
1474
  }
1449
1475
  /**
@@ -1467,7 +1493,10 @@ window.__ModuleLoader__.load({
1467
1493
  for (const k of [
1468
1494
  "reasoningMs",
1469
1495
  "textMs",
1470
- "toolArgMs"
1496
+ "toolArgMs",
1497
+ "reasoningBlocks",
1498
+ "textBlocks",
1499
+ "toolArgBlocks"
1471
1500
  ]) {
1472
1501
  const v = t[k];
1473
1502
  if (v !== void 0 && (typeof v !== "number" || !Number.isFinite(v) || v < 0)) return false;
@@ -1515,12 +1544,18 @@ window.__ModuleLoader__.load({
1515
1544
  toolCalls: msNumOf(data.toolCalls),
1516
1545
  tools
1517
1546
  };
1518
- const reasoning = optMsNumOf(data.reasoningMs);
1547
+ const reasoning = optNumOf(data.reasoningMs);
1519
1548
  if (reasoning !== void 0) totals.reasoningMs = reasoning;
1520
- const textMs = optMsNumOf(data.textMs);
1549
+ const reasoningBlocks = optNumOf(data.reasoningBlocks);
1550
+ if (reasoningBlocks !== void 0) totals.reasoningBlocks = reasoningBlocks;
1551
+ const textMs = optNumOf(data.textMs);
1521
1552
  if (textMs !== void 0) totals.textMs = textMs;
1522
- const toolArgMs = optMsNumOf(data.toolArgMs);
1553
+ const textBlocks = optNumOf(data.textBlocks);
1554
+ if (textBlocks !== void 0) totals.textBlocks = textBlocks;
1555
+ const toolArgMs = optNumOf(data.toolArgMs);
1523
1556
  if (toolArgMs !== void 0) totals.toolArgMs = toolArgMs;
1557
+ const toolArgBlocks = optNumOf(data.toolArgBlocks);
1558
+ if (toolArgBlocks !== void 0) totals.toolArgBlocks = toolArgBlocks;
1524
1559
  return totals;
1525
1560
  }
1526
1561
  /**
@@ -2445,6 +2480,16 @@ window.__ModuleLoader__.load({
2445
2480
  return kindOfTool(tool);
2446
2481
  }
2447
2482
  /**
2483
+ * Whether a settled call's arguments can yield an op at all: the file tools
2484
+ * plus `str_replace_editor`, whose purpose follows its `command`. The public
2485
+ * face of the gate {@link opsOfCall} short-circuits on — callers that decide
2486
+ * whether to parse or to KEEP a call's raw arguments ask here instead of
2487
+ * restating the tool list.
2488
+ */
2489
+ function opBearingTool(tool) {
2490
+ return tool === "str_replace_editor" || kindOfTool(tool) !== null;
2491
+ }
2492
+ /**
2448
2493
  * The operation's target path: the path-ish argument of read/write tools;
2449
2494
  * for searches the narrowing `path`, else the pattern itself (a pathless
2450
2495
  * grep/glob's target IS the pattern — the workspace-wide search text).
@@ -2583,7 +2628,7 @@ window.__ModuleLoader__.load({
2583
2628
  * resolve no target) rows nothing.
2584
2629
  */
2585
2630
  function opsOfCall(input) {
2586
- const args = parseCallArgs(input.argsRaw);
2631
+ const args = opBearingTool(input.tool) ? parseCallArgs(input.argsRaw) : null;
2587
2632
  const kind = kindOfCall(input.tool, args);
2588
2633
  if (kind === null) return [];
2589
2634
  const stamp = {
@@ -3411,6 +3456,39 @@ window.__ModuleLoader__.load({
3411
3456
  return t("browser.loading");
3412
3457
  }
3413
3458
  //#endregion
3459
+ //#region src/client/primitives.ts
3460
+ /** The platform module's export surface, read by name (the two vocabularies meet here). */
3461
+ const ns = _deepseek_ai_dsh_client_ui_primitives;
3462
+ /**
3463
+ * Read one export through a bounded catch: partial module mocks (and any
3464
+ * hostile namespace) may throw on the property access itself, which must
3465
+ * degrade to "not this spelling", never crash the importer.
3466
+ */
3467
+ function read(source, key) {
3468
+ try {
3469
+ return source[key];
3470
+ } catch {
3471
+ return;
3472
+ }
3473
+ }
3474
+ /**
3475
+ * Resolve one icon across the range's two spellings: the modern
3476
+ * `…OutlineRegular`/`…OutlineMedium` name first, then the legacy
3477
+ * `…Outline16`/`…Outline14` one; neither present (or not a component)
3478
+ * renders nothing. `source` overrides the platform namespace for tests.
3479
+ */
3480
+ function resolveIcon(modern, legacy, source = ns) {
3481
+ const found = read(source, modern) ?? read(source, legacy);
3482
+ return typeof found === "function" ? found : () => null;
3483
+ }
3484
+ const IconBranch = resolveIcon("IconBranchOutlineRegular", "IconBranchOutline16");
3485
+ const IconPlus = resolveIcon("IconPlusOutlineRegular", "IconPlusOutline16");
3486
+ const IconCheck = resolveIcon("IconCheckOutlineRegular", "IconCheckOutline16");
3487
+ const IconCopy = resolveIcon("IconCopyOutlineRegular", "IconCopyOutline16");
3488
+ const IconClose = resolveIcon("IconCloseOutlineRegular", "IconCloseOutline16");
3489
+ const IconSettings = resolveIcon("IconSettingsOutlineMedium", "IconSettingsOutline14");
3490
+ const IconChevronDown = resolveIcon("IconChevronDownOutlineMedium", "IconChevronDownOutline14");
3491
+ //#endregion
3414
3492
  //#region src/client/format.ts
3415
3493
  /** `fmt`: the k/M/B suffix style shared by bars/details/stats; `fmtTime`: local HH:MM:SS. */
3416
3494
  function fmt(n) {
@@ -3686,7 +3764,7 @@ window.__ModuleLoader__.load({
3686
3764
  className: "lc-att-lightbox-close",
3687
3765
  "aria-label": labels.close,
3688
3766
  onClick: onClose,
3689
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconCloseOutline16, { size: 16 })
3767
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconClose, { size: 16 })
3690
3768
  })
3691
3769
  ]
3692
3770
  }), document.body);
@@ -3852,7 +3930,7 @@ window.__ModuleLoader__.load({
3852
3930
  title: label,
3853
3931
  "aria-label": label,
3854
3932
  onClick: onCopy,
3855
- children: copied ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconCheckOutline16, { size: 13 }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconCopyOutline16, { size: 13 })
3933
+ children: copied ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconCheck, { size: 13 }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconCopy, { size: 13 })
3856
3934
  });
3857
3935
  }
3858
3936
  function RichText(props) {
@@ -7241,18 +7319,28 @@ window.__ModuleLoader__.load({
7241
7319
  //#endregion
7242
7320
  //#region src/client/settingsJump.ts
7243
7321
  /**
7244
- * Best-effort jump to this plugin's settings page (Settings → Plugins →
7245
- * Plugin configuration). The harness settings panel keeps its open state
7246
- * inside the shell component — no plugin-facing open face exists — so the
7247
- * jump drives the real chrome: click the sidebar's settings trigger, then
7248
- * the Plugins section nav row, both found by their shipped attributes and
7249
- * labels. EVERY step is individually guarded: a shell that doesn't match
7250
- * (older host, different layout, missing sidebar) degrades the whole jump
7251
- * to a silent no-op — this path must never throw into the caller's render.
7252
- *
7322
+ * Best-effort jump to this plugin's preferences. Two generations of chrome:
7323
+ * - dsh 0.1.7+ (Config-form generation): the preferences live on the
7324
+ * bundle's page of the Plugins main panel — the jump clicks the panel's
7325
+ * sidebar entry (its shipped aria-label), then follows the page with a
7326
+ * bounded poll for the bundle card's open control, found by the shipped,
7327
+ * locale-independent `data-plugin-package` list identity: the card lists
7328
+ * once the page's package load settles, and inside it the one button
7329
+ * that is not the enable switch is the title that opens the page. A
7330
+ * poll that never finds the card (still loading, plugin absent, or the
7331
+ * page already open on the detail view — the list unmounts there)
7332
+ * degrades to the card list, and every step stays silent.
7333
+ * - older lines: Settings → Plugins → Plugin configuration — the settings
7334
+ * panel keeps its open state inside the shell component (no plugin-facing
7335
+ * open face exists), so the jump drives the real chrome: click the
7336
+ * sidebar's settings trigger, then the Plugins section nav row, both
7337
+ * found by their shipped attributes and labels.
7253
7338
  * The last leg — the card itself — is ours: the module also carries a
7254
- * short-lived in-bundle expand request that the plugin's settings card
7255
- * consumes on mount, so the jump lands on the configuration already open.
7339
+ * short-lived in-bundle expand request that the settings-section card
7340
+ * consumes on mount, so that jump lands on the configuration already open.
7341
+ * EVERY step is individually guarded: a shell that doesn't match (different
7342
+ * layout, missing sidebar) degrades the whole jump to a silent no-op — this
7343
+ * path must never throw into the caller's render.
7256
7344
  */
7257
7345
  let requestedAt = 0;
7258
7346
  /** Raise a short-lived request for the plugin's settings card to mount expanded. */
@@ -7265,24 +7353,66 @@ window.__ModuleLoader__.load({
7265
7353
  requestedAt = 0;
7266
7354
  return fresh;
7267
7355
  }
7268
- /** The Plugins settings section's shipped nav labels (en/zh locales). */
7269
- const SECTION_LABELS = /* @__PURE__ */ new Set(["Plugins", "插件"]);
7356
+ /**
7357
+ * The shipped Plugins label in both locales — the main-panel entry's
7358
+ * aria-label and the settings section's nav row text are the same word.
7359
+ */
7360
+ const PLUGIN_LABELS = /* @__PURE__ */ new Set(["Plugins", "插件"]);
7361
+ /** The bundle card's shipped list identity (locale-independent). */
7362
+ const PACKAGE_KEY = "dsh-context";
7363
+ /** How long the card poll follows the panel: 40 ticks at the 100ms tick, enough for the page's package load. */
7364
+ const POLL_TICKS = 40;
7270
7365
  /** Buttons the jump may operate on, in document order. */
7271
7366
  function buttonsOf(doc) {
7272
7367
  return [...doc.querySelectorAll("button")];
7273
7368
  }
7369
+ /** The Plugins main-panel entry, matched by its shipped aria-label. */
7370
+ function findPanelEntry(doc) {
7371
+ return buttonsOf(doc).find((b) => PLUGIN_LABELS.has(b.getAttribute("aria-label") ?? ""));
7372
+ }
7373
+ /**
7374
+ * The bundle card's open control: the one button inside the card that is not
7375
+ * the enable switch (the primitives switch carries `role="switch"`); the
7376
+ * title button's own aria-label is localized.
7377
+ */
7378
+ function findCardOpenControl(doc) {
7379
+ const card = doc.querySelector(`[data-plugin-package="${PACKAGE_KEY}"]`);
7380
+ if (card === null) return void 0;
7381
+ const open = [...card.querySelectorAll("button")].filter((b) => b.getAttribute("role") !== "switch");
7382
+ return open.length === 1 ? open[0] : void 0;
7383
+ }
7274
7384
  /** The sidebar's settings triggers: dialog semantics plus an expanded flag. */
7275
7385
  function findTriggers(doc) {
7276
7386
  return buttonsOf(doc).filter((b) => b.getAttribute("aria-haspopup") === "dialog" && b.hasAttribute("aria-expanded"));
7277
7387
  }
7278
- /** The Plugins section's nav row, matched by its shipped label. */
7388
+ /** The Plugins section's nav row, matched by its shipped label text. */
7279
7389
  function findSectionRow(doc) {
7280
- return buttonsOf(doc).find((b) => SECTION_LABELS.has(b.textContent.trim()));
7390
+ return buttonsOf(doc).find((b) => PLUGIN_LABELS.has(b.textContent.trim()));
7281
7391
  }
7282
7392
  function openPluginSettings(doc = document, schedule = (run, ms) => {
7283
7393
  window.setTimeout(run, ms);
7284
7394
  }) {
7285
7395
  try {
7396
+ const panel = findPanelEntry(doc);
7397
+ if (panel !== void 0) {
7398
+ panel.click();
7399
+ const poll = (left) => {
7400
+ try {
7401
+ const open = findCardOpenControl(doc);
7402
+ if (open !== void 0) {
7403
+ open.click();
7404
+ return;
7405
+ }
7406
+ } catch {}
7407
+ if (left > 0) schedule(() => {
7408
+ poll(left - 1);
7409
+ }, 100);
7410
+ };
7411
+ schedule(() => {
7412
+ poll(POLL_TICKS);
7413
+ }, 200);
7414
+ return;
7415
+ }
7286
7416
  const triggers = findTriggers(doc);
7287
7417
  if (triggers.find((b) => b.getAttribute("aria-expanded") === "true") === void 0 && triggers.length === 0) return;
7288
7418
  triggers.find((b) => b.getAttribute("aria-expanded") !== "true")?.click();
@@ -7297,13 +7427,17 @@ window.__ModuleLoader__.load({
7297
7427
  //#endregion
7298
7428
  //#region src/client/components/settingsCard.tsx
7299
7429
  /**
7300
- * The dsh-context card in Settings → Plugins → Plugin configuration, registered on the framework's `settings.plugin.item` slot keyed on
7301
- * the
7302
- * Host-served `dsh-context` settings namespace — the section itself supplies nothing; it renders nothing while the namespace is
7303
- * unavailable
7304
- * (a deployment without the Host half, or a remote browser, shows no trace).
7305
- * Mounts expanded when the Plugin Info card's "Open in Settings" jump left a
7306
- * fresh expand request (settingsJump.ts), scrolling itself into view.
7430
+ * The dsh-context preference cards — two seats over the same six rows. The
7431
+ * settings-section card (`settings.plugin.item`, the older harness lines)
7432
+ * renders a collapsible list item in Settings → Plugins → Plugin
7433
+ * configuration; the Plugins-page card (`plugins.bundle.config`, the
7434
+ * Config-form generation) renders the rows flat inside the section chrome the
7435
+ * Plugins page draws for the bundle. Both are keyed on the Host-served
7436
+ * `dsh-context` namespace and render nothing while it is unavailable (a
7437
+ * deployment without the Host half, or a remote browser, shows no trace).
7438
+ * The settings-section card mounts expanded when the Plugin Info card's
7439
+ * "Open preferences" jump left a fresh expand request (settingsJump.ts),
7440
+ * scrolling itself into view.
7307
7441
  */
7308
7442
  function PrefRow(props) {
7309
7443
  const [open, setOpen] = (0, react.useState)(false);
@@ -7335,11 +7469,129 @@ window.__ModuleLoader__.load({
7335
7469
  onClick: () => {
7336
7470
  setOpen((v) => !v);
7337
7471
  },
7338
- children: [active, /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronDownOutline14, {})]
7472
+ children: [active, /* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconChevronDown, {})]
7339
7473
  })
7340
7474
  })]
7341
7475
  });
7342
7476
  }
7477
+ /** The six preference rows shared by both seats. */
7478
+ function PreferenceRows(props) {
7479
+ const { t, state, set } = props;
7480
+ const disabled = state.status !== "ready" || !state.writable;
7481
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
7482
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(PrefRow, {
7483
+ label: t("settings.placement"),
7484
+ value: state.placement,
7485
+ disabled,
7486
+ options: [
7487
+ {
7488
+ id: "all",
7489
+ label: t("placement.all")
7490
+ },
7491
+ {
7492
+ id: "tab",
7493
+ label: t("placement.tab")
7494
+ },
7495
+ {
7496
+ id: "sidebar",
7497
+ label: t("placement.sidebar")
7498
+ }
7499
+ ],
7500
+ onPick: (id) => {
7501
+ set?.("defaultPlacement", id);
7502
+ }
7503
+ }),
7504
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(PrefRow, {
7505
+ label: t("settings.insightsEntry"),
7506
+ value: state.insightsEntry,
7507
+ disabled,
7508
+ options: [{
7509
+ id: "show",
7510
+ label: t("insightsEntry.show")
7511
+ }, {
7512
+ id: "hide",
7513
+ label: t("insightsEntry.hide")
7514
+ }],
7515
+ onPick: (id) => {
7516
+ set?.("insightsEntry", id);
7517
+ }
7518
+ }),
7519
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(PrefRow, {
7520
+ label: t("settings.gran"),
7521
+ value: state.granularity,
7522
+ disabled,
7523
+ options: [{
7524
+ id: "step",
7525
+ label: t("gran.step")
7526
+ }, {
7527
+ id: "turn",
7528
+ label: t("gran.turn")
7529
+ }],
7530
+ onPick: (id) => {
7531
+ set?.("defaultGranularity", id);
7532
+ }
7533
+ }),
7534
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(PrefRow, {
7535
+ label: t("settings.mode"),
7536
+ value: state.mode,
7537
+ disabled,
7538
+ options: [{
7539
+ id: "total",
7540
+ label: t("gran.total")
7541
+ }, {
7542
+ id: "delta",
7543
+ label: t("gran.delta")
7544
+ }],
7545
+ onPick: (id) => {
7546
+ set?.("defaultTrendMode", id);
7547
+ }
7548
+ }),
7549
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(PrefRow, {
7550
+ label: t("settings.toolSort"),
7551
+ value: state.toolSort,
7552
+ disabled,
7553
+ options: [
7554
+ {
7555
+ id: "size",
7556
+ label: t("tool.sort.size")
7557
+ },
7558
+ {
7559
+ id: "count",
7560
+ label: t("tool.sort.count")
7561
+ },
7562
+ {
7563
+ id: "name",
7564
+ label: t("tool.sort.name")
7565
+ }
7566
+ ],
7567
+ onPick: (id) => {
7568
+ set?.("defaultToolSort", id);
7569
+ }
7570
+ }),
7571
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(PrefRow, {
7572
+ label: t("settings.fileSort"),
7573
+ value: state.fileSort,
7574
+ disabled,
7575
+ options: [
7576
+ {
7577
+ id: "count",
7578
+ label: t("files.sort.count")
7579
+ },
7580
+ {
7581
+ id: "latest",
7582
+ label: t("files.sort.latest")
7583
+ },
7584
+ {
7585
+ id: "path",
7586
+ label: t("files.sort.path")
7587
+ }
7588
+ ],
7589
+ onPick: (id) => {
7590
+ set?.("defaultFileSort", id);
7591
+ }
7592
+ })
7593
+ ] });
7594
+ }
7343
7595
  function makeSettingsCard(kit) {
7344
7596
  const { t } = kit;
7345
7597
  return function SettingsCard(props) {
@@ -7354,7 +7606,6 @@ window.__ModuleLoader__.load({
7354
7606
  }, []);
7355
7607
  const state = typeof props.useContextSettings === "function" ? props.useContextSettings((s) => s) : void 0;
7356
7608
  if (state === void 0 || state.status === "unavailable") return null;
7357
- const disabled = state.status !== "ready" || !state.writable;
7358
7609
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("li", {
7359
7610
  ref: itemRef,
7360
7611
  className: "lc-settings-card" + (open ? " lc-settings-open" : ""),
@@ -7375,131 +7626,47 @@ window.__ModuleLoader__.load({
7375
7626
  className: "lc-settings-desc",
7376
7627
  children: t("settings.desc")
7377
7628
  })]
7378
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronDownOutline14, { className: "lc-settings-chevron" })]
7629
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconChevronDown, { className: "lc-settings-chevron" })]
7379
7630
  }), open ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
7380
7631
  className: "lc-settings-body",
7381
- children: [
7382
- !state.writable && state.status === "ready" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
7383
- className: "lc-settings-note",
7384
- role: "status",
7385
- children: t("settings.readOnly")
7386
- }) : null,
7387
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(PrefRow, {
7388
- label: t("settings.placement"),
7389
- value: state.placement,
7390
- disabled,
7391
- options: [
7392
- {
7393
- id: "all",
7394
- label: t("placement.all")
7395
- },
7396
- {
7397
- id: "tab",
7398
- label: t("placement.tab")
7399
- },
7400
- {
7401
- id: "sidebar",
7402
- label: t("placement.sidebar")
7403
- }
7404
- ],
7405
- onPick: (id) => {
7406
- props.set?.("defaultPlacement", id);
7407
- }
7408
- }),
7409
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(PrefRow, {
7410
- label: t("settings.insightsEntry"),
7411
- value: state.insightsEntry,
7412
- disabled,
7413
- options: [{
7414
- id: "show",
7415
- label: t("insightsEntry.show")
7416
- }, {
7417
- id: "hide",
7418
- label: t("insightsEntry.hide")
7419
- }],
7420
- onPick: (id) => {
7421
- props.set?.("insightsEntry", id);
7422
- }
7423
- }),
7424
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(PrefRow, {
7425
- label: t("settings.gran"),
7426
- value: state.granularity,
7427
- disabled,
7428
- options: [{
7429
- id: "step",
7430
- label: t("gran.step")
7431
- }, {
7432
- id: "turn",
7433
- label: t("gran.turn")
7434
- }],
7435
- onPick: (id) => {
7436
- props.set?.("defaultGranularity", id);
7437
- }
7438
- }),
7439
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(PrefRow, {
7440
- label: t("settings.mode"),
7441
- value: state.mode,
7442
- disabled,
7443
- options: [{
7444
- id: "total",
7445
- label: t("gran.total")
7446
- }, {
7447
- id: "delta",
7448
- label: t("gran.delta")
7449
- }],
7450
- onPick: (id) => {
7451
- props.set?.("defaultTrendMode", id);
7452
- }
7453
- }),
7454
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(PrefRow, {
7455
- label: t("settings.toolSort"),
7456
- value: state.toolSort,
7457
- disabled,
7458
- options: [
7459
- {
7460
- id: "size",
7461
- label: t("tool.sort.size")
7462
- },
7463
- {
7464
- id: "count",
7465
- label: t("tool.sort.count")
7466
- },
7467
- {
7468
- id: "name",
7469
- label: t("tool.sort.name")
7470
- }
7471
- ],
7472
- onPick: (id) => {
7473
- props.set?.("defaultToolSort", id);
7474
- }
7475
- }),
7476
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(PrefRow, {
7477
- label: t("settings.fileSort"),
7478
- value: state.fileSort,
7479
- disabled,
7480
- options: [
7481
- {
7482
- id: "count",
7483
- label: t("files.sort.count")
7484
- },
7485
- {
7486
- id: "latest",
7487
- label: t("files.sort.latest")
7488
- },
7489
- {
7490
- id: "path",
7491
- label: t("files.sort.path")
7492
- }
7493
- ],
7494
- onPick: (id) => {
7495
- props.set?.("defaultFileSort", id);
7496
- }
7497
- })
7498
- ]
7632
+ children: [!state.writable && state.status === "ready" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
7633
+ className: "lc-settings-note",
7634
+ role: "status",
7635
+ children: t("settings.readOnly")
7636
+ }) : null, /* @__PURE__ */ (0, react_jsx_runtime.jsx)(PreferenceRows, {
7637
+ t,
7638
+ state,
7639
+ set: props.set
7640
+ })]
7499
7641
  }) : null]
7500
7642
  });
7501
7643
  };
7502
7644
  }
7645
+ /**
7646
+ * The Plugins-page card (the Config-form generation's `plugins.bundle.config`
7647
+ * seat, `view: 'page'`): the page owns the bundle's page and section chrome,
7648
+ * so the rows render flat. Same unserved/absent degradation as the
7649
+ * settings-section card.
7650
+ */
7651
+ function makePluginConfigCard(kit) {
7652
+ const { t } = kit;
7653
+ return function PluginConfigCard(props) {
7654
+ const state = typeof props.useContextSettings === "function" ? props.useContextSettings((s) => s) : void 0;
7655
+ if (state === void 0 || state.status === "unavailable") return null;
7656
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
7657
+ className: "lc-settings-prefs",
7658
+ children: [!state.writable && state.status === "ready" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
7659
+ className: "lc-settings-note",
7660
+ role: "status",
7661
+ children: t("settings.readOnly")
7662
+ }) : null, /* @__PURE__ */ (0, react_jsx_runtime.jsx)(PreferenceRows, {
7663
+ t,
7664
+ state,
7665
+ set: props.set
7666
+ })]
7667
+ });
7668
+ };
7669
+ }
7503
7670
  //#endregion
7504
7671
  //#region src/client/settings.ts
7505
7672
  function prefsOf(value) {
@@ -9292,7 +9459,7 @@ window.__ModuleLoader__.load({
9292
9459
  children: sorted.map((ev) => {
9293
9460
  const label = eventLabel(ev);
9294
9461
  const at = eventAt(ev);
9295
- const glyph = ev.kind === "inject" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconPlusOutline16, {}) : ev.kind === "model" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconBranchOutline16, {}) : EVENT_ICONS[ev.kind] || "•";
9462
+ const glyph = ev.kind === "inject" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconPlus, {}) : ev.kind === "model" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconBranch, {}) : EVENT_ICONS[ev.kind] || "•";
9296
9463
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
9297
9464
  className: "lc-event @max-[380px]/lc-card:flex-wrap",
9298
9465
  children: [
@@ -9737,7 +9904,7 @@ window.__ModuleLoader__.load({
9737
9904
  return function PluginInfo() {
9738
9905
  const [latest, setLatest] = (0, react.useState)(null);
9739
9906
  (0, react.useEffect)(() => {
9740
- if ("0.54.3".includes("-dev")) return;
9907
+ if ("0.55.0".includes("-dev")) return;
9741
9908
  let on = true;
9742
9909
  fetchLatestVersion().then((v) => {
9743
9910
  if (on && v) setLatest(v);
@@ -9746,8 +9913,8 @@ window.__ModuleLoader__.load({
9746
9913
  on = false;
9747
9914
  };
9748
9915
  }, []);
9749
- const update = latest !== null && isNewerVersion(latest, "0.54.3") ? latest : null;
9750
- const nameText = "dsh-context (v0.54.3)";
9916
+ const update = latest !== null && isNewerVersion(latest, "0.55.0") ? latest : null;
9917
+ const nameText = "dsh-context (v0.55.0)";
9751
9918
  const nameValue = [nameText];
9752
9919
  if (update) nameValue.push(/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
9753
9920
  className: "lc-pi-update",
@@ -9780,7 +9947,7 @@ window.__ModuleLoader__.load({
9780
9947
  },
9781
9948
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
9782
9949
  className: "lc-pi-label",
9783
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconSettingsOutline14, {
9950
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconSettings, {
9784
9951
  size: 12,
9785
9952
  className: "lc-pi-labelicon"
9786
9953
  }), t("plugin.settings")]
@@ -10463,10 +10630,10 @@ window.__ModuleLoader__.load({
10463
10630
  * lands in the residue. When the host folded the generation split (see
10464
10631
  * TimingTotals), the generation slice expands into what was being decoded:
10465
10632
  * thinking, answer text, and tool-call arguments. The slice rows lead with
10466
- * the true duration and qualify it with the call count on the secondary line —
10467
- * except the decode slices, which count BLOCKS (zero to many per call, so a
10468
- * call count would be a false tally) and therefore carry no qualifier. A slice
10469
- * that never happened (zero time) is omitted entirely — the ring already skips
10633
+ * the true duration and qualify it with a tally on the secondary line — the
10634
+ * model calls (TTFT), the tool runs, and the decode slices' own BLOCK counts
10635
+ * (zero to many per call, so a call count would be a false tally there). A
10636
+ * slice that never happened (zero time) is omitted entirely — the ring already skips
10470
10637
  * its arc, and a zero row would otherwise borrow the session's call count and
10471
10638
  * read as "no time, many calls". The donut and the rows sit side by side so
10472
10639
  * the head row stays half-height. Parallel tool calls each count, so the tools
@@ -10508,31 +10675,36 @@ window.__ModuleLoader__.load({
10508
10675
  ms: timing.ttftMs,
10509
10676
  times: callTimes
10510
10677
  }];
10678
+ const blockTimes = (blocks) => blocks !== void 0 && blocks > 0 ? t("timing.blockTimes", { n: fmt(blocks) }) : void 0;
10511
10679
  const buckets = [
10512
10680
  [
10513
10681
  "reasoning",
10514
10682
  COLOR.reasoning,
10515
10683
  t("timing.reasoning"),
10516
- timing.reasoningMs ?? 0
10684
+ timing.reasoningMs ?? 0,
10685
+ blockTimes(timing.reasoningBlocks)
10517
10686
  ],
10518
10687
  [
10519
10688
  "text",
10520
10689
  COLOR.text,
10521
10690
  t("timing.text"),
10522
- timing.textMs ?? 0
10691
+ timing.textMs ?? 0,
10692
+ blockTimes(timing.textBlocks)
10523
10693
  ],
10524
10694
  [
10525
10695
  "toolarg",
10526
10696
  COLOR.toolarg,
10527
10697
  t("timing.toolArgs"),
10528
- timing.toolArgMs ?? 0
10698
+ timing.toolArgMs ?? 0,
10699
+ blockTimes(timing.toolArgBlocks)
10529
10700
  ]
10530
10701
  ];
10531
- if (buckets.some(([, , , ms]) => ms > 0)) for (const [key, color, label, ms] of buckets) modelSlices.push({
10702
+ if (buckets.some(([, , , ms]) => ms > 0)) for (const [key, color, label, ms, times] of buckets) modelSlices.push({
10532
10703
  key,
10533
10704
  color,
10534
10705
  label,
10535
- ms
10706
+ ms,
10707
+ ...times !== void 0 ? { times } : {}
10536
10708
  });
10537
10709
  else modelSlices.push({
10538
10710
  key: "gen",
@@ -11565,7 +11737,7 @@ window.__ModuleLoader__.load({
11565
11737
  }
11566
11738
  //#endregion
11567
11739
  //#region \0dsh-global-css:/home/runner/work/dsh-context/dsh-context/src/client/styles/settings.css.mjs
11568
- const css$11 = ".lc-settings-card{background:var(--dsw-alias-bg-layer-3);border:1px solid var(--dsw-alias-border-l2);transition:border-color var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out), background-color var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out);border-radius:12px}.lc-settings-card:hover{border-color:var(--dsw-alias-label-dimmed)}.lc-settings-open,.lc-settings-open:hover{background:var(--dsw-alias-bg-layer-2);border-color:var(--dsw-alias-label-dimmed)}.lc-settings-head{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}.lc-settings-headtext{flex-direction:column;flex:1;gap:4px;min-width:0;display:flex}.lc-settings-name{color:var(--dsw-alias-label-primary);font-size:15px;font-weight:600;line-height:1.4}.lc-settings-desc{color:var(--dsw-alias-label-tertiary);font-size:13px;line-height:1.5}.lc-settings-chevron{color:var(--dsw-alias-label-tertiary);transition:transform var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out);flex:none}.lc-settings-open .lc-settings-chevron{transform:rotate(180deg)}.lc-settings-body{border-top:1px solid var(--dsw-alias-border-l2);margin:0 16px;padding:4px 0 12px}.lc-settings-row{align-items:center;gap:8px;padding:8px 0;display:flex}.lc-settings-label{min-width:0;color:var(--dsw-alias-label-primary);flex:1;font-size:14px}.lc-settings-select{background:var(--dsw-alias-bg-module-platform);height:36px;font:inherit;color:var(--dsw-alias-label-primary);cursor:pointer;transition:background-color var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out);border:none;border-radius:18px;align-items:center;gap:12px;padding:0 14px;font-size:14px;line-height:22px;display:inline-flex}.lc-settings-note{color:var(--dsw-alias-label-tertiary);margin:12px 0 4px;font-size:12px;line-height:1.5}";
11740
+ const css$11 = ".lc-settings-card{background:var(--dsw-alias-bg-layer-3);border:1px solid var(--dsw-alias-border-l2);transition:border-color var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out), background-color var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out);border-radius:12px}.lc-settings-card:hover{border-color:var(--dsw-alias-label-dimmed)}.lc-settings-open,.lc-settings-open:hover{background:var(--dsw-alias-bg-layer-2);border-color:var(--dsw-alias-label-dimmed)}.lc-settings-head{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}.lc-settings-headtext{flex-direction:column;flex:1;gap:4px;min-width:0;display:flex}.lc-settings-name{color:var(--dsw-alias-label-primary);font-size:15px;font-weight:600;line-height:1.4}.lc-settings-desc{color:var(--dsw-alias-label-tertiary);font-size:13px;line-height:1.5}.lc-settings-chevron{color:var(--dsw-alias-label-tertiary);transition:transform var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out);flex:none}.lc-settings-open .lc-settings-chevron{transform:rotate(180deg)}.lc-settings-body{border-top:1px solid var(--dsw-alias-border-l2);margin:0 16px;padding:4px 0 12px}.lc-settings-prefs{padding:4px 0}.lc-settings-row{align-items:center;gap:8px;padding:8px 0;display:flex}.lc-settings-label{min-width:0;color:var(--dsw-alias-label-primary);flex:1;font-size:14px}.lc-settings-select{background:var(--dsw-alias-bg-module-platform);height:36px;font:inherit;color:var(--dsw-alias-label-primary);cursor:pointer;transition:background-color var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out);border:none;border-radius:18px;align-items:center;gap:12px;padding:0 14px;font-size:14px;line-height:22px;display:inline-flex}.lc-settings-note{color:var(--dsw-alias-label-tertiary);margin:12px 0 4px;font-size:12px;line-height:1.5}";
11569
11741
  const tagId$11 = "dsh-context/settings.css";
11570
11742
  if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$11) + "]") === null) {
11571
11743
  const tag = document.createElement("style");
@@ -11783,6 +11955,14 @@ window.__ModuleLoader__.load({
11783
11955
  locale: NS
11784
11956
  }, (props) => (0, react.createElement)(OverviewPanel, props));
11785
11957
  });
11958
+ /** The injected face both preference cards ride: the settings store as the
11959
+ * framework's hooks-compartment `useContextSettings` seat, plus the set verb. */
11960
+ const cardFace = () => ({
11961
+ hooks: { contextSettings: settings.store },
11962
+ set: (field, value) => {
11963
+ settings.set(field, value);
11964
+ }
11965
+ });
11786
11966
  ctx.inject(["settingsScope"], (raw) => {
11787
11967
  const c = raw;
11788
11968
  const binder = c.settingsScope;
@@ -11794,15 +11974,27 @@ window.__ModuleLoader__.load({
11794
11974
  name: "settings.plugin.item",
11795
11975
  key: NS,
11796
11976
  locale: NS,
11797
- inject: () => ({
11798
- hooks: { contextSettings: settings.store },
11799
- set: (field, value) => {
11800
- settings.set(field, value);
11801
- }
11802
- })
11977
+ inject: cardFace
11803
11978
  }, (props) => (0, react.createElement)(SettingsCard, props));
11804
11979
  });
11805
11980
  });
11981
+ ctx.inject(["configForms"], (raw) => {
11982
+ const c = raw;
11983
+ const forms = c.configForms;
11984
+ if (forms === void 0 || typeof forms.get !== "function" || typeof forms.whileServed !== "function") return;
11985
+ c.effect(() => settings.attach(forms.get(NS)), "dsh-context: config forms");
11986
+ const PluginConfigCard = makePluginConfigCard(kit);
11987
+ c.effect(() => forms.whileServed([NS], () => {
11988
+ return c.slots.inject("plugins.bundle.config", () => {
11989
+ return c.slots.register({
11990
+ name: "plugins.bundle.config",
11991
+ key: NS,
11992
+ locale: NS,
11993
+ inject: cardFace
11994
+ }, (props) => (0, react.createElement)(PluginConfigCard, props));
11995
+ });
11996
+ }), "dsh-context: plugins-page card");
11997
+ });
11806
11998
  }
11807
11999
  module.exports = {
11808
12000
  name: "dsh-context",