dsh-workbuddy-xdpool 1.6.1 → 1.7.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
@@ -5,7 +5,6 @@ window.__ModuleLoader__.load({
5
5
  var exports = module.exports;
6
6
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
7
7
  let react = require("react");
8
- let _deepseek_ai_dsh_client_ui_primitives = require("@deepseek-ai/dsh-client-ui-primitives");
9
8
  let react_jsx_runtime = require("react/jsx-runtime");
10
9
  //#region src/status-paths.ts
11
10
  /**
@@ -64,28 +63,38 @@ window.__ModuleLoader__.load({
64
63
  * naturally next to the other configuration rows instead of looking like a
65
64
  * bright Google-Material block on top of DSH's dark surface.
66
65
  *
67
- * The collapsible shell mirrors dingminhua/dsh-connect-trae (which itself
68
- * borrows from the LaoDing plugin family) so the row header behaves exactly
69
- * like the built-in cards next to it; the inner workbuddy-specific classes
70
- * are renamed to `dsm-workbuddy-xdpool-*` to stay namespaced.
66
+ * The page renders at full height with no collapse affordance, and its content is
67
+ * split into cards: a header row with the primary actions, a status card (region
68
+ * switch + distribution), then one card per feature area. The inner
69
+ * workbuddy-specific classes are namespaced `dsm-workbuddy-xdpool-*`.
71
70
  *
72
71
  * @module dsh-workbuddy-xdpool/client/styles
73
72
  */
74
73
  const POOL_CARD_CSS = `
75
- /* Shell: same collapse affordance as every other plugin config row. */
76
- .dsm-plugin-card{border:1px solid var(--dsw-alias-border-l2,#36373b);background:var(--dsw-alias-bg-module-platform,#202126);border-radius:12px;list-style:none;transition:border-color .16s,background .16s}
77
- .dsm-plugin-card:hover{border-color:var(--dsw-alias-label-dimmed,#777)}
78
- .dsm-plugin-card-open{background:var(--dsw-alias-bg-layer-2,#25262b);border-color:var(--dsw-alias-label-dimmed,#777)}
79
- .dsm-plugin-card-header{appearance:none;width:100%;font:inherit;color:inherit;text-align:left;cursor:pointer;background:transparent;border:0;border-radius:12px;align-items:center;gap:12px;padding:14px 16px;display:flex}
80
- .dsm-plugin-card-header:focus-visible{outline:2px solid var(--dsw-alias-brand-primary,#5686fe);outline-offset:-2px}
81
- .dsm-plugin-card-head{flex-direction:column;flex:1;gap:4px;min-width:0;display:flex}
82
- .dsm-plugin-card-title{color:var(--dsw-alias-label-primary,#e6e6e6);font-size:15px;font-weight:600;line-height:1.4}
83
- .dsm-plugin-card-description{color:var(--dsw-alias-label-tertiary,#999);font-size:13px;line-height:1.5}
84
- .dsm-plugin-card-chevron{color:var(--dsw-alias-label-tertiary,#999);flex:none;display:inline-flex;transition:transform .16s}
85
- .dsm-plugin-card-chevron-open{transform:rotate(180deg)}
86
- .dsm-plugin-card-body{border-top:1px solid var(--dsw-alias-border-l2,#36373b);margin:0 16px;padding:0 0 8px}
87
- .dsm-plugin-card-icon{width:32px;height:32px;flex:none;border-radius:7px}
88
-
74
+ /*
75
+ * Page shell. The settings shell renders this inside its own scrollable content
76
+ * column, so the page supplies only the rhythm and the cards — no outer border
77
+ * and no extra margin (the column pads its own edges).
78
+ */
79
+ .dsm-workbuddy-xdpool-page{max-width:860px;flex-direction:column;gap:16px;display:flex}
80
+ /*
81
+ * Header row: identity on the left, primary actions on the right. Wraps so a
82
+ * narrow panel drops the buttons below the title instead of squeezing it.
83
+ */
84
+ .dsm-workbuddy-xdpool-page-head{align-items:center;gap:12px;flex-wrap:wrap;display:flex}
85
+ .dsm-workbuddy-xdpool-page-icon{width:32px;height:32px;flex:none;border-radius:7px}
86
+ .dsm-workbuddy-xdpool-page-copy{flex-direction:column;gap:4px;min-width:0;flex:1 1 220px;display:flex}
87
+ .dsm-workbuddy-xdpool-page-title{color:var(--dsw-alias-label-primary,#e6e6e6);margin:0;font-size:16px;font-weight:600;line-height:1.4}
88
+ .dsm-workbuddy-xdpool-page-desc{color:var(--dsw-alias-label-tertiary,#999);margin:0;font-size:13px;line-height:1.5}
89
+ .dsm-workbuddy-xdpool-page-actions{align-items:center;gap:8px;flex-wrap:wrap;flex:none;display:flex}
90
+ /*
91
+ * One feature area per card. The surface separates the sections the way the
92
+ * built-in settings pages do; without it every panel ran together into a single
93
+ * column of text with no visible boundary.
94
+ */
95
+ .dsm-workbuddy-xdpool-card{border:1px solid var(--dsw-alias-border-l2,#36373b);background:var(--dsw-alias-bg-layer-2,#232529);border-radius:14px;flex-direction:column;gap:12px;padding:14px 16px;display:flex}
96
+ /* Status card: the region switch sits above the state line, then the switch. */
97
+ .dsm-workbuddy-xdpool-status{gap:14px}
89
98
  /* Reusable button primitives shared with the rest of the card body. */
90
99
  .dsm-btn{appearance:none;font:inherit;cursor:pointer;border:1px solid transparent;border-radius:8px;padding:5px 14px;font-size:13px;line-height:1.5}
91
100
  .dsm-btn:focus-visible{outline:2px solid var(--dsw-alias-brand-primary,#5686fe);outline-offset:1px}
@@ -95,12 +104,10 @@ window.__ModuleLoader__.load({
95
104
  .dsm-btn-primary{background:var(--dsw-alias-label-primary,#e6e6e6);color:var(--dsw-alias-bg-layer-3,#202126)}
96
105
  .dsm-btn-primary:hover:not(:disabled){opacity:.9}
97
106
 
98
- /* Body layout: status row + accounts list + models list. */
99
- .dsm-workbuddy-xdpool-usage{display:flex;flex-direction:column;gap:14px;margin:0;padding:14px 0 4px}
100
107
  /* Region tabs: two independent suppliers, one shown at a time. */
101
108
  .dsm-workbuddy-xdpool-tabs{display:flex;gap:6px;padding:4px;border:1px solid var(--dsw-alias-border-l2,#3a3d45);border-radius:10px;background:var(--dsw-alias-bg-layer-3,#2a2c33)}
102
109
  /* Empty-region guide: steps for signing in on the other gateway. */
103
- .dsm-workbuddy-xdpool-empty{display:flex;flex-direction:column;gap:10px;padding:14px;border:1px solid var(--dsw-alias-border-l2,#3a3d45);border-radius:12px;background:var(--dsw-alias-bg-layer-2,#24262c)}
110
+ .dsm-workbuddy-xdpool-empty{gap:10px}
104
111
  .dsm-workbuddy-xdpool-empty-title{margin:0;color:var(--dsw-alias-label-primary,#e6e6e6);font-size:14px;font-weight:600;line-height:20px}
105
112
  .dsm-workbuddy-xdpool-empty-steps{display:flex;flex-direction:column;gap:6px;padding:12px;border-radius:10px;background:var(--dsw-alias-bg-layer-3,#2a2c33)}
106
113
  .dsm-workbuddy-xdpool-empty-steps-title{margin:0;color:var(--dsw-alias-label-secondary,#c6c9d0);font-size:12px;font-weight:600;line-height:18px}
@@ -114,14 +121,14 @@ window.__ModuleLoader__.load({
114
121
  .dsm-workbuddy-xdpool-tab-dot{display:inline-block;width:7px;height:7px;border-radius:50%;margin-right:6px;vertical-align:baseline;background:var(--dsw-alias-state-success-primary,#22a06b)}
115
122
  .dsm-workbuddy-xdpool-tab-dot[data-state="error"]{background:var(--dsw-alias-state-error-primary,#ef4444)}
116
123
  .dsm-workbuddy-xdpool-tab-dot[data-state="idle"]{background:var(--dsw-alias-label-dimmed,#9aa0a6)}
117
- .dsm-workbuddy-xdpool-usage-head{display:flex;align-items:flex-start;justify-content:space-between;gap:12px;flex-wrap:wrap}
124
+ .dsm-workbuddy-xdpool-usage-head{display:flex;flex-direction:column;gap:12px;min-width:0}
118
125
  .dsm-workbuddy-xdpool-usage-copy{display:flex;flex-direction:column;gap:3px;min-width:0}
119
- .dsm-workbuddy-xdpool-usage-status{display:flex;align-items:center;gap:10px;font-size:15px;font-weight:500;color:var(--dsw-alias-label-primary,#e6e6e6)}
126
+ .dsm-workbuddy-xdpool-usage-status{display:flex;align-items:center;gap:10px;font-size:15px;font-weight:600;color:var(--dsw-alias-label-primary,#e6e6e6)}
120
127
  .dsm-workbuddy-xdpool-usage-dot{width:9px;height:9px;border-radius:50%;flex:0 0 auto}
121
- .dsm-workbuddy-xdpool-usage-hint{padding-left:19px;color:var(--dsw-alias-label-tertiary,#9aa0a8);font-size:12px;line-height:18px}
128
+ .dsm-workbuddy-xdpool-usage-hint{margin:0;color:var(--dsw-alias-label-tertiary,#9aa0a8);font-size:12px;line-height:18px}
122
129
  /* Distribution switch: priority (drain one) vs round-robin (spread). */
123
- .dsm-workbuddy-xdpool-dist{display:flex;flex-direction:column;gap:6px;padding-left:19px;margin-top:8px}
124
- .dsm-workbuddy-xdpool-dist-title{color:var(--dsw-alias-label-tertiary,#9aa0a8);font-size:12px;line-height:18px}
130
+ .dsm-workbuddy-xdpool-dist{display:flex;flex-direction:column;gap:7px}
131
+ .dsm-workbuddy-xdpool-dist-title{color:var(--dsw-alias-label-tertiary,#9aa0a8);font-size:11px;line-height:16px;letter-spacing:.03em;text-transform:uppercase;font-weight:600}
125
132
  .dsm-workbuddy-xdpool-dist-option{appearance:none;font:inherit;cursor:pointer;text-align:left;display:flex;flex-direction:column;justify-content:center;gap:2px;min-height:44px;border:1px solid var(--dsw-alias-border-l2,#3a3d45);border-radius:8px;padding:6px 10px;background:transparent;color:var(--dsw-alias-label-tertiary,#9aa0a8);transition:color .16s,border-color .16s,background .16s}
126
133
  .dsm-workbuddy-xdpool-dist-option:hover:not(:disabled):not(.dsm-workbuddy-xdpool-dist-option-active){color:var(--dsw-alias-label-primary,#e6e6e6);border-color:var(--dsw-alias-label-dimmed,#777)}
127
134
  .dsm-workbuddy-xdpool-dist-option:focus-visible{outline:2px solid var(--dsw-alias-brand-primary,#5686fe);outline-offset:1px}
@@ -133,7 +140,7 @@ window.__ModuleLoader__.load({
133
140
  .dsm-workbuddy-xdpool-usage-actions{display:flex;align-items:center;gap:8px;flex-wrap:wrap}
134
141
 
135
142
  /* Account list (each account = a labeled subpanel, same as dingminhua). */
136
- .dsm-workbuddy-xdpool-accounts{display:flex;flex-direction:column;gap:14px;border-top:1px solid var(--dsw-alias-border-l2,#36373b);padding-top:14px}
143
+ .dsm-workbuddy-xdpool-accounts{gap:14px}
137
144
  .dsm-workbuddy-xdpool-accounts-head{display:flex;align-items:center;justify-content:space-between;gap:12px}
138
145
  /* "In use now": answers which account is serving without scanning every row. */
139
146
  /* A hairline + tint reads as status; a filled bar would read as a call to action. */
@@ -196,7 +203,7 @@ window.__ModuleLoader__.load({
196
203
  .dsm-workbuddy-xdpool-total-value{color:var(--dsw-alias-state-success-primary,#22a06b);font-size:28px;line-height:32px;font-weight:800;letter-spacing:-.02em;white-space:nowrap;font-variant-numeric:tabular-nums}
197
204
 
198
205
  /* Model directory list. */
199
- .dsm-workbuddy-xdpool-models{display:flex;flex-direction:column;gap:10px;border-top:1px solid var(--dsw-alias-border-l2,#36373b);padding-top:14px}
206
+ .dsm-workbuddy-xdpool-models{gap:10px}
200
207
  .dsm-workbuddy-xdpool-models-head{display:flex;align-items:center;justify-content:space-between;gap:12px}
201
208
  .dsm-workbuddy-xdpool-models-title{margin:0;color:var(--dsw-alias-label-primary,#e6e6e6);font-size:14px;font-weight:600;line-height:20px}
202
209
  .dsm-workbuddy-xdpool-models-summary{margin:2px 0 0;color:var(--dsw-alias-label-tertiary,#999);font-size:12px;line-height:18px}
@@ -225,6 +232,12 @@ window.__ModuleLoader__.load({
225
232
  .dsm-workbuddy-xdpool-models-actions{display:flex;align-items:center;gap:8px;flex:none}
226
233
 
227
234
 
235
+ /* Automation: run-now button and the per-job progress line. */
236
+ .dsm-workbuddy-xdpool-auto-run{flex:none}
237
+ .dsm-workbuddy-xdpool-auto-job-detail{grid-column:1/-1;color:var(--dsw-alias-label-tertiary,#9aa0a8);font-size:11px;line-height:16px}
238
+ /* Credit packages panel heading inside the right-hand column. */
239
+ .dsm-workbuddy-xdpool-panel-packages{display:flex;flex-direction:column;gap:7px;min-width:0}
240
+
228
241
  /* Check-in docked under the total, inside the right-hand panel. */
229
242
  .dsm-workbuddy-xdpool-checkin{display:flex;flex-direction:column;align-items:center;gap:7px;width:100%;margin-top:10px;padding-top:11px;border-top:1px solid var(--dsw-alias-border-l2,#36373b)}
230
243
  .dsm-workbuddy-xdpool-checkin-meta{display:flex;flex-direction:column;align-items:center;gap:2px;width:100%}
@@ -305,8 +318,9 @@ window.__ModuleLoader__.load({
305
318
  * token expiry / cooldown info / credit packages, then the model directory
306
319
  * with per-model free/limited/night/image badges and context size.
307
320
  *
308
- * The outer shell reuses the host's `dsm-plugin-card*` classes so the
309
- * collapse affordance is identical to every other plugin configuration row.
321
+ * Rendered as a full settings page (see `client/index.tsx`): the shell gives it
322
+ * a left-nav row and a scrollable content column, so everything is visible at
323
+ * once — a fold inside the page only hid the pool behind a click.
310
324
  *
311
325
  * @module dsh-workbuddy-xdpool/client/PoolCard
312
326
  */
@@ -470,7 +484,6 @@ window.__ModuleLoader__.load({
470
484
  const settingsWritable = settingsScope?.getSnapshot().writable === true;
471
485
  /** Which region tab is showing. A CN-only install never leaves this. */
472
486
  const [activeRegion, setActiveRegion] = (0, react.useState)("cn");
473
- const [open, setOpen] = (0, react.useState)(false);
474
487
  /**
475
488
  * Last-known status per region. Kept per region (not a single slot) so
476
489
  * switching tabs shows the other side's last answer immediately instead of
@@ -568,7 +581,6 @@ window.__ModuleLoader__.load({
568
581
  }
569
582
  }, []);
570
583
  (0, react.useEffect)(() => {
571
- if (!open) return;
572
584
  const controller = new AbortController();
573
585
  refresh(activeRegion, controller.signal);
574
586
  const timer = window.setInterval(() => {
@@ -578,11 +590,7 @@ window.__ModuleLoader__.load({
578
590
  window.clearInterval(timer);
579
591
  controller.abort();
580
592
  };
581
- }, [
582
- open,
583
- refresh,
584
- activeRegion
585
- ]);
593
+ }, [refresh, activeRegion]);
586
594
  const rescan = async () => {
587
595
  setBusy(true);
588
596
  setFlash(void 0);
@@ -929,392 +937,398 @@ window.__ModuleLoader__.load({
929
937
  const stateLabel = error !== void 0 ? t?.("row.requestFailed") ?? "Request failed" : accountCount === 0 ? t?.("row.regionEmpty") ?? t?.("row.poolEmpty") ?? "No account yet" : state === "ok" ? t?.("row.ok") ?? "Healthy" : t?.("row.allCooling") ?? "All cooling";
930
938
  const shimRunning = status?.shim.running === true;
931
939
  const shimHint = status === void 0 ? null : shimRunning ? `${t?.("row.shimRunning") ?? "Provider listening"}${status.shim.baseUrl === void 0 ? "" : ` · ${status.shim.baseUrl}`}` : t?.("row.shimStopped") ?? "Provider loopback not running";
932
- return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("li", {
933
- className: `dsm-plugin-card${open ? " dsm-plugin-card-open" : ""}`,
934
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
935
- type: "button",
936
- className: "dsm-plugin-card-header",
937
- "aria-expanded": open,
938
- "aria-label": `${t?.(open ? "row.collapse" : "row.expand") ?? ""}: ${title}`,
939
- onClick: () => {
940
- setOpen(!open);
941
- },
942
- children: [
943
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("img", {
944
- className: "dsm-plugin-card-icon",
945
- src: POOL_PLUGIN_ICON,
946
- alt: ""
947
- }),
948
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
949
- className: "dsm-plugin-card-head",
950
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
951
- className: "dsm-plugin-card-title",
952
- children: title
953
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
954
- className: "dsm-plugin-card-description",
955
- children: description
956
- })]
957
- }),
958
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
959
- "aria-hidden": "true",
960
- className: `dsm-plugin-card-chevron${open ? " dsm-plugin-card-chevron-open" : ""}`,
961
- children: (0, react.createElement)(_deepseek_ai_dsh_client_ui_primitives.IconChevronDownOutline14, { size: 14 })
962
- })
963
- ]
964
- }), open ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
965
- className: "dsm-plugin-card-body",
966
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
967
- className: "dsm-workbuddy-xdpool-usage",
940
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
941
+ className: "dsm-workbuddy-xdpool-page",
942
+ children: [
943
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("header", {
944
+ className: "dsm-workbuddy-xdpool-page-head",
968
945
  children: [
969
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
970
- className: "dsm-workbuddy-xdpool-tabs",
971
- role: "tablist",
972
- children: status?.regions.map((region) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
946
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("img", {
947
+ className: "dsm-workbuddy-xdpool-page-icon",
948
+ src: POOL_PLUGIN_ICON,
949
+ alt: ""
950
+ }),
951
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
952
+ className: "dsm-workbuddy-xdpool-page-copy",
953
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h2", {
954
+ className: "dsm-workbuddy-xdpool-page-title",
955
+ children: title
956
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
957
+ className: "dsm-workbuddy-xdpool-page-desc",
958
+ children: description
959
+ })]
960
+ }),
961
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
962
+ className: "dsm-workbuddy-xdpool-page-actions",
963
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
973
964
  type: "button",
974
- role: "tab",
975
- "aria-selected": region === activeRegion,
976
- className: `dsm-workbuddy-xdpool-tab${region === activeRegion ? " dsm-workbuddy-xdpool-tab-active" : ""}`,
965
+ className: "dsm-btn dsm-btn-outline",
966
+ disabled: busy,
977
967
  onClick: () => {
978
- setActiveRegion(region);
968
+ rescan();
979
969
  },
980
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
981
- className: "dsm-workbuddy-xdpool-tab-dot",
982
- "data-state": state
983
- }), region === "cn" ? t?.("row.tabCn") ?? "CN" : t?.("row.tabGlobal") ?? "Global"]
984
- }, region))
985
- }),
970
+ children: busy ? t?.("row.accountsScanning") ?? "Detecting…" : t?.("row.accountsRescan") ?? "Detect accounts again"
971
+ }), cooling > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
972
+ type: "button",
973
+ className: "dsm-btn dsm-btn-outline",
974
+ disabled: cooldownBusy,
975
+ onClick: () => {
976
+ resetCooldowns();
977
+ },
978
+ children: cooldownBusy ? t?.("row.resetCooldownsBusy") ?? "Clearing…" : t?.("row.resetCooldowns") ?? "Clear all cooldowns"
979
+ }) : null]
980
+ })
981
+ ]
982
+ }),
983
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
984
+ className: "dsm-workbuddy-xdpool-card dsm-workbuddy-xdpool-status",
985
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
986
+ className: "dsm-workbuddy-xdpool-tabs",
987
+ role: "tablist",
988
+ children: status?.regions.map((region) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
989
+ type: "button",
990
+ role: "tab",
991
+ "aria-selected": region === activeRegion,
992
+ className: `dsm-workbuddy-xdpool-tab${region === activeRegion ? " dsm-workbuddy-xdpool-tab-active" : ""}`,
993
+ onClick: () => {
994
+ setActiveRegion(region);
995
+ },
996
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
997
+ className: "dsm-workbuddy-xdpool-tab-dot",
998
+ "data-state": state
999
+ }), region === "cn" ? t?.("row.tabCn") ?? "CN" : t?.("row.tabGlobal") ?? "Global"]
1000
+ }, region))
1001
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1002
+ className: "dsm-workbuddy-xdpool-usage-head",
1003
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1004
+ className: "dsm-workbuddy-xdpool-usage-copy",
1005
+ role: "status",
1006
+ children: [
1007
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1008
+ className: "dsm-workbuddy-xdpool-usage-status",
1009
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1010
+ "aria-hidden": "true",
1011
+ className: "dsm-workbuddy-xdpool-usage-dot",
1012
+ style: { background: dotColor(state) }
1013
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: stateLabel })]
1014
+ }),
1015
+ accountCount > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1016
+ className: "dsm-workbuddy-xdpool-usage-hint",
1017
+ children: t?.("row.accountsSummary", {
1018
+ count: accountCount,
1019
+ cooling
1020
+ }) ?? `${accountCount} account(s) · ${cooling} cooling`
1021
+ }) : null,
1022
+ shimHint === null ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1023
+ className: "dsm-workbuddy-xdpool-usage-hint",
1024
+ children: shimHint
1025
+ }),
1026
+ status === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1027
+ className: "dsm-workbuddy-xdpool-dist",
1028
+ role: "radiogroup",
1029
+ "aria-label": t?.("row.distTitle") ?? "Account usage",
1030
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1031
+ className: "dsm-workbuddy-xdpool-dist-title",
1032
+ children: t?.("row.distTitle") ?? "Account usage"
1033
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1034
+ className: "dsm-workbuddy-xdpool-dist-options",
1035
+ children: [
1036
+ "priority",
1037
+ "balanced",
1038
+ "round-robin"
1039
+ ].map((option) => {
1040
+ const active = (status.distribution ?? "priority") === option;
1041
+ const label = option === "priority" ? t?.("row.distPriority") ?? "Priority" : option === "balanced" ? t?.("row.distBalanced") ?? "Balanced" : t?.("row.distRoundRobin") ?? "Round-robin";
1042
+ const hint = option === "priority" ? t?.("row.distPriorityHint") ?? "" : option === "balanced" ? t?.("row.distBalancedHint") ?? "" : t?.("row.distRoundRobinHint") ?? "";
1043
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
1044
+ type: "button",
1045
+ role: "radio",
1046
+ "aria-checked": active,
1047
+ title: hint,
1048
+ disabled: !modelsEditable,
1049
+ className: "dsm-workbuddy-xdpool-dist-option" + (active ? " dsm-workbuddy-xdpool-dist-option-active" : ""),
1050
+ onClick: () => {
1051
+ setDistribution(option);
1052
+ },
1053
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1054
+ className: "dsm-workbuddy-xdpool-dist-option-name",
1055
+ children: label
1056
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1057
+ className: "dsm-workbuddy-xdpool-dist-option-hint",
1058
+ children: hint
1059
+ })]
1060
+ }, option);
1061
+ })
1062
+ })]
1063
+ })
1064
+ ]
1065
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1066
+ className: "dsm-workbuddy-xdpool-usage-actions",
1067
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
1068
+ type: "button",
1069
+ className: "dsm-btn dsm-btn-outline",
1070
+ disabled: busy,
1071
+ onClick: () => {
1072
+ rescan();
1073
+ },
1074
+ children: busy ? t?.("row.accountsScanning") ?? "Detecting…" : t?.("row.accountsRescan") ?? "Detect accounts again"
1075
+ }), cooling > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
1076
+ type: "button",
1077
+ className: "dsm-btn dsm-btn-outline",
1078
+ disabled: cooldownBusy,
1079
+ onClick: () => {
1080
+ resetCooldowns();
1081
+ },
1082
+ children: cooldownBusy ? t?.("row.resetCooldownsBusy") ?? "Clearing…" : t?.("row.resetCooldowns") ?? "Clear all cooldowns"
1083
+ }) : null]
1084
+ })]
1085
+ })]
1086
+ }),
1087
+ activeRegion !== "cn" || status?.automation === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
1088
+ className: "dsm-workbuddy-xdpool-card dsm-workbuddy-xdpool-auto",
1089
+ "aria-label": t?.("row.autoTitle") ?? "Automation",
1090
+ children: [
986
1091
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
987
- className: "dsm-workbuddy-xdpool-usage-head",
988
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
989
- className: "dsm-workbuddy-xdpool-usage-copy",
990
- role: "status",
991
- children: [
992
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
993
- className: "dsm-workbuddy-xdpool-usage-status",
994
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
995
- "aria-hidden": "true",
996
- className: "dsm-workbuddy-xdpool-usage-dot",
997
- style: { background: dotColor(state) }
998
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: stateLabel })]
999
- }),
1000
- accountCount > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1001
- className: "dsm-workbuddy-xdpool-usage-hint",
1002
- children: t?.("row.accountsSummary", {
1003
- count: accountCount,
1004
- cooling
1005
- }) ?? `${accountCount} account(s) · ${cooling} cooling`
1006
- }) : null,
1007
- shimHint === null ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1008
- className: "dsm-workbuddy-xdpool-usage-hint",
1009
- children: shimHint
1010
- }),
1011
- status === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1012
- className: "dsm-workbuddy-xdpool-dist",
1013
- role: "radiogroup",
1014
- "aria-label": t?.("row.distTitle") ?? "Account usage",
1015
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1016
- className: "dsm-workbuddy-xdpool-dist-title",
1017
- children: t?.("row.distTitle") ?? "Account usage"
1018
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1019
- className: "dsm-workbuddy-xdpool-dist-options",
1020
- children: [
1021
- "priority",
1022
- "balanced",
1023
- "round-robin"
1024
- ].map((option) => {
1025
- const active = (status.distribution ?? "priority") === option;
1026
- const label = option === "priority" ? t?.("row.distPriority") ?? "Priority" : option === "balanced" ? t?.("row.distBalanced") ?? "Balanced" : t?.("row.distRoundRobin") ?? "Round-robin";
1027
- const hint = option === "priority" ? t?.("row.distPriorityHint") ?? "" : option === "balanced" ? t?.("row.distBalancedHint") ?? "" : t?.("row.distRoundRobinHint") ?? "";
1028
- return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
1029
- type: "button",
1030
- role: "radio",
1031
- "aria-checked": active,
1032
- title: hint,
1033
- disabled: !modelsEditable,
1034
- className: "dsm-workbuddy-xdpool-dist-option" + (active ? " dsm-workbuddy-xdpool-dist-option-active" : ""),
1035
- onClick: () => {
1036
- setDistribution(option);
1037
- },
1038
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1039
- className: "dsm-workbuddy-xdpool-dist-option-name",
1040
- children: label
1041
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1042
- className: "dsm-workbuddy-xdpool-dist-option-hint",
1043
- children: hint
1044
- })]
1045
- }, option);
1046
- })
1047
- })]
1048
- })
1049
- ]
1050
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1051
- className: "dsm-workbuddy-xdpool-usage-actions",
1052
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
1092
+ className: "dsm-workbuddy-xdpool-auto-head",
1093
+ children: [
1094
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1095
+ className: "dsm-workbuddy-xdpool-auto-title",
1096
+ children: t?.("row.autoTitle") ?? "Automation"
1097
+ }),
1098
+ !status.automation.enabled ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
1053
1099
  type: "button",
1054
- className: "dsm-btn dsm-btn-outline",
1055
- disabled: busy,
1100
+ className: "dsm-btn dsm-btn-outline dsm-workbuddy-xdpool-auto-run",
1101
+ disabled: automationRun !== void 0,
1056
1102
  onClick: () => {
1057
- rescan();
1103
+ runAutomationJob();
1058
1104
  },
1059
- children: busy ? t?.("row.accountsScanning") ?? "Detecting…" : t?.("row.accountsRescan") ?? "Detect accounts again"
1060
- }), cooling > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
1105
+ children: automationRun !== void 0 ? t?.("row.autoRunning") ?? "Running…" : t?.("row.autoRunAll") ?? "Run all now"
1106
+ }),
1107
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
1061
1108
  type: "button",
1062
- className: "dsm-btn dsm-btn-outline",
1063
- disabled: cooldownBusy,
1109
+ role: "switch",
1110
+ "aria-checked": status.automation.enabled,
1111
+ disabled: !settingsWritable || automationBusy,
1112
+ className: `dsm-workbuddy-xdpool-auto-switch${status.automation.enabled ? " dsm-workbuddy-xdpool-auto-switch-on" : ""}`,
1064
1113
  onClick: () => {
1065
- resetCooldowns();
1114
+ setAutomationEnabled(!status.automation.enabled);
1066
1115
  },
1067
- children: cooldownBusy ? t?.("row.resetCooldownsBusy") ?? "Clearing…" : t?.("row.resetCooldowns") ?? "Clear all cooldowns"
1068
- }) : null]
1116
+ children: automationBusy ? t?.("row.autoBusy") ?? "Saving…" : status.automation.enabled ? t?.("row.autoOn") ?? "On" : t?.("row.autoOff") ?? "Off"
1117
+ })
1118
+ ]
1119
+ }),
1120
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1121
+ className: "dsm-workbuddy-xdpool-auto-hint",
1122
+ children: status.automation.enabled ? t?.("row.autoHintOn") ?? "Reports activity, claims task rewards and checks in once a day." : t?.("row.autoHintOff") ?? "Off: no background requests are made for you."
1123
+ }),
1124
+ automationTotals.credit === 0 && automationTotals.checkinCredit === 0 && automationTotals.bonusCredit === 0 && automationTotals.travelCredit === 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1125
+ className: "dsm-workbuddy-xdpool-auto-total",
1126
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1127
+ className: "dsm-workbuddy-xdpool-auto-total-label",
1128
+ children: t?.("row.autoToday") ?? "Today"
1129
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
1130
+ className: "dsm-workbuddy-xdpool-auto-total-list",
1131
+ children: [
1132
+ automationTotals.credit > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1133
+ className: "dsm-workbuddy-xdpool-auto-total-row",
1134
+ children: t?.("row.autoFromTasks", {
1135
+ credit: automationTotals.credit,
1136
+ energy: automationTotals.energy,
1137
+ count: automationTotals.claimed
1138
+ }) ?? `Tasks +${automationTotals.credit}`
1139
+ }) : null,
1140
+ automationTotals.checkinCredit > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1141
+ className: "dsm-workbuddy-xdpool-auto-total-row",
1142
+ children: t?.("row.autoFromCheckin", { credit: automationTotals.checkinCredit }) ?? `Check-in +${automationTotals.checkinCredit}`
1143
+ }) : null,
1144
+ automationTotals.bonusCredit > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1145
+ className: "dsm-workbuddy-xdpool-auto-total-row",
1146
+ children: t?.("row.autoFromBonus", { credit: automationTotals.bonusCredit }) ?? `Streak +${automationTotals.bonusCredit}`
1147
+ }) : null,
1148
+ automationTotals.travelCredit > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1149
+ className: "dsm-workbuddy-xdpool-auto-total-row",
1150
+ children: t?.("row.autoFromTravel", { credit: automationTotals.travelCredit }) ?? `Buddy +${automationTotals.travelCredit}`
1151
+ }) : null
1152
+ ]
1069
1153
  })]
1070
1154
  }),
1071
- activeRegion !== "cn" || status?.automation === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
1072
- className: "dsm-workbuddy-xdpool-auto",
1073
- "aria-label": t?.("row.autoTitle") ?? "Automation",
1074
- children: [
1075
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1076
- className: "dsm-workbuddy-xdpool-auto-head",
1155
+ status.automation.enabled ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1156
+ className: "dsm-workbuddy-xdpool-auto-jobs",
1157
+ children: AUTOMATION_JOBS.map((kind) => {
1158
+ const job = automationJob(status, kind);
1159
+ const hours = automationHours(status, kind);
1160
+ const label = t?.(`row.autoJob_${kind}`) ?? kind;
1161
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1162
+ className: "dsm-workbuddy-xdpool-auto-job",
1077
1163
  children: [
1078
1164
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1079
- className: "dsm-workbuddy-xdpool-auto-title",
1080
- children: t?.("row.autoTitle") ?? "Automation"
1165
+ className: "dsm-workbuddy-xdpool-auto-job-name",
1166
+ children: label
1167
+ }),
1168
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1169
+ className: "dsm-workbuddy-xdpool-auto-job-when",
1170
+ children: hours.length === 0 ? t?.("row.autoHourNone") ?? "not scheduled" : hours.map((hour) => `${String(hour).padStart(2, "0")}:00`).join(" · ")
1171
+ }),
1172
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1173
+ className: "dsm-workbuddy-xdpool-auto-job-last",
1174
+ children: job?.lastRunDate === void 0 ? t?.("row.autoNever") ?? "not run yet" : `${job.lastRunDate} · ${job.ok}${job.failed > 0 ? `/${job.failed}` : ""}`
1081
1175
  }),
1082
- !status.automation.enabled ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
1083
- type: "button",
1084
- className: "dsm-btn dsm-btn-outline dsm-workbuddy-xdpool-auto-run",
1085
- disabled: automationRun !== void 0,
1086
- onClick: () => {
1087
- runAutomationJob();
1088
- },
1089
- children: automationRun !== void 0 ? t?.("row.autoRunning") ?? "Running…" : t?.("row.autoRunAll") ?? "Run all now"
1176
+ job?.progress === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1177
+ className: "dsm-workbuddy-xdpool-auto-job-note",
1178
+ children: job.progress
1090
1179
  }),
1091
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
1092
- type: "button",
1093
- role: "switch",
1094
- "aria-checked": status.automation.enabled,
1095
- disabled: !settingsWritable || automationBusy,
1096
- className: `dsm-workbuddy-xdpool-auto-switch${status.automation.enabled ? " dsm-workbuddy-xdpool-auto-switch-on" : ""}`,
1097
- onClick: () => {
1098
- setAutomationEnabled(!status.automation.enabled);
1099
- },
1100
- children: automationBusy ? t?.("row.autoBusy") ?? "Saving…" : status.automation.enabled ? t?.("row.autoOn") ?? "On" : t?.("row.autoOff") ?? "Off"
1180
+ job?.detail === void 0 || job.detail.length === 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1181
+ className: "dsm-workbuddy-xdpool-auto-job-detail",
1182
+ children: job.detail.join(" · ")
1101
1183
  })
1102
1184
  ]
1103
- }),
1104
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1105
- className: "dsm-workbuddy-xdpool-auto-hint",
1106
- children: status.automation.enabled ? t?.("row.autoHintOn") ?? "Reports activity, claims task rewards and checks in once a day." : t?.("row.autoHintOff") ?? "Off: no background requests are made for you."
1107
- }),
1108
- automationTotals.credit === 0 && automationTotals.checkinCredit === 0 && automationTotals.bonusCredit === 0 && automationTotals.travelCredit === 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1109
- className: "dsm-workbuddy-xdpool-auto-total",
1110
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1111
- className: "dsm-workbuddy-xdpool-auto-total-label",
1112
- children: t?.("row.autoToday") ?? "Today"
1113
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
1114
- className: "dsm-workbuddy-xdpool-auto-total-list",
1115
- children: [
1116
- automationTotals.credit > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1117
- className: "dsm-workbuddy-xdpool-auto-total-row",
1118
- children: t?.("row.autoFromTasks", {
1119
- credit: automationTotals.credit,
1120
- energy: automationTotals.energy,
1121
- count: automationTotals.claimed
1122
- }) ?? `Tasks +${automationTotals.credit}`
1123
- }) : null,
1124
- automationTotals.checkinCredit > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1125
- className: "dsm-workbuddy-xdpool-auto-total-row",
1126
- children: t?.("row.autoFromCheckin", { credit: automationTotals.checkinCredit }) ?? `Check-in +${automationTotals.checkinCredit}`
1127
- }) : null,
1128
- automationTotals.bonusCredit > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1129
- className: "dsm-workbuddy-xdpool-auto-total-row",
1130
- children: t?.("row.autoFromBonus", { credit: automationTotals.bonusCredit }) ?? `Streak +${automationTotals.bonusCredit}`
1131
- }) : null,
1132
- automationTotals.travelCredit > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1133
- className: "dsm-workbuddy-xdpool-auto-total-row",
1134
- children: t?.("row.autoFromTravel", { credit: automationTotals.travelCredit }) ?? `Buddy +${automationTotals.travelCredit}`
1135
- }) : null
1136
- ]
1137
- })]
1138
- }),
1139
- status.automation.enabled ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1140
- className: "dsm-workbuddy-xdpool-auto-jobs",
1141
- children: AUTOMATION_JOBS.map((kind) => {
1142
- const job = automationJob(status, kind);
1143
- const hours = automationHours(status, kind);
1144
- const label = t?.(`row.autoJob_${kind}`) ?? kind;
1145
- return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1146
- className: "dsm-workbuddy-xdpool-auto-job",
1147
- children: [
1148
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1149
- className: "dsm-workbuddy-xdpool-auto-job-name",
1150
- children: label
1151
- }),
1152
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1153
- className: "dsm-workbuddy-xdpool-auto-job-when",
1154
- children: hours.length === 0 ? t?.("row.autoHourNone") ?? "not scheduled" : hours.map((hour) => `${String(hour).padStart(2, "0")}:00`).join(" · ")
1155
- }),
1156
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1157
- className: "dsm-workbuddy-xdpool-auto-job-last",
1158
- children: job?.lastRunDate === void 0 ? t?.("row.autoNever") ?? "not run yet" : `${job.lastRunDate} · ${job.ok}${job.failed > 0 ? `/${job.failed}` : ""}`
1159
- }),
1160
- job?.progress === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1161
- className: "dsm-workbuddy-xdpool-auto-job-note",
1162
- children: job.progress
1163
- }),
1164
- job?.detail === void 0 || job.detail.length === 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1165
- className: "dsm-workbuddy-xdpool-auto-job-detail",
1166
- children: job.detail.join(" · ")
1167
- })
1168
- ]
1169
- }, kind);
1170
- })
1171
- }) : null
1172
- ]
1173
- }),
1174
- flash === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1175
- className: "dsm-workbuddy-xdpool-note",
1176
- children: flash
1185
+ }, kind);
1186
+ })
1187
+ }) : null
1188
+ ]
1189
+ }),
1190
+ flash === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1191
+ className: "dsm-workbuddy-xdpool-note",
1192
+ children: flash
1193
+ }),
1194
+ error === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1195
+ className: "dsm-workbuddy-xdpool-error",
1196
+ children: t?.("row.error", { message: error }) ?? `Pool status unavailable: ${error}`
1197
+ }),
1198
+ accountCount === 0 && error === void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
1199
+ className: "dsm-workbuddy-xdpool-card dsm-workbuddy-xdpool-empty",
1200
+ children: [
1201
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1202
+ className: "dsm-workbuddy-xdpool-empty-title",
1203
+ children: t?.("row.regionEmptyTitle", { region: regionLabel }) ?? t?.("row.regionEmpty") ?? "No account yet"
1177
1204
  }),
1178
- error === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1179
- className: "dsm-workbuddy-xdpool-error",
1180
- children: t?.("row.error", { message: error }) ?? `Pool status unavailable: ${error}`
1205
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1206
+ className: "dsm-workbuddy-xdpool-empty-steps",
1207
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1208
+ className: "dsm-workbuddy-xdpool-empty-steps-title",
1209
+ children: t?.("row.regionHowToTitle", { region: regionLabel }) ?? `How to sign in to the ${regionLabel} version`
1210
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("ol", {
1211
+ className: "dsm-workbuddy-xdpool-empty-list",
1212
+ children: [
1213
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("li", { children: t?.("row.regionHowTo1") ?? "" }),
1214
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("li", { children: t?.("row.regionHowTo2") ?? "" }),
1215
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("li", { children: t?.("row.regionHowTo3") ?? "" }),
1216
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("li", { children: t?.("row.regionHowTo4") ?? "" })
1217
+ ]
1218
+ })]
1181
1219
  }),
1182
- accountCount === 0 && error === void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
1183
- className: "dsm-workbuddy-xdpool-empty",
1184
- children: [
1185
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1186
- className: "dsm-workbuddy-xdpool-empty-title",
1187
- children: t?.("row.regionEmptyTitle", { region: regionLabel }) ?? t?.("row.regionEmpty") ?? "No account yet"
1188
- }),
1189
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1190
- className: "dsm-workbuddy-xdpool-empty-steps",
1191
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1192
- className: "dsm-workbuddy-xdpool-empty-steps-title",
1193
- children: t?.("row.regionHowToTitle", { region: regionLabel }) ?? `How to sign in to the ${regionLabel} version`
1194
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("ol", {
1195
- className: "dsm-workbuddy-xdpool-empty-list",
1196
- children: [
1197
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("li", { children: t?.("row.regionHowTo1") ?? "" }),
1198
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("li", { children: t?.("row.regionHowTo2") ?? "" }),
1199
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("li", { children: t?.("row.regionHowTo3") ?? "" }),
1200
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("li", { children: t?.("row.regionHowTo4") ?? "" })
1201
- ]
1202
- })]
1203
- }),
1204
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1205
- className: "dsm-workbuddy-xdpool-empty-note",
1206
- children: t?.("row.regionHowToNote") ?? ""
1207
- })
1208
- ]
1209
- }) : null,
1210
- accountCount > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
1211
- className: "dsm-workbuddy-xdpool-accounts",
1212
- "aria-label": t?.("row.accountsTitle") ?? "Accounts",
1213
- children: [
1214
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1215
- className: "dsm-workbuddy-xdpool-accounts-head",
1216
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", {
1217
- className: "dsm-workbuddy-xdpool-accounts-title",
1218
- children: t?.("row.accountsTitle") ?? "Accounts in the pool"
1219
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1220
- className: "dsm-workbuddy-xdpool-accounts-summary",
1221
- children: t?.("row.accountsSummary", {
1222
- count: accountCount,
1223
- cooling
1224
- }) ?? `${accountCount} account(s) · ${cooling} cooling`
1225
- })]
1226
- }),
1227
- (() => {
1228
- const active = status?.activeAccountId === void 0 ? void 0 : status.accounts.find((account) => account.id === status.activeAccountId);
1229
- if (active === void 0) return null;
1230
- return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1231
- className: "dsm-workbuddy-xdpool-current",
1232
- children: [
1233
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: "dsm-workbuddy-xdpool-current-dot" }),
1234
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1235
- className: "dsm-workbuddy-xdpool-current-label",
1236
- children: t?.("row.currentAccount") ?? "In use now"
1237
- }),
1238
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1239
- className: "dsm-workbuddy-xdpool-current-name",
1240
- children: active.label
1241
- }),
1242
- active.disabled === true ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1243
- className: "dsm-workbuddy-xdpool-current-note",
1244
- children: t?.("row.currentAccountDisabled") ?? "disabled — will switch on the next request"
1245
- }) : null
1246
- ]
1247
- });
1248
- })(),
1249
- status?.accounts.map((account) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(AccountBlock, {
1250
- account,
1251
- ...checkinBusyId === void 0 ? {} : { checkinBusyId },
1252
- onClaimCheckin: (accountId) => {
1253
- claimCheckin(accountId);
1254
- },
1255
- onSaveCreditReserve: (accountId, reserve) => saveCreditReserve(accountId, reserve),
1256
- onToggleDisabled: (accountId, disabled) => {
1257
- toggleAccountDisabled(accountId, disabled);
1258
- },
1259
- ...accountBusyId === void 0 ? {} : { accountBusyId },
1260
- t
1261
- }, account.id))
1262
- ]
1263
- }) : null,
1264
- (status?.models.length ?? 0) > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
1265
- className: "dsm-workbuddy-xdpool-models",
1266
- "aria-label": t?.("row.modelsTitle") ?? "Models",
1267
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1268
- className: "dsm-workbuddy-xdpool-models-head",
1269
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1270
- className: "dsm-workbuddy-xdpool-models-heading",
1271
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", {
1272
- className: "dsm-workbuddy-xdpool-models-title",
1273
- children: t?.("row.modelsTitle") ?? "Models"
1274
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1275
- className: "dsm-workbuddy-xdpool-models-summary",
1276
- children: t?.("row.modelsEnabledCount", {
1277
- enabled: enabledCount,
1278
- total: status?.models.length ?? 0
1279
- }) ?? `${enabledCount} / ${status?.models.length ?? 0} enabled`
1280
- })]
1281
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1282
- className: "dsm-workbuddy-xdpool-models-actions",
1283
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
1284
- type: "button",
1285
- className: "dsm-btn dsm-btn-outline",
1286
- disabled: !modelsDirty || savingModels,
1287
- onClick: discardModels,
1288
- children: t?.("row.modelsDiscard") ?? "Discard"
1289
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
1290
- type: "button",
1291
- className: "dsm-btn dsm-btn-primary",
1292
- disabled: !modelsDirty || savingModels || enabledCount === 0,
1293
- onClick: () => {
1294
- saveModels();
1295
- },
1296
- children: savingModels ? t?.("row.modelsSaving") ?? "Saving…" : t?.("row.modelsSave") ?? "Save"
1297
- })]
1298
- })]
1299
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1300
- className: "dsm-workbuddy-xdpool-model-list",
1301
- children: status?.models.map((model) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ModelRow, {
1302
- model,
1303
- t,
1304
- draft: modelDraft[model.id] ?? {
1305
- enabled: model.enabled,
1306
- images: model.supportsImages
1307
- },
1308
- editable: modelsEditable,
1309
- onToggle: toggleModel,
1310
- onToggleImage: toggleModelImage,
1311
- onBudget: setModelBudget
1312
- }, model.id))
1220
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1221
+ className: "dsm-workbuddy-xdpool-empty-note",
1222
+ children: t?.("row.regionHowToNote") ?? ""
1223
+ })
1224
+ ]
1225
+ }) : null,
1226
+ accountCount > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
1227
+ className: "dsm-workbuddy-xdpool-card dsm-workbuddy-xdpool-accounts",
1228
+ "aria-label": t?.("row.accountsTitle") ?? "Accounts",
1229
+ children: [
1230
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1231
+ className: "dsm-workbuddy-xdpool-accounts-head",
1232
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", {
1233
+ className: "dsm-workbuddy-xdpool-accounts-title",
1234
+ children: t?.("row.accountsTitle") ?? "Accounts in the pool"
1235
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1236
+ className: "dsm-workbuddy-xdpool-accounts-summary",
1237
+ children: t?.("row.accountsSummary", {
1238
+ count: accountCount,
1239
+ cooling
1240
+ }) ?? `${accountCount} account(s) · ${cooling} cooling`
1313
1241
  })]
1314
- }) : null
1242
+ }),
1243
+ (() => {
1244
+ const active = status?.activeAccountId === void 0 ? void 0 : status.accounts.find((account) => account.id === status.activeAccountId);
1245
+ if (active === void 0) return null;
1246
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1247
+ className: "dsm-workbuddy-xdpool-current",
1248
+ children: [
1249
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: "dsm-workbuddy-xdpool-current-dot" }),
1250
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1251
+ className: "dsm-workbuddy-xdpool-current-label",
1252
+ children: t?.("row.currentAccount") ?? "In use now"
1253
+ }),
1254
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1255
+ className: "dsm-workbuddy-xdpool-current-name",
1256
+ children: active.label
1257
+ }),
1258
+ active.disabled === true ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1259
+ className: "dsm-workbuddy-xdpool-current-note",
1260
+ children: t?.("row.currentAccountDisabled") ?? "disabled — will switch on the next request"
1261
+ }) : null
1262
+ ]
1263
+ });
1264
+ })(),
1265
+ status?.accounts.map((account) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(AccountBlock, {
1266
+ account,
1267
+ ...checkinBusyId === void 0 ? {} : { checkinBusyId },
1268
+ onClaimCheckin: (accountId) => {
1269
+ claimCheckin(accountId);
1270
+ },
1271
+ onSaveCreditReserve: (accountId, reserve) => saveCreditReserve(accountId, reserve),
1272
+ onToggleDisabled: (accountId, disabled) => {
1273
+ toggleAccountDisabled(accountId, disabled);
1274
+ },
1275
+ ...accountBusyId === void 0 ? {} : { accountBusyId },
1276
+ t
1277
+ }, account.id))
1315
1278
  ]
1316
- })
1317
- }) : null]
1279
+ }) : null,
1280
+ (status?.models.length ?? 0) > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
1281
+ className: "dsm-workbuddy-xdpool-card dsm-workbuddy-xdpool-models",
1282
+ "aria-label": t?.("row.modelsTitle") ?? "Models",
1283
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1284
+ className: "dsm-workbuddy-xdpool-models-head",
1285
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1286
+ className: "dsm-workbuddy-xdpool-models-heading",
1287
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", {
1288
+ className: "dsm-workbuddy-xdpool-models-title",
1289
+ children: t?.("row.modelsTitle") ?? "Models"
1290
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1291
+ className: "dsm-workbuddy-xdpool-models-summary",
1292
+ children: t?.("row.modelsEnabledCount", {
1293
+ enabled: enabledCount,
1294
+ total: status?.models.length ?? 0
1295
+ }) ?? `${enabledCount} / ${status?.models.length ?? 0} enabled`
1296
+ })]
1297
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1298
+ className: "dsm-workbuddy-xdpool-models-actions",
1299
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
1300
+ type: "button",
1301
+ className: "dsm-btn dsm-btn-outline",
1302
+ disabled: !modelsDirty || savingModels,
1303
+ onClick: discardModels,
1304
+ children: t?.("row.modelsDiscard") ?? "Discard"
1305
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
1306
+ type: "button",
1307
+ className: "dsm-btn dsm-btn-primary",
1308
+ disabled: !modelsDirty || savingModels || enabledCount === 0,
1309
+ onClick: () => {
1310
+ saveModels();
1311
+ },
1312
+ children: savingModels ? t?.("row.modelsSaving") ?? "Saving…" : t?.("row.modelsSave") ?? "Save"
1313
+ })]
1314
+ })]
1315
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1316
+ className: "dsm-workbuddy-xdpool-model-list",
1317
+ children: status?.models.map((model) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ModelRow, {
1318
+ model,
1319
+ t,
1320
+ draft: modelDraft[model.id] ?? {
1321
+ enabled: model.enabled,
1322
+ images: model.supportsImages
1323
+ },
1324
+ editable: modelsEditable,
1325
+ onToggle: toggleModel,
1326
+ onToggleImage: toggleModelImage,
1327
+ onBudget: setModelBudget
1328
+ }, model.id))
1329
+ })]
1330
+ }) : null
1331
+ ]
1318
1332
  });
1319
1333
  }
1320
1334
  /** One account block: label + status tag + meta + optional credit panels. */
@@ -1732,6 +1746,50 @@ window.__ModuleLoader__.load({
1732
1746
  });
1733
1747
  }
1734
1748
  //#endregion
1749
+ //#region src/client/nav-icon.ts
1750
+ /**
1751
+ * Nav glyph for the WorkBuddy XD Pool settings page.
1752
+ *
1753
+ * The host's settings shell draws its own 16px `svg` in every nav row and the
1754
+ * `settings.section` registration contract projects only `id` / `order` /
1755
+ * `label` — there is no `icon` field to pass. A third-party page therefore has
1756
+ * to mark its row in the DOM and mask this artwork over the shell's glyph, the
1757
+ * same technique `dshmarket` uses (`installSettingsNavIcon`).
1758
+ *
1759
+ * Kept as a single monochrome path so `mask-image` + `currentColor` can tint it
1760
+ * with whatever the active theme uses for nav text: a filled mask cannot carry
1761
+ * its own palette, and a two-tone icon would render as a flat silhouette.
1762
+ *
1763
+ * @module dsh-workbuddy-xdpool/client/nav-icon
1764
+ */
1765
+ /**
1766
+ * The nav mark: a stack of three rounded "accounts" under a rotation arc.
1767
+ *
1768
+ * Reads as "a pool of accounts being cycled" at 16px, and — unlike a literal
1769
+ * droplet or cloud — stays legible when reduced to a single-color silhouette.
1770
+ * `fill-rule="evenodd"` cuts the interior notches out of the silhouette so the
1771
+ * three layers stay distinguishable at that size.
1772
+ */
1773
+ const POOL_NAV_ICON_SVG = [
1774
+ "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" width=\"16\" height=\"16\">",
1775
+ "<path fill=\"currentColor\" fill-rule=\"evenodd\" d=\"",
1776
+ "M12 2.6a3.1 3.1 0 1 1 0 6.2 3.1 3.1 0 0 1 0-6.2Zm0 1.7a1.4 1.4 0 1 0 0 2.8 1.4 1.4 0 0 0 0-2.8Z",
1777
+ "M6.6 8.9a2.6 2.6 0 1 1 0 5.2 2.6 2.6 0 0 1 0-5.2Zm0 1.6a1 1 0 1 0 0 2 1 1 0 0 0 0-2Z",
1778
+ "M17.4 8.9a2.6 2.6 0 1 1 0 5.2 2.6 2.6 0 0 1 0-5.2Zm0 1.6a1 1 0 1 0 0 2 1 1 0 0 0 0-2Z",
1779
+ "\"/>",
1780
+ "<path fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.7\" stroke-linecap=\"round\"",
1781
+ " d=\"M4.4 17.2a8.6 8.6 0 0 0 15.2 0\" stroke-dasharray=\"2.6 2.2\"/>",
1782
+ "</svg>"
1783
+ ].join("");
1784
+ /**
1785
+ * The same artwork as a `mask-image` URL.
1786
+ *
1787
+ * `encodeURIComponent` keeps the `#`-free markup safe inside a `url("…")` in a
1788
+ * stylesheet, and `currentColor` is resolved by the mask's own element rather
1789
+ * than by the SVG, so the glyph follows the theme.
1790
+ */
1791
+ const POOL_NAV_ICON_MASK_URL = `data:image/svg+xml;utf8,${encodeURIComponent(POOL_NAV_ICON_SVG)}`;
1792
+ //#endregion
1735
1793
  //#region src/client/locales.ts
1736
1794
  /**
1737
1795
  * Plugin-card copy registered under the `settings.workbuddy-xdpool` locale
@@ -1741,6 +1799,7 @@ window.__ModuleLoader__.load({
1741
1799
  * @module dsh-workbuddy-xdpool/client/locales
1742
1800
  */
1743
1801
  const en = {
1802
+ "row.navLabel": "XD Pool",
1744
1803
  "row.title": "WorkBuddy XD Pool (dsh-workbuddy-xdpool)",
1745
1804
  "row.desc": "Route every WorkBuddy sign-in on this machine into DSH as one auto-failing-over model pool.",
1746
1805
  "row.expand": "Expand",
@@ -1872,6 +1931,7 @@ window.__ModuleLoader__.load({
1872
1931
  "row.autoFromTravel": "Buddy travel +{credit} credits"
1873
1932
  };
1874
1933
  const zh = {
1934
+ "row.navLabel": "XD Pool",
1875
1935
  "row.title": "WorkBuddy 池(dsh-workbuddy-xdpool)",
1876
1936
  "row.desc": "把本机所有已登录的 WorkBuddy 账号并入 DSH,作为一个自动容错的模型池使用。",
1877
1937
  "row.expand": "展开",
@@ -2006,13 +2066,26 @@ window.__ModuleLoader__.load({
2006
2066
  //#region src/client/index.tsx
2007
2067
  /** Stable browser-plugin name. */
2008
2068
  const name = "dsh-workbuddy-xdpool-client";
2009
- /** Client services required by the Plugin configuration contribution. */
2010
- const inject = [
2011
- "slots",
2012
- "locale",
2013
- "settingsScope"
2014
- ];
2015
- /** Register card copy and the pool card under Plugin configuration. */
2069
+ /**
2070
+ * Client services required by the settings page.
2071
+ *
2072
+ * Deliberately only the two services present on BOTH host lines. The settings
2073
+ * surface differs by line — 0.1.5 provides `settingsScope`, 0.1.7 replaces it
2074
+ * with `configForms` — and cordis' dependency gate is hard: any inject entry the
2075
+ * running line does not provide keeps `apply` from ever running. Probing the one
2076
+ * that exists through `ctx.get()` (which returns undefined, never throws, for an
2077
+ * absent service) is what lets one build serve both lines.
2078
+ */
2079
+ const inject = ["slots", "locale"];
2080
+ /** Settings namespace the host-side section registers (shared with the entry). */
2081
+ const WORKBUDDY_POOL_SETTINGS_NS = "workbuddy-xdpool";
2082
+ /**
2083
+ * Host plugin entry id this bundle is mounted under in `cordis.patch.yml`.
2084
+ *
2085
+ * `configForms` is addressed by this id on the 0.1.7 line.
2086
+ */
2087
+ const WORKBUDDY_POOL_ENTRY_ID = "llm-workbuddy-xdpool";
2088
+ /** Register card copy and the pool page under Settings. */
2016
2089
  function apply(ctx) {
2017
2090
  try {
2018
2091
  const namespace = "settings.workbuddy-xdpool";
@@ -2021,19 +2094,127 @@ window.__ModuleLoader__.load({
2021
2094
  en
2022
2095
  }), "dsh-workbuddy-xdpool: settings copy");
2023
2096
  const t = ctx.locale.bind(namespace);
2024
- const settingsScope = ctx.settingsScope.bind({ namespace: "workbuddy-xdpool" });
2025
- ctx.slots.inject("settings.plugin.item", () => ctx.slots.register({
2026
- name: "settings.plugin.item",
2027
- key: "workbuddy-xdpool",
2028
- priority: 30,
2029
- inject: () => ({
2097
+ const softGet = (serviceName) => ctx.get(serviceName);
2098
+ const settingsScope = resolveSettingsScope(softGet);
2099
+ ctx.slots.inject("settings.section", () => ctx.slots.register({
2100
+ name: "settings.section",
2101
+ id: "workbuddy-xdpool",
2102
+ order: 440,
2103
+ label: () => t("row.navLabel"),
2104
+ inject: () => settingsScope === void 0 ? { t } : {
2030
2105
  t,
2031
2106
  settingsScope
2032
- })
2107
+ }
2033
2108
  }, PoolCard));
2109
+ installNavIcon(ctx, () => t("row.navLabel"));
2034
2110
  } catch (error) {
2035
- console.error("[dsh-workbuddy-xdpool] client card failed to load (host provider unaffected):", error);
2111
+ console.error("[dsh-workbuddy-xdpool] client page failed to load (host provider unaffected):", error);
2112
+ }
2113
+ }
2114
+ /** Attribute carrying the nav-row marker this module installs. */
2115
+ const NAV_ICON_MARKER = "data-dsh-xdpool-nav-icon";
2116
+ /**
2117
+ * The settings nav rows, as the shell renders them. Scoped to the settings
2118
+ * dialog on purpose: the main sidebar has its own nav, and matching rows there
2119
+ * would stamp this glyph onto an unrelated control.
2120
+ */
2121
+ const NAV_ROW_SELECTOR = "[role=\"dialog\"] nav button";
2122
+ /**
2123
+ * Draw this page's own glyph in its Settings nav row.
2124
+ *
2125
+ * The `settings.section` contract carries no icon: the shell decides the glyph
2126
+ * from the section id and falls back to a gear for anything it does not know.
2127
+ * So the row is matched by its LABEL (the same thunk passed to the
2128
+ * registration, re-read on every pass so a locale switch is followed) and
2129
+ * marked; CSS then hides the shell svg and masks this artwork into the row.
2130
+ *
2131
+ * Re-scanned on DOM mutations because the shell re-renders the nav on locale
2132
+ * and theme changes, which replaces the row elements and drops the marker.
2133
+ *
2134
+ * No-op off the browser (the node-side probe imports this module for types).
2135
+ */
2136
+ function installNavIcon(ctx, resolveLabel) {
2137
+ if (typeof document === "undefined") return;
2138
+ ctx.effect(() => {
2139
+ const tag = document.createElement("style");
2140
+ tag.dataset.plugin = "dsh-workbuddy-xdpool";
2141
+ tag.dataset.pluginCss = "dsh-workbuddy-xdpool/settings-nav-icon";
2142
+ tag.textContent = [
2143
+ `[${NAV_ICON_MARKER}] > svg { display: none; }`,
2144
+ `[${NAV_ICON_MARKER}]::before {`,
2145
+ " content: '';",
2146
+ " flex: none;",
2147
+ " width: 16px;",
2148
+ " height: 16px;",
2149
+ " background-color: currentColor;",
2150
+ ` -webkit-mask-image: url("${POOL_NAV_ICON_MASK_URL}");`,
2151
+ ` mask-image: url("${POOL_NAV_ICON_MASK_URL}");`,
2152
+ " -webkit-mask-repeat: no-repeat;",
2153
+ " mask-repeat: no-repeat;",
2154
+ " -webkit-mask-position: center;",
2155
+ " mask-position: center;",
2156
+ " -webkit-mask-size: 16px 16px;",
2157
+ " mask-size: 16px 16px;",
2158
+ "}"
2159
+ ].join("\n");
2160
+ document.head.appendChild(tag);
2161
+ let disposed = false;
2162
+ let scheduled = false;
2163
+ const sync = () => {
2164
+ scheduled = false;
2165
+ if (disposed) return;
2166
+ const wanted = String(resolveLabel() ?? "").trim();
2167
+ if (wanted === "") return;
2168
+ for (const row of document.querySelectorAll(NAV_ROW_SELECTOR)) if (String(row.textContent ?? "").trim() === wanted) row.setAttribute(NAV_ICON_MARKER, "");
2169
+ else row.removeAttribute(NAV_ICON_MARKER);
2170
+ };
2171
+ const schedule = () => {
2172
+ if (scheduled || disposed) return;
2173
+ scheduled = true;
2174
+ queueMicrotask(sync);
2175
+ };
2176
+ sync();
2177
+ const observer = new MutationObserver(schedule);
2178
+ observer.observe(document.body, {
2179
+ childList: true,
2180
+ subtree: true,
2181
+ characterData: true
2182
+ });
2183
+ return () => {
2184
+ disposed = true;
2185
+ observer.disconnect();
2186
+ for (const row of document.querySelectorAll(`[${NAV_ICON_MARKER}]`)) row.removeAttribute(NAV_ICON_MARKER);
2187
+ tag.remove();
2188
+ };
2189
+ }, "dsh-workbuddy-xdpool: settings nav icon");
2190
+ }
2191
+ /**
2192
+ * Bind the settings form the card reads and writes, on whichever line is
2193
+ * running.
2194
+ *
2195
+ * 0.1.7 exposes `configForms.get(entryId)`, keyed by the HOST plugin entry id —
2196
+ * the id this bundle registers under in `cordis.patch.yml`, not the settings
2197
+ * namespace. 0.1.5 exposes `settingsScope.bind({ namespace })`, keyed by the
2198
+ * namespace the host-side `installSection` registered. Both controllers answer
2199
+ * the same two methods the card uses (`getSnapshot()`, `set(field, value)`), so
2200
+ * the card needs no per-line branch of its own.
2201
+ *
2202
+ * Returns undefined when neither service is present (a locked-down host): the
2203
+ * card then renders read-only, which is the documented degradation.
2204
+ */
2205
+ function resolveSettingsScope(softGet) {
2206
+ const forms = softGet("configForms");
2207
+ if (forms !== void 0) {
2208
+ let entryId = WORKBUDDY_POOL_ENTRY_ID;
2209
+ try {
2210
+ const namespaces = forms.describe().getSnapshot().view?.namespaces ?? [];
2211
+ const served = namespaces.find((entry) => entry.ns === WORKBUDDY_POOL_ENTRY_ID) ?? namespaces.find((entry) => /workbuddy-xdpool/.test(entry.ns));
2212
+ if (served !== void 0) entryId = served.ns;
2213
+ } catch {}
2214
+ return forms.get(entryId);
2036
2215
  }
2216
+ const scope = softGet("settingsScope");
2217
+ if (scope !== void 0) return scope.bind({ namespace: WORKBUDDY_POOL_SETTINGS_NS });
2037
2218
  }
2038
2219
  //#endregion
2039
2220
  exports.apply = apply;