dsh-connect-workbuddy 4.15.1 → 5.0.1

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
@@ -264,6 +264,20 @@ window.__ModuleLoader__.load({
264
264
  function regionCreditsShownOf(value, region) {
265
265
  return regionSlotOf(value, region)["showCreditsInMainUi"] !== false;
266
266
  }
267
+ /**
268
+ * Whether the model may call the image-generation tool in one region.
269
+ *
270
+ * OPT-IN, unlike every other switch in this file: absent reads as `false`. The
271
+ * asymmetry is deliberate. The credit line and the provider switch are things
272
+ * whose absence should behave like a fresh install (on), because a hidden line
273
+ * or a withdrawn provider is recoverable by clicking. This one authorizes the
274
+ * MODEL to spend credits on its own — about 5.71 per image, with no per-call
275
+ * approval available in the host — so an upgrade or an unreadable setting must
276
+ * leave it OFF rather than silently grant it.
277
+ */
278
+ function regionImageToolEnabledOf(value, region) {
279
+ return regionSlotOf(value, region)["imageToolEnabled"] === true;
280
+ }
267
281
  /** The seat a region's readout uses, defaulting to the composer row. */
268
282
  function regionCreditPositionOf(value, region) {
269
283
  return regionSlotOf(value, region)["creditPosition"] === "stats" ? "stats" : "composer";
@@ -295,6 +309,18 @@ window.__ModuleLoader__.load({
295
309
  });
296
310
  }
297
311
  /**
312
+ * Build the next `regions` settings value for the image-tool opt-in.
313
+ *
314
+ * Same merge rule: ONLY the target region's flag changes, so the directory,
315
+ * the model selection and the sibling region survive the write.
316
+ */
317
+ function nextRegionImageToolEnabled(value, region, enabled) {
318
+ return nextRegionSlots(regionsMapOf(value), region, {
319
+ ...regionSlotOf(value, region),
320
+ imageToolEnabled: enabled
321
+ });
322
+ }
323
+ /**
298
324
  * Project one card row into its persisted `lastCatalog` shape: the native
299
325
  * context window becomes the stored `contextWindow`, and the card-only
300
326
  * presentation fields (`nativeContextWindow`, `multimodal`) are removed BY
@@ -673,6 +699,12 @@ window.__ModuleLoader__.load({
673
699
  * @param slot - the region's COMPLETE next slot (caller owns the merge).
674
700
  * @throws {WorkBuddySettingsWriteError} when neither writer persists the value.
675
701
  */
702
+ async function writeRegionImageToolEnabled(scope, region, enabled, slot) {
703
+ await writeField(scope, "regions", region, slot, (readBack) => {
704
+ const stored = readBack;
705
+ return stored !== null && stored !== void 0 && stored["imageToolEnabled"] === enabled;
706
+ });
707
+ }
676
708
  async function writeRegionCreditsShown(scope, region, shown, slot) {
677
709
  await writeField(scope, "regions", region, slot, (readBack) => {
678
710
  const stored = readBack;
@@ -1524,17 +1556,30 @@ window.__ModuleLoader__.load({
1524
1556
  /**
1525
1557
  * Ids the upstream lists in the model roster that are ROUTERS, not models.
1526
1558
  *
1527
- * `auto` (CN) is the one observed today: the gateway resolves it to whichever
1528
- * model it considers the best fit (measured: CN `auto` → `hy4-preview`), so its
1529
- * price, context window and behaviour are decided at request time by something
1530
- * else. Offering it as a row advertises a model whose terms the plugin cannot
1531
- * state, and it duplicates a real row already in the same roster.
1559
+ * Two of them are known, one per region, and they are the SAME concept under
1560
+ * two ids:
1561
+ *
1562
+ * - `auto` (CN gateway) — the gateway resolves it to whichever model it
1563
+ * considers the best fit (measured: CN `auto` → `hy4-preview`), and its own
1564
+ * description says so outright: 「平衡效果与速度。自动为每个任务匹配最优模型,
1565
+ * 积分倍率随之浮动」 ("picks the best model per task; the credit multiplier
1566
+ * floats accordingly").
1567
+ * - `default-model` (international gateway) — identical role (name "Auto",
1568
+ * `isDefault: true`, the region's default pick), also the ONLY entry in that
1569
+ * roster that declares no `credits` at all.
1570
+ *
1571
+ * The evidence for treating both as routers is the shared, self-describing
1572
+ * shape, not the name: measured 2026-10-22 against both live gateways, these
1573
+ * two entries are the ONLY ones whose `credits` field is absent/empty (CN 19
1574
+ * of 20 priced, global 27 of 28), and the ones carrying `isDefault: true`.
1575
+ * A row the plugin cannot price is a row whose cost it cannot state, and the
1576
+ * router duplicates a real row already in the same roster.
1532
1577
  *
1533
1578
  * Kept as an explicit table rather than a heuristic: a router is an upstream
1534
1579
  * concept with no declared flag, and guessing from the name would eventually
1535
1580
  * hide a real model. Add an id here only after the same kind of evidence.
1536
1581
  */
1537
- const ROUTER_MODEL_IDS = /* @__PURE__ */ new Set(["auto"]);
1582
+ const ROUTER_MODEL_IDS = /* @__PURE__ */ new Set(["auto", "default-model"]);
1538
1583
  /** Whether this id is an upstream router rather than a model. */
1539
1584
  function isRouterModelId(modelId) {
1540
1585
  return ROUTER_MODEL_IDS.has(modelId);
@@ -4038,6 +4083,37 @@ window.__ModuleLoader__.load({
4038
4083
  .dsm-workbuddy-tab-switch{display:inline-flex;align-items:center;flex:none;margin-left:auto;padding-left:10px;border-left:1px solid var(--dsw-alias-border-l1,rgba(127,127,127,.24));cursor:pointer}
4039
4084
  .dsm-workbuddy-tab-switch input{margin:0;cursor:pointer;accent-color:var(--dsw-alias-brand-primary,#5686fe)}
4040
4085
  .dsm-workbuddy-tab-switch input:disabled{cursor:default}
4086
+ /* SECTION TABS: the card's own top-level split (accounts / models / diagnostics).
4087
+ *
4088
+ * Deliberately NOT the boxed cells above. Those are the REGION scope — a
4089
+ * two-way either/or, each side carrying its own on/off switch — while these are
4090
+ * panels of ONE document, the shape a reader already knows from the app's own
4091
+ * settings window. Two rows of boxes would read as one four-way choice.
4092
+ *
4093
+ * The hidden attribute on each panel is what does the work: an inactive panel
4094
+ * stays MOUNTED, so switching back does not refetch, a half-filled catalog draft
4095
+ * survives the trip, and the account pool keeps its log. It also takes the
4096
+ * inactive panel out of the accessibility tree, which is exactly what
4097
+ * role="tabpanel" wants — unlike display:none reached by a CSS class, it
4098
+ * cannot be overridden by a later rule. */
4099
+ .dsm-workbuddy-panels{display:flex;align-items:stretch;gap:2px;margin:16px 0 0;border-bottom:1px solid var(--dsw-alias-border-l1,rgba(127,127,127,.24))}
4100
+ .dsm-workbuddy-panel-tab{appearance:none;font:inherit;cursor:pointer;border:0;background:transparent;padding:7px 11px;margin-bottom:-1px;border-bottom:2px solid transparent;color:var(--dsw-alias-label-tertiary,#999);font-size:13px;font-weight:600;line-height:20px;white-space:nowrap;transition:color .15s,border-color .15s}
4101
+ .dsm-workbuddy-panel-tab:hover:not(.dsm-workbuddy-panel-tab-active){color:var(--dsw-alias-label-primary,#e6e6e6)}
4102
+ .dsm-workbuddy-panel-tab:focus-visible{outline:2px solid var(--dsw-alias-brand-primary,#5686fe);outline-offset:1px}
4103
+ .dsm-workbuddy-panel-tab-active{color:var(--dsw-alias-label-primary,#e6e6e6);border-bottom-color:var(--dsw-alias-brand-primary,#5686fe)}
4104
+ .dsm-workbuddy-panel{margin-top:12px}
4105
+ /* DIAGNOSTICS: a two-column fact table rather than prose. Each row is one thing
4106
+ * the card actually READ, so a bug report can quote a line instead of
4107
+ * paraphrasing the UI. No control lives here on purpose — a diagnostics panel
4108
+ * that can change settings is a settings panel with a worse name. */
4109
+ .dsm-workbuddy-diag{display:flex;flex-direction:column;border:1px solid var(--dsw-alias-border-l2,#36373b);border-radius:10px;overflow:hidden;background:var(--dsw-alias-bg-layer-2,#232529)}
4110
+ .dsm-workbuddy-diag-row{display:flex;align-items:baseline;justify-content:space-between;gap:16px;padding:8px 12px}
4111
+ .dsm-workbuddy-diag-row+.dsm-workbuddy-diag-row{border-top:1px solid var(--dsw-alias-border-l1,rgba(127,127,127,.24))}
4112
+ .dsm-workbuddy-diag-label{flex:none;color:var(--dsw-alias-label-tertiary,#999);font-size:12px;line-height:18px}
4113
+ .dsm-workbuddy-diag-value{min-width:0;color:var(--dsw-alias-label-primary,#e6e6e6);font-size:12px;line-height:18px;text-align:right;overflow-wrap:anywhere}
4114
+ .dsm-workbuddy-diag-mono{font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace}
4115
+ .dsm-workbuddy-diag-bad{color:var(--dsw-alias-label-caution,#e8b34b)}
4116
+ .dsm-workbuddy-diag-hint{margin:10px 0 0;color:var(--dsw-alias-label-tertiary,#999);font-size:12px;line-height:18px}
4041
4117
  /* PREFERENCE LINES: a checkbox on a line that commits on the click, packed from
4042
4118
  the LEFT — the shape the reference card uses for this exact block.
4043
4119
  *
@@ -4067,7 +4143,7 @@ window.__ModuleLoader__.load({
4067
4143
  CONTRAST colour, so a ticked box painted itself as a heavy black pill — the
4068
4144
  loudest object in a section made of plain 13px text. */
4069
4145
  .dsm-workbuddy-pref input{flex:none;cursor:pointer;accent-color:var(--dsw-alias-brand-primary,#5686fe)}
4070
- .dsm-workbuddy-pref-check{inline-size:15px;block-size:15px;margin:3px 0 0}
4146
+ .dsm-workbuddy-pref-check,.dsm-workbuddy-image-tool-check{inline-size:15px;block-size:15px;margin:3px 0 0}
4071
4147
  .dsm-workbuddy-pref input:disabled{cursor:default}
4072
4148
  .dsm-workbuddy-pref:has(input:disabled){opacity:.6;cursor:default}
4073
4149
  /* The NAME and what it does are TWO lines, not one wrapped blob: first what the
@@ -4168,6 +4244,21 @@ window.__ModuleLoader__.load({
4168
4244
  an error banner, because the fact it states is normal product behaviour
4169
4245
  rather than a failure. Weight is the only signal it needs. */
4170
4246
  .dsm-workbuddy-free-quota-warn{font-weight:600}
4247
+ /* Every model is laid out in full — NO inner scroll box.
4248
+ *
4249
+ * The previous rule capped this at max-height 520px with overflow-y auto, so a
4250
+ * 20-model roster showed about seven rows and hid the rest behind a scrollbar
4251
+ * the user had to discover and then drag inside a panel that already scrolls.
4252
+ * A model picker whose whole job is "let me see and tick models" should not
4253
+ * have two scrollbars stacked on top of each other.
4254
+ *
4255
+ * overflow:hidden stays, and it is load-bearing rather than vestigial: the
4256
+ * rounded corners are drawn by this bordered box, so without clipping the first
4257
+ * and last row would paint square corners over it. Nothing is CLIPPED by it any
4258
+ * more, because the box is now exactly as tall as its contents.
4259
+ *
4260
+ * NOTE: no backticks in CSS comments inside this template literal — one would
4261
+ * terminate the string early and silently drop every rule after it. */
4171
4262
  .dsm-workbuddy-model-list{display:flex;flex-direction:column;border:1px solid var(--dsw-alias-border-l2,#36373b);border-radius:10px;overflow:hidden}
4172
4263
  .dsm-workbuddy-model{display:grid;grid-template-columns:minmax(0,1fr);gap:7px;padding:10px 12px;background:var(--dsw-alias-bg-layer-2,#232529);transition:opacity .16s}
4173
4264
  .dsm-workbuddy-model-disabled{opacity:.55}
@@ -5190,6 +5281,21 @@ window.__ModuleLoader__.load({
5190
5281
  * appear before anything billing-side has answered.
5191
5282
  */
5192
5283
  const [statusByRegion, setStatusByRegion] = (0, react.useState)({});
5284
+ /**
5285
+ * Which of the card's three sections is on screen.
5286
+ *
5287
+ * The section tabs exist because the card grew into one 3,500 px column that
5288
+ * carried two unrelated jobs: operating an account (who serves, how much is
5289
+ * left, which order) and editing the catalog (which models, what context
5290
+ * budget, which reasoning tiers). They even commit differently — an account
5291
+ * change lands on the click, a catalog change is a draft until saved — so a
5292
+ * single column put the save button three screens away from what it saves.
5293
+ *
5294
+ * Only the ACTIVE section is visible; the others stay MOUNTED (the `hidden`
5295
+ * attribute), so switching back never refetches, a half-filled draft
5296
+ * survives, and the pool keeps its log.
5297
+ */
5298
+ const [panel, setPanel] = (0, react.useState)("accounts");
5193
5299
  const [busy, setBusy] = (0, react.useState)(false);
5194
5300
  const [settingsRevision, setSettingsRevision] = (0, react.useState)(0);
5195
5301
  /** Per-region unsaved model edits; a draft on one tab is never dropped by
@@ -5409,6 +5515,14 @@ window.__ModuleLoader__.load({
5409
5515
  selectionExplicit: false
5410
5516
  };
5411
5517
  /**
5518
+ * Whether the ACTIVE region has a usable sign-in.
5519
+ *
5520
+ * The section tabs render only here: a signed-out card is one screen of
5521
+ * instructions, and splitting that into panels would hide the only thing it
5522
+ * has to say behind a tab.
5523
+ */
5524
+ const signedIn = status.status === "signed-in";
5525
+ /**
5412
5526
  * Whether the ACTIVE region knows of any account at all.
5413
5527
  *
5414
5528
  * This is the poll's precondition in place of `status === 'signed-in'`. The
@@ -5520,6 +5634,19 @@ window.__ModuleLoader__.load({
5520
5634
  return regionCreditsShownOf(settingsScope?.getSnapshot().value, item);
5521
5635
  };
5522
5636
  /**
5637
+ * Whether the model may call the image tool in this region.
5638
+ *
5639
+ * Read off the Host's answer first, like {@link creditsShownOn} and for the
5640
+ * same measured reason — the browser mirror is stale for this namespace on the
5641
+ * affected 0.1.7 deployments, and here a stale `true` would be worse than a
5642
+ * dead checkbox: it would claim the spending capability is on.
5643
+ */
5644
+ const imageToolOn = (item) => {
5645
+ const usage = statusByRegion[item];
5646
+ if (typeof usage?.imageToolEnabled === "boolean") return usage.imageToolEnabled;
5647
+ return regionImageToolEnabledOf(settingsScope?.getSnapshot().value, item);
5648
+ };
5649
+ /**
5523
5650
  * Switch one region's provider off or on. The write carries the region's
5524
5651
  * whole slot through untouched — only `enabled` changes — so the user's
5525
5652
  * directory, model picks, image opt-ins and budgets survive a round trip.
@@ -5605,6 +5732,19 @@ window.__ModuleLoader__.load({
5605
5732
  if (mounted.current) setTogglingCredits(void 0);
5606
5733
  }
5607
5734
  };
5735
+ const toggleImageTool = async (item, enabled) => {
5736
+ if (settingsScope === void 0) return;
5737
+ setTogglingCredits(item);
5738
+ try {
5739
+ const slot = nextRegionImageToolEnabled(settingsScope.getSnapshot().value, item, enabled)[item];
5740
+ await writeRegionImageToolEnabled(settingsScope, item, enabled, typeof slot === "object" && slot !== null ? slot : {});
5741
+ await refreshUsage(item);
5742
+ } catch (error) {
5743
+ if (mounted.current) setAccountError(error instanceof Error ? error.message : t("row.requestFailed"));
5744
+ } finally {
5745
+ if (mounted.current) setTogglingCredits(void 0);
5746
+ }
5747
+ };
5608
5748
  const refreshModels = async () => {
5609
5749
  setBusy(true);
5610
5750
  try {
@@ -5990,46 +6130,82 @@ window.__ModuleLoader__.load({
5990
6130
  });
5991
6131
  const creditPositionCaption = t("row.creditPositionHintInline");
5992
6132
  const creditPositionDisabledCaption = t("row.creditPositionDisabledHint");
6133
+ /**
6134
+ * The image tool opt-in, rendered above the credit preferences.
6135
+ *
6136
+ * It sits first because it is the only preference here that authorizes SPEND
6137
+ * rather than display: the credit line is a readout, this one lets the model
6138
+ * buy an image (about 5.71 credits) without asking again. CN-only, because the
6139
+ * upstream publishes a `text-to-image` model on the CN gateway and none at all
6140
+ * internationally — showing the box on the global tab would offer a control
6141
+ * that can never do anything.
6142
+ */
6143
+ const imageToolPreference = activeRegion === "cn" ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
6144
+ className: "dsm-workbuddy-pref",
6145
+ title: t("row.imageToolHint"),
6146
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
6147
+ type: "checkbox",
6148
+ className: "dsm-workbuddy-image-tool-check",
6149
+ checked: activeRegionOn ? imageToolOn(activeRegion) : false,
6150
+ disabled: !activeRegionOn || togglingCredits === activeRegion || !canWrite || saving || poolBusy,
6151
+ onChange: (event) => {
6152
+ toggleImageTool(activeRegion, event.target.checked);
6153
+ }
6154
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
6155
+ className: "dsm-workbuddy-pref-copy",
6156
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
6157
+ className: "dsm-workbuddy-pref-label",
6158
+ children: t("row.imageToolLabel")
6159
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
6160
+ className: "dsm-workbuddy-pref-hint",
6161
+ children: t("row.imageToolHint")
6162
+ })]
6163
+ })]
6164
+ }) : null;
5993
6165
  const creditPreferences = /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
5994
6166
  className: "dsm-workbuddy-prefs",
5995
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
5996
- className: "dsm-workbuddy-pref",
5997
- title: activeRegion === "cn" ? t("row.showCreditsCnHint") : t("row.showCreditsGlobalHint"),
5998
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
5999
- type: "checkbox",
6000
- className: "dsm-workbuddy-pref-check",
6001
- checked: activeRegionOn ? creditsShownOn(activeRegion) : false,
6002
- disabled: !activeRegionOn || togglingCredits === activeRegion || !canWrite || saving || poolBusy,
6003
- onChange: (event) => {
6004
- toggleCredits(activeRegion, event.target.checked);
6005
- }
6006
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
6007
- className: "dsm-workbuddy-pref-copy",
6008
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
6009
- className: "dsm-workbuddy-pref-label",
6010
- children: activeRegion === "cn" ? t("row.showCreditsCn") : t("row.showCreditsGlobal")
6011
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
6012
- className: "dsm-workbuddy-pref-hint",
6013
- children: t("row.showCreditsHintInline")
6014
- })]
6015
- })]
6016
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("label", {
6017
- className: "dsm-workbuddy-pref dsm-workbuddy-pref-indent",
6018
- title: t("row.creditPositionHint"),
6019
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
6020
- className: "dsm-workbuddy-pref-copy",
6021
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
6022
- className: "dsm-workbuddy-pref-lead",
6167
+ children: [
6168
+ imageToolPreference,
6169
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
6170
+ className: "dsm-workbuddy-pref",
6171
+ title: activeRegion === "cn" ? t("row.showCreditsCnHint") : t("row.showCreditsGlobalHint"),
6172
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
6173
+ type: "checkbox",
6174
+ className: "dsm-workbuddy-pref-check",
6175
+ checked: activeRegionOn ? creditsShownOn(activeRegion) : false,
6176
+ disabled: !activeRegionOn || togglingCredits === activeRegion || !canWrite || saving || poolBusy,
6177
+ onChange: (event) => {
6178
+ toggleCredits(activeRegion, event.target.checked);
6179
+ }
6180
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
6181
+ className: "dsm-workbuddy-pref-copy",
6023
6182
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
6024
6183
  className: "dsm-workbuddy-pref-label",
6025
- children: t("row.creditPositionLabel")
6026
- }), creditPositionPicker]
6027
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
6028
- className: "dsm-workbuddy-pref-hint",
6029
- children: activeRegionOn ? creditPositionCaption : creditPositionDisabledCaption
6184
+ children: activeRegion === "cn" ? t("row.showCreditsCn") : t("row.showCreditsGlobal")
6185
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
6186
+ className: "dsm-workbuddy-pref-hint",
6187
+ children: t("row.showCreditsHintInline")
6188
+ })]
6030
6189
  })]
6190
+ }),
6191
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("label", {
6192
+ className: "dsm-workbuddy-pref dsm-workbuddy-pref-indent",
6193
+ title: t("row.creditPositionHint"),
6194
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
6195
+ className: "dsm-workbuddy-pref-copy",
6196
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
6197
+ className: "dsm-workbuddy-pref-lead",
6198
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
6199
+ className: "dsm-workbuddy-pref-label",
6200
+ children: t("row.creditPositionLabel")
6201
+ }), creditPositionPicker]
6202
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
6203
+ className: "dsm-workbuddy-pref-hint",
6204
+ children: activeRegionOn ? creditPositionCaption : creditPositionDisabledCaption
6205
+ })]
6206
+ })
6031
6207
  })
6032
- })]
6208
+ ]
6033
6209
  });
6034
6210
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("li", {
6035
6211
  className: `dsm-plugin-card${open ? " dsm-plugin-card-open" : ""}`,
@@ -6107,81 +6283,140 @@ window.__ModuleLoader__.load({
6107
6283
  }, region);
6108
6284
  })
6109
6285
  }),
6110
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
6111
- className: "dsm-workbuddy-models-summary",
6112
- children: t("row.tabHint")
6113
- }),
6114
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
6115
- className: "dsm-workbuddy-models-summary dsm-workbuddy-free-quota-warn",
6116
- children: t("row.tabFreeQuotaWarn")
6117
- }),
6118
- !activeRegionOn ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
6119
- className: "dsm-workbuddy-tab-off-notice",
6120
- children: t("row.tabOffNotice")
6121
- }) : null,
6122
- creditPreferences,
6123
- status.status === "error" || status.status === "signed-in" && status.creditsError !== void 0 ? status.status === "signed-in" && status.credentialRejected === true ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
6124
- className: "dsm-workbuddy-usage-hint",
6125
- children: t("row.reloginHint")
6126
- }) : null,
6127
- accountError === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
6128
- className: "dsm-workbuddy-account-error",
6129
- role: "alert",
6130
- children: accountError
6131
- }),
6132
- selectionLost ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
6133
- className: "dsm-workbuddy-usage-hint",
6134
- children: t("row.selectionLostHint")
6286
+ signedIn ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
6287
+ className: "dsm-workbuddy-panels",
6288
+ role: "tablist",
6289
+ "aria-label": t("row.panelAria"),
6290
+ children: [
6291
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
6292
+ type: "button",
6293
+ role: "tab",
6294
+ id: "dsm-workbuddy-tab-accounts",
6295
+ "aria-controls": "dsm-workbuddy-panel-accounts",
6296
+ "aria-selected": panel === "accounts",
6297
+ className: `dsm-workbuddy-panel-tab${panel === "accounts" ? " dsm-workbuddy-panel-tab-active" : ""}`,
6298
+ onClick: () => {
6299
+ setPanel("accounts");
6300
+ },
6301
+ children: t("row.panelAccounts")
6302
+ }),
6303
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
6304
+ type: "button",
6305
+ role: "tab",
6306
+ id: "dsm-workbuddy-tab-models",
6307
+ "aria-controls": "dsm-workbuddy-panel-models",
6308
+ "aria-selected": panel === "models",
6309
+ className: `dsm-workbuddy-panel-tab${panel === "models" ? " dsm-workbuddy-panel-tab-active" : ""}`,
6310
+ onClick: () => {
6311
+ setPanel("models");
6312
+ },
6313
+ children: t("row.panelModels")
6314
+ }),
6315
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
6316
+ type: "button",
6317
+ role: "tab",
6318
+ id: "dsm-workbuddy-tab-diag",
6319
+ "aria-controls": "dsm-workbuddy-panel-diag",
6320
+ "aria-selected": panel === "diag",
6321
+ className: `dsm-workbuddy-panel-tab${panel === "diag" ? " dsm-workbuddy-panel-tab-active" : ""}`,
6322
+ onClick: () => {
6323
+ setPanel("diag");
6324
+ },
6325
+ children: t("row.panelDiag")
6326
+ })
6327
+ ]
6135
6328
  }) : null,
6136
- status.status === "signed-in" ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
6137
- status.credentialRejected === true ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
6138
- className: "dsm-workbuddy-usage-error",
6139
- role: "alert",
6140
- children: [
6141
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: t("row.credentialRejectedTitle") }),
6142
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", { children: t("row.credentialRejectedIntro", { accountName: nameOf(status.accountName) }) }),
6143
- status.recovery?.usableAccount !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", { children: t("row.credentialRejectedSwitch", { accountName: nameOf(status.recovery.usableAccount.accountName) }) }) : status.recovery?.reloginRequired === true ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", { children: t("row.credentialRejectedRelogin") }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", { children: t("row.credentialRejectedChoose") }),
6144
- status.recovery?.usableAccount === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
6145
- type: "button",
6146
- className: "dsm-btn dsm-btn-outline",
6147
- disabled: switchingAccount || !canWrite,
6148
- onClick: () => {
6149
- const target = status.recovery?.usableAccount;
6150
- if (target !== void 0) switchAccount(target.accountId);
6329
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
6330
+ className: "dsm-workbuddy-panel",
6331
+ role: "tabpanel",
6332
+ id: "dsm-workbuddy-panel-accounts",
6333
+ "aria-labelledby": "dsm-workbuddy-tab-accounts",
6334
+ hidden: signedIn && panel !== "accounts",
6335
+ children: [
6336
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
6337
+ className: "dsm-workbuddy-models-summary",
6338
+ children: t("row.tabHint")
6339
+ }),
6340
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
6341
+ className: "dsm-workbuddy-models-summary dsm-workbuddy-free-quota-warn",
6342
+ children: t("row.tabFreeQuotaWarn")
6343
+ }),
6344
+ !activeRegionOn ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
6345
+ className: "dsm-workbuddy-tab-off-notice",
6346
+ children: t("row.tabOffNotice")
6347
+ }) : null,
6348
+ creditPreferences,
6349
+ status.status === "error" || status.status === "signed-in" && status.creditsError !== void 0 ? status.status === "signed-in" && status.credentialRejected === true ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
6350
+ className: "dsm-workbuddy-usage-hint",
6351
+ children: t("row.reloginHint")
6352
+ }) : null,
6353
+ accountError === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
6354
+ className: "dsm-workbuddy-account-error",
6355
+ role: "alert",
6356
+ children: accountError
6357
+ }),
6358
+ selectionLost ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
6359
+ className: "dsm-workbuddy-usage-hint",
6360
+ children: t("row.selectionLostHint")
6361
+ }) : null,
6362
+ status.status === "signed-in" ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
6363
+ status.credentialRejected === true ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
6364
+ className: "dsm-workbuddy-usage-error",
6365
+ role: "alert",
6366
+ children: [
6367
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: t("row.credentialRejectedTitle") }),
6368
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", { children: t("row.credentialRejectedIntro", { accountName: nameOf(status.accountName) }) }),
6369
+ status.recovery?.usableAccount !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", { children: t("row.credentialRejectedSwitch", { accountName: nameOf(status.recovery.usableAccount.accountName) }) }) : status.recovery?.reloginRequired === true ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", { children: t("row.credentialRejectedRelogin") }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", { children: t("row.credentialRejectedChoose") }),
6370
+ status.recovery?.usableAccount === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
6371
+ type: "button",
6372
+ className: "dsm-btn dsm-btn-outline",
6373
+ disabled: switchingAccount || !canWrite,
6374
+ onClick: () => {
6375
+ const target = status.recovery?.usableAccount;
6376
+ if (target !== void 0) switchAccount(target.accountId);
6377
+ },
6378
+ children: switchingAccount ? t("row.accountsScanning") : t("row.credentialRejectedSwitchAction", { accountName: nameOf(status.recovery.usableAccount.accountName) })
6379
+ })
6380
+ ]
6381
+ }) : null,
6382
+ status.creditsError === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
6383
+ className: "dsm-workbuddy-usage-error",
6384
+ children: t("row.creditsError", { message: status.creditsError })
6385
+ }),
6386
+ status.refreshError === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
6387
+ className: "dsm-workbuddy-usage-error",
6388
+ role: "alert",
6389
+ children: t("row.requestFailedHint", { message: status.refreshError })
6390
+ }),
6391
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(AccountPool, {
6392
+ t,
6393
+ region: activeRegion,
6394
+ ...status.pool === void 0 ? {} : { pool: status.pool },
6395
+ ...settingsScope === void 0 ? {} : { settingsScope },
6396
+ onRescan: () => {
6397
+ rescanAccounts();
6151
6398
  },
6152
- children: switchingAccount ? t("row.accountsScanning") : t("row.credentialRejectedSwitchAction", { accountName: nameOf(status.recovery.usableAccount.accountName) })
6399
+ rescanning: busy,
6400
+ onSelectAccount: (accountId) => {
6401
+ switchAccount(accountId);
6402
+ },
6403
+ selectingAccount: switchingAccount,
6404
+ accountTools,
6405
+ siblingBusy: saving || togglingRegion !== void 0,
6406
+ onBusyChange: setPoolBusy,
6407
+ onSaved: async () => await refreshUsage(activeRegion) !== void 0,
6408
+ onRefresh: () => refreshUsage(activeRegion)
6153
6409
  })
6154
- ]
6155
- }) : null,
6156
- status.creditsError === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
6157
- className: "dsm-workbuddy-usage-error",
6158
- children: t("row.creditsError", { message: status.creditsError })
6159
- }),
6160
- status.refreshError === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
6161
- className: "dsm-workbuddy-usage-error",
6162
- role: "alert",
6163
- children: t("row.requestFailedHint", { message: status.refreshError })
6164
- }),
6165
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(AccountPool, {
6166
- t,
6167
- region: activeRegion,
6168
- ...status.pool === void 0 ? {} : { pool: status.pool },
6169
- ...settingsScope === void 0 ? {} : { settingsScope },
6170
- onRescan: () => {
6171
- rescanAccounts();
6172
- },
6173
- rescanning: busy,
6174
- onSelectAccount: (accountId) => {
6175
- switchAccount(accountId);
6176
- },
6177
- selectingAccount: switchingAccount,
6178
- accountTools,
6179
- siblingBusy: saving || togglingRegion !== void 0,
6180
- onBusyChange: setPoolBusy,
6181
- onSaved: async () => await refreshUsage(activeRegion) !== void 0,
6182
- onRefresh: () => refreshUsage(activeRegion)
6183
- }),
6184
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("section", {
6410
+ ] }) : null
6411
+ ]
6412
+ }),
6413
+ signedIn ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
6414
+ className: "dsm-workbuddy-panel",
6415
+ role: "tabpanel",
6416
+ id: "dsm-workbuddy-panel-models",
6417
+ "aria-labelledby": "dsm-workbuddy-tab-models",
6418
+ hidden: panel !== "models",
6419
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("section", {
6185
6420
  className: "dsm-workbuddy-models",
6186
6421
  "aria-label": t("row.modelsTitle"),
6187
6422
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
@@ -6430,7 +6665,111 @@ window.__ModuleLoader__.load({
6430
6665
  ]
6431
6666
  })
6432
6667
  })
6433
- ] }) : null,
6668
+ }) : null,
6669
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
6670
+ className: "dsm-workbuddy-panel",
6671
+ role: "tabpanel",
6672
+ id: "dsm-workbuddy-panel-diag",
6673
+ "aria-labelledby": "dsm-workbuddy-tab-diag",
6674
+ hidden: !signedIn || panel !== "diag",
6675
+ children: status.status !== "signed-in" ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
6676
+ className: "dsm-workbuddy-diag",
6677
+ children: [
6678
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
6679
+ className: "dsm-workbuddy-diag-row",
6680
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
6681
+ className: "dsm-workbuddy-diag-label",
6682
+ children: t("row.diagRegion")
6683
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
6684
+ className: "dsm-workbuddy-diag-value",
6685
+ children: status.region === "cn" ? t("row.tabCn") : t("row.tabGlobal")
6686
+ })]
6687
+ }),
6688
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
6689
+ className: "dsm-workbuddy-diag-row",
6690
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
6691
+ className: "dsm-workbuddy-diag-label",
6692
+ children: t("row.diagDomain")
6693
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
6694
+ className: "dsm-workbuddy-diag-value dsm-workbuddy-diag-mono",
6695
+ children: status.domain === void 0 || status.domain === "" ? t("row.modelUnknown") : status.domain
6696
+ })]
6697
+ }),
6698
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
6699
+ className: "dsm-workbuddy-diag-row",
6700
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
6701
+ className: "dsm-workbuddy-diag-label",
6702
+ children: t("row.diagAccount")
6703
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
6704
+ className: "dsm-workbuddy-diag-value",
6705
+ children: nameOf(status.accountName)
6706
+ })]
6707
+ }),
6708
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
6709
+ className: "dsm-workbuddy-diag-row",
6710
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
6711
+ className: "dsm-workbuddy-diag-label",
6712
+ children: t("row.diagToken")
6713
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
6714
+ className: "dsm-workbuddy-diag-value dsm-workbuddy-diag-mono",
6715
+ children: formatDate(status.tokenExpiresAtMs)
6716
+ })]
6717
+ }),
6718
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
6719
+ className: "dsm-workbuddy-diag-row",
6720
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
6721
+ className: "dsm-workbuddy-diag-label",
6722
+ children: t("row.diagCatalog")
6723
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
6724
+ className: "dsm-workbuddy-diag-value",
6725
+ children: formatNumber(status.models.length)
6726
+ })]
6727
+ }),
6728
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
6729
+ className: "dsm-workbuddy-diag-row",
6730
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
6731
+ className: "dsm-workbuddy-diag-label",
6732
+ children: t("row.diagEnabled")
6733
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
6734
+ className: "dsm-workbuddy-diag-value",
6735
+ children: formatNumber(status.enabledModelIds.length)
6736
+ })]
6737
+ }),
6738
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
6739
+ className: "dsm-workbuddy-diag-row",
6740
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
6741
+ className: "dsm-workbuddy-diag-label",
6742
+ children: t("row.diagPool")
6743
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
6744
+ className: "dsm-workbuddy-diag-value",
6745
+ children: status.pool?.enabled === true ? t("row.diagPoolOn", { count: status.pool.accounts.length }) : t("row.diagPoolOff")
6746
+ })]
6747
+ }),
6748
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
6749
+ className: "dsm-workbuddy-diag-row",
6750
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
6751
+ className: "dsm-workbuddy-diag-label",
6752
+ children: t("row.diagFailures")
6753
+ }), (() => {
6754
+ const failures = [];
6755
+ if (status.credentialRejected === true) failures.push(t("row.credentialRejectedTitle"));
6756
+ if (status.refreshError !== void 0) failures.push(status.refreshError);
6757
+ if (status.creditsError !== void 0) failures.push(status.creditsError);
6758
+ return failures.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
6759
+ className: "dsm-workbuddy-diag-value",
6760
+ children: t("row.diagNoFailures")
6761
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
6762
+ className: "dsm-workbuddy-diag-value dsm-workbuddy-diag-bad dsm-workbuddy-diag-mono",
6763
+ children: failures.join(" · ")
6764
+ });
6765
+ })()]
6766
+ })
6767
+ ]
6768
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
6769
+ className: "dsm-workbuddy-diag-hint",
6770
+ children: t("row.diagHint")
6771
+ })] })
6772
+ }),
6434
6773
  status.status === "signed-out" && loading ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
6435
6774
  className: "dsm-workbuddy-usage-text",
6436
6775
  role: "status",
@@ -7500,6 +7839,22 @@ window.__ModuleLoader__.load({
7500
7839
  "row.accountsWriteFailed": "The account change was not saved: {message} — the active profile's configuration file could not be updated (on Windows it may be locked by an antivirus scanner or a sync client). Your previous choice is still in effect; close whatever holds that file and try again (see docs/WINDOWS.md in the plugin repo for the exact path).",
7501
7840
  "row.selectionLostHint": "The saved account no longer exists locally.",
7502
7841
  "row.selectionLostMessage": "The account saved for this region is no longer among the local sign-ins (WorkBuddy replaced its login or cleaned up backups). Your other sign-ins are fine — pick a current one from the list above. Signing in again in the desktop app will not fix this.",
7842
+ "row.panelAccounts": "Accounts",
7843
+ "row.panelModels": "Models",
7844
+ "row.panelDiag": "Diagnostics",
7845
+ "row.panelAria": "Card sections",
7846
+ "row.diagHint": "What this card actually read, so a report can quote it. Nothing here changes a setting.",
7847
+ "row.diagRegion": "Region",
7848
+ "row.diagDomain": "Upstream domain",
7849
+ "row.diagAccount": "Serving account",
7850
+ "row.diagToken": "Sign-in expires",
7851
+ "row.diagCatalog": "Models in the catalog",
7852
+ "row.diagEnabled": "Enabled models",
7853
+ "row.diagPool": "Account pool",
7854
+ "row.diagPoolOn": "on · {count} accounts",
7855
+ "row.diagPoolOff": "off",
7856
+ "row.diagFailures": "Last failure",
7857
+ "row.diagNoFailures": "Nothing reported.",
7503
7858
  "row.modelsTitle": "Models",
7504
7859
  "row.modelsSummary": "{count} enabled",
7505
7860
  "row.modelsDirty": "unsaved changes",
@@ -7732,6 +8087,8 @@ window.__ModuleLoader__.load({
7732
8087
  "row.poolRowRemoved": "{accountName}: hidden from the list. 「Detect accounts again」 re-adds it; the desktop app stays signed in.",
7733
8088
  "row.poolRowRemoving": "Removing the account…",
7734
8089
  "row.poolRowRemoveDesktopOnly": "{accountName}: hidden from the list — this account comes from the desktop app's own sign-in, which is left untouched. 「Detect accounts again」 re-adds it.",
8090
+ "row.imageToolLabel": "Let the model draw images",
8091
+ "row.imageToolHint": "Adds the workbuddy_draw_image tool, which the MODEL may call on its own — about 5.71 credits per image, with no per-call confirmation. Off means the tool is not registered at all.",
7735
8092
  "row.showCreditsCn": "Show credits in the composer toolbar",
7736
8093
  "row.showCreditsGlobal": "Show credits in the composer toolbar",
7737
8094
  "row.showCreditsCnHint": "Show the domestic (WB CN) credit balance in the composer toolbar, but only while this session uses a WB CN model. Click it for per-account balances and a manual refresh (refreshes by itself every 5 minutes).",
@@ -7826,6 +8183,22 @@ window.__ModuleLoader__.load({
7826
8183
  "row.accountsWriteFailed": "账号改动未保存:{message} —— 当前 profile 的配置文件写入失败(Windows 上可能被杀毒软件或同步盘占用)。当前仍是原来保存的选择,请先关闭占用该文件的程序再重试(文件的确切路径见插件仓库的 docs/WINDOWS.md)。",
7827
8184
  "row.selectionLostHint": "已保存的账号在本机已不存在。",
7828
8185
  "row.selectionLostMessage": "该区域保存的账号已不在本机登录列表中(WorkBuddy 更换了登录或清理了备份文件)。本机其他登录仍然可用——请在上方列表中重新选择一个当前账号。重新登录桌面端 App 无法修复此问题。",
8186
+ "row.panelAccounts": "账号",
8187
+ "row.panelModels": "模型",
8188
+ "row.panelDiag": "诊断",
8189
+ "row.panelAria": "卡片分区",
8190
+ "row.diagHint": "这张卡片实际读到了什么,方便贴进问题反馈。这里没有任何会改动设置的控件。",
8191
+ "row.diagRegion": "区域",
8192
+ "row.diagDomain": "上游域名",
8193
+ "row.diagAccount": "当前服务账号",
8194
+ "row.diagToken": "登录有效期至",
8195
+ "row.diagCatalog": "目录中的模型",
8196
+ "row.diagEnabled": "已启用的模型",
8197
+ "row.diagPool": "账号池",
8198
+ "row.diagPoolOn": "开启 · {count} 个账号",
8199
+ "row.diagPoolOff": "关闭",
8200
+ "row.diagFailures": "最近一次失败",
8201
+ "row.diagNoFailures": "没有报错。",
7829
8202
  "row.modelsTitle": "模型",
7830
8203
  "row.modelsSummary": "已启用 {count} 个",
7831
8204
  "row.modelsDirty": "有未保存的修改",
@@ -8058,6 +8431,8 @@ window.__ModuleLoader__.load({
8058
8431
  "row.poolRowRemoved": "{accountName}:已从列表中隐藏。点「重新检测账号」会重新加入,桌面 App 的登录不受影响。",
8059
8432
  "row.poolRowRemoving": "正在删除账号…",
8060
8433
  "row.poolRowRemoveDesktopOnly": "{accountName}:已从列表中隐藏——这个账号来自桌面 App 自己的登录,该登录未被改动。点「重新检测账号」会重新加入。",
8434
+ "row.imageToolLabel": "允许模型自主生图",
8435
+ "row.imageToolHint": "开启后模型可以自己调用 workbuddy_draw_image 画图——约 5.71 积分/张,没有逐次确认。关闭时该工具根本不会注册。",
8061
8436
  "row.showCreditsCn": "在输入框工具栏显示积分读数",
8062
8437
  "row.showCreditsGlobal": "在输入框工具栏显示积分读数",
8063
8438
  "row.showCreditsCnHint": "在对话输入框工具栏显示国内版(WB CN)剩余积分,仅当本会话使用 WB CN 模型时出现。点击可看各账号余额并手动刷新(每 5 分钟也会自动刷新)。",