dsh-connect-trae 3.4.2 → 3.5.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
@@ -203,6 +203,39 @@ window.__ModuleLoader__.load({
203
203
  });
204
204
  }
205
205
  /**
206
+ * Which seat one region's readout uses, defaulting to the composer row.
207
+ *
208
+ * Anything other than an explicit `'stats'` reads as `'composer'`, which covers
209
+ * the two cases that matter: a profile written BEFORE this setting existed (the
210
+ * composer row was the only seat, so an upgrade moves nothing on screen), and a
211
+ * hand-edited value the schema does not know. Falling back rather than
212
+ * rendering nowhere is deliberate — an unrecognized value must not make the
213
+ * readout disappear, which is the very defect this setting exists to fix.
214
+ *
215
+ * `value` may be the whole settings section or the `regions` map (see
216
+ * {@link regionsMapOf}).
217
+ */
218
+ function regionCreditPositionOf(value, region) {
219
+ return regionSlotOf(value, region)["creditPosition"] === "stats" ? "stats" : "composer";
220
+ }
221
+ /**
222
+ * Build the next `regions` settings value for one region's credit seat.
223
+ *
224
+ * Same merge rule as every sibling region write: only the target region's
225
+ * `creditPosition` changes, so a seat change cannot drop that slot's model
226
+ * directory, selection, budgets or enabled flag, nor touch the other region.
227
+ *
228
+ * `value` may be the whole settings section or the `regions` map; the RETURN
229
+ * value is always the `regions` map, i.e. exactly what `settingsScope.set(
230
+ * 'regions', ...)` needs.
231
+ */
232
+ function nextRegionCreditPosition(value, region, position) {
233
+ return nextRegionSlots(regionsMapOf(value), region, {
234
+ ...regionSlotOf(value, region),
235
+ creditPosition: position
236
+ });
237
+ }
238
+ /**
206
239
  * Whether Trae's plan identity says this account is on a paying plan.
207
240
  *
208
241
  * `has_package` alone is NOT that question — it reports whether a package
@@ -299,6 +332,16 @@ window.__ModuleLoader__.load({
299
332
  .dsm-trae-pref b{font-weight:500;color:var(--dsw-alias-label-primary,#e6e6e6)}
300
333
  .dsm-trae-pref-hint{color:var(--dsw-alias-label-tertiary,#9aa0a8)}
301
334
  .dsm-trae-pref span{min-width:0}
335
+ /* The credit readout's seat picker (learned from workbuddy #62). Its OWN label
336
+ class, deliberately not the plain .dsm-trae-pref the two checkboxes use: a
337
+ query for "the composer-credits switch" must not be able to pick this control
338
+ up, because the two are different widgets and a test selecting by position
339
+ would otherwise start asserting about a <select>.
340
+ The select keeps its own class for the same reason, and takes the checkbox
341
+ column's width so it reads as a sibling of the box rather than a stray row. */
342
+ .dsm-trae-credit-position-pref{align-items:flex-start}
343
+ .dsm-trae-credit-position{flex:none;max-width:150px;margin-top:-1px;padding:2px 6px;font-size:12px}
344
+ .dsm-trae-credit-position:disabled{cursor:default}
302
345
  /* The card's checkboxes are the platform's own, styled only with
303
346
  accent-color 鈥?the SAME idiom .dsm-trae-mainui-switch-label above already
304
347
  uses for the composer-line switch, and the one the sibling
@@ -478,6 +521,51 @@ window.__ModuleLoader__.load({
478
521
  an error: it is Trae's one-per-device-per-day rule, and the day is simply
479
522
  spent. Rendering it in the error colour would read as a plugin fault. */
480
523
  .dsm-trae-checkin-note{margin:8px 0 0;font-size:12px;line-height:18px;color:var(--dsw-alias-label-secondary,#c6c9d0)}
524
+ /* The busy veil: one overlay for every account write whose result only lands
525
+ after the Host re-reads its settings document. Fixed so it covers the card
526
+ no matter which section started the operation, and translucent so the table
527
+ stays visible underneath — the user is watching a specific row change, and
528
+ hiding it would defeat the point.
529
+
530
+ The mask and card surface are the HOST's own tokens, not this file's
531
+ invention. --dsw-alias-bg-mask-1 + --dsw-mask-blur is exactly what the
532
+ host's own overlays use, so the veil dims the way every other overlay in the
533
+ shell does instead of laying its own black wash on top. A hardcoded flat
534
+ black mask (e.g. a mid-transparency black) was tuned against neither theme:
535
+ too weak next to the host's .24 mask in light mode, and it ignored the
536
+ host's blur entirely.
537
+
538
+ bg-layer-2 (not bg-primary, which does not exist in the host's token set at
539
+ all and therefore always fell through to this file's near-black #1b1c20) is
540
+ the same surface the host puts behind its panels — that near-black fallback
541
+ is what made the veil read as a black slab with unreadable text on it. */
542
+ .dsm-trae-veil{position:fixed;inset:0;z-index:60;display:flex;align-items:center;justify-content:center;background:var(--dsw-alias-bg-mask-1,rgba(0,0,0,.32));backdrop-filter:var(--dsw-mask-blur);cursor:progress}
543
+ /* --dsw-elevation-prominent is the host's own panel shadow (a hairline stroke
544
+ plus two soft layers); a hand-rolled drop shadow read as a heavy grey box. */
545
+ .dsm-trae-veil-card{display:flex;flex-direction:column;align-items:center;gap:8px;max-width:min(340px,80vw);padding:16px 20px;border:1px solid var(--dsw-alias-border-l2,#0000001a);border-radius:12px;background:var(--dsw-alias-bg-layer-2,#fff);box-shadow:var(--dsw-elevation-prominent,0 8px 28px rgba(0,0,0,.35))}
546
+ .dsm-trae-veil-text{font-size:13px;line-height:20px;color:var(--dsw-alias-label-primary,#0f1115);text-align:center}
547
+ /* label-secondary, not label-tertiary: this line carries the operation's
548
+ detail and must stay legible. tertiary is the host's dimmest tier
549
+ (neutral-bluish-600 on light, neutral-bluish-400 on dark) and is meant for
550
+ metadata, not for a sentence the user has to read while waiting. */
551
+ .dsm-trae-veil-hint{font-size:12px;line-height:18px;color:var(--dsw-alias-label-secondary,#61666b);text-align:center}
552
+ /* The spinner proves the UI is alive when the row under it has not changed yet
553
+ — which is precisely the confusion the veil exists to remove. It follows the
554
+ host's own state spinner (dsh's _dsh-state-dot-spin): an SVG arc whose dash
555
+ breathes while it turns, currentColor so it inherits the tier it sits in,
556
+ and the same 1.5s beat. The previous 18px border-ring at .7s was roughly
557
+ twice the host's speed and visibly out of step with every other spinner in
558
+ the shell — that mismatch, more than the shape itself, is what made it look
559
+ cheap.
560
+
561
+ Reduced-motion users get the static arc rather than a frozen ring, matching
562
+ how the host degrades its own spinner. */
563
+ .dsm-trae-veil-spinner{width:20px;height:20px;color:var(--dsw-alias-label-secondary,#61666b);animation:dsm-trae-veil-spin 1.5s linear infinite}
564
+ .dsm-trae-veil-spinner-track{opacity:.25}
565
+ .dsm-trae-veil-spinner-arc{stroke-dasharray:12 150;animation:dsm-trae-veil-dash 1.5s ease-in-out infinite}
566
+ @keyframes dsm-trae-veil-spin{to{transform:rotate(360deg)}}
567
+ @keyframes dsm-trae-veil-dash{0%{stroke-dasharray:12 150;stroke-dashoffset:0}50%{stroke-dasharray:24 150;stroke-dashoffset:-6}to{stroke-dasharray:12 150;stroke-dashoffset:0}}
568
+ @media (prefers-reduced-motion: reduce){.dsm-trae-veil-spinner{animation:none}.dsm-trae-veil-spinner-arc{animation:none;stroke-dasharray:18 150;stroke-dashoffset:-3}}
481
569
  `;
482
570
  /**
483
571
  * Composer-row readout styles, injected by the readout itself.
@@ -626,6 +714,76 @@ window.__ModuleLoader__.load({
626
714
  }
627
715
  .dsm-trae-expiry-detail{font-size:11px;line-height:16px;opacity:.85}
628
716
  `;
717
+ //#endregion
718
+ //#region src/client/BusyOverlay.tsx
719
+ /**
720
+ * The shared busy veil's spinner: an arc that breathes while it turns.
721
+ *
722
+ * Deliberately the host's own state-spinner shape (dsh's `_dsh-state-dot-spin`)
723
+ * rather than a border-ring: a two-tone CSS ring at .7s read as a loading
724
+ * indicator from another decade and was out of step with every spinner in the
725
+ * shell. The motion lives in CSS (`dsm-trae-veil-spin` / `-dash`), so this
726
+ * element only describes the geometry; `currentColor` lets it inherit whatever
727
+ * tier it sits in.
728
+ *
729
+ * `aria-hidden` because the veil's `role="status"` already announces the
730
+ * message — a second announcement from a decorative arc is noise.
731
+ */
732
+ function VeilSpinner() {
733
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("svg", {
734
+ className: "dsm-trae-veil-spinner",
735
+ viewBox: "0 0 20 20",
736
+ "aria-hidden": "true",
737
+ focusable: "false",
738
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("circle", {
739
+ className: "dsm-trae-veil-spinner-track",
740
+ cx: "10",
741
+ cy: "10",
742
+ r: "8",
743
+ fill: "none",
744
+ stroke: "currentColor",
745
+ strokeWidth: "2"
746
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("circle", {
747
+ className: "dsm-trae-veil-spinner-arc",
748
+ cx: "10",
749
+ cy: "10",
750
+ r: "8",
751
+ fill: "none",
752
+ stroke: "currentColor",
753
+ strokeWidth: "2",
754
+ strokeLinecap: "round"
755
+ })]
756
+ });
757
+ }
758
+ /**
759
+ * The veil. Deliberately NOT a modal dialog: it carries no focusable control
760
+ * and no dismissal — there is nothing for the user to decide, only a step to
761
+ * wait through. `aria-busy` plus a polite live region state that to screen
762
+ * readers, which is the whole accessibility contract a wait screen needs.
763
+ */
764
+ function BusyOverlay({ message, hint, visible }) {
765
+ if (!visible) return null;
766
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
767
+ className: "dsm-trae-veil",
768
+ role: "status",
769
+ "aria-busy": "true",
770
+ "aria-live": "polite",
771
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
772
+ className: "dsm-trae-veil-card",
773
+ children: [
774
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(VeilSpinner, {}),
775
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
776
+ className: "dsm-trae-veil-text",
777
+ children: message
778
+ }),
779
+ hint === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
780
+ className: "dsm-trae-veil-hint",
781
+ children: hint
782
+ })
783
+ ]
784
+ })
785
+ });
786
+ }
629
787
  //#endregion
630
788
  //#region src/client/plan-text.ts
631
789
  /**
@@ -1008,6 +1166,49 @@ window.__ModuleLoader__.load({
1008
1166
  setSettingsRevision((value) => value + 1);
1009
1167
  }), [settingsScope]);
1010
1168
  /**
1169
+ * WHERE the active tab's credit readout sits (learned from workbuddy #62).
1170
+ *
1171
+ * Read from the committed SETTINGS document, through the same
1172
+ * `regionCreditPositionOf` helper the Host uses — so the control reads
1173
+ * instantly and can never disagree with the readout's own gate, which reads
1174
+ * the same scope. This mirrors how the show/hide box beside it works
1175
+ * (`committedSettings`), rather than how the sibling workbuddy card does it:
1176
+ * that card reads the Host's usage answer because a measured defect on its
1177
+ * 0.1.7 deployments left the browser settings mirror for its namespace STALE.
1178
+ * No such staleness is reproducible here, and reading the mirror eagerly is
1179
+ * what keeps this control correct on first paint — a picker fed only by the
1180
+ * usage document would sit on the default until a fetch landed, which is the
1181
+ * "looks like it did nothing" failure this control must not have.
1182
+ *
1183
+ * `settingsRevision` is read through the setter above, and the whole block
1184
+ * re-renders when the scope notifies, so a write made elsewhere (another
1185
+ * window, the DSH settings form) converges here.
1186
+ */
1187
+ const creditPositionOn = (item) => regionCreditPositionOf(settingsScope?.getSnapshot().value, item);
1188
+ /**
1189
+ * Move one region's credit readout between its two seats.
1190
+ *
1191
+ * Same shape as every other region write on this card: the whole target slot
1192
+ * goes in (so a seat change cannot drop that region's model picks, budgets or
1193
+ * enabled flag, nor touch the sibling region), the write is read-back
1194
+ * verified, and the Host is re-read afterwards. The re-read is load-bearing
1195
+ * here — the control renders the Host's answer, so without it the picker
1196
+ * keeps showing the seat it had and the change looks like it did nothing.
1197
+ */
1198
+ const changeCreditPosition = async (item, position) => {
1199
+ if (settingsScope === void 0 || settingsScope.getSnapshot().writable !== true) return;
1200
+ setSavingSettings(true);
1201
+ setWriteError(void 0);
1202
+ try {
1203
+ await writeSettingsField(settingsScope, "regions", nextRegionCreditPosition(settingsScope.getSnapshot().value, item, position), (readBack) => regionCreditPositionOf(readBack, item) === position);
1204
+ await refreshUsage(item);
1205
+ } catch (error) {
1206
+ if (mounted.current) setWriteError(error instanceof Error ? error.message : t("row.requestFailed"));
1207
+ } finally {
1208
+ if (mounted.current) setSavingSettings(false);
1209
+ }
1210
+ };
1211
+ /**
1011
1212
  * Whether one region's provider is switched on, read off the SAME committed
1012
1213
  * settings document the Host reads (`regionEnabledOf` mirrors the Host's
1013
1214
  * `regionStateOf` opt-out rule: only an explicit `false` disables). Reading
@@ -1706,539 +1907,576 @@ window.__ModuleLoader__.load({
1706
1907
  const label = status.status === "signed-in" ? t("row.signedIn", { accountName: status.accountName }) : status.status === "error" ? t("row.requestFailed") : t("row.signedOut");
1707
1908
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("li", {
1708
1909
  className: `dsm-plugin-card${open ? " dsm-plugin-card-open" : ""}`,
1709
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
1710
- type: "button",
1711
- className: "dsm-plugin-card-header",
1712
- "aria-expanded": open,
1713
- "aria-label": `${t(open ? "row.collapse" : "row.expand")}: ${title}`,
1714
- onClick: () => {
1715
- setOpen(!open);
1716
- },
1717
- children: [
1718
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("img", {
1719
- className: "dsm-plugin-card-icon",
1720
- src: TRAE_PLUGIN_ICON,
1721
- alt: ""
1722
- }),
1723
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
1724
- className: "dsm-plugin-card-head",
1725
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1726
- className: "dsm-plugin-card-title",
1727
- children: title
1728
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1729
- className: "dsm-plugin-card-description",
1730
- children: t("row.desc")
1731
- })]
1732
- }),
1733
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1734
- "aria-hidden": "true",
1735
- className: `dsm-plugin-card-chevron${open ? " dsm-plugin-card-chevron-open" : ""}`
1736
- })
1737
- ]
1738
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1739
- className: "dsm-plugin-card-body",
1740
- hidden: !open,
1741
- children: open ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1742
- className: "dsm-trae-usage",
1910
+ children: [
1911
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
1912
+ type: "button",
1913
+ className: "dsm-plugin-card-header",
1914
+ "aria-expanded": open,
1915
+ "aria-label": `${t(open ? "row.collapse" : "row.expand")}: ${title}`,
1916
+ onClick: () => {
1917
+ setOpen(!open);
1918
+ },
1743
1919
  children: [
1744
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1745
- className: "dsm-trae-tabs",
1746
- role: "tablist",
1747
- "aria-label": title,
1748
- children: TRAE_REGIONS.map((item) => {
1749
- const regionStatus = statusByRegion[item];
1750
- const on = regionOn(item);
1751
- return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1752
- className: "dsm-trae-tab-cell",
1753
- role: "presentation",
1754
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
1755
- type: "button",
1756
- role: "tab",
1757
- "aria-selected": item === activeRegion,
1758
- className: `dsm-trae-tab${item === activeRegion ? " dsm-trae-tab-active" : ""}${on ? "" : " dsm-trae-tab-off"}`,
1759
- onClick: () => {
1760
- setActiveRegion(item);
1761
- },
1762
- children: [regionStatus === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1763
- "aria-hidden": "true",
1764
- className: "dsm-trae-tab-dot",
1765
- style: dotStyle(regionStatus.status)
1766
- }), item === "cn" ? t("row.tabCn") : t("row.tabAi")]
1767
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("label", {
1768
- className: "dsm-trae-switch dsm-trae-tab-switch",
1769
- title: t("row.tabSwitchHint"),
1770
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
1771
- type: "checkbox",
1772
- checked: on,
1773
- disabled: settingsScope?.getSnapshot().writable !== true || togglingRegion !== void 0,
1774
- "aria-label": t("row.tabSwitchAria", { region: item === "cn" ? t("row.tabCn") : t("row.tabAi") }),
1775
- onChange: (event) => {
1776
- toggleRegion(item, event.currentTarget.checked);
1777
- }
1778
- })
1779
- })]
1780
- }, item);
1781
- })
1782
- }),
1783
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1784
- className: "dsm-trae-tab-hint",
1785
- children: t("row.tabHint")
1920
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("img", {
1921
+ className: "dsm-plugin-card-icon",
1922
+ src: TRAE_PLUGIN_ICON,
1923
+ alt: ""
1786
1924
  }),
1787
- writeError === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1788
- className: "dsm-trae-usage-error",
1789
- role: "alert",
1790
- children: t("row.settingsWriteFailed", { message: writeError })
1791
- }),
1792
- regionOn(activeRegion) ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1793
- className: "dsm-trae-tab-off-notice",
1794
- role: "status",
1795
- children: t("row.tabOffNotice")
1796
- }),
1797
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1798
- className: "dsm-trae-prefs",
1799
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
1800
- className: "dsm-trae-pref",
1801
- title: t("row.followAppLoginHint"),
1802
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
1803
- type: "checkbox",
1804
- checked: settingsEdits.followAppLogin,
1805
- disabled: settingsScope?.getSnapshot().writable !== true || savingSettings,
1806
- "aria-describedby": followHintId,
1807
- onChange: (event) => {
1808
- writePreference("followAppLogin", event.currentTarget.checked);
1809
- }
1810
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
1811
- id: followHintId,
1812
- children: [
1813
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("b", {
1814
- id: followLabelId,
1815
- children: t("row.followAppLogin")
1816
- }),
1817
- " ",
1818
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1819
- className: "dsm-trae-pref-hint",
1820
- children: t("row.followAppLoginHint")
1821
- })
1822
- ]
1823
- })]
1824
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
1825
- className: "dsm-trae-pref",
1826
- title: t(activeRegion === "cn" ? "row.showPointsInMainUiHint" : "row.showPointsInMainUiHintAi"),
1827
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
1828
- type: "checkbox",
1829
- checked: settingsEdits.showPointsInMainUi,
1830
- disabled: settingsScope?.getSnapshot().writable !== true || savingSettings,
1831
- "aria-describedby": creditsHintId,
1832
- onChange: (event) => {
1833
- writePreference("showPointsInMainUi", event.currentTarget.checked);
1834
- }
1835
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
1836
- id: creditsHintId,
1837
- children: [
1838
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("b", {
1839
- id: creditsLabelId,
1840
- children: t("row.showPointsInMainUi")
1841
- }),
1842
- " ",
1843
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1844
- className: "dsm-trae-pref-hint",
1845
- children: t(activeRegion === "cn" ? "row.showPointsInMainUiHint" : "row.showPointsInMainUiHintAi")
1846
- })
1847
- ]
1848
- })]
1925
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
1926
+ className: "dsm-plugin-card-head",
1927
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1928
+ className: "dsm-plugin-card-title",
1929
+ children: title
1930
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1931
+ className: "dsm-plugin-card-description",
1932
+ children: t("row.desc")
1849
1933
  })]
1850
1934
  }),
1851
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
1852
- className: "dsm-trae-accounts",
1853
- "aria-label": t("row.accountsTitle"),
1854
- children: [
1855
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1856
- className: "dsm-trae-accounts-head",
1857
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", {
1858
- className: "dsm-trae-accounts-title",
1859
- children: t("row.accountsTitle")
1860
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1861
- className: "dsm-trae-accounts-hint",
1862
- children: t("row.accountsHint")
1863
- })] }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
1864
- type: "button",
1865
- className: "dsm-btn dsm-btn-outline",
1866
- disabled: busy,
1867
- onClick: () => {
1868
- rescanAccounts();
1869
- },
1870
- children: busy ? t("row.accountsScanning") : t("row.refreshTokens")
1871
- })]
1872
- }),
1873
- status.status !== "signed-in" || status.accounts.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1874
- className: "dsm-trae-usage-account",
1875
- role: "status",
1876
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1877
- className: "dsm-trae-usage-account-copy",
1878
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1879
- className: "dsm-trae-usage-status",
1880
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1935
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1936
+ "aria-hidden": "true",
1937
+ className: `dsm-plugin-card-chevron${open ? " dsm-plugin-card-chevron-open" : ""}`
1938
+ })
1939
+ ]
1940
+ }),
1941
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1942
+ className: "dsm-plugin-card-body",
1943
+ hidden: !open,
1944
+ children: open ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1945
+ className: "dsm-trae-usage",
1946
+ children: [
1947
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1948
+ className: "dsm-trae-tabs",
1949
+ role: "tablist",
1950
+ "aria-label": title,
1951
+ children: TRAE_REGIONS.map((item) => {
1952
+ const regionStatus = statusByRegion[item];
1953
+ const on = regionOn(item);
1954
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1955
+ className: "dsm-trae-tab-cell",
1956
+ role: "presentation",
1957
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
1958
+ type: "button",
1959
+ role: "tab",
1960
+ "aria-selected": item === activeRegion,
1961
+ className: `dsm-trae-tab${item === activeRegion ? " dsm-trae-tab-active" : ""}${on ? "" : " dsm-trae-tab-off"}`,
1962
+ onClick: () => {
1963
+ setActiveRegion(item);
1964
+ },
1965
+ children: [regionStatus === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1881
1966
  "aria-hidden": "true",
1882
- className: "dsm-trae-usage-dot",
1883
- style: dotStyle(status.status)
1884
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: label })]
1885
- }), status.status === "signed-in" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1886
- className: "dsm-trae-usage-expiry",
1887
- children: t("row.tokenExpiry", { expiresAt: formatDateTime(status.tokenExpiresAtMs) })
1888
- }) : null]
1889
- })
1890
- }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1891
- className: `dsm-trae-account-table${activeRegion === "cn" ? " dsm-trae-account-table-checkin" : ""}`,
1892
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1893
- className: "dsm-trae-account-row dsm-trae-account-row-head",
1894
- role: "row",
1895
- children: [
1896
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("row.accountColumnAccount") }),
1897
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("row.accountColumnValue") }),
1898
- activeRegion === "cn" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("row.accountColumnCheckin") }) : null,
1899
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {})
1900
- ]
1901
- }), status.accounts.map((account) => {
1902
- const current = status.status === "signed-in" && account.id === status.accountId;
1903
- const state = accountStatusByRegion[activeRegion][account.id];
1904
- const error = claimErrorByRegion[activeRegion][account.id];
1905
- return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1906
- className: `dsm-trae-account-row${current ? " dsm-trae-account-row-current" : ""}`,
1907
- role: "row",
1967
+ className: "dsm-trae-tab-dot",
1968
+ style: dotStyle(regionStatus.status)
1969
+ }), item === "cn" ? t("row.tabCn") : t("row.tabAi")]
1970
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("label", {
1971
+ className: "dsm-trae-switch dsm-trae-tab-switch",
1972
+ title: t("row.tabSwitchHint"),
1973
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
1974
+ type: "checkbox",
1975
+ checked: on,
1976
+ disabled: settingsScope?.getSnapshot().writable !== true || togglingRegion !== void 0,
1977
+ "aria-label": t("row.tabSwitchAria", { region: item === "cn" ? t("row.tabCn") : t("row.tabAi") }),
1978
+ onChange: (event) => {
1979
+ toggleRegion(item, event.currentTarget.checked);
1980
+ }
1981
+ })
1982
+ })]
1983
+ }, item);
1984
+ })
1985
+ }),
1986
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1987
+ className: "dsm-trae-tab-hint",
1988
+ children: t("row.tabHint")
1989
+ }),
1990
+ writeError === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1991
+ className: "dsm-trae-usage-error",
1992
+ role: "alert",
1993
+ children: t("row.settingsWriteFailed", { message: writeError })
1994
+ }),
1995
+ regionOn(activeRegion) ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1996
+ className: "dsm-trae-tab-off-notice",
1997
+ role: "status",
1998
+ children: t("row.tabOffNotice")
1999
+ }),
2000
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2001
+ className: "dsm-trae-prefs",
2002
+ children: [
2003
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
2004
+ className: "dsm-trae-pref",
2005
+ title: t("row.followAppLoginHint"),
2006
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
2007
+ type: "checkbox",
2008
+ checked: settingsEdits.followAppLogin,
2009
+ disabled: settingsScope?.getSnapshot().writable !== true || savingSettings,
2010
+ "aria-describedby": followHintId,
2011
+ onChange: (event) => {
2012
+ writePreference("followAppLogin", event.currentTarget.checked);
2013
+ }
2014
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
2015
+ id: followHintId,
1908
2016
  children: [
1909
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
1910
- className: "dsm-trae-account-who",
1911
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1912
- "aria-hidden": "true",
1913
- className: "dsm-trae-usage-dot",
1914
- style: dotStyle(account.selected ? "signed-in" : "signed-out")
1915
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
1916
- className: "dsm-trae-account-name",
1917
- children: [
1918
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("b", { children: account.accountName }),
1919
- current ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1920
- className: "dsm-trae-account-badge",
1921
- children: t("row.accountCurrentBadge")
1922
- }) : null,
1923
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
1924
- className: "dsm-trae-account-meta",
1925
- children: [
1926
- editionLabel(account.edition),
1927
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("i", {
1928
- "aria-hidden": "true",
1929
- children: "·"
1930
- }),
1931
- sourceLabel(account.source)
1932
- ]
1933
- }),
1934
- error === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1935
- className: "dsm-trae-account-note",
1936
- role: "alert",
1937
- children: t("row.checkinError", { message: error })
1938
- })
1939
- ]
1940
- })]
2017
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("b", {
2018
+ id: followLabelId,
2019
+ children: t("row.followAppLogin")
1941
2020
  }),
2021
+ " ",
1942
2022
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1943
- className: "dsm-trae-account-worth",
1944
- children: rowValue(state)
2023
+ className: "dsm-trae-pref-hint",
2024
+ children: t("row.followAppLoginHint")
2025
+ })
2026
+ ]
2027
+ })]
2028
+ }),
2029
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
2030
+ className: "dsm-trae-pref",
2031
+ title: t(activeRegion === "cn" ? "row.showPointsInMainUiHint" : "row.showPointsInMainUiHintAi"),
2032
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
2033
+ type: "checkbox",
2034
+ checked: settingsEdits.showPointsInMainUi,
2035
+ disabled: settingsScope?.getSnapshot().writable !== true || savingSettings,
2036
+ "aria-describedby": creditsHintId,
2037
+ onChange: (event) => {
2038
+ writePreference("showPointsInMainUi", event.currentTarget.checked);
2039
+ }
2040
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
2041
+ id: creditsHintId,
2042
+ children: [
2043
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("b", {
2044
+ id: creditsLabelId,
2045
+ children: t("row.showPointsInMainUi")
1945
2046
  }),
1946
- activeRegion === "cn" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1947
- className: "dsm-trae-account-checkin-cell",
1948
- children: checkinControl(account, state)
1949
- }) : null,
2047
+ " ",
1950
2048
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1951
- className: "dsm-trae-account-actions",
1952
- children: current ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
1953
- type: "button",
1954
- className: "dsm-btn dsm-btn-outline dsm-trae-account-switch",
1955
- disabled: switchingAccount || settingsScope?.getSnapshot()?.writable !== true,
1956
- onClick: () => {
1957
- switchAccount(account.id);
1958
- },
1959
- children: switchingAccount ? t("row.accountSwitching") : t("row.accountSwitch")
1960
- })
2049
+ className: "dsm-trae-pref-hint",
2050
+ children: t(activeRegion === "cn" ? "row.showPointsInMainUiHint" : "row.showPointsInMainUiHintAi")
1961
2051
  })
1962
2052
  ]
1963
- }, account.id);
1964
- })]
1965
- }),
1966
- status.status === "signed-in" && status.alternatives !== void 0 && status.alternatives.length > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1967
- className: "dsm-trae-usage-alternative",
1968
- role: "status",
1969
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("p", {
1970
- className: "dsm-trae-usage-alternative-text",
1971
- children: [
1972
- t("row.creditsExhausted", { account: status.accountName }),
2053
+ })]
2054
+ }),
2055
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
2056
+ className: "dsm-trae-pref dsm-trae-credit-position-pref",
2057
+ title: t("row.creditPositionHint"),
2058
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
2059
+ className: "dsm-trae-credit-position",
2060
+ value: activeRegionOn ? creditPositionOn(activeRegion) : "composer",
2061
+ disabled: !activeRegionOn || settingsScope?.getSnapshot().writable !== true || savingSettings,
2062
+ "aria-label": t("row.creditPositionAria", { region: activeRegion === "cn" ? t("row.tabCn") : t("row.tabAi") }),
2063
+ onChange: (event) => {
2064
+ changeCreditPosition(activeRegion, event.currentTarget.value === "stats" ? "stats" : "composer");
2065
+ },
2066
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
2067
+ value: "composer",
2068
+ children: t("row.creditPositionComposer")
2069
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
2070
+ value: "stats",
2071
+ children: t("row.creditPositionStats")
2072
+ })]
2073
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: [
2074
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("b", { children: t("row.creditPositionLabel") }),
1973
2075
  " ",
1974
- t("row.creditsAlternative", { accounts: status.alternatives.map((alt) => `${alt.accountName}(${t(`row.edition${alt.edition === "cn" ? "Cn" : alt.edition === "sg" ? "Sg" : alt.edition === "solo" ? "Solo" : "SoloSg"}`)}:${formatNumber(alt.workAvailable + alt.generalAvailable)})`).join("、") })
1975
- ]
1976
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
1977
- type: "button",
1978
- className: "dsm-trae-usage-switch-button",
1979
- disabled: switchingAccount || settingsScope?.getSnapshot()?.writable !== true,
1980
- onClick: () => {
1981
- const target = status.alternatives?.[0];
1982
- if (target !== void 0) switchAccount(target.id);
1983
- },
1984
- children: t("row.creditsAlternativeSwitch")
1985
- })]
1986
- }) : null,
1987
- status.status === "signed-in" && status.credits?.expiry !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ExpiryNotice, {
1988
- expiry: status.credits.expiry,
1989
- t,
1990
- formatNumber,
1991
- formatDateTime
1992
- }) : null,
1993
- status.status === "signed-in" && status.appLogin !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1994
- className: "dsm-trae-usage-alternative",
1995
- role: "status",
1996
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1997
- className: "dsm-trae-usage-alternative-text",
1998
- children: t("row.appLoginMismatch", {
1999
- account: status.appLogin.accountName,
2000
- bound: status.accountName
2001
- })
2002
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
2003
- type: "button",
2004
- className: "dsm-trae-usage-switch-button",
2005
- disabled: switchingAccount || settingsScope?.getSnapshot()?.writable !== true,
2006
- onClick: () => {
2007
- followAppLogin();
2008
- },
2009
- children: t("row.appLoginFollow", { account: status.appLogin.accountName })
2010
- })]
2011
- }) : null,
2012
- rescanNote === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
2013
- className: "dsm-trae-usage-note",
2014
- role: "status",
2015
- children: rescanNote
2016
- })
2017
- ]
2018
- }),
2019
- status.status === "signed-in" ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
2020
- status.checkin !== void 0 && !status.checkin.enabled ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
2021
- className: "dsm-trae-usage-error",
2022
- children: t("row.checkinDisabled")
2023
- }) : null,
2024
- status.checkinError === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
2025
- className: "dsm-trae-usage-error",
2026
- children: t("row.checkinError", { message: status.checkinError })
2076
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2077
+ className: "dsm-trae-pref-hint",
2078
+ children: t("row.creditPositionHint")
2079
+ })
2080
+ ] })]
2081
+ })
2082
+ ]
2027
2083
  }),
2028
2084
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
2029
- className: "dsm-trae-models",
2030
- "aria-label": t("row.modelsTitle"),
2085
+ className: "dsm-trae-accounts",
2086
+ "aria-label": t("row.accountsTitle"),
2031
2087
  children: [
2032
2088
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2033
- className: "dsm-trae-models-head",
2089
+ className: "dsm-trae-accounts-head",
2034
2090
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", {
2035
- className: "dsm-trae-models-title",
2036
- children: t("row.modelsTitle")
2091
+ className: "dsm-trae-accounts-title",
2092
+ children: t("row.accountsTitle")
2037
2093
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
2038
- className: "dsm-trae-models-summary",
2039
- children: t("row.modelsSummary", { count: activeEnabledIds.size })
2040
- })] }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2041
- className: "dsm-trae-models-actions",
2042
- children: [
2043
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
2044
- type: "button",
2045
- className: "dsm-btn dsm-btn-outline",
2046
- disabled: busy,
2047
- onClick: () => {
2048
- refreshModels();
2049
- },
2050
- children: busy ? t("row.modelsRefreshing") : t("row.modelsRefresh")
2051
- }),
2052
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
2053
- type: "button",
2054
- className: "dsm-btn dsm-btn-outline",
2055
- disabled: busy || testing !== void 0 || activeEnabledIds.size === 0,
2056
- title: t("row.modelsTestHint"),
2057
- onClick: () => {
2058
- testEnabledModels();
2059
- },
2060
- children: testing === void 0 ? t("row.modelsTestEnabled") : t("row.modelsTesting", {
2061
- done: testing.done,
2062
- total: testing.total
2063
- })
2064
- }),
2065
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
2066
- type: "button",
2067
- className: "dsm-btn dsm-btn-outline",
2068
- disabled: busy || testing !== void 0 || visibleModels.length === 0,
2069
- title: t("row.modelsTestAllHint"),
2070
- onClick: () => {
2071
- testAllModels();
2072
- },
2073
- children: testing === void 0 ? t("row.modelsTestAll") : t("row.modelsTesting", {
2074
- done: testing.done,
2075
- total: testing.total
2076
- })
2077
- })
2078
- ]
2094
+ className: "dsm-trae-accounts-hint",
2095
+ children: t("row.accountsHint")
2096
+ })] }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
2097
+ type: "button",
2098
+ className: "dsm-btn dsm-btn-outline",
2099
+ disabled: busy,
2100
+ onClick: () => {
2101
+ rescanAccounts();
2102
+ },
2103
+ children: busy ? t("row.accountsScanning") : t("row.refreshTokens")
2079
2104
  })]
2080
2105
  }),
2081
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
2082
- className: "dsm-trae-models-hint",
2083
- children: t("row.modelsTestHint")
2084
- }),
2085
- testSummary === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
2086
- className: "dsm-trae-models-summary",
2087
- children: testSummary
2088
- }),
2089
- testError === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
2090
- className: "dsm-trae-usage-error",
2091
- role: "alert",
2092
- children: testError
2093
- }),
2094
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2095
- className: "dsm-trae-model-list",
2096
- children: visibleModels.map((model) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2097
- className: `dsm-trae-model${activeEnabledIds.has(model.id) ? "" : " dsm-trae-model-disabled"}`,
2106
+ status.status !== "signed-in" || status.accounts.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2107
+ className: "dsm-trae-usage-account",
2108
+ role: "status",
2109
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2110
+ className: "dsm-trae-usage-account-copy",
2098
2111
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2099
- className: "dsm-trae-model-head",
2100
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
2101
- className: "dsm-trae-model-enabled",
2102
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
2103
- type: "checkbox",
2104
- checked: activeEnabledIds.has(model.id),
2105
- disabled: settingsScope?.getSnapshot().writable !== true || saving,
2106
- onChange: () => {
2107
- toggleModel(model.id);
2108
- }
2109
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2110
- className: "dsm-trae-model-copy",
2111
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
2112
- className: "dsm-trae-model-name",
2112
+ className: "dsm-trae-usage-status",
2113
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2114
+ "aria-hidden": "true",
2115
+ className: "dsm-trae-usage-dot",
2116
+ style: dotStyle(status.status)
2117
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: label })]
2118
+ }), status.status === "signed-in" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2119
+ className: "dsm-trae-usage-expiry",
2120
+ children: t("row.tokenExpiry", { expiresAt: formatDateTime(status.tokenExpiresAtMs) })
2121
+ }) : null]
2122
+ })
2123
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2124
+ className: `dsm-trae-account-table${activeRegion === "cn" ? " dsm-trae-account-table-checkin" : ""}`,
2125
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2126
+ className: "dsm-trae-account-row dsm-trae-account-row-head",
2127
+ role: "row",
2128
+ children: [
2129
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("row.accountColumnAccount") }),
2130
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("row.accountColumnValue") }),
2131
+ activeRegion === "cn" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("row.accountColumnCheckin") }) : null,
2132
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {})
2133
+ ]
2134
+ }), status.accounts.map((account) => {
2135
+ const current = status.status === "signed-in" && account.id === status.accountId;
2136
+ const state = accountStatusByRegion[activeRegion][account.id];
2137
+ const error = claimErrorByRegion[activeRegion][account.id];
2138
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2139
+ className: `dsm-trae-account-row${current ? " dsm-trae-account-row-current" : ""}`,
2140
+ role: "row",
2141
+ children: [
2142
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
2143
+ className: "dsm-trae-account-who",
2144
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2145
+ "aria-hidden": "true",
2146
+ className: "dsm-trae-usage-dot",
2147
+ style: dotStyle(account.selected ? "signed-in" : "signed-out")
2148
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
2149
+ className: "dsm-trae-account-name",
2113
2150
  children: [
2114
- model.name,
2115
- model.creditMultiplier === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
2116
- className: "dsm-trae-model-name-rate",
2117
- children: ["· x", model.creditMultiplier.toFixed(2)]
2151
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("b", { children: account.accountName }),
2152
+ current ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2153
+ className: "dsm-trae-account-badge",
2154
+ children: t("row.accountCurrentBadge")
2155
+ }) : null,
2156
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
2157
+ className: "dsm-trae-account-meta",
2158
+ children: [
2159
+ editionLabel(account.edition),
2160
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("i", {
2161
+ "aria-hidden": "true",
2162
+ children: "·"
2163
+ }),
2164
+ sourceLabel(account.source)
2165
+ ]
2118
2166
  }),
2119
- probeBadge(probeByRegion[activeRegion][model.id], t)
2167
+ error === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2168
+ className: "dsm-trae-account-note",
2169
+ role: "alert",
2170
+ children: t("row.checkinError", { message: error })
2171
+ })
2120
2172
  ]
2173
+ })]
2174
+ }),
2175
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2176
+ className: "dsm-trae-account-worth",
2177
+ children: rowValue(state)
2178
+ }),
2179
+ activeRegion === "cn" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2180
+ className: "dsm-trae-account-checkin-cell",
2181
+ children: checkinControl(account, state)
2182
+ }) : null,
2183
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2184
+ className: "dsm-trae-account-actions",
2185
+ children: current ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
2186
+ type: "button",
2187
+ className: "dsm-btn dsm-btn-outline dsm-trae-account-switch",
2188
+ disabled: switchingAccount || settingsScope?.getSnapshot()?.writable !== true,
2189
+ onClick: () => {
2190
+ switchAccount(account.id);
2191
+ },
2192
+ children: switchingAccount ? t("row.accountSwitching") : t("row.accountSwitch")
2121
2193
  })
2122
- })]
2123
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2124
- className: "dsm-trae-model-options",
2194
+ })
2195
+ ]
2196
+ }, account.id);
2197
+ })]
2198
+ }),
2199
+ status.status === "signed-in" && status.alternatives !== void 0 && status.alternatives.length > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2200
+ className: "dsm-trae-usage-alternative",
2201
+ role: "status",
2202
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("p", {
2203
+ className: "dsm-trae-usage-alternative-text",
2204
+ children: [
2205
+ t("row.creditsExhausted", { account: status.accountName }),
2206
+ " ",
2207
+ t("row.creditsAlternative", { accounts: status.alternatives.map((alt) => `${alt.accountName}(${t(`row.edition${alt.edition === "cn" ? "Cn" : alt.edition === "sg" ? "Sg" : alt.edition === "solo" ? "Solo" : "SoloSg"}`)}:${formatNumber(alt.workAvailable + alt.generalAvailable)})`).join("、") })
2208
+ ]
2209
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
2210
+ type: "button",
2211
+ className: "dsm-trae-usage-switch-button",
2212
+ disabled: switchingAccount || settingsScope?.getSnapshot()?.writable !== true,
2213
+ onClick: () => {
2214
+ const target = status.alternatives?.[0];
2215
+ if (target !== void 0) switchAccount(target.id);
2216
+ },
2217
+ children: t("row.creditsAlternativeSwitch")
2218
+ })]
2219
+ }) : null,
2220
+ status.status === "signed-in" && status.credits?.expiry !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ExpiryNotice, {
2221
+ expiry: status.credits.expiry,
2222
+ t,
2223
+ formatNumber,
2224
+ formatDateTime
2225
+ }) : null,
2226
+ status.status === "signed-in" && status.appLogin !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2227
+ className: "dsm-trae-usage-alternative",
2228
+ role: "status",
2229
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
2230
+ className: "dsm-trae-usage-alternative-text",
2231
+ children: t("row.appLoginMismatch", {
2232
+ account: status.appLogin.accountName,
2233
+ bound: status.accountName
2234
+ })
2235
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
2236
+ type: "button",
2237
+ className: "dsm-trae-usage-switch-button",
2238
+ disabled: switchingAccount || settingsScope?.getSnapshot()?.writable !== true,
2239
+ onClick: () => {
2240
+ followAppLogin();
2241
+ },
2242
+ children: t("row.appLoginFollow", { account: status.appLogin.accountName })
2243
+ })]
2244
+ }) : null,
2245
+ rescanNote === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
2246
+ className: "dsm-trae-usage-note",
2247
+ role: "status",
2248
+ children: rescanNote
2249
+ })
2250
+ ]
2251
+ }),
2252
+ status.status === "signed-in" ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
2253
+ status.checkin !== void 0 && !status.checkin.enabled ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
2254
+ className: "dsm-trae-usage-error",
2255
+ children: t("row.checkinDisabled")
2256
+ }) : null,
2257
+ status.checkinError === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
2258
+ className: "dsm-trae-usage-error",
2259
+ children: t("row.checkinError", { message: status.checkinError })
2260
+ }),
2261
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
2262
+ className: "dsm-trae-models",
2263
+ "aria-label": t("row.modelsTitle"),
2264
+ children: [
2265
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2266
+ className: "dsm-trae-models-head",
2267
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", {
2268
+ className: "dsm-trae-models-title",
2269
+ children: t("row.modelsTitle")
2270
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
2271
+ className: "dsm-trae-models-summary",
2272
+ children: t("row.modelsSummary", { count: activeEnabledIds.size })
2273
+ })] }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2274
+ className: "dsm-trae-models-actions",
2275
+ children: [
2276
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
2277
+ type: "button",
2278
+ className: "dsm-btn dsm-btn-outline",
2279
+ disabled: busy,
2280
+ onClick: () => {
2281
+ refreshModels();
2282
+ },
2283
+ children: busy ? t("row.modelsRefreshing") : t("row.modelsRefresh")
2284
+ }),
2285
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
2286
+ type: "button",
2287
+ className: "dsm-btn dsm-btn-outline",
2288
+ disabled: busy || testing !== void 0 || activeEnabledIds.size === 0,
2289
+ title: t("row.modelsTestHint"),
2290
+ onClick: () => {
2291
+ testEnabledModels();
2292
+ },
2293
+ children: testing === void 0 ? t("row.modelsTestEnabled") : t("row.modelsTesting", {
2294
+ done: testing.done,
2295
+ total: testing.total
2296
+ })
2297
+ }),
2298
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
2299
+ type: "button",
2300
+ className: "dsm-btn dsm-btn-outline",
2301
+ disabled: busy || testing !== void 0 || visibleModels.length === 0,
2302
+ title: t("row.modelsTestAllHint"),
2303
+ onClick: () => {
2304
+ testAllModels();
2305
+ },
2306
+ children: testing === void 0 ? t("row.modelsTestAll") : t("row.modelsTesting", {
2307
+ done: testing.done,
2308
+ total: testing.total
2309
+ })
2310
+ })
2311
+ ]
2312
+ })]
2313
+ }),
2314
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
2315
+ className: "dsm-trae-models-hint",
2316
+ children: t("row.modelsTestHint")
2317
+ }),
2318
+ testSummary === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
2319
+ className: "dsm-trae-models-summary",
2320
+ children: testSummary
2321
+ }),
2322
+ testError === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
2323
+ className: "dsm-trae-usage-error",
2324
+ role: "alert",
2325
+ children: testError
2326
+ }),
2327
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2328
+ className: "dsm-trae-model-list",
2329
+ children: visibleModels.map((model) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2330
+ className: `dsm-trae-model${activeEnabledIds.has(model.id) ? "" : " dsm-trae-model-disabled"}`,
2331
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2332
+ className: "dsm-trae-model-head",
2125
2333
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
2126
- className: "dsm-trae-model-image",
2334
+ className: "dsm-trae-model-enabled",
2127
2335
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
2128
2336
  type: "checkbox",
2129
- checked: rowAcceptsImage(model),
2130
- disabled: rowImageIsAutomatic(model) || settingsScope?.getSnapshot().writable !== true || saving,
2131
- title: rowImageIsAutomatic(model) ? t("row.modelImageAuto") : void 0,
2132
- onChange: () => {
2133
- toggleImage(model.id);
2134
- }
2135
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("row.modelImage") })]
2136
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("fieldset", {
2137
- className: "dsm-trae-context-budget",
2138
- "aria-label": t("row.contextBudget"),
2139
- children: [model.maxContextWindow !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
2140
- type: "radio",
2141
- name: `context-${model.id}`,
2142
- checked: activeContextBudgets[model.id] !== model.maxContextWindow,
2337
+ checked: activeEnabledIds.has(model.id),
2143
2338
  disabled: settingsScope?.getSnapshot().writable !== true || saving,
2144
2339
  onChange: () => {
2145
- setContextBudget(model.id, model.contextWindow);
2146
- }
2147
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: formatCapacity(model.contextWindow, t("row.modelUnknown")) })] }) : null, /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
2148
- type: "radio",
2149
- name: `context-${model.id}`,
2150
- checked: model.maxContextWindow === void 0 || activeContextBudgets[model.id] === model.maxContextWindow,
2151
- disabled: model.maxContextWindow === void 0 || settingsScope?.getSnapshot().writable !== true || saving,
2152
- onChange: () => {
2153
- setContextBudget(model.id, model.maxContextWindow);
2340
+ toggleModel(model.id);
2154
2341
  }
2155
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: formatCapacity(model.maxContextWindow ?? model.contextWindow, t("row.modelUnknown")) })] })]
2342
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2343
+ className: "dsm-trae-model-copy",
2344
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
2345
+ className: "dsm-trae-model-name",
2346
+ children: [
2347
+ model.name,
2348
+ model.creditMultiplier === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
2349
+ className: "dsm-trae-model-name-rate",
2350
+ children: ["· x", model.creditMultiplier.toFixed(2)]
2351
+ }),
2352
+ probeBadge(probeByRegion[activeRegion][model.id], t)
2353
+ ]
2354
+ })
2355
+ })]
2356
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2357
+ className: "dsm-trae-model-options",
2358
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
2359
+ className: "dsm-trae-model-image",
2360
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
2361
+ type: "checkbox",
2362
+ checked: rowAcceptsImage(model),
2363
+ disabled: rowImageIsAutomatic(model) || settingsScope?.getSnapshot().writable !== true || saving,
2364
+ title: rowImageIsAutomatic(model) ? t("row.modelImageAuto") : void 0,
2365
+ onChange: () => {
2366
+ toggleImage(model.id);
2367
+ }
2368
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("row.modelImage") })]
2369
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("fieldset", {
2370
+ className: "dsm-trae-context-budget",
2371
+ "aria-label": t("row.contextBudget"),
2372
+ children: [model.maxContextWindow !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
2373
+ type: "radio",
2374
+ name: `context-${model.id}`,
2375
+ checked: activeContextBudgets[model.id] !== model.maxContextWindow,
2376
+ disabled: settingsScope?.getSnapshot().writable !== true || saving,
2377
+ onChange: () => {
2378
+ setContextBudget(model.id, model.contextWindow);
2379
+ }
2380
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: formatCapacity(model.contextWindow, t("row.modelUnknown")) })] }) : null, /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
2381
+ type: "radio",
2382
+ name: `context-${model.id}`,
2383
+ checked: model.maxContextWindow === void 0 || activeContextBudgets[model.id] === model.maxContextWindow,
2384
+ disabled: model.maxContextWindow === void 0 || settingsScope?.getSnapshot().writable !== true || saving,
2385
+ onChange: () => {
2386
+ setContextBudget(model.id, model.maxContextWindow);
2387
+ }
2388
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: formatCapacity(model.maxContextWindow ?? model.contextWindow, t("row.modelUnknown")) })] })]
2389
+ })]
2156
2390
  })]
2391
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2392
+ className: "dsm-trae-model-meta",
2393
+ children: [
2394
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("row.modelContext", { context: formatCapacity(model.maxContextWindow ?? model.contextWindow, t("row.modelUnknown")) }) }),
2395
+ model.maxTokens === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("row.modelOutput", { output: formatCapacity(model.maxTokens, t("row.modelUnknown")) }) }),
2396
+ model.reasoning === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("row.modelReasoning", { efforts: model.reasoning.supported.join(" / ") }) })
2397
+ ]
2398
+ })]
2399
+ }, model.id))
2400
+ }),
2401
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
2402
+ className: "dsm-trae-model-capability-note",
2403
+ children: t("row.modelCapabilityPending")
2404
+ }),
2405
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2406
+ className: "dsm-trae-model-actions",
2407
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("a", {
2408
+ className: "dsm-trae-usage-cheer",
2409
+ href: TRAE_GITHUB_URL,
2410
+ target: "_blank",
2411
+ rel: "noopener noreferrer",
2412
+ children: [t("row.cheer"), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2413
+ className: "dsm-trae-usage-cheer-star",
2414
+ "aria-hidden": "true",
2415
+ children: "★"
2157
2416
  })]
2158
2417
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2159
- className: "dsm-trae-model-meta",
2160
- children: [
2161
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("row.modelContext", { context: formatCapacity(model.maxContextWindow ?? model.contextWindow, t("row.modelUnknown")) }) }),
2162
- model.maxTokens === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("row.modelOutput", { output: formatCapacity(model.maxTokens, t("row.modelUnknown")) }) }),
2163
- model.reasoning === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("row.modelReasoning", { efforts: model.reasoning.supported.join(" / ") }) })
2164
- ]
2418
+ className: "dsm-trae-model-actions-buttons",
2419
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
2420
+ type: "button",
2421
+ className: "dsm-btn dsm-btn-outline",
2422
+ disabled: !dirty || saving,
2423
+ onClick: discardModels,
2424
+ children: t("row.discard")
2425
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
2426
+ type: "button",
2427
+ className: "dsm-btn dsm-btn-primary",
2428
+ disabled: !dirty || saving || activeEnabledIds.size === 0,
2429
+ onClick: () => {
2430
+ saveModels();
2431
+ },
2432
+ children: saving ? t("row.saving") : t("row.save")
2433
+ })]
2165
2434
  })]
2166
- }, model.id))
2167
- }),
2435
+ })
2436
+ ]
2437
+ })
2438
+ ] }) : null,
2439
+ status.status === "signed-out" ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
2440
+ className: "dsm-trae-usage-text",
2441
+ children: status.message ?? t("row.signedOutHint")
2442
+ }), status.searched && status.searched.length > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("details", {
2443
+ className: "dsm-trae-searched",
2444
+ children: [
2445
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("summary", { children: [
2446
+ t("row.searchedTitle"),
2447
+ " (",
2448
+ status.searched.length,
2449
+ ")"
2450
+ ] }),
2168
2451
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
2169
- className: "dsm-trae-model-capability-note",
2170
- children: t("row.modelCapabilityPending")
2452
+ className: "dsm-trae-searched-hint",
2453
+ children: t("row.searchedHint")
2171
2454
  }),
2172
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2173
- className: "dsm-trae-model-actions",
2174
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("a", {
2175
- className: "dsm-trae-usage-cheer",
2176
- href: TRAE_GITHUB_URL,
2177
- target: "_blank",
2178
- rel: "noopener noreferrer",
2179
- children: [t("row.cheer"), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2180
- className: "dsm-trae-usage-cheer-star",
2181
- "aria-hidden": "true",
2182
- children: "★"
2183
- })]
2184
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2185
- className: "dsm-trae-model-actions-buttons",
2186
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
2187
- type: "button",
2188
- className: "dsm-btn dsm-btn-outline",
2189
- disabled: !dirty || saving,
2190
- onClick: discardModels,
2191
- children: t("row.discard")
2192
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
2193
- type: "button",
2194
- className: "dsm-btn dsm-btn-primary",
2195
- disabled: !dirty || saving || activeEnabledIds.size === 0,
2196
- onClick: () => {
2197
- saveModels();
2198
- },
2199
- children: saving ? t("row.saving") : t("row.save")
2200
- })]
2201
- })]
2455
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("ul", {
2456
+ className: "dsm-trae-searched-list",
2457
+ children: status.searched.map((item) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("li", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("code", { children: item.path }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
2458
+ className: "dsm-trae-searched-reason",
2459
+ children: [
2460
+ t(item.source === "cli" ? "row.sourceCli" : "row.sourceDesktop"),
2461
+ " · ",
2462
+ t(item.reason === "missing" ? "row.reasonMissing" : item.reason === "unreadable" ? "row.reasonUnreadable" : "row.reasonInvalid")
2463
+ ]
2464
+ })] }, `${item.source}:${item.path}`))
2202
2465
  })
2203
2466
  ]
2204
- })
2205
- ] }) : null,
2206
- status.status === "signed-out" ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
2207
- className: "dsm-trae-usage-text",
2208
- children: status.message ?? t("row.signedOutHint")
2209
- }), status.searched && status.searched.length > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("details", {
2210
- className: "dsm-trae-searched",
2211
- children: [
2212
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("summary", { children: [
2213
- t("row.searchedTitle"),
2214
- " (",
2215
- status.searched.length,
2216
- ")"
2217
- ] }),
2218
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
2219
- className: "dsm-trae-searched-hint",
2220
- children: t("row.searchedHint")
2221
- }),
2222
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("ul", {
2223
- className: "dsm-trae-searched-list",
2224
- children: status.searched.map((item) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("li", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("code", { children: item.path }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
2225
- className: "dsm-trae-searched-reason",
2226
- children: [
2227
- t(item.source === "cli" ? "row.sourceCli" : "row.sourceDesktop"),
2228
- " · ",
2229
- t(item.reason === "missing" ? "row.reasonMissing" : item.reason === "unreadable" ? "row.reasonUnreadable" : "row.reasonInvalid")
2230
- ]
2231
- })] }, `${item.source}:${item.path}`))
2232
- })
2233
- ]
2234
- }) : null] }) : null,
2235
- status.status === "error" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
2236
- className: "dsm-trae-usage-error",
2237
- children: status.message
2238
- }) : null
2239
- ]
2240
- }) : null
2241
- })]
2467
+ }) : null] }) : null,
2468
+ status.status === "error" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
2469
+ className: "dsm-trae-usage-error",
2470
+ children: status.message
2471
+ }) : null
2472
+ ]
2473
+ }) : null
2474
+ }),
2475
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(BusyOverlay, {
2476
+ visible: saving || savingSettings,
2477
+ message: t("row.veilSaving")
2478
+ })
2479
+ ]
2242
2480
  });
2243
2481
  }
2244
2482
  //#endregion
@@ -2853,6 +3091,23 @@ window.__ModuleLoader__.load({
2853
3091
  * row is shared with the shell's own permission/agent/model controls, so the
2854
3092
  * readout must be asked for, not imposed — 2.13.0 rendered it unconditionally
2855
3093
  * and 2.13.3 restored the opt-in after the user went looking for the switch.
3094
+ *
3095
+ * WHY THERE IS A SECOND SEAT (learned from dsh-connect-workbuddy issue #62).
3096
+ * That row is a LIST seat, so it is shared not only with the shell's own
3097
+ * controls but with any other plugin that registers there — and one report is
3098
+ * exactly that: with a third-party plugin installed the readout stopped
3099
+ * appearing. A list seat cannot promise that every occupant is visible, so the
3100
+ * seat becomes the USER's choice: `composer` (default, unchanged for anyone who
3101
+ * does not choose) or `stats`, the ambient dock below the composer card where
3102
+ * the shell renders its own round/step/token pills. This plugin has already
3103
+ * lost this readout once to a shared seat — it used to live at the sidebar
3104
+ * foot, where it was clipped to "Tr" beside dsh-connect-workbuddy's rows.
3105
+ *
3106
+ * The gate is registered in BOTH seats and renders in the one the setting
3107
+ * names, which keeps the decision in ONE function instead of re-registering
3108
+ * slots when the setting changes — and this repository already documents that
3109
+ * kind of binding as fragile (the rc.6→rc.7 `id`/`order` → `key`/`priority`
3110
+ * rename). Registration is unconditional; only the render is conditional.
2856
3111
  */
2857
3112
  /**
2858
3113
  * The provider route the session is currently pointed at.
@@ -2865,6 +3120,17 @@ window.__ModuleLoader__.load({
2865
3120
  return typeof provider === "string" && provider !== "" ? provider : void 0;
2866
3121
  }
2867
3122
  /**
3123
+ * The settings tree this plugin writes, unwrapped to a plain object.
3124
+ *
3125
+ * `unwrapVolatileDeep` first: on DSH 0.1.7 the scope's value is a live
3126
+ * cosmokit reference, and reading a field off it without unwrapping yields a
3127
+ * proxy whose `===` comparison against a string is false — which would make the
3128
+ * seat test below reject EVERY seat and silently hide the readout.
3129
+ */
3130
+ function scopeValueOf(scope) {
3131
+ return scope === void 0 ? void 0 : unwrapVolatileDeep(scope.getSnapshot().value);
3132
+ }
3133
+ /**
2868
3134
  * Read the composer-readout switch out of a committed settings snapshot.
2869
3135
  *
2870
3136
  * Absent means OFF, matching the host default (`false`): a scope that has not
@@ -2872,11 +3138,10 @@ window.__ModuleLoader__.load({
2872
3138
  * the composer toolbar that the user never asked for.
2873
3139
  */
2874
3140
  function composerPointsEnabledOf(scope) {
2875
- if (scope === void 0) return false;
2876
- return unwrapVolatileDeep(scope.getSnapshot().value)?.showPointsInMainUi === true;
3141
+ return scopeValueOf(scope)?.showPointsInMainUi === true;
2877
3142
  }
2878
3143
  function ComposerPointsGate(props) {
2879
- const { t, settingsScope, useProjection } = props;
3144
+ const { t, position, settingsScope, useProjection } = props;
2880
3145
  const [enabled, setEnabled] = (0, react.useState)(() => composerPointsEnabledOf(settingsScope));
2881
3146
  (0, react.useEffect)(() => settingsScope?.subscribe(() => {
2882
3147
  setEnabled(composerPointsEnabledOf(settingsScope));
@@ -2886,6 +3151,8 @@ window.__ModuleLoader__.load({
2886
3151
  const provider = selectedProviderOf(projection);
2887
3152
  const region = provider === void 0 ? void 0 : TRAE_COMPOSER_PROVIDERS[provider];
2888
3153
  if (region === void 0) return null;
3154
+ if (!regionEnabledOf(scopeValueOf(settingsScope), region)) return null;
3155
+ if (regionCreditPositionOf(scopeValueOf(settingsScope), region) !== (position ?? "composer")) return null;
2889
3156
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ComposerPoints, {
2890
3157
  t,
2891
3158
  provider,
@@ -2981,6 +3248,11 @@ window.__ModuleLoader__.load({
2981
3248
  "row.showPointsInMainUi": "Show credits in the composer row",
2982
3249
  "row.showPointsInMainUiHint": "A clickable readout in the composer tool row while a model from this plugin is selected: Trae CN · <balance> on the CN side, Trae Global · <plan> on the international one.",
2983
3250
  "row.showPointsInMainUiHintAi": "A clickable readout in the composer tool row while a model from this plugin is selected: Trae Global · <plan>.",
3251
+ "row.creditPositionLabel": "position",
3252
+ "row.creditPositionComposer": "composer toolbar",
3253
+ "row.creditPositionStats": "below the input box",
3254
+ "row.creditPositionAria": "Credit readout position for {region}",
3255
+ "row.creditPositionHint": "Where the credit readout sits. \"composer toolbar\" is the default (the row with the model picker). Choose \"below the input box\" — the row carrying the round / step / token statistics — if another plugin occupies the composer toolbar and the readout does not appear.",
2984
3256
  "composer.switchTo": "Switch to {account}",
2985
3257
  "composer.noAccounts": "No account in this region",
2986
3258
  "composer.panelTitle": "Trae credits",
@@ -3029,6 +3301,7 @@ window.__ModuleLoader__.load({
3029
3301
  "row.discard": "Discard changes",
3030
3302
  "row.save": "Save",
3031
3303
  "row.saving": "Saving…",
3304
+ "row.veilSaving": "Saving changes…",
3032
3305
  "row.modelContext": "Max context {context}",
3033
3306
  "row.contextBudget": "DSH context budget",
3034
3307
  "row.modelOutput": "Output {output}",
@@ -3122,6 +3395,11 @@ window.__ModuleLoader__.load({
3122
3395
  "row.showPointsInMainUi": "在输入框工具栏显示积分读数",
3123
3396
  "row.showPointsInMainUiHint": "在输入框工具栏显示可点击读数(选中本插件模型时出现):国内版为「Trae CN · 余额」,国际版为「Trae 国际版 · 套餐」。",
3124
3397
  "row.showPointsInMainUiHintAi": "在输入框工具栏显示可点击读数(选中本插件模型时出现):国际版为「Trae 国际版 · 套餐」。",
3398
+ "row.creditPositionLabel": "显示位置",
3399
+ "row.creditPositionComposer": "输入框工具栏",
3400
+ "row.creditPositionStats": "输入框下方",
3401
+ "row.creditPositionAria": "{region} 的积分显示位置",
3402
+ "row.creditPositionHint": "积分显示在哪里。「输入框工具栏」是默认位置(与模型选择同一行)。如果装了别的插件后读数不显示,改成「输入框下方」—— 那是「轮次 / 步数 / token」统计所在的那一行。",
3125
3403
  "composer.switchTo": "切换到 {account}",
3126
3404
  "composer.noAccounts": "该区域暂无账号",
3127
3405
  "composer.panelTitle": "Trae 积分",
@@ -3170,6 +3448,7 @@ window.__ModuleLoader__.load({
3170
3448
  "row.discard": "放弃修改",
3171
3449
  "row.save": "保存",
3172
3450
  "row.saving": "保存中…",
3451
+ "row.veilSaving": "正在保存更改…",
3173
3452
  "row.modelContext": "最大上下文 {context}",
3174
3453
  "row.contextBudget": "DSH 上下文预算",
3175
3454
  "row.modelOutput": "最大输出 {output}",
@@ -3320,24 +3599,37 @@ window.__ModuleLoader__.load({
3320
3599
  * Registration is unconditional; the gate reads the Host's modelSelection
3321
3600
  * projection and returns null for every other provider, so nothing is drawn
3322
3601
  * and no fetch loop starts while another provider is selected.
3602
+ *
3603
+ * THE SAME READOUT IS REGISTERED IN TWO SEATS (learned from workbuddy issue
3604
+ * #62), and BOTH registrations are unconditional too. Which one actually
3605
+ * draws is decided inside the gate by the per-region `creditPosition`
3606
+ * setting, so changing the setting is a settings write rather than a slot
3607
+ * re-registration — this file's own header records how fragile rebinding a
3608
+ * slot is across DSH lines (the rc.6→rc.7 `id`/`order` → `key`/`priority`
3609
+ * rename). The composer row is a LIST seat shared with every other bundle,
3610
+ * so a third party parking something there can crowd this readout out of
3611
+ * existence; the second seat is what keeps one plugin's presence from
3612
+ * deciding another's.
3323
3613
  */
3324
- const registerComposerPoints = () => {
3614
+ const registerPointsSlot = (slotName, id, order, position) => {
3325
3615
  try {
3326
- ctx.slots.inject("conversation.input.left", () => ctx.slots.register({
3327
- name: "conversation.input.left",
3328
- id: "dsh-connect-trae-composer-points",
3329
- order: 4,
3616
+ ctx.slots.inject(slotName, () => ctx.slots.register({
3617
+ name: slotName,
3618
+ id,
3619
+ order,
3330
3620
  locale: namespace,
3331
3621
  inject: () => ({
3332
3622
  t,
3333
- settingsScope
3623
+ settingsScope,
3624
+ position
3334
3625
  })
3335
3626
  }, ComposerPointsGate));
3336
3627
  } catch (error) {
3337
- console.error("[dsh-connect-trae] composer points slot failed to register (host provider unaffected):", error);
3628
+ console.error(`[dsh-connect-trae] points slot "${slotName}" failed to register (host provider unaffected):`, error);
3338
3629
  }
3339
3630
  };
3340
- registerComposerPoints();
3631
+ registerPointsSlot("conversation.input.left", "dsh-connect-trae-composer-points", 4, "composer");
3632
+ registerPointsSlot("conversation.composer.dock", "dsh-connect-trae-stats-points", 10, "stats");
3341
3633
  } catch (error) {
3342
3634
  console.error("[dsh-connect-trae] client card failed to load (host provider unaffected):", error);
3343
3635
  }