dsh-connect-workbuddy 5.8.5 → 5.10.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
@@ -50,6 +50,18 @@ window.__ModuleLoader__.load({
50
50
  const WORKBUDDY_BENEFIT_CLAIM_PATH = "/plugins/dsh-connect-workbuddy/benefit/claim";
51
51
  /** Plugin-owned live model refresh endpoint. */
52
52
  const WORKBUDDY_MODELS_REFRESH_PATH = "/plugins/dsh-connect-workbuddy/models/refresh";
53
+ /**
54
+ * Plugin-owned MEDIA model list endpoint (image + video, read from the region's
55
+ * `/v3/config`).
56
+ *
57
+ * Separate from {@link WORKBUDDY_MODELS_REFRESH_PATH} because it answers a
58
+ * different question from a different document projection: the refresh route
59
+ * returns the CHAT roster, from which `selectCliModels` drops every media
60
+ * entry, while this one keeps the `text-to-image`/`image-to-image`/
61
+ * `text-to-video`/`image-to-video` rows the card lists. Read-only and free —
62
+ * it is the same GET the image tool already performs.
63
+ */
64
+ const WORKBUDDY_MEDIA_MODELS_PATH = "/plugins/dsh-connect-workbuddy/media-models";
53
65
  /** Plugin-owned local account rescan endpoint. */
54
66
  const WORKBUDDY_ACCOUNTS_REFRESH_PATH = "/plugins/dsh-connect-workbuddy/accounts/refresh";
55
67
  /**
@@ -2129,8 +2141,27 @@ window.__ModuleLoader__.load({
2129
2141
  *
2130
2142
  * Any other malformed value is left to the caller to drop — a wrong window
2131
2143
  * would hide a live promotion, and guessing is not this parser's job.
2144
+ *
2145
+ * THE TYPE GUARD IS LOAD-BEARING, NOT DEFENSIVE DECORATION (issue #139). The
2146
+ * upstream feeds a string, and `parseDaily`'s own `text()` would already have
2147
+ * dropped a number — but the SAVED `lastCatalog` is a second, independent
2148
+ * entrance to this function, and nothing on that path type-checks. A promotion
2149
+ * object rides through `toPersistedWorkBuddyModel` untouched (the settings
2150
+ * schema keeps unknown keys), survives `resolveRoster`, and reaches the
2151
+ * renderer's `selectPromoBadge`. A numeric `start` there used to throw
2152
+ * `value.trim is not a function` DURING RENDER, which the host's slot error
2153
+ * boundary turned into a blank configuration section — the page looked as if
2154
+ * the plugin had never installed.
2155
+ *
2156
+ * Refusing a non-string is the same answer the parser already gives every
2157
+ * other unreadable shape: drop the window rather than guess. Interpreting a
2158
+ * number as "minutes since midnight" was considered and rejected — `1380`
2159
+ * happens to mean 23:00, but no upstream has ever published that shape, and
2160
+ * inventing hours for a promotion the upstream never described is exactly the
2161
+ * overstatement this module exists to prevent.
2132
2162
  */
2133
2163
  function minutesOfClock(value) {
2164
+ if (typeof value !== "string") return void 0;
2134
2165
  const match = /^(\d{1,2}):(\d{2})$/.exec(value.trim());
2135
2166
  if (match === null) return void 0;
2136
2167
  const hours = Number(match[1]);
@@ -4962,6 +4993,27 @@ window.__ModuleLoader__.load({
4962
4993
  .dsm-workbuddy-pref-lead{display:flex;align-items:center;justify-content:flex-start;gap:10px;min-width:0}
4963
4994
  .dsm-workbuddy-pref-lead .dsm-workbuddy-pref-label{flex:none}
4964
4995
  .dsm-workbuddy-pref-hint{color:var(--dsw-alias-label-tertiary,#999);font-size:12px;line-height:17px}
4996
+ /* The media roster: a READ-ONLY list, so it is drawn as quiet rows rather than
4997
+ as a table — it answers "what can this tab draw with" in passing, and must
4998
+ not compete with the switches above it for attention. The id is monospace
4999
+ and dimmer than the name because it is the string a tool call has to pass,
5000
+ not the thing a human reads; the tag chips carry the only colour, one hue
5001
+ per direction (text→image vs image→image), so the grouping is visible
5002
+ without a legend. */
5003
+ /* Aligned with the indented rows above it, but under its OWN class: the
5004
+ credit position picker owns .dsm-workbuddy-pref-indent, and the card suite
5005
+ resolves that selector expecting the picker. This block is a block (lead
5006
+ row + hint + list), not a single indented line. */
5007
+ .dsm-workbuddy-media{padding-left:25px}
5008
+ .dsm-workbuddy-media-list{margin:2px 0 0;padding:0;list-style:none;display:flex;flex-direction:column;gap:4px}
5009
+ .dsm-workbuddy-media-row{display:flex;align-items:baseline;flex-wrap:wrap;gap:6px;min-width:0}
5010
+ .dsm-workbuddy-media-name{color:var(--dsw-alias-label-secondary,#b8bcc4);font-size:12px}
5011
+ .dsm-workbuddy-media-id{color:var(--dsw-alias-label-tertiary,#8b8f98);font-size:11px;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;overflow-wrap:anywhere}
5012
+ .dsm-workbuddy-media-tags{display:inline-flex;flex-wrap:wrap;gap:4px}
5013
+ .dsm-workbuddy-media-tag{border:1px solid var(--dsw-alias-border-l2);border-radius:6px;padding:0 6px;font-size:11px;line-height:16px;color:var(--dsw-alias-label-tertiary,#8b8f98)}
5014
+ .dsm-workbuddy-media-empty,.dsm-workbuddy-media-error{font-size:12px;line-height:17px}
5015
+ .dsm-workbuddy-media-empty{color:var(--dsw-alias-label-tertiary,#8b8f98)}
5016
+ .dsm-workbuddy-media-error{color:var(--dsw-alias-label-error,#e5675f)}
4965
5017
  /* The supplement to the host's fixed quota sentence (issue #63). The plugin no
4966
5018
  longer claims the host's shell.quota-notice chain seat, so this no longer
4967
5019
  paints into shell.overlay — that seat is the reason the previous build's
@@ -6043,6 +6095,32 @@ window.__ModuleLoader__.load({
6043
6095
  * text-only model and an unverified one look identically broken next to their
6044
6096
  * checked siblings.
6045
6097
  */
6098
+ /**
6099
+ * The media roster from the Host's JSON, tolerantly.
6100
+ *
6101
+ * Anything that is not a row with a string id and a name is DROPPED, never
6102
+ * rendered: this is a read-only list, and a half-shaped entry is not worth an
6103
+ * exception that would take down the whole card. A Host older than the route
6104
+ * (or any other answer without `models`) yields an empty list, which the block
6105
+ * shows as "this tab publishes none" instead of as a failure.
6106
+ */
6107
+ function readMediaRoster(value) {
6108
+ const models = value?.models;
6109
+ if (!Array.isArray(models)) return void 0;
6110
+ const rows = [];
6111
+ for (const entry of models) {
6112
+ const record = entry;
6113
+ if (record === null || typeof record !== "object") continue;
6114
+ if (typeof record.id !== "string" || record.id === "") continue;
6115
+ const tags = Array.isArray(record.tags) ? record.tags.filter((tag) => typeof tag === "string") : [];
6116
+ rows.push({
6117
+ id: record.id,
6118
+ name: typeof record.name === "string" ? record.name : record.id,
6119
+ tags
6120
+ });
6121
+ }
6122
+ return rows;
6123
+ }
6046
6124
  function imageCheckboxHint(model, t) {
6047
6125
  switch (nativeModalityOf(model.id)) {
6048
6126
  case "multimodal": return t("row.modelImage");
@@ -6306,8 +6384,82 @@ window.__ModuleLoader__.load({
6306
6384
  };
6307
6385
  }
6308
6386
  }
6309
- /** Render WorkBuddy sign-in state, credits, and model selection as one card. */
6310
- function WorkBuddyCard({ t, settingsScope, view }) {
6387
+ /**
6388
+ * The configuration area's own crash face (issue #139).
6389
+ *
6390
+ * WHY THIS EXISTS. The host wraps every slot entry in its own
6391
+ * `SlotErrorBoundary`, and that boundary's failure face is an EMPTY
6392
+ * `<div data-slot-error>` (`dsh-client-ui-renderer`). So a render-time throw
6393
+ * anywhere inside this card does not look like an error — it looks like the
6394
+ * plugin never installed: the header stays, the whole configuration area
6395
+ * vanishes, and the only trace is one `console.error` line. That is precisely
6396
+ * how issue #139 was reported ("配置区整块消失"), and it is why the reporter
6397
+ * could not tell a real crash from a missing card.
6398
+ *
6399
+ * WHAT IT BUYS. The same catch, with a face that says what happened: the
6400
+ * message goes on screen, so a report can quote it without opening devtools.
6401
+ * It does NOT hide the error — `componentDidCatch` still logs it, so the
6402
+ * console keeps the full stack the host would have printed anyway.
6403
+ *
6404
+ * SCOPE: the BODY only, never the header. A user whose configuration crashed
6405
+ * must still be able to collapse and re-expand the card, and the header is
6406
+ * pure static markup — it cannot be the thing that threw.
6407
+ *
6408
+ * LIMITS, stated so nobody expects more than React gives: a boundary catches
6409
+ * errors thrown during RENDER, in lifecycle methods, and in constructors. It
6410
+ * does NOT catch throws from event handlers, timers, or promise callbacks —
6411
+ * so an async failure still surfaces the way it did before (inline error text
6412
+ * or the console), which is the correct behaviour for those paths.
6413
+ */
6414
+ var CardBodyBoundary = class extends react.Component {
6415
+ state = {
6416
+ failed: false,
6417
+ message: void 0
6418
+ };
6419
+ static getDerivedStateFromError(error) {
6420
+ if (error instanceof Error && error.name === "SlotAssemblyError") throw error;
6421
+ return {
6422
+ failed: true,
6423
+ message: error instanceof Error ? error.message : String(error)
6424
+ };
6425
+ }
6426
+ componentDidCatch(error, info) {
6427
+ try {
6428
+ console.error("[dsh-connect-workbuddy] configuration area failed to render:", error, info.componentStack);
6429
+ } catch {}
6430
+ }
6431
+ render() {
6432
+ if (!this.state.failed) return this.props.children;
6433
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
6434
+ className: "dsm-workbuddy-usage",
6435
+ children: [
6436
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
6437
+ className: "dsm-workbuddy-usage-error",
6438
+ children: this.props.t("row.cardCrashed")
6439
+ }),
6440
+ this.state.message === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
6441
+ className: "dsm-workbuddy-usage-error dsm-workbuddy-diag-mono",
6442
+ children: this.state.message
6443
+ }),
6444
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
6445
+ className: "dsm-workbuddy-diag-hint",
6446
+ children: this.props.t("row.cardCrashedHint")
6447
+ })
6448
+ ]
6449
+ });
6450
+ }
6451
+ };
6452
+ /**
6453
+ * Render WorkBuddy sign-in state, credits, and model selection as one card.
6454
+ *
6455
+ * The body is wrapped in {@link CardBodyBoundary} INSIDE the render, not here:
6456
+ * the header must keep working when the body throws (see that class).
6457
+ */
6458
+ function WorkBuddyCard(props) {
6459
+ if (props.t === void 0) throw new Error("WorkBuddy plugin card requires its translation function");
6460
+ return (0, react.createElement)(WorkBuddyCardBody, props);
6461
+ }
6462
+ function WorkBuddyCardBody({ t, settingsScope, view }) {
6311
6463
  if (t === void 0) throw new Error("WorkBuddy plugin card requires its translation function");
6312
6464
  const [open, setOpen] = (0, react.useState)(view === "page");
6313
6465
  /** The region whose tab is on screen; each tab is its own provider stack. */
@@ -6350,6 +6502,18 @@ window.__ModuleLoader__.load({
6350
6502
  const [saveError, setSaveError] = (0, react.useState)(void 0);
6351
6503
  const [switchingAccount, setSwitchingAccount] = (0, react.useState)(false);
6352
6504
  /**
6505
+ * The region's MEDIA model roster (read-only), keyed by region.
6506
+ *
6507
+ * NOT part of `statusByRegion`: the usage document is the editable model
6508
+ * directory, and folding media rows into it would put them in the saved
6509
+ * `lastCatalog` — where the adapter would offer an image model as a chat
6510
+ * model. It is a separate, cheap, read-only read that only the capability
6511
+ * panel needs.
6512
+ */
6513
+ const [mediaByRegion, setMediaByRegion] = (0, react.useState)({});
6514
+ const [mediaError, setMediaError] = (0, react.useState)(void 0);
6515
+ const [mediaBusy, setMediaBusy] = (0, react.useState)(false);
6516
+ /**
6353
6517
  * Whether the account pool's save is in flight. Both sections write into one
6354
6518
  * region slot and the Host merges per-region, so two concurrent saves can
6355
6519
  * interleave on a stale base and revert each other while both report success
@@ -7019,6 +7183,72 @@ window.__ModuleLoader__.load({
7019
7183
  if (mounted.current) setTogglingCredits(void 0);
7020
7184
  }
7021
7185
  };
7186
+ /**
7187
+ * Read the active region's media roster. Read-only and free (the same GET the
7188
+ * image tool performs), so it is safe to run whenever the capability panel is
7189
+ * shown; `refresh` bypasses the Host's 5-minute cache for the explicit
7190
+ * button, because a user who just enabled an image model upstream should not
7191
+ * have to wait out a TTL to see it.
7192
+ *
7193
+ * A failure lands in `mediaError` INSIDE the block and nowhere else: it must
7194
+ * not touch `statusByRegion`, or one unreachable roster would replace the
7195
+ * whole panel with an error screen.
7196
+ */
7197
+ const loadMediaModels = (0, react.useCallback)(async (region, refresh, signal) => {
7198
+ try {
7199
+ const url = refresh ? `${withWorkBuddyRegion(WORKBUDDY_MEDIA_MODELS_PATH, region)}&refresh=1` : withWorkBuddyRegion(WORKBUDDY_MEDIA_MODELS_PATH, region);
7200
+ const response = await fetch(url, {
7201
+ method: "POST",
7202
+ headers: { accept: "application/json" },
7203
+ credentials: "same-origin",
7204
+ ...signal === void 0 ? {} : { signal }
7205
+ });
7206
+ if (!response.ok) throw new Error(`HTTP ${response.status}`);
7207
+ const models = readMediaRoster(await response.json().catch(() => void 0));
7208
+ if (models === void 0) throw new Error(`HTTP ${response.status}`);
7209
+ if (!mounted.current || signal?.aborted === true) return;
7210
+ setMediaByRegion((prev) => ({
7211
+ ...prev,
7212
+ [region]: models
7213
+ }));
7214
+ setMediaError(void 0);
7215
+ } catch (error) {
7216
+ if (!mounted.current || signal?.aborted === true) return;
7217
+ setMediaError(error instanceof Error ? error.message : t("row.requestFailed"));
7218
+ }
7219
+ }, [t]);
7220
+ /**
7221
+ * Regions whose media roster has already been asked for, so showing the
7222
+ * capability panel does not re-read the roster on every render — and so a
7223
+ * FAILED read is not retried in a loop. The explicit "re-read" button is the
7224
+ * only thing that asks twice, because a user who just enabled a model
7225
+ * upstream is the one case where a fresh answer is the point.
7226
+ */
7227
+ const mediaRequested = (0, react.useRef)(/* @__PURE__ */ new Set());
7228
+ /**
7229
+ * Read the media roster once per region, when the capability panel is opened.
7230
+ *
7231
+ * Lazy rather than alongside the usage fetch: the roster is only visible in
7232
+ * this one panel, and the usage fetch runs for BOTH regions on every open. If
7233
+ * the secret store happens to hold a credential for the region the user is not
7234
+ * looking at, an eager read would spend a request nobody asked for.
7235
+ */
7236
+ (0, react.useEffect)(() => {
7237
+ if (!open || panel !== "capabilities" || !hasAccounts) return;
7238
+ if (mediaRequested.current.has(activeRegion)) return;
7239
+ mediaRequested.current.add(activeRegion);
7240
+ const controller = new AbortController();
7241
+ loadMediaModels(activeRegion, false, controller.signal);
7242
+ return () => {
7243
+ controller.abort();
7244
+ };
7245
+ }, [
7246
+ open,
7247
+ panel,
7248
+ activeRegion,
7249
+ hasAccounts,
7250
+ loadMediaModels
7251
+ ]);
7022
7252
  const refreshModels = async () => {
7023
7253
  setBusy(true);
7024
7254
  try {
@@ -7508,12 +7738,14 @@ window.__ModuleLoader__.load({
7508
7738
  *
7509
7739
  * It sits first because it is the only preference here that authorizes SPEND
7510
7740
  * rather than display: the credit line is a readout, this one lets the model
7511
- * buy an image (about 5.71 credits) without asking again. CN-only, because the
7512
- * upstream publishes a `text-to-image` model on the CN gateway and none at all
7513
- * internationally — showing the box on the global tab would offer a control
7514
- * that can never do anything.
7741
+ * buy an image without asking again. Shown on BOTH tabs: each gateway now
7742
+ * publishes its own `text-to-image` model (CN `hunyuan-image-alpha`,
7743
+ * international `hy-image-3.5`, measured 2026-10-25), so the global box is no
7744
+ * longer a control that can never do anything. The flag is per-region and the
7745
+ * tool uses whichever region is BOTH enabled and switched on (CN first), so
7746
+ * ticking it here only ever authorizes spending on THIS tab.
7515
7747
  */
7516
- const imageToolPreference = activeRegion === "cn" ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
7748
+ const imageToolPreference = /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
7517
7749
  className: "dsm-workbuddy-pref",
7518
7750
  title: t("row.imageToolHint"),
7519
7751
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
@@ -7534,7 +7766,86 @@ window.__ModuleLoader__.load({
7534
7766
  children: t("row.imageToolHint")
7535
7767
  })]
7536
7768
  })]
7537
- }) : null;
7769
+ });
7770
+ /**
7771
+ * What this region can actually draw, READ-ONLY.
7772
+ *
7773
+ * There is deliberately no picker here. Each tag group holds exactly one
7774
+ * model in both regions today (measured 2026-10-25), so a dropdown would be a
7775
+ * choice of one — the card would be inviting a decision that does not exist.
7776
+ * What the user DOES need is the answer to "can this tab draw at all, and
7777
+ * with what" — which is also what makes the image-tool switch above
7778
+ * meaningful on the international tab.
7779
+ *
7780
+ * Indented like the position picker, because it is a property of the switch
7781
+ * above it rather than a fourth independent setting.
7782
+ */
7783
+ const mediaTagLabels = {
7784
+ "text-to-image": t("row.mediaTagTextToImage"),
7785
+ "image-to-image": t("row.mediaTagImageToImage"),
7786
+ "text-to-video": t("row.mediaTagTextToVideo"),
7787
+ "image-to-video": t("row.mediaTagImageToVideo")
7788
+ };
7789
+ const activeMedia = mediaByRegion[activeRegion];
7790
+ const mediaModelsPreference = /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
7791
+ className: "dsm-workbuddy-pref dsm-workbuddy-media",
7792
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
7793
+ className: "dsm-workbuddy-pref-copy",
7794
+ children: [
7795
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
7796
+ className: "dsm-workbuddy-pref-lead",
7797
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
7798
+ className: "dsm-workbuddy-pref-label",
7799
+ children: t("row.mediaModelsLabel")
7800
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
7801
+ type: "button",
7802
+ className: "dsm-btn dsm-btn-outline",
7803
+ disabled: mediaBusy || !activeRegionOn,
7804
+ onClick: () => {
7805
+ setMediaBusy(true);
7806
+ loadMediaModels(activeRegion, true).finally(() => {
7807
+ if (mounted.current) setMediaBusy(false);
7808
+ });
7809
+ },
7810
+ children: mediaBusy ? t("row.mediaModelsRefreshing") : t("row.mediaModelsRefresh")
7811
+ })]
7812
+ }),
7813
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
7814
+ className: "dsm-workbuddy-pref-hint",
7815
+ children: t("row.mediaModelsHint")
7816
+ }),
7817
+ mediaError !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
7818
+ className: "dsm-workbuddy-media-error",
7819
+ children: t("row.mediaModelsError", { message: mediaError })
7820
+ }) : activeMedia === void 0 ? null : activeMedia.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
7821
+ className: "dsm-workbuddy-media-empty",
7822
+ children: t("row.mediaModelsEmpty")
7823
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("ul", {
7824
+ className: "dsm-workbuddy-media-list",
7825
+ children: activeMedia.map((model) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("li", {
7826
+ className: "dsm-workbuddy-media-row",
7827
+ children: [
7828
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
7829
+ className: "dsm-workbuddy-media-name",
7830
+ children: model.name
7831
+ }),
7832
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
7833
+ className: "dsm-workbuddy-media-id",
7834
+ children: model.id
7835
+ }),
7836
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
7837
+ className: "dsm-workbuddy-media-tags",
7838
+ children: model.tags.map((tag) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
7839
+ className: "dsm-workbuddy-media-tag",
7840
+ children: mediaTagLabels[tag] ?? tag
7841
+ }, tag))
7842
+ })
7843
+ ]
7844
+ }, model.id))
7845
+ })
7846
+ ]
7847
+ })
7848
+ });
7538
7849
  /**
7539
7850
  * The CLI-route switch.
7540
7851
  *
@@ -7777,6 +8088,7 @@ window.__ModuleLoader__.load({
7777
8088
  cliBridgePreference,
7778
8089
  cliBridgeSwitchedOn() ? cliBridgePickers : null,
7779
8090
  imageToolPreference,
8091
+ mediaModelsPreference,
7780
8092
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
7781
8093
  className: "dsm-workbuddy-pref",
7782
8094
  title: activeRegion === "cn" ? t("row.showCreditsCnHint") : t("row.showCreditsGlobalHint"),
@@ -7852,627 +8164,632 @@ window.__ModuleLoader__.load({
7852
8164
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
7853
8165
  className: "dsm-plugin-card-body",
7854
8166
  hidden: !open,
7855
- children: open ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
7856
- className: "dsm-workbuddy-usage",
7857
- children: [
7858
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
7859
- className: "dsm-workbuddy-tabs",
7860
- role: "tablist",
7861
- "aria-label": title,
7862
- children: WORKBUDDY_REGIONS.map((region) => {
7863
- const regionStatus = statusByRegion[region];
7864
- const regionOnState = regionOn(region);
7865
- return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
7866
- className: "dsm-workbuddy-tab-cell",
7867
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
8167
+ children: open ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CardBodyBoundary, {
8168
+ t,
8169
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
8170
+ className: "dsm-workbuddy-usage",
8171
+ children: [
8172
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
8173
+ className: "dsm-workbuddy-tabs",
8174
+ role: "tablist",
8175
+ "aria-label": title,
8176
+ children: WORKBUDDY_REGIONS.map((region) => {
8177
+ const regionStatus = statusByRegion[region];
8178
+ const regionOnState = regionOn(region);
8179
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
8180
+ className: "dsm-workbuddy-tab-cell",
8181
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
8182
+ type: "button",
8183
+ role: "tab",
8184
+ "aria-selected": region === activeRegion,
8185
+ className: `dsm-workbuddy-tab${region === activeRegion ? " dsm-workbuddy-tab-active" : ""}${regionOnState ? "" : " dsm-workbuddy-tab-off"}`,
8186
+ onClick: () => {
8187
+ setActiveRegion(region);
8188
+ setAccountError(void 0);
8189
+ },
8190
+ children: [regionStatus === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
8191
+ "aria-hidden": "true",
8192
+ className: "dsm-workbuddy-tab-dot",
8193
+ style: dotStyle(regionStatus.status)
8194
+ }), region === "cn" ? t("row.tabCn") : t("row.tabGlobal")]
8195
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("label", {
8196
+ className: "dsm-workbuddy-tab-switch",
8197
+ title: t("row.tabSwitchHint"),
8198
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
8199
+ type: "checkbox",
8200
+ checked: regionOnState,
8201
+ disabled: togglingRegion === region || !canWrite || saving || poolBusy,
8202
+ "aria-label": t("row.tabSwitchAria", { region: region === "cn" ? t("row.tabCn") : t("row.tabGlobal") }),
8203
+ onChange: (event) => {
8204
+ toggleRegion(region, event.target.checked);
8205
+ }
8206
+ })
8207
+ })]
8208
+ }, region);
8209
+ })
8210
+ }),
8211
+ signedIn ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
8212
+ className: "dsm-workbuddy-panels",
8213
+ role: "tablist",
8214
+ "aria-label": t("row.panelAria"),
8215
+ children: [
8216
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
7868
8217
  type: "button",
7869
8218
  role: "tab",
7870
- "aria-selected": region === activeRegion,
7871
- className: `dsm-workbuddy-tab${region === activeRegion ? " dsm-workbuddy-tab-active" : ""}${regionOnState ? "" : " dsm-workbuddy-tab-off"}`,
8219
+ id: "dsm-workbuddy-tab-accounts",
8220
+ "aria-controls": "dsm-workbuddy-panel-accounts",
8221
+ "aria-selected": panel === "accounts",
8222
+ className: `dsm-workbuddy-panel-tab${panel === "accounts" ? " dsm-workbuddy-panel-tab-active" : ""}`,
7872
8223
  onClick: () => {
7873
- setActiveRegion(region);
7874
- setAccountError(void 0);
8224
+ setPanel("accounts");
7875
8225
  },
7876
- children: [regionStatus === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
7877
- "aria-hidden": "true",
7878
- className: "dsm-workbuddy-tab-dot",
7879
- style: dotStyle(regionStatus.status)
7880
- }), region === "cn" ? t("row.tabCn") : t("row.tabGlobal")]
7881
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("label", {
7882
- className: "dsm-workbuddy-tab-switch",
7883
- title: t("row.tabSwitchHint"),
7884
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
7885
- type: "checkbox",
7886
- checked: regionOnState,
7887
- disabled: togglingRegion === region || !canWrite || saving || poolBusy,
7888
- "aria-label": t("row.tabSwitchAria", { region: region === "cn" ? t("row.tabCn") : t("row.tabGlobal") }),
7889
- onChange: (event) => {
7890
- toggleRegion(region, event.target.checked);
7891
- }
7892
- })
7893
- })]
7894
- }, region);
7895
- })
7896
- }),
7897
- signedIn ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
7898
- className: "dsm-workbuddy-panels",
7899
- role: "tablist",
7900
- "aria-label": t("row.panelAria"),
7901
- children: [
7902
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
7903
- type: "button",
7904
- role: "tab",
7905
- id: "dsm-workbuddy-tab-accounts",
7906
- "aria-controls": "dsm-workbuddy-panel-accounts",
7907
- "aria-selected": panel === "accounts",
7908
- className: `dsm-workbuddy-panel-tab${panel === "accounts" ? " dsm-workbuddy-panel-tab-active" : ""}`,
7909
- onClick: () => {
7910
- setPanel("accounts");
7911
- },
7912
- children: t("row.panelAccounts")
7913
- }),
7914
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
7915
- type: "button",
7916
- role: "tab",
7917
- id: "dsm-workbuddy-tab-models",
7918
- "aria-controls": "dsm-workbuddy-panel-models",
7919
- "aria-selected": panel === "models",
7920
- className: `dsm-workbuddy-panel-tab${panel === "models" ? " dsm-workbuddy-panel-tab-active" : ""}`,
7921
- onClick: () => {
7922
- setPanel("models");
7923
- },
7924
- children: t("row.panelModels")
7925
- }),
7926
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
7927
- type: "button",
7928
- role: "tab",
7929
- id: "dsm-workbuddy-tab-capabilities",
7930
- "aria-controls": "dsm-workbuddy-panel-capabilities",
7931
- "aria-selected": panel === "capabilities",
7932
- className: `dsm-workbuddy-panel-tab${panel === "capabilities" ? " dsm-workbuddy-panel-tab-active" : ""}`,
7933
- onClick: () => {
7934
- setPanel("capabilities");
7935
- },
7936
- children: t("row.panelCapabilities")
7937
- }),
7938
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
7939
- type: "button",
7940
- role: "tab",
7941
- id: "dsm-workbuddy-tab-diag",
7942
- "aria-controls": "dsm-workbuddy-panel-diag",
7943
- "aria-selected": panel === "diag",
7944
- className: `dsm-workbuddy-panel-tab${panel === "diag" ? " dsm-workbuddy-panel-tab-active" : ""}`,
7945
- onClick: () => {
7946
- setPanel("diag");
7947
- },
7948
- children: t("row.panelDiag")
7949
- })
7950
- ]
7951
- }) : null,
7952
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
7953
- className: "dsm-workbuddy-panel",
7954
- role: "tabpanel",
7955
- id: "dsm-workbuddy-panel-accounts",
7956
- "aria-labelledby": "dsm-workbuddy-tab-accounts",
7957
- hidden: signedIn && panel !== "accounts",
7958
- children: [
7959
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
7960
- className: "dsm-workbuddy-models-summary",
7961
- children: t("row.tabHint")
7962
- }),
7963
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
7964
- className: "dsm-workbuddy-models-summary dsm-workbuddy-free-quota-warn",
7965
- children: t("row.tabFreeQuotaWarn")
7966
- }),
7967
- !activeRegionOn ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
7968
- className: "dsm-workbuddy-tab-off-notice",
7969
- children: t("row.tabOffNotice")
7970
- }) : null,
7971
- 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", {
7972
- className: "dsm-workbuddy-usage-hint",
7973
- children: t("row.reloginHint")
7974
- }) : null,
7975
- accountError === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
7976
- className: "dsm-workbuddy-account-error",
7977
- role: "alert",
7978
- children: accountError
7979
- }),
7980
- selectionLost ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
7981
- className: "dsm-workbuddy-usage-hint",
7982
- children: t("row.selectionLostHint")
7983
- }) : null,
7984
- status.status === "signed-in" ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
7985
- status.credentialRejected === true ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
7986
- className: "dsm-workbuddy-usage-error",
7987
- role: "alert",
7988
- children: [
7989
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: t("row.credentialRejectedTitle") }),
7990
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", { children: t("row.credentialRejectedIntro", { accountName: nameOf(status.accountName) }) }),
7991
- 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") }),
7992
- status.recovery?.usableAccount === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
7993
- type: "button",
7994
- className: "dsm-btn dsm-btn-outline",
7995
- disabled: switchingAccount || !canWrite,
7996
- onClick: () => {
7997
- const target = status.recovery?.usableAccount;
7998
- if (target !== void 0) switchAccount(target.accountId);
7999
- },
8000
- children: switchingAccount ? t("row.accountsScanning") : t("row.credentialRejectedSwitchAction", { accountName: nameOf(status.recovery.usableAccount.accountName) })
8001
- })
8002
- ]
8003
- }) : null,
8004
- status.creditsError === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
8005
- className: "dsm-workbuddy-usage-error",
8006
- children: t("row.creditsError", { message: status.creditsError })
8226
+ children: t("row.panelAccounts")
8007
8227
  }),
8008
- status.refreshError === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
8009
- className: "dsm-workbuddy-usage-error",
8010
- role: "alert",
8011
- children: t("row.requestFailedHint", { message: status.refreshError })
8228
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
8229
+ type: "button",
8230
+ role: "tab",
8231
+ id: "dsm-workbuddy-tab-models",
8232
+ "aria-controls": "dsm-workbuddy-panel-models",
8233
+ "aria-selected": panel === "models",
8234
+ className: `dsm-workbuddy-panel-tab${panel === "models" ? " dsm-workbuddy-panel-tab-active" : ""}`,
8235
+ onClick: () => {
8236
+ setPanel("models");
8237
+ },
8238
+ children: t("row.panelModels")
8012
8239
  }),
8013
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(AccountPool, {
8014
- t,
8015
- region: activeRegion,
8016
- ...status.pool === void 0 ? {} : { pool: status.pool },
8017
- ...settingsScope === void 0 ? {} : { settingsScope },
8018
- onRescan: () => {
8019
- rescanAccounts();
8240
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
8241
+ type: "button",
8242
+ role: "tab",
8243
+ id: "dsm-workbuddy-tab-capabilities",
8244
+ "aria-controls": "dsm-workbuddy-panel-capabilities",
8245
+ "aria-selected": panel === "capabilities",
8246
+ className: `dsm-workbuddy-panel-tab${panel === "capabilities" ? " dsm-workbuddy-panel-tab-active" : ""}`,
8247
+ onClick: () => {
8248
+ setPanel("capabilities");
8020
8249
  },
8021
- rescanning: busy,
8022
- onSelectAccount: (accountId) => {
8023
- switchAccount(accountId);
8250
+ children: t("row.panelCapabilities")
8251
+ }),
8252
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
8253
+ type: "button",
8254
+ role: "tab",
8255
+ id: "dsm-workbuddy-tab-diag",
8256
+ "aria-controls": "dsm-workbuddy-panel-diag",
8257
+ "aria-selected": panel === "diag",
8258
+ className: `dsm-workbuddy-panel-tab${panel === "diag" ? " dsm-workbuddy-panel-tab-active" : ""}`,
8259
+ onClick: () => {
8260
+ setPanel("diag");
8024
8261
  },
8025
- selectingAccount: switchingAccount,
8026
- accountTools,
8027
- siblingBusy: saving || togglingRegion !== void 0,
8028
- onBusyChange: setPoolBusy,
8029
- onSaved: async () => await refreshUsage(activeRegion) !== void 0,
8030
- onRefresh: () => refreshUsage(activeRegion)
8262
+ children: t("row.panelDiag")
8031
8263
  })
8032
- ] }) : null
8033
- ]
8034
- }),
8035
- signedIn ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
8036
- className: "dsm-workbuddy-panel",
8037
- role: "tabpanel",
8038
- id: "dsm-workbuddy-panel-models",
8039
- "aria-labelledby": "dsm-workbuddy-tab-models",
8040
- hidden: panel !== "models",
8041
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("section", {
8042
- className: "dsm-workbuddy-models",
8043
- "aria-label": t("row.modelsTitle"),
8044
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
8045
- className: "dsm-workbuddy-models-fold",
8046
- children: [
8047
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
8048
- className: "dsm-workbuddy-models-head",
8049
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
8050
- className: "dsm-workbuddy-models-title-row",
8051
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("h3", {
8052
- className: "dsm-workbuddy-models-title",
8053
- children: [t("row.modelsTitle"), dirty ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
8054
- className: "dsm-workbuddy-models-dirty",
8055
- children: t("row.modelsDirty")
8056
- }) : null]
8264
+ ]
8265
+ }) : null,
8266
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
8267
+ className: "dsm-workbuddy-panel",
8268
+ role: "tabpanel",
8269
+ id: "dsm-workbuddy-panel-accounts",
8270
+ "aria-labelledby": "dsm-workbuddy-tab-accounts",
8271
+ hidden: signedIn && panel !== "accounts",
8272
+ children: [
8273
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
8274
+ className: "dsm-workbuddy-models-summary",
8275
+ children: t("row.tabHint")
8276
+ }),
8277
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
8278
+ className: "dsm-workbuddy-models-summary dsm-workbuddy-free-quota-warn",
8279
+ children: t("row.tabFreeQuotaWarn")
8280
+ }),
8281
+ !activeRegionOn ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
8282
+ className: "dsm-workbuddy-tab-off-notice",
8283
+ children: t("row.tabOffNotice")
8284
+ }) : null,
8285
+ 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", {
8286
+ className: "dsm-workbuddy-usage-hint",
8287
+ children: t("row.reloginHint")
8288
+ }) : null,
8289
+ accountError === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
8290
+ className: "dsm-workbuddy-account-error",
8291
+ role: "alert",
8292
+ children: accountError
8293
+ }),
8294
+ selectionLost ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
8295
+ className: "dsm-workbuddy-usage-hint",
8296
+ children: t("row.selectionLostHint")
8297
+ }) : null,
8298
+ status.status === "signed-in" ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
8299
+ status.credentialRejected === true ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
8300
+ className: "dsm-workbuddy-usage-error",
8301
+ role: "alert",
8302
+ children: [
8303
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: t("row.credentialRejectedTitle") }),
8304
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", { children: t("row.credentialRejectedIntro", { accountName: nameOf(status.accountName) }) }),
8305
+ 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") }),
8306
+ status.recovery?.usableAccount === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
8307
+ type: "button",
8308
+ className: "dsm-btn dsm-btn-outline",
8309
+ disabled: switchingAccount || !canWrite,
8310
+ onClick: () => {
8311
+ const target = status.recovery?.usableAccount;
8312
+ if (target !== void 0) switchAccount(target.accountId);
8313
+ },
8314
+ children: switchingAccount ? t("row.accountsScanning") : t("row.credentialRejectedSwitchAction", { accountName: nameOf(status.recovery.usableAccount.accountName) })
8057
8315
  })
8058
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
8059
- className: "dsm-workbuddy-models-summary",
8060
- children: t("row.modelsSummary", { count: activeEnabledIds.size })
8061
- })] }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
8062
- className: "dsm-workbuddy-models-head-actions",
8063
- children: [
8064
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
8065
- type: "button",
8066
- className: "dsm-btn dsm-btn-outline",
8067
- disabled: busy,
8068
- onClick: () => {
8069
- refreshModels();
8070
- },
8071
- children: busy ? t("row.modelsRefreshing") : t("row.modelsRefresh")
8072
- }),
8073
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
8074
- type: "button",
8075
- className: "dsm-btn dsm-btn-outline dsm-workbuddy-models-sweep",
8076
- disabled: busy || sweeping !== void 0 || activeEnabledIds.size === 0,
8077
- title: t("row.modelsTestEnabledHint"),
8078
- onClick: sweepEnabledModels,
8079
- children: sweeping === void 0 ? t("row.modelsTestEnabled") : t("row.modelsTesting", {
8080
- done: sweeping.done,
8081
- total: sweeping.total
8082
- })
8083
- }),
8084
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
8085
- type: "button",
8086
- className: "dsm-btn dsm-btn-outline dsm-workbuddy-models-sweep",
8087
- disabled: busy || sweeping !== void 0 || visibleModels.length === 0,
8088
- title: t("row.modelsTestAllHint"),
8089
- onClick: sweepAllModels,
8090
- children: sweeping === void 0 ? t("row.modelsTestAll") : t("row.modelsTesting", {
8091
- done: sweeping.done,
8092
- total: sweeping.total
8093
- })
8094
- }),
8095
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
8096
- type: "button",
8097
- className: "dsm-btn dsm-btn-outline dsm-workbuddy-models-sweep",
8098
- disabled: busy || tpsSweeping !== void 0 || tpsBusy || activeEnabledIds.size === 0,
8099
- title: t("row.tpsSweepEnabledHint"),
8100
- onClick: sweepTpsEnabledModels,
8101
- children: tpsSweeping === void 0 ? t("row.tpsSweepEnabled") : t("row.tpsTesting", {
8102
- done: tpsSweeping.done,
8103
- total: tpsSweeping.total
8104
- })
8105
- })
8106
- ]
8107
- })]
8316
+ ]
8317
+ }) : null,
8318
+ status.creditsError === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
8319
+ className: "dsm-workbuddy-usage-error",
8320
+ children: t("row.creditsError", { message: status.creditsError })
8108
8321
  }),
8109
- probeError === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
8110
- className: "dsm-workbuddy-model-probe-result dsm-workbuddy-model-probe-result-bad",
8322
+ status.refreshError === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
8323
+ className: "dsm-workbuddy-usage-error",
8111
8324
  role: "alert",
8112
- children: t("row.probeError", { message: probeError })
8113
- }),
8114
- sweepSummary === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
8115
- className: "dsm-workbuddy-models-sweep-summary",
8116
- role: "status",
8117
- children: sweepSummary
8325
+ children: t("row.requestFailedHint", { message: status.refreshError })
8118
8326
  }),
8119
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
8120
- className: "dsm-workbuddy-model-list",
8121
- children: visibleModels.map((model) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
8122
- className: `dsm-workbuddy-model${activeEnabledIds.has(model.id) ? "" : " dsm-workbuddy-model-disabled"}`,
8123
- children: [
8124
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
8125
- className: "dsm-workbuddy-model-head",
8126
- children: [
8127
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
8128
- className: "dsm-workbuddy-model-enabled",
8129
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
8130
- type: "checkbox",
8131
- checked: activeEnabledIds.has(model.id),
8132
- disabled: !canWrite || saving,
8133
- onChange: () => {
8134
- toggleModel(model.id);
8135
- }
8136
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
8137
- className: "dsm-workbuddy-model-copy",
8138
- children: [
8139
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
8140
- className: "dsm-workbuddy-model-name",
8141
- children: [model.name, effectiveRate(model) === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
8142
- className: "dsm-workbuddy-model-name-rate",
8143
- children: ["x", effectiveRate(model).toFixed(2)]
8144
- })]
8145
- }),
8146
- promoChip(model),
8147
- busyIcon(busyLevels[model.id], uiLanguage())
8148
- ]
8149
- })]
8150
- }),
8151
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
8152
- className: "dsm-workbuddy-model-image",
8153
- title: imageCheckboxHint(model, t),
8154
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
8155
- type: "checkbox",
8156
- checked: activeImageIds.has(model.id),
8157
- disabled: !canWrite || saving,
8158
- onChange: () => {
8159
- toggleImage(model.id);
8160
- }
8161
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("row.modelImage") })]
8162
- }),
8163
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
8164
- className: `dsm-workbuddy-model-off${offBlocked(model) ? " dsm-workbuddy-model-off-blocked" : ""}`,
8165
- title: offBlocked(model) ? t("row.modelOffRefusedHint") : t("row.modelOffHint"),
8166
- children: [
8167
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
8327
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(AccountPool, {
8328
+ t,
8329
+ region: activeRegion,
8330
+ ...status.pool === void 0 ? {} : { pool: status.pool },
8331
+ ...settingsScope === void 0 ? {} : { settingsScope },
8332
+ onRescan: () => {
8333
+ rescanAccounts();
8334
+ },
8335
+ rescanning: busy,
8336
+ onSelectAccount: (accountId) => {
8337
+ switchAccount(accountId);
8338
+ },
8339
+ selectingAccount: switchingAccount,
8340
+ accountTools,
8341
+ siblingBusy: saving || togglingRegion !== void 0,
8342
+ onBusyChange: setPoolBusy,
8343
+ onSaved: async () => await refreshUsage(activeRegion) !== void 0,
8344
+ onRefresh: () => refreshUsage(activeRegion)
8345
+ })
8346
+ ] }) : null
8347
+ ]
8348
+ }),
8349
+ signedIn ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
8350
+ className: "dsm-workbuddy-panel",
8351
+ role: "tabpanel",
8352
+ id: "dsm-workbuddy-panel-models",
8353
+ "aria-labelledby": "dsm-workbuddy-tab-models",
8354
+ hidden: panel !== "models",
8355
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("section", {
8356
+ className: "dsm-workbuddy-models",
8357
+ "aria-label": t("row.modelsTitle"),
8358
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
8359
+ className: "dsm-workbuddy-models-fold",
8360
+ children: [
8361
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
8362
+ className: "dsm-workbuddy-models-head",
8363
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
8364
+ className: "dsm-workbuddy-models-title-row",
8365
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("h3", {
8366
+ className: "dsm-workbuddy-models-title",
8367
+ children: [t("row.modelsTitle"), dirty ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
8368
+ className: "dsm-workbuddy-models-dirty",
8369
+ children: t("row.modelsDirty")
8370
+ }) : null]
8371
+ })
8372
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
8373
+ className: "dsm-workbuddy-models-summary",
8374
+ children: t("row.modelsSummary", { count: activeEnabledIds.size })
8375
+ })] }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
8376
+ className: "dsm-workbuddy-models-head-actions",
8377
+ children: [
8378
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
8379
+ type: "button",
8380
+ className: "dsm-btn dsm-btn-outline",
8381
+ disabled: busy,
8382
+ onClick: () => {
8383
+ refreshModels();
8384
+ },
8385
+ children: busy ? t("row.modelsRefreshing") : t("row.modelsRefresh")
8386
+ }),
8387
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
8388
+ type: "button",
8389
+ className: "dsm-btn dsm-btn-outline dsm-workbuddy-models-sweep",
8390
+ disabled: busy || sweeping !== void 0 || activeEnabledIds.size === 0,
8391
+ title: t("row.modelsTestEnabledHint"),
8392
+ onClick: sweepEnabledModels,
8393
+ children: sweeping === void 0 ? t("row.modelsTestEnabled") : t("row.modelsTesting", {
8394
+ done: sweeping.done,
8395
+ total: sweeping.total
8396
+ })
8397
+ }),
8398
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
8399
+ type: "button",
8400
+ className: "dsm-btn dsm-btn-outline dsm-workbuddy-models-sweep",
8401
+ disabled: busy || sweeping !== void 0 || visibleModels.length === 0,
8402
+ title: t("row.modelsTestAllHint"),
8403
+ onClick: sweepAllModels,
8404
+ children: sweeping === void 0 ? t("row.modelsTestAll") : t("row.modelsTesting", {
8405
+ done: sweeping.done,
8406
+ total: sweeping.total
8407
+ })
8408
+ }),
8409
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
8410
+ type: "button",
8411
+ className: "dsm-btn dsm-btn-outline dsm-workbuddy-models-sweep",
8412
+ disabled: busy || tpsSweeping !== void 0 || tpsBusy || activeEnabledIds.size === 0,
8413
+ title: t("row.tpsSweepEnabledHint"),
8414
+ onClick: sweepTpsEnabledModels,
8415
+ children: tpsSweeping === void 0 ? t("row.tpsSweepEnabled") : t("row.tpsTesting", {
8416
+ done: tpsSweeping.done,
8417
+ total: tpsSweeping.total
8418
+ })
8419
+ })
8420
+ ]
8421
+ })]
8422
+ }),
8423
+ probeError === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
8424
+ className: "dsm-workbuddy-model-probe-result dsm-workbuddy-model-probe-result-bad",
8425
+ role: "alert",
8426
+ children: t("row.probeError", { message: probeError })
8427
+ }),
8428
+ sweepSummary === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
8429
+ className: "dsm-workbuddy-models-sweep-summary",
8430
+ role: "status",
8431
+ children: sweepSummary
8432
+ }),
8433
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
8434
+ className: "dsm-workbuddy-model-list",
8435
+ children: visibleModels.map((model) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
8436
+ className: `dsm-workbuddy-model${activeEnabledIds.has(model.id) ? "" : " dsm-workbuddy-model-disabled"}`,
8437
+ children: [
8438
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
8439
+ className: "dsm-workbuddy-model-head",
8440
+ children: [
8441
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
8442
+ className: "dsm-workbuddy-model-enabled",
8443
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
8168
8444
  type: "checkbox",
8169
- checked: activeOffIds.has(model.id),
8170
- disabled: !canWrite || saving || offBlocked(model),
8171
- onChange: () => {
8172
- toggleOff(model.id);
8173
- }
8174
- }),
8175
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("row.modelOff") }),
8176
- offBlocked(model) ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
8177
- className: "dsm-workbuddy-model-off-flag",
8178
- children: t("row.modelOffRefused")
8179
- }) : null
8180
- ]
8181
- }),
8182
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("fieldset", {
8183
- className: "dsm-workbuddy-context-budget",
8184
- "aria-label": t("row.contextBudget"),
8185
- children: [
8186
- model.nativeContextWindow > 2e5 ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
8187
- type: "radio",
8188
- name: `context-${model.id}`,
8189
- checked: (activeContextBudgets[model.id] ?? 2e5) === 2e5,
8445
+ checked: activeEnabledIds.has(model.id),
8190
8446
  disabled: !canWrite || saving,
8191
8447
  onChange: () => {
8192
- setContextBudget(model.id, 2e5);
8448
+ toggleModel(model.id);
8193
8449
  }
8194
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: "200K" })] }) : null,
8195
- model.nativeContextWindow > 5e5 ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
8196
- type: "radio",
8197
- name: `context-${model.id}`,
8198
- checked: activeContextBudgets[model.id] === 5e5,
8450
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
8451
+ className: "dsm-workbuddy-model-copy",
8452
+ children: [
8453
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
8454
+ className: "dsm-workbuddy-model-name",
8455
+ children: [model.name, effectiveRate(model) === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
8456
+ className: "dsm-workbuddy-model-name-rate",
8457
+ children: ["x", effectiveRate(model).toFixed(2)]
8458
+ })]
8459
+ }),
8460
+ promoChip(model),
8461
+ busyIcon(busyLevels[model.id], uiLanguage())
8462
+ ]
8463
+ })]
8464
+ }),
8465
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
8466
+ className: "dsm-workbuddy-model-image",
8467
+ title: imageCheckboxHint(model, t),
8468
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
8469
+ type: "checkbox",
8470
+ checked: activeImageIds.has(model.id),
8199
8471
  disabled: !canWrite || saving,
8200
8472
  onChange: () => {
8201
- setContextBudget(model.id, 5e5);
8473
+ toggleImage(model.id);
8202
8474
  }
8203
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: "500K" })] }) : null,
8204
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
8205
- type: "radio",
8206
- name: `context-${model.id}`,
8207
- checked: model.nativeContextWindow <= 2e5 || activeContextBudgets[model.id] === model.nativeContextWindow,
8208
- disabled: model.nativeContextWindow <= 2e5 || !canWrite || saving,
8209
- onChange: () => {
8210
- setContextBudget(model.id, model.nativeContextWindow);
8211
- }
8212
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: formatCapacity(model.nativeContextWindow, t("row.modelUnknown")) })] })
8475
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("row.modelImage") })]
8476
+ }),
8477
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
8478
+ className: `dsm-workbuddy-model-off${offBlocked(model) ? " dsm-workbuddy-model-off-blocked" : ""}`,
8479
+ title: offBlocked(model) ? t("row.modelOffRefusedHint") : t("row.modelOffHint"),
8480
+ children: [
8481
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
8482
+ type: "checkbox",
8483
+ checked: activeOffIds.has(model.id),
8484
+ disabled: !canWrite || saving || offBlocked(model),
8485
+ onChange: () => {
8486
+ toggleOff(model.id);
8487
+ }
8488
+ }),
8489
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("row.modelOff") }),
8490
+ offBlocked(model) ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
8491
+ className: "dsm-workbuddy-model-off-flag",
8492
+ children: t("row.modelOffRefused")
8493
+ }) : null
8494
+ ]
8495
+ }),
8496
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("fieldset", {
8497
+ className: "dsm-workbuddy-context-budget",
8498
+ "aria-label": t("row.contextBudget"),
8499
+ children: [
8500
+ model.nativeContextWindow > 2e5 ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
8501
+ type: "radio",
8502
+ name: `context-${model.id}`,
8503
+ checked: (activeContextBudgets[model.id] ?? 2e5) === 2e5,
8504
+ disabled: !canWrite || saving,
8505
+ onChange: () => {
8506
+ setContextBudget(model.id, 2e5);
8507
+ }
8508
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: "200K" })] }) : null,
8509
+ model.nativeContextWindow > 5e5 ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
8510
+ type: "radio",
8511
+ name: `context-${model.id}`,
8512
+ checked: activeContextBudgets[model.id] === 5e5,
8513
+ disabled: !canWrite || saving,
8514
+ onChange: () => {
8515
+ setContextBudget(model.id, 5e5);
8516
+ }
8517
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: "500K" })] }) : null,
8518
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
8519
+ type: "radio",
8520
+ name: `context-${model.id}`,
8521
+ checked: model.nativeContextWindow <= 2e5 || activeContextBudgets[model.id] === model.nativeContextWindow,
8522
+ disabled: model.nativeContextWindow <= 2e5 || !canWrite || saving,
8523
+ onChange: () => {
8524
+ setContextBudget(model.id, model.nativeContextWindow);
8525
+ }
8526
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: formatCapacity(model.nativeContextWindow, t("row.modelUnknown")) })] })
8527
+ ]
8528
+ }),
8529
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
8530
+ className: "dsm-workbuddy-model-actions-buttons",
8531
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
8532
+ type: "button",
8533
+ className: "dsm-btn dsm-btn-outline dsm-workbuddy-model-probe",
8534
+ disabled: probing[model.id] === true || status.status !== "signed-in",
8535
+ title: t("row.probeHint"),
8536
+ onClick: () => {
8537
+ probeModels([model.id]);
8538
+ },
8539
+ children: probing[model.id] === true ? t("row.probing") : t("row.probe")
8540
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
8541
+ type: "button",
8542
+ className: "dsm-btn dsm-btn-outline dsm-workbuddy-model-probe",
8543
+ disabled: tpsProbing[model.id] === true || status.status !== "signed-in",
8544
+ title: t("row.tpsHint"),
8545
+ onClick: () => {
8546
+ measureTpsFor([model.id]);
8547
+ },
8548
+ children: tpsProbing[model.id] === true ? t("row.tpsMeasuring") : t("row.tpsMeasure")
8549
+ })]
8550
+ })
8551
+ ]
8552
+ }),
8553
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
8554
+ className: "dsm-workbuddy-model-details",
8555
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
8556
+ className: "dsm-workbuddy-model-meta",
8557
+ children: [
8558
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("row.modelContext", { context: formatCapacity(model.nativeContextWindow, t("row.modelUnknown")) }) }),
8559
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("row.modelOutput", { output: formatCapacity(model.maxTokens, t("row.modelUnknown")) }) }),
8560
+ model.reasoning === void 0 || model.reasoning.supportedEfforts === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("row.modelReasoning", { efforts: model.reasoning.supportedEfforts.join(" / ") }) })
8213
8561
  ]
8214
- }),
8215
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
8216
- className: "dsm-workbuddy-model-actions-buttons",
8217
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
8218
- type: "button",
8219
- className: "dsm-btn dsm-btn-outline dsm-workbuddy-model-probe",
8220
- disabled: probing[model.id] === true || status.status !== "signed-in",
8221
- title: t("row.probeHint"),
8222
- onClick: () => {
8223
- probeModels([model.id]);
8224
- },
8225
- children: probing[model.id] === true ? t("row.probing") : t("row.probe")
8226
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
8227
- type: "button",
8228
- className: "dsm-btn dsm-btn-outline dsm-workbuddy-model-probe",
8229
- disabled: tpsProbing[model.id] === true || status.status !== "signed-in",
8230
- title: t("row.tpsHint"),
8231
- onClick: () => {
8232
- measureTpsFor([model.id]);
8233
- },
8234
- children: tpsProbing[model.id] === true ? t("row.tpsMeasuring") : t("row.tpsMeasure")
8235
- })]
8236
8562
  })
8237
- ]
8563
+ }),
8564
+ (() => {
8565
+ const result = probes[activeRegion]?.[model.id];
8566
+ if (result === void 0) return null;
8567
+ const view = probeResultView(result, t);
8568
+ const reason = inlineProbeReason(result.outcome, result.message);
8569
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("p", {
8570
+ className: `dsm-workbuddy-model-probe-result dsm-workbuddy-model-probe-result-${view.tone}`,
8571
+ role: "status",
8572
+ ...result.message === void 0 ? {} : { title: result.message },
8573
+ children: [view.text, reason === void 0 ? null : " · " + reason]
8574
+ });
8575
+ })(),
8576
+ (() => {
8577
+ const measured = tpsResults[model.id];
8578
+ if (measured === void 0) return null;
8579
+ const view = tpsResultView(measured, t, tpsMeasuredAt[model.id]);
8580
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
8581
+ className: `dsm-workbuddy-model-probe-result dsm-workbuddy-model-probe-result-${view.tone}`,
8582
+ role: "status",
8583
+ children: view.text
8584
+ });
8585
+ })()
8586
+ ]
8587
+ }, model.id))
8588
+ }),
8589
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
8590
+ className: "dsm-workbuddy-model-capability-note",
8591
+ children: t("row.modelCapabilityPending")
8592
+ }),
8593
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
8594
+ className: "dsm-workbuddy-model-actions",
8595
+ children: [
8596
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("a", {
8597
+ className: "dsm-workbuddy-usage-cheer",
8598
+ href: WORKBUDDY_GITHUB_URL,
8599
+ target: "_blank",
8600
+ rel: "noopener noreferrer",
8601
+ children: [t("row.cheer"), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
8602
+ className: "dsm-workbuddy-usage-cheer-star",
8603
+ "aria-hidden": "true",
8604
+ children: "★"
8605
+ })]
8238
8606
  }),
8239
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
8240
- className: "dsm-workbuddy-model-details",
8241
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
8242
- className: "dsm-workbuddy-model-meta",
8243
- children: [
8244
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("row.modelContext", { context: formatCapacity(model.nativeContextWindow, t("row.modelUnknown")) }) }),
8245
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("row.modelOutput", { output: formatCapacity(model.maxTokens, t("row.modelUnknown")) }) }),
8246
- model.reasoning === void 0 || model.reasoning.supportedEfforts === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("row.modelReasoning", { efforts: model.reasoning.supportedEfforts.join(" / ") }) })
8247
- ]
8248
- })
8607
+ saveError === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
8608
+ className: "dsm-workbuddy-model-save-error",
8609
+ children: t("row.saveError", { message: saveError })
8249
8610
  }),
8250
- (() => {
8251
- const result = probes[activeRegion]?.[model.id];
8252
- if (result === void 0) return null;
8253
- const view = probeResultView(result, t);
8254
- const reason = inlineProbeReason(result.outcome, result.message);
8255
- return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("p", {
8256
- className: `dsm-workbuddy-model-probe-result dsm-workbuddy-model-probe-result-${view.tone}`,
8257
- role: "status",
8258
- ...result.message === void 0 ? {} : { title: result.message },
8259
- children: [view.text, reason === void 0 ? null : " · " + reason]
8260
- });
8261
- })(),
8262
- (() => {
8263
- const measured = tpsResults[model.id];
8264
- if (measured === void 0) return null;
8265
- const view = tpsResultView(measured, t, tpsMeasuredAt[model.id]);
8266
- return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
8267
- className: `dsm-workbuddy-model-probe-result dsm-workbuddy-model-probe-result-${view.tone}`,
8268
- role: "status",
8269
- children: view.text
8270
- });
8271
- })()
8611
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
8612
+ className: "dsm-workbuddy-model-actions-buttons",
8613
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
8614
+ type: "button",
8615
+ className: "dsm-btn dsm-btn-outline",
8616
+ disabled: !dirty || saving || poolBusy || togglingRegion !== void 0,
8617
+ onClick: discardModels,
8618
+ children: t("row.discard")
8619
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
8620
+ type: "button",
8621
+ className: "dsm-btn dsm-btn-primary",
8622
+ disabled: !dirty || saving || poolBusy || togglingRegion !== void 0 || activeEnabledIds.size === 0,
8623
+ onClick: () => {
8624
+ saveModels();
8625
+ },
8626
+ children: saving ? t("row.saving") : t("row.save")
8627
+ })]
8628
+ })
8272
8629
  ]
8273
- }, model.id))
8630
+ })
8631
+ ]
8632
+ })
8633
+ })
8634
+ }) : null,
8635
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
8636
+ className: "dsm-workbuddy-panel",
8637
+ role: "tabpanel",
8638
+ id: "dsm-workbuddy-panel-capabilities",
8639
+ "aria-labelledby": "dsm-workbuddy-tab-capabilities",
8640
+ hidden: signedIn && panel !== "capabilities",
8641
+ children: [
8642
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
8643
+ className: "dsm-workbuddy-panel-note",
8644
+ children: t("row.panelCapabilitiesHint")
8645
+ }),
8646
+ creditPreferences,
8647
+ upstreamSwitches
8648
+ ]
8649
+ }),
8650
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
8651
+ className: "dsm-workbuddy-panel",
8652
+ role: "tabpanel",
8653
+ id: "dsm-workbuddy-panel-diag",
8654
+ "aria-labelledby": "dsm-workbuddy-tab-diag",
8655
+ hidden: !signedIn || panel !== "diag",
8656
+ children: status.status !== "signed-in" ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
8657
+ className: "dsm-workbuddy-diag",
8658
+ children: [
8659
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
8660
+ className: "dsm-workbuddy-diag-row",
8661
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
8662
+ className: "dsm-workbuddy-diag-label",
8663
+ children: t("row.diagRegion")
8664
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
8665
+ className: "dsm-workbuddy-diag-value",
8666
+ children: status.region === "cn" ? t("row.tabCn") : t("row.tabGlobal")
8667
+ })]
8274
8668
  }),
8275
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
8276
- className: "dsm-workbuddy-model-capability-note",
8277
- children: t("row.modelCapabilityPending")
8669
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
8670
+ className: "dsm-workbuddy-diag-row",
8671
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
8672
+ className: "dsm-workbuddy-diag-label",
8673
+ children: t("row.diagDomain")
8674
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
8675
+ className: "dsm-workbuddy-diag-value dsm-workbuddy-diag-mono",
8676
+ children: status.domain === void 0 || status.domain === "" ? t("row.modelUnknown") : status.domain
8677
+ })]
8278
8678
  }),
8279
8679
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
8280
- className: "dsm-workbuddy-model-actions",
8281
- children: [
8282
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("a", {
8283
- className: "dsm-workbuddy-usage-cheer",
8284
- href: WORKBUDDY_GITHUB_URL,
8285
- target: "_blank",
8286
- rel: "noopener noreferrer",
8287
- children: [t("row.cheer"), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
8288
- className: "dsm-workbuddy-usage-cheer-star",
8289
- "aria-hidden": "true",
8290
- children: "★"
8291
- })]
8292
- }),
8293
- saveError === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
8294
- className: "dsm-workbuddy-model-save-error",
8295
- children: t("row.saveError", { message: saveError })
8296
- }),
8297
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
8298
- className: "dsm-workbuddy-model-actions-buttons",
8299
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
8300
- type: "button",
8301
- className: "dsm-btn dsm-btn-outline",
8302
- disabled: !dirty || saving || poolBusy || togglingRegion !== void 0,
8303
- onClick: discardModels,
8304
- children: t("row.discard")
8305
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
8306
- type: "button",
8307
- className: "dsm-btn dsm-btn-primary",
8308
- disabled: !dirty || saving || poolBusy || togglingRegion !== void 0 || activeEnabledIds.size === 0,
8309
- onClick: () => {
8310
- saveModels();
8311
- },
8312
- children: saving ? t("row.saving") : t("row.save")
8313
- })]
8314
- })
8315
- ]
8680
+ className: "dsm-workbuddy-diag-row",
8681
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
8682
+ className: "dsm-workbuddy-diag-label",
8683
+ children: t("row.diagAccount")
8684
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
8685
+ className: "dsm-workbuddy-diag-value",
8686
+ children: nameOf(status.accountName)
8687
+ })]
8688
+ }),
8689
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
8690
+ className: "dsm-workbuddy-diag-row",
8691
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
8692
+ className: "dsm-workbuddy-diag-label",
8693
+ children: t("row.diagToken")
8694
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
8695
+ className: "dsm-workbuddy-diag-value dsm-workbuddy-diag-mono",
8696
+ children: formatDate(status.tokenExpiresAtMs)
8697
+ })]
8698
+ }),
8699
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
8700
+ className: "dsm-workbuddy-diag-row",
8701
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
8702
+ className: "dsm-workbuddy-diag-label",
8703
+ children: t("row.diagCatalog")
8704
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
8705
+ className: "dsm-workbuddy-diag-value",
8706
+ children: formatNumber(status.models.length)
8707
+ })]
8708
+ }),
8709
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
8710
+ className: "dsm-workbuddy-diag-row",
8711
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
8712
+ className: "dsm-workbuddy-diag-label",
8713
+ children: t("row.diagEnabled")
8714
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
8715
+ className: "dsm-workbuddy-diag-value",
8716
+ children: formatNumber(status.enabledModelIds.length)
8717
+ })]
8718
+ }),
8719
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
8720
+ className: "dsm-workbuddy-diag-row",
8721
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
8722
+ className: "dsm-workbuddy-diag-label",
8723
+ children: t("row.diagPool")
8724
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
8725
+ className: "dsm-workbuddy-diag-value",
8726
+ children: status.pool?.enabled === true ? t("row.diagPoolOn", { count: status.pool.accounts.length }) : t("row.diagPoolOff")
8727
+ })]
8728
+ }),
8729
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
8730
+ className: "dsm-workbuddy-diag-row",
8731
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
8732
+ className: "dsm-workbuddy-diag-label",
8733
+ children: t("row.diagFailures")
8734
+ }), (() => {
8735
+ const failures = [];
8736
+ if (status.credentialRejected === true) failures.push(t("row.credentialRejectedTitle"));
8737
+ if (status.refreshError !== void 0) failures.push(status.refreshError);
8738
+ if (status.creditsError !== void 0) failures.push(status.creditsError);
8739
+ const unreadable = status.models.reduce((count, model) => count + (model.promos ?? []).filter((promo) => promo.schedule?.dailyUnusable === true).length, 0);
8740
+ if (unreadable > 0) failures.push(t("row.diagPromoUnreadable", { count: unreadable }));
8741
+ return failures.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
8742
+ className: "dsm-workbuddy-diag-value",
8743
+ children: t("row.diagNoFailures")
8744
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
8745
+ className: "dsm-workbuddy-diag-value dsm-workbuddy-diag-bad dsm-workbuddy-diag-mono",
8746
+ children: failures.join(" · ")
8747
+ });
8748
+ })()]
8316
8749
  })
8317
8750
  ]
8318
- })
8319
- })
8320
- }) : null,
8321
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
8322
- className: "dsm-workbuddy-panel",
8323
- role: "tabpanel",
8324
- id: "dsm-workbuddy-panel-capabilities",
8325
- "aria-labelledby": "dsm-workbuddy-tab-capabilities",
8326
- hidden: signedIn && panel !== "capabilities",
8327
- children: [
8751
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
8752
+ className: "dsm-workbuddy-diag-hint",
8753
+ children: t("row.diagHint")
8754
+ })] })
8755
+ }),
8756
+ status.status === "signed-out" && loading ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
8757
+ className: "dsm-workbuddy-usage-text",
8758
+ role: "status",
8759
+ children: t("row.accountsLoading")
8760
+ }) : null,
8761
+ status.status === "signed-out" && !loading ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
8328
8762
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
8329
- className: "dsm-workbuddy-panel-note",
8330
- children: t("row.panelCapabilitiesHint")
8763
+ className: "dsm-workbuddy-usage-text",
8764
+ children: signedOutText(notice, t)
8331
8765
  }),
8332
- creditPreferences,
8333
- upstreamSwitches
8334
- ]
8335
- }),
8336
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
8337
- className: "dsm-workbuddy-panel",
8338
- role: "tabpanel",
8339
- id: "dsm-workbuddy-panel-diag",
8340
- "aria-labelledby": "dsm-workbuddy-tab-diag",
8341
- hidden: !signedIn || panel !== "diag",
8342
- children: status.status !== "signed-in" ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
8343
- className: "dsm-workbuddy-diag",
8344
- children: [
8345
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
8346
- className: "dsm-workbuddy-diag-row",
8347
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
8348
- className: "dsm-workbuddy-diag-label",
8349
- children: t("row.diagRegion")
8350
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
8351
- className: "dsm-workbuddy-diag-value",
8352
- children: status.region === "cn" ? t("row.tabCn") : t("row.tabGlobal")
8353
- })]
8354
- }),
8355
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
8356
- className: "dsm-workbuddy-diag-row",
8357
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
8358
- className: "dsm-workbuddy-diag-label",
8359
- children: t("row.diagDomain")
8360
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
8361
- className: "dsm-workbuddy-diag-value dsm-workbuddy-diag-mono",
8362
- children: status.domain === void 0 || status.domain === "" ? t("row.modelUnknown") : status.domain
8363
- })]
8364
- }),
8365
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
8366
- className: "dsm-workbuddy-diag-row",
8367
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
8368
- className: "dsm-workbuddy-diag-label",
8369
- children: t("row.diagAccount")
8370
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
8371
- className: "dsm-workbuddy-diag-value",
8372
- children: nameOf(status.accountName)
8373
- })]
8374
- }),
8375
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
8376
- className: "dsm-workbuddy-diag-row",
8377
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
8378
- className: "dsm-workbuddy-diag-label",
8379
- children: t("row.diagToken")
8380
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
8381
- className: "dsm-workbuddy-diag-value dsm-workbuddy-diag-mono",
8382
- children: formatDate(status.tokenExpiresAtMs)
8383
- })]
8384
- }),
8385
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
8386
- className: "dsm-workbuddy-diag-row",
8387
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
8388
- className: "dsm-workbuddy-diag-label",
8389
- children: t("row.diagCatalog")
8390
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
8391
- className: "dsm-workbuddy-diag-value",
8392
- children: formatNumber(status.models.length)
8393
- })]
8394
- }),
8395
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
8396
- className: "dsm-workbuddy-diag-row",
8397
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
8398
- className: "dsm-workbuddy-diag-label",
8399
- children: t("row.diagEnabled")
8400
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
8401
- className: "dsm-workbuddy-diag-value",
8402
- children: formatNumber(status.enabledModelIds.length)
8403
- })]
8404
- }),
8405
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
8406
- className: "dsm-workbuddy-diag-row",
8407
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
8408
- className: "dsm-workbuddy-diag-label",
8409
- children: t("row.diagPool")
8410
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
8411
- className: "dsm-workbuddy-diag-value",
8412
- children: status.pool?.enabled === true ? t("row.diagPoolOn", { count: status.pool.accounts.length }) : t("row.diagPoolOff")
8413
- })]
8414
- }),
8415
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
8416
- className: "dsm-workbuddy-diag-row",
8417
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
8418
- className: "dsm-workbuddy-diag-label",
8419
- children: t("row.diagFailures")
8420
- }), (() => {
8421
- const failures = [];
8422
- if (status.credentialRejected === true) failures.push(t("row.credentialRejectedTitle"));
8423
- if (status.refreshError !== void 0) failures.push(status.refreshError);
8424
- if (status.creditsError !== void 0) failures.push(status.creditsError);
8425
- return failures.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
8426
- className: "dsm-workbuddy-diag-value",
8427
- children: t("row.diagNoFailures")
8428
- }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
8429
- className: "dsm-workbuddy-diag-value dsm-workbuddy-diag-bad dsm-workbuddy-diag-mono",
8430
- children: failures.join(" · ")
8431
- });
8432
- })()]
8766
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
8767
+ className: "dsm-workbuddy-usage-account-actions",
8768
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
8769
+ type: "button",
8770
+ className: "dsm-btn dsm-btn-outline",
8771
+ disabled: busy,
8772
+ onClick: () => {
8773
+ rescanAccounts();
8774
+ },
8775
+ children: busy ? t("row.accountsScanning") : t("row.accountsRescan")
8433
8776
  })
8434
- ]
8435
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
8436
- className: "dsm-workbuddy-diag-hint",
8437
- children: t("row.diagHint")
8438
- })] })
8439
- }),
8440
- status.status === "signed-out" && loading ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
8441
- className: "dsm-workbuddy-usage-text",
8442
- role: "status",
8443
- children: t("row.accountsLoading")
8444
- }) : null,
8445
- status.status === "signed-out" && !loading ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
8446
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
8447
- className: "dsm-workbuddy-usage-text",
8448
- children: signedOutText(notice, t)
8449
- }),
8450
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
8451
- className: "dsm-workbuddy-usage-account-actions",
8452
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
8453
- type: "button",
8454
- className: "dsm-btn dsm-btn-outline",
8455
- disabled: busy,
8456
- onClick: () => {
8457
- rescanAccounts();
8458
- },
8459
- children: busy ? t("row.accountsScanning") : t("row.accountsRescan")
8460
- })
8461
- }),
8462
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
8463
- className: "dsm-workbuddy-pool-tools",
8464
- children: accountTools
8465
- }),
8466
- searched.length > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SearchedPaths, {
8467
- items: searched,
8468
- t
8777
+ }),
8778
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
8779
+ className: "dsm-workbuddy-pool-tools",
8780
+ children: accountTools
8781
+ }),
8782
+ searched.length > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SearchedPaths, {
8783
+ items: searched,
8784
+ t
8785
+ }) : null
8786
+ ] }) : null,
8787
+ status.status === "error" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
8788
+ className: "dsm-workbuddy-usage-error",
8789
+ children: status.message
8469
8790
  }) : null
8470
- ] }) : null,
8471
- status.status === "error" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
8472
- className: "dsm-workbuddy-usage-error",
8473
- children: status.message
8474
- }) : null
8475
- ]
8791
+ ]
8792
+ })
8476
8793
  }) : null
8477
8794
  })]
8478
8795
  });
@@ -10229,6 +10546,9 @@ window.__ModuleLoader__.load({
10229
10546
  "row.diagPoolOff": "off",
10230
10547
  "row.diagFailures": "Last failure",
10231
10548
  "row.diagNoFailures": "Nothing reported.",
10549
+ "row.diagPromoUnreadable": "{count} promotion hour(s) unreadable — badge hidden",
10550
+ "row.cardCrashed": "The configuration area failed to render.",
10551
+ "row.cardCrashedHint": "The model provider itself is unaffected — chat and credits keep working. Quote the line above in a bug report.",
10232
10552
  "row.modelsTitle": "Models",
10233
10553
  "row.modelsSummary": "{count} enabled",
10234
10554
  "row.modelsDirty": "unsaved changes",
@@ -10515,7 +10835,17 @@ window.__ModuleLoader__.load({
10515
10835
  "row.cliBridgeRegionOrderGlobalOnly": "Global only",
10516
10836
  "row.cliBridgePickersHint": "All three pickers write the global cliBridge block; \"CLI default\" on model and effort is an explicit empty value, not \"unset\" — the CLI then uses its own defaults; the region picker writes cliBridge.regionOrder.",
10517
10837
  "row.imageToolLabel": "Let the model draw images",
10518
- "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.",
10838
+ "row.imageToolHint": "Adds the workbuddy_draw_image tool, which the MODEL may call on its own — about 5.71 credits per image on the CN gateway and about 0.03 on the international one, with no per-call confirmation. Ticking it here only authorizes THIS tab's region. Off everywhere means the tool is not registered at all.",
10839
+ "row.mediaModelsLabel": "Image models in this region",
10840
+ "row.mediaModelsHint": "Read from this account's product configuration. The draw tool picks its model from here: a prompt without an image uses a text-to-image model, one WITH an image uses an image-to-image model. Read-only — to use a different model, pass it to the tool.",
10841
+ "row.mediaModelsEmpty": "This region publishes no image or video model.",
10842
+ "row.mediaModelsError": "Could not read the media model list: {message}",
10843
+ "row.mediaModelsRefresh": "Re-read",
10844
+ "row.mediaModelsRefreshing": "Reading…",
10845
+ "row.mediaTagTextToImage": "text → image",
10846
+ "row.mediaTagImageToImage": "image → image",
10847
+ "row.mediaTagTextToVideo": "text → video",
10848
+ "row.mediaTagImageToVideo": "image → video",
10519
10849
  "row.showCreditsCn": "Show credits in the composer toolbar",
10520
10850
  "row.showCreditsGlobal": "Show credits in the composer toolbar",
10521
10851
  "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 one-click test of the checked accounts (the test spends real credits). The readout refreshes by itself every 5 minutes; the per-account table every 60 seconds while it is open.",
@@ -10641,6 +10971,9 @@ window.__ModuleLoader__.load({
10641
10971
  "row.diagPoolOff": "关闭",
10642
10972
  "row.diagFailures": "最近一次失败",
10643
10973
  "row.diagNoFailures": "没有报错。",
10974
+ "row.diagPromoUnreadable": "{count} 条活动时段读不懂 —— 徽标已隐藏",
10975
+ "row.cardCrashed": "配置区渲染失败。",
10976
+ "row.cardCrashedHint": "模型服务本身未受影响,对话与积分仍可用。反馈问题时请附上上面这行。",
10644
10977
  "row.modelsTitle": "模型",
10645
10978
  "row.modelsSummary": "已启用 {count} 个",
10646
10979
  "row.modelsDirty": "有未保存的修改",
@@ -10927,7 +11260,17 @@ window.__ModuleLoader__.load({
10927
11260
  "row.cliBridgeRegionOrderGlobalOnly": "仅国际",
10928
11261
  "row.cliBridgePickersHint": "三个选择都写入全局 cliBridge 块;模型/档位的\"CLI 默认\"是显式的空值而非未设置,届时 CLI 使用自己的默认;区域选择写入 cliBridge.regionOrder。",
10929
11262
  "row.imageToolLabel": "允许模型自主生图",
10930
- "row.imageToolHint": "开启后模型可以自己调用 workbuddy_draw_image 画图——约 5.71 积分/张,没有逐次确认。关闭时该工具根本不会注册。",
11263
+ "row.imageToolHint": "开启后模型可以自己调用 workbuddy_draw_image 画图——国内区约 5.71、国际区约 0.03 积分/张,没有逐次确认。在这里勾选只授权当前这一区的账号。两区都关着时该工具根本不会注册。",
11264
+ "row.mediaModelsLabel": "本区可用的图像模型",
11265
+ "row.mediaModelsHint": "读自当前账号的产品配置。生图工具就按这里挑模型:不填图片走「文生图」那一组,填了图片走「图生图」那一组。这里是只读的——想换模型请在调用工具时指定。",
11266
+ "row.mediaModelsEmpty": "这一区的产品配置里没有图像或视频模型。",
11267
+ "row.mediaModelsError": "读取媒体模型清单失败:{message}",
11268
+ "row.mediaModelsRefresh": "重新读取",
11269
+ "row.mediaModelsRefreshing": "正在读取…",
11270
+ "row.mediaTagTextToImage": "文生图",
11271
+ "row.mediaTagImageToImage": "图生图",
11272
+ "row.mediaTagTextToVideo": "文生视频",
11273
+ "row.mediaTagImageToVideo": "图生视频",
10931
11274
  "row.showCreditsCn": "在输入框工具栏显示积分读数",
10932
11275
  "row.showCreditsGlobal": "在输入框工具栏显示积分读数",
10933
11276
  "row.showCreditsCnHint": "在对话输入框工具栏显示国内版(WB CN)剩余积分,仅当本会话使用 WB CN 模型时出现。点击可看各账号余额并一键测试勾选的账号(测试会真实消耗积分)。读数每 5 分钟自动刷新;各账号明细在面板打开时每 60 秒刷新。",