dsh-connect-trae 4.2.0 → 4.4.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
@@ -18,6 +18,11 @@ window.__ModuleLoader__.load({
18
18
  function regionOfPanel(panel) {
19
19
  return panel === "ai" ? "ai" : "cn";
20
20
  }
21
+ /** The provider id a panel publishes. */
22
+ function providerOfPanel(panel) {
23
+ if (panel === "enterprise") return "trae-enterprise";
24
+ return panel === "ai" ? "trae-global" : "trae";
25
+ }
21
26
  //#endregion
22
27
  //#region src/status-paths.ts
23
28
  /** Plugin-owned usage endpoint consumed by its browser half. */
@@ -359,6 +364,33 @@ window.__ModuleLoader__.load({
359
364
  });
360
365
  }
361
366
  /**
367
+ * Build the next `regions` settings value for one panel's MODEL DIRECTORY.
368
+ *
369
+ * The model-directory save REPLACES the whole region slot, so every field of
370
+ * that slot must be carried through — the ones the save does not name are
371
+ * silently dropped. Two were being dropped this way:
372
+ *
373
+ * - `pool` (issue #44): the account-pool preferences. Enabling the pool and
374
+ * then saving the model list turned the pool back OFF, and the card came
375
+ * back showing "not saved" — the switch looked broken.
376
+ * - `creditPosition`: which seat this region's credit readout occupies, so
377
+ * saving the model list silently moved the readout back.
378
+ *
379
+ * Both are the user's standing choices, not the model directory's business.
380
+ * The stored slot is therefore SPREAD and the directory fields override it, so
381
+ * the save owns exactly the five fields it knows about and inherits the rest.
382
+ *
383
+ * `value` may be the whole settings section or the `regions` map; the RETURN
384
+ * value is always the `regions` map, i.e. exactly what `settingsScope.set(
385
+ * 'regions', ...)` needs.
386
+ */
387
+ function nextRegionDirectory(value, panel, directory) {
388
+ return nextRegionSlots(regionsMapOf(value), panel, {
389
+ ...regionSlotOf(value, panel),
390
+ ...directory
391
+ });
392
+ }
393
+ /**
362
394
  * Whether Trae's plan identity says this account is on a paying plan.
363
395
  *
364
396
  * `has_package` alone is NOT that question — it reports whether a package
@@ -556,6 +588,45 @@ window.__ModuleLoader__.load({
556
588
  .dsm-trae-tab-switch{flex:none;margin-left:auto;padding-left:10px;border-left:1px solid var(--dsw-alias-border-l1,rgba(127,127,127,.24))}
557
589
  .dsm-trae-tab-dot{display:inline-block;width:7px;height:7px;border-radius:50%;margin-right:7px;vertical-align:baseline}
558
590
  .dsm-trae-tab-hint{margin:0;padding:0 2px;color:var(--dsw-alias-label-tertiary,#999);font-size:12px;line-height:18px}
591
+ /* SECTION TABS: the card's own top-level split (account / models / diagnostics).
592
+ *
593
+ * Deliberately NOT the boxed chips above. Those are the REGION scope — a
594
+ * three-way choice of which PROVIDER this whole card is about, each chip
595
+ * carrying its own on/off switch — while these are panels of ONE document, the
596
+ * shape a reader already knows from the app's own settings window. Two rows of
597
+ * boxes would read as one six-way choice.
598
+ *
599
+ * The hidden ATTRIBUTE on each panel is what does the work: an inactive panel
600
+ * stays MOUNTED, so switching back does not refetch, a half-filled model draft
601
+ * survives the trip, and the account pool keeps its log. It also takes the
602
+ * inactive panel out of the accessibility tree, which is exactly what
603
+ * role="tabpanel" wants — unlike display:none reached by a CSS class, the user
604
+ * agent's own rule for [hidden] cannot be overridden by a later declaration. */
605
+ .dsm-trae-panels{display:flex;align-items:stretch;gap:2px;margin:4px 0 0;border-bottom:1px solid var(--dsw-alias-border-l1,rgba(127,127,127,.24))}
606
+ .dsm-trae-panel-tab{appearance:none;font:inherit;cursor:pointer;border:0;background:transparent;padding:7px 11px;margin-bottom:-1px;border-bottom:2px solid transparent;color:var(--dsw-alias-label-tertiary,#999);font-size:13px;font-weight:600;line-height:20px;white-space:nowrap;transition:color .15s,border-color .15s}
607
+ .dsm-trae-panel-tab:hover:not(.dsm-trae-panel-tab-active){color:var(--dsw-alias-label-primary,#e6e6e6)}
608
+ .dsm-trae-panel-tab:focus-visible{outline:2px solid var(--dsw-alias-brand-primary,#5686fe);outline-offset:1px}
609
+ .dsm-trae-panel-tab-active{color:var(--dsw-alias-label-primary,#e6e6e6);border-bottom-color:var(--dsw-alias-brand-primary,#5686fe)}
610
+ /* No top margin of its own: the strip above already carries a border, and the
611
+ card's 16px stack gap would double the separation. */
612
+ .dsm-trae-panel{margin-top:0}
613
+ /* DIAGNOSTICS: a two-column fact table rather than prose. Each row is one thing
614
+ * the card actually READ, so a bug report can quote a line instead of
615
+ * paraphrasing the UI. No control lives here on purpose — a diagnostics panel
616
+ * that can change settings is a settings panel with a worse name. */
617
+ .dsm-trae-diag{display:flex;flex-direction:column;border:1px solid var(--dsw-alias-border-l2,#36373b);border-radius:10px;overflow:hidden;background:var(--dsw-alias-bg-layer-2,#232529)}
618
+ .dsm-trae-diag-row{display:flex;align-items:baseline;justify-content:space-between;gap:16px;padding:8px 12px}
619
+ .dsm-trae-diag-row+.dsm-trae-diag-row{border-top:1px solid var(--dsw-alias-border-l1,rgba(127,127,127,.24))}
620
+ .dsm-trae-diag-label{flex:none;color:var(--dsw-alias-label-tertiary,#999);font-size:12px;line-height:18px}
621
+ .dsm-trae-diag-value{min-width:0;color:var(--dsw-alias-label-primary,#e6e6e6);font-size:12px;line-height:18px;text-align:right;overflow-wrap:anywhere}
622
+ .dsm-trae-diag-mono{font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace}
623
+ /* A failure line, in the same warn tone the card already uses for an expiring
624
+ credit — NOT a hand-rolled amber. The token set the card asks DSH for is
625
+ audited by the card-styles spec (every name must be one the host actually
626
+ defines), so this deliberately names state-warn-primary rather than the
627
+ sibling card's label-caution, which this suite has never verified. */
628
+ .dsm-trae-diag-bad{color:var(--dsw-alias-state-warn-primary,#b45309)}
629
+ .dsm-trae-diag-hint{margin:10px 0 0;color:var(--dsw-alias-label-tertiary,#999);font-size:12px;line-height:18px}
559
630
  .dsm-trae-credits-unavailable{margin:10px 0 0;padding:10px 12px;border:1px solid var(--dsw-alias-border-l2,#3a3d45);border-radius:10px;color:var(--dsw-alias-label-secondary,#c6c9d0);font-size:12px;line-height:18px;background:var(--dsw-alias-bg-layer-3,#2a2c33)}
560
631
  .dsm-trae-account-value-muted{color:var(--dsw-alias-label-secondary,#c6c9d0);font-size:12px}
561
632
  .dsm-trae-tab-off-notice{margin:0;padding:10px 12px;border:1px solid var(--dsw-alias-border-l2,#3a3d45);border-radius:10px;color:var(--dsw-alias-label-secondary,#c6c9d0);font-size:12px;line-height:18px;background:var(--dsw-alias-bg-layer-3,#2a2c33)}
@@ -738,6 +809,17 @@ window.__ModuleLoader__.load({
738
809
  .dsm-trae-models-head{display:flex;align-items:center;justify-content:space-between;gap:12px}
739
810
  .dsm-trae-models-title{margin:0;color:var(--dsw-alias-label-primary,#e6e6e6);font-size:14px;font-weight:600;line-height:20px}
740
811
  .dsm-trae-models-summary{margin:2px 0 0;color:var(--dsw-alias-label-tertiary,#999);font-size:12px;line-height:18px}
812
+ /* EVERY model is on the page — no inner scroller, no max-height.
813
+ *
814
+ * A capped list with its own scrollbar was tried and rejected by the user: it
815
+ * turned a settings section into a little window that has to be scrolled inside
816
+ * a page that already scrolls, and put the models a user is looking for behind a
817
+ * gesture that does not match the rest of the card. The section is allowed to be
818
+ * as tall as the catalog is; the card's own page scroll is the only scroll.
819
+ *
820
+ * overflow:hidden is for the rounded border only (so the row hairlines cannot
821
+ * paint outside the radius). It is deliberately NOT auto: nothing here clips
822
+ * content vertically. */
741
823
  .dsm-trae-model-list{display:flex;flex-direction:column;border:1px solid var(--dsw-alias-border-l2,#36373b);border-radius:10px;overflow:hidden}
742
824
  .dsm-trae-model{display:grid;grid-template-columns:minmax(0,1fr);gap:7px;padding:10px 12px;background:var(--dsw-alias-bg-layer-2,#232529);transition:opacity .16s}
743
825
  .dsm-trae-model-disabled{opacity:.55}
@@ -2928,6 +3010,418 @@ window.__ModuleLoader__.load({
2928
3010
  });
2929
3011
  }
2930
3012
  //#endregion
3013
+ //#region src/client/display.ts
3014
+ /** A group-separated integer, in the reader's own locale. */
3015
+ function formatNumber(value) {
3016
+ return new Intl.NumberFormat(void 0, {
3017
+ minimumFractionDigits: 0,
3018
+ maximumFractionDigits: 0
3019
+ }).format(value);
3020
+ }
3021
+ /** A date and time, in the reader's own locale. */
3022
+ function formatDateTime(value) {
3023
+ return new Intl.DateTimeFormat(void 0, {
3024
+ year: "numeric",
3025
+ month: "2-digit",
3026
+ day: "2-digit",
3027
+ hour: "2-digit",
3028
+ minute: "2-digit"
3029
+ }).format(new Date(value));
3030
+ }
3031
+ /**
3032
+ * The colour of a status dot, as an inline style.
3033
+ *
3034
+ * Inline rather than a class because the colour IS the value (signed-in / error
3035
+ * / signed-out); a class per state would be three names that say the same thing.
3036
+ */
3037
+ function dotStyle(status) {
3038
+ return { background: status === "signed-in" ? "var(--dsw-alias-state-success-primary, #22a06b)" : status === "error" ? "var(--dsw-alias-state-error-primary, #d92d20)" : "var(--dsw-alias-label-dimmed, #9aa0a6)" };
3039
+ }
3040
+ /** How close a refresh token must be to lapsing before the card warns, in days. */
3041
+ const REFRESH_WARN_DAYS = 30;
3042
+ /**
3043
+ * The refresh-token warning for one account, or undefined when there is nothing
3044
+ * worth saying.
3045
+ *
3046
+ * The access token renews itself every few minutes, so its own expiry answers
3047
+ * "when does this need attention" with "never, until the refresh token lapses".
3048
+ * The refresh token is therefore the only honest countdown on this card, and it
3049
+ * has to be shown BEFORE it runs out: once it lapses the plugin cannot renew at
3050
+ * all and the only recovery is signing in to Trae again, which a user who has
3051
+ * been reading "auto-renewal" for months has no reason to expect.
3052
+ *
3053
+ * Three tones, matching the credit-expiry notice: `lapsed` (renewal already
3054
+ * dead), `soon` (inside the warning window), and nothing otherwise — a healthy
3055
+ * token must not add a line to every account row forever.
3056
+ */
3057
+ function refreshTokenWarning(expiresAtMs, nowMs, t, formatTime) {
3058
+ if (expiresAtMs === void 0) return void 0;
3059
+ const text = formatTime(expiresAtMs);
3060
+ if (expiresAtMs <= nowMs) return {
3061
+ tone: "lapsed",
3062
+ text: t("row.refreshTokenLapsed", { expiresAt: text })
3063
+ };
3064
+ const days = Math.ceil((expiresAtMs - nowMs) / 864e5);
3065
+ if (days > REFRESH_WARN_DAYS) return void 0;
3066
+ return {
3067
+ tone: "soon",
3068
+ text: t("row.refreshTokenExpiring", {
3069
+ expiresAt: text,
3070
+ days
3071
+ })
3072
+ };
3073
+ }
3074
+ //#endregion
3075
+ //#region src/client/TraeAccountPanel.tsx
3076
+ /**
3077
+ * A warning that credit is about to lapse.
3078
+ *
3079
+ * The balance alone cannot express this: a pack lapsing in an hour and one
3080
+ * lapsing next month both render as "150", so a user watching only the total
3081
+ * loses the first one silently. The notice names the amount, the pack count and
3082
+ * the soonest expiry, and escalates 7d → 24h → already-lapsed.
3083
+ *
3084
+ * Renders nothing when the upstream reported no end times at all — absence of
3085
+ * expiry data is not evidence that nothing lapses, so the component stays silent
3086
+ * rather than reassuring.
3087
+ */
3088
+ function ExpiryNotice({ expiry, t }) {
3089
+ const { expired, within24h, within7d } = expiry;
3090
+ let message;
3091
+ let soonest;
3092
+ let tone;
3093
+ if (within24h.packs > 0) {
3094
+ message = t("row.expiryWarningSoon", {
3095
+ total: formatNumber(within24h.total),
3096
+ packs: String(within24h.packs)
3097
+ });
3098
+ soonest = within24h.earliestExpiresAtMs;
3099
+ tone = "dsm-trae-expiry-soon";
3100
+ } else if (within7d.packs > 0) {
3101
+ message = t("row.expiryWarningWeek", {
3102
+ total: formatNumber(within7d.total),
3103
+ packs: String(within7d.packs)
3104
+ });
3105
+ soonest = within7d.earliestExpiresAtMs;
3106
+ tone = "dsm-trae-expiry-week";
3107
+ } else if (expired.packs > 0) {
3108
+ message = t("row.expiryWarningLapsed", {
3109
+ total: formatNumber(expired.total),
3110
+ packs: String(expired.packs)
3111
+ });
3112
+ tone = "dsm-trae-expiry-lapsed";
3113
+ } else return null;
3114
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3115
+ className: `dsm-trae-expiry ${tone}`,
3116
+ role: "status",
3117
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: message }), soonest === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3118
+ className: "dsm-trae-expiry-detail",
3119
+ children: t("row.expiryEarliest", { time: formatDateTime(soonest) })
3120
+ })]
3121
+ });
3122
+ }
3123
+ /** The CN check-in cell for one row: an outcome once there is nothing left to do. */
3124
+ function ClaimCell({ claim, onClaim, t }) {
3125
+ if (!claim.claimable) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3126
+ className: "dsm-trae-account-checkin-state",
3127
+ children: claim.settled === "claimed" ? t("row.poolCheckedIn") : t("row.poolDeviceUsed")
3128
+ });
3129
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
3130
+ type: "button",
3131
+ className: "dsm-trae-account-checkin",
3132
+ disabled: claim.disabled,
3133
+ title: claim.title,
3134
+ onClick: onClaim,
3135
+ children: claim.label
3136
+ });
3137
+ }
3138
+ /**
3139
+ * The account section: preferences, the roster, the pool, and every notice about
3140
+ * the credential.
3141
+ *
3142
+ * Everything here commits ON THE CLICK (the preference boxes write themselves,
3143
+ * the check-in claims immediately, the pool saves through its own footer). That
3144
+ * is precisely why this section has no 「保存」 of its own — the model catalog,
3145
+ * which does, lives in its own section.
3146
+ */
3147
+ function TraeAccountPanel({ t, settingsScope, panel, status, statusLabel, credentialRegionIsAi, regionOn, showPointsInMainUi, onToggleShowPoints, creditPosition, onCreditPositionChange, savingPreferences, creditsLabelId, creditsHintId, rows, busy, rescanNote, onRescan, switchingAccount, onSwitchAccount, onClaim, onTransferChanged, poolDetails, poolClaims, models, deviceClaimed, onPoolChanged, onPoolRefresh, creditsUnavailable, creditsExpiry, alternatives, onSwitchToAlternative }) {
3148
+ const writable = settingsScope?.getSnapshot().writable === true;
3149
+ const signedIn = status.status === "signed-in";
3150
+ const accounts = signedIn ? status.accounts : [];
3151
+ const pool = signedIn ? status.pool : void 0;
3152
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
3153
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3154
+ className: "dsm-trae-prefs",
3155
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
3156
+ className: "dsm-trae-pref",
3157
+ title: t(credentialRegionIsAi ? "row.showPointsInMainUiHintAi" : "row.showPointsInMainUiHint"),
3158
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
3159
+ type: "checkbox",
3160
+ className: "dsm-trae-pref-check",
3161
+ checked: showPointsInMainUi,
3162
+ disabled: !writable || savingPreferences,
3163
+ "aria-describedby": creditsHintId,
3164
+ onChange: (event) => {
3165
+ onToggleShowPoints(event.currentTarget.checked);
3166
+ }
3167
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
3168
+ className: "dsm-trae-pref-copy",
3169
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3170
+ className: "dsm-trae-pref-label",
3171
+ id: creditsLabelId,
3172
+ children: t("row.showPointsInMainUi")
3173
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3174
+ className: "dsm-trae-pref-hint",
3175
+ id: creditsHintId,
3176
+ children: t(credentialRegionIsAi ? "row.showPointsInMainUiHintAi" : "row.showPointsInMainUiHint")
3177
+ })]
3178
+ })]
3179
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("label", {
3180
+ className: "dsm-trae-pref dsm-trae-credit-position-pref",
3181
+ title: t("row.creditPositionHint"),
3182
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
3183
+ className: "dsm-trae-pref-copy",
3184
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
3185
+ className: "dsm-trae-pref-lead",
3186
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3187
+ className: "dsm-trae-pref-label",
3188
+ children: t("row.creditPositionLabel")
3189
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
3190
+ className: "dsm-trae-credit-position",
3191
+ value: regionOn ? creditPosition : "composer",
3192
+ disabled: !regionOn || !writable || savingPreferences,
3193
+ "aria-label": t("row.creditPositionAria", { region: panel === "cn" ? t("row.tabCn") : panel === "enterprise" ? t("row.tabEnterprise") : t("row.tabAi") }),
3194
+ onChange: (event) => {
3195
+ onCreditPositionChange(event.currentTarget.value === "stats" ? "stats" : "composer");
3196
+ },
3197
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
3198
+ value: "composer",
3199
+ children: t("row.creditPositionComposer")
3200
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
3201
+ value: "stats",
3202
+ children: t("row.creditPositionStats")
3203
+ })]
3204
+ })]
3205
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3206
+ className: "dsm-trae-pref-hint",
3207
+ children: t("row.creditPositionHint")
3208
+ })]
3209
+ })
3210
+ })]
3211
+ }),
3212
+ pool !== void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
3213
+ className: "dsm-trae-accounts",
3214
+ "aria-label": t("row.accountsTitle"),
3215
+ children: [
3216
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3217
+ className: "dsm-trae-accounts-head",
3218
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", {
3219
+ className: "dsm-trae-accounts-title",
3220
+ children: t("row.accountsTitle")
3221
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
3222
+ className: "dsm-trae-accounts-hint",
3223
+ children: t("row.accountsHint")
3224
+ })] }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
3225
+ type: "button",
3226
+ className: "dsm-btn dsm-btn-outline",
3227
+ disabled: busy,
3228
+ onClick: onRescan,
3229
+ children: busy ? t("row.accountsScanning") : t("row.refreshTokens")
3230
+ })]
3231
+ }),
3232
+ !signedIn || accounts.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
3233
+ className: "dsm-trae-usage-account",
3234
+ role: "status",
3235
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3236
+ className: "dsm-trae-usage-account-copy",
3237
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3238
+ className: "dsm-trae-usage-status",
3239
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3240
+ "aria-hidden": "true",
3241
+ className: "dsm-trae-usage-dot",
3242
+ style: dotStyle(status.status)
3243
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: statusLabel })]
3244
+ }), signedIn ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3245
+ className: "dsm-trae-usage-expiry",
3246
+ children: t("row.tokenExpiry", { expiresAt: formatDateTime(status.tokenExpiresAtMs) })
3247
+ }) : null]
3248
+ })
3249
+ }) : null,
3250
+ status.status !== "error" && rows.length > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3251
+ className: `dsm-trae-account-table${credentialRegionIsAi ? "" : " dsm-trae-account-table-checkin"}`,
3252
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3253
+ className: "dsm-trae-account-row dsm-trae-account-row-head",
3254
+ role: "row",
3255
+ children: [
3256
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("row.accountColumnAccount") }),
3257
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("row.accountColumnValue") }),
3258
+ credentialRegionIsAi ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("row.accountColumnCheckin") }),
3259
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {})
3260
+ ]
3261
+ }), rows.map((row) => {
3262
+ const warning = refreshTokenWarning(row.refreshExpiresAtMs, Date.now(), t, formatDateTime);
3263
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3264
+ className: `dsm-trae-account-row${row.current ? " dsm-trae-account-row-current" : ""}`,
3265
+ role: "row",
3266
+ children: [
3267
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
3268
+ className: "dsm-trae-account-who",
3269
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3270
+ "aria-hidden": "true",
3271
+ className: "dsm-trae-usage-dot",
3272
+ style: dotStyle(row.selected ? "signed-in" : "signed-out")
3273
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
3274
+ className: "dsm-trae-account-name",
3275
+ children: [
3276
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("b", { children: row.accountName }),
3277
+ row.current ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3278
+ className: "dsm-trae-account-badge",
3279
+ children: t("row.accountCurrentBadge")
3280
+ }) : null,
3281
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3282
+ className: "dsm-trae-account-meta",
3283
+ children: row.meta
3284
+ }),
3285
+ warning === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3286
+ className: `dsm-trae-refresh-warn dsm-trae-refresh-warn-${warning.tone}`,
3287
+ role: "status",
3288
+ children: warning.text
3289
+ }),
3290
+ row.error === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3291
+ className: "dsm-trae-account-note",
3292
+ role: "alert",
3293
+ children: row.error
3294
+ })
3295
+ ]
3296
+ })]
3297
+ }),
3298
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3299
+ className: "dsm-trae-account-worth",
3300
+ children: row.value
3301
+ }),
3302
+ credentialRegionIsAi ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3303
+ className: "dsm-trae-account-checkin-cell",
3304
+ children: row.claim === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ClaimCell, {
3305
+ claim: row.claim,
3306
+ t,
3307
+ onClaim: () => {
3308
+ onClaim(row.id);
3309
+ }
3310
+ })
3311
+ }),
3312
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3313
+ className: "dsm-trae-account-actions",
3314
+ children: row.current ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
3315
+ type: "button",
3316
+ className: "dsm-btn dsm-btn-outline dsm-trae-account-switch",
3317
+ disabled: switchingAccount || !writable,
3318
+ onClick: () => {
3319
+ onSwitchAccount(row.id);
3320
+ },
3321
+ children: switchingAccount ? t("row.accountSwitching") : t("row.accountSwitch")
3322
+ })
3323
+ })
3324
+ ]
3325
+ }, row.id);
3326
+ })]
3327
+ }) : null,
3328
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(AccountTransfer, {
3329
+ t,
3330
+ region: panel === "ai" ? "ai" : "cn",
3331
+ accounts: accounts.map((account) => ({
3332
+ id: account.id,
3333
+ accountName: account.accountName
3334
+ })),
3335
+ onChanged: onTransferChanged
3336
+ })
3337
+ ]
3338
+ }),
3339
+ signedIn && creditsUnavailable === "enterprise" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
3340
+ className: "dsm-trae-credits-unavailable",
3341
+ role: "note",
3342
+ children: t("row.enterpriseCreditsNotice")
3343
+ }) : null,
3344
+ signedIn && alternatives !== void 0 && alternatives.length > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3345
+ className: "dsm-trae-usage-alternative",
3346
+ role: "status",
3347
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("p", {
3348
+ className: "dsm-trae-usage-alternative-text",
3349
+ children: [
3350
+ t("row.creditsExhausted", { account: status.accountName }),
3351
+ " ",
3352
+ t("row.creditsAlternative", { accounts: 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("、") })
3353
+ ]
3354
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
3355
+ type: "button",
3356
+ className: "dsm-trae-usage-switch-button",
3357
+ disabled: switchingAccount || !writable,
3358
+ onClick: () => {
3359
+ const [target] = alternatives;
3360
+ if (target !== void 0) onSwitchToAlternative(target.id);
3361
+ },
3362
+ children: t("row.creditsAlternativeSwitch")
3363
+ })]
3364
+ }) : null,
3365
+ signedIn && creditsExpiry !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ExpiryNotice, {
3366
+ expiry: creditsExpiry,
3367
+ t
3368
+ }) : null,
3369
+ rescanNote === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
3370
+ className: "dsm-trae-usage-note",
3371
+ role: "status",
3372
+ children: rescanNote
3373
+ }),
3374
+ signedIn ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
3375
+ pool !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(AccountPool, {
3376
+ panel,
3377
+ pool,
3378
+ models,
3379
+ t,
3380
+ settingsScope,
3381
+ topActions: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
3382
+ type: "button",
3383
+ className: "dsm-btn dsm-btn-outline",
3384
+ disabled: busy,
3385
+ onClick: onRescan,
3386
+ children: busy ? t("row.accountsScanning") : t("row.poolRescan")
3387
+ }),
3388
+ tools: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3389
+ className: "dsm-trae-pool-tool-row",
3390
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3391
+ className: "dsm-trae-pool-tool-label",
3392
+ children: t("row.poolToolsLabel")
3393
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(AccountTransfer, {
3394
+ t,
3395
+ hideLabel: true,
3396
+ region: panel === "ai" ? "ai" : "cn",
3397
+ accounts: accounts.map((account) => ({
3398
+ id: account.id,
3399
+ accountName: account.accountName
3400
+ })),
3401
+ onChanged: onTransferChanged
3402
+ })]
3403
+ }),
3404
+ details: poolDetails,
3405
+ claims: pool.checkinSupported === true ? poolClaims : void 0,
3406
+ onClaim,
3407
+ deviceClaimed,
3408
+ onBind: onSwitchAccount,
3409
+ bindingBusy: switchingAccount,
3410
+ onChanged: onPoolChanged,
3411
+ onRefresh: onPoolRefresh
3412
+ }) : null,
3413
+ status.checkin !== void 0 && !status.checkin.enabled ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
3414
+ className: "dsm-trae-usage-error",
3415
+ children: t("row.checkinDisabled")
3416
+ }) : null,
3417
+ status.checkinError === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
3418
+ className: "dsm-trae-usage-error",
3419
+ children: t("row.checkinError", { message: status.checkinError })
3420
+ })
3421
+ ] }) : null
3422
+ ] });
3423
+ }
3424
+ //#endregion
2931
3425
  //#region src/client/plan-text.ts
2932
3426
  /**
2933
3427
  * The international region's "what plan is this account on" text.
@@ -3023,55 +3517,6 @@ window.__ModuleLoader__.load({
3023
3517
  document.head.appendChild(styleTag);
3024
3518
  }
3025
3519
  }
3026
- function formatNumber(value) {
3027
- return new Intl.NumberFormat(void 0, {
3028
- minimumFractionDigits: 0,
3029
- maximumFractionDigits: 0
3030
- }).format(value);
3031
- }
3032
- function formatDateTime(value) {
3033
- return new Intl.DateTimeFormat(void 0, {
3034
- year: "numeric",
3035
- month: "2-digit",
3036
- day: "2-digit",
3037
- hour: "2-digit",
3038
- minute: "2-digit"
3039
- }).format(new Date(value));
3040
- }
3041
- /** How close a refresh token must be to lapsing before the card warns, in days. */
3042
- const REFRESH_WARN_DAYS = 30;
3043
- /**
3044
- * The refresh-token warning for one account, or undefined when there is nothing
3045
- * worth saying.
3046
- *
3047
- * The access token renews itself every few minutes, so its own expiry answers
3048
- * "when does this need attention" with "never, until the refresh token lapses".
3049
- * The refresh token is therefore the only honest countdown on this card, and it
3050
- * has to be shown BEFORE it runs out: once it lapses the plugin cannot renew at
3051
- * all and the only recovery is signing in to Trae again, which a user who has
3052
- * been reading "auto-renewal" for months has no reason to expect.
3053
- *
3054
- * Three tones, matching the credit-expiry notice: `lapsed` (renewal already
3055
- * dead), `soon` (inside the warning window), and nothing otherwise — a healthy
3056
- * token must not add a line to every account row forever.
3057
- */
3058
- function refreshTokenWarning(expiresAtMs, nowMs, t, formatTime) {
3059
- if (expiresAtMs === void 0) return void 0;
3060
- const text = formatTime(expiresAtMs);
3061
- if (expiresAtMs <= nowMs) return {
3062
- tone: "lapsed",
3063
- text: t("row.refreshTokenLapsed", { expiresAt: text })
3064
- };
3065
- const days = Math.ceil((expiresAtMs - nowMs) / 864e5);
3066
- if (days > REFRESH_WARN_DAYS) return void 0;
3067
- return {
3068
- tone: "soon",
3069
- text: t("row.refreshTokenExpiring", {
3070
- expiresAt: text,
3071
- days
3072
- })
3073
- };
3074
- }
3075
3520
  function formatCapacity(value, unknown) {
3076
3521
  if (value === void 0) return unknown;
3077
3522
  if (value >= 1e6 && value % 1e6 === 0) return `${value / 1e6}M`;
@@ -3167,56 +3612,6 @@ window.__ModuleLoader__.load({
3167
3612
  })
3168
3613
  });
3169
3614
  }
3170
- function dotStyle(status) {
3171
- return { background: status === "signed-in" ? "var(--dsw-alias-state-success-primary, #22a06b)" : status === "error" ? "var(--dsw-alias-state-error-primary, #d92d20)" : "var(--dsw-alias-label-dimmed, #9aa0a6)" };
3172
- }
3173
- /**
3174
- * A warning that credit is about to lapse.
3175
- *
3176
- * The balance alone cannot express this: a pack lapsing in an hour and one
3177
- * lapsing next month both render as "150", so a user watching only the total
3178
- * loses the first one silently. The notice names the amount, the pack count and
3179
- * the soonest expiry, and escalates 7d → 24h → already-lapsed.
3180
- *
3181
- * Renders nothing when the upstream reported no end times at all — absence of
3182
- * expiry data is not evidence that nothing lapses, so the component stays
3183
- * silent rather than reassuring.
3184
- */
3185
- function ExpiryNotice({ expiry, t, formatNumber: fmt, formatDateTime: fmtTime }) {
3186
- const { expired, within24h, within7d } = expiry;
3187
- let message;
3188
- let soonest;
3189
- let tone;
3190
- if (within24h.packs > 0) {
3191
- message = t("row.expiryWarningSoon", {
3192
- total: fmt(within24h.total),
3193
- packs: String(within24h.packs)
3194
- });
3195
- soonest = within24h.earliestExpiresAtMs;
3196
- tone = "dsm-trae-expiry-soon";
3197
- } else if (within7d.packs > 0) {
3198
- message = t("row.expiryWarningWeek", {
3199
- total: fmt(within7d.total),
3200
- packs: String(within7d.packs)
3201
- });
3202
- soonest = within7d.earliestExpiresAtMs;
3203
- tone = "dsm-trae-expiry-week";
3204
- } else if (expired.packs > 0) {
3205
- message = t("row.expiryWarningLapsed", {
3206
- total: fmt(expired.total),
3207
- packs: String(expired.packs)
3208
- });
3209
- tone = "dsm-trae-expiry-lapsed";
3210
- } else return null;
3211
- return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3212
- className: `dsm-trae-expiry ${tone}`,
3213
- role: "status",
3214
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: message }), soonest === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3215
- className: "dsm-trae-expiry-detail",
3216
- children: t("row.expiryEarliest", { time: fmtTime(soonest) })
3217
- })]
3218
- });
3219
- }
3220
3615
  /** Render Trae sign-in state and the total usage summary as one expandable card. */
3221
3616
  function TraeUsageCard({ t, settingsScope, view }) {
3222
3617
  if (t === void 0) throw new Error("Trae usage card requires its translation function");
@@ -3224,6 +3619,27 @@ window.__ModuleLoader__.load({
3224
3619
  /** The region whose tab is on screen; each tab is its own provider stack. */
3225
3620
  const [activeRegion, setActiveRegion] = (0, react.useState)("cn");
3226
3621
  /**
3622
+ * Which of the card's three sections is on screen (learned from the sibling
3623
+ * `dsh-connect-workbuddy` card, whose settings page split for the same
3624
+ * reason).
3625
+ *
3626
+ * The card had grown into one long column mixing two unrelated jobs —
3627
+ * operating an account (who serves, how much is left, the check-in, the pool)
3628
+ * and editing the catalog (which models, image input, context budget) — and
3629
+ * the two do not even commit the same way: an account change lands on the
3630
+ * click, a catalog change is a draft until 「保存」. One column therefore put
3631
+ * the save button screens away from what it saves.
3632
+ *
3633
+ * The region row above stays the SCOPE (which provider this whole card is
3634
+ * about); these tabs are the sections of that one scope. Only the ACTIVE
3635
+ * section is visible, and the others stay MOUNTED through the `hidden`
3636
+ * attribute, so switching back never refetches, a half-filled model draft
3637
+ * survives the trip, and the pool keeps its log. `hidden` also takes the
3638
+ * inactive panel out of the accessibility tree, which is exactly what
3639
+ * `role="tabpanel"` wants.
3640
+ */
3641
+ const [panel, setPanel] = (0, react.useState)("accounts");
3642
+ /**
3227
3643
  * Last-known usage per region, so tab dots survive tab switches.
3228
3644
  *
3229
3645
  * A region is ABSENT until something has been read for it (issue #77). It used
@@ -3571,6 +3987,16 @@ window.__ModuleLoader__.load({
3571
3987
  accounts: []
3572
3988
  };
3573
3989
  /**
3990
+ * Whether the ACTIVE panel has a usable sign-in.
3991
+ *
3992
+ * The section tabs render only here: a signed-out card is one screen of
3993
+ * instructions ("sign in once in the Trae app" plus the probed paths), and
3994
+ * splitting that into panels would hide the only thing it has to say behind a
3995
+ * tab the user cannot see the point of. The sibling WorkBuddy card made the
3996
+ * same call for the same reason.
3997
+ */
3998
+ const signedIn = status.status === "signed-in";
3999
+ /**
3574
4000
  * Clear in-flight claim state when the tab changes. `activeRegion` is a single
3575
4001
  * value across both tabs, so a switch must not leave one row's claim spinning
3576
4002
  * under the other tab.
@@ -4143,7 +4569,7 @@ window.__ModuleLoader__.load({
4143
4569
  imageModelIds: [...activeImageIds].filter((id) => activeEnabledIds.has(id)),
4144
4570
  contextBudgets: activeContextBudgets
4145
4571
  };
4146
- await writeSettingsField(settingsScope, "regions", nextRegionSlots(configuredRegions, activeRegion, nextSlot), (readBack) => {
4572
+ await writeSettingsField(settingsScope, "regions", nextRegionDirectory(configuredRegions, activeRegion, nextSlot), (readBack) => {
4147
4573
  const ids = regionSlotsOf(readBack)[activeRegion]?.enabledModelIds;
4148
4574
  return Array.isArray(ids) && ids.length === nextSlot.enabledModelIds.length && nextSlot.enabledModelIds.every((id) => ids.includes(id));
4149
4575
  });
@@ -4262,23 +4688,63 @@ window.__ModuleLoader__.load({
4262
4688
  settled: claimed ? "claimed" : deviceSpent ? "device" : void 0
4263
4689
  };
4264
4690
  };
4265
- const checkinControl = (account, state) => {
4266
- const view = claimViewOf(account.id, state);
4267
- if (!view.claimable) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
4268
- className: "dsm-trae-account-checkin-state",
4269
- children: view.settled === "claimed" ? t("row.poolCheckedIn") : t("row.poolDeviceUsed")
4270
- });
4271
- return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
4272
- type: "button",
4273
- className: "dsm-trae-account-checkin",
4274
- disabled: view.disabled,
4275
- title: view.title,
4276
- onClick: () => {
4277
- claimFor(account.id);
4278
- },
4279
- children: view.label
4280
- });
4281
- };
4691
+ /**
4692
+ * The account roster, ALREADY JUDGED — one row per locally detected sign-in.
4693
+ *
4694
+ * Every verdict the table draws is decided here rather than in the section
4695
+ * component, because each one is a rule with a write or a Host answer behind
4696
+ * it: which account is serving (the document's own `accountId`, or the
4697
+ * roster's `selected` flag when signed out), what an account is worth
4698
+ * (`rowValue`, judged by CREDENTIAL region so the enterprise panel reads CN
4699
+ * balances), and whether a row may claim (`claimViewOf`, which knows the
4700
+ * device-wide check-in rule). A section that re-derived any of them could
4701
+ * disagree with the pool table below it, which reads the same three answers.
4702
+ */
4703
+ const accountRows = status.status === "error" ? [] : (status.accounts ?? []).map((account) => {
4704
+ const current = status.status === "signed-in" ? account.id === status.accountId : account.selected === true;
4705
+ const state = accountStatusByRegion[activeRegion][account.id];
4706
+ const error = claimErrorByRegion[activeRegion][account.id];
4707
+ return {
4708
+ id: account.id,
4709
+ accountName: account.accountName,
4710
+ current,
4711
+ selected: account.selected === true,
4712
+ meta: `${editionLabel(account.edition)} · ${sourceLabel(account.source)}`,
4713
+ value: rowValue(state),
4714
+ refreshExpiresAtMs: account.refreshExpiresAtMs,
4715
+ error: error === void 0 ? void 0 : t("row.checkinError", { message: error }),
4716
+ claim: isCnPool ? (() => {
4717
+ const view = claimViewOf(account.id, state);
4718
+ return {
4719
+ label: view.label,
4720
+ title: view.title,
4721
+ disabled: view.disabled,
4722
+ busy: view.busy,
4723
+ claimable: view.claimable,
4724
+ settled: view.settled
4725
+ };
4726
+ })() : void 0
4727
+ };
4728
+ });
4729
+ /**
4730
+ * The pool's per-row details, joined from the roster above by id.
4731
+ *
4732
+ * The pool document carries the accounts but not their edition, source or
4733
+ * refresh-token countdown, so those three are spliced in here — by the same
4734
+ * `editionLabel` / `sourceLabel` / `refreshTokenWarning` the roster uses, so
4735
+ * one account cannot read "国内版 · 桌面端" in one table and something else in
4736
+ * the next.
4737
+ */
4738
+ const poolDetails = Object.fromEntries((status.status === "signed-in" ? status.accounts : []).map((account) => {
4739
+ const warn = refreshTokenWarning(account.refreshExpiresAtMs, Date.now(), t, formatDateTime);
4740
+ return [account.id, {
4741
+ meta: `${editionLabel(account.edition)} · ${sourceLabel(account.source)}`,
4742
+ ...warn === void 0 ? {} : {
4743
+ warn: warn.text,
4744
+ warnTone: warn.tone
4745
+ }
4746
+ }];
4747
+ }));
4282
4748
  const label = loading ? t("row.accountsLoading") : status.status === "signed-in" ? t("row.signedIn", { accountName: status.accountName }) : status.status === "error" ? t("row.requestFailed") : t("row.signedOut");
4283
4749
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("li", {
4284
4750
  className: `dsm-plugin-card${open ? " dsm-plugin-card-open" : ""}`,
@@ -4372,324 +4838,127 @@ window.__ModuleLoader__.load({
4372
4838
  role: "status",
4373
4839
  children: t("row.tabOffNotice")
4374
4840
  }),
4375
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4376
- className: "dsm-trae-prefs",
4377
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
4378
- className: "dsm-trae-pref",
4379
- title: t(isCnPool ? "row.showPointsInMainUiHint" : "row.showPointsInMainUiHintAi"),
4380
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
4381
- type: "checkbox",
4382
- className: "dsm-trae-pref-check",
4383
- checked: settingsEdits.showPointsInMainUi,
4384
- disabled: settingsScope?.getSnapshot().writable !== true || savingSettings,
4385
- "aria-describedby": creditsHintId,
4386
- onChange: (event) => {
4387
- writePreference("showPointsInMainUi", event.currentTarget.checked);
4388
- }
4389
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
4390
- className: "dsm-trae-pref-copy",
4391
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
4392
- className: "dsm-trae-pref-label",
4393
- id: creditsLabelId,
4394
- children: t("row.showPointsInMainUi")
4395
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
4396
- className: "dsm-trae-pref-hint",
4397
- id: creditsHintId,
4398
- children: t(isCnPool ? "row.showPointsInMainUiHint" : "row.showPointsInMainUiHintAi")
4399
- })]
4400
- })]
4401
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("label", {
4402
- className: "dsm-trae-pref dsm-trae-credit-position-pref",
4403
- title: t("row.creditPositionHint"),
4404
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
4405
- className: "dsm-trae-pref-copy",
4406
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
4407
- className: "dsm-trae-pref-lead",
4408
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
4409
- className: "dsm-trae-pref-label",
4410
- children: t("row.creditPositionLabel")
4411
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
4412
- className: "dsm-trae-credit-position",
4413
- value: activeRegionOn ? creditPositionOn(activeRegion) : "composer",
4414
- disabled: !activeRegionOn || settingsScope?.getSnapshot().writable !== true || savingSettings,
4415
- "aria-label": t("row.creditPositionAria", { region: activeRegion === "cn" ? t("row.tabCn") : activeRegion === "enterprise" ? t("row.tabEnterprise") : t("row.tabAi") }),
4416
- onChange: (event) => {
4417
- changeCreditPosition(activeRegion, event.currentTarget.value === "stats" ? "stats" : "composer");
4418
- },
4419
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
4420
- value: "composer",
4421
- children: t("row.creditPositionComposer")
4422
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
4423
- value: "stats",
4424
- children: t("row.creditPositionStats")
4425
- })]
4426
- })]
4427
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
4428
- className: "dsm-trae-pref-hint",
4429
- children: t("row.creditPositionHint")
4430
- })]
4431
- })
4432
- })]
4433
- }),
4434
- status.status === "signed-in" && status.pool !== void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
4435
- className: "dsm-trae-accounts",
4436
- "aria-label": t("row.accountsTitle"),
4841
+ signedIn ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4842
+ className: "dsm-trae-panels",
4843
+ role: "tablist",
4844
+ "aria-label": t("row.panelAria"),
4437
4845
  children: [
4438
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4439
- className: "dsm-trae-accounts-head",
4440
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", {
4441
- className: "dsm-trae-accounts-title",
4442
- children: t("row.accountsTitle")
4443
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
4444
- className: "dsm-trae-accounts-hint",
4445
- children: t("row.accountsHint")
4446
- })] }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
4447
- type: "button",
4448
- className: "dsm-btn dsm-btn-outline",
4449
- disabled: busy,
4450
- onClick: () => {
4451
- rescanAccounts();
4452
- },
4453
- children: busy ? t("row.accountsScanning") : t("row.refreshTokens")
4454
- })]
4846
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
4847
+ type: "button",
4848
+ role: "tab",
4849
+ id: "dsm-trae-tab-accounts",
4850
+ "aria-controls": "dsm-trae-panel-accounts",
4851
+ "aria-selected": panel === "accounts",
4852
+ className: `dsm-trae-panel-tab${panel === "accounts" ? " dsm-trae-panel-tab-active" : ""}`,
4853
+ onClick: () => {
4854
+ setPanel("accounts");
4855
+ },
4856
+ children: t("row.panelAccounts")
4455
4857
  }),
4456
- status.status !== "signed-in" || status.accounts.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
4457
- className: "dsm-trae-usage-account",
4458
- role: "status",
4459
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4460
- className: "dsm-trae-usage-account-copy",
4461
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4462
- className: "dsm-trae-usage-status",
4463
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
4464
- "aria-hidden": "true",
4465
- className: "dsm-trae-usage-dot",
4466
- style: dotStyle(status.status)
4467
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: label })]
4468
- }), status.status === "signed-in" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
4469
- className: "dsm-trae-usage-expiry",
4470
- children: t("row.tokenExpiry", { expiresAt: formatDateTime(status.tokenExpiresAtMs) })
4471
- }) : null]
4472
- })
4473
- }) : null,
4474
- status.status !== "error" && status.accounts.length > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4475
- className: `dsm-trae-account-table${isCnPool ? " dsm-trae-account-table-checkin" : ""}`,
4476
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4477
- className: "dsm-trae-account-row dsm-trae-account-row-head",
4478
- role: "row",
4479
- children: [
4480
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("row.accountColumnAccount") }),
4481
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("row.accountColumnValue") }),
4482
- isCnPool ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("row.accountColumnCheckin") }) : null,
4483
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {})
4484
- ]
4485
- }), status.accounts.map((account) => {
4486
- const current = status.status === "signed-in" ? account.id === status.accountId : account.selected === true;
4487
- const state = accountStatusByRegion[activeRegion][account.id];
4488
- const error = claimErrorByRegion[activeRegion][account.id];
4489
- return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4490
- className: `dsm-trae-account-row${current ? " dsm-trae-account-row-current" : ""}`,
4491
- role: "row",
4492
- children: [
4493
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
4494
- className: "dsm-trae-account-who",
4495
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
4496
- "aria-hidden": "true",
4497
- className: "dsm-trae-usage-dot",
4498
- style: dotStyle(account.selected ? "signed-in" : "signed-out")
4499
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
4500
- className: "dsm-trae-account-name",
4501
- children: [
4502
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("b", { children: account.accountName }),
4503
- current ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
4504
- className: "dsm-trae-account-badge",
4505
- children: t("row.accountCurrentBadge")
4506
- }) : null,
4507
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
4508
- className: "dsm-trae-account-meta",
4509
- children: [
4510
- editionLabel(account.edition),
4511
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("i", {
4512
- "aria-hidden": "true",
4513
- children: "·"
4514
- }),
4515
- sourceLabel(account.source)
4516
- ]
4517
- }),
4518
- (() => {
4519
- const warning = refreshTokenWarning(account.refreshExpiresAtMs, Date.now(), t, formatDateTime);
4520
- return warning === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
4521
- className: `dsm-trae-refresh-warn dsm-trae-refresh-warn-${warning.tone}`,
4522
- role: "status",
4523
- children: warning.text
4524
- });
4525
- })(),
4526
- error === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
4527
- className: "dsm-trae-account-note",
4528
- role: "alert",
4529
- children: t("row.checkinError", { message: error })
4530
- })
4531
- ]
4532
- })]
4533
- }),
4534
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
4535
- className: "dsm-trae-account-worth",
4536
- children: rowValue(state)
4537
- }),
4538
- isCnPool ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
4539
- className: "dsm-trae-account-checkin-cell",
4540
- children: checkinControl(account, state)
4541
- }) : null,
4542
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
4543
- className: "dsm-trae-account-actions",
4544
- children: current ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
4545
- type: "button",
4546
- className: "dsm-btn dsm-btn-outline dsm-trae-account-switch",
4547
- disabled: switchingAccount || settingsScope?.getSnapshot()?.writable !== true,
4548
- onClick: () => {
4549
- switchAccount(account.id);
4550
- },
4551
- children: switchingAccount ? t("row.accountSwitching") : t("row.accountSwitch")
4552
- })
4553
- })
4554
- ]
4555
- }, account.id);
4556
- })]
4557
- }) : null,
4558
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(AccountTransfer, {
4559
- t,
4560
- region: activeRegion === "ai" ? "ai" : "cn",
4561
- accounts: status.status === "signed-in" ? status.accounts.map((account) => ({
4562
- id: account.id,
4563
- accountName: account.accountName
4564
- })) : [],
4565
- onChanged: async () => {
4566
- const fresh = await refreshUsage(activeRegion);
4567
- if (fresh !== void 0) setStatusByRegion((current) => ({
4568
- ...current,
4569
- [activeRegion]: fresh
4570
- }));
4571
- }
4858
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
4859
+ type: "button",
4860
+ role: "tab",
4861
+ id: "dsm-trae-tab-models",
4862
+ "aria-controls": "dsm-trae-panel-models",
4863
+ "aria-selected": panel === "models",
4864
+ className: `dsm-trae-panel-tab${panel === "models" ? " dsm-trae-panel-tab-active" : ""}`,
4865
+ onClick: () => {
4866
+ setPanel("models");
4867
+ },
4868
+ children: t("row.panelModels")
4869
+ }),
4870
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
4871
+ type: "button",
4872
+ role: "tab",
4873
+ id: "dsm-trae-tab-diag",
4874
+ "aria-controls": "dsm-trae-panel-diag",
4875
+ "aria-selected": panel === "diag",
4876
+ className: `dsm-trae-panel-tab${panel === "diag" ? " dsm-trae-panel-tab-active" : ""}`,
4877
+ onClick: () => {
4878
+ setPanel("diag");
4879
+ },
4880
+ children: t("row.panelDiag")
4572
4881
  })
4573
4882
  ]
4574
- }),
4575
- status.status === "signed-in" && status.creditsUnavailable === "enterprise" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
4576
- className: "dsm-trae-credits-unavailable",
4577
- role: "note",
4578
- children: t("row.enterpriseCreditsNotice")
4579
4883
  }) : null,
4580
- status.status === "signed-in" && status.alternatives !== void 0 && status.alternatives.length > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4581
- className: "dsm-trae-usage-alternative",
4582
- role: "status",
4583
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("p", {
4584
- className: "dsm-trae-usage-alternative-text",
4585
- children: [
4586
- t("row.creditsExhausted", { account: status.accountName }),
4587
- " ",
4588
- 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("、") })
4589
- ]
4590
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
4591
- type: "button",
4592
- className: "dsm-trae-usage-switch-button",
4593
- disabled: switchingAccount || settingsScope?.getSnapshot()?.writable !== true,
4594
- onClick: () => {
4595
- const target = status.alternatives?.[0];
4596
- if (target !== void 0) switchAccount(target.id);
4597
- },
4598
- children: t("row.creditsAlternativeSwitch")
4599
- })]
4600
- }) : null,
4601
- status.status === "signed-in" && status.credits?.expiry !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ExpiryNotice, {
4602
- expiry: status.credits.expiry,
4603
- t,
4604
- formatNumber,
4605
- formatDateTime
4606
- }) : null,
4607
- rescanNote === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
4608
- className: "dsm-trae-usage-note",
4609
- role: "status",
4610
- children: rescanNote
4611
- }),
4612
- status.status === "signed-in" ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
4613
- status.pool !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(AccountPool, {
4614
- panel: activeRegion,
4615
- pool: status.pool,
4616
- models: visibleModels,
4884
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
4885
+ className: "dsm-trae-panel",
4886
+ role: "tabpanel",
4887
+ id: "dsm-trae-panel-accounts",
4888
+ "aria-labelledby": "dsm-trae-tab-accounts",
4889
+ hidden: signedIn && panel !== "accounts",
4890
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(TraeAccountPanel, {
4617
4891
  t,
4618
4892
  settingsScope,
4619
- topActions: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
4620
- type: "button",
4621
- className: "dsm-btn dsm-btn-outline",
4622
- disabled: busy,
4623
- onClick: () => {
4624
- rescanAccounts();
4625
- },
4626
- children: busy ? t("row.accountsScanning") : t("row.poolRescan")
4627
- }),
4628
- tools: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4629
- className: "dsm-trae-pool-tool-row",
4630
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
4631
- className: "dsm-trae-pool-tool-label",
4632
- children: t("row.poolToolsLabel")
4633
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(AccountTransfer, {
4634
- t,
4635
- hideLabel: true,
4636
- region: activeRegion === "ai" ? "ai" : "cn",
4637
- accounts: status.accounts.map((account) => ({
4638
- id: account.id,
4639
- accountName: account.accountName
4640
- })),
4641
- onChanged: async () => {
4642
- const fresh = await refreshUsage(activeRegion);
4643
- if (fresh !== void 0) setStatusByRegion((current) => ({
4644
- ...current,
4645
- [activeRegion]: fresh
4646
- }));
4647
- }
4648
- })]
4649
- }),
4650
- details: Object.fromEntries(status.accounts.map((account) => {
4651
- const warn = refreshTokenWarning(account.refreshExpiresAtMs, Date.now(), t, formatDateTime);
4652
- return [account.id, {
4653
- meta: `${editionLabel(account.edition)} · ${sourceLabel(account.source)}`,
4654
- ...warn === void 0 ? {} : {
4655
- warn: warn.text,
4656
- warnTone: warn.tone
4657
- }
4658
- }];
4659
- })),
4660
- claims: status.pool.checkinSupported === true ? Object.fromEntries(status.accounts.map((account) => [account.id, claimViewOf(account.id, accountStatusByRegion[activeRegion][account.id])])) : void 0,
4893
+ panel: activeRegion,
4894
+ status,
4895
+ statusLabel: label,
4896
+ credentialRegionIsAi,
4897
+ regionOn: activeRegionOn,
4898
+ showPointsInMainUi: settingsEdits.showPointsInMainUi,
4899
+ onToggleShowPoints: (value) => {
4900
+ writePreference("showPointsInMainUi", value);
4901
+ },
4902
+ creditPosition: creditPositionOn(activeRegion),
4903
+ onCreditPositionChange: (position) => {
4904
+ changeCreditPosition(activeRegion, position);
4905
+ },
4906
+ savingPreferences: savingSettings,
4907
+ creditsLabelId,
4908
+ creditsHintId,
4909
+ rows: accountRows,
4910
+ busy,
4911
+ rescanNote,
4912
+ onRescan: () => {
4913
+ rescanAccounts();
4914
+ },
4915
+ switchingAccount,
4916
+ onSwitchAccount: (accountId) => {
4917
+ switchAccount(accountId);
4918
+ },
4661
4919
  onClaim: (accountId) => {
4662
4920
  claimFor(accountId);
4663
4921
  },
4664
- deviceClaimed: deviceClaimedToday,
4665
- onBind: (accountId) => {
4666
- switchAccount(accountId);
4922
+ onTransferChanged: async () => {
4923
+ const fresh = await refreshUsage(activeRegion);
4924
+ if (fresh !== void 0) setStatusByRegion((current) => ({
4925
+ ...current,
4926
+ [activeRegion]: fresh
4927
+ }));
4667
4928
  },
4668
- bindingBusy: switchingAccount,
4669
- onChanged: async () => {
4929
+ poolDetails,
4930
+ poolClaims: status.status === "signed-in" && status.pool?.checkinSupported === true ? Object.fromEntries(status.accounts.map((account) => [account.id, claimViewOf(account.id, accountStatusByRegion[activeRegion][account.id])])) : void 0,
4931
+ models: visibleModels,
4932
+ deviceClaimed: deviceClaimedToday,
4933
+ onPoolChanged: async () => {
4670
4934
  const fresh = await refreshUsage(activeRegion);
4671
4935
  if (fresh !== void 0) setStatusByRegion((current) => ({
4672
4936
  ...current,
4673
4937
  [activeRegion]: fresh
4674
4938
  }));
4675
4939
  },
4676
- onRefresh: async () => {
4940
+ onPoolRefresh: async () => {
4677
4941
  const fresh = await refreshUsage(activeRegion);
4678
4942
  if (fresh !== void 0) setStatusByRegion((current) => ({
4679
4943
  ...current,
4680
4944
  [activeRegion]: fresh
4681
4945
  }));
4946
+ },
4947
+ creditsUnavailable: status.status === "signed-in" ? status.creditsUnavailable : void 0,
4948
+ creditsExpiry: status.status === "signed-in" ? status.credits?.expiry : void 0,
4949
+ alternatives: status.status === "signed-in" ? status.alternatives : void 0,
4950
+ onSwitchToAlternative: (accountId) => {
4951
+ switchAccount(accountId);
4682
4952
  }
4683
- }) : null,
4684
- status.checkin !== void 0 && !status.checkin.enabled ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
4685
- className: "dsm-trae-usage-error",
4686
- children: t("row.checkinDisabled")
4687
- }) : null,
4688
- status.checkinError === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
4689
- className: "dsm-trae-usage-error",
4690
- children: t("row.checkinError", { message: status.checkinError })
4691
- }),
4692
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
4953
+ })
4954
+ }),
4955
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
4956
+ className: "dsm-trae-panel",
4957
+ role: "tabpanel",
4958
+ id: "dsm-trae-panel-models",
4959
+ "aria-labelledby": "dsm-trae-tab-models",
4960
+ hidden: !signedIn || panel !== "models",
4961
+ children: signedIn ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_jsx_runtime.Fragment, { children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
4693
4962
  className: "dsm-trae-models",
4694
4963
  "aria-label": t("row.modelsTitle"),
4695
4964
  children: [
@@ -4883,8 +5152,143 @@ window.__ModuleLoader__.load({
4883
5152
  })]
4884
5153
  })
4885
5154
  ]
4886
- })
4887
- ] }) : null,
5155
+ }) }) : null
5156
+ }),
5157
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
5158
+ className: "dsm-trae-panel",
5159
+ role: "tabpanel",
5160
+ id: "dsm-trae-panel-diag",
5161
+ "aria-labelledby": "dsm-trae-tab-diag",
5162
+ hidden: !signedIn || panel !== "diag",
5163
+ children: status.status !== "signed-in" || panel !== "diag" ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
5164
+ className: "dsm-trae-diag",
5165
+ children: [
5166
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
5167
+ className: "dsm-trae-diag-row",
5168
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
5169
+ className: "dsm-trae-diag-label",
5170
+ children: t("row.diagRegion")
5171
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
5172
+ className: "dsm-trae-diag-value",
5173
+ children: activeRegion === "cn" ? t("row.tabCn") : activeRegion === "enterprise" ? t("row.tabEnterprise") : t("row.tabAi")
5174
+ })]
5175
+ }),
5176
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
5177
+ className: "dsm-trae-diag-row",
5178
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
5179
+ className: "dsm-trae-diag-label",
5180
+ children: t("row.diagProvider")
5181
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
5182
+ className: "dsm-trae-diag-value dsm-trae-diag-mono",
5183
+ children: providerOfPanel(activeRegion)
5184
+ })]
5185
+ }),
5186
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
5187
+ className: "dsm-trae-diag-row",
5188
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
5189
+ className: "dsm-trae-diag-label",
5190
+ children: t("row.diagAccount")
5191
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
5192
+ className: "dsm-trae-diag-value",
5193
+ children: status.accountName
5194
+ })]
5195
+ }),
5196
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
5197
+ className: "dsm-trae-diag-row",
5198
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
5199
+ className: "dsm-trae-diag-label",
5200
+ children: t("row.diagToken")
5201
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
5202
+ className: "dsm-trae-diag-value dsm-trae-diag-mono",
5203
+ children: formatDateTime(status.tokenExpiresAtMs)
5204
+ })]
5205
+ }),
5206
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
5207
+ className: "dsm-trae-diag-row",
5208
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
5209
+ className: "dsm-trae-diag-label",
5210
+ children: t("row.diagRefresh")
5211
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
5212
+ className: "dsm-trae-diag-value dsm-trae-diag-mono",
5213
+ children: status.refreshExpiresAtMs === void 0 ? t("row.diagRefreshNone") : formatDateTime(status.refreshExpiresAtMs)
5214
+ })]
5215
+ }),
5216
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
5217
+ className: "dsm-trae-diag-row",
5218
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
5219
+ className: "dsm-trae-diag-label",
5220
+ children: t("row.diagCatalog")
5221
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
5222
+ className: "dsm-trae-diag-value",
5223
+ children: formatNumber(status.models.length)
5224
+ })]
5225
+ }),
5226
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
5227
+ className: "dsm-trae-diag-row",
5228
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
5229
+ className: "dsm-trae-diag-label",
5230
+ children: t("row.diagEnabled")
5231
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
5232
+ className: "dsm-trae-diag-value",
5233
+ children: formatNumber(status.enabledModelIds.length)
5234
+ })]
5235
+ }),
5236
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
5237
+ className: "dsm-trae-diag-row",
5238
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
5239
+ className: "dsm-trae-diag-label",
5240
+ children: t("row.diagPool")
5241
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
5242
+ className: "dsm-trae-diag-value",
5243
+ children: status.pool === void 0 ? t("row.diagPoolOff") : status.pool.enabled ? t("row.diagPoolOn", { count: status.pool.effectiveMemberAccountIds?.length ?? status.pool.accounts.length }) : t("row.diagPoolOff")
5244
+ })]
5245
+ }),
5246
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
5247
+ className: "dsm-trae-diag-row",
5248
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
5249
+ className: "dsm-trae-diag-label",
5250
+ children: t("row.diagCheckin")
5251
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
5252
+ className: "dsm-trae-diag-value",
5253
+ children: status.checkin !== void 0 || status.pool?.checkinSupported === true ? t("row.diagCheckinOn") : t("row.diagCheckinOff")
5254
+ })]
5255
+ }),
5256
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
5257
+ className: "dsm-trae-diag-row",
5258
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
5259
+ className: "dsm-trae-diag-label",
5260
+ children: t("row.diagRaw")
5261
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
5262
+ className: "dsm-trae-diag-value dsm-trae-diag-mono",
5263
+ children: status.rawChat === void 0 ? t("row.diagRawOff") : status.rawChat.state
5264
+ })]
5265
+ }),
5266
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
5267
+ className: "dsm-trae-diag-row",
5268
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
5269
+ className: "dsm-trae-diag-label",
5270
+ children: t("row.diagFailures")
5271
+ }), (() => {
5272
+ const failures = [];
5273
+ if (status.creditsError !== void 0) failures.push(status.creditsError);
5274
+ if (status.payStatusError !== void 0) failures.push(status.payStatusError);
5275
+ if (status.checkinError !== void 0) failures.push(status.checkinError);
5276
+ if (writeError !== void 0) failures.push(writeError);
5277
+ return failures.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
5278
+ className: "dsm-trae-diag-value",
5279
+ children: t("row.diagNoFailures")
5280
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
5281
+ className: "dsm-trae-diag-value dsm-trae-diag-bad dsm-trae-diag-mono",
5282
+ children: failures.join(" · ")
5283
+ });
5284
+ })()]
5285
+ })
5286
+ ]
5287
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
5288
+ className: "dsm-trae-diag-hint",
5289
+ children: t("row.diagHint")
5290
+ })] })
5291
+ }),
4888
5292
  status.status === "signed-out" && loading ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_jsx_runtime.Fragment, { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
4889
5293
  className: "dsm-trae-usage-text",
4890
5294
  role: "status",
@@ -5634,6 +6038,29 @@ window.__ModuleLoader__.load({
5634
6038
  "row.tabSwitchHint": "Turns this provider off: its models leave the DSH picker. Account and settings are kept.",
5635
6039
  "row.tabSwitchAria": "Offer the {region} provider to DSH",
5636
6040
  "row.tabOffNotice": "This provider is off: its models are not offered to DSH. Account and settings are kept.",
6041
+ "row.panelAria": "Card sections",
6042
+ "row.panelAccounts": "Account",
6043
+ "row.panelModels": "Models",
6044
+ "row.panelDiag": "Diagnostics",
6045
+ "row.diagRegion": "Panel",
6046
+ "row.diagProvider": "Provider id",
6047
+ "row.diagAccount": "Serving account",
6048
+ "row.diagToken": "Access token expires",
6049
+ "row.diagRefresh": "Re-sign-in deadline",
6050
+ "row.diagRefreshNone": "this credential carries none",
6051
+ "row.diagCatalog": "Models in catalog",
6052
+ "row.diagEnabled": "Models enabled",
6053
+ "row.diagPool": "Account pool",
6054
+ "row.diagPoolOn": "on · {count} account(s)",
6055
+ "row.diagPoolOff": "off",
6056
+ "row.diagCheckin": "Daily check-in",
6057
+ "row.diagCheckinOn": "supported",
6058
+ "row.diagCheckinOff": "not offered here",
6059
+ "row.diagRaw": "Raw chat channel",
6060
+ "row.diagRawOff": "not checked",
6061
+ "row.diagFailures": "Last reported failure",
6062
+ "row.diagNoFailures": "none",
6063
+ "row.diagHint": "Everything above is read-only and can be quoted verbatim into a bug report. The values are this panel’s own last refresh, not a summary of it.",
5637
6064
  "row.settingsWriteFailed": "The setting was not saved: {message}",
5638
6065
  "row.signedOut": "Not signed in",
5639
6066
  "row.signedOutHint": "Sign in once in the Trae desktop app; this plugin follows that sign-in automatically.",
@@ -5960,6 +6387,29 @@ window.__ModuleLoader__.load({
5960
6387
  "row.tabSwitchHint": "关闭该供应商:其模型不再提供给 DSH;账号与设置保留,可随时恢复。",
5961
6388
  "row.tabSwitchAria": "是否向 DSH 提供{region}供应商",
5962
6389
  "row.tabOffNotice": "该供应商已关闭,模型不会提供给 DSH。账号与设置均已保留。",
6390
+ "row.panelAria": "卡片分栏",
6391
+ "row.panelAccounts": "账号",
6392
+ "row.panelModels": "模型",
6393
+ "row.panelDiag": "诊断",
6394
+ "row.diagRegion": "面板",
6395
+ "row.diagProvider": "供应商 id",
6396
+ "row.diagAccount": "服务账号",
6397
+ "row.diagToken": "访问令牌到期",
6398
+ "row.diagRefresh": "重新登录最后期限",
6399
+ "row.diagRefreshNone": "该凭据不携带",
6400
+ "row.diagCatalog": "目录模型数",
6401
+ "row.diagEnabled": "已启用模型数",
6402
+ "row.diagPool": "账号池",
6403
+ "row.diagPoolOn": "已开启 · {count} 个账号",
6404
+ "row.diagPoolOff": "未开启",
6405
+ "row.diagCheckin": "每日签到",
6406
+ "row.diagCheckinOn": "支持",
6407
+ "row.diagCheckinOff": "本面板不提供",
6408
+ "row.diagRaw": "Raw 会话通道",
6409
+ "row.diagRawOff": "未检测",
6410
+ "row.diagFailures": "最近一次报错",
6411
+ "row.diagNoFailures": "无",
6412
+ "row.diagHint": "以上全部只读,可整段原样贴进反馈。数值来自本面板最近一次刷新,不是概括。",
5963
6413
  "row.settingsWriteFailed": "设置未能保存:{message}",
5964
6414
  "row.signedOut": "未登录",
5965
6415
  "row.signedOutHint": "在 Trae 桌面 App 里登录一次即可,插件会自动跟随当前登录的账号。",