dsh-connect-trae 4.9.1 → 4.14.2

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
@@ -7,6 +7,253 @@ window.__ModuleLoader__.load({
7
7
  let react = require("react");
8
8
  let react_jsx_runtime = require("react/jsx-runtime");
9
9
  let react_dom = require("react-dom");
10
+ //#region src/client/series-layout.ts
11
+ /** 同系列的公共布局规则。两个桥接插件同步维护此文件,功能专属样式留在 styles.ts。 */
12
+ function seriesLayoutCss(plugin) {
13
+ const p = `.dsm-${plugin}`;
14
+ const u = `${p}-usage`;
15
+ const members = plugin === "trae" ? `${p}-pool-card` : `${p}-pool-members`;
16
+ const memberHead = plugin === "trae" ? `${p}-pool-card-head` : `${p}-pool-members-head`;
17
+ const memberTitle = plugin === "trae" ? `${p}-pool-card-title` : `${p}-pool-members-title`;
18
+ const memberActions = plugin === "trae" ? `${p}-pool-card-tools` : `${p}-pool-members-actions`;
19
+ const saveBar = plugin === "trae" ? `${p}-pool-save-row` : `${p}-pool-save-bar`;
20
+ return `
21
+ ${p}-page{font-family:inherit;--dsm-series-sticky-top:-28px}
22
+ ${p}-page${p}-page{height:calc(100% - 16px);margin-bottom:16px;padding:28px clamp(24px,4vw,48px) 48px;gap:20px}
23
+ ${u} ${p}-pool-auto-checkin-status{margin:0;font-size:12px;line-height:18px;color:var(--dsw-alias-label-secondary,#777)}
24
+ ${p}-page-desc{font-size:13px;line-height:20px;color:var(--dsw-alias-label-tertiary,#999)}
25
+ ${u} ${p}-pool-auto-checkin{display:flex;flex-direction:column;gap:6px;margin-top:8px}
26
+ ${p}-page>*{width:100%;max-width:860px;min-width:0}
27
+ ${u}{gap:16px;font-size:13px;line-height:1.5;min-width:0}
28
+ ${u} .dsm-btn{box-sizing:border-box;font:inherit;font-size:13px;line-height:20px;padding:5px 14px;border-radius:8px;min-height:32px}
29
+ ${u} .dsm-btn-outline{background:transparent;border-color:var(--dsw-alias-border-l2,#777);color:var(--dsw-alias-label-secondary,#999)}
30
+ ${u} ${p}-tabs{display:flex;gap:8px}
31
+ ${u} ${p}-tab-cell{flex:1;min-width:0;padding:8px 12px;border-radius:10px}
32
+ ${u} ${p}-panels{position:sticky;top:var(--dsm-series-sticky-top,0px);z-index:2;display:flex;gap:2px;margin:4px 0 0;padding:8px 0 0;border-bottom:1px solid var(--dsw-alias-border-l1,#777);background:var(--dsw-alias-bg-layer-2,#24262c)}
33
+ /* 吸顶背景覆盖卡片两侧内边距,顶部偏移抵消滚动容器的顶部内边距。 */
34
+ ${p}-page-body ${u} ${p}-panels{margin-inline:0;padding:8px 0;border:0;border-bottom:1px solid var(--dsw-alias-border-l2,#777);border-radius:0;background:var(--dsw-alias-bg-layer-3,#202126)}
35
+ ${p}-page[data-tabs-stuck="true"] ${p}-panels{margin-inline:-20px;padding-inline:20px;border-top:1px solid var(--dsw-alias-border-l2,#777);box-shadow:0 3px 6px rgba(0,0,0,.06)}
36
+ ${u} ${p}-panel-tab{font-size:13px;font-weight:600;line-height:20px;padding:7px 11px}
37
+ ${u} ${p}-panel{margin-top:0;min-width:0}
38
+ ${u} ${p}-models-summary,${u} ${p}-panel-note{margin:2px 0 0;font-size:12px;line-height:18px;color:var(--dsw-alias-label-tertiary,#999)}
39
+ ${u} ${p}-free-quota-warn{font-weight:600}
40
+ /* 说明段落的间距由容器统一控制,避免与卡片的纵向 gap 叠加。 */
41
+ ${u} ${p}-region-summary{display:flex;flex-direction:column;gap:2px}
42
+ ${u} ${p}-region-summary ${p}-models-summary{margin:0}
43
+ /* 页签底边已经分隔账号区,不在账号池顶部重复画线。 */
44
+ ${u} #dsm-${plugin}-panel-accounts ${p}-pool{border-top:0;padding-top:0}
45
+
46
+ ${u} ${p}-pool{display:flex;flex-direction:column;gap:12px;margin:0;padding:14px 0 0;border-top:1px solid var(--dsw-alias-border-l2,#777)}
47
+ ${u} ${p}-pool-head{display:flex;align-items:center;justify-content:space-between;gap:12px;flex-wrap:wrap}
48
+ ${u} ${p}-pool-title{margin:0;font-size:13px;font-weight:600;line-height:18px}
49
+ ${u} ${plugin === "trae" ? `${p}-pool-current` : `${p}-pool-current-badge`}{margin-left:auto;max-width:100%;overflow-wrap:anywhere;white-space:normal;padding:2px 8px;border-radius:999px;border:1px solid var(--dsw-alias-border-l2,#777);font-size:11px;font-weight:500;line-height:16px;background:var(--dsw-alias-bg-layer-2,#24262c);color:var(--dsw-alias-label-secondary,#999)}
50
+ ${u} ${p}-pool-settings${p}-pool-modebar{padding:12px 14px;gap:8px;border:1px solid var(--dsw-alias-border-l2,#777);border-radius:12px;background:color-mix(in srgb,var(--dsw-alias-brand-primary,#cf795b) 5%,var(--dsw-alias-bg-layer-2,#24262c))}
51
+ ${u} ${p}-pool-modebar-row{display:flex;align-items:center;gap:10px;flex-wrap:wrap;min-height:20px}
52
+ ${u} ${p}-pool-modebar-label{font-size:13px;font-weight:600;line-height:20px}
53
+ ${u} ${p}-pool-modebar-state{padding:1px 9px;font-size:12px;line-height:18px;border-radius:999px;color:var(--dsw-alias-label-secondary,#999);border:1px solid var(--dsw-alias-border-l2,#777);background:var(--dsw-alias-bg-layer-2,#24262c)}
54
+ ${u} ${p}-pool-note,${u} ${p}-pool-modebar ${p}-pool-note{margin:0;font-size:12px;line-height:18px;color:var(--dsw-alias-label-tertiary,#999)}
55
+ ${u} ${members}{display:flex;flex-direction:column;gap:0;min-width:0;border:1px solid var(--dsw-alias-border-l2,#777);border-radius:12px;background:var(--dsw-alias-bg-layer-2,#24262c);overflow:hidden}
56
+ ${u} ${memberHead}{display:flex;align-items:center;justify-content:space-between;flex-wrap:wrap;gap:12px;padding:14px 14px 12px}
57
+ ${u} ${memberTitle}{margin:0;font-size:13px;font-weight:600;line-height:18px;color:var(--dsw-alias-label-primary,#eee)}
58
+ ${u} ${memberHead} ${p}-pool-note{margin-top:4px}
59
+ ${u} ${memberActions}{display:flex;align-items:center;gap:8px;flex-wrap:wrap}
60
+ ${u} ${memberActions} .dsm-btn{padding:5px 14px;font-size:13px;line-height:20px}
61
+ ${u} ${p}-pool-count,${u} ${memberActions} ${p}-pool-hint{font-size:12px;line-height:18px;color:var(--dsw-alias-label-tertiary,#999)}
62
+ ${u} ${members} ${p}-pool-actions{display:flex;align-items:center;justify-content:space-between;flex-wrap:wrap;gap:8px;padding:0 14px 12px}
63
+ ${u} ${p}-pool-actions-end{margin-left:auto}
64
+ ${u} ${members} ${p}-pool-action-note{margin:0;padding:0 14px 12px;font-size:12px;line-height:18px}
65
+ ${u} ${members} ${p}-pool-table{border:0;border-top:1px solid var(--dsw-alias-border-l1,#777);border-radius:0}
66
+ ${u} ${p}-pool-row{padding:10px 14px;align-items:start;font-size:13px;line-height:18px}
67
+ ${u} ${p}-pool-row-head{padding:8px 14px;font-size:11px;line-height:16px;align-items:center}
68
+ ${u} ${p}-pool-row-current{background:color-mix(in srgb,var(--dsw-alias-brand-primary,#cf795b) 5%,var(--dsw-alias-bg-layer-2,#24262c));box-shadow:inset 3px 0 0 var(--dsw-alias-brand-primary,#cf795b)}
69
+ ${u} ${p}-pool-row-actions .dsm-btn{min-height:26px;padding:2px 10px;font-size:12px;line-height:20px}
70
+ ${u} ${p}-pool-settings-wrap{display:flex;flex-direction:column;gap:10px;border-top:1px solid var(--dsw-alias-border-l2,#777);padding-top:14px}
71
+ ${u} ${p}-pool-settings{border-radius:12px;gap:0}
72
+ ${u} ${p}-pool-settings-wrap :is(h3,h4){margin:0;font-size:13px;font-weight:600;line-height:18px;color:var(--dsw-alias-label-primary,#eee)}
73
+ ${u} ${p}-pool-set-ctl select{box-sizing:border-box;width:240px;min-width:0;max-width:100%}
74
+ ${u} ${p}-pool-set{display:flex;align-items:center;justify-content:space-between;gap:16px;padding:12px 14px}
75
+ ${u} ${p}-pool-set-copy{flex:1;min-width:0;gap:3px}
76
+ ${u} ${p}-pool-set-copy b{font-size:13px;font-weight:500;line-height:18px}
77
+ ${u} ${p}-pool-set-copy span{font-size:12px;line-height:18px}
78
+ ${u} ${p}-models-title{font-size:13px;line-height:18px;font-weight:600}
79
+ ${u} ${p}-model-list{border-radius:12px}
80
+ ${u} ${p}-model{padding:12px 14px;gap:8px}
81
+ ${u} ${p}-model-name{font-size:13px;line-height:20px}
82
+ ${u} ${p}-model-actions,${u} ${p}-pool-save-row{gap:12px;padding-top:12px}
83
+ /* 保存栏以整个账号池为粘附范围,避免被更多设置的短父级截住。 */
84
+ ${u} ${p}-pool-settings-wrap{display:contents}
85
+ ${u} ${saveBar}{position:sticky;bottom:0;z-index:2;isolation:isolate;padding-bottom:12px;background:var(--dsw-alias-bg-layer-3,#202126)}
86
+ /* 滚动容器底部的内边距也属于可见区域,负偏移让背景覆盖到视口底边。 */
87
+ ${p}-page ${saveBar}{bottom:-48px}
88
+ ${p}-page ${saveBar}[data-save-stuck="true"]{margin-inline:-20px;padding-inline:20px;border-top:1px solid var(--dsw-alias-border-l2,#777);border-bottom:1px solid var(--dsw-alias-border-l2,#777);border-radius:0;box-shadow:0 -3px 6px rgba(0,0,0,.06)}
89
+
90
+ ${u} #dsm-${plugin}-panel-capabilities:not([hidden]){display:flex;flex-direction:column;gap:14px}
91
+ ${u} ${p}-prefs{gap:14px;padding:0}
92
+ ${u} ${p}-pref{font-size:13px;line-height:18px;gap:10px}
93
+ ${u} ${p}-pref-hint{font-size:12px;line-height:18px}
94
+ ${u} ${p}-draw-image{margin:0;gap:10px;font-size:13px;line-height:18px}
95
+ ${u} ${p}-draw-image>span{display:flex;flex-direction:column;gap:3px}
96
+ ${u} ${p}-draw-image-hint{font-size:12px;line-height:18px}
97
+ ${u} ${p}-pool-table{container-type:inline-size}
98
+ ${u} ${p}-pool-current-tag{border:1px solid var(--dsw-alias-border-l2,#777);background:var(--dsw-alias-bg-layer-2,#24262c);color:var(--dsw-alias-label-secondary,#999)}
99
+ @container(max-width:700px){
100
+ ${u} ${p}-pool-row{display:flex;flex-wrap:wrap;align-items:baseline;gap:4px 12px;padding:10px 12px}
101
+ ${u} ${p}-pool-row-head{display:none}
102
+ ${u} ${p}-pool-account{flex:1 1 auto;min-width:0}
103
+ ${u} ${p}-pool-row-actions{flex:1 1 100%;justify-content:flex-start;padding-top:4px}
104
+ }
105
+ /* 自动签到、设备参数和 SDK 是功能差异;沿用公共间距,不移除控件。 */
106
+ ${u} ${p}-device-mode{margin:0;padding:0 14px 12px}
107
+ ${u} ${p}-pool .sw{width:36px;height:20px}
108
+ ${u} ${p}-pool .sw-track::after{top:2px;left:2px;width:16px;height:16px;background:var(--dsw-alias-bg-layer-3,#fff)}
109
+ ${u} ${p}-pool .sw input:checked+.sw-track::after{transform:translateX(16px)}
110
+ @media(max-width:600px){
111
+ ${p}-page${p}-page{padding:20px 16px 32px;--dsm-series-sticky-top:-20px}
112
+ ${p}-page ${saveBar}{bottom:-32px}
113
+ ${u} ${memberHead}{align-items:flex-start}
114
+ ${u} ${memberActions}{flex-wrap:wrap}
115
+ ${u} ${p}-pool-set{flex-wrap:wrap}
116
+ ${u} ${p}-pool-set-ctl{max-width:100%;flex-wrap:wrap}
117
+ ${u} ${p}-pool-set-ctl select{min-width:0;max-width:100%}
118
+ }
119
+ `;
120
+ }
121
+ /** 根据页签与保存栏的实际吸附位置显示边界。 */
122
+ function updateStickyTabs(page) {
123
+ const tabs = page.querySelector("[role=\"tablist\"][class$=\"-panels\"]");
124
+ const stuck = tabs !== null && page.scrollTop > 0 && tabs.getBoundingClientRect().top <= page.getBoundingClientRect().top + 1;
125
+ page.dataset.tabsStuck = String(stuck);
126
+ const viewportBottom = page.getBoundingClientRect().bottom;
127
+ for (const bar of Array.from(page.querySelectorAll("[class$=\"-pool-save-row\"],[class$=\"-pool-save-bar\"]"))) {
128
+ const visible = bar.getClientRects().length > 0;
129
+ const bottom = Number.parseFloat(getComputedStyle(bar).bottom) || 0;
130
+ bar.dataset.saveStuck = String(visible && Math.abs(bar.getBoundingClientRect().bottom + bottom - viewportBottom) <= 1);
131
+ }
132
+ }
133
+ //#endregion
134
+ //#region src/client/icon.ts
135
+ /**
136
+ * Plugin icon (LD brand logo, 64px) shared across the same-family plugins.
137
+ * Same data URI used by `dsh-subagent-default-model` so the external image
138
+ * stays consistent.
139
+ */
140
+ const TRAE_PLUGIN_ICON = "data:image/png;base64,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";
141
+ //#endregion
142
+ //#region src/client/plugin-icon.tsx
143
+ const storageKey = "dsh-connect-trae.icon-style";
144
+ const changeEvent = storageKey + ".changed";
145
+ /**
146
+ * Appearance is local to this browser; account and provider settings are untouched.
147
+ *
148
+ * THE DEFAULT IS THIS PLUGIN'S OWN ICON ('original'), not the Claude skin.
149
+ *
150
+ * This shipped the other way round at first — an unset preference read as
151
+ * 'claude' — which meant every new user got a re-drawn silhouette before they
152
+ * had asked for anything, and the plugin's own artwork was the option they had
153
+ * to go find. A preference whose unset state is the non-default is a poor
154
+ * default: it makes the product's identity something you have to opt back into.
155
+ *
156
+ * An explicit 'claude' in storage still wins, so anyone who already chose the
157
+ * skin keeps it. Only the ABSENT case changed.
158
+ */
159
+ function iconStyleSnapshot() {
160
+ try {
161
+ return window.localStorage.getItem(storageKey) === "claude" ? "claude" : "original";
162
+ } catch {
163
+ return "original";
164
+ }
165
+ }
166
+ function subscribe(listener) {
167
+ const onStorage = (event) => {
168
+ if (event.key === storageKey || event.key === null) listener();
169
+ };
170
+ window.addEventListener(changeEvent, listener);
171
+ window.addEventListener("storage", onStorage);
172
+ return () => {
173
+ window.removeEventListener(changeEvent, listener);
174
+ window.removeEventListener("storage", onStorage);
175
+ };
176
+ }
177
+ function setIconStyle(value) {
178
+ window.localStorage.setItem(storageKey, value);
179
+ window.dispatchEvent(new Event(changeEvent));
180
+ }
181
+ function PluginIcon(props) {
182
+ const saved = (0, react.useSyncExternalStore)(subscribe, iconStyleSnapshot, () => "original");
183
+ const variant = props.variant ?? saved;
184
+ const shared = {
185
+ className: props.className,
186
+ width: props.size ?? 16,
187
+ height: props.size ?? 16,
188
+ style: {
189
+ ...props.style,
190
+ flexShrink: 0
191
+ },
192
+ "aria-hidden": true
193
+ };
194
+ if (variant === "original") return (0, react.createElement)("img", {
195
+ ...shared,
196
+ src: TRAE_PLUGIN_ICON,
197
+ alt: ""
198
+ });
199
+ return (0, react.createElement)("svg", {
200
+ ...shared,
201
+ viewBox: "0 0 24 24",
202
+ fill: "currentColor",
203
+ "data-icon-style": "claude",
204
+ "data-plugin-icon": "trae"
205
+ }, (0, react.createElement)("path", { d: "M4.5 5.5h3v11h3l3-3v4l-2 2h-7z" }), (0, react.createElement)("path", { d: "M10.5 5.5H15a7 7 0 0 1 0 14h-2v-3h2a4 4 0 0 0 0-8h-1.5v2l-3 3z" }));
206
+ }
207
+ /** Each choice previews the real icon and immediately persists its selection. */
208
+ function IconStylePreference({ t }) {
209
+ const selected = (0, react.useSyncExternalStore)(subscribe, iconStyleSnapshot, () => "original");
210
+ const [error, setError] = (0, react.useState)(false);
211
+ return (0, react.createElement)("div", { className: "dsm-icon-preference" }, (0, react.createElement)("div", { className: "dsm-icon-preference-copy" }, (0, react.createElement)("b", null, t("row.iconStyleLabel")), (0, react.createElement)("span", null, t("row.iconStyleHint"))), (0, react.createElement)("div", {
212
+ className: "dsm-icon-choices",
213
+ role: "radiogroup",
214
+ "aria-label": t("row.iconStyleLabel")
215
+ }, ...["claude", "original"].map((variant) => (0, react.createElement)("button", {
216
+ key: variant,
217
+ type: "button",
218
+ role: "radio",
219
+ "aria-checked": selected === variant,
220
+ tabIndex: selected === variant ? 0 : -1,
221
+ onKeyDown: (event) => {
222
+ if (![
223
+ "ArrowLeft",
224
+ "ArrowRight",
225
+ "ArrowUp",
226
+ "ArrowDown",
227
+ "Home",
228
+ "End"
229
+ ].includes(event.key)) return;
230
+ event.preventDefault();
231
+ const next = event.key === "Home" ? "claude" : event.key === "End" ? "original" : variant === "claude" ? "original" : "claude";
232
+ try {
233
+ setIconStyle(next);
234
+ setError(false);
235
+ (event.currentTarget.parentElement?.querySelectorAll("[role=radio]")[next === "claude" ? 0 : 1])?.focus();
236
+ } catch {
237
+ setError(true);
238
+ }
239
+ },
240
+ onClick: () => {
241
+ try {
242
+ setIconStyle(variant);
243
+ setError(false);
244
+ } catch {
245
+ setError(true);
246
+ }
247
+ }
248
+ }, (0, react.createElement)(PluginIcon, {
249
+ variant,
250
+ size: 20
251
+ }), t(variant === "claude" ? "row.iconStyleClaude" : "row.iconStyleOriginal")))), error ? (0, react.createElement)("p", {
252
+ role: "alert",
253
+ className: "dsm-icon-save-error"
254
+ }, t("row.iconStyleSaveFailed")) : null);
255
+ }
256
+ //#endregion
10
257
  //#region src/region.ts
11
258
  /** Every panel, in card tab order. */
12
259
  const TRAE_PANELS = [
@@ -937,14 +1184,6 @@ window.__ModuleLoader__.load({
937
1184
  }
938
1185
  }
939
1186
  //#endregion
940
- //#region src/client/icon.ts
941
- /**
942
- * Plugin icon (LD brand logo, 64px) shared across the LaoDing plugin family.
943
- * Same data URI used by `dsh-subagent-default-model` so the external image
944
- * stays consistent.
945
- */
946
- const TRAE_PLUGIN_ICON = "data:image/png;base64,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";
947
- //#endregion
948
1187
  //#region src/client/styles.ts
949
1188
  /**
950
1189
  * Client styles for the Trae plugin card.
@@ -953,6 +1192,16 @@ window.__ModuleLoader__.load({
953
1192
  * button primitives, same `--dsw-alias-*` token palette).
954
1193
  */
955
1194
  const TRAE_CARD_CSS = `
1195
+ .dsm-icon-preference{display:flex;flex-wrap:wrap;align-items:center;justify-content:space-between;gap:12px;padding:12px 0;color:var(--dsw-alias-label-primary,#e6e6e6)}
1196
+ .dsm-icon-preference-copy{display:flex;flex-direction:column;gap:4px;flex:1;min-width:180px;font-size:13px}
1197
+ .dsm-icon-preference-copy>span{font-size:12px;line-height:1.5;color:var(--dsw-alias-label-tertiary,#999)}
1198
+ .dsm-icon-choices{display:flex;flex-wrap:wrap;gap:6px}
1199
+ .dsm-icon-choices>button{appearance:none;display:inline-flex;align-items:center;gap:8px;font:inherit;font-size:12px;line-height:20px;color:inherit;background:transparent;border:1px solid var(--dsw-alias-border-l2,#777);border-radius:8px;padding:7px 10px;cursor:pointer}
1200
+ .dsm-icon-choices>button[aria-checked="true"]{border-color:var(--dsw-alias-brand-primary,#cf795b);background:var(--dsw-alias-bg-layer-2,#25262b)}
1201
+ .dsm-icon-choices>button:focus-visible{outline:2px solid var(--dsw-alias-brand-primary,#cf795b);outline-offset:2px}
1202
+ .dsm-icon-save-error{flex-basis:100%;margin:0;color:var(--dsw-alias-state-error-primary,#c33);font-size:12px}
1203
+ .dsm-trae-pool-auto-checkin{display:flex;flex-direction:column;gap:6px;margin-top:8px}
1204
+ .dsm-trae-pool-auto-checkin-status{margin:0;font-size:12px;line-height:18px;color:var(--dsw-alias-label-secondary,#999)}
956
1205
  .dsm-plugin-card{border:1px solid var(--dsw-alias-border-l2,#36373b);background:var(--dsw-alias-bg-layer-3,#202126);border-radius:12px;list-style:none;transition:border-color .16s,background .16s}
957
1206
  .dsm-plugin-card:hover{border-color:var(--dsw-alias-label-dimmed,#777)}
958
1207
  .dsm-plugin-card-open{background:var(--dsw-alias-bg-layer-2,#25262b);border-color:var(--dsw-alias-label-dimmed,#777)}
@@ -1047,7 +1296,7 @@ window.__ModuleLoader__.load({
1047
1296
  * 作用域,在卡片上留下两个回归)。卡片与整页没有"外壳的滚动列",那里的分栏条
1048
1297
  * 跟着内容正常滚走即可;sticky 在没有滚动祖先时不会生效,却会把底色带进来——
1049
1298
  * 而底色取 layer-2,卡片本身是 layer-3,色差在卡片上就成了一条浅色带。 */
1050
- .dsm-trae-section .dsm-trae-panels{position:sticky;top:-1px;z-index:2;background:var(--dsw-alias-bg-layer-2,#24262c)}
1299
+ .dsm-trae-section .dsm-trae-usage .dsm-trae-panels{position:sticky;top:-1px;z-index:2;padding:0;background:var(--dsw-alias-bg-layer-2,#24262c)}
1051
1300
  .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}
1052
1301
  .dsm-trae-panel-tab:hover:not(.dsm-trae-panel-tab-active){color:var(--dsw-alias-label-primary,#e6e6e6)}
1053
1302
  .dsm-trae-panel-tab:focus-visible{outline:2px solid var(--dsw-alias-brand-primary,#5686fe);outline-offset:1px}
@@ -1055,6 +1304,12 @@ window.__ModuleLoader__.load({
1055
1304
  /* No top margin of its own: the strip above already carries a border, and the
1056
1305
  card's 16px stack gap would double the separation. */
1057
1306
  .dsm-trae-panel{margin-top:0}
1307
+ /* The one-line note at the top of a section that holds controls but no Save
1308
+ * button. A section with switches and no footer is the first thing a reader
1309
+ * doubts — "did that press stick?" — so the note answers it before the doubt
1310
+ * forms, and it is styled as a muted paragraph rather than a warning because
1311
+ * "takes effect immediately" is a fact, not a caveat. */
1312
+ .dsm-trae-panel-note{margin:0 0 4px;padding:0;font-size:12px;line-height:18px;color:var(--dsw-alias-label-tertiary,#9aa0a8)}
1058
1313
  /* DIAGNOSTICS: a two-column fact table rather than prose. Each row is one thing
1059
1314
  * the card actually READ, so a bug report can quote a line instead of
1060
1315
  * paraphrasing the UI. No control lives here on purpose — a diagnostics panel
@@ -1100,6 +1355,20 @@ window.__ModuleLoader__.load({
1100
1355
  .dsm-trae-prefs{display:flex;flex-direction:column;gap:14px;padding:2px 2px 0}
1101
1356
  .dsm-trae-pref{display:flex;align-items:flex-start;gap:10px;font-size:13px;line-height:18px;color:var(--dsw-alias-label-secondary,#c6c9d0);cursor:pointer}
1102
1357
  .dsm-trae-pref input{flex:none;cursor:pointer;accent-color:var(--dsw-alias-brand-primary,#5686fe)}
1358
+ /* The icon picker inside the 「能力」 list.
1359
+ *
1360
+ * It is a shared component (plugin-icon.tsx) that also renders standalone, so
1361
+ * its own rule carries a 12px block padding for that standalone use. Inside the
1362
+ * prefs column that padding doubles up with the container's 14px gap and the row
1363
+ * drifts away from its three neighbours, which are bare lines with no padding of
1364
+ * their own. Strip it here rather than in the shared rule: the standalone
1365
+ * spacing is still correct wherever else this component is mounted.
1366
+ *
1367
+ * min-width:0 on the copy because the labels here are Chinese sentences, not the
1368
+ * short English words the 180px floor was chosen for; without it a long hint
1369
+ * pushes the two icon buttons out of the 564px column instead of wrapping. */
1370
+ .dsm-trae-prefs .dsm-icon-preference{padding:0;align-items:flex-start}
1371
+ .dsm-trae-prefs .dsm-icon-preference-copy{min-width:0}
1103
1372
  .dsm-trae-pref-check{inline-size:15px;block-size:15px;margin:3px 0 0}
1104
1373
  /* The label and its explanation used to run together as one wrapped blob
1105
1374
  ("在输入框工具栏显示积分读数 在输入框工具栏显示可点击读数…"), which is what made
@@ -1171,7 +1440,7 @@ window.__ModuleLoader__.load({
1171
1440
  /* The account rows: two lines each 鈥?who the account is, then what it is worth
1172
1441
  鈥?with the one action that applies to THAT row on the right. A four-column
1173
1442
  table was the wrong instrument here: every field demanded its own header, the
1174
- columns drifted with the content ("LaoDing" vs a full expiry stamp), and the
1443
+ columns drifted with the content (a short nickname vs a full expiry stamp), and the
1175
1444
  bound row repeated its "in use" badge in two different columns. */
1176
1445
  /* The account table, shaped like the sibling WorkBuddy card's pool table: a
1177
1446
  bordered container, a tinted header row, and one grid row per account whose
@@ -1432,9 +1701,21 @@ window.__ModuleLoader__.load({
1432
1701
  .dsm-trae-model-effort-select:disabled{opacity:.6;cursor:not-allowed}
1433
1702
  .dsm-trae-model-image{display:inline-flex;align-items:center;gap:4px;color:var(--dsw-alias-label-secondary,#c6c9d0);font-size:11px;line-height:16px;cursor:pointer}
1434
1703
  .dsm-trae-model-image input{margin:0;accent-color:var(--dsw-alias-brand-primary,#5686fe)}
1704
+ /* The context-budget control: one select per row, not a row of radios.
1705
+ *
1706
+ * The fieldset stays as the grouping element — it carries the accessible name
1707
+ * and the hint title, and a select has no group of its own. flex:none keeps
1708
+ * it from stealing width from the model name, which is the thing that has to
1709
+ * stay readable on a 564px column.
1710
+ *
1711
+ * The select width is FIXED rather than content-sized: with 2-3 options the
1712
+ * widest label dictates the width, and a per-row width made the list ragged
1713
+ * (a 3-option row's control was visibly wider than its neighbours). */
1435
1714
  .dsm-trae-context-budget{display:flex;align-items:center;justify-content:flex-end;gap:12px;flex:none;margin:0;padding:0;border:0;color:var(--dsw-alias-label-secondary,#c6c9d0);font-size:11px;line-height:16px}
1436
1715
  .dsm-trae-context-budget label{display:inline-flex;align-items:center;gap:4px;cursor:pointer}
1437
1716
  .dsm-trae-context-budget input{margin:0;accent-color:var(--dsw-alias-brand-primary,#5686fe)}
1717
+ .dsm-trae-context-select{flex:none;width:96px;max-width:none;padding:2px 6px;font:inherit;font-size:11px;line-height:16px;color:var(--dsw-alias-label-primary,#e6e6e6);background:var(--dsw-alias-bg-layer-3,#2a2c33);border:1px solid var(--dsw-alias-border-l2,#3a3d45);border-radius:7px;cursor:pointer}
1718
+ .dsm-trae-context-select:disabled{cursor:default;opacity:.6}
1438
1719
  .dsm-trae-model-capability-note{margin:0;color:var(--dsw-alias-label-tertiary,#999);font-size:12px;line-height:18px}
1439
1720
  .dsm-trae-model-actions{display:flex;align-items:center;justify-content:space-between;gap:12px;border-top:1px solid var(--dsw-alias-border-l2,#36373b);padding-top:12px}
1440
1721
  .dsm-trae-model-actions-buttons{display:flex;align-items:center;justify-content:flex-end;gap:8px}
@@ -1615,7 +1896,7 @@ window.__ModuleLoader__.load({
1615
1896
  * 这张卡同时画在三处,宽度与窗口毫无关系:输入框卡片、整页版(上限 860px)、
1616
1897
  * 以及设置面板的内容列(1400px 屏上是 564px)。本文件里所有 @media 断点测的
1617
1898
  * 都是**视口**,所以在设置面板里它们永远不触发——564px 的列拿到了五列桌面表格,
1618
- * 每个单元格都在截断(实测账号列被压到 76px,「用户7520607992」只剩「用户75」)。
1899
+ * 每个单元格都在截断(实测账号列被压到 76px,一个 9 位数字账号名只剩前 5 个字符)。
1619
1900
  *
1620
1901
  * container-type:inline-size 让下面的断点改测**这张表**的宽度,那才是行真正要
1621
1902
  * 塞进去的宽度。
@@ -1641,7 +1922,23 @@ window.__ModuleLoader__.load({
1641
1922
  reads as a selection control, and the checkbox beside it already is one. */
1642
1923
  .dsm-trae-pool-row-current{background:rgba(86,134,254,.07);box-shadow:inset 3px 0 0 var(--dsw-alias-brand-primary,#5686fe)}
1643
1924
  .dsm-trae-pool-row-ghost{opacity:.7}
1644
- .dsm-trae-pool-row-actions{display:flex;flex-wrap:wrap;gap:6px;justify-content:flex-end}
1925
+ /* The row's buttons: RIGHT side of the row, pulled there by the auto margin
1926
+ * (learned from dsh-connect-workbuddy, which had the same complaint).
1927
+ *
1928
+ * WHY THE AUTO MARGIN AND NOT JUST justify-content. In the wide grid this cell
1929
+ * IS the last track, so justify-content:flex-end was enough. But in the
1930
+ * NARROW form the row becomes a wrapping flex line, and this cell is a sibling
1931
+ * of the account line rather than a track beside it -- so it claimed a line of
1932
+ * its own and the row spent a whole line on two small buttons.
1933
+ *
1934
+ * margin-inline-start:auto moves it to the END of whatever line has room. In
1935
+ * the narrow form that is the account line, which is the SHORTEST line in the
1936
+ * row, so the buttons land beside the account name where the eye already is
1937
+ * instead of under it.
1938
+ *
1939
+ * flex:none is load-bearing: the account name may truncate, the buttons may
1940
+ * not, and a shrunken button is unclickable. */
1941
+ .dsm-trae-pool-row-actions{display:flex;flex-wrap:wrap;gap:6px;justify-content:flex-end;flex:none;margin-inline-start:auto}
1645
1942
  .dsm-trae-pool-account{display:grid;grid-template-columns:auto minmax(0,1fr);align-items:center;gap:2px 8px;min-width:0}
1646
1943
  .dsm-trae-pool-check{display:inline-flex;align-items:center;flex:none;grid-column:1;grid-row:1;cursor:pointer}
1647
1944
  .dsm-trae-pool-check input{flex:none;margin:0;cursor:pointer;accent-color:var(--dsw-alias-brand-primary,#5686fe)}
@@ -1719,20 +2016,55 @@ window.__ModuleLoader__.load({
1719
2016
  *
1720
2017
  * .dsm-trae-pool-row-head 隐藏:它的标签描述的是已经不存在的轨道。 */
1721
2018
  @container (max-width:700px){
1722
- .dsm-trae-pool-row{display:flex;flex-wrap:wrap;align-items:baseline;gap:4px 12px;padding:var(--dsm-trae-pool-pad-y) 12px}
2019
+ /* The dsm-trae-usage hop is load-bearing: series-layout.ts appends a
2020
+ dsm-trae-usage dsm-trae-pool-row rule inside this same container query, and
2021
+ at 0,2,0 it beat the plain dsm-trae-pool-row below (0,1,0). Measured after
2022
+ the 4.14.0 merge: display came back as flex, rows grew 75 -> 111px and the
2023
+ check-in fell to line 2 again, while the grid COLUMN rules still applied —
2024
+ a half-applied layout, which is why it looked like a padding problem rather
2025
+ than a specificity one. */
2026
+ .dsm-trae-usage .dsm-trae-pool-row{display:flex;flex-wrap:wrap;align-items:baseline;gap:4px 12px;padding:var(--dsm-trae-pool-pad-y) 12px}
1723
2027
  .dsm-trae-pool-row-head{display:none}
1724
- .dsm-trae-pool-account{flex:1 1 100%;align-items:center}
1725
- .dsm-trae-pool-credit{flex:0 1 auto;flex-direction:row;flex-wrap:wrap;align-items:baseline;gap:2px 8px}
1726
- .dsm-trae-pool-probe{flex:1 1 auto}
1727
- .dsm-trae-pool-checkin{flex:none}
1728
- .dsm-trae-pool-row-actions{flex:1 1 100%;justify-content:flex-start;padding-top:4px}
2028
+ /* TWO ROWS, EXPLICITLY PLACED. Not "let flex wrap and hope".
2029
+ *
2030
+ * 每一行账号只有两行:
2031
+ *
2032
+ * 第一行 [勾选 + 账号名 + 标签] [签到状态 + 按钮] [积分]
2033
+ * 第二行 [状态 · 测得时间 · 来源] [测速] [删除]
2034
+ *
2035
+ * 以前是 flex-wrap,由内容决定怎么折——结果是同一个表格里的行落在不同的
2036
+ * 位置(实测三行分别占地 4 / 4 / 3 个纵向位置),有的积分被挤到中间(x=588)。
2037
+ * 网格把每个事实钉在一个格子里:无论账号名多长,积分永远停在同一右缘,
2038
+ * 「签到」永远和名字同一行。
2039
+ *
2040
+ * 列的含义:
2041
+ * 1fr —— 会伸缩的那一列(账号名、状态)。
2042
+ * auto —— 签到:宽度由文字决定(「已签到」「未签到」「本设备今日已用」)。
2043
+ * auto —— 积分:贴在右缘,flex:none,不许被压缩。
2044
+ *
2045
+ * 积分为什么单独成列而不是跟在名字后面:余额是这一行里唯一会被**跨行横向比对**
2046
+ * 的数字(「哪个账号还剩得多」),而跟着名字走时三行分别落在 593 / 614 / 498,
2047
+ * 眼睛没法竖着扫。等宽字形让小数点也对齐。
2048
+ *
2049
+ * 状态那一格(probe)独占第二行:它讲的是「能不能用」,与「还剩多少」不是同一个
2050
+ * 问题;按钮也放在第二行,因为第一行已经有三个事实,再塞两个按钮会把账号名压窄。 */
2051
+ .dsm-trae-usage .dsm-trae-pool-row:not(.dsm-trae-pool-row-head){
2052
+ display:grid;grid-template-columns:minmax(0,1fr) auto auto;
2053
+ align-items:center;gap:4px 10px}
2054
+ .dsm-trae-pool-account{grid-column:1;grid-row:1;display:flex;flex-wrap:wrap;align-items:baseline;gap:2px 8px;min-width:0}
2055
+ .dsm-trae-pool-checkin{grid-column:2;grid-row:1;justify-self:end;flex:none}
2056
+ .dsm-trae-pool-credit{grid-column:3;grid-row:1;justify-self:end;flex:none;flex-direction:row;align-items:baseline;gap:2px 8px;text-align:right;font-variant-numeric:tabular-nums}
2057
+ .dsm-trae-pool-probe{grid-column:1;grid-row:2;min-width:0}
2058
+ /* Spans from the check-in column to the edge: the buttons sit under the figures
2059
+ they act on, flush with the credit above them. */
2060
+ .dsm-trae-pool-row-actions{grid-column:2/-1;grid-row:2;justify-self:end;justify-content:flex-end;margin-inline-start:0;padding-top:0}
1729
2061
  /* THE PROBE CELL'S OWN COLUMN, collapsed to one line.
1730
2062
  *
1731
2063
  * 这一条是最容易漏掉的:.dsm-trae-pool-probe 只是个 min-width:0 的外层包装,
1732
2064
  * 真正竖排的是它内部的 .dsm-trae-pool-probe-cell(flex-direction:column)。
1733
2065
  * 只改外层包装的话,状态列仍然一句一行,被挤成「正常 / 测得 3 天前 / 来自测速 /
1734
2066
  * upstream answered」四行——旁边只有 12px 间距,整行高度就被这一格撑到 100px
1735
- * 以上,账号名也跟着被压窄(实测截成「LaoDin」「用户7」)。
2067
+ * 以上,账号名也跟着被压窄(实测截成前几个字符「用户7」)。
1736
2068
  *
1737
2069
  * 改成横排并允许在自己内部换行:这四个事实读起来本来就是一句话
1738
2070
  * (「正常 · 测得 3 天前 · 来自测速 · upstream answered」),宽时从左排到右,
@@ -1742,7 +2074,7 @@ window.__ModuleLoader__.load({
1742
2074
  *
1743
2075
  * 宽的时候它是个三行网格(名字 / 标签 / 探针时间),第一列固定给勾选框。
1744
2076
  * 在窄列里这三行没有理由继续各占一行——它们本来就是一句话
1745
- * (「LaoDing · 已在池中 · 测试于 11/03, 06:35」),横排后名字与这两条短事实
2077
+ * (「demo-usr · 已在池中 · 测试于 11/03, 06:35」),横排后名字与这两条短事实
1746
2078
  * 共享一行,只有真的放不下时才折行。勾选框仍然排在最前,并保持垂直居中对齐
1747
2079
  * 到第一行基线(align-self:baseline 会让它沉到行底)。 */
1748
2080
  .dsm-trae-pool-account{display:flex;flex-wrap:wrap;align-items:baseline;gap:2px 8px}
@@ -1806,7 +2138,7 @@ window.__ModuleLoader__.load({
1806
2138
  .dsm-trae-section-title{margin:0;font-size:15px;font-weight:600;line-height:22px}
1807
2139
  .dsm-trae-section-desc{margin:0;font-size:12px;line-height:18px;color:var(--dsw-alias-label-tertiary,#999)}
1808
2140
  .dsm-trae-panel-icon{display:block;border-radius:4px;flex:none}
1809
- `;
2141
+ ` + seriesLayoutCss("trae");
1810
2142
  /**
1811
2143
  * Composer-row readout styles, injected by the readout itself.
1812
2144
  *
@@ -2360,6 +2692,7 @@ window.__ModuleLoader__.load({
2360
2692
  const base = regionPoolOf(settingsScope?.getSnapshot().value, panel);
2361
2693
  return {
2362
2694
  enabled: base.enabled === true,
2695
+ autoCheckin: base.autoCheckin === true,
2363
2696
  targetModelId: base.targetModelId ?? pool.targetModelId ?? pool.staleTargetModelId ?? "",
2364
2697
  memberAccountIds: [...base.memberAccountIds ?? []],
2365
2698
  probeInputTokens: numberOr(base.probeInputTokens, numberOr(pool.probeInputTokens, 0))
@@ -2383,6 +2716,7 @@ window.__ModuleLoader__.load({
2383
2716
  */
2384
2717
  const probeSupported = typeof pool.probeInputTokens === "number";
2385
2718
  const [enabled, setEnabled] = (0, react.useState)(saved.enabled);
2719
+ const [autoCheckin, setAutoCheckin] = (0, react.useState)(saved.autoCheckin);
2386
2720
  const [targetModelId, setTargetModelId] = (0, react.useState)(saved.targetModelId ?? "");
2387
2721
  const [probeInputTokens, setProbeInputTokens] = (0, react.useState)(saved.probeInputTokens);
2388
2722
  const [selected, setSelected] = (0, react.useState)(() => new Set(saved.memberAccountIds ?? []));
@@ -2434,8 +2768,11 @@ window.__ModuleLoader__.load({
2434
2768
  pool,
2435
2769
  knownIds
2436
2770
  ]);
2437
- const dirty = enabled !== saved.enabled || (targetModelId ?? "") !== (saved.targetModelId ?? "") || probeSupported && probeInputTokens !== saved.probeInputTokens || !sameIds([...selected], saved.memberAccountIds);
2771
+ const dirty = enabled !== saved.enabled || autoCheckin !== saved.autoCheckin || (targetModelId ?? "") !== (saved.targetModelId ?? "") || probeSupported && probeInputTokens !== saved.probeInputTokens || !sameIds([...selected], saved.memberAccountIds);
2438
2772
  const writable = settingsScope?.getSnapshot().writable === true;
2773
+ (0, react.useEffect)(() => {
2774
+ setAutoCheckin(saved.autoCheckin);
2775
+ }, [panel, saved.autoCheckin]);
2439
2776
  const savedMemberKey = (0, react.useMemo)(() => [...saved.memberAccountIds ?? []].sort().join("\0"), [saved]);
2440
2777
  (0, react.useEffect)(() => {
2441
2778
  setEnabled(saved.enabled);
@@ -2473,6 +2810,7 @@ window.__ModuleLoader__.load({
2473
2810
  const selectNone = () => setSelected(/* @__PURE__ */ new Set());
2474
2811
  const discard = () => {
2475
2812
  setEnabled(saved.enabled);
2813
+ setAutoCheckin(saved.autoCheckin);
2476
2814
  setTargetModelId(saved.targetModelId ?? "");
2477
2815
  setProbeInputTokens(saved.probeInputTokens);
2478
2816
  setSelected(new Set(saved.memberAccountIds ?? []));
@@ -2487,13 +2825,14 @@ window.__ModuleLoader__.load({
2487
2825
  try {
2488
2826
  const next = {
2489
2827
  enabled,
2828
+ autoCheckin,
2490
2829
  ...targetModelId === "" ? {} : { targetModelId },
2491
2830
  memberAccountIds: [...selected],
2492
2831
  ...probeSupported ? { probeInputTokens } : {}
2493
2832
  };
2494
2833
  await writeSettingsField(settingsScope, "regions", nextRegionPool(settingsScope.getSnapshot().value, panel, next), (readBack) => {
2495
2834
  const readPool = regionPoolOf(readBack, panel);
2496
- return readPool.enabled === (next.enabled ?? false) && sameIds(readPool.memberAccountIds, next.memberAccountIds) && (readPool.targetModelId ?? "") === (next.targetModelId ?? "") && (!probeSupported || (readPool.probeInputTokens ?? 0) === probeInputTokens);
2835
+ return readPool.autoCheckin === next.autoCheckin && readPool.enabled === (next.enabled ?? false) && sameIds(readPool.memberAccountIds, next.memberAccountIds) && (readPool.targetModelId ?? "") === (next.targetModelId ?? "") && (!probeSupported || (readPool.probeInputTokens ?? 0) === probeInputTokens);
2497
2836
  });
2498
2837
  pushLog("ok", t("row.poolLogSaved"));
2499
2838
  onChanged?.();
@@ -2770,39 +3109,82 @@ window.__ModuleLoader__.load({
2770
3109
  }),
2771
3110
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2772
3111
  className: "dsm-trae-pool-settings dsm-trae-pool-modebar",
2773
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2774
- className: "dsm-trae-pool-modebar-row",
2775
- children: [
2776
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("label", {
2777
- className: "dsm-trae-switch dsm-trae-switch-pill",
2778
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
2779
- type: "checkbox",
2780
- checked: enabled,
2781
- disabled: busy !== void 0 || !writable,
2782
- "aria-label": t("row.poolEnabled"),
2783
- onChange: (e) => {
2784
- setEnabled(e.target.checked);
2785
- }
2786
- })
2787
- }),
2788
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("b", {
2789
- className: "dsm-trae-pool-modebar-label",
2790
- children: t("row.poolEnabled")
2791
- }),
2792
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2793
- className: "dsm-trae-pool-modebar-state",
2794
- children: pool.enabled === true ? t("row.poolModeAuto") : t("row.poolModeManual")
2795
- }),
2796
- dirty ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2797
- className: "dsm-trae-pool-dirty",
2798
- role: "status",
2799
- children: t("row.poolDirty")
2800
- }) : null
2801
- ]
2802
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
2803
- className: "dsm-trae-pool-note",
2804
- children: t("row.poolEnabledHint")
2805
- })]
3112
+ children: [
3113
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3114
+ className: "dsm-trae-pool-modebar-row",
3115
+ children: [
3116
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("label", {
3117
+ className: "dsm-trae-switch dsm-trae-switch-pill",
3118
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
3119
+ type: "checkbox",
3120
+ checked: enabled,
3121
+ disabled: busy !== void 0 || !writable,
3122
+ "aria-label": t("row.poolEnabled"),
3123
+ onChange: (e) => {
3124
+ setEnabled(e.target.checked);
3125
+ }
3126
+ })
3127
+ }),
3128
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("b", {
3129
+ className: "dsm-trae-pool-modebar-label",
3130
+ children: t("row.poolEnabled")
3131
+ }),
3132
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3133
+ className: "dsm-trae-pool-modebar-state",
3134
+ children: pool.enabled === true ? t("row.poolModeAuto") : t("row.poolModeManual")
3135
+ }),
3136
+ dirty ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3137
+ className: "dsm-trae-pool-dirty",
3138
+ role: "status",
3139
+ children: t("row.poolDirty")
3140
+ }) : null
3141
+ ]
3142
+ }),
3143
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("p", {
3144
+ className: "dsm-trae-pool-note",
3145
+ children: [
3146
+ t("row.poolEnabledHint"),
3147
+ " ",
3148
+ t("row.poolFailoverHint")
3149
+ ]
3150
+ }),
3151
+ panel === "cn" && pool.checkinSupported && typeof pool.autoCheckin === "boolean" ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3152
+ className: "dsm-trae-pool-auto-checkin",
3153
+ children: [
3154
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3155
+ className: "dsm-trae-pool-modebar-row",
3156
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("label", {
3157
+ className: "dsm-trae-switch dsm-trae-switch-pill",
3158
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
3159
+ type: "checkbox",
3160
+ checked: autoCheckin,
3161
+ disabled: busy !== void 0 || !writable,
3162
+ "aria-label": t("row.poolAutoCheckin"),
3163
+ onChange: (event) => {
3164
+ setAutoCheckin(event.target.checked);
3165
+ }
3166
+ })
3167
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("b", {
3168
+ className: "dsm-trae-pool-modebar-label",
3169
+ children: t("row.poolAutoCheckin")
3170
+ })]
3171
+ }),
3172
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
3173
+ className: "dsm-trae-pool-note",
3174
+ children: t("row.poolAutoCheckinHint")
3175
+ }),
3176
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
3177
+ className: "dsm-trae-pool-note",
3178
+ children: t("row.poolAutoCheckinDeviceHint")
3179
+ }),
3180
+ pool.autoCheckin === true ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
3181
+ className: "dsm-trae-pool-auto-checkin-status",
3182
+ role: "status",
3183
+ children: pool.autoCheckinState?.deviceBlocked === true ? t("row.poolAutoCheckinDeviceDone") : pool.autoCheckinState !== void 0 ? t(pool.autoCheckinState.failures > 0 ? "row.poolAutoCheckinRetry" : "row.poolAutoCheckinProgress", { count: pool.autoCheckinState.done }) : t("row.poolAutoCheckinWaiting")
3184
+ }) : null
3185
+ ]
3186
+ }) : null
3187
+ ]
2806
3188
  }),
2807
3189
  pool.enabled === true && serving !== void 0 && serving.member !== true ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
2808
3190
  className: "dsm-trae-pool-note dsm-trae-pool-serving-note",
@@ -4027,96 +4409,97 @@ window.__ModuleLoader__.load({
4027
4409
  * is precisely why this section has no 「保存」 of its own — the model catalog,
4028
4410
  * which does, lives in its own section.
4029
4411
  */
4030
- function TraeAccountPanel({ t, settingsScope, panel, status, statusLabel, credentialRegionIsAi, regionOn, showPointsInMainUi, onToggleShowPoints, creditPosition, onCreditPositionChange, sidebarPanelOn, sidebarPanelBusy, onToggleSidebarPanel, savingPreferences, creditsLabelId, creditsHintId, rows, busy, rescanNote, onRescan, switchingAccount, onSwitchAccount, onClaim, onTransferChanged, poolDetails, poolClaims, models, deviceClaimed, onPoolChanged, onPoolRefresh, creditsUnavailable, creditsExpiry, alternatives, onSwitchToAlternative }) {
4412
+ function TraeAccountPanel({ t, settingsScope, panel, status, statusLabel, credentialRegionIsAi, regionOn, showPointsInMainUi, onToggleShowPoints, creditPosition, onCreditPositionChange, sidebarPanelOn, sidebarPanelBusy, onToggleSidebarPanel, savingPreferences, creditsLabelId, creditsHintId, rows, busy, rescanNote, onRescan, switchingAccount, onSwitchAccount, onClaim, onTransferChanged, poolDetails, poolClaims, models = [], deviceClaimed = false, onPoolChanged = () => {}, onPoolRefresh = () => {}, creditsUnavailable, creditsExpiry, alternatives, onSwitchToAlternative, section = "all" }) {
4031
4413
  const writable = settingsScope?.getSnapshot().writable === true;
4032
4414
  const signedIn = status.status === "signed-in";
4033
4415
  const accounts = signedIn ? status.accounts : [];
4034
4416
  const pool = signedIn ? status.pool : void 0;
4035
- return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
4036
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4037
- className: "dsm-trae-prefs",
4038
- children: [
4039
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
4040
- className: "dsm-trae-pref",
4041
- title: t(credentialRegionIsAi ? "row.showPointsInMainUiHintAi" : "row.showPointsInMainUiHint"),
4042
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
4043
- type: "checkbox",
4044
- className: "dsm-trae-pref-check",
4045
- checked: showPointsInMainUi,
4046
- disabled: !writable || savingPreferences,
4047
- "aria-describedby": creditsHintId,
4048
- onChange: (event) => {
4049
- onToggleShowPoints(event.currentTarget.checked);
4050
- }
4051
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
4052
- className: "dsm-trae-pref-copy",
4053
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
4054
- className: "dsm-trae-pref-label",
4055
- id: creditsLabelId,
4056
- children: t("row.showPointsInMainUi")
4057
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
4058
- className: "dsm-trae-pref-hint",
4059
- id: creditsHintId,
4060
- children: t(credentialRegionIsAi ? "row.showPointsInMainUiHintAi" : "row.showPointsInMainUiHint")
4061
- })]
4417
+ if (section === "prefs") return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_jsx_runtime.Fragment, { children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4418
+ className: "dsm-trae-prefs",
4419
+ children: [
4420
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
4421
+ className: "dsm-trae-pref",
4422
+ title: t(credentialRegionIsAi ? "row.showPointsInMainUiHintAi" : "row.showPointsInMainUiHint"),
4423
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
4424
+ type: "checkbox",
4425
+ className: "dsm-trae-pref-check",
4426
+ checked: showPointsInMainUi,
4427
+ disabled: !writable || savingPreferences,
4428
+ "aria-describedby": creditsHintId,
4429
+ onChange: (event) => {
4430
+ onToggleShowPoints(event.currentTarget.checked);
4431
+ }
4432
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
4433
+ className: "dsm-trae-pref-copy",
4434
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
4435
+ className: "dsm-trae-pref-label",
4436
+ id: creditsLabelId,
4437
+ children: t("row.showPointsInMainUi")
4438
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
4439
+ className: "dsm-trae-pref-hint",
4440
+ id: creditsHintId,
4441
+ children: t(credentialRegionIsAi ? "row.showPointsInMainUiHintAi" : "row.showPointsInMainUiHint")
4062
4442
  })]
4063
- }),
4064
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("label", {
4065
- className: "dsm-trae-pref dsm-trae-credit-position-pref",
4066
- title: t("row.creditPositionHint"),
4067
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
4068
- className: "dsm-trae-pref-copy",
4069
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
4070
- className: "dsm-trae-pref-lead",
4071
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
4072
- className: "dsm-trae-pref-label",
4073
- children: t("row.creditPositionLabel")
4074
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
4075
- className: "dsm-trae-credit-position",
4076
- value: regionOn ? creditPosition : "composer",
4077
- disabled: !regionOn || !writable || savingPreferences,
4078
- "aria-label": t("row.creditPositionAria", { region: panel === "cn" ? t("row.tabCn") : panel === "enterprise" ? t("row.tabEnterprise") : t("row.tabAi") }),
4079
- onChange: (event) => {
4080
- onCreditPositionChange(event.currentTarget.value === "stats" ? "stats" : "composer");
4081
- },
4082
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
4083
- value: "composer",
4084
- children: t("row.creditPositionComposer")
4085
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
4086
- value: "stats",
4087
- children: t("row.creditPositionStats")
4088
- })]
4089
- })]
4090
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
4091
- className: "dsm-trae-pref-hint",
4092
- children: t("row.creditPositionHint")
4093
- })]
4094
- })
4095
- }),
4096
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
4097
- className: "dsm-trae-pref",
4098
- title: t("row.sidebarPanelHint"),
4099
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
4100
- type: "checkbox",
4101
- className: "dsm-trae-pref-check",
4102
- checked: sidebarPanelOn,
4103
- disabled: sidebarPanelBusy || savingPreferences,
4104
- onChange: (event) => {
4105
- onToggleSidebarPanel(event.currentTarget.checked);
4106
- }
4107
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
4108
- className: "dsm-trae-pref-copy",
4443
+ })]
4444
+ }),
4445
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("label", {
4446
+ className: "dsm-trae-pref dsm-trae-credit-position-pref",
4447
+ title: t("row.creditPositionHint"),
4448
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
4449
+ className: "dsm-trae-pref-copy",
4450
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
4451
+ className: "dsm-trae-pref-lead",
4109
4452
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
4110
4453
  className: "dsm-trae-pref-label",
4111
- children: t("row.sidebarPanelLabel")
4112
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
4113
- className: "dsm-trae-pref-hint",
4114
- children: t("row.sidebarPanelHint")
4454
+ children: t("row.creditPositionLabel")
4455
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
4456
+ className: "dsm-trae-credit-position",
4457
+ value: regionOn ? creditPosition : "composer",
4458
+ disabled: !regionOn || !writable || savingPreferences,
4459
+ "aria-label": t("row.creditPositionAria", { region: panel === "cn" ? t("row.tabCn") : panel === "enterprise" ? t("row.tabEnterprise") : t("row.tabAi") }),
4460
+ onChange: (event) => {
4461
+ onCreditPositionChange(event.currentTarget.value === "stats" ? "stats" : "composer");
4462
+ },
4463
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
4464
+ value: "composer",
4465
+ children: t("row.creditPositionComposer")
4466
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
4467
+ value: "stats",
4468
+ children: t("row.creditPositionStats")
4469
+ })]
4115
4470
  })]
4471
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
4472
+ className: "dsm-trae-pref-hint",
4473
+ children: t("row.creditPositionHint")
4116
4474
  })]
4117
4475
  })
4118
- ]
4119
- }),
4476
+ }),
4477
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
4478
+ className: "dsm-trae-pref",
4479
+ title: t("row.sidebarPanelHint"),
4480
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
4481
+ type: "checkbox",
4482
+ className: "dsm-trae-pref-check",
4483
+ checked: sidebarPanelOn,
4484
+ disabled: sidebarPanelBusy || savingPreferences,
4485
+ onChange: (event) => {
4486
+ onToggleSidebarPanel(event.currentTarget.checked);
4487
+ }
4488
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
4489
+ className: "dsm-trae-pref-copy",
4490
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
4491
+ className: "dsm-trae-pref-label",
4492
+ children: t("row.sidebarPanelLabel")
4493
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
4494
+ className: "dsm-trae-pref-hint",
4495
+ children: t("row.sidebarPanelHint")
4496
+ })]
4497
+ })]
4498
+ }),
4499
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconStylePreference, { t })
4500
+ ]
4501
+ }) });
4502
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
4120
4503
  pool !== void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
4121
4504
  className: "dsm-trae-accounts",
4122
4505
  "aria-label": t("row.accountsTitle"),
@@ -4322,9 +4705,9 @@ window.__ModuleLoader__.load({
4322
4705
  className: "dsm-trae-usage-error",
4323
4706
  children: t("row.checkinDisabled")
4324
4707
  }) : null,
4325
- status.checkinError === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
4708
+ status.checkinError === void 0 || status.checkin?.checkedIn === true || status.checkin?.didCheckedIn === true || status.pool?.autoCheckinState?.deviceBlocked === true ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
4326
4709
  className: "dsm-trae-usage-error",
4327
- children: t("row.checkinError", { message: status.checkinError })
4710
+ children: t("row.checkinStatusError", { message: status.checkinError })
4328
4711
  })
4329
4712
  ] }) : null
4330
4713
  ] });
@@ -5244,7 +5627,6 @@ window.__ModuleLoader__.load({
5244
5627
  if (mounted.current) {
5245
5628
  const message = error instanceof Error ? error.message : t("row.requestFailed");
5246
5629
  setRescanNote(t("row.accountsRescanFailed", { message }));
5247
- setWriteError(message);
5248
5630
  }
5249
5631
  } finally {
5250
5632
  if (mounted.current) setBusy(false);
@@ -6148,9 +6530,22 @@ window.__ModuleLoader__.load({
6148
6530
  }, item);
6149
6531
  })
6150
6532
  }),
6151
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
6152
- className: "dsm-trae-tab-hint",
6153
- children: t("row.tabHint")
6533
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
6534
+ className: "dsm-trae-region-summary",
6535
+ children: [
6536
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
6537
+ className: "dsm-trae-models-summary dsm-trae-tab-hint",
6538
+ children: t("row.tabHint")
6539
+ }),
6540
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
6541
+ className: "dsm-trae-models-summary",
6542
+ children: t("row.tabCredentialHint")
6543
+ }),
6544
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
6545
+ className: "dsm-trae-models-summary dsm-trae-free-quota-warn",
6546
+ children: t("row.tabFreeQuotaWarn")
6547
+ })
6548
+ ]
6154
6549
  }),
6155
6550
  writeError === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
6156
6551
  className: "dsm-trae-usage-error",
@@ -6191,6 +6586,18 @@ window.__ModuleLoader__.load({
6191
6586
  },
6192
6587
  children: t("row.panelModels")
6193
6588
  }),
6589
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
6590
+ type: "button",
6591
+ role: "tab",
6592
+ id: "dsm-trae-tab-capabilities",
6593
+ "aria-controls": "dsm-trae-panel-capabilities",
6594
+ "aria-selected": panel === "capabilities",
6595
+ className: `dsm-trae-panel-tab${panel === "capabilities" ? " dsm-trae-panel-tab-active" : ""}`,
6596
+ onClick: () => {
6597
+ setPanel("capabilities");
6598
+ },
6599
+ children: t("row.panelCapabilities")
6600
+ }),
6194
6601
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
6195
6602
  type: "button",
6196
6603
  role: "tab",
@@ -6214,6 +6621,7 @@ window.__ModuleLoader__.load({
6214
6621
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(TraeAccountPanel, {
6215
6622
  t,
6216
6623
  settingsScope,
6624
+ section: "accounts",
6217
6625
  panel: activeRegion,
6218
6626
  status,
6219
6627
  statusLabel: label,
@@ -6281,6 +6689,68 @@ window.__ModuleLoader__.load({
6281
6689
  }
6282
6690
  })
6283
6691
  }),
6692
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
6693
+ className: "dsm-trae-panel",
6694
+ role: "tabpanel",
6695
+ id: "dsm-trae-panel-capabilities",
6696
+ "aria-labelledby": "dsm-trae-tab-capabilities",
6697
+ hidden: signedIn && panel !== "capabilities",
6698
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
6699
+ className: "dsm-trae-panel-note",
6700
+ children: t("row.panelCapabilitiesHint")
6701
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(TraeAccountPanel, {
6702
+ t,
6703
+ settingsScope,
6704
+ section: "prefs",
6705
+ panel: activeRegion,
6706
+ status,
6707
+ statusLabel: label,
6708
+ credentialRegionIsAi,
6709
+ regionOn: activeRegionOn,
6710
+ showPointsInMainUi: settingsEdits.showPointsInMainUi,
6711
+ onToggleShowPoints: (value) => {
6712
+ writePreference("showPointsInMainUi", value);
6713
+ },
6714
+ creditPosition: creditPositionOn(activeRegion),
6715
+ onCreditPositionChange: (position) => {
6716
+ changeCreditPosition(activeRegion, position);
6717
+ },
6718
+ sidebarPanelOn,
6719
+ sidebarPanelBusy,
6720
+ onToggleSidebarPanel: (value) => {
6721
+ writeSidebarPanel(value);
6722
+ },
6723
+ savingPreferences: savingSettings,
6724
+ creditsLabelId,
6725
+ creditsHintId,
6726
+ rows: accountRows,
6727
+ busy,
6728
+ rescanNote,
6729
+ onRescan: () => {
6730
+ rescanAccounts();
6731
+ },
6732
+ switchingAccount,
6733
+ onSwitchAccount: (accountId) => {
6734
+ switchAccount(accountId);
6735
+ },
6736
+ onClaim: (accountId) => {
6737
+ claimFor(accountId);
6738
+ },
6739
+ onTransferChanged: async () => {
6740
+ const fresh = await refreshUsage(activeRegion);
6741
+ if (fresh !== void 0) setStatusByRegion((current) => ({
6742
+ ...current,
6743
+ [activeRegion]: fresh
6744
+ }));
6745
+ },
6746
+ creditsUnavailable: status.status === "signed-in" ? status.creditsUnavailable : void 0,
6747
+ creditsExpiry: status.status === "signed-in" ? status.credits?.expiry : void 0,
6748
+ alternatives: status.status === "signed-in" ? status.alternatives : void 0,
6749
+ onSwitchToAlternative: (accountId) => {
6750
+ switchAccount(accountId);
6751
+ }
6752
+ })]
6753
+ }),
6284
6754
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
6285
6755
  className: "dsm-trae-panel",
6286
6756
  role: "tabpanel",
@@ -6484,19 +6954,25 @@ window.__ModuleLoader__.load({
6484
6954
  className: "dsm-trae-context-budget",
6485
6955
  "aria-label": t("row.contextBudget"),
6486
6956
  title: t("row.contextBudgetHint"),
6487
- children: contextBudgetOptions(model).map((option) => {
6957
+ children: (() => {
6958
+ const options = contextBudgetOptions(model);
6488
6959
  const selectable = model.maxContextWindow !== void 0;
6489
- const checked = (activeContextBudgets[model.id] ?? model.contextWindow) === option;
6490
- return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
6491
- type: "radio",
6492
- name: `context-${model.id}`,
6493
- checked,
6960
+ const current = activeContextBudgets[model.id] ?? model.contextWindow;
6961
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("select", {
6962
+ className: "dsm-trae-context-select",
6963
+ "aria-label": t("row.contextBudget"),
6964
+ title: t("row.contextBudgetHint"),
6965
+ value: current === void 0 ? "" : String(current),
6494
6966
  disabled: !selectable || settingsScope?.getSnapshot().writable !== true || saving,
6495
- onChange: () => {
6496
- setContextBudget(model.id, option);
6497
- }
6498
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: formatCapacity(option, t("row.modelUnknown")) })] }, option);
6499
- })
6967
+ onChange: (event) => {
6968
+ setContextBudget(model.id, Number(event.currentTarget.value));
6969
+ },
6970
+ children: options.map((option) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
6971
+ value: String(option),
6972
+ children: formatCapacity(option, t("row.modelUnknown"))
6973
+ }, option))
6974
+ });
6975
+ })()
6500
6976
  })]
6501
6977
  })]
6502
6978
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
@@ -6878,11 +7354,7 @@ window.__ModuleLoader__.load({
6878
7354
  className: "dsm-trae-page-back-caret"
6879
7355
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("row.pageBack") })]
6880
7356
  }),
6881
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("img", {
6882
- className: "dsm-trae-page-icon",
6883
- src: TRAE_PLUGIN_ICON,
6884
- alt: ""
6885
- }),
7357
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(PluginIcon, { className: "dsm-trae-page-icon" }),
6886
7358
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
6887
7359
  className: "dsm-trae-page-title",
6888
7360
  children: title
@@ -6919,6 +7391,14 @@ window.__ModuleLoader__.load({
6919
7391
  }) : null;
6920
7392
  if (pageSurface) return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
6921
7393
  className: "dsm-trae-page",
7394
+ onScroll: (event) => {
7395
+ updateStickyTabs(event.currentTarget);
7396
+ },
7397
+ ref: (page) => {
7398
+ if (page !== null) requestAnimationFrame(() => {
7399
+ updateStickyTabs(page);
7400
+ });
7401
+ },
6922
7402
  children: [
6923
7403
  pageHead,
6924
7404
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
@@ -6954,11 +7434,7 @@ window.__ModuleLoader__.load({
6954
7434
  setCardOpen(!open);
6955
7435
  },
6956
7436
  children: [
6957
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("img", {
6958
- className: "dsm-plugin-card-icon",
6959
- src: TRAE_PLUGIN_ICON,
6960
- alt: ""
6961
- }),
7437
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(PluginIcon, { className: "dsm-plugin-card-icon" }),
6962
7438
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
6963
7439
  className: "dsm-plugin-card-head",
6964
7440
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
@@ -7993,41 +8469,178 @@ window.__ModuleLoader__.load({
7993
8469
  /** The shipped slot whose store seat is the settings shell's state handle. */
7994
8470
  const SETTINGS_SLOT = "sidebar.settings";
7995
8471
  /**
7996
- * Open this plugin's settings page — the wide Settings dialog, on this
7997
- * plugin's section.
8472
+ * The sidebar's settings trigger: dialog semantics plus an expanded state.
7998
8473
  *
7999
- * @param get - the entry's soft service probe (`ctx.get`), which returns
8000
- * `undefined` for an absent service instead of throwing. Called HERE, on every
8001
- * invocation, so a service that appears after `apply` is still found.
8002
- * @returns whether a route was taken. `false` means the caller's control should
8003
- * report that this host has no way there — see the module note.
8474
+ * Both attributes are load-bearing. `aria-haspopup="dialog"` identifies it as
8475
+ * the thing that opens the panel; `aria-expanded` is how route 2 tells an open
8476
+ * panel from a closed one WITHOUT clicking, because clicking a trigger that is
8477
+ * already expanded CLOSES the dialog — a jump that would read as the settings
8478
+ * window flickering shut.
8479
+ */
8480
+ const SETTINGS_TRIGGER_SELECTOR = "button[aria-haspopup=\"dialog\"][aria-expanded]";
8481
+ /**
8482
+ * Budget for "panel mounted + nav row rendered".
8483
+ *
8484
+ * The panel mounts synchronously on the click, but the nav rows' labels wait
8485
+ * for a locale snapshot, so a same-frame query is often one tick too early.
8486
+ * Five attempts over ~300ms covers one layout pass while staying under the
8487
+ * threshold where a user starts to think the press did nothing.
8488
+ */
8489
+ const RETRY_DELAYS_MS = [
8490
+ 0,
8491
+ 60,
8492
+ 70,
8493
+ 80,
8494
+ 90
8495
+ ];
8496
+ function defaultSchedule(run, ms) {
8497
+ try {
8498
+ window.setTimeout(run, ms);
8499
+ } catch {
8500
+ run();
8501
+ }
8502
+ }
8503
+ /**
8504
+ * Find the settings nav row whose label is this plugin's.
8505
+ *
8506
+ * Trimmed equality, not `includes`: 「dsh-connect-trae」 is a SUBSTRING of
8507
+ * nothing here, but 「模型」 is a substring of 「模型与预设」 on a differently
8508
+ * worded shell, and a loose match would click the wrong row.
8509
+ */
8510
+ function findSectionRow(scope, label) {
8511
+ const want = label.trim();
8512
+ if (want === "") return void 0;
8513
+ return Array.from(scope.querySelectorAll("button")).find((button) => (button.textContent ?? "").trim() === want);
8514
+ }
8515
+ /**
8516
+ * Route 1: open this plugin's section through the shell's store seat.
8517
+ *
8518
+ * @returns `'opened'` when the dialog is on the right section, `'shell'` when
8519
+ * the dialog opened but is NOT preselected (route 2 can still click the row),
8520
+ * and `undefined` when the seat is absent or unusable.
8004
8521
  */
8005
- function openPluginSettingsPage(get) {
8006
- const slots = get("slots");
8007
- if (slots === void 0 || typeof slots.entries !== "function") return false;
8522
+ function openThroughStoreSeat(get, sectionId) {
8523
+ let slots;
8524
+ try {
8525
+ slots = get("slots");
8526
+ } catch {
8527
+ return;
8528
+ }
8529
+ if (slots === void 0 || typeof slots.entries !== "function") return void 0;
8008
8530
  let seat;
8009
8531
  try {
8010
8532
  seat = slots.entries(SETTINGS_SLOT)[0]?.store;
8011
8533
  } catch {
8012
- return false;
8534
+ return;
8013
8535
  }
8014
- if (seat === null || typeof seat !== "object") return false;
8536
+ if (seat === null || typeof seat !== "object") return void 0;
8015
8537
  const handle = seat;
8016
- if (typeof handle.create !== "function") return false;
8538
+ if (typeof handle.create !== "function") return void 0;
8017
8539
  try {
8018
8540
  const instance = handle.create();
8019
8541
  const openSection = instance?.actions?.openSection;
8020
8542
  if (typeof openSection === "function") {
8021
- openSection(TRAE_SETTINGS_ID);
8022
- return true;
8543
+ openSection(sectionId);
8544
+ return "opened";
8023
8545
  }
8024
8546
  const open = instance?.actions?.open;
8025
8547
  if (typeof open === "function") {
8026
8548
  open();
8027
- return true;
8549
+ return "shell";
8028
8550
  }
8029
8551
  } catch {}
8030
- return false;
8552
+ }
8553
+ /**
8554
+ * Open this plugin's settings page — the wide Settings dialog, on THIS
8555
+ * plugin's section.
8556
+ *
8557
+ * ## Two routes, best first (the shape borrowed from dsh-connect-workbuddy 5.20.4)
8558
+ *
8559
+ * 1. **The shell's store seat** — one call, exact, no timing:
8560
+ * `slots.entries(SETTINGS_SLOT)[0].store.create().actions.openSection(id)`.
8561
+ * 2. **Driving the chrome** — click the sidebar's settings trigger, wait for the
8562
+ * panel, then click this plugin's nav row by its label.
8563
+ *
8564
+ * Route 1 is strictly better where it exists: `openSection(id)` sets `activeId`
8565
+ * AND `open` in one action, so there is no window in which the dialog is open
8566
+ * on the wrong section, no retry loop, and no dependency on the nav row's
8567
+ * localized text. Route 2 is kept because the host range this plugin declares
8568
+ * spans shell lines that may predate the seat; deleting it would trade a
8569
+ * working fallback for nothing.
8570
+ *
8571
+ * ## Why both routes are needed even now
8572
+ *
8573
+ * A seat that carries `open()` but not `openSection()` is a real shell shape
8574
+ * (the store gained `openSection` after `open`). Returning `true` on that seat —
8575
+ * which 4.9.x did — claimed success while leaving the user on the shell's
8576
+ * default section, so the button appeared to do something other than what its
8577
+ * label promised. Handing back `'shell'` and continuing to route 2 is what
8578
+ * makes the button land on the page it names on EVERY shell.
8579
+ *
8580
+ * ## Why the probe happens per PRESS, not during `apply`
8581
+ *
8582
+ * The seat belongs to ANOTHER bundle whose apply order is not ours to assume.
8583
+ * Probing once during `apply()` would capture `undefined` on a host whose
8584
+ * settings bundle applied later, leaving the button dead for the whole session.
8585
+ * `entries()` answers `[]` for an undeclared slot, so a per-press probe is
8586
+ * stateless and always current — the same reason the caller passes `softGet`.
8587
+ *
8588
+ * ## Why it never throws
8589
+ *
8590
+ * Every path runs from a RENDER path (a click handler in the credits popover).
8591
+ * A throw here would take out the whole readout rather than one button, so each
8592
+ * stage swallows its own failure and the jump degrades to a silent no-op when
8593
+ * the shell has a different shape.
8594
+ *
8595
+ * @param get - the entry's soft service probe (`ctx.get`), which returns
8596
+ * `undefined` for an absent service instead of throwing. Called HERE, on every
8597
+ * invocation, so a service that appears after `apply` is still found.
8598
+ * @param options - label thunk (required for route 2) plus test seams.
8599
+ * @returns whether a route was taken. `false` means `onGiveUp` already ran or
8600
+ * there was nothing to fall back to.
8601
+ */
8602
+ function openPluginSettingsPage(get, options) {
8603
+ const { doc = document, label } = options;
8604
+ const schedule = options.schedule ?? defaultSchedule;
8605
+ let seatResult;
8606
+ try {
8607
+ seatResult = openThroughStoreSeat(get, TRAE_SETTINGS_ID);
8608
+ } catch {
8609
+ seatResult = void 0;
8610
+ }
8611
+ if (seatResult === "opened") return true;
8612
+ try {
8613
+ if (doc.querySelector("[data-shortcut-modal=\"settings\"]") === null) {
8614
+ const collapsed = Array.from(doc.querySelectorAll(SETTINGS_TRIGGER_SELECTOR)).find((button) => button.getAttribute("aria-expanded") !== "true");
8615
+ if (collapsed === void 0) {
8616
+ try {
8617
+ options.onGiveUp?.();
8618
+ } catch {}
8619
+ return false;
8620
+ }
8621
+ collapsed.click();
8622
+ }
8623
+ let clicked = false;
8624
+ let remaining = RETRY_DELAYS_MS.length;
8625
+ for (const delay of RETRY_DELAYS_MS) schedule(() => {
8626
+ if (clicked) return;
8627
+ remaining -= 1;
8628
+ try {
8629
+ const row = findSectionRow(doc.querySelector("[data-shortcut-modal=\"settings\"]") ?? doc, label());
8630
+ if (row !== void 0) {
8631
+ clicked = true;
8632
+ row.click();
8633
+ return;
8634
+ }
8635
+ } catch {}
8636
+ if (remaining === 0) try {
8637
+ options.onGiveUp?.();
8638
+ } catch {}
8639
+ }, delay);
8640
+ return true;
8641
+ } catch {
8642
+ return false;
8643
+ }
8031
8644
  }
8032
8645
  //#endregion
8033
8646
  //#region src/client/locales.ts
@@ -8037,46 +8650,64 @@ window.__ModuleLoader__.load({
8037
8650
  * two plugins share one external-presentation language.
8038
8651
  */
8039
8652
  const en = {
8040
- "row.title": "Trae credits & models (dsh-connect-trae)",
8041
- "row.desc": "Use Trae models in DSH and see your remaining credits; the domestic and international sides are two independent providers, each with its own account — use both at the same time.",
8653
+ "row.tabCredentialHint": "Domestic and enterprise providers share Trae CN credentials and save account bindings separately.",
8654
+ "row.tabFreeQuotaWarn": "Free models can still encounter upstream request limits or busy responses.",
8655
+ "row.poolFailoverHint": "Trae also tries another account after server failures. Invalid requests and model permission refusals are reported directly.",
8656
+ "row.iconStyleLabel": "Icon style",
8657
+ "row.iconStyleHint": "Updates the sidebar and page icons immediately. Saved separately for this plugin in this browser.",
8658
+ "row.iconStyleClaude": "Claude style",
8659
+ "row.iconStyleOriginal": "Default icon",
8660
+ "row.iconStyleSaveFailed": "The browser could not save the icon preference. Please check site storage permissions.",
8661
+ "row.poolAutoCheckinDeviceHint": "Trae limits daily rewards per device, so some members may not receive a reward.",
8662
+ "row.checkinStatusError": "Could not read today's check-in status: {message}",
8663
+ "row.poolAutoCheckinDeviceDone": "Today: this device has reached its daily reward limit.",
8664
+ "row.poolAutoCheckinRetry": "Today: {count} accounts checked in. Failed requests will retry automatically.",
8665
+ "row.poolAutoCheckinProgress": "Today: {count} accounts checked in.",
8666
+ "row.poolAutoCheckinWaiting": "Waiting for the next background check.",
8667
+ "row.poolAutoCheckin": "Check in automatically each day",
8668
+ "row.poolAutoCheckinHint": "Enable and save to claim daily rewards for selected domestic personal accounts while DSH is running. After a restart, the plugin checks today's progress before processing remaining accounts. Failed attempts retry automatically.",
8669
+ "row.title": "Trae credits & models",
8670
+ "row.desc": "Use Trae domestic, enterprise and global models in DSH. Manage accounts, select models, view credits and claim daily check-in rewards. Refreshing the model catalog also updates credit multipliers.",
8042
8671
  "row.expand": "Expand",
8043
8672
  "row.collapse": "Collapse",
8044
8673
  "row.tabCn": "Domestic",
8045
8674
  "row.tabEnterprise": "Enterprise",
8046
8675
  "row.tabAi": "Global",
8047
- "row.tabHint": "Three independent providers, each with its own account, usage, and models. All are live at once and never affect each other.",
8676
+ "row.tabHint": "Domestic, enterprise and global versions can be enabled together. Use the version tabs above to view and manage each version's accounts, credits and models.",
8048
8677
  "row.tabEnterpriseHint": "Enterprise bills the tenant pool of the same Trae CN account instead of the personal one. It never falls back: a refusal is reported as an error rather than quietly spending personal credits.",
8049
- "row.tabSwitchHint": "Turns this provider off: its models leave the DSH picker. Account and settings are kept.",
8678
+ "row.tabSwitchHint": "Uncheck to switch this provider off. Its models disappear from the DSH model picker; your account, credits, and model settings are kept and return when you check it again.",
8050
8679
  "row.tabSwitchAria": "Offer the {region} provider to DSH",
8051
- "row.tabOffNotice": "This provider is off: its models are not offered to DSH. Account and settings are kept.",
8680
+ "row.tabOffNotice": "This provider is switched off. None of its models are offered to DSH. Your account, credits, and model settings are kept. Check the box above the tab to bring it back.",
8052
8681
  "row.panelAria": "Card sections",
8053
8682
  "row.panelAccounts": "Account",
8683
+ "row.panelCapabilities": "Capabilities",
8684
+ "row.panelCapabilitiesHint": "What the plugin may do on your behalf. These switches take effect on the click — there is no Save button for them.",
8054
8685
  "row.panelModels": "Models",
8055
8686
  "row.panelDiag": "Diagnostics",
8056
- "row.diagRegion": "Panel",
8687
+ "row.diagRegion": "Region",
8057
8688
  "row.diagProvider": "Provider id",
8058
8689
  "row.diagAccount": "Serving account",
8059
- "row.diagToken": "Access token expires",
8690
+ "row.diagToken": "Sign-in expires",
8060
8691
  "row.diagRefresh": "Re-sign-in deadline",
8061
8692
  "row.diagRefreshNone": "this credential carries none",
8062
- "row.diagCatalog": "Models in catalog",
8063
- "row.diagEnabled": "Models enabled",
8693
+ "row.diagCatalog": "Models in the catalog",
8694
+ "row.diagEnabled": "Enabled models",
8064
8695
  "row.diagPool": "Account pool",
8065
- "row.diagPoolOn": "on · {count} account(s)",
8696
+ "row.diagPoolOn": "on · {count} accounts",
8066
8697
  "row.diagPoolOff": "off",
8067
8698
  "row.diagCheckin": "Daily check-in",
8068
8699
  "row.diagCheckinOn": "supported",
8069
8700
  "row.diagCheckinOff": "not offered here",
8070
8701
  "row.diagRaw": "Raw chat channel",
8071
8702
  "row.diagRawOff": "not checked",
8072
- "row.diagFailures": "Last reported failure",
8073
- "row.diagNoFailures": "none",
8703
+ "row.diagFailures": "Last failure",
8704
+ "row.diagNoFailures": "Nothing reported.",
8074
8705
  "row.diagQuarantine": "Quarantined copies",
8075
8706
  "row.diagQuarantineNone": "none",
8076
- "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.",
8707
+ "row.diagHint": "These values come from this panel's latest refresh. Remove account identifiers and personal information in local paths before sharing a report.",
8077
8708
  "row.settingsWriteFailed": "The setting was not saved: {message}",
8078
8709
  "row.signedOut": "Not signed in",
8079
- "row.signedOutHint": "Sign in once in the Trae desktop app; this plugin follows that sign-in automatically.",
8710
+ "row.signedOutHint": "Sign in to the Trae desktop app, then click Detect accounts again to save a plugin-owned credential copy.",
8080
8711
  "row.unsupportedRegionTitle": "US region is not supported yet",
8081
8712
  "row.unsupportedRegionHint": "A Trae account on this machine belongs to the United States region (userRegion \"US\" / host *.traeapi.us). This build does not serve that region, so the account is NOT used and no request is sent to the Singapore gateway — signing in again changes nothing. Use a CN or Singapore account (or the Trae CN build) in this panel.",
8082
8713
  "row.diagUnsupportedRegion": "Unsupported region",
@@ -8084,7 +8715,7 @@ window.__ModuleLoader__.load({
8084
8715
  "row.searchedHint": "No sign-in was found. These locations were probed; if your Trae stores its login elsewhere, please report the path.",
8085
8716
  "row.searchedMore": "Show {count} more",
8086
8717
  "row.quarantineTitle": "Quarantined credential copies",
8087
- "row.quarantineHint": "A credential copy could not be read, so the plugin did not overwrite it — it was renamed aside with its bytes intact. The accounts it held exist only in that file; delete it once you have them back.",
8718
+ "row.quarantineHint": "The plugin could not read a credential copy and renamed the file to preserve it. Inspect that file if you need to recover accounts. Delete it only after confirming that it is no longer needed.",
8088
8719
  "row.quarantineAccounts": "{count} account(s)",
8089
8720
  "row.quarantineAccountsUnknown": "account count unreadable",
8090
8721
  "row.quarantineBytes": "{bytes} bytes",
@@ -8094,7 +8725,7 @@ window.__ModuleLoader__.load({
8094
8725
  "row.sourceDesktop": "desktop app",
8095
8726
  "row.sourceCli": "Trae CLI",
8096
8727
  "row.signedIn": "Signed in: {accountName}",
8097
- "row.tokenExpiry": "Access token expires {expiresAt} (auto-renewal)",
8728
+ "row.tokenExpiry": "Access token expires at {expiresAt}; the plugin will attempt to renew it automatically",
8098
8729
  "row.refreshTokenExpiring": "Sign in to Trae again by {expiresAt} ({days} day(s) left) — automatic renewal stops after that.",
8099
8730
  "row.refreshTokenLapsed": "Automatic renewal has stopped: the Trae sign-in expired {expiresAt}. Sign in to Trae again to restore it.",
8100
8731
  "row.requestFailed": "Request failed",
@@ -8130,26 +8761,26 @@ window.__ModuleLoader__.load({
8130
8761
  "row.refreshCredits": "Refresh usage",
8131
8762
  "row.refreshing": "Refreshing…",
8132
8763
  "row.accountsTitle": "Trae account",
8133
- "row.accountsHint": "Copies this plugin keeps of every Trae sign-in on this machine (mirrored on refresh). Refresh after signing in, or Trae's next sign-in overwrites that login. Tokens are never shown.",
8134
- "row.transferTitle": "Import & export accounts",
8135
- "row.transferExport": "Export selected",
8136
- "row.transferImport": "Import from file",
8764
+ "row.accountsHint": "On refresh, the plugin saves copies of detected Trae credentials. Refresh after signing in to a new account, before another sign-in replaces credentials that have not yet been copied.",
8765
+ "row.transferTitle": "Export / import accounts",
8766
+ "row.transferExport": "Export accounts",
8767
+ "row.transferImport": "Import accounts",
8137
8768
  "row.transferCancel": "Cancel",
8138
8769
  "row.transferSelectAll": "Select all",
8139
- "row.transferExportPick": "Choose which of the {total} account copies to export.",
8140
- "row.transferExportConfirm": "Export {count}",
8141
- "row.transferExported": "Exported {count} account copy/copies to {fileName}.",
8142
- "row.transferExporting": "Preparing the export…",
8770
+ "row.transferExportPick": "Choose the accounts to export ({total} available):",
8771
+ "row.transferExportConfirm": "Export {count} account(s)",
8772
+ "row.transferExported": "Exported {count} account(s) to {fileName}.",
8773
+ "row.transferExporting": "Exporting…",
8143
8774
  "row.transferImportPick": "This file holds {total} record(s). Ticked records are added as plugin-owned copies; the Trae app is not touched.",
8144
- "row.transferImportConfirm": "Import {count}",
8775
+ "row.transferImportConfirm": "Import {count} account(s)",
8145
8776
  "row.transferImported": "Imported {imported} account copy/copies, skipped {skipped}.",
8146
8777
  "row.transferImporting": "Importing…",
8147
8778
  "row.transferReading": "Reading the file…",
8148
- "row.transferHint": "The exported file holds this plugin's own copies, including live tokens — treat it as a password. Records belonging to the other region are skipped.",
8779
+ "row.transferHint": "Export accounts to a JSON file or import accounts from one. The file contains login credentials; restrict access to it. Accounts from other regions are skipped during import.",
8149
8780
  "row.transferError": "Import/export failed: {message}",
8150
8781
  "row.transferVeilHint": "The account list refreshes when this finishes.",
8151
8782
  "row.transferUnnamed": "Unnamed account",
8152
- "row.transferNoToken": "no token, will be skipped",
8783
+ "row.transferNoToken": "no token in this record",
8153
8784
  "row.accountsRescan": "Detect accounts again",
8154
8785
  "row.accountsScanning": "Detecting…",
8155
8786
  "row.accountsLoading": "Loading your accounts…",
@@ -8162,7 +8793,7 @@ window.__ModuleLoader__.load({
8162
8793
  "row.accountColumnCheckin": "Check-in",
8163
8794
  "row.accountBalance": "{credits} credits",
8164
8795
  "row.balanceConsoleOnly": "In the enterprise console",
8165
- "row.enterpriseCreditsNotice": "Enterprise spend is not reported here. The Trae client cannot read the tenant balance — every enterprise quota endpoint answers 404 — so the only place to see it is the enterprise console (console.enterprise.trae.cn). The personal credit figure is deliberately NOT shown on this tab: it belongs to the consumer pool and would be misread as the tenant balance.",
8796
+ "row.enterpriseCreditsNotice": "The plugin cannot currently read the tenant balance. Check console.enterprise.trae.cn. Personal credits are omitted here to avoid presenting them as enterprise quota.",
8166
8797
  "row.sourceDsh": "plugin copy",
8167
8798
  "row.modelsTitle": "Models",
8168
8799
  "row.modelsSummary": "{count} enabled",
@@ -8176,7 +8807,7 @@ window.__ModuleLoader__.load({
8176
8807
  "row.modelsViewLabel": "Model scope",
8177
8808
  "row.modelsViewCommon": "Common channels · {count}",
8178
8809
  "row.modelsViewAll": "All · {count}",
8179
- "row.modelsViewCommonHint": "Models a normal SOLO workspace chat can call (solo_work_remote / solo_work_lite).",
8810
+ "row.modelsViewCommonHint": "Models offered through solo_work_remote or solo_work_lite.",
8180
8811
  "row.modelsViewAllHint": "Also includes models only offered by a specialist channel: solo_coder, chat_v3, solo_agent_remote.",
8181
8812
  "row.modelsViewShowAll": "Show all",
8182
8813
  "row.modelsSearchOutsideView": "No match for 「{query}」 on the common channels; the full catalog has {count}.",
@@ -8190,20 +8821,20 @@ window.__ModuleLoader__.load({
8190
8821
  "row.modelsTestEnabled": "Test enabled models",
8191
8822
  "row.modelsTestAll": "Test all models",
8192
8823
  "row.modelsTesting": "Testing {done}/{total}…",
8193
- "row.modelsTestHint": "Sends one minimal message per enabled model to see which ones actually answer. Costs a small amount of quota.",
8194
- "row.modelsTestAllHint": "The「test all」button probes every model in the catalog, enabled or not, so it spends noticeably more quota than the enabled-only run.",
8824
+ "row.modelsTestHint": "Send a minimal test request to each enabled model and show its result. Each request is billed at that model's rate.",
8825
+ "row.modelsTestAllHint": "Test every model in this panel's catalog, including disabled models. Each request is billed at that model's rate.",
8195
8826
  "row.showPointsInMainUi": "Show credits in the composer row",
8196
- "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.",
8827
+ "row.showPointsInMainUiHint": "Show a clickable Trae readout at the selected position, including when another provider's model is selected. Click it to view account details.",
8197
8828
  "row.drawImage": "Let the model draw images (trae_draw_image)",
8198
8829
  "row.drawImageHint": "Adds a trae_draw_image tool to this panel: the model can generate one image when you ask for one, at this account's real credit cost (measured ~8.8 for 1920x1920 on Trae CN). Off by default — while this is unticked the tool does not exist, so the model cannot spend credits on its own.",
8199
8830
  "row.drawImageHintEnterprise": "Not available on the enterprise panel: that path bills through the tenant gateway and has not been verified.",
8200
- "row.drawImageHintAi": "Not available on the international panel: image generation has only been measured on Trae CN, and this path bills on a different plan, so the credit figures shown here would be wrong. Switch to the CN panel to use it.",
8201
- "row.showPointsInMainUiHintAi": "A clickable readout in the composer tool row while a model from this plugin is selected: Trae Global · <plan>.",
8831
+ "row.drawImageHintAi": "Image generation and billing have not been verified for the global provider, so the tool is unavailable here. Use the domestic panel for image generation.",
8832
+ "row.showPointsInMainUiHintAi": "Show the global plan at the selected position, including when a model from another provider is selected.",
8202
8833
  "row.creditPositionLabel": "position",
8203
8834
  "row.creditPositionComposer": "composer toolbar",
8204
8835
  "row.creditPositionStats": "below the input box",
8205
8836
  "row.creditPositionAria": "Credit readout position for {region}",
8206
- "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.",
8837
+ "row.creditPositionHint": "The default position is on the model picker row. If another connector occupies the same position, the readout loaded later moves left automatically. Choose below the input box to show it beside the round, step and token statistics.",
8207
8838
  "composer.switchTo": "Switch to {account}",
8208
8839
  "composer.noAccounts": "No account in this region",
8209
8840
  "composer.panelTitle": "Trae credits",
@@ -8245,11 +8876,11 @@ window.__ModuleLoader__.load({
8245
8876
  "row.settingsDirty": "Unsaved changes",
8246
8877
  "row.modelsTestEmpty": "Enable at least one model first.",
8247
8878
  "row.contextAllMid": "Switch all to 500K",
8248
- "row.contextAllMidHint": "Sets the DSH context budget of every model in this region whose Max window is at least 500K to the 500K middle step, leaving the rest alone. Half-way between the default window and Max — for when 1M is more than you need. Still a draft — press Save to apply.",
8879
+ "row.contextAllMidHint": "Set the DSH context budget to 500K for models in this panel with a Max window of at least 500K. Other models keep their current values. Click Save to apply.",
8249
8880
  "row.contextAllMax": "Switch all to Max",
8250
- "row.contextAllMaxHint": "Sets the DSH context budget of every model in this region that has a Max window to its Max window at once. Still a draft — press Save to apply. Models without a Max window are left alone.",
8881
+ "row.contextAllMaxHint": "Set each model with a Max window to that maximum DSH context budget. Other models keep their current values. Click Save to apply.",
8251
8882
  "row.contextAllDefault": "Restore default context",
8252
- "row.contextAllDefaultHint": "Clears every context-budget override in this region, so all models go back to Trae's default window. Still a draft — press Save to apply.",
8883
+ "row.contextAllDefaultHint": "Clear this panel's custom context budgets and use the Trae catalog defaults. Click Save to apply.",
8253
8884
  "row.modelsTestError": "Test failed: {message}",
8254
8885
  "row.modelsTestDone": "{available} of {total} answered.",
8255
8886
  "row.probeAvailable": "works",
@@ -8284,21 +8915,21 @@ window.__ModuleLoader__.load({
8284
8915
  "row.modelCapabilityPending": "Image input follows Trae's own multimodal flag, plus anything you tick yourself.",
8285
8916
  "row.poolTitle": "Account pool",
8286
8917
  "row.poolEnabled": "Enable the account pool",
8287
- "row.poolEnabledHint": "The pool picks the account to bill for this panel's requests, ranking by credits and availability. Off means the manually bound account serves.",
8918
+ "row.poolEnabledHint": "When enabled, the plugin selects accounts automatically and tries another account after account rate limits, depleted credits or account failures. When disabled, select the account to use from the account list.",
8288
8919
  "row.poolCurrentHeader": "Billing now: {account}",
8289
8920
  "row.poolModeAuto": "pool picks",
8290
8921
  "row.poolModeManual": "you pick",
8291
8922
  "row.poolMember": "in pool",
8292
- "row.poolNotMember": "not in pool",
8293
- "row.poolBusyHint": "Working through accounts one at a time…",
8294
- "row.poolTestAll": "Test all accounts",
8923
+ "row.poolNotMember": "Not in pool",
8924
+ "row.poolBusyHint": "Running one account at a time…",
8925
+ "row.poolTestAll": "Test selected",
8295
8926
  "row.poolTesting": "Testing…",
8296
- "row.poolOffNotice": "The pool is off — the bound account is what serves. Turn the switch below on to let the pool decide.",
8297
- "row.poolNoCandidate": "No usable pool member can serve this panel right now.",
8927
+ "row.poolOffNotice": "The pool is off and requests use the bound account. Enable the switch above and save to let the pool select accounts.",
8928
+ "row.poolNoCandidate": "No account in this pool can be used right now.",
8298
8929
  "row.poolNoCandidateEmpty": "No pool members are selected yet.",
8299
- "row.poolNoCandidateEmptyHint": "Tick accounts in the membership list to bring them into the pool.",
8930
+ "row.poolNoCandidateEmptyHint": "Select accounts below and save the pool settings to add them to the pool.",
8300
8931
  "row.poolNoCandidateUnusable": "Every selected member is unavailable right now.",
8301
- "row.poolNoCandidateHint": "Traffic keeps landing on the saved selection until one recovers.",
8932
+ "row.poolNoCandidateHint": "Requests will try the saved account binding while the pool has no usable member. Member status: ",
8302
8933
  "row.poolServingOutside": "{account} is billing right now but is not in the pool — until a member becomes available, requests fall back to the saved selection.",
8303
8934
  "row.poolMembersTitle": "Accounts in this pool",
8304
8935
  "row.poolRescan": "Re-detect accounts",
@@ -8309,59 +8940,59 @@ window.__ModuleLoader__.load({
8309
8940
  "row.poolSettingsNote": "Preferences below take effect when you save; test results are written by the plugin and are not affected by that button.",
8310
8941
  "row.poolSelectedCount": "Selected {count} / {total}",
8311
8942
  "row.poolNoCopyTag": "no copy here",
8312
- "row.poolCopyFile": "Copies live in {path} — {count} right now. Switching accounts in Trae adds a copy; it never overwrites an existing one.",
8313
- "row.poolNoCopyTestHint": "This machine holds no sign-in for this account, so it cannot be tested. Sign in to Trae with it again to recreate a copy, or delete the row.",
8314
- "row.poolSelectAll": "All",
8315
- "row.poolSelectNone": "None",
8943
+ "row.poolCopyFile": "The plugin keeps {count} credential copies in {path}. Refreshing creates copies for newly detected logins and retains existing copies.",
8944
+ "row.poolNoCopyTestHint": "The plugin cannot read credentials for this account. Sign in again in Trae and detect accounts, or remove it from the pool.",
8945
+ "row.poolSelectAll": "Select all",
8946
+ "row.poolSelectNone": "Clear all",
8316
8947
  "row.poolColumnAccount": "Account",
8317
8948
  "row.poolColumnCredits": "Credits",
8318
8949
  "row.poolColumnProbe": "Status",
8319
8950
  "row.poolColumnCheckin": "Check-in",
8320
- "row.poolGeneralLabel": "General",
8321
- "row.poolWorkHint": "Work credits are not spendable by DSH — only General powers chat.",
8951
+ "row.poolGeneralLabel": "Credits",
8952
+ "row.poolWorkHint": "DSH chat uses the credits balance. Work credits cannot be used for these requests.",
8322
8953
  "row.poolCreditNone": "—",
8323
- "row.poolTestedAt": "tested {at}",
8324
- "row.poolNeverTested": "not tested yet",
8325
- "row.poolRetryAt": "retry {at}",
8954
+ "row.poolTestedAt": "Tested {at}",
8955
+ "row.poolNeverTested": "Untested",
8956
+ "row.poolRetryAt": "Retry after {at}",
8326
8957
  "row.poolRetryUnknown": "retry time unknown",
8327
- "row.poolTargetCurrentPreferred": "Current: your choice {model}",
8328
- "row.poolTargetCurrentFree": "Current: free model {model}",
8329
- "row.poolTargetStale": "Model {model} is no longer offered by this panel",
8958
+ "row.poolTargetCurrentPreferred": "Current: {model} (your choice).",
8959
+ "row.poolTargetCurrentFree": "Current: {model} (auto-picked free model).",
8960
+ "row.poolTargetStale": "The target model you saved is no longer offered: {model}",
8330
8961
  "row.poolTargetStaleOption": "{model} (no longer offered)",
8331
- "row.poolTargetStaleClear": "Back to automatic",
8962
+ "row.poolTargetStaleClear": "Switch back to automatic",
8332
8963
  "row.poolTargetNone": "No free model available for this panel",
8333
- "row.poolTargetNoneHint": "The pool can only pick a free model; none resolves right now, so tests are disabled.",
8334
- "row.poolActionNoteTestNone": "No free model resolves in this panel — pick a target model before testing.",
8335
- "row.poolActionNoteNoMembers": "Tick the accounts you want in the pool and save first.",
8336
- "row.poolTargetAutoOption": "Automatic (free model)",
8337
- "row.poolTargetLabel": "Target test model",
8338
- "row.poolTargetHint": "The model a probe test uses.",
8339
- "row.poolProbeSizeLabel": "Probe request size",
8340
- "row.poolProbeSizeHint": "How much text a test sends. A one-word probe only proves the account can answer a one-word request — if the upstream refuses long turns, this is where you find out. Bigger is closer to a real conversation, and costs more credit on every account in the batch.",
8964
+ "row.poolTargetNoneHint": "Automatic selection uses free models only. Refresh the catalog or select a test model manually. Paid models spend the tested account's credits.",
8965
+ "row.poolActionNoteTestNone": "No free model was found in this panel. Select a target model before testing accounts.",
8966
+ "row.poolActionNoteNoMembers": "Select accounts to test and save the pool settings.",
8967
+ "row.poolTargetAutoOption": "Auto-select a free model",
8968
+ "row.poolTargetLabel": "Account test model",
8969
+ "row.poolTargetHint": "Batch tests use this model. Auto selects this region's free model with the largest context window. Selecting a paid model spends credits from every account tested.",
8970
+ "row.poolProbeSizeLabel": "Test input size",
8971
+ "row.poolProbeSizeHint": "Choose an input length close to your conversations. A short test can succeed while a longer request encounters account limits. Longer tests cost more when the model charges for input.",
8341
8972
  "row.poolProbeSizeDefault": "Minimal (default)",
8342
8973
  "row.poolProbeSizeOption": "{wan}0k tokens",
8343
8974
  "row.poolSettingsTitle": "More settings",
8344
8975
  "row.poolMoreRulesTitle": "Account copies & saving rules",
8345
- "row.poolManualAccountLabel": "Manually billed account",
8976
+ "row.poolManualAccountLabel": "Account in use",
8346
8977
  "row.poolManualAccountNone": "No account in effect",
8347
- "row.poolDirty": "unsaved",
8978
+ "row.poolDirty": "● Unsaved changes",
8348
8979
  "row.poolSaved": "Save",
8349
8980
  "row.poolSaving": "Saving…",
8350
- "row.poolDiscard": "Discard",
8981
+ "row.poolDiscard": "Discard changes",
8351
8982
  "row.poolSaveFailed": "Save failed: {message}",
8352
8983
  "row.poolGhostMembers": "{count} saved member(s) no longer resolve to a local sign-in: {names}",
8353
- "row.poolUnsavedMembers": "Unsaved membership changes — Save below to apply.",
8984
+ "row.poolUnsavedMembers": "Selection changed. Save to apply.",
8354
8985
  "row.poolRowTest": "Test",
8355
8986
  "row.poolLogTitle": "Activity",
8356
8987
  "row.poolLogClear": "Clear",
8357
8988
  "row.poolLogEmpty": "No pool activity yet.",
8358
- "row.poolLogTestStart": "Test started for {count} account(s) against {model}.",
8359
- "row.poolLogTestRow": "{accountName}: {outcome}.",
8360
- "row.poolLogTestRowMalformed": "{accountName}: malformed result.",
8361
- "row.poolLogTestDone": "Test finished.",
8362
- "row.poolLogBatchFailed": "Pool action failed: {message}",
8363
- "row.poolLogTimeout": "The test did not answer in time — it was cancelled, so the button works again. Check the network and retry: {message}",
8364
- "row.poolSelectAria": "Include {accountName} in the pool",
8989
+ "row.poolLogTestStart": "Testing {count} account(s) against {model}",
8990
+ "row.poolLogTestRow": "{accountName}: {outcome}",
8991
+ "row.poolLogTestRowMalformed": "{accountName}: the host returned no result for this account",
8992
+ "row.poolLogTestDone": "Test finished; the pool was re-ranked",
8993
+ "row.poolLogBatchFailed": "Batch operation failed: {message}",
8994
+ "row.poolLogTimeout": "The account test timed out and was cancelled. Check the network before retrying. Error: {message}",
8995
+ "row.poolSelectAria": "Include {accountName} in this region's account pool",
8365
8996
  "row.poolCheckedIn": "Checked in",
8366
8997
  "row.poolNotCheckedIn": "Not checked in",
8367
8998
  "row.poolCheckinUnknown": "Unknown",
@@ -8377,16 +9008,16 @@ window.__ModuleLoader__.load({
8377
9008
  "row.poolProbeUnavailable": "unavailable",
8378
9009
  "row.poolProbeNotFound": "not served",
8379
9010
  "row.poolProbeFailed": "failed",
8380
- "row.poolProbeSourceUnknown": "reason unknown",
8381
- "row.poolProbeSourceLive": "from a live request",
8382
- "row.poolProbeSourceTest": "from a test",
9011
+ "row.poolProbeSourceUnknown": "source unknown",
9012
+ "row.poolProbeSourceLive": "hit by a live request",
9013
+ "row.poolProbeSourceTest": "tested",
8383
9014
  "row.poolProbeModel": "model {model}",
8384
- "row.poolProbeModelUnknown": "model unknown",
9015
+ "row.poolProbeModelUnknown": "model not recorded",
8385
9016
  "row.poolExcludedUnusable": "unusable",
8386
9017
  "row.poolTokenExpired": "sign-in expired",
8387
9018
  "row.poolExcludedRateLimited": "rate limited",
8388
9019
  "row.poolExcludedModelBusy": "model busy",
8389
- "row.poolExcludedOutOfCredit": "out of credit",
9020
+ "row.poolExcludedOutOfCredit": "Out of credits",
8390
9021
  "row.poolExcludedCredentialRejected": "sign-in rejected",
8391
9022
  "row.poolExcludedPolicyRejected": "policy rejected",
8392
9023
  "row.poolExcludedPlanGated": "needs a higher plan",
@@ -8397,18 +9028,18 @@ window.__ModuleLoader__.load({
8397
9028
  "row.poolAgoMin": "{n} min ago",
8398
9029
  "row.poolAgoHour": "{n} h ago",
8399
9030
  "row.poolAgoDay": "{n} d ago",
8400
- "row.poolErrOrigin": "the page is not the plugin's own page, so the request was refused",
8401
- "row.poolErrUnavailable": "the pool is unavailable right now",
9031
+ "row.poolErrOrigin": "Request origin validation failed; no pool action was executed",
9032
+ "row.poolErrUnavailable": "The account pool is not available in this build (or its batch route is missing). Restart the app; if it persists, the plugin needs reinstalling.",
8402
9033
  "row.poolErrMethod": "the pool does not accept that request method",
8403
9034
  "row.poolErrBadRequest": "the pool refused the request",
8404
- "row.poolErrNoMembers": "the pool has no members to act on",
8405
- "row.poolErrNoLiveMembers": "every member is unavailable — nothing can serve",
9035
+ "row.poolErrNoMembers": "No accounts are selected for this pool. Select accounts below and save.",
9036
+ "row.poolErrNoLiveMembers": "The plugin cannot read valid login credentials for the selected accounts. Sign in to Trae again, or remove these accounts from the pool and save.",
8406
9037
  "row.poolErrCheckinUnsupported": "this panel does not offer a daily check-in",
8407
9038
  "row.poolErrTargetStale": "the saved target model is no longer offered",
8408
- "row.poolErrNoFreeModel": "no free model is available for this panel",
9039
+ "row.poolErrNoFreeModel": "No free model is available in this region. Refresh the catalog or select a test model manually before retrying.",
8409
9040
  "row.poolErrConflict": "the pool refused the request",
8410
9041
  "row.poolErrGeneric": "pool request failed (HTTP {status})",
8411
- "row.poolSettingsHint": "The switch and the target model are preferences — save them. Test, check in and delete act at once.",
9042
+ "row.poolSettingsHint": "These are preferences: they take effect when you save (same as the model list). Test results are written automatically and are not affected by this button.",
8412
9043
  "row.poolLogSaved": "Pool settings saved.",
8413
9044
  "row.poolLogSaveFailed": "Could not save the pool settings.",
8414
9045
  "row.poolLogNoMembers": "Nothing to do — no pool members are selected.",
@@ -8425,8 +9056,8 @@ window.__ModuleLoader__.load({
8425
9056
  "row.poolRowDeleted": "Deleted the credential copy of {accountName}.",
8426
9057
  "row.poolDeleteStillSigned": "Deleted this plugin's copy of {accountName}, but it is still signed in to Trae, so the account itself is not gone. Sign out inside Trae to remove it from this machine.",
8427
9058
  "row.poolRowDeletedSignedOut": "Deleted {accountName} and signed it out of {apps}.",
8428
- "row.poolRowDeleteAppRunning": "Could not delete {accountName}: {apps} is running and would restore the login. Quit {apps} completely, then delete again.",
8429
- "row.poolDeleteAppRunning": "Nothing was changed: {apps} is running, and it would write the login back over the file on exit. Quit {apps} completely, then delete again.",
9059
+ "row.poolRowDeleteAppRunning": "{apps} is running and may overwrite login file changes. Quit those apps completely before deleting {accountName}.",
9060
+ "row.poolDeleteAppRunning": "No files were changed because {apps} is running and may overwrite login file changes. Quit those apps completely before deleting the account.",
8430
9061
  "row.poolDeleteNoCopy": "There was no plugin copy of {accountName} left to delete — it only exists as a Trae app login, and that sign-out did not take effect.",
8431
9062
  "row.poolRowDeleteStillSigned": "removed this copy, but {accountName} is still signed in to Trae",
8432
9063
  "row.poolRowDeleteNoCopy": "{accountName} had no plugin copy — only its Trae app login",
@@ -8435,67 +9066,85 @@ window.__ModuleLoader__.load({
8435
9066
  "nav.panel": "Trae",
8436
9067
  "row.pageBack": "Back to conversation",
8437
9068
  "row.sidebarPanelLabel": "Show Trae in the sidebar",
8438
- "row.sidebarPanelHint": "Adds a Trae row to the DSH sidebar that opens this card as a full page. Off by default: the row takes a global sidebar seat shared by every installed plugin, and the Plugins page already reaches the same content. Takes effect on the click — no Save needed — and switching it off while the page is open returns you to the conversation."
9069
+ "row.sidebarPanelHint": "Show the Trae settings entry in the sidebar, disabled by default. Changes take effect immediately; disabling it returns an open settings page to the conversation."
8439
9070
  };
8440
9071
  const zh = {
8441
- "row.title": "接入使用Trae积分与模型(dsh-connect-trae)",
8442
- "row.desc": "在 DSH 中使用 Trae 模型并随时查看剩余积分;国内版与国际版是两个独立供应商,各有自己的账号,可同时使用。",
9072
+ "row.tabCredentialHint": "国内版与企业版共用 Trae CN 登录凭据,账号绑定分别保存。",
9073
+ "row.tabFreeQuotaWarn": "免费模型也可能遇到上游请求限制或服务繁忙。",
9074
+ "row.poolFailoverHint": "Trae 也会在服务端故障时尝试换号;参数错误和模型权限不足会直接显示报错。",
9075
+ "row.iconStyleLabel": "图标样式",
9076
+ "row.iconStyleHint": "立即更新侧边栏和页面图标,当前浏览器分别保存每个插件的选择。",
9077
+ "row.iconStyleClaude": "Claude 风格",
9078
+ "row.iconStyleOriginal": "默认图标",
9079
+ "row.iconStyleSaveFailed": "浏览器无法保存图标选择,请检查网站存储权限。",
9080
+ "row.poolAutoCheckinDeviceHint": "Trae 限制每台设备的每日奖励,部分成员可能无法领取。",
9081
+ "row.checkinStatusError": "无法读取今日签到状态:{message}",
9082
+ "row.poolAutoCheckinDeviceDone": "今日:本机的每日奖励领取次数已用完。",
9083
+ "row.poolAutoCheckinRetry": "今日:{count} 个账号已签,失败的请求将自动重试。",
9084
+ "row.poolAutoCheckinProgress": "今日:{count} 个账号已签。",
9085
+ "row.poolAutoCheckinWaiting": "等待下一次后台检查。",
9086
+ "row.poolAutoCheckin": "每天自动签到",
9087
+ "row.poolAutoCheckinHint": "开启并保存后,插件在 DSH 运行期间每天为所选国内版个人账号领取签到奖励。重启后,插件先检查当天的签到进度,再处理尚未签到的账号。失败时自动重试。",
9088
+ "row.title": "Trae 积分与模型",
9089
+ "row.desc": "在 DSH 中使用 Trae 国内版、企业版和国际版模型。可管理账号、选择模型、查询积分和领取每日签到奖励。刷新模型目录时,插件同时更新积分倍率。",
8443
9090
  "row.expand": "展开",
8444
9091
  "row.collapse": "收起",
8445
9092
  "row.tabCn": "国内版",
8446
9093
  "row.tabEnterprise": "企业版",
8447
9094
  "row.tabAi": "国际版",
8448
- "row.tabHint": "国内版、企业版、国际版各有账号、用量与模型,可同时启用。企业版与国内版共用同一份 Trae CN 登录。",
8449
- "row.tabEnterpriseHint": "企业版用同一个 Trae CN 账号走企业租户池计费,而不是个人积分池。它不会回落:被拒时如实报错,绝不悄悄花掉个人额度。",
8450
- "row.tabSwitchHint": "关闭该供应商:其模型不再提供给 DSH;账号与设置保留,可随时恢复。",
8451
- "row.tabSwitchAria": "是否向 DSH 提供{region}供应商",
8452
- "row.tabOffNotice": "该供应商已关闭,模型不会提供给 DSH。账号与设置均已保留。",
8453
- "row.panelAria": "卡片分栏",
9095
+ "row.tabHint": "国内版、企业版和国际版可同时启用。切换上方版本页签,可分别查看和管理各版本的账号、积分和模型。",
9096
+ "row.tabEnterpriseHint": "企业版请求使用 Trae CN 账号的租户额度,失败时直接显示错误,不会改用个人积分重试。",
9097
+ "row.tabSwitchHint": "关闭此开关后,插件会从模型列表中隐藏该区域的模型,并保留账号和设置。再次打开开关即可恢复。",
9098
+ "row.tabSwitchAria": "启用{region}模型",
9099
+ "row.tabOffNotice": "该区域已关闭,账号和设置已保留。勾选上方开关可恢复模型。",
9100
+ "row.panelAria": "卡片分区",
8454
9101
  "row.panelAccounts": "账号",
9102
+ "row.panelCapabilities": "能力",
9103
+ "row.panelCapabilitiesHint": "插件可以替你做什么。这些开关点一下立即生效 —— 开关没有保存按钮。",
8455
9104
  "row.panelModels": "模型",
8456
9105
  "row.panelDiag": "诊断",
8457
- "row.diagRegion": "面板",
9106
+ "row.diagRegion": "区域",
8458
9107
  "row.diagProvider": "供应商 id",
8459
- "row.diagAccount": "服务账号",
8460
- "row.diagToken": "访问令牌到期",
9108
+ "row.diagAccount": "当前使用账号",
9109
+ "row.diagToken": "登录有效期至",
8461
9110
  "row.diagRefresh": "重新登录最后期限",
8462
9111
  "row.diagRefreshNone": "该凭据不携带",
8463
- "row.diagCatalog": "目录模型数",
8464
- "row.diagEnabled": "已启用模型数",
9112
+ "row.diagCatalog": "目录中的模型",
9113
+ "row.diagEnabled": "已启用的模型",
8465
9114
  "row.diagPool": "账号池",
8466
- "row.diagPoolOn": "已开启 · {count} 个账号",
8467
- "row.diagPoolOff": "未开启",
9115
+ "row.diagPoolOn": "开启 · {count} 个账号",
9116
+ "row.diagPoolOff": "关闭",
8468
9117
  "row.diagCheckin": "每日签到",
8469
9118
  "row.diagCheckinOn": "支持",
8470
9119
  "row.diagCheckinOff": "本面板不提供",
8471
9120
  "row.diagRaw": "Raw 会话通道",
8472
9121
  "row.diagRawOff": "未检测",
8473
- "row.diagFailures": "最近一次报错",
8474
- "row.diagNoFailures": "无",
9122
+ "row.diagFailures": "最近一次失败",
9123
+ "row.diagNoFailures": "没有报错。",
8475
9124
  "row.diagQuarantine": "损坏副本留存",
8476
9125
  "row.diagQuarantineNone": "无",
8477
- "row.diagHint": "以上全部只读,可整段原样贴进反馈。数值来自本面板最近一次刷新,不是概括。",
9126
+ "row.diagHint": "这里显示本面板最近一次刷新得到的状态,可用于问题反馈;分享前请隐去账号标识和本机路径中的个人信息。",
8478
9127
  "row.settingsWriteFailed": "设置未能保存:{message}",
8479
9128
  "row.signedOut": "未登录",
8480
- "row.signedOutHint": "在 Trae 桌面 App 里登录一次即可,插件会自动跟随当前登录的账号。",
9129
+ "row.signedOutHint": "请先登录 Trae 桌面端,再点击「重新检测账号」,将登录凭据保存为插件副本。",
8481
9130
  "row.unsupportedRegionTitle": "美国区(US)暂不支持",
8482
- "row.unsupportedRegionHint": "本机有一个 Trae 账号属于美国区(userRegion \"US\" / 主机 *.traeapi.us)。本版本尚未支持该区,因此不会使用这个账号,也不会把请求发往新加坡网关——重新登录不会有任何变化。请在该面板改用国内版或新加坡区账号(或使用 Trae 国内版客户端)。",
9131
+ "row.unsupportedRegionHint": "本机有一个 Trae 账号属于美国区(userRegion \"US\" / 主机 *.traeapi.us)。本版本尚未支持该区,因此不会使用这个账号,也不会把请求发往新加坡网关。重新登录不会改变账号所属区域。请在该面板改用国内版或新加坡区账号(或使用 Trae 国内版客户端)。",
8483
9132
  "row.diagUnsupportedRegion": "未支持的区域",
8484
9133
  "row.searchedTitle": "已检查的路径",
8485
9134
  "row.searchedHint": "未找到登录信息。以下是插件探测过的位置;如果你的 Trae 把登录信息存在别处,请把该路径反馈给我们。",
8486
9135
  "row.searchedMore": "展开其余 {count} 条",
8487
9136
  "row.quarantineTitle": "被隔离的账号副本",
8488
- "row.quarantineHint": "有一份账号副本文件读不出来,插件没有覆盖它,而是改名留存(字节完整)。它曾经保存的账号只可能在那份文件里;确认已取回后删掉它即可不再提示。",
9137
+ "row.quarantineHint": "插件无法读取该账号副本,已将文件改名保留。需要恢复账号时请检查该文件;确认不再需要后,可自行删除留存文件。",
8489
9138
  "row.quarantineAccounts": "含 {count} 个账号",
8490
9139
  "row.quarantineAccountsUnknown": "账号数无法读取",
8491
9140
  "row.quarantineBytes": "{bytes} 字节",
8492
9141
  "row.reasonMissing": "文件不存在",
8493
9142
  "row.reasonUnreadable": "无法读取",
8494
9143
  "row.reasonInvalid": "格式无法识别",
8495
- "row.sourceDesktop": "桌面版",
9144
+ "row.sourceDesktop": "桌面端",
8496
9145
  "row.sourceCli": "Trae CLI",
8497
9146
  "row.signedIn": "已登录:{accountName}",
8498
- "row.tokenExpiry": "访问令牌 {expiresAt} 过期(自动续期)",
9147
+ "row.tokenExpiry": "访问令牌将于 {expiresAt} 过期,插件会尝试自动续期",
8499
9148
  "row.refreshTokenExpiring": "请在 {expiresAt} 前重新登录一次 Trae(还剩 {days} 天),此后将无法自动续期。",
8500
9149
  "row.refreshTokenLapsed": "自动续期已停止:Trae 登录凭据已于 {expiresAt} 过期。请重新登录一次 Trae 以恢复。",
8501
9150
  "row.requestFailed": "请求失败",
@@ -8516,7 +9165,7 @@ window.__ModuleLoader__.load({
8516
9165
  "row.checkinClaimed": "已领取",
8517
9166
  "row.checkinRefused": "Trae 拒绝了本次签到(业务码 {code})",
8518
9167
  "row.checkinError": "签到失败:{message}",
8519
- "row.checkinDeviceSpent": "本机今日的签到已用掉——Trae 按「每台设备每天一次」限制,所以当前账号在本机今天无法再签。可换一台设备领取,或用今天已签到的那台设备/账号,或明天再来。",
9168
+ "row.checkinDeviceSpent": "Trae 每台设备每天只能签到一次。本机今日已签到,当前账号今天无法在本机领取。请换一台尚未签到的设备,或明天再试。",
8520
9169
  "row.checkinDisabled": "该账号当前没有可用的签到活动。",
8521
9170
  "row.checkinDeviceSpentShort": "本机已签",
8522
9171
  "row.subscriptionLabel": "订阅状态",
@@ -8531,26 +9180,26 @@ window.__ModuleLoader__.load({
8531
9180
  "row.refreshCredits": "刷新用量",
8532
9181
  "row.refreshing": "正在刷新…",
8533
9182
  "row.accountsTitle": "Trae 账号",
8534
- "row.accountsHint": "本插件保管的账号副本:本机每个 Trae 登录都会在刷新时补建一份副本。登录新号后要刷新一次,否则它会被 Trae 的下一次登录覆盖。Token 不会显示。",
8535
- "row.transferTitle": "导入 / 导出账号",
8536
- "row.transferExport": "导出所选",
8537
- "row.transferImport": "从文件导入",
9183
+ "row.accountsHint": "插件刷新账号时会为检测到的 Trae 登录保存凭据副本。切换到新账号后请刷新一次,以免下次登录覆盖尚未保存的凭据。",
9184
+ "row.transferTitle": "导出 / 导入账号",
9185
+ "row.transferExport": "导出账号",
9186
+ "row.transferImport": "导入账号",
8538
9187
  "row.transferCancel": "取消",
8539
9188
  "row.transferSelectAll": "全选",
8540
- "row.transferExportPick": "从这 {total} 个账号副本中选择要导出的。",
8541
- "row.transferExportConfirm": "导出 {count} 个",
8542
- "row.transferExported": "已导出 {count} 个账号副本到 {fileName}。",
8543
- "row.transferExporting": "正在准备导出…",
8544
- "row.transferImportPick": "该文件包含 {total} 条记录;勾选的会作为插件副本导入,不会写入 Trae 应用。",
8545
- "row.transferImportConfirm": "导入 {count} 个",
9189
+ "row.transferExportPick": "选择要导出的账号(共 {total} 个):",
9190
+ "row.transferExportConfirm": "导出 {count} 个账号",
9191
+ "row.transferExported": "已导出 {count} 个账号到 {fileName}。",
9192
+ "row.transferExporting": "正在导出…",
9193
+ "row.transferImportPick": "文件包含 {total} 条记录,请勾选要导入的账号。插件将保存为自己的凭据副本,不改动 Trae 应用登录。",
9194
+ "row.transferImportConfirm": "导入 {count} 个账号",
8546
9195
  "row.transferImported": "已导入 {imported} 个账号副本,跳过 {skipped} 个。",
8547
9196
  "row.transferImporting": "正在导入…",
8548
9197
  "row.transferReading": "正在读取文件…",
8549
- "row.transferHint": "导出的文件里是插件保管的账号副本(含有效 Token),请当作密码保管。属于另一区域的记录会被跳过。",
9198
+ "row.transferHint": "使用 JSON 文件导出账号,或将文件中的账号导入插件。文件包含登录凭据,请限制文件的访问权限。导入时会跳过其他区域的账号。",
8550
9199
  "row.transferError": "导入/导出失败:{message}",
8551
9200
  "row.transferVeilHint": "完成后账号列表会自动刷新。",
8552
9201
  "row.transferUnnamed": "未命名账号",
8553
- "row.transferNoToken": "无 Token,将被跳过",
9202
+ "row.transferNoToken": "缺少登录凭据",
8554
9203
  "row.accountsRescan": "重新检测账号",
8555
9204
  "row.accountsScanning": "正在检测…",
8556
9205
  "row.accountsLoading": "正在加载账号…",
@@ -8563,7 +9212,7 @@ window.__ModuleLoader__.load({
8563
9212
  "row.accountColumnCheckin": "签到",
8564
9213
  "row.accountBalance": "积分 {credits}",
8565
9214
  "row.balanceConsoleOnly": "见企业版控制台",
8566
- "row.enterpriseCreditsNotice": "企业版额度不在这里显示。Trae 客户端读不到企业租户的余额(所有企业额度接口都返回 404),只能到企业版控制台(console.enterprise.trae.cn)查看。这里也刻意不显示个人积分:那是消费池的数字,放在企业版标签下会被误认为企业额度。",
9215
+ "row.enterpriseCreditsNotice": "插件尚不能读取企业租户余额,请到企业版控制台 console.enterprise.trae.cn 查看。此处不显示个人积分,以免将其误认为企业额度。",
8567
9216
  "row.sourceDsh": "插件副本",
8568
9217
  "row.modelsTitle": "模型",
8569
9218
  "row.modelsSummary": "已启用 {count} 个",
@@ -8577,7 +9226,7 @@ window.__ModuleLoader__.load({
8577
9226
  "row.modelsViewLabel": "模型范围",
8578
9227
  "row.modelsViewCommon": "常用通道 · {count}",
8579
9228
  "row.modelsViewAll": "全部 · {count}",
8580
- "row.modelsViewCommonHint": "普通 SOLO 工作区对话能调到的模型(solo_work_remote / solo_work_lite)。",
9229
+ "row.modelsViewCommonHint": "显示可通过 solo_work_remote 或 solo_work_lite 调用的模型。",
8581
9230
  "row.modelsViewAllHint": "还包含只在专用通道提供的模型:solo_coder、chat_v3、solo_agent_remote。",
8582
9231
  "row.modelsViewShowAll": "查看全部",
8583
9232
  "row.modelsSearchOutsideView": "常用通道内没有匹配「{query}」的模型,全部目录里有 {count} 个。",
@@ -8587,24 +9236,24 @@ window.__ModuleLoader__.load({
8587
9236
  "row.modelsChannelAgentRemote": "SOLO 智能体 · 远程",
8588
9237
  "row.modelsChannelChatV3": "对话 · v3",
8589
9238
  "row.modelsChannelCoder": "代码 · solo_coder",
8590
- "row.modelsChannelDefault": "未标注模式(走 solo_work_lite)",
8591
- "row.modelsTestEnabled": "一键测试启用模型",
8592
- "row.modelsTestAll": "一键测试所有模型",
8593
- "row.modelsTesting": "正在测试 {done}/{total}…",
8594
- "row.modelsTestHint": "给每个已启用模型发一条最小消息,看哪些真的能应答。会消耗少量额度。",
8595
- "row.modelsTestAllHint": "「测试所有模型」会遍历目录里的每一个模型(含未启用的),消耗的额度明显多于「仅测试启用」。",
9239
+ "row.modelsChannelDefault": "未标注模式,使用 solo_work_lite",
9240
+ "row.modelsTestEnabled": "测试已启用模型",
9241
+ "row.modelsTestAll": "测试所有模型",
9242
+ "row.modelsTesting": "测试中 {done}/{total}…",
9243
+ "row.modelsTestHint": "向每个已启用模型发送最小测试请求,按模型费率消耗额度,并分别显示结果。",
9244
+ "row.modelsTestAllHint": "测试当前目录中的所有模型,包含未启用的模型;每次请求均按对应模型费率计费。",
8596
9245
  "row.showPointsInMainUi": "在输入框工具栏显示积分读数",
8597
- "row.showPointsInMainUiHint": "在输入框工具栏显示可点击读数(选中本插件模型时出现):国内版为「Trae CN · 余额」,国际版为「Trae 国际版 · 套餐」。",
8598
- "row.showPointsInMainUiHintAi": "在输入框工具栏显示可点击读数(选中本插件模型时出现):国际版为「Trae 国际版 · 套餐」。",
9246
+ "row.showPointsInMainUiHint": "开启后在所选位置显示可点击读数:国内版为「Trae - CN · 余额」,国际版为「Trae 国际版 · 套餐」。选择其他供应商模型时仍可查看。",
9247
+ "row.showPointsInMainUiHintAi": "开启后在所选位置显示国际版套餐,选择其他供应商模型时仍可查看。",
8599
9248
  "row.drawImage": "允许模型自主生图(trae_draw_image)",
8600
- "row.drawImageHint": "为本面板挂上 trae_draw_image 工具:你要求时模型能生成一张图,按本账号真实扣积分(国内版实测 1920x1920 约 8.8)。默认关闭——不勾选时工具根本不存在,模型无法自己花额度。",
8601
- "row.drawImageHintEnterprise": "企业面板不提供:那条路径走租户网关计费,未经验证。",
8602
- "row.drawImageHintAi": "国际版面板不提供:生图只在 Trae CN 实测过,国际版按另一套套餐计费,这里标注的积分价格会对不上。要用请切到国内版面板。",
9249
+ "row.drawImageHint": "开启后提供 trae_draw_image 工具,模型可按你的要求生成图片并消耗当前账号积分。国内版曾测得 1920x1920 图片约 8.8 积分,实际费用以上游扣费为准;默认关闭。",
9250
+ "row.drawImageHintEnterprise": "企业版的生图调用与计费尚未验证,因此暂不提供该功能。",
9251
+ "row.drawImageHintAi": "国际版的生图调用与计费尚未验证,因此暂不提供该功能;需要生图时请使用国内版面板。",
8603
9252
  "row.creditPositionLabel": "显示位置",
8604
9253
  "row.creditPositionComposer": "输入框工具栏",
8605
9254
  "row.creditPositionStats": "输入框下方",
8606
9255
  "row.creditPositionAria": "{region} 的积分显示位置",
8607
- "row.creditPositionHint": "积分显示在哪里。「输入框工具栏」是默认位置(与模型选择同一行)。如果装了别的插件后读数不显示,改成「输入框下方」—— 那是「轮次 / 步数 / token」统计所在的那一行。",
9256
+ "row.creditPositionHint": "默认与模型选择同一行显示;遇到其他连接器占用排序位置时,后加载的读数自动向左排列。也可选择「输入框下方」,与轮次、步数和 token 统计同一行显示。",
8608
9257
  "composer.switchTo": "切换到 {account}",
8609
9258
  "composer.noAccounts": "该区域暂无账号",
8610
9259
  "composer.panelTitle": "Trae 积分",
@@ -8646,11 +9295,11 @@ window.__ModuleLoader__.load({
8646
9295
  "row.settingsDirty": "有未保存的修改",
8647
9296
  "row.modelsTestEmpty": "请先启用至少一个模型。",
8648
9297
  "row.contextAllMid": "整区切到 500K",
8649
- "row.contextAllMidHint": "把本区所有 Max 档不小于 500K 的模型的 DSH 上下文预算一次性设为 500K 中间档,其余模型不动。这是默认窗口与 Max 之间的半步——觉得 1M 用不上、又嫌默认太小时用。这只是草稿——要按「保存」才生效。",
9298
+ "row.contextAllMidHint": "将本面板中 Max 窗口不小于 500K 的模型设为 500K 上下文预算,其余模型保持原值;点击「保存」后生效。",
8650
9299
  "row.contextAllMax": "整区切到 Max",
8651
- "row.contextAllMaxHint": "把本区所有带 Max 档的模型的 DSH 上下文预算一次性设为各自的 Max 档。这只是草稿——要按「保存」才生效。没有 Max 档的模型不会被改动。",
9300
+ "row.contextAllMaxHint": "将本面板中提供 Max 窗口的模型设为各自的最大上下文预算,其余模型保持原值;点击「保存」后生效。",
8652
9301
  "row.contextAllDefault": "全部恢复默认上下文",
8653
- "row.contextAllDefaultHint": "清掉本区所有上下文预算覆盖,让每个模型都回到 Trae 的默认窗口。这只是草稿——要按「保存」才生效。",
9302
+ "row.contextAllDefaultHint": "清除本面板的自定义上下文预算,恢复使用 Trae 目录默认值;点击「保存」后生效。",
8654
9303
  "row.modelsTestError": "测试失败:{message}",
8655
9304
  "row.modelsTestDone": "{total} 个里有 {available} 个正常应答。",
8656
9305
  "row.probeAvailable": "可用",
@@ -8672,7 +9321,7 @@ window.__ModuleLoader__.load({
8672
9321
  "row.veilSaving": "正在保存更改…",
8673
9322
  "row.modelContext": "最大上下文 {context}",
8674
9323
  "row.contextBudget": "DSH 上下文预算",
8675
- "row.contextBudgetHint": "只影响 DSH 侧的上下文预算。插件不会向上游发送窗口或 Max 模式字段,所以这不会改变 Trae 实际服务这次请求所用的窗口。",
9324
+ "row.contextBudgetHint": "设置 DSH 组织请求时使用的上下文预算。插件不向上游发送窗口或 Max 模式参数,因此不会据此切换 Trae 的服务窗口。",
8676
9325
  "row.modelOutput": "最大输出 {output}",
8677
9326
  "row.modelImage": "图片",
8678
9327
  "row.modelImageAuto": "已自动开启:Trae 把该模型标为多模态。",
@@ -8682,87 +9331,87 @@ window.__ModuleLoader__.load({
8682
9331
  "row.modelReasoningFollowUpstream": "默认",
8683
9332
  "row.modelReasoningFollowUpstreamNamed": "默认({effort})",
8684
9333
  "row.modelUnknown": "未知",
8685
- "row.modelCapabilityPending": "图片输入跟随 Trae 自身的多模态标注,并叠加你手动勾选的模型。",
9334
+ "row.modelCapabilityPending": "Trae 标注为多模态的模型会自动启用图片输入,也可手动为其他模型启用。",
8686
9335
  "row.poolTitle": "账号池",
8687
9336
  "row.poolEnabled": "启用账号池",
8688
- "row.poolEnabledHint": "账号池按积分与可用性挑选本面板请求的计费账号;关闭时由手动绑定的账号计费。",
9337
+ "row.poolEnabledHint": "开启后,插件自动选择账号;遇到账号限流、积分耗尽或账号故障时尝试换号。关闭后,请从账号列表中选择要使用的账号。",
8689
9338
  "row.poolCurrentHeader": "当前计费:{account}",
8690
- "row.poolModeAuto": "自动挑号",
8691
- "row.poolModeManual": "手动选号",
9339
+ "row.poolModeAuto": "自动选择",
9340
+ "row.poolModeManual": "手动选择",
8692
9341
  "row.poolMember": "已在池中",
8693
- "row.poolNotMember": "未入池",
8694
- "row.poolBusyHint": "逐个处理账号中…",
8695
- "row.poolTestAll": "一键测试所有账号",
8696
- "row.poolTesting": "测速中…",
8697
- "row.poolOffNotice": "账号池已关闭——由绑定的账号计费。开启上方开关即可让账号池派单。",
8698
- "row.poolNoCandidate": "当前没有可用的池成员能为本面板提供请求。",
8699
- "row.poolNoCandidateEmpty": "尚未勾选任何池成员。",
8700
- "row.poolNoCandidateEmptyHint": "在成员列表中勾选账号即可加入账号池。",
9342
+ "row.poolNotMember": "未加入",
9343
+ "row.poolBusyHint": "插件正在处理所选账号…",
9344
+ "row.poolTestAll": "测试勾选的账号",
9345
+ "row.poolTesting": "测试中…",
9346
+ "row.poolOffNotice": "账号池已关闭,当前请求由绑定账号计费。开启上方开关后,插件按账号池规则选择账号。",
9347
+ "row.poolNoCandidate": "账号池里目前没有可用的账号。",
9348
+ "row.poolNoCandidateEmpty": "账号池尚未选择成员。",
9349
+ "row.poolNoCandidateEmptyHint": "请在下方勾选账号并保存,插件才会将其加入账号池。",
8701
9350
  "row.poolNoCandidateUnusable": "所有已选成员当前均不可用。",
8702
- "row.poolNoCandidateHint": "请求会继续落在已保存的账号上,直到有成员恢复可用。",
8703
- "row.poolServingOutside": "{account} 当前在计费,但它不在池中——在池里有可用成员之前,请求会落回你保存的选择。",
8704
- "row.poolMembersTitle": "本池包含的账号",
9351
+ "row.poolNoCandidateHint": "账号池没有可用成员时,请求会尝试使用已保存的绑定账号。成员当前状态:",
9352
+ "row.poolServingOutside": "当前由未加入账号池的 {account} 计费,账号池恢复可用成员前会继续使用已保存的绑定账号。",
9353
+ "row.poolMembersTitle": "账号池成员",
8705
9354
  "row.poolRescan": "重新检测账号",
8706
9355
  "row.poolToolsLabel": "导出 / 导入账号",
8707
9356
  "row.poolCreditExpiry": "{when} 过期",
8708
9357
  "row.poolProbeMeasured": "测得 {when}",
8709
9358
  "row.poolProbeAt": "测试于 {when}",
8710
- "row.poolSettingsNote": "下面是偏好设置:点保存才生效;测试结果由插件自动写入,不受这个保存按钮影响。",
9359
+ "row.poolSettingsNote": "修改下方设置后请点击「保存」;测试结果由插件单独保存。",
8711
9360
  "row.poolSelectedCount": "已选 {count} / {total}",
8712
9361
  "row.poolNoCopyTag": "本机无副本",
8713
- "row.poolCopyFile": "副本就存在 {path},当前 {count} 份。在 Trae 里换号只会新增一份,不会覆盖已有的副本。",
8714
- "row.poolNoCopyTestHint": "本机没有这个账号的登录,无法测速。需要它请在 Trae 里重新登录以重建副本,或删除这一行。",
8715
- "row.poolSelectAll": "全部",
8716
- "row.poolSelectNone": "清空",
9362
+ "row.poolCopyFile": "当前共有 {count} 份账号副本,保存位置为 {path}。刷新账号时,插件为新登录创建副本,并保留已有副本。",
9363
+ "row.poolNoCopyTestHint": "插件无法读取该账号的登录凭据,请在 Trae 中重新登录后检测账号,或将其移出账号池。",
9364
+ "row.poolSelectAll": "全选",
9365
+ "row.poolSelectNone": "全不选",
8717
9366
  "row.poolColumnAccount": "账号",
8718
9367
  "row.poolColumnCredits": "积分",
8719
9368
  "row.poolColumnProbe": "状态",
8720
9369
  "row.poolColumnCheckin": "签到",
8721
- "row.poolGeneralLabel": "通用",
8722
- "row.poolWorkHint": "工作积分不能由 DSH 消费——只有通用积分用于对话。",
9370
+ "row.poolGeneralLabel": "积分",
9371
+ "row.poolWorkHint": "DSH 对话使用积分余额,不能使用工作积分。",
8723
9372
  "row.poolCreditNone": "—",
8724
- "row.poolTestedAt": "测于 {at}",
8725
- "row.poolNeverTested": "尚未测速",
8726
- "row.poolRetryAt": "{at} 后重试",
9373
+ "row.poolTestedAt": "测试于 {at}",
9374
+ "row.poolNeverTested": "未测试",
9375
+ "row.poolRetryAt": "{at} 后可重试",
8727
9376
  "row.poolRetryUnknown": "重试时间未知",
8728
- "row.poolTargetCurrentPreferred": "当前:你选定的模型 {model}",
8729
- "row.poolTargetCurrentFree": "当前:免费模型 {model}",
8730
- "row.poolTargetStale": "模型 {model} 已不再由本面板提供",
9377
+ "row.poolTargetCurrentPreferred": "测试模型:{model}(手动选择)",
9378
+ "row.poolTargetCurrentFree": "测试模型:{model}(自动选择)",
9379
+ "row.poolTargetStale": "你保存的目标模型已不在目录中:{model}",
8731
9380
  "row.poolTargetStaleOption": "{model}(已下架)",
8732
- "row.poolTargetStaleClear": "改回自动",
9381
+ "row.poolTargetStaleClear": "切回自动",
8733
9382
  "row.poolTargetNone": "本面板暂无可用免费模型",
8734
- "row.poolTargetNoneHint": "账号池只会挑免费模型;当前无法解析出可用模型,测速暂被禁用。",
8735
- "row.poolActionNoteTestNone": "本面板没有解析出免费模型——先指定一个目标模型再测速。",
8736
- "row.poolActionNoteNoMembers": "先勾选要入池的账号并保存。",
8737
- "row.poolTargetAutoOption": "自动(免费模型)",
8738
- "row.poolTargetLabel": "目标测速模型",
8739
- "row.poolTargetHint": "探针测速所用的模型。",
8740
- "row.poolProbeSizeLabel": "测试请求体积",
8741
- "row.poolProbeSizeHint": "测试时发送多少文本。只发一个词的探针,只能证明「这个账号能回答一个词」——如果上游拒绝长请求,问题只会在你真实对话时才出现。填得越多越接近真实对话,每个账号每次测试消耗的积分也越多。",
9383
+ "row.poolTargetNoneHint": "自动模式仅选择免费模型,请刷新目录后重试,或手动选择测试模型;选择收费模型时会消耗被测账号积分。",
9384
+ "row.poolActionNoteTestNone": "本面板未找到免费模型,请选择目标模型后再测试账号。",
9385
+ "row.poolActionNoteNoMembers": "请勾选要测试的账号,并保存账号池设置。",
9386
+ "row.poolTargetAutoOption": "自动选择免费模型",
9387
+ "row.poolTargetLabel": "账号测试模型",
9388
+ "row.poolTargetHint": "批量测试使用此处选择的模型。选择「自动」时,插件使用该区域上下文窗口最大的免费模型;选择收费模型时,测试会消耗各被测账号的积分。",
9389
+ "row.poolProbeSizeLabel": "测试输入大小",
9390
+ "row.poolProbeSizeHint": "建议选择接近会话请求的输入长度。短请求通过测试后,较长的请求也可能触发账号限制。输入越长,收费模型的测试费用越高。",
8742
9391
  "row.poolProbeSizeDefault": "最小请求(默认)",
8743
9392
  "row.poolProbeSizeOption": "{wan} 万 token",
8744
9393
  "row.poolSettingsTitle": "更多设置",
8745
9394
  "row.poolMoreRulesTitle": "账号副本与保存规则",
8746
- "row.poolManualAccountLabel": "手动计费账号",
8747
- "row.poolManualAccountNone": "当前无生效账号",
8748
- "row.poolDirty": "未保存",
9395
+ "row.poolManualAccountLabel": "当前账号",
9396
+ "row.poolManualAccountNone": "当前没有生效的账号",
9397
+ "row.poolDirty": "● 有未保存的修改",
8749
9398
  "row.poolSaved": "保存",
8750
9399
  "row.poolSaving": "保存中…",
8751
- "row.poolDiscard": "放弃",
9400
+ "row.poolDiscard": "放弃修改",
8752
9401
  "row.poolSaveFailed": "保存失败:{message}",
8753
- "row.poolGhostMembers": "{count} 个已保存成员不再对应本机登录:{names}",
8754
- "row.poolUnsavedMembers": "成员改动尚未保存——请点击下方保存。",
8755
- "row.poolRowTest": "测速",
9402
+ "row.poolGhostMembers": "无法读取 {count} 个已保存成员的登录凭据:{names}",
9403
+ "row.poolUnsavedMembers": "成员选择已修改,保存后生效",
9404
+ "row.poolRowTest": "测试",
8756
9405
  "row.poolLogTitle": "活动记录",
8757
9406
  "row.poolLogClear": "清空",
8758
9407
  "row.poolLogEmpty": "暂无账号池活动。",
8759
- "row.poolLogTestStart": "开始为 {count} 个账号测速({model})。",
8760
- "row.poolLogTestRow": "{accountName}:{outcome}。",
8761
- "row.poolLogTestRowMalformed": "{accountName}:结果异常。",
8762
- "row.poolLogTestDone": "测速完成。",
8763
- "row.poolLogBatchFailed": "账号池操作失败:{message}",
8764
- "row.poolLogTimeout": "测速超时,已自动取消(按钮已恢复可用)。请检查网络后重试:{message}",
8765
- "row.poolSelectAria": "把 {accountName} 加入账号池",
9408
+ "row.poolLogTestStart": "开始测试 {count} 个账号 × {model}",
9409
+ "row.poolLogTestRow": "{accountName}:{outcome}",
9410
+ "row.poolLogTestRowMalformed": "{accountName}:服务端未返回测试结果",
9411
+ "row.poolLogTestDone": "测试完成,已更新可用账号列表",
9412
+ "row.poolLogBatchFailed": "批量操作失败:{message}",
9413
+ "row.poolLogTimeout": "测试超时后已取消,可以重新操作。请检查网络后重试,错误信息:{message}",
9414
+ "row.poolSelectAria": "把 {accountName} 加入本区域的账号池",
8766
9415
  "row.poolCheckedIn": "已签到",
8767
9416
  "row.poolNotCheckedIn": "未签到",
8768
9417
  "row.poolCheckinUnknown": "未知",
@@ -8778,16 +9427,16 @@ window.__ModuleLoader__.load({
8778
9427
  "row.poolProbeUnavailable": "不可用",
8779
9428
  "row.poolProbeNotFound": "上游不提供",
8780
9429
  "row.poolProbeFailed": "失败",
8781
- "row.poolProbeSourceUnknown": "原因未知",
8782
- "row.poolProbeSourceLive": "来自实时请求",
8783
- "row.poolProbeSourceTest": "来自测速",
9430
+ "row.poolProbeSourceUnknown": "来源未知",
9431
+ "row.poolProbeSourceLive": "对话请求",
9432
+ "row.poolProbeSourceTest": "手动测试",
8784
9433
  "row.poolProbeModel": "模型 {model}",
8785
- "row.poolProbeModelUnknown": "模型未知",
9434
+ "row.poolProbeModelUnknown": "未记录模型",
8786
9435
  "row.poolExcludedUnusable": "不可用",
8787
9436
  "row.poolTokenExpired": "登录已失效",
8788
9437
  "row.poolExcludedRateLimited": "被限流",
8789
9438
  "row.poolExcludedModelBusy": "模型繁忙",
8790
- "row.poolExcludedOutOfCredit": "积分不足",
9439
+ "row.poolExcludedOutOfCredit": "积分耗尽",
8791
9440
  "row.poolExcludedCredentialRejected": "登录凭据被拒",
8792
9441
  "row.poolExcludedPolicyRejected": "被策略拒绝",
8793
9442
  "row.poolExcludedPlanGated": "需要更高套餐",
@@ -8798,45 +9447,45 @@ window.__ModuleLoader__.load({
8798
9447
  "row.poolAgoMin": "{n} 分钟前",
8799
9448
  "row.poolAgoHour": "{n} 小时前",
8800
9449
  "row.poolAgoDay": "{n} 天前",
8801
- "row.poolErrOrigin": "页面并非插件自身页面,请求因而不被接受",
8802
- "row.poolErrUnavailable": "账号池当前不可用",
9450
+ "row.poolErrOrigin": "请求来源未通过验证,账号池未执行操作",
9451
+ "row.poolErrUnavailable": "账号池接口不可用,请重启 DSH;问题持续时请重新安装插件。",
8803
9452
  "row.poolErrMethod": "账号池不接受该请求方法",
8804
9453
  "row.poolErrBadRequest": "账号池拒绝了该请求",
8805
- "row.poolErrNoMembers": "账号池没有任何成员可处理",
8806
- "row.poolErrNoLiveMembers": "所有成员均不可用——没有可派单的账号",
9454
+ "row.poolErrNoMembers": "请先在账号池中勾选账号并保存,再点击签到或测试按钮。",
9455
+ "row.poolErrNoLiveMembers": "插件无法读取所选账号的有效登录凭据。请重新登录这些账号,或将它们移出账号池后保存。",
8807
9456
  "row.poolErrCheckinUnsupported": "本面板不提供每日签到",
8808
9457
  "row.poolErrTargetStale": "已保存的目标模型已下架",
8809
- "row.poolErrNoFreeModel": "本面板没有可用的免费模型",
9458
+ "row.poolErrNoFreeModel": "当前区域没有可用的免费模型,请刷新目录,或手动选择测试模型后再试。",
8810
9459
  "row.poolErrConflict": "账号池拒绝了该请求",
8811
9460
  "row.poolErrGeneric": "账号池请求失败(HTTP {status})",
8812
- "row.poolSettingsHint": "开关与目标模型是偏好,改完要保存;行内的测速、签到与删除立即生效。",
9461
+ "row.poolSettingsHint": "修改账号池设置后,请点击保存。插件会单独保存测试结果。",
8813
9462
  "row.poolLogSaved": "账号池设置已保存。",
8814
9463
  "row.poolLogSaveFailed": "无法保存账号池设置。",
8815
- "row.poolLogNoMembers": "没有可处理的内容——尚未勾选任何池成员。",
9464
+ "row.poolLogNoMembers": "尚未勾选账号池成员,未执行操作。",
8816
9465
  "row.poolLogDiscarded": "已放弃未保存的账号池修改。",
8817
9466
  "row.poolLogGeneric": "账号池未返回可用的结果",
8818
9467
  "row.poolLogRowTesting": "正在测速 {accountName}…",
8819
9468
  "row.poolLogTargetCleared": "测速目标已改回自动。",
8820
9469
  "row.poolRowDelete": "删除",
8821
- "row.poolRowDeleteAria": "从本机删除 {accountName}:插件的副本连同它在 Trae 应用里的登录(会把它从 Trae 退出登录)",
9470
+ "row.poolRowDeleteAria": "删除 {accountName} 的插件副本,并将该账号从 Trae 应用中退出登录",
8822
9471
  "row.poolRowDeleting": "删除中…",
8823
9472
  "row.poolGhostDeleteAria": "从本机删除已保存但无法登录的账号 {accountName}",
8824
9473
  "row.poolRowDeleteError": "无法删除 {accountName}:{message}",
8825
9474
  "row.poolRowDeleteFailed": "删除未得到确认",
8826
9475
  "row.poolRowDeleted": "已删除 {accountName} 的凭据副本。",
8827
- "row.poolDeleteStillSigned": "已删除本插件对 {accountName} 的副本,但它仍登录在 Trae 里,所以账号本身还在。要彻底从本机移除,请在 Trae 内退出登录。",
9476
+ "row.poolDeleteStillSigned": "插件副本已删除,但 {accountName} 仍登录在 Trae 中;若要移除本机登录,请在 Trae 内退出该账号。",
8828
9477
  "row.poolRowDeletedSignedOut": "已删除 {accountName},并将它从 {apps} 中退出登录。",
8829
- "row.poolRowDeleteAppRunning": "无法删除 {accountName}:{apps} 正在运行,退出时会把登录写回去。请先完全退出 {apps},再重新删除。",
8830
- "row.poolDeleteAppRunning": "这次没有改动任何文件:{apps} 正在运行,退出时会把自己的状态回写到文件、覆盖这次修改。请先完全退出 {apps},再点「删除」。",
8831
- "row.poolDeleteNoCopy": "{accountName} 本来就没有插件副本可删——它只存在于 Trae 应用登录里,而这次退出登录没有生效。",
9478
+ "row.poolRowDeleteAppRunning": "{apps} 正在运行,可能覆盖登录文件修改;请完全退出这些应用后,再删除 {accountName}。",
9479
+ "row.poolDeleteAppRunning": "为避免 {apps} 覆盖登录文件修改,本次未改动任何文件;请完全退出这些应用后再删除。",
9480
+ "row.poolDeleteNoCopy": "{accountName} 仅保留在 Trae 的登录状态中,本次退出登录未生效,因此没有删除任何插件副本。",
8832
9481
  "row.poolRowDeleteStillSigned": "已删除副本,但 {accountName} 仍登录在 Trae 中",
8833
- "row.poolRowDeleteNoCopy": "{accountName} 没有插件副本——它只存在于 Trae 应用登录里",
8834
- "row.poolDeleteHint": "本插件会为本机每个 Trae 登录各创建一份自己的副本(刷新时自动创建,已有副本则不动),卡片只显示、也只使用这些副本。「删除」会同时删掉副本并把该账号从 Trae 应用中退出登录,改写的每个文件都留一份备份;只要该账号还登录在 Trae 里,就不会被重新收录。Trae 正在运行时会把自己的状态回写到文件,所以要先完全退出——卡片会点名是哪个应用。",
9482
+ "row.poolRowDeleteNoCopy": "{accountName} 没有插件副本,仅保留在 Trae 应用登录状态中",
9483
+ "row.poolDeleteHint": "刷新账号时,插件会为新检测到的 Trae 登录保存副本,并保留已有副本。删除账号会移除副本、将账号从 Trae 退出登录,并为修改过的文件保留备份;操作前需完全退出相关 Trae 应用。如果退出登录未生效,插件会阻止该账号在保持登录期间再次导入。",
8835
9484
  "row.cheer": "鼓励一下",
8836
9485
  "nav.panel": "Trae",
8837
9486
  "row.pageBack": "返回会话",
8838
9487
  "row.sidebarPanelLabel": "在侧边栏显示 Trae",
8839
- "row.sidebarPanelHint": "在 DSH 侧边栏加一个 Trae 行,点开就是这张卡片的整页版。默认关闭:这一行占的是所有插件共用的全局侧边栏座位,而「插件」页本来就能看到同样的内容。点一下即生效,不用保存;页面开着时关掉它,会直接回到会话。"
9488
+ "row.sidebarPanelHint": "在侧边栏显示 Trae 设置入口,默认关闭。开关即时生效;关闭时,已打开的设置页会返回对话。"
8840
9489
  };
8841
9490
  //#endregion
8842
9491
  //#region src/client/TraeModelLabel.tsx
@@ -9131,21 +9780,35 @@ window.__ModuleLoader__.load({
9131
9780
  * different priority to shadow it (lowest renders)". Priority 0 is the DEFAULT,
9132
9781
  * so it is the one value that is guaranteed to be contested.
9133
9782
  *
9134
- * WHY `-0.25` AND NOT 0. Measured on the live desktop host (0.2.1-rc.5) the
9135
- * cell is already shared:
9136
- *
9137
- * dsh-connect-qoder-client priority -1 (active — lowest renders)
9138
- * dsh-connect-workbuddy-client priority -0.5
9139
- * dsh-connect-trae-client priority 0 (this plugin)
9140
- *
9141
- * -1 and -0.5 are the two sibling connectors' seats and must NOT be reused —
9142
- * two plugins on one priority collide for exactly the reason 0 did. `-0.25`
9143
- * sits between them, legal, and collides with neither. The value only has to be
9144
- * DISTINCT from whatever else claims the cell; it deliberately does not read the
9145
- * slot ledger or negotiate with other plugins (this repository's §零 invariant,
9146
- * and the host's own shipped README reaches the same conclusion — its documented
9147
- * remedy for a same-priority conflict is a configuration decision, not a runtime
9148
- * handshake).
9783
+ * WHY `-102` AND NOT `-0.25`, WHICH THIS USED TO BE. The old value was picked
9784
+ * from a reading of the siblings that was WRONG — and the comment said so with
9785
+ * confidence, which is how a wrong reading survives a code review. Re-measured
9786
+ * from the three plugins' sources:
9787
+ *
9788
+ * dsh-connect-qoder-client priority -101 (active — lowest renders)
9789
+ * dsh-connect-workbuddy-client priority -100
9790
+ * dsh-connect-trae-client priority -0.25 ← never rendered
9791
+ *
9792
+ * The previous note claimed qoder `-1` and workbuddy `-0.5`, i.e. that `-0.25`
9793
+ * sat "between them". Both numbers were stale: the siblings had each moved DOWN
9794
+ * a hundred while this file still described the arrangement they had left. The
9795
+ * consequence was exactly the report that started this change — "the model
9796
+ * picker has no capsule" — because `-0.25` is the HIGHEST of the three, and a
9797
+ * `single` slot renders the LOWEST.
9798
+ *
9799
+ * `-102` is chosen to sit just under qoder, not at some far negative number
9800
+ * like `-1000`: this only needs the one seat, and a needlessly extreme value
9801
+ * would push any future plugin that picks a reasonable number behind us forever.
9802
+ * qoder made the same call when it took `-101` under workbuddy's `-100`.
9803
+ *
9804
+ * WHAT THIS COSTS. Winning a `single` slot means the losers do not render at
9805
+ * all here: qoder's own capsule disappears from the picker the moment this
9806
+ * registers. That is the documented trade-off of the slot, not a bug — and it
9807
+ * is why {@link TraeModelLabel} must reproduce the host's presentation (catalog
9808
+ * name plus the model's own badge) for anything that is not this plugin's
9809
+ * model, rather than drawing nothing for foreign rows. A picker whose every row
9810
+ * lost its name because a connector won the cell would be a much worse outcome
9811
+ * than a lost promotion chip.
9149
9812
  *
9150
9813
  * WHAT WINNING/HOLDING COSTS. This cell is `single`, so the other registrants'
9151
9814
  * decorations on the rows this plugin claims are not composed. That is the
@@ -9163,7 +9826,7 @@ window.__ModuleLoader__.load({
9163
9826
  * disposes its previous entry before every attempt. Priority alone would have
9164
9827
  * made the throw rarer and harder to read, not absent.
9165
9828
  */
9166
- const MODEL_LABEL_PRIORITY = -.25;
9829
+ const MODEL_LABEL_PRIORITY = -102;
9167
9830
  /** The tone colours, mapped onto the same theme tokens the host's Tag paints. */
9168
9831
  const HOST_BADGE_TONES = {
9169
9832
  neutral: {
@@ -9407,7 +10070,7 @@ window.__ModuleLoader__.load({
9407
10070
  * happens on every press and a shell that appears later is still found. See
9408
10071
  * `settings-route.ts` for the finding that established which seam this is.
9409
10072
  */
9410
- const onOpenSettings = () => openPluginSettingsPage(softGet);
10073
+ const onOpenSettings = () => openPluginSettingsPage(softGet, { label: () => t("nav.panel") });
9411
10074
  const forms = softGet("configForms");
9412
10075
  /**
9413
10076
  * The card's settings scope, bound to whichever namespace the Host actually
@@ -9636,13 +10299,9 @@ window.__ModuleLoader__.load({
9636
10299
  */
9637
10300
  function TraePanelIcon(props) {
9638
10301
  const edge = typeof props.size === "number" && props.size > 0 ? props.size : 16;
9639
- return (0, react.createElement)("img", {
10302
+ return (0, react.createElement)(PluginIcon, {
9640
10303
  className: "dsm-trae-panel-icon",
9641
- src: TRAE_PLUGIN_ICON,
9642
- alt: "",
9643
- width: edge,
9644
- height: edge,
9645
- "aria-hidden": "true"
10304
+ size: edge
9646
10305
  });
9647
10306
  }
9648
10307
  /**
@@ -9664,14 +10323,10 @@ window.__ModuleLoader__.load({
9664
10323
  */
9665
10324
  function SettingsSectionIcon(props) {
9666
10325
  const edge = typeof props.size === "number" && props.size > 0 ? props.size : 16;
9667
- return (0, react.createElement)("img", {
10326
+ return (0, react.createElement)(PluginIcon, {
9668
10327
  className: "dsm-trae-section-icon",
9669
- src: TRAE_PLUGIN_ICON,
9670
- alt: "",
9671
- width: edge,
9672
- height: edge,
9673
- style: { opacity: props.active === false ? .75 : 1 },
9674
- "aria-hidden": "true"
10328
+ size: edge,
10329
+ style: { opacity: props.active === false ? .75 : 1 }
9675
10330
  });
9676
10331
  }
9677
10332
  /**