dsh-connect-trae 3.7.0 → 3.9.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +261 -0
- package/README.en.md +17 -11
- package/README.md +26 -16
- package/lib/client.js +1992 -154
- package/lib/index.d.ts +1589 -716
- package/lib/index.js +2676 -307
- package/package.json +16 -15
package/lib/client.js
CHANGED
|
@@ -7,7 +7,24 @@ 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/region.ts
|
|
11
|
+
/** Every panel, in card tab order. */
|
|
12
|
+
const TRAE_PANELS = [
|
|
13
|
+
"cn",
|
|
14
|
+
"enterprise",
|
|
15
|
+
"ai"
|
|
16
|
+
];
|
|
17
|
+
/** The region whose credential/account pool a panel draws on. */
|
|
18
|
+
function regionOfPanel(panel) {
|
|
19
|
+
return panel === "ai" ? "ai" : "cn";
|
|
20
|
+
}
|
|
21
|
+
//#endregion
|
|
10
22
|
//#region src/status-paths.ts
|
|
23
|
+
/**
|
|
24
|
+
* Node-free constants and types shared by the Host and browser halves.
|
|
25
|
+
* Mirrors the `dsh-workbuddy-connect` status-route pattern so the plugin card
|
|
26
|
+
* stays consistent with that project's external presentation.
|
|
27
|
+
*/
|
|
11
28
|
/** Plugin-owned usage endpoint consumed by its browser half. */
|
|
12
29
|
const TRAE_USAGE_PATH = "/plugins/dsh-connect-trae/usage";
|
|
13
30
|
/** Plugin-owned live model refresh endpoint. */
|
|
@@ -49,6 +66,50 @@ window.__ModuleLoader__.load({
|
|
|
49
66
|
*/
|
|
50
67
|
const TRAE_CHECKIN_PATH = "/plugins/dsh-connect-trae/checkin";
|
|
51
68
|
/**
|
|
69
|
+
* Plugin-owned account-pool endpoint: one route for the pool's batch actions.
|
|
70
|
+
*
|
|
71
|
+
* 参考:`dsh-connect-workbuddy` 的 `WORKBUDDY_POOL_PATH`。本路由与该实现同形
|
|
72
|
+
* (同一 `?action=` 风格、POST、loopback-only、per-row `accountId`),这样同时
|
|
73
|
+
* 装了两个插件的用户面对的是同一套交互。
|
|
74
|
+
*
|
|
75
|
+
* `?action=checkin` and `?action=test` run over the whole member set; `remove`
|
|
76
|
+
* addresses ONE account (the body carries `accountId`) and is the only way a
|
|
77
|
+
* measurement of an account the user no longer wants can leave the store — a
|
|
78
|
+
* deleted member otherwise keeps its row in the file forever, and a re-added
|
|
79
|
+
* member would silently inherit an old exclusion.
|
|
80
|
+
*
|
|
81
|
+
* POST + loopback-only because both batches SPEND something: check-in sends a
|
|
82
|
+
* claim, and a test is a real chat call. A page that is not the Host's own
|
|
83
|
+
* loopback must not be able to trigger either.
|
|
84
|
+
*/
|
|
85
|
+
const TRAE_POOL_PATH = "/plugins/dsh-connect-trae/pool";
|
|
86
|
+
/** Query parameter selecting which pool batch action a request means. */
|
|
87
|
+
const TRAE_POOL_ACTION_PARAM = "action";
|
|
88
|
+
/** Query parameter naming ONE pool account a per-row request addresses. */
|
|
89
|
+
const TRAE_POOL_ACCOUNT_PARAM = "accountId";
|
|
90
|
+
/**
|
|
91
|
+
* Address one panel's pool route with its action.
|
|
92
|
+
*
|
|
93
|
+
* Kept as its own helper (rather than a `withTraeRegion` + hand-appended
|
|
94
|
+
* `&action=`) because a missing action parameter means "no action", not a
|
|
95
|
+
* default one — the typo has to be impossible rather than merely unlikely.
|
|
96
|
+
*/
|
|
97
|
+
function withTraePoolAction(panel, action) {
|
|
98
|
+
return `${TRAE_POOL_PATH}?${TRAE_REGION_PARAM}=${panel}&${TRAE_POOL_ACTION_PARAM}=${action}`;
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Address one panel's pool route for ONE account, e.g. a row's Test or Remove.
|
|
102
|
+
*
|
|
103
|
+
* Kept beside {@link withTraePoolAction} for the same reason that one exists: the
|
|
104
|
+
* account id is a 24-char hex string, and hand-appending `&accountId=` at a call
|
|
105
|
+
* site is one typo away from a request the route answers 400 to — or worse, from
|
|
106
|
+
* one that addresses the wrong action. Both parameters are therefore spelled by
|
|
107
|
+
* this module and nowhere else.
|
|
108
|
+
*/
|
|
109
|
+
function withTraePoolAccount(panel, action, accountId) {
|
|
110
|
+
return `${withTraePoolAction(panel, action)}&${TRAE_POOL_ACCOUNT_PARAM}=${encodeURIComponent(accountId)}`;
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
52
113
|
* Plugin-owned account transfer endpoint: batch export and import of a region's
|
|
53
114
|
* stored credentials (issue #37).
|
|
54
115
|
*
|
|
@@ -61,14 +122,12 @@ window.__ModuleLoader__.load({
|
|
|
61
122
|
const TRAE_TRANSFER_PATH = "/plugins/dsh-connect-trae/transfer";
|
|
62
123
|
/** Query parameter naming the region a card request addresses. */
|
|
63
124
|
const TRAE_REGION_PARAM = "region";
|
|
64
|
-
/** Every region, in card tab order. */
|
|
65
|
-
const TRAE_REGIONS = ["cn", "ai"];
|
|
66
125
|
/**
|
|
67
126
|
* Address one region's status route. The two regions are separate provider
|
|
68
127
|
* stacks; every card request carries the region whose tab the user is on.
|
|
69
128
|
*/
|
|
70
|
-
function withTraeRegion(path,
|
|
71
|
-
return `${path}?${TRAE_REGION_PARAM}=${
|
|
129
|
+
function withTraeRegion(path, panel) {
|
|
130
|
+
return `${path}?${TRAE_REGION_PARAM}=${panel}`;
|
|
72
131
|
}
|
|
73
132
|
/**
|
|
74
133
|
* Address one region's transfer route with its step, e.g.
|
|
@@ -79,8 +138,8 @@ window.__ModuleLoader__.load({
|
|
|
79
138
|
* away from a 400, which is exactly the failure the sibling workbuddy plugin
|
|
80
139
|
* hit (#42) when five requests dropped that parameter.
|
|
81
140
|
*/
|
|
82
|
-
function withTraeTransferAction(path,
|
|
83
|
-
return `${path}?${TRAE_REGION_PARAM}=${
|
|
141
|
+
function withTraeTransferAction(path, panel, action) {
|
|
142
|
+
return `${path}?${TRAE_REGION_PARAM}=${panel}&action=${action}`;
|
|
84
143
|
}
|
|
85
144
|
/**
|
|
86
145
|
* Peel ONE `{get(): T}` live reference, the shape DSH 0.1.7 delivers a
|
|
@@ -157,11 +216,11 @@ window.__ModuleLoader__.load({
|
|
|
157
216
|
* region's picks. Tolerates any stored shape (absent, non-object) by starting
|
|
158
217
|
* from an empty document.
|
|
159
218
|
*/
|
|
160
|
-
function nextRegionSlots(regions,
|
|
219
|
+
function nextRegionSlots(regions, panel, slot) {
|
|
161
220
|
const unwrapped = unwrapVolatileDeep(regions);
|
|
162
221
|
return {
|
|
163
222
|
...typeof unwrapped === "object" && unwrapped !== null && !Array.isArray(unwrapped) ? unwrapped : {},
|
|
164
|
-
[
|
|
223
|
+
[panel]: slot
|
|
165
224
|
};
|
|
166
225
|
}
|
|
167
226
|
/**
|
|
@@ -186,8 +245,8 @@ window.__ModuleLoader__.load({
|
|
|
186
245
|
return record;
|
|
187
246
|
}
|
|
188
247
|
/** One region's stored slot as a plain object; any other shape reads as empty. */
|
|
189
|
-
function regionSlotOf(value,
|
|
190
|
-
const slot = unwrapVolatile(regionsMapOf(value)[
|
|
248
|
+
function regionSlotOf(value, panel) {
|
|
249
|
+
const slot = unwrapVolatile(regionsMapOf(value)[panel]);
|
|
191
250
|
return typeof slot === "object" && slot !== null && !Array.isArray(slot) ? slot : {};
|
|
192
251
|
}
|
|
193
252
|
/**
|
|
@@ -200,8 +259,10 @@ window.__ModuleLoader__.load({
|
|
|
200
259
|
* `value` may be the whole settings section or the `regions` map (see
|
|
201
260
|
* {@link regionsMapOf}).
|
|
202
261
|
*/
|
|
203
|
-
function regionEnabledOf(value,
|
|
204
|
-
|
|
262
|
+
function regionEnabledOf(value, panel) {
|
|
263
|
+
const stored = regionSlotOf(value, panel)["enabled"];
|
|
264
|
+
if (typeof stored === "boolean") return stored;
|
|
265
|
+
return panel !== "enterprise";
|
|
205
266
|
}
|
|
206
267
|
/**
|
|
207
268
|
* Build the next `regions` settings value for a provider on/off toggle. ONLY
|
|
@@ -219,9 +280,9 @@ window.__ModuleLoader__.load({
|
|
|
219
280
|
* value is always the `regions` map, i.e. exactly what `settingsScope.set(
|
|
220
281
|
* 'regions', ...)` needs.
|
|
221
282
|
*/
|
|
222
|
-
function nextRegionEnabled(value,
|
|
223
|
-
return nextRegionSlots(regionsMapOf(value),
|
|
224
|
-
...regionSlotOf(value,
|
|
283
|
+
function nextRegionEnabled(value, panel, enabled) {
|
|
284
|
+
return nextRegionSlots(regionsMapOf(value), panel, {
|
|
285
|
+
...regionSlotOf(value, panel),
|
|
225
286
|
enabled
|
|
226
287
|
});
|
|
227
288
|
}
|
|
@@ -238,8 +299,8 @@ window.__ModuleLoader__.load({
|
|
|
238
299
|
* `value` may be the whole settings section or the `regions` map (see
|
|
239
300
|
* {@link regionsMapOf}).
|
|
240
301
|
*/
|
|
241
|
-
function regionCreditPositionOf(value,
|
|
242
|
-
return regionSlotOf(value,
|
|
302
|
+
function regionCreditPositionOf(value, panel) {
|
|
303
|
+
return regionSlotOf(value, panel)["creditPosition"] === "stats" ? "stats" : "composer";
|
|
243
304
|
}
|
|
244
305
|
/**
|
|
245
306
|
* Build the next `regions` settings value for one region's credit seat.
|
|
@@ -252,13 +313,55 @@ window.__ModuleLoader__.load({
|
|
|
252
313
|
* value is always the `regions` map, i.e. exactly what `settingsScope.set(
|
|
253
314
|
* 'regions', ...)` needs.
|
|
254
315
|
*/
|
|
255
|
-
function nextRegionCreditPosition(value,
|
|
256
|
-
return nextRegionSlots(regionsMapOf(value),
|
|
257
|
-
...regionSlotOf(value,
|
|
316
|
+
function nextRegionCreditPosition(value, panel, position) {
|
|
317
|
+
return nextRegionSlots(regionsMapOf(value), panel, {
|
|
318
|
+
...regionSlotOf(value, panel),
|
|
258
319
|
creditPosition: position
|
|
259
320
|
});
|
|
260
321
|
}
|
|
261
322
|
/**
|
|
323
|
+
* One panel's saved pool PREFERENCES, as the card's settings block edits them.
|
|
324
|
+
*
|
|
325
|
+
* Read from the settings document rather than from the usage answer for the same
|
|
326
|
+
* reason `regionCreditPositionOf` is: the card must paint its controls correctly
|
|
327
|
+
* on the FIRST frame, before any fetch lands, and a control that starts on a
|
|
328
|
+
* default and jumps when data arrives is the "looks like it did nothing" defect.
|
|
329
|
+
*
|
|
330
|
+
* Absent reads as `{}` — every field of {@link TraePoolPreferences} is optional
|
|
331
|
+
* by design, and a profile written by an older build has no `pool` slot at all.
|
|
332
|
+
* The caller applies the defaults it means (OFF, automatic model, no members);
|
|
333
|
+
* this function never invents them, because `targetModelId: ''` and
|
|
334
|
+
* `targetModelId: undefined` mean the same thing and only the CALLER may decide
|
|
335
|
+
* to collapse them.
|
|
336
|
+
*
|
|
337
|
+
* `value` may be the whole settings section or the `regions` map (see
|
|
338
|
+
* {@link regionsMapOf}).
|
|
339
|
+
*/
|
|
340
|
+
function regionPoolOf(value, panel) {
|
|
341
|
+
const unwrapped = unwrapVolatileDeep(regionSlotOf(value, panel)["pool"]);
|
|
342
|
+
return typeof unwrapped === "object" && unwrapped !== null && !Array.isArray(unwrapped) ? unwrapped : {};
|
|
343
|
+
}
|
|
344
|
+
/**
|
|
345
|
+
* Build the next `regions` settings value for one panel's pool preferences.
|
|
346
|
+
*
|
|
347
|
+
* Same merge rule as every sibling region write: only the addressed panel's
|
|
348
|
+
* `pool` slot changes, so saving the pool cannot drop that slot's model
|
|
349
|
+
* directory, selection, budgets, enabled flag or credit seat, and cannot touch
|
|
350
|
+
* the other panels. Panels are independent even where they share credentials
|
|
351
|
+
* (`cn` and `enterprise` bill the same Trae CN account): membership is a choice
|
|
352
|
+
* per panel, not per credential.
|
|
353
|
+
*
|
|
354
|
+
* `value` may be the whole settings section or the `regions` map; the RETURN
|
|
355
|
+
* value is always the `regions` map, i.e. exactly what `settingsScope.set(
|
|
356
|
+
* 'regions', ...)` needs.
|
|
357
|
+
*/
|
|
358
|
+
function nextRegionPool(value, panel, pool) {
|
|
359
|
+
return nextRegionSlots(regionsMapOf(value), panel, {
|
|
360
|
+
...regionSlotOf(value, panel),
|
|
361
|
+
pool
|
|
362
|
+
});
|
|
363
|
+
}
|
|
364
|
+
/**
|
|
262
365
|
* Whether Trae's plan identity says this account is on a paying plan.
|
|
263
366
|
*
|
|
264
367
|
* `has_package` alone is NOT that question — it reports whether a package
|
|
@@ -295,9 +398,9 @@ window.__ModuleLoader__.load({
|
|
|
295
398
|
.dsm-plugin-card-open{background:var(--dsw-alias-bg-layer-2,#25262b);border-color:var(--dsw-alias-label-dimmed,#777)}
|
|
296
399
|
.dsm-plugin-card-header{appearance:none;width:100%;font:inherit;color:inherit;text-align:left;cursor:pointer;background:transparent;border:0;border-radius:12px;align-items:center;gap:12px;padding:14px 16px;display:flex}
|
|
297
400
|
.dsm-plugin-card-header:focus-visible{outline:2px solid var(--dsw-alias-brand-primary,#5686fe);outline-offset:-2px}
|
|
298
|
-
.dsm-plugin-card-head{flex-direction:column;flex:1;gap:
|
|
401
|
+
.dsm-plugin-card-head{flex-direction:column;flex:1;gap:6px;min-width:0;display:flex}
|
|
299
402
|
.dsm-plugin-card-title{color:var(--dsw-alias-label-primary,#e6e6e6);font-size:15px;font-weight:600;line-height:1.4}
|
|
300
|
-
.dsm-plugin-card-description{color:var(--dsw-alias-label-tertiary,#999);font-size:
|
|
403
|
+
.dsm-plugin-card-description{color:var(--dsw-alias-label-tertiary,#999);font-size:12.5px;line-height:1.55}
|
|
301
404
|
/* Pure-CSS caret: the host primitives' chevron icon names are not part of any
|
|
302
405
|
stable contract across DSH releases, so no static import is used 鈥?a border
|
|
303
406
|
caret in the plugin's own CSS is version-proof. */
|
|
@@ -319,18 +422,26 @@ window.__ModuleLoader__.load({
|
|
|
319
422
|
.dsm-trae-mainui-switch-label input{margin:0;accent-color:var(--dsw-alias-brand-primary,#5686fe)}
|
|
320
423
|
.dsm-trae-mainui-switch-label:has(input:disabled){opacity:.6;cursor:default}
|
|
321
424
|
.dsm-trae-usage{display:flex;flex-direction:column;gap:16px;margin:0;padding:16px 0 4px}
|
|
322
|
-
.dsm-trae-tabs{display:
|
|
323
|
-
.dsm-trae-tab-cell{display:flex;align-items:center;gap:
|
|
324
|
-
.dsm-trae-tab
|
|
425
|
+
.dsm-trae-tabs{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:8px}
|
|
426
|
+
.dsm-trae-tab-cell{display:flex;align-items:center;gap:4px;min-width:0;padding:8px 12px;border:1px solid var(--dsw-alias-border-l2,#3a3d45);border-radius:10px;background:var(--dsw-alias-bg-layer-3,#2a2c33);transition:border-color .15s,background .15s}
|
|
427
|
+
.dsm-trae-tab-cell:hover{border-color:var(--dsw-alias-label-dimmed,#777)}
|
|
428
|
+
/* The selected tab is marked on its CHIP, not on the button inside it: an
|
|
429
|
+
inset ring on an inner button, inside a bordered strip, drew two borders
|
|
430
|
+
a few pixels apart and read as a rendering mistake. The chip carries the
|
|
431
|
+
accent border, so the switch beside the label stays in the same unit. */
|
|
432
|
+
.dsm-trae-tab-cell:has(.dsm-trae-tab-active){border-color:var(--dsw-alias-brand-primary,#5686fe);background:var(--dsw-alias-bg-layer-2,#232529)}
|
|
433
|
+
.dsm-trae-tab{appearance:none;font:inherit;cursor:pointer;flex:1;min-width:0;border:0;border-radius:7px;padding:0;color:var(--dsw-alias-label-tertiary,#999);font-size:13px;font-weight:600;line-height:20px;background:transparent;text-align:left;transition:color .15s;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
|
|
325
434
|
.dsm-trae-tab:hover:not(:disabled):not(.dsm-trae-tab-active){color:var(--dsw-alias-label-primary,#e6e6e6)}
|
|
326
435
|
.dsm-trae-tab:focus-visible{outline:2px solid var(--dsw-alias-brand-primary,#5686fe);outline-offset:1px}
|
|
327
|
-
.dsm-trae-tab-active{color:var(--dsw-alias-label-primary,#e6e6e6)
|
|
436
|
+
.dsm-trae-tab-active{color:var(--dsw-alias-label-primary,#e6e6e6)}
|
|
328
437
|
.dsm-trae-tab-off{opacity:.55}
|
|
329
438
|
/* Same checkbox as the settings block (.dsm-trae-switch); only the padding
|
|
330
439
|
differs, so it sits inside the tab cell without crowding the label. */
|
|
331
|
-
.dsm-trae-tab-switch{flex:none;padding:
|
|
332
|
-
.dsm-trae-tab-dot{display:inline-block;width:7px;height:7px;border-radius:50%;margin-right:
|
|
333
|
-
.dsm-trae-tab-hint{margin:0;color:var(--dsw-alias-label-tertiary,#999);font-size:12px;line-height:18px}
|
|
440
|
+
.dsm-trae-tab-switch{flex:none;margin-left:auto;padding-left:10px;border-left:1px solid var(--dsw-alias-border-l1,rgba(127,127,127,.24))}
|
|
441
|
+
.dsm-trae-tab-dot{display:inline-block;width:7px;height:7px;border-radius:50%;margin-right:7px;vertical-align:baseline}
|
|
442
|
+
.dsm-trae-tab-hint{margin:0;padding:0 2px;color:var(--dsw-alias-label-tertiary,#999);font-size:12px;line-height:18px}
|
|
443
|
+
.dsm-trae-credits-unavailable{margin:10px 0 0;padding:10px 12px;border:1px solid var(--dsw-alias-border-l2,#3a3d45);border-radius:10px;color:var(--dsw-alias-label-secondary,#c6c9d0);font-size:12px;line-height:18px;background:var(--dsw-alias-bg-layer-3,#2a2c33)}
|
|
444
|
+
.dsm-trae-account-value-muted{color:var(--dsw-alias-label-secondary,#c6c9d0);font-size:12px}
|
|
334
445
|
.dsm-trae-tab-off-notice{margin:0;padding:10px 12px;border:1px solid var(--dsw-alias-border-l2,#3a3d45);border-radius:10px;color:var(--dsw-alias-label-secondary,#c6c9d0);font-size:12px;line-height:18px;background:var(--dsw-alias-bg-layer-3,#2a2c33)}
|
|
335
446
|
.dsm-trae-usage-account{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:12px 14px;border:1px solid var(--dsw-alias-border-l2,#3a3d45);border-radius:14px;background:var(--dsw-alias-bg-layer-2,#24262c)}
|
|
336
447
|
.dsm-trae-usage-account-copy{display:flex;flex-direction:column;gap:3px;min-width:0}
|
|
@@ -354,13 +465,27 @@ window.__ModuleLoader__.load({
|
|
|
354
465
|
* 銆屼繚瀛樸€?銆屾斁寮冦€?footer. That is gone with the draft it existed to serve 鈥?this
|
|
355
466
|
* is the sibling dsh-connect-workbuddy card's shape, where a preference is a
|
|
356
467
|
* checkbox on a line that commits on the click. */
|
|
357
|
-
.dsm-trae-prefs{display:flex;flex-direction:column;gap:
|
|
358
|
-
.dsm-trae-pref{display:flex;align-items:flex-start;gap:
|
|
359
|
-
.dsm-trae-pref input{flex:none;
|
|
468
|
+
.dsm-trae-prefs{display:flex;flex-direction:column;gap:14px;padding:2px 2px 0}
|
|
469
|
+
.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}
|
|
470
|
+
.dsm-trae-pref input{flex:none;cursor:pointer;accent-color:var(--dsw-alias-brand-primary,#5686fe)}
|
|
471
|
+
.dsm-trae-pref-check{inline-size:15px;block-size:15px;margin:3px 0 0}
|
|
472
|
+
/* The label and its explanation used to run together as one wrapped blob
|
|
473
|
+
("跟随 Trae 当前登录的账号 在 Trae 桌面端切换登录后…"), which is what made
|
|
474
|
+
this block read as a wall of text. They are two lines now: what the switch
|
|
475
|
+
does, then what it means. */
|
|
476
|
+
.dsm-trae-pref-copy{display:flex;flex-direction:column;gap:3px;min-width:0}
|
|
477
|
+
.dsm-trae-pref-label{font-weight:500;color:var(--dsw-alias-label-primary,#e6e6e6)}
|
|
478
|
+
/* The select sits NEXT TO its label ("显示位置 [输入框工具栏 ▾]"), not at the far
|
|
479
|
+
right of the card. Pushing it to the opposite edge split one sentence into
|
|
480
|
+
two objects with half a card of empty space between them, and left the reader
|
|
481
|
+
to guess which label the control belonged to. The row packs from the left;
|
|
482
|
+
nothing in it stretches. */
|
|
483
|
+
.dsm-trae-pref-lead{display:flex;align-items:center;justify-content:flex-start;gap:10px;min-width:0}
|
|
484
|
+
.dsm-trae-pref-lead .dsm-trae-pref-label{flex:none}
|
|
360
485
|
.dsm-trae-pref input:disabled{cursor:default}
|
|
361
486
|
.dsm-trae-pref:has(input:disabled){opacity:.6;cursor:default}
|
|
362
487
|
.dsm-trae-pref b{font-weight:500;color:var(--dsw-alias-label-primary,#e6e6e6)}
|
|
363
|
-
.dsm-trae-pref-hint{color:var(--dsw-alias-label-tertiary,#9aa0a8)}
|
|
488
|
+
.dsm-trae-pref-hint{color:var(--dsw-alias-label-tertiary,#9aa0a8);font-size:12px;line-height:17px}
|
|
364
489
|
.dsm-trae-pref span{min-width:0}
|
|
365
490
|
/* The credit readout's seat picker (learned from workbuddy #62). Its OWN label
|
|
366
491
|
class, deliberately not the plain .dsm-trae-pref the two checkboxes use: a
|
|
@@ -369,8 +494,13 @@ window.__ModuleLoader__.load({
|
|
|
369
494
|
would otherwise start asserting about a <select>.
|
|
370
495
|
The select keeps its own class for the same reason, and takes the checkbox
|
|
371
496
|
column's width so it reads as a sibling of the box rather than a stray row. */
|
|
372
|
-
|
|
373
|
-
|
|
497
|
+
/* The select row has no checkbox, so it borrows the checkbox's column as
|
|
498
|
+
padding — its label must start on the same left edge as the two labels
|
|
499
|
+
above it (15px box + 10px gap). */
|
|
500
|
+
.dsm-trae-credit-position-pref{align-items:flex-start;padding-left:25px}
|
|
501
|
+
/* Sized by its own two options, not stretched: the control answers one short
|
|
502
|
+
question and sits beside the words that ask it. */
|
|
503
|
+
.dsm-trae-credit-position{flex:none;max-width:none;margin-top:0;padding:3px 8px;font-size:12px;line-height:16px;border:1px solid var(--dsw-alias-border-l2,#3a3d45);border-radius:7px;background:var(--dsw-alias-bg-layer-3,#2a2c33);color:var(--dsw-alias-label-primary,#e6e6e6);cursor:pointer}
|
|
374
504
|
.dsm-trae-credit-position:disabled{cursor:default}
|
|
375
505
|
/* The card's checkboxes are the platform's own, styled only with
|
|
376
506
|
accent-color 鈥?the SAME idiom .dsm-trae-mainui-switch-label above already
|
|
@@ -387,6 +517,16 @@ window.__ModuleLoader__.load({
|
|
|
387
517
|
.dsm-trae-switch input{margin:0;cursor:pointer;accent-color:var(--dsw-alias-brand-primary,#5686fe)}
|
|
388
518
|
.dsm-trae-switch input:disabled{cursor:default}
|
|
389
519
|
.dsm-trae-switch:has(input:disabled){opacity:.6;cursor:default}
|
|
520
|
+
/* A pill, only where a pill is the idiom: the standalone preference rows.
|
|
521
|
+
appearance:none hands the geometry to the stylesheet, so the track and the
|
|
522
|
+
knob are ours. It is scoped rather than global because the tab row's switch
|
|
523
|
+
sits inside a chip — a pill there reads as a second button, which is exactly
|
|
524
|
+
how it looked when this was global. */
|
|
525
|
+
.dsm-trae-switch-pill{display:inline-flex;align-items:center}
|
|
526
|
+
.dsm-trae-switch-pill input{appearance:none;-webkit-appearance:none;position:relative;inline-size:36px;block-size:20px;border:0;border-radius:999px;background:var(--dsw-alias-border-l2,#c4c7cc);transition:background-color .15s ease}
|
|
527
|
+
.dsm-trae-switch-pill input::after{content:"";position:absolute;inset-block-start:2px;inset-inline-start:2px;inline-size:16px;block-size:16px;border-radius:50%;background:var(--dsw-alias-bg-layer-3,#fff);box-shadow:0 1px 2px rgba(0,0,0,.24);transition:transform .15s ease}
|
|
528
|
+
.dsm-trae-switch-pill input:checked{background:var(--dsw-alias-brand-primary,#1b1c1e)}
|
|
529
|
+
.dsm-trae-switch-pill input:checked::after{transform:translateX(16px)}
|
|
390
530
|
/* The account block is a real table: the question it answers is "which of these
|
|
391
531
|
accounts, and what is the action on each one" 鈥?a dropdown can only ever show
|
|
392
532
|
the one already chosen. */
|
|
@@ -407,7 +547,7 @@ window.__ModuleLoader__.load({
|
|
|
407
547
|
content. That is the whole point 鈥?the previous four-column table let each
|
|
408
548
|
column grow with its own text, so the columns did not line up from row to row
|
|
409
549
|
and the table read as noise in a narrow panel. */
|
|
410
|
-
.dsm-trae-account-table{display:flex;flex-direction:column;border:1px solid var(--dsw-alias-border-l2,#3a3d45);border-radius:
|
|
550
|
+
.dsm-trae-account-table{display:flex;flex-direction:column;border:1px solid var(--dsw-alias-border-l2,#3a3d45);border-radius:var(--dsm-trae-pool-radius);overflow:hidden;background:var(--dsw-alias-bg-layer-2,#24262c)}
|
|
411
551
|
/* Four tracks where the region has a check-in column, three where it does not
|
|
412
552
|
(the international region has no check-in at all: the ug/* family answers
|
|
413
553
|
404 on every ai gateway, so the column is absent rather than empty and
|
|
@@ -622,6 +762,160 @@ window.__ModuleLoader__.load({
|
|
|
622
762
|
@keyframes dsm-trae-veil-spin{to{transform:rotate(360deg)}}
|
|
623
763
|
@keyframes dsm-trae-veil-dash{0%{stroke-dasharray:12 150;stroke-dashoffset:0}50%{stroke-dasharray:24 150;stroke-dashoffset:-6}to{stroke-dasharray:12 150;stroke-dashoffset:0}}
|
|
624
764
|
@media (prefers-reduced-motion: reduce){.dsm-trae-veil-spinner{animation:none}.dsm-trae-veil-spinner-arc{animation:none;stroke-dasharray:18 150;stroke-dashoffset:-3}}
|
|
765
|
+
|
|
766
|
+
/* ------------------------------------------------------------------ *
|
|
767
|
+
* The account pool (3.9.0). *
|
|
768
|
+
* *
|
|
769
|
+
* Five tracks, because the table answers "which accounts, and what is *
|
|
770
|
+
* each one worth right now" — a single picker could only ever show the *
|
|
771
|
+
* one already chosen, and every field here is a reason to include or *
|
|
772
|
+
* exclude a row. The check-in track is CONDITIONAL (only the CN panel *
|
|
773
|
+
* grants a daily reward), so the actions cell is pinned to the LAST *
|
|
774
|
+
* track (grid-column:-2/-1) rather than to the fifth one: otherwise *
|
|
775
|
+
* a four-cell row would drop Test/Remove into the check-in slot and *
|
|
776
|
+
* leave a hole on the right. *
|
|
777
|
+
* ------------------------------------------------------------------ */
|
|
778
|
+
.dsm-trae-pool{--dsm-trae-pool-pad-x:14px;--dsm-trae-pool-pad-y:10px;--dsm-trae-pool-radius:14px;display:flex;flex-direction:column;gap:12px;border-top:1px solid var(--dsw-alias-border-l2,#36373b);padding:14px 0 0}
|
|
779
|
+
.dsm-trae-pool-head{display:flex;align-items:flex-start;justify-content:space-between;gap:12px}
|
|
780
|
+
.dsm-trae-pool-title{margin:0;font-size:13px;font-weight:600;line-height:18px;color:var(--dsw-alias-label-primary,#e6e6e6)}
|
|
781
|
+
.dsm-trae-pool-region-note{margin:3px 0 0;font-size:12px;line-height:18px;color:var(--dsw-alias-label-tertiary,#9aa0a8)}
|
|
782
|
+
.dsm-trae-pool-current{flex:none;padding:2px 8px;border-radius:999px;border:1px solid var(--dsw-alias-border-l2,#3a3d45);font-size:11px;line-height:16px;color:var(--dsw-alias-label-secondary,#c6c9d0);white-space:nowrap}
|
|
783
|
+
.dsm-trae-pool-actions{display:flex;align-items:center;flex-wrap:wrap;gap:8px}
|
|
784
|
+
.dsm-trae-pool-action-checkin{flex:none;white-space:nowrap}
|
|
785
|
+
.dsm-trae-pool-action-test{flex:none;white-space:nowrap}
|
|
786
|
+
/* Removing an account from the pool is destructive (it drops THIS panel's saved
|
|
787
|
+
membership), so the hover state takes the error tone rather than the neutral
|
|
788
|
+
one every other outline button gets. */
|
|
789
|
+
.dsm-trae-pool-remove{color:var(--dsw-alias-label-secondary,#c6c9d0)}
|
|
790
|
+
.dsm-trae-pool-remove:hover:not(:disabled){color:var(--dsw-alias-state-error-primary,#d92d20);border-color:var(--dsw-alias-state-error-primary,#d92d20)}
|
|
791
|
+
.dsm-trae-pool-busy{font-size:12px;line-height:18px;color:var(--dsw-alias-label-tertiary,#9aa0a8)}
|
|
792
|
+
.dsm-trae-pool-card{display:flex;flex-direction:column;min-width:0;border:1px solid var(--dsw-alias-border-l2,#3a3d45);border-radius:var(--dsm-trae-pool-radius);background:var(--dsw-alias-bg-layer-2,#24262c);overflow:hidden}
|
|
793
|
+
.dsm-trae-pool-card-head{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:14px var(--dsm-trae-pool-pad-x) 0}
|
|
794
|
+
.dsm-trae-pool-card-title{margin:0;font-size:13px;line-height:18px;font-weight:600;color:var(--dsw-alias-label-primary,#e6e6e6)}
|
|
795
|
+
.dsm-trae-pool-card-tools{display:flex;align-items:center;gap:8px;flex:none}
|
|
796
|
+
.dsm-trae-pool-count{font-size:12px;line-height:18px;color:var(--dsw-alias-label-tertiary,#9aa0a8)}
|
|
797
|
+
.dsm-trae-pool-card-note{margin:0;padding:6px var(--dsm-trae-pool-pad-x) 12px;font-size:12px;line-height:18px;color:var(--dsw-alias-label-tertiary,#9aa0a8)}
|
|
798
|
+
.dsm-trae-pool-card .dsm-trae-pool-table{border:0;border-radius:0;border-top:1px solid var(--dsw-alias-border-l1,rgba(127,127,127,.18))}
|
|
799
|
+
.dsm-trae-pool-members{display:flex;flex-direction:column;gap:10px}
|
|
800
|
+
/* Prose, not a box. Every explanation in this section is one line of muted
|
|
801
|
+
text; boxing each one is what made the block look like a stack of cards. */
|
|
802
|
+
.dsm-trae-pool-actions-end{margin-inline-start:auto}
|
|
803
|
+
.dsm-trae-pool-credit-meta{display:block;margin-top:2px;font-size:11px;line-height:16px;white-space:nowrap;color:var(--dsw-alias-label-tertiary,#9aa0a8)}
|
|
804
|
+
.dsm-trae-pool-account-note{grid-column:2;grid-row:3;font-size:11px;line-height:16px;color:var(--dsw-alias-label-tertiary,#9aa0a8)}
|
|
805
|
+
.dsm-trae-pool-tools-dashed{border:1px dashed var(--dsw-alias-border-l2,#3a3d45);border-radius:var(--dsm-trae-pool-radius);padding:var(--dsm-trae-pool-pad-y) var(--dsm-trae-pool-pad-x)}
|
|
806
|
+
.dsm-trae-pool-tool-label{margin-inline-end:auto;font-size:12px;line-height:18px;color:var(--dsw-alias-label-secondary,#c6c9d0)}
|
|
807
|
+
.dsm-trae-pool-note{margin:0;font-size:12px;line-height:18px;color:var(--dsw-alias-label-tertiary,#9aa0a8)}
|
|
808
|
+
/* A setting row: label + hint on the left, the control on the right, one
|
|
809
|
+
rhythm for the switch, the target select and the manual binding. */
|
|
810
|
+
.dsm-trae-pool-set{display:flex;align-items:center;justify-content:space-between;gap:16px;padding:var(--dsm-trae-pool-pad-y) var(--dsm-trae-pool-pad-x);border-top:1px solid var(--dsw-alias-border-l1,rgba(127,127,127,.18))}
|
|
811
|
+
.dsm-trae-pool-set:first-child{border-top:0}
|
|
812
|
+
.dsm-trae-pool-set-copy{display:flex;flex-direction:column;gap:3px;min-width:0}
|
|
813
|
+
.dsm-trae-pool-set-copy b{font-size:13px;line-height:18px;font-weight:500;color:var(--dsw-alias-label-primary,#e6e6e6)}
|
|
814
|
+
.dsm-trae-pool-set-copy span{font-size:12px;line-height:18px;color:var(--dsw-alias-label-tertiary,#9aa0a8)}
|
|
815
|
+
.dsm-trae-pool-set-ctl{display:flex;align-items:center;gap:8px;flex:none}
|
|
816
|
+
.dsm-trae-pool-set-ctl select{min-width:180px}
|
|
817
|
+
.dsm-trae-pool-bind{border:1px solid var(--dsw-alias-border-l2,#3a3d45);border-radius:var(--dsm-trae-pool-radius);background:var(--dsw-alias-bg-layer-2,#24262c)}
|
|
818
|
+
.dsm-trae-pool-account-select{min-width:180px}
|
|
819
|
+
.dsm-trae-pool-tools{display:flex;flex-direction:column;gap:8px}
|
|
820
|
+
.dsm-trae-pool-tool-row{display:flex;align-items:center;gap:8px;flex-wrap:wrap}
|
|
821
|
+
.dsm-trae-pool-meta{font-size:11px;line-height:16px;color:var(--dsw-alias-label-tertiary,#9aa0a8);white-space:nowrap}
|
|
822
|
+
.dsm-trae-pool-warn-tag{padding:0 6px;border-radius:999px;font-size:11px;line-height:16px;white-space:nowrap}
|
|
823
|
+
.dsm-trae-pool-warn-tag-soon{color:var(--dsw-alias-state-warn-primary,#b45309)}
|
|
824
|
+
.dsm-trae-pool-warn-tag-lapsed{color:var(--dsw-alias-state-error-primary,#d92d20)}
|
|
825
|
+
.dsm-trae-pool-claim{padding:2px 8px;font-size:12px;line-height:16px}
|
|
826
|
+
.dsm-trae-pool-settings-wrap{display:flex;flex-direction:column;gap:10px}
|
|
827
|
+
|
|
828
|
+
.dsm-trae-pool-settings{display:flex;flex-direction:column;gap:0;padding:0;border:1px solid var(--dsw-alias-border-l2,#3a3d45);border-radius:var(--dsm-trae-pool-radius);overflow:hidden;background:var(--dsw-alias-bg-layer-2,#24262c)}
|
|
829
|
+
.dsm-trae-pool-members-head{display:flex;align-items:flex-start;justify-content:space-between;gap:12px}
|
|
830
|
+
.dsm-trae-pool-settings-head{display:flex;align-items:flex-start;justify-content:space-between;gap:12px}
|
|
831
|
+
.dsm-trae-pool-subtitle{margin:0;font-size:12px;font-weight:600;line-height:18px;color:var(--dsw-alias-label-primary,#e6e6e6)}
|
|
832
|
+
.dsm-trae-pool-subnote{margin:2px 0 0;font-size:12px;line-height:18px;color:var(--dsw-alias-label-tertiary,#9aa0a8)}
|
|
833
|
+
.dsm-trae-pool-select-actions{display:flex;flex:none;gap:6px}
|
|
834
|
+
.dsm-trae-pool-select-actions .dsm-btn,.dsm-trae-pool-row-actions .dsm-btn,.dsm-trae-pool-save-row .dsm-btn{padding:4px 10px;font-size:12px;line-height:1.4}
|
|
835
|
+
.dsm-trae-pool-table{--dsm-trae-pool-cols:minmax(0,1.4fr) 128px minmax(0,1.2fr) 96px 128px;display:flex;flex-direction:column;border:1px solid var(--dsw-alias-border-l2,#3a3d45);border-radius:var(--dsm-trae-pool-radius);overflow:hidden;background:var(--dsw-alias-bg-layer-2,#24262c)}
|
|
836
|
+
.dsm-trae-pool-table-no-checkin{--dsm-trae-pool-cols:minmax(0,1.4fr) 128px minmax(0,1.2fr) 128px}
|
|
837
|
+
.dsm-trae-pool-row{display:grid;grid-template-columns:var(--dsm-trae-pool-cols);align-items:start;gap:10px;padding:var(--dsm-trae-pool-pad-y) var(--dsm-trae-pool-pad-x);border-top:1px solid var(--dsw-alias-border-l1,rgba(127,127,127,.18))}
|
|
838
|
+
.dsm-trae-pool-row:first-child{border-top:0}
|
|
839
|
+
/* The header carries labels for the first three tracks only: the check-in and
|
|
840
|
+
action cells are self-describing ("Not checked in", "Test"), and a header word
|
|
841
|
+
for an empty cell would be a promise the table does not keep. */
|
|
842
|
+
.dsm-trae-pool-col-account{padding-inline-start:23px}
|
|
843
|
+
.dsm-trae-pool-row-head{align-items:center;padding:8px var(--dsm-trae-pool-pad-x);border-top:0;background:var(--dsw-alias-bg-layer-3,#2a2c33);font-size:11px;line-height:16px;font-weight:600;letter-spacing:.02em;text-transform:uppercase;color:var(--dsw-alias-label-tertiary,#9aa0a8)}
|
|
844
|
+
/* The row that would bill the next request. Tinted, not bordered: a border here
|
|
845
|
+
reads as a selection control, and the checkbox beside it already is one. */
|
|
846
|
+
.dsm-trae-pool-row-current{background:rgba(86,134,254,.07);box-shadow:inset 3px 0 0 var(--dsw-alias-brand-primary,#5686fe)}
|
|
847
|
+
.dsm-trae-pool-row-ghost{opacity:.7}
|
|
848
|
+
.dsm-trae-pool-row-actions{display:flex;flex-wrap:wrap;gap:6px;justify-content:flex-end}
|
|
849
|
+
.dsm-trae-pool-account{display:grid;grid-template-columns:auto minmax(0,1fr);align-items:center;gap:2px 8px;min-width:0}
|
|
850
|
+
.dsm-trae-pool-check{display:inline-flex;align-items:center;flex:none;grid-column:1;grid-row:1;cursor:pointer}
|
|
851
|
+
.dsm-trae-pool-check input{flex:none;margin:0;cursor:pointer;accent-color:var(--dsw-alias-brand-primary,#5686fe)}
|
|
852
|
+
.dsm-trae-pool-check:has(input:disabled){opacity:.6;cursor:default}
|
|
853
|
+
.dsm-trae-pool-account-name{display:flex;align-items:center;gap:6px;min-width:0;grid-column:2;grid-row:1;font-size:13px;line-height:18px;color:var(--dsw-alias-label-primary,#e6e6e6);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
|
854
|
+
.dsm-trae-pool-dot{flex:none;width:7px;height:7px;border-radius:50%;background:var(--dsw-alias-label-dimmed,#777)}
|
|
855
|
+
.dsm-trae-pool-dot-ok{background:var(--dsw-alias-state-success-primary,#12b76a)}
|
|
856
|
+
.dsm-trae-pool-dot-excluded{background:var(--dsw-alias-state-warn-primary,#b45309)}
|
|
857
|
+
.dsm-trae-pool-dot-ghost{background:var(--dsw-alias-label-dimmed,#777)}
|
|
858
|
+
.dsm-trae-pool-account-tags{display:flex;flex-wrap:wrap;align-items:center;gap:6px;grid-column:2;grid-row:2;min-width:0}
|
|
859
|
+
.dsm-trae-pool-tag{padding:0 6px;border-radius:999px;font-size:10px;line-height:16px;white-space:nowrap}
|
|
860
|
+
.dsm-trae-pool-member{font-size:11px;line-height:16px;color:var(--dsw-alias-brand-primary,#5686fe)}
|
|
861
|
+
.dsm-trae-pool-not-member{font-size:11px;line-height:16px;color:var(--dsw-alias-label-dimmed,#9aa0a6)}
|
|
862
|
+
.dsm-trae-pool-tag-current{padding:1px 6px;border-radius:999px;background:rgba(86,134,254,.16);color:var(--dsw-alias-brand-primary,#5686fe);font-size:11px;font-weight:600;line-height:16px}
|
|
863
|
+
.dsm-trae-pool-tag-member{border:1px solid var(--dsw-alias-border-l2,#3a3d45);color:var(--dsw-alias-label-tertiary,#9aa0a8)}
|
|
864
|
+
.dsm-trae-pool-tag-excluded{background:var(--dsw-alias-state-warn-tertiary,#fef3c7);color:var(--dsw-alias-state-warn-primary,#b45309)}
|
|
865
|
+
.dsm-trae-pool-tag-ghost{border:1px dashed var(--dsw-alias-border-l2,#3a3d45);color:var(--dsw-alias-label-tertiary,#9aa0a8)}
|
|
866
|
+
.dsm-trae-pool-credit{display:flex;flex-direction:column;gap:1px;min-width:0}
|
|
867
|
+
.dsm-trae-pool-credit-num{font-size:13px;line-height:18px;color:var(--dsw-alias-label-primary,#e6e6e6);font-variant-numeric:tabular-nums}
|
|
868
|
+
.dsm-trae-pool-credit-label{font-size:10px;line-height:14px;color:var(--dsw-alias-label-tertiary,#9aa0a8)}
|
|
869
|
+
.dsm-trae-pool-probe{min-width:0}
|
|
870
|
+
.dsm-trae-pool-probe-cell{display:flex;flex-direction:column;gap:1px;min-width:0}
|
|
871
|
+
.dsm-trae-pool-probe-outcome{display:flex;align-items:center;gap:5px;font-size:12px;line-height:16px;color:var(--dsw-alias-label-secondary,#c6c9d0)}
|
|
872
|
+
.dsm-trae-pool-probe-ok{color:var(--dsw-alias-state-success-primary,#12b76a)}
|
|
873
|
+
.dsm-trae-pool-probe-warn{color:var(--dsw-alias-state-warn-primary,#b45309)}
|
|
874
|
+
.dsm-trae-pool-probe-meta{font-size:11px;line-height:16px;color:var(--dsw-alias-label-tertiary,#9aa0a8);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
|
875
|
+
.dsm-trae-pool-probe-detail{font-size:11px;line-height:15px;color:var(--dsw-alias-label-tertiary,#9aa0a8);overflow-wrap:anywhere}
|
|
876
|
+
.dsm-trae-pool-probe-none{font-size:11px;line-height:16px;color:var(--dsw-alias-label-tertiary,#9aa0a8)}
|
|
877
|
+
.dsm-trae-pool-checkin{min-width:0}
|
|
878
|
+
.dsm-trae-pool-checkin-state{font-size:11px;line-height:16px;color:var(--dsw-alias-label-tertiary,#9aa0a8)}
|
|
879
|
+
.dsm-trae-pool-checkin-ok{color:var(--dsw-alias-state-success-primary,#12b76a)}
|
|
880
|
+
.dsm-trae-pool-checkin-no{color:var(--dsw-alias-state-warn-primary,#b45309)}
|
|
881
|
+
/* "Unknown" and "device already claimed" are facts, not faults: a muted line
|
|
882
|
+
says so without dressing either one up as an error. */
|
|
883
|
+
.dsm-trae-pool-checkin-unknown{color:var(--dsw-alias-label-tertiary,#9aa0a8)}
|
|
884
|
+
.dsm-trae-pool-checkin-device{color:var(--dsw-alias-label-tertiary,#9aa0a8)}
|
|
885
|
+
/* The advisory lines share one shape (a quiet bordered box): each is a fact the
|
|
886
|
+
user may need to act on, none of them is an error, and five different
|
|
887
|
+
rectangles in one section would read as five different severities. */
|
|
888
|
+
.dsm-trae-pool-ghost-note{margin:0;padding:8px var(--dsm-trae-pool-pad-x);border:1px solid var(--dsw-alias-border-l2,#3a3d45);border-radius:10px;font-size:12px;line-height:18px;color:var(--dsw-alias-label-secondary,#c6c9d0);background:var(--dsw-alias-bg-layer-3,#2a2c33)}
|
|
889
|
+
.dsm-trae-pool-unsaved{margin:0;padding:8px var(--dsm-trae-pool-pad-x);border:1px solid var(--dsw-alias-border-l2,#3a3d45);border-radius:10px;font-size:12px;line-height:18px;color:var(--dsw-alias-label-secondary,#c6c9d0);background:var(--dsw-alias-bg-layer-3,#2a2c33)}
|
|
890
|
+
.dsm-trae-pool-off-notice{margin:0;padding:0 var(--dsm-trae-pool-pad-x) 10px;font-size:12px;line-height:18px;color:var(--dsw-alias-state-warn-primary,#b45309)}
|
|
891
|
+
.dsm-trae-pool-target-none{margin:0;padding:0 12px 10px;font-size:12px;line-height:18px;color:var(--dsw-alias-state-warn-primary,#b45309)}
|
|
892
|
+
.dsm-trae-pool-target-stale{display:flex;align-items:center;justify-content:space-between;gap:10px;margin:0 var(--dsm-trae-pool-pad-x);padding-bottom:10px;color:var(--dsw-alias-state-warn-primary,#b45309)}
|
|
893
|
+
.dsm-trae-pool-warning{display:flex;flex-direction:column;gap:2px;padding:8px var(--dsm-trae-pool-pad-x);border:1px solid var(--dsw-alias-state-warn-secondary,#fbbf24);border-radius:10px;background:var(--dsw-alias-state-warn-tertiary,#fef3c7);color:var(--dsw-alias-state-warn-primary,#b45309);font-size:12px;line-height:18px}
|
|
894
|
+
.dsm-trae-pool-warning-title{font-weight:600}
|
|
895
|
+
.dsm-trae-pool-warning-hint{opacity:.85}
|
|
896
|
+
/* "Empty" is an unfinished setup, not a fault: neutral tone. */
|
|
897
|
+
.dsm-trae-pool-warning-empty{border-color:var(--dsw-alias-border-l2,#3a3d45);background:var(--dsw-alias-bg-layer-3,#2a2c33);color:var(--dsw-alias-label-secondary,#c6c9d0)}
|
|
898
|
+
/* "Every member is unusable" is the one case that IS the pool's fault, so it
|
|
899
|
+
keeps the warn tone the base class sets. */
|
|
900
|
+
.dsm-trae-pool-warning-unusable{border-color:var(--dsw-alias-state-warn-secondary,#fbbf24);background:var(--dsw-alias-state-warn-tertiary,#fef3c7);color:var(--dsw-alias-state-warn-primary,#b45309)}
|
|
901
|
+
.dsm-trae-pool-save-row{display:flex;align-items:center;gap:8px}
|
|
902
|
+
.dsm-trae-pool-star{margin-inline-end:auto;font-size:12px;line-height:18px;color:var(--dsw-alias-label-secondary,#c6c9d0);text-decoration:none}
|
|
903
|
+
.dsm-trae-pool-dirty{padding:0 6px;border-radius:999px;background:var(--dsw-alias-state-warn-tertiary,#fef3c7);color:var(--dsw-alias-state-warn-primary,#b45309);font-size:10px;line-height:16px}
|
|
904
|
+
.dsm-trae-pool-save-error{margin:0;font-size:12px;line-height:18px;color:var(--dsw-alias-state-error-primary,#d92d20)}
|
|
905
|
+
.dsm-trae-pool-target{display:flex;flex-direction:column;gap:6px;font-size:12px;line-height:18px;color:var(--dsw-alias-label-secondary,#c6c9d0)}
|
|
906
|
+
.dsm-trae-pool-target-label{color:var(--dsw-alias-label-secondary,#c6c9d0)}
|
|
907
|
+
.dsm-trae-select{max-width:220px;padding:2px 6px;font:inherit;font-size:12px;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:8px}
|
|
908
|
+
.dsm-trae-select:disabled{opacity:.6;cursor:default}
|
|
909
|
+
.dsm-trae-pool-log{display:flex;flex-direction:column;gap:6px;padding-top:2px}
|
|
910
|
+
.dsm-trae-pool-log-head{display:flex;align-items:center;justify-content:space-between;gap:10px}
|
|
911
|
+
.dsm-trae-pool-log-empty{margin:0;font-size:12px;line-height:18px;color:var(--dsw-alias-label-tertiary,#9aa0a8)}
|
|
912
|
+
.dsm-trae-pool-log-list{display:flex;flex-direction:column;gap:3px;margin:0;padding:0;list-style:none;max-height:160px;overflow-y:auto}
|
|
913
|
+
.dsm-trae-pool-log-item{padding:0 0 0 8px;border-left:2px solid var(--dsw-alias-border-l2,#3a3d45);font-size:12px;line-height:18px;color:var(--dsw-alias-label-secondary,#c6c9d0)}
|
|
914
|
+
.dsm-trae-pool-log-info{border-left-color:var(--dsw-alias-border-l2,#3a3d45)}
|
|
915
|
+
.dsm-trae-pool-log-ok{border-left-color:var(--dsw-alias-state-success-primary,#12b76a)}
|
|
916
|
+
.dsm-trae-pool-log-warn{border-left-color:var(--dsw-alias-state-warn-primary,#b45309)}
|
|
917
|
+
.dsm-trae-pool-log-error{border-left-color:var(--dsw-alias-state-error-primary,#d92d20)}
|
|
918
|
+
.dsm-trae-pool-log-text{overflow-wrap:anywhere}
|
|
625
919
|
`;
|
|
626
920
|
/**
|
|
627
921
|
* Composer-row readout styles, injected by the readout itself.
|
|
@@ -841,6 +1135,1122 @@ window.__ModuleLoader__.load({
|
|
|
841
1135
|
});
|
|
842
1136
|
}
|
|
843
1137
|
//#endregion
|
|
1138
|
+
//#region src/client/AccountPool.tsx
|
|
1139
|
+
/**
|
|
1140
|
+
* The account-pool card UI for Trae, ported from the WorkBuddy sibling.
|
|
1141
|
+
*
|
|
1142
|
+
* Mounted by {@link TraeUsageCard} inside the signed-in branch, below the
|
|
1143
|
+
* account table, rendered only when the usage document carries `pool` (the Host
|
|
1144
|
+
* omits it when the panel is unconfigured).
|
|
1145
|
+
*
|
|
1146
|
+
* This half of the feature is deliberately dumb about rules: every verdict an
|
|
1147
|
+
* account needs — credits, exclusion, the in-pool flag, the serving row — comes
|
|
1148
|
+
* pre-derived by the Host (`TraeWebPoolAccount`), so the card never re-ranks on
|
|
1149
|
+
* its own idea of "which account should bill". It drafts the saved preferences
|
|
1150
|
+
* (`TraePoolPreferences`) and writes them back through the card's verified
|
|
1151
|
+
* regions-write helper.
|
|
1152
|
+
*/
|
|
1153
|
+
/**
|
|
1154
|
+
* A finite number or the fallback.
|
|
1155
|
+
*
|
|
1156
|
+
* The usage document is served by a Host that may be OLDER than this bundle (the
|
|
1157
|
+
* card ships in the client bundle and updates on refresh, the Host only on
|
|
1158
|
+
* restart), so a field the Host does not send yet must degrade to a usable default
|
|
1159
|
+
* instead of rendering `undefined` into a select.
|
|
1160
|
+
*/
|
|
1161
|
+
function numberOr(value, fallback) {
|
|
1162
|
+
return typeof value === "number" && Number.isFinite(value) ? value : fallback;
|
|
1163
|
+
}
|
|
1164
|
+
/**
|
|
1165
|
+
* Probe sizes the card offers, in tokens. `0` is the minimal one-word probe.
|
|
1166
|
+
*
|
|
1167
|
+
* Deliberately a short, coarse list: the number is a spend decision (a probe at
|
|
1168
|
+
* 50k tokens costs real credit on every account in the batch), and offering a
|
|
1169
|
+
* free-text field invites values the upstream rounds away anyway.
|
|
1170
|
+
*/
|
|
1171
|
+
const PROBE_INPUT_TOKEN_CHOICES = [
|
|
1172
|
+
0,
|
|
1173
|
+
1e4,
|
|
1174
|
+
2e4,
|
|
1175
|
+
3e4,
|
|
1176
|
+
5e4,
|
|
1177
|
+
1e5
|
|
1178
|
+
];
|
|
1179
|
+
/** Compare two arrays of ids order-insensitively. */
|
|
1180
|
+
function sameIds(a, b) {
|
|
1181
|
+
const lhs = [...a ?? []].sort();
|
|
1182
|
+
const rhs = [...b ?? []].sort();
|
|
1183
|
+
return lhs.length === rhs.length && lhs.every((v, i) => v === rhs[i]);
|
|
1184
|
+
}
|
|
1185
|
+
function formatNumber$1(value) {
|
|
1186
|
+
return new Intl.NumberFormat(void 0, { maximumFractionDigits: 1 }).format(value);
|
|
1187
|
+
}
|
|
1188
|
+
/** `MM/DD, HH:mm` — the absolute stamp a row shows under its age. */
|
|
1189
|
+
function shortWhen(atMs) {
|
|
1190
|
+
const at = new Date(atMs);
|
|
1191
|
+
const pad = (value) => value < 10 ? `0${value}` : String(value);
|
|
1192
|
+
return `${pad(at.getMonth() + 1)}/${pad(at.getDate())}, ${pad(at.getHours())}:${pad(at.getMinutes())}`;
|
|
1193
|
+
}
|
|
1194
|
+
/** `MM/DD`, for a line that has room for a date but not a clock time. */
|
|
1195
|
+
function shortDate(atMs) {
|
|
1196
|
+
const at = new Date(atMs);
|
|
1197
|
+
const pad = (value) => value < 10 ? `0${value}` : String(value);
|
|
1198
|
+
return `${pad(at.getMonth() + 1)}/${pad(at.getDate())}`;
|
|
1199
|
+
}
|
|
1200
|
+
function relativeAt(atMs, now, t) {
|
|
1201
|
+
const mins = Math.max(0, Math.round((now - atMs) / 6e4));
|
|
1202
|
+
if (mins < 1) return t("row.poolAgoNow");
|
|
1203
|
+
if (mins < 60) return t("row.poolAgoMin", { n: mins });
|
|
1204
|
+
const hours = Math.round(mins / 60);
|
|
1205
|
+
if (hours < 24) return t("row.poolAgoHour", { n: hours });
|
|
1206
|
+
return t("row.poolAgoDay", { n: Math.round(hours / 24) });
|
|
1207
|
+
}
|
|
1208
|
+
/** A human label for every value of {@link TraeWebPoolOutcome}. */
|
|
1209
|
+
function outcomeLabel(outcome, t) {
|
|
1210
|
+
switch (outcome) {
|
|
1211
|
+
case "ok": return t("row.poolProbeOk");
|
|
1212
|
+
case "rate-limited": return t("row.poolProbeRateLimited");
|
|
1213
|
+
case "model-busy": return t("row.poolProbeModelBusy");
|
|
1214
|
+
case "out-of-credit": return t("row.poolProbeOutOfCredit");
|
|
1215
|
+
case "credential-rejected": return t("row.poolProbeCredentialRejected");
|
|
1216
|
+
case "policy-rejected": return t("row.poolProbePolicyRejected");
|
|
1217
|
+
case "plan-gated": return t("row.poolProbePlanGated");
|
|
1218
|
+
case "unavailable": return t("row.poolProbeUnavailable");
|
|
1219
|
+
case "not-found": return t("row.poolProbeNotFound");
|
|
1220
|
+
case "failed": return t("row.poolProbeFailed");
|
|
1221
|
+
}
|
|
1222
|
+
}
|
|
1223
|
+
/** A human label for every value of {@link TraeWebPoolExclusion}. */
|
|
1224
|
+
function exclusionLabel(exclusion, t) {
|
|
1225
|
+
switch (exclusion) {
|
|
1226
|
+
case "unusable": return t("row.poolExcludedUnusable");
|
|
1227
|
+
case "rate-limited": return t("row.poolExcludedRateLimited");
|
|
1228
|
+
case "model-busy": return t("row.poolExcludedModelBusy");
|
|
1229
|
+
case "out-of-credit": return t("row.poolExcludedOutOfCredit");
|
|
1230
|
+
case "credential-rejected": return t("row.poolExcludedCredentialRejected");
|
|
1231
|
+
case "policy-rejected": return t("row.poolExcludedPolicyRejected");
|
|
1232
|
+
case "plan-gated": return t("row.poolExcludedPlanGated");
|
|
1233
|
+
case "not-found": return t("row.poolExcludedNotFound");
|
|
1234
|
+
case "unavailable": return t("row.poolExcludedUnavailable");
|
|
1235
|
+
case "failed": return t("row.poolExcludedFailed");
|
|
1236
|
+
}
|
|
1237
|
+
}
|
|
1238
|
+
function probeSourceLabel(source, t) {
|
|
1239
|
+
switch (source) {
|
|
1240
|
+
case "test-batch": return t("row.poolProbeSourceTest");
|
|
1241
|
+
case "live-request": return t("row.poolProbeSourceLive");
|
|
1242
|
+
case void 0: return t("row.poolProbeSourceUnknown");
|
|
1243
|
+
}
|
|
1244
|
+
}
|
|
1245
|
+
/** The tone to log a test row result under. */
|
|
1246
|
+
function outcomeTone(outcome) {
|
|
1247
|
+
return outcome === "ok" ? "ok" : "warn";
|
|
1248
|
+
}
|
|
1249
|
+
/** The serving account (Host's answer), for the header badge. */
|
|
1250
|
+
function servingAccount(pool) {
|
|
1251
|
+
return pool.accounts.find((account) => account.current === true);
|
|
1252
|
+
}
|
|
1253
|
+
/**
|
|
1254
|
+
* Render a route refusal into a card-safe string. The error statuses the Host
|
|
1255
|
+
* can answer with are listed in the card doc; every one here must render as
|
|
1256
|
+
* TEXT (never crash). `origin-not-trusted`/`pool-unavailable` carry no body, so
|
|
1257
|
+
* they get a dedicated copy line keyed off the status.
|
|
1258
|
+
*/
|
|
1259
|
+
function renderRouteError(t, status, body) {
|
|
1260
|
+
if (status === 403 && body?.reason === "origin-not-trusted") return t("row.poolErrOrigin");
|
|
1261
|
+
if (status === 503) return t("row.poolErrUnavailable");
|
|
1262
|
+
if (status === 405) return t("row.poolErrMethod");
|
|
1263
|
+
if (status === 400) return t("row.poolErrBadRequest");
|
|
1264
|
+
if (status === 409) switch (body?.reason) {
|
|
1265
|
+
case "no-members": return t("row.poolErrNoMembers");
|
|
1266
|
+
case "no-live-members": return t("row.poolErrNoLiveMembers");
|
|
1267
|
+
case "checkin-unsupported": return t("row.poolErrCheckinUnsupported");
|
|
1268
|
+
case "target-model-stale": return t("row.poolErrTargetStale");
|
|
1269
|
+
case "no-free-model": return t("row.poolErrNoFreeModel");
|
|
1270
|
+
default: return t("row.poolErrConflict");
|
|
1271
|
+
}
|
|
1272
|
+
return t("row.poolErrGeneric", { status: String(status) });
|
|
1273
|
+
}
|
|
1274
|
+
/**
|
|
1275
|
+
* Consume a `test` NDJSON answer line-by-line so rows render while the batch
|
|
1276
|
+
* runs. A non-JSON line is skipped; a `{reason:'pool-failed'}` line reports the
|
|
1277
|
+
* failure and counts as "saw something". Returns whether any usable line was
|
|
1278
|
+
* seen (a body with nothing parseable is a route bug the caller should surface).
|
|
1279
|
+
*/
|
|
1280
|
+
function consumeTestNdjson(text, t, onRow, onFailure) {
|
|
1281
|
+
let sawAny = false;
|
|
1282
|
+
for (const line of text.split(/\r?\n/)) {
|
|
1283
|
+
if (line.trim() === "") continue;
|
|
1284
|
+
let parsed;
|
|
1285
|
+
try {
|
|
1286
|
+
parsed = JSON.parse(line);
|
|
1287
|
+
} catch {
|
|
1288
|
+
continue;
|
|
1289
|
+
}
|
|
1290
|
+
if (parsed.row !== void 0) {
|
|
1291
|
+
sawAny = true;
|
|
1292
|
+
onRow(parsed.row);
|
|
1293
|
+
} else if (parsed.rows !== void 0) {
|
|
1294
|
+
sawAny = true;
|
|
1295
|
+
for (const r of parsed.rows) onRow(r);
|
|
1296
|
+
} else if (parsed.reason === "pool-failed") {
|
|
1297
|
+
sawAny = true;
|
|
1298
|
+
onFailure(parsed.error ?? t("row.poolLogGeneric"));
|
|
1299
|
+
}
|
|
1300
|
+
}
|
|
1301
|
+
return sawAny;
|
|
1302
|
+
}
|
|
1303
|
+
function AccountPool({ panel, pool, models, t, settingsScope, onChanged, onRefresh, tools, topActions, details, claims, onClaim, deviceClaimed = false, onBind, bindingBusy }) {
|
|
1304
|
+
const [settingsTick, setSettingsTick] = (0, react.useState)(0);
|
|
1305
|
+
const saved = (0, react.useMemo)(() => {
|
|
1306
|
+
const base = regionPoolOf(settingsScope?.getSnapshot().value, panel);
|
|
1307
|
+
return {
|
|
1308
|
+
enabled: base.enabled === true,
|
|
1309
|
+
targetModelId: base.targetModelId ?? pool.targetModelId ?? pool.staleTargetModelId ?? "",
|
|
1310
|
+
memberAccountIds: [...base.memberAccountIds ?? []],
|
|
1311
|
+
probeInputTokens: numberOr(base.probeInputTokens, numberOr(pool.probeInputTokens, 0))
|
|
1312
|
+
};
|
|
1313
|
+
}, [
|
|
1314
|
+
settingsTick,
|
|
1315
|
+
settingsScope,
|
|
1316
|
+
panel,
|
|
1317
|
+
pool.targetModelId,
|
|
1318
|
+
pool.staleTargetModelId,
|
|
1319
|
+
pool.probeInputTokens
|
|
1320
|
+
]);
|
|
1321
|
+
/**
|
|
1322
|
+
* Whether the running Host knows the probe-size preference at all.
|
|
1323
|
+
*
|
|
1324
|
+
* The card bundle and the Host update on different clocks (a page refresh vs a
|
|
1325
|
+
* restart), so an older Host serves a pool document without this field. Writing
|
|
1326
|
+
* a key that Host's schema does not declare would be dropped on the way in and
|
|
1327
|
+
* then fail the read-back check — a save that reports an error and changes
|
|
1328
|
+
* nothing. Hiding the row is the honest state: this Host cannot do it yet.
|
|
1329
|
+
*/
|
|
1330
|
+
const probeSupported = typeof pool.probeInputTokens === "number";
|
|
1331
|
+
const [enabled, setEnabled] = (0, react.useState)(saved.enabled);
|
|
1332
|
+
const [targetModelId, setTargetModelId] = (0, react.useState)(saved.targetModelId ?? "");
|
|
1333
|
+
const [probeInputTokens, setProbeInputTokens] = (0, react.useState)(saved.probeInputTokens);
|
|
1334
|
+
const [selected, setSelected] = (0, react.useState)(() => new Set(saved.memberAccountIds ?? []));
|
|
1335
|
+
const [busy, setBusy] = (0, react.useState)(void 0);
|
|
1336
|
+
const [saveError, setSaveError] = (0, react.useState)(void 0);
|
|
1337
|
+
const [testing, setTesting] = (0, react.useState)(void 0);
|
|
1338
|
+
const [rows, setRows] = (0, react.useState)({});
|
|
1339
|
+
const [removed, setRemoved] = (0, react.useState)({});
|
|
1340
|
+
const [log, setLog] = (0, react.useState)([]);
|
|
1341
|
+
const [now, setNow] = (0, react.useState)(Date.now());
|
|
1342
|
+
const logId = (0, react.useRef)(0);
|
|
1343
|
+
const mountRef = (0, react.useRef)(true);
|
|
1344
|
+
(0, react.useEffect)(() => {
|
|
1345
|
+
mountRef.current = true;
|
|
1346
|
+
return () => {
|
|
1347
|
+
mountRef.current = false;
|
|
1348
|
+
};
|
|
1349
|
+
}, []);
|
|
1350
|
+
(0, react.useEffect)(() => settingsScope?.subscribe(() => {
|
|
1351
|
+
setSettingsTick((tick) => tick + 1);
|
|
1352
|
+
}), [settingsScope]);
|
|
1353
|
+
const pushLog = (tone, text) => {
|
|
1354
|
+
if (!mountRef.current) return;
|
|
1355
|
+
logId.current += 1;
|
|
1356
|
+
const id = logId.current;
|
|
1357
|
+
setLog((prev) => [...prev.slice(-49), {
|
|
1358
|
+
id,
|
|
1359
|
+
tone,
|
|
1360
|
+
text
|
|
1361
|
+
}]);
|
|
1362
|
+
};
|
|
1363
|
+
const knownIds = (0, react.useMemo)(() => new Set(pool.accounts.map((account) => account.accountId)), [pool]);
|
|
1364
|
+
const ghosts = (0, react.useMemo)(() => {
|
|
1365
|
+
const names = pool.memberNames ?? {};
|
|
1366
|
+
const result = [];
|
|
1367
|
+
for (const id of saved.memberAccountIds ?? []) if (!knownIds.has(id)) result.push({
|
|
1368
|
+
accountId: id,
|
|
1369
|
+
accountName: names[id] ?? id
|
|
1370
|
+
});
|
|
1371
|
+
return result;
|
|
1372
|
+
}, [
|
|
1373
|
+
saved,
|
|
1374
|
+
pool,
|
|
1375
|
+
knownIds
|
|
1376
|
+
]);
|
|
1377
|
+
const dirty = enabled !== saved.enabled || (targetModelId ?? "") !== (saved.targetModelId ?? "") || probeSupported && probeInputTokens !== saved.probeInputTokens || !sameIds([...selected], saved.memberAccountIds);
|
|
1378
|
+
const writable = settingsScope?.getSnapshot().writable === true;
|
|
1379
|
+
const savedKey = (0, react.useMemo)(() => `${panel}:${saved.enabled}:${saved.targetModelId ?? ""}:${saved.probeInputTokens}:${[...saved.memberAccountIds ?? []].sort().join("\0")}`, [panel, saved]);
|
|
1380
|
+
(0, react.useEffect)(() => {
|
|
1381
|
+
setEnabled(saved.enabled);
|
|
1382
|
+
setTargetModelId(saved.targetModelId ?? "");
|
|
1383
|
+
setProbeInputTokens(saved.probeInputTokens);
|
|
1384
|
+
setSelected(new Set(saved.memberAccountIds ?? []));
|
|
1385
|
+
setSaveError(void 0);
|
|
1386
|
+
}, [savedKey]);
|
|
1387
|
+
const toggleMember = (accountId) => {
|
|
1388
|
+
setSelected((prev) => {
|
|
1389
|
+
const next = new Set(prev);
|
|
1390
|
+
if (next.has(accountId)) next.delete(accountId);
|
|
1391
|
+
else next.add(accountId);
|
|
1392
|
+
return next;
|
|
1393
|
+
});
|
|
1394
|
+
};
|
|
1395
|
+
const selectAll = () => {
|
|
1396
|
+
setSelected(/* @__PURE__ */ new Set([...pool.accounts.map((a) => a.accountId), ...saved.memberAccountIds ?? []]));
|
|
1397
|
+
};
|
|
1398
|
+
const selectNone = () => setSelected(/* @__PURE__ */ new Set());
|
|
1399
|
+
const discard = () => {
|
|
1400
|
+
setEnabled(saved.enabled);
|
|
1401
|
+
setTargetModelId(saved.targetModelId ?? "");
|
|
1402
|
+
setProbeInputTokens(saved.probeInputTokens);
|
|
1403
|
+
setSelected(new Set(saved.memberAccountIds ?? []));
|
|
1404
|
+
setSaveError(void 0);
|
|
1405
|
+
pushLog("info", t("row.poolLogDiscarded"));
|
|
1406
|
+
setNow(Date.now());
|
|
1407
|
+
};
|
|
1408
|
+
const save = async () => {
|
|
1409
|
+
if (!writable || busy !== void 0) return;
|
|
1410
|
+
setBusy("save");
|
|
1411
|
+
setSaveError(void 0);
|
|
1412
|
+
try {
|
|
1413
|
+
const next = {
|
|
1414
|
+
enabled,
|
|
1415
|
+
...targetModelId === "" ? {} : { targetModelId },
|
|
1416
|
+
memberAccountIds: [...selected],
|
|
1417
|
+
...probeSupported ? { probeInputTokens } : {}
|
|
1418
|
+
};
|
|
1419
|
+
await writeSettingsField(settingsScope, "regions", nextRegionPool(settingsScope.getSnapshot().value, panel, next), (readBack) => {
|
|
1420
|
+
const readPool = regionPoolOf(readBack, panel);
|
|
1421
|
+
return readPool.enabled === (next.enabled ?? false) && sameIds(readPool.memberAccountIds, next.memberAccountIds) && (readPool.targetModelId ?? "") === (next.targetModelId ?? "") && (!probeSupported || (readPool.probeInputTokens ?? 0) === probeInputTokens);
|
|
1422
|
+
});
|
|
1423
|
+
pushLog("ok", t("row.poolLogSaved"));
|
|
1424
|
+
onChanged?.();
|
|
1425
|
+
onRefresh?.();
|
|
1426
|
+
setNow(Date.now());
|
|
1427
|
+
} catch (error) {
|
|
1428
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1429
|
+
setSaveError(t("row.poolSaveFailed", { message }));
|
|
1430
|
+
pushLog("error", t("row.poolLogSaveFailed"));
|
|
1431
|
+
} finally {
|
|
1432
|
+
if (mountRef.current) setBusy(void 0);
|
|
1433
|
+
}
|
|
1434
|
+
};
|
|
1435
|
+
const runCheckin = async () => {
|
|
1436
|
+
if (busy !== void 0) return;
|
|
1437
|
+
const members = [...selected];
|
|
1438
|
+
if (members.length === 0) {
|
|
1439
|
+
pushLog("warn", t("row.poolLogNoMembers"));
|
|
1440
|
+
return;
|
|
1441
|
+
}
|
|
1442
|
+
setBusy("checkin");
|
|
1443
|
+
setNow(Date.now());
|
|
1444
|
+
pushLog("info", t("row.poolLogCheckinStart", { count: members.length }));
|
|
1445
|
+
try {
|
|
1446
|
+
const response = await fetch(withTraePoolAction(panel, "checkin"), { method: "POST" });
|
|
1447
|
+
const body = await response.json();
|
|
1448
|
+
if (!response.ok) throw new Error(renderRouteError(t, response.status, body));
|
|
1449
|
+
for (const row of body?.rows ?? []) switch (row.status) {
|
|
1450
|
+
case "claimed":
|
|
1451
|
+
pushLog("ok", t("row.poolLogCheckinClaimed", { accountName: row.accountName }));
|
|
1452
|
+
break;
|
|
1453
|
+
case "already":
|
|
1454
|
+
case "device-already":
|
|
1455
|
+
pushLog("info", t("row.poolLogCheckinAlready", { accountName: row.accountName }));
|
|
1456
|
+
break;
|
|
1457
|
+
case "failed":
|
|
1458
|
+
case "disabled": pushLog("error", t("row.poolLogCheckinFailed", {
|
|
1459
|
+
accountName: row.accountName,
|
|
1460
|
+
message: row.message ?? ""
|
|
1461
|
+
}));
|
|
1462
|
+
}
|
|
1463
|
+
pushLog(members.length > 0 && body?.rows ? "ok" : "info", t("row.poolLogCheckinDone"));
|
|
1464
|
+
onRefresh?.();
|
|
1465
|
+
setNow(Date.now());
|
|
1466
|
+
} catch (error) {
|
|
1467
|
+
pushLog("error", t("row.poolLogBatchFailed", { message: error instanceof Error ? error.message : String(error) }));
|
|
1468
|
+
} finally {
|
|
1469
|
+
if (mountRef.current) setBusy(void 0);
|
|
1470
|
+
}
|
|
1471
|
+
};
|
|
1472
|
+
const runTest = async () => {
|
|
1473
|
+
if (busy !== void 0) return;
|
|
1474
|
+
const members = [...selected];
|
|
1475
|
+
if (members.length === 0) {
|
|
1476
|
+
pushLog("warn", t("row.poolLogNoMembers"));
|
|
1477
|
+
return;
|
|
1478
|
+
}
|
|
1479
|
+
setBusy("test");
|
|
1480
|
+
setRows({});
|
|
1481
|
+
setTesting({ done: 0 });
|
|
1482
|
+
setNow(Date.now());
|
|
1483
|
+
const targetLabel = targetModelId === "" ? t("row.poolTargetAutoOption") : models.find((m) => m.id === targetModelId)?.name ?? targetModelId;
|
|
1484
|
+
pushLog("info", t("row.poolLogTestStart", {
|
|
1485
|
+
count: members.length,
|
|
1486
|
+
model: targetLabel
|
|
1487
|
+
}));
|
|
1488
|
+
try {
|
|
1489
|
+
const text = await (await fetch(withTraePoolAction(panel, "test"), { method: "POST" })).text();
|
|
1490
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1491
|
+
if (!consumeTestNdjson(text, t, (r) => {
|
|
1492
|
+
setRows((prev) => ({
|
|
1493
|
+
...prev,
|
|
1494
|
+
[r.accountId]: r
|
|
1495
|
+
}));
|
|
1496
|
+
seen.add(r.accountId);
|
|
1497
|
+
if (mountRef.current) setTesting({ done: seen.size });
|
|
1498
|
+
pushLog(outcomeTone(r.outcome), t("row.poolLogTestRow", {
|
|
1499
|
+
accountName: r.accountName,
|
|
1500
|
+
outcome: outcomeLabel(r.outcome, t)
|
|
1501
|
+
}));
|
|
1502
|
+
}, (errorText) => {
|
|
1503
|
+
pushLog("error", t("row.poolLogBatchFailed", { message: errorText }));
|
|
1504
|
+
})) throw new Error(t("row.poolLogGeneric"));
|
|
1505
|
+
pushLog("ok", t("row.poolLogTestDone"));
|
|
1506
|
+
onRefresh?.();
|
|
1507
|
+
setNow(Date.now());
|
|
1508
|
+
} catch (error) {
|
|
1509
|
+
pushLog("error", t("row.poolLogBatchFailed", { message: error instanceof Error ? error.message : String(error) }));
|
|
1510
|
+
} finally {
|
|
1511
|
+
if (mountRef.current) {
|
|
1512
|
+
setBusy(void 0);
|
|
1513
|
+
setTesting(void 0);
|
|
1514
|
+
}
|
|
1515
|
+
}
|
|
1516
|
+
};
|
|
1517
|
+
const runSingleTest = async (account) => {
|
|
1518
|
+
if (busy !== void 0) return;
|
|
1519
|
+
setBusy("test");
|
|
1520
|
+
setNow(Date.now());
|
|
1521
|
+
const targetLabel = targetModelId === "" ? t("row.poolTargetAutoOption") : models.find((m) => m.id === targetModelId)?.name ?? targetModelId;
|
|
1522
|
+
pushLog("info", t("row.poolLogRowTesting", { accountName: account.accountName }));
|
|
1523
|
+
try {
|
|
1524
|
+
if (!consumeTestNdjson(await (await fetch(withTraePoolAccount(panel, "test", account.accountId), { method: "POST" })).text(), t, (r) => {
|
|
1525
|
+
setRows((prev) => ({
|
|
1526
|
+
...prev,
|
|
1527
|
+
[r.accountId]: r
|
|
1528
|
+
}));
|
|
1529
|
+
pushLog(outcomeTone(r.outcome), t("row.poolLogTestRow", {
|
|
1530
|
+
accountName: r.accountName,
|
|
1531
|
+
outcome: outcomeLabel(r.outcome, t),
|
|
1532
|
+
model: targetLabel
|
|
1533
|
+
}));
|
|
1534
|
+
}, (errorText) => {
|
|
1535
|
+
pushLog("error", t("row.poolLogBatchFailed", { message: errorText }));
|
|
1536
|
+
})) throw new Error(t("row.poolLogGeneric"));
|
|
1537
|
+
onRefresh?.();
|
|
1538
|
+
setNow(Date.now());
|
|
1539
|
+
} catch (error) {
|
|
1540
|
+
pushLog("error", t("row.poolLogBatchFailed", { message: error instanceof Error ? error.message : String(error) }));
|
|
1541
|
+
} finally {
|
|
1542
|
+
if (mountRef.current) setBusy(void 0);
|
|
1543
|
+
}
|
|
1544
|
+
};
|
|
1545
|
+
const removeAccount = async (account) => {
|
|
1546
|
+
if (busy !== void 0) return;
|
|
1547
|
+
setBusy("remove");
|
|
1548
|
+
setRemoved((prev) => ({
|
|
1549
|
+
...prev,
|
|
1550
|
+
[account.accountId]: false
|
|
1551
|
+
}));
|
|
1552
|
+
try {
|
|
1553
|
+
const response = await fetch(withTraePoolAccount(panel, "remove", account.accountId), { method: "POST" });
|
|
1554
|
+
const body = await response.json();
|
|
1555
|
+
if (!response.ok) throw new Error(renderRouteError(t, response.status, body));
|
|
1556
|
+
if (body?.removed !== true) throw new Error(t("row.poolRowRemoveFailed"));
|
|
1557
|
+
if (mountRef.current) setRemoved((prev) => ({
|
|
1558
|
+
...prev,
|
|
1559
|
+
[account.accountId]: true
|
|
1560
|
+
}));
|
|
1561
|
+
pushLog("ok", t("row.poolRowRemoved", { accountName: account.accountName }));
|
|
1562
|
+
setSelected((prev) => {
|
|
1563
|
+
const next = new Set(prev);
|
|
1564
|
+
next.delete(account.accountId);
|
|
1565
|
+
return next;
|
|
1566
|
+
});
|
|
1567
|
+
onRefresh?.();
|
|
1568
|
+
setNow(Date.now());
|
|
1569
|
+
} catch (error) {
|
|
1570
|
+
pushLog("error", t("row.poolRowRemoveError", { message: error instanceof Error ? error.message : String(error) }));
|
|
1571
|
+
} finally {
|
|
1572
|
+
if (mountRef.current) setBusy(void 0);
|
|
1573
|
+
}
|
|
1574
|
+
};
|
|
1575
|
+
const noCandidate = pool.noCandidate;
|
|
1576
|
+
const showNoCandidate = pool.enabled && noCandidate !== void 0;
|
|
1577
|
+
const serving = servingAccount(pool);
|
|
1578
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
|
|
1579
|
+
className: "dsm-trae-pool",
|
|
1580
|
+
"aria-label": t("row.poolTitle"),
|
|
1581
|
+
children: [
|
|
1582
|
+
pool.enabled !== true && onBind !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1583
|
+
className: "dsm-trae-pool-set dsm-trae-pool-bind",
|
|
1584
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
1585
|
+
className: "dsm-trae-pool-set-copy",
|
|
1586
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("b", { children: t("row.poolManualAccountLabel") }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("row.poolManualAccountHint") })]
|
|
1587
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1588
|
+
className: "dsm-trae-pool-set-ctl",
|
|
1589
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
|
|
1590
|
+
className: "dsm-trae-select dsm-trae-pool-account-select",
|
|
1591
|
+
value: serving?.accountId ?? "",
|
|
1592
|
+
disabled: bindingBusy === true || !writable,
|
|
1593
|
+
onChange: (event) => {
|
|
1594
|
+
const next = event.target.value;
|
|
1595
|
+
if (next !== "") onBind(next);
|
|
1596
|
+
},
|
|
1597
|
+
children: [serving === void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
1598
|
+
value: "",
|
|
1599
|
+
disabled: true,
|
|
1600
|
+
children: t("row.poolManualAccountNone")
|
|
1601
|
+
}) : null, pool.accounts.filter((account) => account.reachable !== false).map((account) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
1602
|
+
value: account.accountId,
|
|
1603
|
+
children: account.accountName
|
|
1604
|
+
}, account.accountId))]
|
|
1605
|
+
})
|
|
1606
|
+
})]
|
|
1607
|
+
}) : null,
|
|
1608
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1609
|
+
className: "dsm-trae-pool-actions",
|
|
1610
|
+
children: [
|
|
1611
|
+
pool.checkinSupported === true ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1612
|
+
type: "button",
|
|
1613
|
+
className: "dsm-btn dsm-btn-outline dsm-trae-pool-action-checkin",
|
|
1614
|
+
disabled: busy !== void 0 || !writable || selected.size === 0 || deviceClaimed,
|
|
1615
|
+
title: deviceClaimed ? t("row.checkinDeviceSpent") : void 0,
|
|
1616
|
+
onClick: () => {
|
|
1617
|
+
runCheckin();
|
|
1618
|
+
},
|
|
1619
|
+
children: busy === "checkin" ? t("row.poolCheckingIn") : t("row.poolCheckinAll")
|
|
1620
|
+
}) : null,
|
|
1621
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1622
|
+
type: "button",
|
|
1623
|
+
className: "dsm-btn dsm-btn-outline dsm-trae-pool-action-test",
|
|
1624
|
+
disabled: busy !== void 0 || selected.size === 0 || pool.targetModelSource === "none",
|
|
1625
|
+
title: pool.targetModelSource === "none" ? t("row.poolTargetNoneHint") : void 0,
|
|
1626
|
+
onClick: () => {
|
|
1627
|
+
runTest();
|
|
1628
|
+
},
|
|
1629
|
+
children: busy === "test" ? t("row.poolTesting") : t("row.poolTestAll")
|
|
1630
|
+
}),
|
|
1631
|
+
busy === "checkin" || busy === "test" || testing !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1632
|
+
className: "dsm-trae-pool-busy",
|
|
1633
|
+
role: "status",
|
|
1634
|
+
children: t("row.poolBusyHint")
|
|
1635
|
+
}) : null,
|
|
1636
|
+
topActions === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1637
|
+
className: "dsm-trae-pool-actions-end",
|
|
1638
|
+
children: topActions
|
|
1639
|
+
})
|
|
1640
|
+
]
|
|
1641
|
+
}),
|
|
1642
|
+
showNoCandidate ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(NoCandidateNotice, {
|
|
1643
|
+
noCandidate,
|
|
1644
|
+
t
|
|
1645
|
+
}) : null,
|
|
1646
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1647
|
+
className: "dsm-trae-pool-card",
|
|
1648
|
+
children: [
|
|
1649
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1650
|
+
className: "dsm-trae-pool-card-head",
|
|
1651
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", {
|
|
1652
|
+
className: "dsm-trae-pool-card-title",
|
|
1653
|
+
children: t("row.poolMembersTitle")
|
|
1654
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1655
|
+
className: "dsm-trae-pool-card-tools",
|
|
1656
|
+
children: [
|
|
1657
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1658
|
+
className: "dsm-trae-pool-count",
|
|
1659
|
+
children: t("row.poolSelectedCount", {
|
|
1660
|
+
count: selected.size,
|
|
1661
|
+
total: pool.accounts.length + ghosts.length
|
|
1662
|
+
})
|
|
1663
|
+
}),
|
|
1664
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1665
|
+
type: "button",
|
|
1666
|
+
className: "dsm-btn dsm-btn-outline",
|
|
1667
|
+
disabled: busy !== void 0,
|
|
1668
|
+
onClick: selectAll,
|
|
1669
|
+
children: t("row.poolSelectAll")
|
|
1670
|
+
}),
|
|
1671
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1672
|
+
type: "button",
|
|
1673
|
+
className: "dsm-btn dsm-btn-outline",
|
|
1674
|
+
disabled: busy !== void 0,
|
|
1675
|
+
onClick: selectNone,
|
|
1676
|
+
children: t("row.poolSelectNone")
|
|
1677
|
+
})
|
|
1678
|
+
]
|
|
1679
|
+
})]
|
|
1680
|
+
}),
|
|
1681
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1682
|
+
className: "dsm-trae-pool-card-note",
|
|
1683
|
+
children: t("row.poolCardHint")
|
|
1684
|
+
}),
|
|
1685
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1686
|
+
className: pool.checkinSupported ? "dsm-trae-pool-table" : "dsm-trae-pool-table dsm-trae-pool-table-no-checkin",
|
|
1687
|
+
role: "table",
|
|
1688
|
+
"aria-label": t("row.poolMembersTitle"),
|
|
1689
|
+
children: [
|
|
1690
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(MemberRowHeader, {
|
|
1691
|
+
t,
|
|
1692
|
+
checkinSupported: pool.checkinSupported
|
|
1693
|
+
}),
|
|
1694
|
+
pool.accounts.map((account) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(MemberRow, {
|
|
1695
|
+
account,
|
|
1696
|
+
showCheckin: pool.checkinSupported,
|
|
1697
|
+
deviceClaimed,
|
|
1698
|
+
models,
|
|
1699
|
+
rows,
|
|
1700
|
+
checked: selected.has(account.accountId),
|
|
1701
|
+
detail: details?.[account.accountId],
|
|
1702
|
+
claim: claims?.[account.accountId],
|
|
1703
|
+
onClaim,
|
|
1704
|
+
onToggle: () => {
|
|
1705
|
+
toggleMember(account.accountId);
|
|
1706
|
+
},
|
|
1707
|
+
onTest: () => {
|
|
1708
|
+
runSingleTest(account);
|
|
1709
|
+
},
|
|
1710
|
+
t,
|
|
1711
|
+
disabled: busy !== void 0 || !writable,
|
|
1712
|
+
removing: removed[account.accountId] === true,
|
|
1713
|
+
onRemove: () => {
|
|
1714
|
+
removeAccount(account);
|
|
1715
|
+
},
|
|
1716
|
+
formatNumber: formatNumber$1,
|
|
1717
|
+
relativeAt
|
|
1718
|
+
}, account.accountId)),
|
|
1719
|
+
ghosts.map((ghost) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(MemberGhostRow, {
|
|
1720
|
+
ghost,
|
|
1721
|
+
showCheckin: pool.checkinSupported,
|
|
1722
|
+
checked: selected.has(ghost.accountId),
|
|
1723
|
+
onToggle: () => {
|
|
1724
|
+
toggleMember(ghost.accountId);
|
|
1725
|
+
},
|
|
1726
|
+
t,
|
|
1727
|
+
disabled: busy !== void 0 || !writable,
|
|
1728
|
+
removing: removed[ghost.accountId] === true,
|
|
1729
|
+
onRemove: () => {
|
|
1730
|
+
removeAccount(ghost);
|
|
1731
|
+
}
|
|
1732
|
+
}, `ghost:${ghost.accountId}`))
|
|
1733
|
+
]
|
|
1734
|
+
}),
|
|
1735
|
+
ghosts.length > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1736
|
+
className: "dsm-trae-pool-ghost-note",
|
|
1737
|
+
role: "status",
|
|
1738
|
+
children: t("row.poolGhostMembers", {
|
|
1739
|
+
count: ghosts.length,
|
|
1740
|
+
names: ghosts.map((g) => g.accountName).join("、")
|
|
1741
|
+
})
|
|
1742
|
+
}) : null,
|
|
1743
|
+
dirty ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1744
|
+
className: "dsm-trae-pool-unsaved",
|
|
1745
|
+
role: "status",
|
|
1746
|
+
children: t("row.poolUnsavedMembers")
|
|
1747
|
+
}) : null
|
|
1748
|
+
]
|
|
1749
|
+
}),
|
|
1750
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1751
|
+
className: "dsm-trae-pool-settings-wrap",
|
|
1752
|
+
children: [
|
|
1753
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1754
|
+
className: "dsm-trae-pool-head",
|
|
1755
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", {
|
|
1756
|
+
className: "dsm-trae-pool-title",
|
|
1757
|
+
children: t("row.poolSettingsTitle")
|
|
1758
|
+
}), dirty ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1759
|
+
className: "dsm-trae-pool-dirty",
|
|
1760
|
+
role: "status",
|
|
1761
|
+
children: t("row.poolDirty")
|
|
1762
|
+
}) : null]
|
|
1763
|
+
}),
|
|
1764
|
+
tools === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1765
|
+
className: "dsm-trae-pool-tools dsm-trae-pool-tools-dashed",
|
|
1766
|
+
children: tools
|
|
1767
|
+
}),
|
|
1768
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1769
|
+
className: "dsm-trae-pool-note",
|
|
1770
|
+
children: t("row.poolSettingsNote")
|
|
1771
|
+
}),
|
|
1772
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1773
|
+
className: "dsm-trae-pool-settings",
|
|
1774
|
+
children: [
|
|
1775
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1776
|
+
className: "dsm-trae-pool-set",
|
|
1777
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
1778
|
+
className: "dsm-trae-pool-set-copy",
|
|
1779
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("b", { children: t("row.poolEnabled") }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("row.poolEnabledHint") })]
|
|
1780
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1781
|
+
className: "dsm-trae-pool-set-ctl",
|
|
1782
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("label", {
|
|
1783
|
+
className: "dsm-trae-switch dsm-trae-switch-pill",
|
|
1784
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
1785
|
+
type: "checkbox",
|
|
1786
|
+
checked: enabled,
|
|
1787
|
+
disabled: busy !== void 0 || !writable,
|
|
1788
|
+
"aria-label": t("row.poolEnabled"),
|
|
1789
|
+
onChange: (e) => {
|
|
1790
|
+
setEnabled(e.target.checked);
|
|
1791
|
+
}
|
|
1792
|
+
})
|
|
1793
|
+
})
|
|
1794
|
+
})]
|
|
1795
|
+
}),
|
|
1796
|
+
saved.enabled === true && enabled === false ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1797
|
+
className: "dsm-trae-pool-off-notice",
|
|
1798
|
+
role: "status",
|
|
1799
|
+
children: t("row.poolOffNotice")
|
|
1800
|
+
}) : null,
|
|
1801
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1802
|
+
className: "dsm-trae-pool-set",
|
|
1803
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
1804
|
+
className: "dsm-trae-pool-set-copy",
|
|
1805
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("b", { children: t("row.poolTargetLabel") }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("row.poolTargetHint") })]
|
|
1806
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1807
|
+
className: "dsm-trae-pool-set-ctl",
|
|
1808
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
|
|
1809
|
+
className: "dsm-trae-select",
|
|
1810
|
+
"aria-label": t("row.poolTargetLabel"),
|
|
1811
|
+
value: targetModelId,
|
|
1812
|
+
disabled: busy !== void 0 || !writable,
|
|
1813
|
+
onChange: (e) => {
|
|
1814
|
+
setTargetModelId(e.target.value);
|
|
1815
|
+
},
|
|
1816
|
+
children: [
|
|
1817
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
1818
|
+
value: "",
|
|
1819
|
+
children: t("row.poolTargetAutoOption")
|
|
1820
|
+
}),
|
|
1821
|
+
models.map((model) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
1822
|
+
value: model.id,
|
|
1823
|
+
children: model.name
|
|
1824
|
+
}, model.id)),
|
|
1825
|
+
pool.targetModelSource === "stale" && pool.staleTargetModelId !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
1826
|
+
value: pool.staleTargetModelId,
|
|
1827
|
+
children: t("row.poolTargetStaleOption", { model: pool.staleTargetModelId })
|
|
1828
|
+
}) : null
|
|
1829
|
+
]
|
|
1830
|
+
})
|
|
1831
|
+
})]
|
|
1832
|
+
}),
|
|
1833
|
+
pool.targetModelSource === "stale" ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1834
|
+
className: "dsm-trae-pool-target-stale",
|
|
1835
|
+
role: "status",
|
|
1836
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("row.poolTargetStale", { model: pool.staleTargetModelId ?? "" }) }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1837
|
+
type: "button",
|
|
1838
|
+
className: "dsm-btn dsm-btn-outline",
|
|
1839
|
+
disabled: busy !== void 0,
|
|
1840
|
+
onClick: () => {
|
|
1841
|
+
setTargetModelId("");
|
|
1842
|
+
pushLog("info", t("row.poolLogTargetCleared"));
|
|
1843
|
+
},
|
|
1844
|
+
children: t("row.poolTargetStaleClear")
|
|
1845
|
+
})]
|
|
1846
|
+
}) : null,
|
|
1847
|
+
pool.targetModelSource === "none" ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("p", {
|
|
1848
|
+
className: "dsm-trae-pool-target-none",
|
|
1849
|
+
role: "status",
|
|
1850
|
+
children: [
|
|
1851
|
+
t("row.poolTargetNone"),
|
|
1852
|
+
" — ",
|
|
1853
|
+
t("row.poolTargetNoneHint")
|
|
1854
|
+
]
|
|
1855
|
+
}) : null,
|
|
1856
|
+
probeSupported ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1857
|
+
className: "dsm-trae-pool-set",
|
|
1858
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
1859
|
+
className: "dsm-trae-pool-set-copy",
|
|
1860
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("b", { children: t("row.poolProbeSizeLabel") }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("row.poolProbeSizeHint") })]
|
|
1861
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1862
|
+
className: "dsm-trae-pool-set-ctl",
|
|
1863
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("select", {
|
|
1864
|
+
className: "dsm-trae-select",
|
|
1865
|
+
"aria-label": t("row.poolProbeSizeLabel"),
|
|
1866
|
+
value: String(probeInputTokens),
|
|
1867
|
+
disabled: busy !== void 0 || !writable,
|
|
1868
|
+
onChange: (e) => {
|
|
1869
|
+
setProbeInputTokens(Number(e.target.value));
|
|
1870
|
+
},
|
|
1871
|
+
children: PROBE_INPUT_TOKEN_CHOICES.map((choice) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
1872
|
+
value: String(choice),
|
|
1873
|
+
children: choice === 0 ? t("row.poolProbeSizeDefault") : t("row.poolProbeSizeOption", { wan: choice / 1e4 })
|
|
1874
|
+
}, choice))
|
|
1875
|
+
})
|
|
1876
|
+
})]
|
|
1877
|
+
}) : null,
|
|
1878
|
+
saveError === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1879
|
+
className: "dsm-trae-pool-save-error",
|
|
1880
|
+
role: "alert",
|
|
1881
|
+
children: saveError
|
|
1882
|
+
})
|
|
1883
|
+
]
|
|
1884
|
+
}),
|
|
1885
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1886
|
+
className: "dsm-trae-pool-save-row",
|
|
1887
|
+
children: [
|
|
1888
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("a", {
|
|
1889
|
+
className: "dsm-trae-pool-star",
|
|
1890
|
+
href: "https://github.com/dingminhua/dsh-connect-trae",
|
|
1891
|
+
target: "_blank",
|
|
1892
|
+
rel: "noreferrer noopener",
|
|
1893
|
+
children: [t("row.poolStar"), " ★"]
|
|
1894
|
+
}),
|
|
1895
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1896
|
+
type: "button",
|
|
1897
|
+
className: "dsm-btn dsm-btn-outline",
|
|
1898
|
+
disabled: !dirty || busy !== void 0,
|
|
1899
|
+
onClick: discard,
|
|
1900
|
+
children: t("row.poolDiscard")
|
|
1901
|
+
}),
|
|
1902
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1903
|
+
type: "button",
|
|
1904
|
+
className: "dsm-btn dsm-btn-primary",
|
|
1905
|
+
disabled: !dirty || busy !== void 0,
|
|
1906
|
+
onClick: () => {
|
|
1907
|
+
save();
|
|
1908
|
+
},
|
|
1909
|
+
children: busy === "save" ? t("row.poolSaving") : t("row.poolSaved")
|
|
1910
|
+
})
|
|
1911
|
+
]
|
|
1912
|
+
})
|
|
1913
|
+
]
|
|
1914
|
+
}),
|
|
1915
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1916
|
+
className: "dsm-trae-pool-note",
|
|
1917
|
+
children: t("row.poolRegionNote")
|
|
1918
|
+
}),
|
|
1919
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(ActivityLog, {
|
|
1920
|
+
log,
|
|
1921
|
+
t,
|
|
1922
|
+
now,
|
|
1923
|
+
relativeAt,
|
|
1924
|
+
onClear: () => setLog([])
|
|
1925
|
+
})
|
|
1926
|
+
]
|
|
1927
|
+
});
|
|
1928
|
+
}
|
|
1929
|
+
function NoCandidateNotice({ noCandidate, t }) {
|
|
1930
|
+
if (noCandidate === "empty") return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1931
|
+
className: "dsm-trae-pool-warning dsm-trae-pool-warning-empty",
|
|
1932
|
+
role: "status",
|
|
1933
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1934
|
+
className: "dsm-trae-pool-warning-title",
|
|
1935
|
+
children: t("row.poolNoCandidateEmpty")
|
|
1936
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1937
|
+
className: "dsm-trae-pool-warning-hint",
|
|
1938
|
+
children: t("row.poolNoCandidateEmptyHint")
|
|
1939
|
+
})]
|
|
1940
|
+
});
|
|
1941
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1942
|
+
className: "dsm-trae-pool-warning dsm-trae-pool-warning-unusable",
|
|
1943
|
+
role: "status",
|
|
1944
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1945
|
+
className: "dsm-trae-pool-warning-title",
|
|
1946
|
+
children: t("row.poolNoCandidateUnusable")
|
|
1947
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
1948
|
+
className: "dsm-trae-pool-warning-hint",
|
|
1949
|
+
children: [
|
|
1950
|
+
t("row.poolNoCandidateHint"),
|
|
1951
|
+
" ",
|
|
1952
|
+
exclusionLabel(noCandidate, t)
|
|
1953
|
+
]
|
|
1954
|
+
})]
|
|
1955
|
+
});
|
|
1956
|
+
}
|
|
1957
|
+
function MemberRowHeader({ t, checkinSupported }) {
|
|
1958
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1959
|
+
className: "dsm-trae-pool-row dsm-trae-pool-row-head",
|
|
1960
|
+
role: "row",
|
|
1961
|
+
children: [
|
|
1962
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1963
|
+
className: "dsm-trae-pool-col-account",
|
|
1964
|
+
children: t("row.poolColumnAccount")
|
|
1965
|
+
}),
|
|
1966
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("row.poolColumnCredits") }),
|
|
1967
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("row.poolColumnProbe") }),
|
|
1968
|
+
checkinSupported ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("row.poolColumnCheckin") }) : null,
|
|
1969
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {})
|
|
1970
|
+
]
|
|
1971
|
+
});
|
|
1972
|
+
}
|
|
1973
|
+
function MemberRow({ account, models, rows, checked, onToggle, onTest, t, disabled, removing, onRemove, formatNumber, relativeAt, detail, claim, onClaim, showCheckin, deviceClaimed }) {
|
|
1974
|
+
const isCurrent = account.current === true;
|
|
1975
|
+
const credit = account.generalAvailable;
|
|
1976
|
+
const excluded = account.excludedBy;
|
|
1977
|
+
const checkin = account.checkedInToday;
|
|
1978
|
+
const device = account.deviceCheckedIn;
|
|
1979
|
+
const testRow = rows[account.accountId];
|
|
1980
|
+
/**
|
|
1981
|
+
* Whether the probe column already explains WHY this row is excluded.
|
|
1982
|
+
*
|
|
1983
|
+
* The exclusion tag and the probe cell are derived from the same verdict, so a
|
|
1984
|
+
* row that shows both says 「登录凭据被拒」 twice, six centimetres apart. Keep
|
|
1985
|
+
* the tag only for the case the cell cannot cover: no probe to show at all
|
|
1986
|
+
* (e.g. a stored member whose last test predates this session).
|
|
1987
|
+
*/
|
|
1988
|
+
const probeOnScreen = testRow !== void 0 || account.probe !== void 0;
|
|
1989
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1990
|
+
className: `dsm-trae-pool-row${isCurrent ? " dsm-trae-pool-row-current" : ""}`,
|
|
1991
|
+
role: "row",
|
|
1992
|
+
"data-account-id": account.accountId,
|
|
1993
|
+
children: [
|
|
1994
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1995
|
+
className: "dsm-trae-pool-account",
|
|
1996
|
+
children: [
|
|
1997
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("label", {
|
|
1998
|
+
className: "dsm-trae-pool-check",
|
|
1999
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
2000
|
+
type: "checkbox",
|
|
2001
|
+
checked,
|
|
2002
|
+
disabled,
|
|
2003
|
+
onChange: onToggle,
|
|
2004
|
+
"aria-label": t("row.poolSelectAria", { accountName: account.accountName })
|
|
2005
|
+
})
|
|
2006
|
+
}),
|
|
2007
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
2008
|
+
className: "dsm-trae-pool-account-name",
|
|
2009
|
+
children: [
|
|
2010
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2011
|
+
className: `dsm-trae-pool-dot dsm-trae-pool-dot-${excluded ? "excluded" : "ok"}`,
|
|
2012
|
+
"aria-hidden": "true"
|
|
2013
|
+
}),
|
|
2014
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("b", { children: account.accountName }),
|
|
2015
|
+
detail?.meta === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2016
|
+
className: "dsm-trae-pool-meta",
|
|
2017
|
+
children: detail.meta
|
|
2018
|
+
}),
|
|
2019
|
+
detail?.warn === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2020
|
+
className: `dsm-trae-pool-warn-tag dsm-trae-pool-warn-tag-${detail.warnTone ?? "soon"}`,
|
|
2021
|
+
role: "status",
|
|
2022
|
+
children: detail.warn
|
|
2023
|
+
})
|
|
2024
|
+
]
|
|
2025
|
+
}),
|
|
2026
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
2027
|
+
className: "dsm-trae-pool-account-tags",
|
|
2028
|
+
children: [
|
|
2029
|
+
isCurrent ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2030
|
+
className: "dsm-trae-pool-tag dsm-trae-pool-tag-current",
|
|
2031
|
+
children: t("row.poolCurrentBadge")
|
|
2032
|
+
}) : null,
|
|
2033
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2034
|
+
className: checked ? "dsm-trae-pool-member" : "dsm-trae-pool-not-member",
|
|
2035
|
+
children: checked ? t("row.poolMember") : t("row.poolNotMember")
|
|
2036
|
+
}),
|
|
2037
|
+
excluded && !probeOnScreen ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2038
|
+
className: "dsm-trae-pool-tag dsm-trae-pool-tag-excluded",
|
|
2039
|
+
children: exclusionLabel(excluded, t)
|
|
2040
|
+
}) : null,
|
|
2041
|
+
account.tokenExpired === true && excluded !== "credential-rejected" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2042
|
+
className: "dsm-trae-pool-warn-tag dsm-trae-pool-warn-tag-lapsed",
|
|
2043
|
+
children: t("row.poolTokenExpired")
|
|
2044
|
+
}) : null
|
|
2045
|
+
]
|
|
2046
|
+
}),
|
|
2047
|
+
account.probe?.atMs === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2048
|
+
className: "dsm-trae-pool-account-note",
|
|
2049
|
+
children: t("row.poolProbeAt", { when: shortWhen(account.probe.atMs) })
|
|
2050
|
+
})
|
|
2051
|
+
]
|
|
2052
|
+
}),
|
|
2053
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2054
|
+
className: "dsm-trae-pool-credit",
|
|
2055
|
+
title: t("row.poolWorkHint"),
|
|
2056
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2057
|
+
className: "dsm-trae-pool-credit-num",
|
|
2058
|
+
children: credit === void 0 ? t("row.poolCreditNone") : formatNumber(credit)
|
|
2059
|
+
}), credit === void 0 || account.nearestExpiryMs === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2060
|
+
className: "dsm-trae-pool-credit-meta",
|
|
2061
|
+
children: t("row.poolCreditExpiry", { when: shortDate(account.nearestExpiryMs) })
|
|
2062
|
+
})]
|
|
2063
|
+
}),
|
|
2064
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
2065
|
+
className: "dsm-trae-pool-probe",
|
|
2066
|
+
children: testRow !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ProbeResult, {
|
|
2067
|
+
outcome: testRow.outcome,
|
|
2068
|
+
modelId: testRow.modelId,
|
|
2069
|
+
atMs: void 0,
|
|
2070
|
+
source: void 0,
|
|
2071
|
+
t,
|
|
2072
|
+
relativeAt,
|
|
2073
|
+
now: Date.now(),
|
|
2074
|
+
detail: testRow.detail
|
|
2075
|
+
}) : account.probe !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ProbeResult, {
|
|
2076
|
+
outcome: account.probe.outcome,
|
|
2077
|
+
modelId: account.probe.modelId,
|
|
2078
|
+
atMs: account.probe.atMs,
|
|
2079
|
+
source: account.probe.source,
|
|
2080
|
+
t,
|
|
2081
|
+
relativeAt,
|
|
2082
|
+
now: Date.now(),
|
|
2083
|
+
detail: account.probe.message
|
|
2084
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2085
|
+
className: "dsm-trae-pool-probe-none",
|
|
2086
|
+
children: t("row.poolNeverTested")
|
|
2087
|
+
})
|
|
2088
|
+
}),
|
|
2089
|
+
showCheckin ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2090
|
+
className: "dsm-trae-pool-checkin",
|
|
2091
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2092
|
+
className: `dsm-trae-pool-checkin-state dsm-trae-pool-checkin-${checkin === void 0 ? "unknown" : checkin ? "ok" : device === true || deviceClaimed ? "device" : "no"}`,
|
|
2093
|
+
children: checkin === void 0 ? t("row.poolCheckinUnknown") : checkin ? t("row.poolCheckedIn") : device === true || deviceClaimed ? t("row.poolDeviceUsed") : t("row.poolNotCheckedIn")
|
|
2094
|
+
}), claim === void 0 || onClaim === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2095
|
+
type: "button",
|
|
2096
|
+
className: "dsm-btn dsm-btn-outline dsm-trae-pool-claim",
|
|
2097
|
+
disabled: claim.disabled || disabled,
|
|
2098
|
+
title: claim.title,
|
|
2099
|
+
onClick: () => {
|
|
2100
|
+
onClaim(account.accountId);
|
|
2101
|
+
},
|
|
2102
|
+
children: claim.label
|
|
2103
|
+
})]
|
|
2104
|
+
}) : null,
|
|
2105
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2106
|
+
className: "dsm-trae-pool-row-actions",
|
|
2107
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2108
|
+
type: "button",
|
|
2109
|
+
className: "dsm-btn dsm-btn-outline",
|
|
2110
|
+
disabled,
|
|
2111
|
+
onClick: onTest,
|
|
2112
|
+
children: t("row.poolRowTest")
|
|
2113
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2114
|
+
type: "button",
|
|
2115
|
+
className: "dsm-btn dsm-btn-outline dsm-trae-pool-remove",
|
|
2116
|
+
disabled: disabled || removing,
|
|
2117
|
+
"aria-label": t("row.poolRowRemoveAria", { accountName: account.accountName }),
|
|
2118
|
+
onClick: onRemove,
|
|
2119
|
+
children: removing ? t("row.poolRowRemoving") : t("row.poolRowRemove")
|
|
2120
|
+
})]
|
|
2121
|
+
})
|
|
2122
|
+
]
|
|
2123
|
+
});
|
|
2124
|
+
}
|
|
2125
|
+
function MemberGhostRow({ ghost, checked, onToggle, t, disabled, removing, onRemove, showCheckin }) {
|
|
2126
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2127
|
+
className: "dsm-trae-pool-row dsm-trae-pool-row-ghost",
|
|
2128
|
+
role: "row",
|
|
2129
|
+
"data-account-id": ghost.accountId,
|
|
2130
|
+
children: [
|
|
2131
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2132
|
+
className: "dsm-trae-pool-account",
|
|
2133
|
+
children: [
|
|
2134
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("label", {
|
|
2135
|
+
className: "dsm-trae-pool-check",
|
|
2136
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
2137
|
+
type: "checkbox",
|
|
2138
|
+
checked,
|
|
2139
|
+
disabled,
|
|
2140
|
+
onChange: onToggle,
|
|
2141
|
+
"aria-label": t("row.poolSelectAria", { accountName: ghost.accountName })
|
|
2142
|
+
})
|
|
2143
|
+
}),
|
|
2144
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
2145
|
+
className: "dsm-trae-pool-account-name",
|
|
2146
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2147
|
+
className: "dsm-trae-pool-dot dsm-trae-pool-dot-ghost",
|
|
2148
|
+
"aria-hidden": "true"
|
|
2149
|
+
}), ghost.accountName]
|
|
2150
|
+
}),
|
|
2151
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
2152
|
+
className: "dsm-trae-pool-account-tags",
|
|
2153
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2154
|
+
className: checked ? "dsm-trae-pool-member" : "dsm-trae-pool-not-member",
|
|
2155
|
+
children: checked ? t("row.poolMember") : t("row.poolNotMember")
|
|
2156
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2157
|
+
className: "dsm-trae-pool-tag dsm-trae-pool-tag-ghost",
|
|
2158
|
+
children: t("row.poolExcludedUnusable")
|
|
2159
|
+
})]
|
|
2160
|
+
})
|
|
2161
|
+
]
|
|
2162
|
+
}),
|
|
2163
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
2164
|
+
className: "dsm-trae-pool-credit",
|
|
2165
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2166
|
+
className: "dsm-trae-pool-credit-num",
|
|
2167
|
+
children: t("row.poolCreditNone")
|
|
2168
|
+
})
|
|
2169
|
+
}),
|
|
2170
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
2171
|
+
className: "dsm-trae-pool-probe",
|
|
2172
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2173
|
+
className: "dsm-trae-pool-probe-none",
|
|
2174
|
+
children: t("row.poolNeverTested")
|
|
2175
|
+
})
|
|
2176
|
+
}),
|
|
2177
|
+
showCheckin ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
2178
|
+
className: "dsm-trae-pool-checkin",
|
|
2179
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2180
|
+
className: "dsm-trae-pool-checkin-state dsm-trae-pool-checkin-unknown",
|
|
2181
|
+
children: t("row.poolCheckinUnknown")
|
|
2182
|
+
})
|
|
2183
|
+
}) : null,
|
|
2184
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
2185
|
+
className: "dsm-trae-pool-row-actions",
|
|
2186
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2187
|
+
type: "button",
|
|
2188
|
+
className: "dsm-btn dsm-btn-outline dsm-trae-pool-remove",
|
|
2189
|
+
disabled: disabled || removing,
|
|
2190
|
+
"aria-label": t("row.poolGhostRemoveAria", { accountName: ghost.accountName }),
|
|
2191
|
+
onClick: onRemove,
|
|
2192
|
+
children: removing ? t("row.poolRowRemoving") : t("row.poolGhostRemove")
|
|
2193
|
+
})
|
|
2194
|
+
})
|
|
2195
|
+
]
|
|
2196
|
+
});
|
|
2197
|
+
}
|
|
2198
|
+
function ProbeResult({ outcome, modelId, atMs, source, detail, t, relativeAt, now }) {
|
|
2199
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2200
|
+
className: "dsm-trae-pool-probe-cell",
|
|
2201
|
+
children: [
|
|
2202
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
2203
|
+
className: `dsm-trae-pool-probe-outcome dsm-trae-pool-probe-${outcome === "ok" ? "ok" : "warn"}`,
|
|
2204
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2205
|
+
className: "dsm-trae-pool-dot dsm-trae-pool-dot-ok",
|
|
2206
|
+
"aria-hidden": "true"
|
|
2207
|
+
}), outcomeLabel(outcome, t)]
|
|
2208
|
+
}),
|
|
2209
|
+
atMs === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2210
|
+
className: "dsm-trae-pool-probe-meta",
|
|
2211
|
+
children: t("row.poolProbeMeasured", { when: relativeAt(atMs, now, t) })
|
|
2212
|
+
}),
|
|
2213
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
2214
|
+
className: "dsm-trae-pool-probe-meta",
|
|
2215
|
+
children: [modelId !== void 0 ? t("row.poolProbeModel", { model: modelId }) : t("row.poolProbeModelUnknown"), source === void 0 ? "" : ` · ${probeSourceLabel(source, t)}`]
|
|
2216
|
+
}),
|
|
2217
|
+
detail !== void 0 && detail !== "" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2218
|
+
className: "dsm-trae-pool-probe-detail",
|
|
2219
|
+
children: detail
|
|
2220
|
+
}) : null
|
|
2221
|
+
]
|
|
2222
|
+
});
|
|
2223
|
+
}
|
|
2224
|
+
function ActivityLog({ log, t, now, relativeAt, onClear }) {
|
|
2225
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2226
|
+
className: "dsm-trae-pool-log",
|
|
2227
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2228
|
+
className: "dsm-trae-pool-log-head",
|
|
2229
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h4", {
|
|
2230
|
+
className: "dsm-trae-pool-subtitle",
|
|
2231
|
+
children: t("row.poolLogTitle")
|
|
2232
|
+
}), log.length > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2233
|
+
type: "button",
|
|
2234
|
+
className: "dsm-btn dsm-btn-outline",
|
|
2235
|
+
onClick: onClear,
|
|
2236
|
+
children: t("row.poolLogClear")
|
|
2237
|
+
}) : null]
|
|
2238
|
+
}), log.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
2239
|
+
className: "dsm-trae-pool-log-empty",
|
|
2240
|
+
children: t("row.poolLogEmpty")
|
|
2241
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("ol", {
|
|
2242
|
+
className: "dsm-trae-pool-log-list",
|
|
2243
|
+
children: log.map((entry) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("li", {
|
|
2244
|
+
className: `dsm-trae-pool-log-item dsm-trae-pool-log-${entry.tone}`,
|
|
2245
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2246
|
+
className: "dsm-trae-pool-log-text",
|
|
2247
|
+
children: entry.text
|
|
2248
|
+
})
|
|
2249
|
+
}, entry.id))
|
|
2250
|
+
})]
|
|
2251
|
+
});
|
|
2252
|
+
}
|
|
2253
|
+
//#endregion
|
|
844
2254
|
//#region src/client/AccountTransfer.tsx
|
|
845
2255
|
/**
|
|
846
2256
|
* The card's account-transfer section: batch export and import of a region's
|
|
@@ -1438,6 +2848,10 @@ window.__ModuleLoader__.load({
|
|
|
1438
2848
|
status: "signed-out",
|
|
1439
2849
|
accounts: []
|
|
1440
2850
|
},
|
|
2851
|
+
enterprise: {
|
|
2852
|
+
status: "signed-out",
|
|
2853
|
+
accounts: []
|
|
2854
|
+
},
|
|
1441
2855
|
ai: {
|
|
1442
2856
|
status: "signed-out",
|
|
1443
2857
|
accounts: []
|
|
@@ -1452,6 +2866,7 @@ window.__ModuleLoader__.load({
|
|
|
1452
2866
|
*/
|
|
1453
2867
|
const [probeByRegion, setProbeByRegion] = (0, react.useState)({
|
|
1454
2868
|
cn: {},
|
|
2869
|
+
enterprise: {},
|
|
1455
2870
|
ai: {}
|
|
1456
2871
|
});
|
|
1457
2872
|
const [testing, setTesting] = (0, react.useState)(void 0);
|
|
@@ -1501,6 +2916,7 @@ window.__ModuleLoader__.load({
|
|
|
1501
2916
|
*/
|
|
1502
2917
|
const [accountStatusByRegion, setAccountStatusByRegion] = (0, react.useState)({
|
|
1503
2918
|
cn: {},
|
|
2919
|
+
enterprise: {},
|
|
1504
2920
|
ai: {}
|
|
1505
2921
|
});
|
|
1506
2922
|
/** Which account row is claiming right now, so only its button spins. */
|
|
@@ -1512,6 +2928,7 @@ window.__ModuleLoader__.load({
|
|
|
1512
2928
|
*/
|
|
1513
2929
|
const [claimErrorByRegion, setClaimErrorByRegion] = (0, react.useState)({
|
|
1514
2930
|
cn: {},
|
|
2931
|
+
enterprise: {},
|
|
1515
2932
|
ai: {}
|
|
1516
2933
|
});
|
|
1517
2934
|
/**
|
|
@@ -1524,6 +2941,7 @@ window.__ModuleLoader__.load({
|
|
|
1524
2941
|
*/
|
|
1525
2942
|
const [deviceSpentByRegion, setDeviceSpentByRegion] = (0, react.useState)({
|
|
1526
2943
|
cn: {},
|
|
2944
|
+
enterprise: {},
|
|
1527
2945
|
ai: {}
|
|
1528
2946
|
});
|
|
1529
2947
|
/**
|
|
@@ -1675,6 +3093,16 @@ window.__ModuleLoader__.load({
|
|
|
1675
3093
|
*/
|
|
1676
3094
|
const regionOn = (item) => regionEnabledOf(settingsScope?.getSnapshot().value, item);
|
|
1677
3095
|
const activeRegionOn = regionOn(activeRegion);
|
|
3096
|
+
/**
|
|
3097
|
+
* Credential region of the ACTIVE PANEL.
|
|
3098
|
+
*
|
|
3099
|
+
* The enterprise panel is a second CN pool, so anything that asks "is this the
|
|
3100
|
+
* international side?" — pay status vs. credit packs, check-in column, the
|
|
3101
|
+
* credit-readout switch — must answer by CREDENTIAL REGION, not by tab
|
|
3102
|
+
* identity. `isCnPool` is the common case (cn + enterprise).
|
|
3103
|
+
*/
|
|
3104
|
+
const credentialRegionIsAi = regionOfPanel(activeRegion) === "ai";
|
|
3105
|
+
const isCnPool = !credentialRegionIsAi;
|
|
1678
3106
|
const refreshUsage = (0, react.useCallback)(async (region, signal) => {
|
|
1679
3107
|
try {
|
|
1680
3108
|
const response = await fetch(withTraeRegion(TRAE_USAGE_PATH, region), {
|
|
@@ -2298,7 +3726,7 @@ window.__ModuleLoader__.load({
|
|
|
2298
3726
|
* "zero".
|
|
2299
3727
|
*/
|
|
2300
3728
|
const rowValue = (state) => {
|
|
2301
|
-
if (
|
|
3729
|
+
if (credentialRegionIsAi) {
|
|
2302
3730
|
const payStatus = state?.payStatus;
|
|
2303
3731
|
if (payStatus === void 0) return void 0;
|
|
2304
3732
|
const verdict = planVerdict(payStatus);
|
|
@@ -2311,6 +3739,10 @@ window.__ModuleLoader__.load({
|
|
|
2311
3739
|
children: verdict.tier === void 0 ? t("row.subscribed") : t("row.planTier", { tier: verdict.tier })
|
|
2312
3740
|
});
|
|
2313
3741
|
}
|
|
3742
|
+
if (activeRegion === "enterprise") return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3743
|
+
className: "dsm-trae-account-value dsm-trae-account-value-muted",
|
|
3744
|
+
children: t("row.balanceConsoleOnly")
|
|
3745
|
+
});
|
|
2314
3746
|
const figure = state?.credits?.generalAvailable;
|
|
2315
3747
|
if (figure === void 0) return void 0;
|
|
2316
3748
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
@@ -2337,23 +3769,52 @@ window.__ModuleLoader__.load({
|
|
|
2337
3769
|
* Until the row's status lands the button waits, disabled, so the row keeps
|
|
2338
3770
|
* its shape instead of reflowing the moment the read returns.
|
|
2339
3771
|
*/
|
|
2340
|
-
|
|
3772
|
+
/**
|
|
3773
|
+
* Everything a check-in button needs, derived once. The pool's table replaces
|
|
3774
|
+
* this card's roster table, so the same derivation has to feed both: two
|
|
3775
|
+
* copies would drift the moment one of them learned about a new refusal.
|
|
3776
|
+
*/
|
|
3777
|
+
/**
|
|
3778
|
+
* Whether THIS machine has already been paid today, for any account.
|
|
3779
|
+
*
|
|
3780
|
+
* `didCheckedIn` is not an account fact. Trae keys the daily claim to
|
|
3781
|
+
* `x-device-id` and answers a second claim with 9095, so once one account has
|
|
3782
|
+
* claimed from this machine, every other account on it is refused for the
|
|
3783
|
+
* rest of the day. Keeping that flag per account made the card invite a
|
|
3784
|
+
* check-in that could only fail — visible as soon as two accounts sit next to
|
|
3785
|
+
* each other in one table.
|
|
3786
|
+
*/
|
|
3787
|
+
const deviceClaimedToday = (() => {
|
|
3788
|
+
for (const state of Object.values(accountStatusByRegion[activeRegion])) if (state?.checkin?.didCheckedIn === true) return true;
|
|
3789
|
+
for (const spent of Object.values(deviceSpentByRegion[activeRegion])) if (spent) return true;
|
|
3790
|
+
return status.status === "signed-in" && status.pool !== void 0 && status.pool.accounts.some((account) => account.deviceCheckedIn === true);
|
|
3791
|
+
})();
|
|
3792
|
+
const claimViewOf = (accountId, state) => {
|
|
2341
3793
|
const checkin = state?.checkin;
|
|
2342
3794
|
const reward = checkin === void 0 ? void 0 : formatNumber(checkin.credits);
|
|
2343
3795
|
const claimed = checkin?.checkedIn === true;
|
|
2344
|
-
const deviceSpent =
|
|
2345
|
-
const spending = claimingFor ===
|
|
3796
|
+
const deviceSpent = deviceClaimedToday;
|
|
3797
|
+
const spending = claimingFor === accountId;
|
|
2346
3798
|
const usable = checkin !== void 0 && checkin.enabled;
|
|
2347
3799
|
const title = reward === void 0 || !usable ? t("row.checkinDisabled") : deviceSpent && !claimed ? t("row.checkinDeviceSpent") : claimed ? t("row.checkinClaimed") : t("row.checkinReward", { reward });
|
|
3800
|
+
return {
|
|
3801
|
+
label: spending ? t("row.checkinClaiming") : claimed ? t("row.checkinClaimed") : deviceSpent ? t("row.checkinDeviceSpentShort") : !usable || reward === void 0 ? t("row.checkinClaim") : t("row.checkinClaimWith", { reward }),
|
|
3802
|
+
title,
|
|
3803
|
+
disabled: !usable || claimed || deviceSpent || spending,
|
|
3804
|
+
busy: spending
|
|
3805
|
+
};
|
|
3806
|
+
};
|
|
3807
|
+
const checkinControl = (account, state) => {
|
|
3808
|
+
const view = claimViewOf(account.id, state);
|
|
2348
3809
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2349
3810
|
type: "button",
|
|
2350
3811
|
className: "dsm-trae-account-checkin",
|
|
2351
|
-
disabled:
|
|
2352
|
-
title,
|
|
3812
|
+
disabled: view.disabled,
|
|
3813
|
+
title: view.title,
|
|
2353
3814
|
onClick: () => {
|
|
2354
3815
|
claimFor(account.id);
|
|
2355
3816
|
},
|
|
2356
|
-
children:
|
|
3817
|
+
children: view.label
|
|
2357
3818
|
});
|
|
2358
3819
|
};
|
|
2359
3820
|
const label = status.status === "signed-in" ? t("row.signedIn", { accountName: status.accountName }) : status.status === "error" ? t("row.requestFailed") : t("row.signedOut");
|
|
@@ -2400,7 +3861,7 @@ window.__ModuleLoader__.load({
|
|
|
2400
3861
|
className: "dsm-trae-tabs",
|
|
2401
3862
|
role: "tablist",
|
|
2402
3863
|
"aria-label": title,
|
|
2403
|
-
children:
|
|
3864
|
+
children: TRAE_PANELS.map((item) => {
|
|
2404
3865
|
const regionStatus = statusByRegion[item];
|
|
2405
3866
|
const on = regionOn(item);
|
|
2406
3867
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
@@ -2418,7 +3879,7 @@ window.__ModuleLoader__.load({
|
|
|
2418
3879
|
"aria-hidden": "true",
|
|
2419
3880
|
className: "dsm-trae-tab-dot",
|
|
2420
3881
|
style: dotStyle(regionStatus.status)
|
|
2421
|
-
}), item === "cn" ? t("row.tabCn") : t("row.tabAi")]
|
|
3882
|
+
}), item === "cn" ? t("row.tabCn") : item === "enterprise" ? t("row.tabEnterprise") : t("row.tabAi")]
|
|
2422
3883
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("label", {
|
|
2423
3884
|
className: "dsm-trae-switch dsm-trae-tab-switch",
|
|
2424
3885
|
title: t("row.tabSwitchHint"),
|
|
@@ -2426,7 +3887,7 @@ window.__ModuleLoader__.load({
|
|
|
2426
3887
|
type: "checkbox",
|
|
2427
3888
|
checked: on,
|
|
2428
3889
|
disabled: settingsScope?.getSnapshot().writable !== true || togglingRegion !== void 0,
|
|
2429
|
-
"aria-label": t("row.tabSwitchAria", { region: item === "cn" ? t("row.tabCn") : t("row.tabAi") }),
|
|
3890
|
+
"aria-label": t("row.tabSwitchAria", { region: item === "cn" ? t("row.tabCn") : item === "enterprise" ? t("row.tabEnterprise") : t("row.tabAi") }),
|
|
2430
3891
|
onChange: (event) => {
|
|
2431
3892
|
toggleRegion(item, event.currentTarget.checked);
|
|
2432
3893
|
}
|
|
@@ -2457,6 +3918,7 @@ window.__ModuleLoader__.load({
|
|
|
2457
3918
|
title: t("row.followAppLoginHint"),
|
|
2458
3919
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
2459
3920
|
type: "checkbox",
|
|
3921
|
+
className: "dsm-trae-pref-check",
|
|
2460
3922
|
checked: settingsEdits.followAppLogin,
|
|
2461
3923
|
disabled: settingsScope?.getSnapshot().writable !== true || savingSettings,
|
|
2462
3924
|
"aria-describedby": followHintId,
|
|
@@ -2464,25 +3926,24 @@ window.__ModuleLoader__.load({
|
|
|
2464
3926
|
writePreference("followAppLogin", event.currentTarget.checked);
|
|
2465
3927
|
}
|
|
2466
3928
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
2467
|
-
|
|
2468
|
-
children: [
|
|
2469
|
-
|
|
2470
|
-
|
|
2471
|
-
|
|
2472
|
-
|
|
2473
|
-
|
|
2474
|
-
|
|
2475
|
-
|
|
2476
|
-
|
|
2477
|
-
})
|
|
2478
|
-
]
|
|
3929
|
+
className: "dsm-trae-pref-copy",
|
|
3930
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3931
|
+
className: "dsm-trae-pref-label",
|
|
3932
|
+
id: followLabelId,
|
|
3933
|
+
children: t("row.followAppLogin")
|
|
3934
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3935
|
+
className: "dsm-trae-pref-hint",
|
|
3936
|
+
id: followHintId,
|
|
3937
|
+
children: t("row.followAppLoginHint")
|
|
3938
|
+
})]
|
|
2479
3939
|
})]
|
|
2480
3940
|
}),
|
|
2481
3941
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
2482
3942
|
className: "dsm-trae-pref",
|
|
2483
|
-
title: t(
|
|
3943
|
+
title: t(isCnPool ? "row.showPointsInMainUiHint" : "row.showPointsInMainUiHintAi"),
|
|
2484
3944
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
2485
3945
|
type: "checkbox",
|
|
3946
|
+
className: "dsm-trae-pref-check",
|
|
2486
3947
|
checked: settingsEdits.showPointsInMainUi,
|
|
2487
3948
|
disabled: settingsScope?.getSnapshot().writable !== true || savingSettings,
|
|
2488
3949
|
"aria-describedby": creditsHintId,
|
|
@@ -2490,50 +3951,53 @@ window.__ModuleLoader__.load({
|
|
|
2490
3951
|
writePreference("showPointsInMainUi", event.currentTarget.checked);
|
|
2491
3952
|
}
|
|
2492
3953
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
2493
|
-
|
|
2494
|
-
children: [
|
|
2495
|
-
|
|
2496
|
-
|
|
2497
|
-
|
|
2498
|
-
|
|
2499
|
-
|
|
2500
|
-
|
|
2501
|
-
|
|
2502
|
-
|
|
2503
|
-
})
|
|
2504
|
-
]
|
|
3954
|
+
className: "dsm-trae-pref-copy",
|
|
3955
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3956
|
+
className: "dsm-trae-pref-label",
|
|
3957
|
+
id: creditsLabelId,
|
|
3958
|
+
children: t("row.showPointsInMainUi")
|
|
3959
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3960
|
+
className: "dsm-trae-pref-hint",
|
|
3961
|
+
id: creditsHintId,
|
|
3962
|
+
children: t(isCnPool ? "row.showPointsInMainUiHint" : "row.showPointsInMainUiHintAi")
|
|
3963
|
+
})]
|
|
2505
3964
|
})]
|
|
2506
3965
|
}),
|
|
2507
|
-
/* @__PURE__ */ (0, react_jsx_runtime.
|
|
3966
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("label", {
|
|
2508
3967
|
className: "dsm-trae-pref dsm-trae-credit-position-pref",
|
|
2509
3968
|
title: t("row.creditPositionHint"),
|
|
2510
|
-
children:
|
|
2511
|
-
className: "dsm-trae-
|
|
2512
|
-
|
|
2513
|
-
|
|
2514
|
-
|
|
2515
|
-
|
|
2516
|
-
|
|
2517
|
-
|
|
2518
|
-
|
|
2519
|
-
|
|
2520
|
-
|
|
2521
|
-
|
|
2522
|
-
|
|
2523
|
-
|
|
2524
|
-
|
|
2525
|
-
|
|
2526
|
-
|
|
2527
|
-
|
|
2528
|
-
|
|
3969
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
3970
|
+
className: "dsm-trae-pref-copy",
|
|
3971
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
3972
|
+
className: "dsm-trae-pref-lead",
|
|
3973
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3974
|
+
className: "dsm-trae-pref-label",
|
|
3975
|
+
children: t("row.creditPositionLabel")
|
|
3976
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
|
|
3977
|
+
className: "dsm-trae-credit-position",
|
|
3978
|
+
value: activeRegionOn ? creditPositionOn(activeRegion) : "composer",
|
|
3979
|
+
disabled: !activeRegionOn || settingsScope?.getSnapshot().writable !== true || savingSettings,
|
|
3980
|
+
"aria-label": t("row.creditPositionAria", { region: activeRegion === "cn" ? t("row.tabCn") : activeRegion === "enterprise" ? t("row.tabEnterprise") : t("row.tabAi") }),
|
|
3981
|
+
onChange: (event) => {
|
|
3982
|
+
changeCreditPosition(activeRegion, event.currentTarget.value === "stats" ? "stats" : "composer");
|
|
3983
|
+
},
|
|
3984
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
3985
|
+
value: "composer",
|
|
3986
|
+
children: t("row.creditPositionComposer")
|
|
3987
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
3988
|
+
value: "stats",
|
|
3989
|
+
children: t("row.creditPositionStats")
|
|
3990
|
+
})]
|
|
3991
|
+
})]
|
|
3992
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2529
3993
|
className: "dsm-trae-pref-hint",
|
|
2530
3994
|
children: t("row.creditPositionHint")
|
|
2531
|
-
})
|
|
2532
|
-
|
|
3995
|
+
})]
|
|
3996
|
+
})
|
|
2533
3997
|
})
|
|
2534
3998
|
]
|
|
2535
3999
|
}),
|
|
2536
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
|
|
4000
|
+
status.status === "signed-in" && status.pool !== void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
|
|
2537
4001
|
className: "dsm-trae-accounts",
|
|
2538
4002
|
"aria-label": t("row.accountsTitle"),
|
|
2539
4003
|
children: [
|
|
@@ -2572,19 +4036,20 @@ window.__ModuleLoader__.load({
|
|
|
2572
4036
|
children: t("row.tokenExpiry", { expiresAt: formatDateTime(status.tokenExpiresAtMs) })
|
|
2573
4037
|
}) : null]
|
|
2574
4038
|
})
|
|
2575
|
-
}) :
|
|
2576
|
-
|
|
4039
|
+
}) : null,
|
|
4040
|
+
status.status !== "error" && status.accounts.length > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4041
|
+
className: `dsm-trae-account-table${isCnPool ? " dsm-trae-account-table-checkin" : ""}`,
|
|
2577
4042
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2578
4043
|
className: "dsm-trae-account-row dsm-trae-account-row-head",
|
|
2579
4044
|
role: "row",
|
|
2580
4045
|
children: [
|
|
2581
4046
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("row.accountColumnAccount") }),
|
|
2582
4047
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("row.accountColumnValue") }),
|
|
2583
|
-
|
|
4048
|
+
isCnPool ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("row.accountColumnCheckin") }) : null,
|
|
2584
4049
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {})
|
|
2585
4050
|
]
|
|
2586
4051
|
}), status.accounts.map((account) => {
|
|
2587
|
-
const current = status.status === "signed-in"
|
|
4052
|
+
const current = status.status === "signed-in" ? account.id === status.accountId : account.selected === true;
|
|
2588
4053
|
const state = accountStatusByRegion[activeRegion][account.id];
|
|
2589
4054
|
const error = claimErrorByRegion[activeRegion][account.id];
|
|
2590
4055
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
@@ -2636,7 +4101,7 @@ window.__ModuleLoader__.load({
|
|
|
2636
4101
|
className: "dsm-trae-account-worth",
|
|
2637
4102
|
children: rowValue(state)
|
|
2638
4103
|
}),
|
|
2639
|
-
|
|
4104
|
+
isCnPool ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2640
4105
|
className: "dsm-trae-account-checkin-cell",
|
|
2641
4106
|
children: checkinControl(account, state)
|
|
2642
4107
|
}) : null,
|
|
@@ -2655,61 +4120,10 @@ window.__ModuleLoader__.load({
|
|
|
2655
4120
|
]
|
|
2656
4121
|
}, account.id);
|
|
2657
4122
|
})]
|
|
2658
|
-
}),
|
|
2659
|
-
status.status === "signed-in" && status.alternatives !== void 0 && status.alternatives.length > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2660
|
-
className: "dsm-trae-usage-alternative",
|
|
2661
|
-
role: "status",
|
|
2662
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("p", {
|
|
2663
|
-
className: "dsm-trae-usage-alternative-text",
|
|
2664
|
-
children: [
|
|
2665
|
-
t("row.creditsExhausted", { account: status.accountName }),
|
|
2666
|
-
" ",
|
|
2667
|
-
t("row.creditsAlternative", { accounts: status.alternatives.map((alt) => `${alt.accountName}(${t(`row.edition${alt.edition === "cn" ? "Cn" : alt.edition === "sg" ? "Sg" : alt.edition === "solo" ? "Solo" : "SoloSg"}`)}:${formatNumber(alt.workAvailable + alt.generalAvailable)})`).join("、") })
|
|
2668
|
-
]
|
|
2669
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2670
|
-
type: "button",
|
|
2671
|
-
className: "dsm-trae-usage-switch-button",
|
|
2672
|
-
disabled: switchingAccount || settingsScope?.getSnapshot()?.writable !== true,
|
|
2673
|
-
onClick: () => {
|
|
2674
|
-
const target = status.alternatives?.[0];
|
|
2675
|
-
if (target !== void 0) switchAccount(target.id);
|
|
2676
|
-
},
|
|
2677
|
-
children: t("row.creditsAlternativeSwitch")
|
|
2678
|
-
})]
|
|
2679
|
-
}) : null,
|
|
2680
|
-
status.status === "signed-in" && status.credits?.expiry !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ExpiryNotice, {
|
|
2681
|
-
expiry: status.credits.expiry,
|
|
2682
|
-
t,
|
|
2683
|
-
formatNumber,
|
|
2684
|
-
formatDateTime
|
|
2685
|
-
}) : null,
|
|
2686
|
-
status.status === "signed-in" && status.appLogin !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2687
|
-
className: "dsm-trae-usage-alternative",
|
|
2688
|
-
role: "status",
|
|
2689
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
2690
|
-
className: "dsm-trae-usage-alternative-text",
|
|
2691
|
-
children: t("row.appLoginMismatch", {
|
|
2692
|
-
account: status.appLogin.accountName,
|
|
2693
|
-
bound: status.accountName
|
|
2694
|
-
})
|
|
2695
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2696
|
-
type: "button",
|
|
2697
|
-
className: "dsm-trae-usage-switch-button",
|
|
2698
|
-
disabled: switchingAccount || settingsScope?.getSnapshot()?.writable !== true,
|
|
2699
|
-
onClick: () => {
|
|
2700
|
-
followAppLogin();
|
|
2701
|
-
},
|
|
2702
|
-
children: t("row.appLoginFollow", { account: status.appLogin.accountName })
|
|
2703
|
-
})]
|
|
2704
4123
|
}) : null,
|
|
2705
|
-
rescanNote === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
2706
|
-
className: "dsm-trae-usage-note",
|
|
2707
|
-
role: "status",
|
|
2708
|
-
children: rescanNote
|
|
2709
|
-
}),
|
|
2710
4124
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(AccountTransfer, {
|
|
2711
4125
|
t,
|
|
2712
|
-
region: activeRegion,
|
|
4126
|
+
region: activeRegion === "ai" ? "ai" : "cn",
|
|
2713
4127
|
accounts: status.status === "signed-in" ? status.accounts.map((account) => ({
|
|
2714
4128
|
id: account.id,
|
|
2715
4129
|
accountName: account.accountName
|
|
@@ -2724,7 +4138,133 @@ window.__ModuleLoader__.load({
|
|
|
2724
4138
|
})
|
|
2725
4139
|
]
|
|
2726
4140
|
}),
|
|
4141
|
+
status.status === "signed-in" && status.creditsUnavailable === "enterprise" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
4142
|
+
className: "dsm-trae-credits-unavailable",
|
|
4143
|
+
role: "note",
|
|
4144
|
+
children: t("row.enterpriseCreditsNotice")
|
|
4145
|
+
}) : null,
|
|
4146
|
+
status.status === "signed-in" && status.alternatives !== void 0 && status.alternatives.length > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4147
|
+
className: "dsm-trae-usage-alternative",
|
|
4148
|
+
role: "status",
|
|
4149
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("p", {
|
|
4150
|
+
className: "dsm-trae-usage-alternative-text",
|
|
4151
|
+
children: [
|
|
4152
|
+
t("row.creditsExhausted", { account: status.accountName }),
|
|
4153
|
+
" ",
|
|
4154
|
+
t("row.creditsAlternative", { accounts: status.alternatives.map((alt) => `${alt.accountName}(${t(`row.edition${alt.edition === "cn" ? "Cn" : alt.edition === "sg" ? "Sg" : alt.edition === "solo" ? "Solo" : "SoloSg"}`)}:${formatNumber(alt.workAvailable + alt.generalAvailable)})`).join("、") })
|
|
4155
|
+
]
|
|
4156
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
4157
|
+
type: "button",
|
|
4158
|
+
className: "dsm-trae-usage-switch-button",
|
|
4159
|
+
disabled: switchingAccount || settingsScope?.getSnapshot()?.writable !== true,
|
|
4160
|
+
onClick: () => {
|
|
4161
|
+
const target = status.alternatives?.[0];
|
|
4162
|
+
if (target !== void 0) switchAccount(target.id);
|
|
4163
|
+
},
|
|
4164
|
+
children: t("row.creditsAlternativeSwitch")
|
|
4165
|
+
})]
|
|
4166
|
+
}) : null,
|
|
4167
|
+
status.status === "signed-in" && status.credits?.expiry !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ExpiryNotice, {
|
|
4168
|
+
expiry: status.credits.expiry,
|
|
4169
|
+
t,
|
|
4170
|
+
formatNumber,
|
|
4171
|
+
formatDateTime
|
|
4172
|
+
}) : null,
|
|
4173
|
+
status.status === "signed-in" && status.appLogin !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4174
|
+
className: "dsm-trae-usage-alternative",
|
|
4175
|
+
role: "status",
|
|
4176
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
4177
|
+
className: "dsm-trae-usage-alternative-text",
|
|
4178
|
+
children: t("row.appLoginMismatch", {
|
|
4179
|
+
account: status.appLogin.accountName,
|
|
4180
|
+
bound: status.accountName
|
|
4181
|
+
})
|
|
4182
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
4183
|
+
type: "button",
|
|
4184
|
+
className: "dsm-trae-usage-switch-button",
|
|
4185
|
+
disabled: switchingAccount || settingsScope?.getSnapshot()?.writable !== true,
|
|
4186
|
+
onClick: () => {
|
|
4187
|
+
followAppLogin();
|
|
4188
|
+
},
|
|
4189
|
+
children: t("row.appLoginFollow", { account: status.appLogin.accountName })
|
|
4190
|
+
})]
|
|
4191
|
+
}) : null,
|
|
4192
|
+
rescanNote === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
4193
|
+
className: "dsm-trae-usage-note",
|
|
4194
|
+
role: "status",
|
|
4195
|
+
children: rescanNote
|
|
4196
|
+
}),
|
|
2727
4197
|
status.status === "signed-in" ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
|
|
4198
|
+
status.pool !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(AccountPool, {
|
|
4199
|
+
panel: activeRegion,
|
|
4200
|
+
pool: status.pool,
|
|
4201
|
+
models: visibleModels,
|
|
4202
|
+
t,
|
|
4203
|
+
settingsScope,
|
|
4204
|
+
topActions: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
4205
|
+
type: "button",
|
|
4206
|
+
className: "dsm-btn dsm-btn-outline",
|
|
4207
|
+
disabled: busy,
|
|
4208
|
+
onClick: () => {
|
|
4209
|
+
rescanAccounts();
|
|
4210
|
+
},
|
|
4211
|
+
children: busy ? t("row.accountsScanning") : t("row.poolRescan")
|
|
4212
|
+
}),
|
|
4213
|
+
tools: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4214
|
+
className: "dsm-trae-pool-tool-row",
|
|
4215
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
4216
|
+
className: "dsm-trae-pool-tool-label",
|
|
4217
|
+
children: t("row.poolToolsLabel")
|
|
4218
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(AccountTransfer, {
|
|
4219
|
+
t,
|
|
4220
|
+
region: activeRegion === "ai" ? "ai" : "cn",
|
|
4221
|
+
accounts: status.accounts.map((account) => ({
|
|
4222
|
+
id: account.id,
|
|
4223
|
+
accountName: account.accountName
|
|
4224
|
+
})),
|
|
4225
|
+
onChanged: async () => {
|
|
4226
|
+
const fresh = await refreshUsage(activeRegion);
|
|
4227
|
+
if (fresh !== void 0) setStatusByRegion((current) => ({
|
|
4228
|
+
...current,
|
|
4229
|
+
[activeRegion]: fresh
|
|
4230
|
+
}));
|
|
4231
|
+
}
|
|
4232
|
+
})]
|
|
4233
|
+
}),
|
|
4234
|
+
details: Object.fromEntries(status.accounts.map((account) => {
|
|
4235
|
+
const warn = refreshTokenWarning(account.refreshExpiresAtMs, Date.now(), t, formatDateTime);
|
|
4236
|
+
return [account.id, {
|
|
4237
|
+
meta: `${editionLabel(account.edition)} · ${sourceLabel(account.source)}`,
|
|
4238
|
+
...warn === void 0 ? {} : {
|
|
4239
|
+
warn: warn.text,
|
|
4240
|
+
warnTone: warn.tone
|
|
4241
|
+
}
|
|
4242
|
+
}];
|
|
4243
|
+
})),
|
|
4244
|
+
claims: status.pool.checkinSupported === true ? Object.fromEntries(status.accounts.map((account) => [account.id, claimViewOf(account.id, accountStatusByRegion[activeRegion][account.id])])) : void 0,
|
|
4245
|
+
onClaim: (accountId) => {
|
|
4246
|
+
claimFor(accountId);
|
|
4247
|
+
},
|
|
4248
|
+
deviceClaimed: deviceClaimedToday,
|
|
4249
|
+
onBind: (accountId) => {
|
|
4250
|
+
switchAccount(accountId);
|
|
4251
|
+
},
|
|
4252
|
+
bindingBusy: switchingAccount,
|
|
4253
|
+
onChanged: async () => {
|
|
4254
|
+
const fresh = await refreshUsage(activeRegion);
|
|
4255
|
+
if (fresh !== void 0) setStatusByRegion((current) => ({
|
|
4256
|
+
...current,
|
|
4257
|
+
[activeRegion]: fresh
|
|
4258
|
+
}));
|
|
4259
|
+
},
|
|
4260
|
+
onRefresh: async () => {
|
|
4261
|
+
const fresh = await refreshUsage(activeRegion);
|
|
4262
|
+
if (fresh !== void 0) setStatusByRegion((current) => ({
|
|
4263
|
+
...current,
|
|
4264
|
+
[activeRegion]: fresh
|
|
4265
|
+
}));
|
|
4266
|
+
}
|
|
4267
|
+
}) : null,
|
|
2728
4268
|
status.checkin !== void 0 && !status.checkin.enabled ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
2729
4269
|
className: "dsm-trae-usage-error",
|
|
2730
4270
|
children: t("row.checkinDisabled")
|
|
@@ -2820,7 +4360,7 @@ window.__ModuleLoader__.load({
|
|
|
2820
4360
|
className: "dsm-trae-model-name",
|
|
2821
4361
|
children: [
|
|
2822
4362
|
model.name,
|
|
2823
|
-
model.creditMultiplier === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
4363
|
+
activeRegion === "enterprise" || model.creditMultiplier === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
2824
4364
|
className: "dsm-trae-model-name-rate",
|
|
2825
4365
|
children: ["· x", model.creditMultiplier.toFixed(2)]
|
|
2826
4366
|
}),
|
|
@@ -3648,8 +5188,10 @@ window.__ModuleLoader__.load({
|
|
|
3648
5188
|
"row.expand": "Expand",
|
|
3649
5189
|
"row.collapse": "Collapse",
|
|
3650
5190
|
"row.tabCn": "Domestic",
|
|
5191
|
+
"row.tabEnterprise": "Enterprise",
|
|
3651
5192
|
"row.tabAi": "Global",
|
|
3652
|
-
"row.tabHint": "
|
|
5193
|
+
"row.tabHint": "Three independent providers, each with its own account, usage, and models. All are live at once and never affect each other.",
|
|
5194
|
+
"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.",
|
|
3653
5195
|
"row.tabSwitchHint": "Turns this provider off: its models leave the DSH picker. Account and settings are kept.",
|
|
3654
5196
|
"row.tabSwitchAria": "Offer the {region} provider to DSH",
|
|
3655
5197
|
"row.tabOffNotice": "This provider is off: its models are not offered to DSH. Account and settings are kept.",
|
|
@@ -3731,6 +5273,8 @@ window.__ModuleLoader__.load({
|
|
|
3731
5273
|
"row.accountColumnValue": "Balance",
|
|
3732
5274
|
"row.accountColumnCheckin": "Check-in",
|
|
3733
5275
|
"row.accountBalance": "{credits} credits",
|
|
5276
|
+
"row.balanceConsoleOnly": "In the enterprise console",
|
|
5277
|
+
"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.",
|
|
3734
5278
|
"row.sourceDsh": "plugin copy",
|
|
3735
5279
|
"row.modelsTitle": "Models",
|
|
3736
5280
|
"row.modelsSummary": "{count} enabled",
|
|
@@ -3808,6 +5352,151 @@ window.__ModuleLoader__.load({
|
|
|
3808
5352
|
"row.modelReasoningSupported": "Reasoning: supported",
|
|
3809
5353
|
"row.modelUnknown": "Unknown",
|
|
3810
5354
|
"row.modelCapabilityPending": "Image input follows Trae's own multimodal flag, plus anything you tick yourself.",
|
|
5355
|
+
"row.poolTitle": "Account pool",
|
|
5356
|
+
"row.poolEnabled": "Enable the account pool",
|
|
5357
|
+
"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.",
|
|
5358
|
+
"row.poolCurrentHeader": "Billing now: {account}",
|
|
5359
|
+
"row.poolCurrentBadge": "serving",
|
|
5360
|
+
"row.poolMember": "in pool",
|
|
5361
|
+
"row.poolNotMember": "not in pool",
|
|
5362
|
+
"row.poolCheckinAll": "Check in all accounts",
|
|
5363
|
+
"row.poolBusyHint": "Working through accounts one at a time…",
|
|
5364
|
+
"row.poolTestAll": "Test all accounts",
|
|
5365
|
+
"row.poolTesting": "Testing…",
|
|
5366
|
+
"row.poolOffNotice": "The pool is off — the bound account is what serves. Turn the switch below on to let the pool decide.",
|
|
5367
|
+
"row.poolNoCandidate": "No usable pool member can serve this panel right now.",
|
|
5368
|
+
"row.poolNoCandidateEmpty": "No pool members are selected yet.",
|
|
5369
|
+
"row.poolNoCandidateEmptyHint": "Tick accounts in the membership list to bring them into the pool.",
|
|
5370
|
+
"row.poolNoCandidateUnusable": "Every selected member is unavailable right now.",
|
|
5371
|
+
"row.poolNoCandidateHint": "Traffic keeps landing on the saved selection until one recovers.",
|
|
5372
|
+
"row.poolMembersTitle": "Accounts in this pool",
|
|
5373
|
+
"row.poolCardHint": "Only checked accounts are checked in, tested and dispatched. Nothing is checked by default — pick the ones you want, then save.",
|
|
5374
|
+
"row.poolRescan": "Re-detect accounts",
|
|
5375
|
+
"row.poolToolsLabel": "Export / import accounts",
|
|
5376
|
+
"row.poolCreditExpiry": "expires {when}",
|
|
5377
|
+
"row.poolProbeMeasured": "measured {when}",
|
|
5378
|
+
"row.poolProbeAt": "tested {when}",
|
|
5379
|
+
"row.poolSettingsNote": "Preferences below take effect when you save; test results are written by the plugin and are not affected by that button.",
|
|
5380
|
+
"row.poolSelectedCount": "Selected {count} / {total}",
|
|
5381
|
+
"row.poolSelectAll": "All",
|
|
5382
|
+
"row.poolSelectNone": "None",
|
|
5383
|
+
"row.poolColumnAccount": "Account",
|
|
5384
|
+
"row.poolColumnCredits": "Credits",
|
|
5385
|
+
"row.poolColumnProbe": "Target model",
|
|
5386
|
+
"row.poolColumnCheckin": "Check-in",
|
|
5387
|
+
"row.poolGeneralLabel": "General",
|
|
5388
|
+
"row.poolWorkHint": "Work credits are not spendable by DSH — only General powers chat.",
|
|
5389
|
+
"row.poolCreditNone": "—",
|
|
5390
|
+
"row.poolTestedAt": "tested {at}",
|
|
5391
|
+
"row.poolNeverTested": "not tested yet",
|
|
5392
|
+
"row.poolRetryAt": "retry {at}",
|
|
5393
|
+
"row.poolRetryUnknown": "retry time unknown",
|
|
5394
|
+
"row.poolTargetCurrentPreferred": "Current: your choice {model}",
|
|
5395
|
+
"row.poolTargetCurrentFree": "Current: free model {model}",
|
|
5396
|
+
"row.poolTargetStale": "Model {model} is no longer offered by this panel",
|
|
5397
|
+
"row.poolTargetStaleOption": "{model} (no longer offered)",
|
|
5398
|
+
"row.poolTargetStaleClear": "Back to automatic",
|
|
5399
|
+
"row.poolTargetNone": "No free model available for this panel",
|
|
5400
|
+
"row.poolTargetNoneHint": "The pool can only pick a free model; none resolves right now, so tests are disabled.",
|
|
5401
|
+
"row.poolTargetAutoOption": "Automatic (free model)",
|
|
5402
|
+
"row.poolTargetLabel": "Target test model",
|
|
5403
|
+
"row.poolTargetHint": "The model a probe test uses.",
|
|
5404
|
+
"row.poolProbeSizeLabel": "Probe request size",
|
|
5405
|
+
"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.",
|
|
5406
|
+
"row.poolProbeSizeDefault": "Minimal (default)",
|
|
5407
|
+
"row.poolProbeSizeOption": "{wan}0k tokens",
|
|
5408
|
+
"row.poolStar": "Star the repo",
|
|
5409
|
+
"row.poolSettingsTitle": "Pool settings",
|
|
5410
|
+
"row.poolManualAccountLabel": "Manually billed account",
|
|
5411
|
+
"row.poolManualAccountHint": "Used while the pool is off. With it on, the Host picks by credits.",
|
|
5412
|
+
"row.poolManualAccountNone": "No account in effect",
|
|
5413
|
+
"row.poolDirty": "unsaved",
|
|
5414
|
+
"row.poolSaved": "Save",
|
|
5415
|
+
"row.poolSaving": "Saving…",
|
|
5416
|
+
"row.poolDiscard": "Discard",
|
|
5417
|
+
"row.poolSaveFailed": "Save failed: {message}",
|
|
5418
|
+
"row.poolGhostMembers": "{count} saved member(s) no longer resolve to a local sign-in: {names}",
|
|
5419
|
+
"row.poolGhostRemove": "Remove {account}",
|
|
5420
|
+
"row.poolGhostRemoveHint": "Forget this panel's binding to {account}.",
|
|
5421
|
+
"row.poolUnsavedMembers": "Unsaved membership changes — Save below to apply.",
|
|
5422
|
+
"row.poolRowTest": "Test",
|
|
5423
|
+
"row.poolRowRemove": "Remove",
|
|
5424
|
+
"row.poolRowRemoveAria": "Remove {accountName} from this panel",
|
|
5425
|
+
"row.poolRowRemoving": "Removing…",
|
|
5426
|
+
"row.poolRowRemoved": "Removed {accountName} from this panel",
|
|
5427
|
+
"row.poolLogTitle": "Activity",
|
|
5428
|
+
"row.poolLogClear": "Clear",
|
|
5429
|
+
"row.poolLogEmpty": "No pool activity yet.",
|
|
5430
|
+
"row.poolLogCheckinStart": "Check-in started for {count} account(s).",
|
|
5431
|
+
"row.poolLogTestStart": "Test started for {count} account(s) against {model}.",
|
|
5432
|
+
"row.poolLogCheckinClaimed": "{accountName} checked in.",
|
|
5433
|
+
"row.poolLogCheckinAlready": "{accountName} already checked in today.",
|
|
5434
|
+
"row.poolLogCheckinFailed": "{accountName} check-in failed: {message}",
|
|
5435
|
+
"row.poolLogCheckinDone": "Check-in finished.",
|
|
5436
|
+
"row.poolLogTestRow": "{accountName}: {outcome}.",
|
|
5437
|
+
"row.poolLogTestRowMalformed": "{accountName}: malformed result.",
|
|
5438
|
+
"row.poolLogTestDone": "Test finished.",
|
|
5439
|
+
"row.poolLogBatchFailed": "Pool action failed: {message}",
|
|
5440
|
+
"row.poolRegionNote": "Each panel has its own pool; enterprise shares the CN account's sign-ins.",
|
|
5441
|
+
"row.poolSelectAria": "Include {accountName} in the pool",
|
|
5442
|
+
"row.poolCheckedIn": "Checked in",
|
|
5443
|
+
"row.poolNotCheckedIn": "Not checked in",
|
|
5444
|
+
"row.poolCheckinUnknown": "Unknown",
|
|
5445
|
+
"row.poolDeviceUsed": "device used",
|
|
5446
|
+
"row.poolTestingPerRow": "testing…",
|
|
5447
|
+
"row.poolProbeOk": "works",
|
|
5448
|
+
"row.poolProbeRateLimited": "rate limited",
|
|
5449
|
+
"row.poolProbeModelBusy": "model busy",
|
|
5450
|
+
"row.poolProbeOutOfCredit": "out of credit",
|
|
5451
|
+
"row.poolProbeCredentialRejected": "sign-in rejected",
|
|
5452
|
+
"row.poolProbePolicyRejected": "policy rejected",
|
|
5453
|
+
"row.poolProbePlanGated": "needs a higher plan",
|
|
5454
|
+
"row.poolProbeUnavailable": "unavailable",
|
|
5455
|
+
"row.poolProbeNotFound": "not served",
|
|
5456
|
+
"row.poolProbeFailed": "failed",
|
|
5457
|
+
"row.poolProbeSourceUnknown": "reason unknown",
|
|
5458
|
+
"row.poolProbeSourceLive": "from a live request",
|
|
5459
|
+
"row.poolProbeSourceTest": "from a test",
|
|
5460
|
+
"row.poolProbeModel": "model {model}",
|
|
5461
|
+
"row.poolProbeModelUnknown": "model unknown",
|
|
5462
|
+
"row.poolExcludedUnusable": "unusable",
|
|
5463
|
+
"row.poolTokenExpired": "sign-in expired",
|
|
5464
|
+
"row.poolExcludedRateLimited": "rate limited",
|
|
5465
|
+
"row.poolExcludedModelBusy": "model busy",
|
|
5466
|
+
"row.poolExcludedOutOfCredit": "out of credit",
|
|
5467
|
+
"row.poolExcludedCredentialRejected": "sign-in rejected",
|
|
5468
|
+
"row.poolExcludedPolicyRejected": "policy rejected",
|
|
5469
|
+
"row.poolExcludedPlanGated": "needs a higher plan",
|
|
5470
|
+
"row.poolExcludedNotFound": "not served",
|
|
5471
|
+
"row.poolExcludedUnavailable": "unavailable",
|
|
5472
|
+
"row.poolExcludedFailed": "failed",
|
|
5473
|
+
"row.poolAgoNow": "just now",
|
|
5474
|
+
"row.poolAgoMin": "{n} min ago",
|
|
5475
|
+
"row.poolAgoHour": "{n} h ago",
|
|
5476
|
+
"row.poolAgoDay": "{n} d ago",
|
|
5477
|
+
"row.poolErrOrigin": "the page is not the plugin's own page, so the request was refused",
|
|
5478
|
+
"row.poolErrUnavailable": "the pool is unavailable right now",
|
|
5479
|
+
"row.poolErrMethod": "the pool does not accept that request method",
|
|
5480
|
+
"row.poolErrBadRequest": "the pool refused the request",
|
|
5481
|
+
"row.poolErrNoMembers": "the pool has no members to act on",
|
|
5482
|
+
"row.poolErrNoLiveMembers": "every member is unavailable — nothing can serve",
|
|
5483
|
+
"row.poolErrCheckinUnsupported": "this panel does not offer a daily check-in",
|
|
5484
|
+
"row.poolErrTargetStale": "the saved target model is no longer offered",
|
|
5485
|
+
"row.poolErrNoFreeModel": "no free model is available for this panel",
|
|
5486
|
+
"row.poolErrConflict": "the pool refused the request",
|
|
5487
|
+
"row.poolErrGeneric": "pool request failed (HTTP {status})",
|
|
5488
|
+
"row.poolCheckingIn": "Checking in…",
|
|
5489
|
+
"row.poolSettingsHint": "The switch and the target model are preferences — save them. Test, check in and remove act at once.",
|
|
5490
|
+
"row.poolGhostRemoveAria": "Remove the saved but unreachable account {accountName} from this panel",
|
|
5491
|
+
"row.poolLogSaved": "Pool settings saved.",
|
|
5492
|
+
"row.poolLogSaveFailed": "Could not save the pool settings.",
|
|
5493
|
+
"row.poolLogNoMembers": "Nothing to do — no pool members are selected.",
|
|
5494
|
+
"row.poolLogDiscarded": "Discarded the unsaved pool changes.",
|
|
5495
|
+
"row.poolLogGeneric": "the pool answered nothing usable",
|
|
5496
|
+
"row.poolLogRowTesting": "Testing {accountName}…",
|
|
5497
|
+
"row.poolLogTargetCleared": "Returned the test target to automatic.",
|
|
5498
|
+
"row.poolRowRemoveFailed": "the pool did not confirm the removal",
|
|
5499
|
+
"row.poolRowRemoveError": "Could not remove {accountName}: {message}",
|
|
3811
5500
|
"row.cheer": "Star on GitHub"
|
|
3812
5501
|
};
|
|
3813
5502
|
const zh = {
|
|
@@ -3816,8 +5505,10 @@ window.__ModuleLoader__.load({
|
|
|
3816
5505
|
"row.expand": "展开",
|
|
3817
5506
|
"row.collapse": "收起",
|
|
3818
5507
|
"row.tabCn": "国内版",
|
|
5508
|
+
"row.tabEnterprise": "企业版",
|
|
3819
5509
|
"row.tabAi": "国际版",
|
|
3820
|
-
"row.tabHint": "
|
|
5510
|
+
"row.tabHint": "国内版、企业版、国际版各有账号、用量与模型,可同时启用。企业版与国内版共用同一份 Trae CN 登录。",
|
|
5511
|
+
"row.tabEnterpriseHint": "企业版用同一个 Trae CN 账号走企业租户池计费,而不是个人积分池。它不会回落:被拒时如实报错,绝不悄悄花掉个人额度。",
|
|
3821
5512
|
"row.tabSwitchHint": "关闭该供应商:其模型不再提供给 DSH;账号与设置保留,可随时恢复。",
|
|
3822
5513
|
"row.tabSwitchAria": "是否向 DSH 提供{region}供应商",
|
|
3823
5514
|
"row.tabOffNotice": "该供应商已关闭,模型不会提供给 DSH。账号与设置均已保留。",
|
|
@@ -3899,6 +5590,8 @@ window.__ModuleLoader__.load({
|
|
|
3899
5590
|
"row.accountColumnValue": "额度",
|
|
3900
5591
|
"row.accountColumnCheckin": "签到",
|
|
3901
5592
|
"row.accountBalance": "积分 {credits}",
|
|
5593
|
+
"row.balanceConsoleOnly": "见企业版控制台",
|
|
5594
|
+
"row.enterpriseCreditsNotice": "企业版额度不在这里显示。Trae 客户端读不到企业租户的余额(所有企业额度接口都返回 404),只能到企业版控制台(console.enterprise.trae.cn)查看。这里也刻意不显示个人积分:那是消费池的数字,放在企业版标签下会被误认为企业额度。",
|
|
3902
5595
|
"row.sourceDsh": "插件副本",
|
|
3903
5596
|
"row.modelsTitle": "模型",
|
|
3904
5597
|
"row.modelsSummary": "已启用 {count} 个",
|
|
@@ -3976,6 +5669,151 @@ window.__ModuleLoader__.load({
|
|
|
3976
5669
|
"row.modelReasoningSupported": "推理:支持",
|
|
3977
5670
|
"row.modelUnknown": "未知",
|
|
3978
5671
|
"row.modelCapabilityPending": "图片输入跟随 Trae 自身的多模态标注,并叠加你手动勾选的模型。",
|
|
5672
|
+
"row.poolTitle": "账号池",
|
|
5673
|
+
"row.poolEnabled": "启用账号池",
|
|
5674
|
+
"row.poolEnabledHint": "账号池按积分与可用性挑选本面板请求的计费账号;关闭时由手动绑定的账号计费。",
|
|
5675
|
+
"row.poolCurrentHeader": "当前计费:{account}",
|
|
5676
|
+
"row.poolCurrentBadge": "派单中",
|
|
5677
|
+
"row.poolMember": "已在池中",
|
|
5678
|
+
"row.poolNotMember": "未入池",
|
|
5679
|
+
"row.poolCheckinAll": "一键签到所有账号",
|
|
5680
|
+
"row.poolBusyHint": "逐个处理账号中…",
|
|
5681
|
+
"row.poolTestAll": "一键测试所有账号",
|
|
5682
|
+
"row.poolTesting": "测速中…",
|
|
5683
|
+
"row.poolOffNotice": "账号池已关闭——由绑定的账号计费。开启上方开关即可让账号池派单。",
|
|
5684
|
+
"row.poolNoCandidate": "当前没有可用的池成员能为本面板提供请求。",
|
|
5685
|
+
"row.poolNoCandidateEmpty": "尚未勾选任何池成员。",
|
|
5686
|
+
"row.poolNoCandidateEmptyHint": "在成员列表中勾选账号即可加入账号池。",
|
|
5687
|
+
"row.poolNoCandidateUnusable": "所有已选成员当前均不可用。",
|
|
5688
|
+
"row.poolNoCandidateHint": "请求会继续落在已保存的账号上,直到有成员恢复可用。",
|
|
5689
|
+
"row.poolMembersTitle": "本池包含的账号",
|
|
5690
|
+
"row.poolCardHint": "只有勾选的账号才会被签到、测试和轮换。默认一个都不勾 —— 请勾选你要的账号,然后保存。",
|
|
5691
|
+
"row.poolRescan": "重新检测账号",
|
|
5692
|
+
"row.poolToolsLabel": "导出 / 导入账号",
|
|
5693
|
+
"row.poolCreditExpiry": "{when} 过期",
|
|
5694
|
+
"row.poolProbeMeasured": "测得 {when}",
|
|
5695
|
+
"row.poolProbeAt": "测试于 {when}",
|
|
5696
|
+
"row.poolSettingsNote": "下面是偏好设置:点保存才生效;测试结果由插件自动写入,不受这个保存按钮影响。",
|
|
5697
|
+
"row.poolSelectedCount": "已选 {count} / {total}",
|
|
5698
|
+
"row.poolSelectAll": "全部",
|
|
5699
|
+
"row.poolSelectNone": "清空",
|
|
5700
|
+
"row.poolColumnAccount": "账号",
|
|
5701
|
+
"row.poolColumnCredits": "积分",
|
|
5702
|
+
"row.poolColumnProbe": "目标模型",
|
|
5703
|
+
"row.poolColumnCheckin": "签到",
|
|
5704
|
+
"row.poolGeneralLabel": "通用",
|
|
5705
|
+
"row.poolWorkHint": "工作积分不能由 DSH 消费——只有通用积分用于对话。",
|
|
5706
|
+
"row.poolCreditNone": "—",
|
|
5707
|
+
"row.poolTestedAt": "测于 {at}",
|
|
5708
|
+
"row.poolNeverTested": "尚未测速",
|
|
5709
|
+
"row.poolRetryAt": "{at} 后重试",
|
|
5710
|
+
"row.poolRetryUnknown": "重试时间未知",
|
|
5711
|
+
"row.poolTargetCurrentPreferred": "当前:你选定的模型 {model}",
|
|
5712
|
+
"row.poolTargetCurrentFree": "当前:免费模型 {model}",
|
|
5713
|
+
"row.poolTargetStale": "模型 {model} 已不再由本面板提供",
|
|
5714
|
+
"row.poolTargetStaleOption": "{model}(已下架)",
|
|
5715
|
+
"row.poolTargetStaleClear": "改回自动",
|
|
5716
|
+
"row.poolTargetNone": "本面板暂无可用免费模型",
|
|
5717
|
+
"row.poolTargetNoneHint": "账号池只会挑免费模型;当前无法解析出可用模型,测速暂被禁用。",
|
|
5718
|
+
"row.poolTargetAutoOption": "自动(免费模型)",
|
|
5719
|
+
"row.poolTargetLabel": "目标测速模型",
|
|
5720
|
+
"row.poolTargetHint": "探针测速所用的模型。",
|
|
5721
|
+
"row.poolProbeSizeLabel": "测试请求体积",
|
|
5722
|
+
"row.poolProbeSizeHint": "测试时发送多少文本。只发一个词的探针,只能证明「这个账号能回答一个词」——如果上游拒绝长请求,问题只会在你真实对话时才出现。填得越多越接近真实对话,每个账号每次测试消耗的积分也越多。",
|
|
5723
|
+
"row.poolProbeSizeDefault": "最小请求(默认)",
|
|
5724
|
+
"row.poolProbeSizeOption": "{wan} 万 token",
|
|
5725
|
+
"row.poolStar": "鼓励一下",
|
|
5726
|
+
"row.poolSettingsTitle": "账号池设置",
|
|
5727
|
+
"row.poolManualAccountLabel": "手动计费账号",
|
|
5728
|
+
"row.poolManualAccountHint": "账号池关闭时由它计费;开启后由 Host 按积分挑选。",
|
|
5729
|
+
"row.poolManualAccountNone": "当前无生效账号",
|
|
5730
|
+
"row.poolDirty": "未保存",
|
|
5731
|
+
"row.poolSaved": "保存",
|
|
5732
|
+
"row.poolSaving": "保存中…",
|
|
5733
|
+
"row.poolDiscard": "放弃",
|
|
5734
|
+
"row.poolSaveFailed": "保存失败:{message}",
|
|
5735
|
+
"row.poolGhostMembers": "{count} 个已保存成员不再对应本机登录:{names}",
|
|
5736
|
+
"row.poolGhostRemove": "移除 {account}",
|
|
5737
|
+
"row.poolGhostRemoveHint": "忘记本面板对 {account} 的绑定。",
|
|
5738
|
+
"row.poolUnsavedMembers": "成员改动尚未保存——请点击下方保存。",
|
|
5739
|
+
"row.poolRowTest": "测速",
|
|
5740
|
+
"row.poolRowRemove": "移除",
|
|
5741
|
+
"row.poolRowRemoveAria": "从本面板移除 {accountName}",
|
|
5742
|
+
"row.poolRowRemoving": "移除中…",
|
|
5743
|
+
"row.poolRowRemoved": "已从本面板移除 {accountName}",
|
|
5744
|
+
"row.poolLogTitle": "活动记录",
|
|
5745
|
+
"row.poolLogClear": "清空",
|
|
5746
|
+
"row.poolLogEmpty": "暂无账号池活动。",
|
|
5747
|
+
"row.poolLogCheckinStart": "开始为 {count} 个账号签到。",
|
|
5748
|
+
"row.poolLogTestStart": "开始为 {count} 个账号测速({model})。",
|
|
5749
|
+
"row.poolLogCheckinClaimed": "{accountName} 已签到。",
|
|
5750
|
+
"row.poolLogCheckinAlready": "{accountName} 今日已签到。",
|
|
5751
|
+
"row.poolLogCheckinFailed": "{accountName} 签到失败:{message}",
|
|
5752
|
+
"row.poolLogCheckinDone": "签到完成。",
|
|
5753
|
+
"row.poolLogTestRow": "{accountName}:{outcome}。",
|
|
5754
|
+
"row.poolLogTestRowMalformed": "{accountName}:结果异常。",
|
|
5755
|
+
"row.poolLogTestDone": "测速完成。",
|
|
5756
|
+
"row.poolLogBatchFailed": "账号池操作失败:{message}",
|
|
5757
|
+
"row.poolRegionNote": "每个面板各有自己的账号池;企业版与国内版共用同一 Trae CN 账号登录。",
|
|
5758
|
+
"row.poolSelectAria": "把 {accountName} 加入账号池",
|
|
5759
|
+
"row.poolCheckedIn": "已签到",
|
|
5760
|
+
"row.poolNotCheckedIn": "未签到",
|
|
5761
|
+
"row.poolCheckinUnknown": "未知",
|
|
5762
|
+
"row.poolDeviceUsed": "本设备今日已用",
|
|
5763
|
+
"row.poolTestingPerRow": "测速中…",
|
|
5764
|
+
"row.poolProbeOk": "正常",
|
|
5765
|
+
"row.poolProbeRateLimited": "被限流",
|
|
5766
|
+
"row.poolProbeModelBusy": "模型繁忙",
|
|
5767
|
+
"row.poolProbeOutOfCredit": "积分不足",
|
|
5768
|
+
"row.poolProbeCredentialRejected": "登录凭据被拒",
|
|
5769
|
+
"row.poolProbePolicyRejected": "被策略拒绝",
|
|
5770
|
+
"row.poolProbePlanGated": "需要更高套餐",
|
|
5771
|
+
"row.poolProbeUnavailable": "不可用",
|
|
5772
|
+
"row.poolProbeNotFound": "上游不提供",
|
|
5773
|
+
"row.poolProbeFailed": "失败",
|
|
5774
|
+
"row.poolProbeSourceUnknown": "原因未知",
|
|
5775
|
+
"row.poolProbeSourceLive": "来自实时请求",
|
|
5776
|
+
"row.poolProbeSourceTest": "来自测速",
|
|
5777
|
+
"row.poolProbeModel": "模型 {model}",
|
|
5778
|
+
"row.poolProbeModelUnknown": "模型未知",
|
|
5779
|
+
"row.poolExcludedUnusable": "不可用",
|
|
5780
|
+
"row.poolTokenExpired": "登录已失效",
|
|
5781
|
+
"row.poolExcludedRateLimited": "被限流",
|
|
5782
|
+
"row.poolExcludedModelBusy": "模型繁忙",
|
|
5783
|
+
"row.poolExcludedOutOfCredit": "积分不足",
|
|
5784
|
+
"row.poolExcludedCredentialRejected": "登录凭据被拒",
|
|
5785
|
+
"row.poolExcludedPolicyRejected": "被策略拒绝",
|
|
5786
|
+
"row.poolExcludedPlanGated": "需要更高套餐",
|
|
5787
|
+
"row.poolExcludedNotFound": "上游不提供",
|
|
5788
|
+
"row.poolExcludedUnavailable": "不可用",
|
|
5789
|
+
"row.poolExcludedFailed": "失败",
|
|
5790
|
+
"row.poolAgoNow": "刚刚",
|
|
5791
|
+
"row.poolAgoMin": "{n} 分钟前",
|
|
5792
|
+
"row.poolAgoHour": "{n} 小时前",
|
|
5793
|
+
"row.poolAgoDay": "{n} 天前",
|
|
5794
|
+
"row.poolErrOrigin": "页面并非插件自身页面,请求因而不被接受",
|
|
5795
|
+
"row.poolErrUnavailable": "账号池当前不可用",
|
|
5796
|
+
"row.poolErrMethod": "账号池不接受该请求方法",
|
|
5797
|
+
"row.poolErrBadRequest": "账号池拒绝了该请求",
|
|
5798
|
+
"row.poolErrNoMembers": "账号池没有任何成员可处理",
|
|
5799
|
+
"row.poolErrNoLiveMembers": "所有成员均不可用——没有可派单的账号",
|
|
5800
|
+
"row.poolErrCheckinUnsupported": "本面板不提供每日签到",
|
|
5801
|
+
"row.poolErrTargetStale": "已保存的目标模型已下架",
|
|
5802
|
+
"row.poolErrNoFreeModel": "本面板没有可用的免费模型",
|
|
5803
|
+
"row.poolErrConflict": "账号池拒绝了该请求",
|
|
5804
|
+
"row.poolErrGeneric": "账号池请求失败(HTTP {status})",
|
|
5805
|
+
"row.poolCheckingIn": "签到中…",
|
|
5806
|
+
"row.poolSettingsHint": "开关与目标模型是偏好,改完要保存;行内的测速、签到与移除立即生效。",
|
|
5807
|
+
"row.poolGhostRemoveAria": "把已保存但本机无法登录的账号 {accountName} 移出本面板",
|
|
5808
|
+
"row.poolLogSaved": "账号池设置已保存。",
|
|
5809
|
+
"row.poolLogSaveFailed": "无法保存账号池设置。",
|
|
5810
|
+
"row.poolLogNoMembers": "没有可处理的内容——尚未勾选任何池成员。",
|
|
5811
|
+
"row.poolLogDiscarded": "已放弃未保存的账号池修改。",
|
|
5812
|
+
"row.poolLogGeneric": "账号池未返回可用的结果",
|
|
5813
|
+
"row.poolLogRowTesting": "正在测速 {accountName}…",
|
|
5814
|
+
"row.poolLogTargetCleared": "测速目标已改回自动。",
|
|
5815
|
+
"row.poolRowRemoveFailed": "账号池未确认移除",
|
|
5816
|
+
"row.poolRowRemoveError": "无法移除 {accountName}:{message}",
|
|
3979
5817
|
"row.cheer": "鼓励一下"
|
|
3980
5818
|
};
|
|
3981
5819
|
//#endregion
|