dsh-connect-trae 3.9.1 → 3.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/client.js CHANGED
@@ -20,11 +20,6 @@ window.__ModuleLoader__.load({
20
20
  }
21
21
  //#endregion
22
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
- */
28
23
  /** Plugin-owned usage endpoint consumed by its browser half. */
29
24
  const TRAE_USAGE_PATH = "/plugins/dsh-connect-trae/usage";
30
25
  /** Plugin-owned live model refresh endpoint. */
@@ -587,6 +582,7 @@ window.__ModuleLoader__.load({
587
582
  .dsm-trae-account-checkin{appearance:none;font:inherit;cursor:pointer;white-space:nowrap;border:1px solid rgba(86,134,254,.4);border-radius:7px;padding:2px 9px;font-size:12px;line-height:18px;font-weight:500;color:var(--dsw-alias-brand-primary,#5686fe);background:rgba(86,134,254,.08)}
588
583
  .dsm-trae-account-checkin:hover:not(:disabled){background:rgba(86,134,254,.16)}
589
584
  .dsm-trae-account-checkin:focus-visible{outline:2px solid var(--dsw-alias-brand-primary,#5686fe);outline-offset:1px}
585
+ .dsm-trae-account-checkin-state{font-size:12px;line-height:18px;color:var(--dsw-alias-label-tertiary,#9aa0a8);white-space:nowrap}
590
586
  /* A disabled check-in still STATES something ("claimed", "device used"), so it
591
587
  stays readable 鈥?clearly inactive, not invisible. */
592
588
  .dsm-trae-account-checkin:disabled{opacity:.55;cursor:default;border-color:transparent;color:var(--dsw-alias-label-tertiary,#9aa0a8);background:transparent}
@@ -658,6 +654,8 @@ window.__ModuleLoader__.load({
658
654
  must stay mounted to be clicked. */
659
655
  .dsm-trae-transfer{margin-top:12px;padding-top:10px;border-top:1px solid var(--dsw-alias-border-l2,rgba(127,127,127,.2))}
660
656
  .dsm-trae-transfer-label{margin:0 0 6px;font-size:11px;line-height:16px;color:var(--dsw-alias-label-secondary,GrayText)}
657
+ .dsm-trae-transfer-flat{margin-top:0;padding-top:0;border-top:0}
658
+ .dsm-trae-transfer-flat .dsm-trae-transfer-actions{flex-wrap:nowrap}
661
659
  .dsm-trae-transfer-actions{display:flex;flex-wrap:wrap;gap:8px}
662
660
  .dsm-trae-transfer-file{display:none}
663
661
  .dsm-trae-transfer-status{margin:6px 0 0;font-size:11px;line-height:16px}
@@ -786,15 +784,18 @@ window.__ModuleLoader__.load({
786
784
  /* Removing an account from the pool is destructive (it drops THIS panel's saved
787
785
  membership), so the hover state takes the error tone rather than the neutral
788
786
  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)}
787
+ /* The destructive Delete is visually heavier than 移除 from the start (error
788
+ colour idle), so the row shows two distinct actions instead of two greys. */
789
+ .dsm-trae-pool-delete{color:var(--dsw-alias-state-error-primary,#d92d20)}
790
+ .dsm-trae-pool-delete:hover:not(:disabled){background:rgba(217,45,32,.08)}
791
+ .dsm-trae-pool-delete-hint{margin:0;padding:8px var(--dsm-trae-pool-pad-x);border:1px solid var(--dsw-alias-state-error-primary,#d92d20);border-radius:10px;font-size:12px;line-height:18px;color:var(--dsw-alias-label-secondary,#c6c9d0);background:var(--dsw-alias-bg-layer-3,#2a2c33)}
791
792
  .dsm-trae-pool-busy{font-size:12px;line-height:18px;color:var(--dsw-alias-label-tertiary,#9aa0a8)}
793
+ .dsm-trae-pool-action-note{margin:0;font-size:12px;line-height:18px;color:var(--dsw-alias-label-tertiary,#9aa0a8)}
792
794
  .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}
795
+ .dsm-trae-pool-card-head{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:14px var(--dsm-trae-pool-pad-x) 12px}
794
796
  .dsm-trae-pool-card-title{margin:0;font-size:13px;line-height:18px;font-weight:600;color:var(--dsw-alias-label-primary,#e6e6e6)}
795
797
  .dsm-trae-pool-card-tools{display:flex;align-items:center;gap:8px;flex:none}
796
798
  .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
799
  .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
800
  .dsm-trae-pool-members{display:flex;flex-direction:column;gap:10px}
800
801
  /* Prose, not a box. Every explanation in this section is one line of muted
@@ -831,7 +832,7 @@ window.__ModuleLoader__.load({
831
832
  .dsm-trae-pool-subtitle{margin:0;font-size:12px;font-weight:600;line-height:18px;color:var(--dsw-alias-label-primary,#e6e6e6)}
832
833
  .dsm-trae-pool-subnote{margin:2px 0 0;font-size:12px;line-height:18px;color:var(--dsw-alias-label-tertiary,#9aa0a8)}
833
834
  .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-select-actions .dsm-btn,.dsm-trae-pool-row-actions .dsm-btn{padding:4px 10px;font-size:12px;line-height:1.4}
835
836
  .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
837
  .dsm-trae-pool-table-no-checkin{--dsm-trae-pool-cols:minmax(0,1.4fr) 128px minmax(0,1.2fr) 128px}
837
838
  .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))}
@@ -898,8 +899,12 @@ window.__ModuleLoader__.load({
898
899
  /* "Every member is unusable" is the one case that IS the pool's fault, so it
899
900
  keeps the warn tone the base class sets. */
900
901
  .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}
902
+ /* The sign-in/refresh workflow sits in the settings block: it keeps the warn
903
+ tone (getting it wrong loses an account) but is not a fault of the pool. */
904
+ .dsm-trae-pool-warning-workflow{margin:0 0 2px}
905
+ .dsm-trae-pool-save-row{display:flex;align-items:center;justify-content:space-between;gap:12px;border-top:1px solid var(--dsw-alias-border-l2,#36373b);padding-top:12px}
906
+ .dsm-trae-pool-save-buttons{display:flex;align-items:center;justify-content:flex-end;gap:8px}
907
+ .dsm-trae-pool-save-row .dsm-btn{padding:5px 14px;font-size:13px;line-height:1.5}
903
908
  .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
909
  .dsm-trae-pool-save-error{margin:0;font-size:12px;line-height:18px;color:var(--dsw-alias-state-error-primary,#d92d20)}
905
910
  .dsm-trae-pool-target{display:flex;flex-direction:column;gap:6px;font-size:12px;line-height:18px;color:var(--dsw-alias-label-secondary,#c6c9d0)}
@@ -1272,34 +1277,74 @@ window.__ModuleLoader__.load({
1272
1277
  return t("row.poolErrGeneric", { status: String(status) });
1273
1278
  }
1274
1279
  /**
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).
1280
+ * Handle ONE NDJSON line of a `test` answer. A non-JSON line is skipped; a
1281
+ * `{reason:'pool-failed'}` line reports the failure and counts as "saw
1282
+ * something". Returns whether the line was usable, so the caller can tell "the
1283
+ * route answered nothing parseable" (a bug to surface) from "not finished yet".
1279
1284
  */
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"));
1285
+ function applyTestLine(line, t, onRow, onFailure) {
1286
+ if (line.trim() === "") return false;
1287
+ let parsed;
1288
+ try {
1289
+ parsed = JSON.parse(line);
1290
+ } catch {
1291
+ return false;
1292
+ }
1293
+ if (parsed.row !== void 0) {
1294
+ onRow(parsed.row);
1295
+ return true;
1296
+ }
1297
+ if (parsed.rows !== void 0) {
1298
+ for (const r of parsed.rows) onRow(r);
1299
+ return true;
1300
+ }
1301
+ if (parsed.reason === "pool-failed") {
1302
+ onFailure(parsed.error ?? t("row.poolLogGeneric"));
1303
+ return true;
1304
+ }
1305
+ return false;
1306
+ }
1307
+ /**
1308
+ * Read the answer line by line AS IT ARRIVES.
1309
+ *
1310
+ * The Host streams one line per account, but `response.text()` waits for the
1311
+ * whole body: with a slow probe the card sat on "逐个处理账号中…" and looked
1312
+ * frozen (a single member could take half a minute). Reading the body stream
1313
+ * makes each finished account appear immediately, and an empty/failed stream
1314
+ * still ends the loop instead of hanging the busy state forever.
1315
+ */
1316
+ async function* testLinesOf(response) {
1317
+ const body = response.body;
1318
+ if (body === null) {
1319
+ for (const line of (await response.text()).split(/\r?\n/)) yield line;
1320
+ return;
1321
+ }
1322
+ const reader = body.getReader();
1323
+ const decoder = new TextDecoder();
1324
+ let buffer = "";
1325
+ for (;;) {
1326
+ const { done, value } = await reader.read();
1327
+ if (done) break;
1328
+ buffer += decoder.decode(value, { stream: true });
1329
+ let cut = buffer.indexOf("\n");
1330
+ while (cut >= 0) {
1331
+ yield buffer.slice(0, cut);
1332
+ buffer = buffer.slice(cut + 1);
1333
+ cut = buffer.indexOf("\n");
1299
1334
  }
1300
1335
  }
1301
- return sawAny;
1336
+ buffer += decoder.decode();
1337
+ if (buffer.trim() !== "") yield buffer;
1302
1338
  }
1339
+ /**
1340
+ * How long one `test` request may take before the card gives up.
1341
+ *
1342
+ * A probe has its own upstream deadline, but a stalled connection (a restarting
1343
+ * Host, a dropped keep-alive) has none: without this the run button stays
1344
+ * disabled until the page is reloaded, which reads as "测速卡住了". Sized for a
1345
+ * batch: a few seconds per account plus room for the slowest upstream.
1346
+ */
1347
+ const TEST_TIMEOUT_MS = 18e4;
1303
1348
  function AccountPool({ panel, pool, models, t, settingsScope, onChanged, onRefresh, tools, topActions, details, claims, onClaim, deviceClaimed = false, onBind, bindingBusy }) {
1304
1349
  const [settingsTick, setSettingsTick] = (0, react.useState)(0);
1305
1350
  const saved = (0, react.useMemo)(() => {
@@ -1336,7 +1381,8 @@ window.__ModuleLoader__.load({
1336
1381
  const [saveError, setSaveError] = (0, react.useState)(void 0);
1337
1382
  const [testing, setTesting] = (0, react.useState)(void 0);
1338
1383
  const [rows, setRows] = (0, react.useState)({});
1339
- const [removed, setRemoved] = (0, react.useState)({});
1384
+ const [deletingIds, setDeletingIds] = (0, react.useState)(() => /* @__PURE__ */ new Set());
1385
+ const [deleteHint, setDeleteHint] = (0, react.useState)(void 0);
1340
1386
  const [log, setLog] = (0, react.useState)([]);
1341
1387
  const [now, setNow] = (0, react.useState)(Date.now());
1342
1388
  const logId = (0, react.useRef)(0);
@@ -1364,10 +1410,14 @@ window.__ModuleLoader__.load({
1364
1410
  const ghosts = (0, react.useMemo)(() => {
1365
1411
  const names = pool.memberNames ?? {};
1366
1412
  const result = [];
1367
- for (const id of saved.memberAccountIds ?? []) if (!knownIds.has(id)) result.push({
1368
- accountId: id,
1369
- accountName: names[id] ?? id
1370
- });
1413
+ for (const id of saved.memberAccountIds ?? []) {
1414
+ if (knownIds.has(id)) continue;
1415
+ const name = names[id];
1416
+ result.push({
1417
+ accountId: id,
1418
+ accountName: name === void 0 || name === "" ? id : name
1419
+ });
1420
+ }
1371
1421
  return result;
1372
1422
  }, [
1373
1423
  saved,
@@ -1376,12 +1426,27 @@ window.__ModuleLoader__.load({
1376
1426
  ]);
1377
1427
  const dirty = enabled !== saved.enabled || (targetModelId ?? "") !== (saved.targetModelId ?? "") || probeSupported && probeInputTokens !== saved.probeInputTokens || !sameIds([...selected], saved.memberAccountIds);
1378
1428
  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]);
1429
+ const savedMemberKey = (0, react.useMemo)(() => [...saved.memberAccountIds ?? []].sort().join("\0"), [saved]);
1380
1430
  (0, react.useEffect)(() => {
1381
1431
  setEnabled(saved.enabled);
1432
+ }, [panel, saved.enabled]);
1433
+ (0, react.useEffect)(() => {
1382
1434
  setTargetModelId(saved.targetModelId ?? "");
1435
+ }, [panel, saved.targetModelId]);
1436
+ (0, react.useEffect)(() => {
1383
1437
  setProbeInputTokens(saved.probeInputTokens);
1438
+ }, [panel, saved.probeInputTokens]);
1439
+ (0, react.useEffect)(() => {
1384
1440
  setSelected(new Set(saved.memberAccountIds ?? []));
1441
+ }, [panel, savedMemberKey]);
1442
+ const savedKey = (0, react.useMemo)(() => `${panel}:${saved.enabled}:${saved.targetModelId ?? ""}:${saved.probeInputTokens}:${savedMemberKey}`, [
1443
+ panel,
1444
+ saved.enabled,
1445
+ saved.targetModelId,
1446
+ saved.probeInputTokens,
1447
+ savedMemberKey
1448
+ ]);
1449
+ (0, react.useEffect)(() => {
1385
1450
  setSaveError(void 0);
1386
1451
  }, [savedKey]);
1387
1452
  const toggleMember = (accountId) => {
@@ -1485,14 +1550,24 @@ window.__ModuleLoader__.load({
1485
1550
  count: members.length,
1486
1551
  model: targetLabel
1487
1552
  }));
1553
+ const controller = new AbortController();
1554
+ const timer = setTimeout(() => {
1555
+ controller.abort();
1556
+ }, TEST_TIMEOUT_MS);
1488
1557
  try {
1489
- const text = await (await fetch(withTraePoolAction(panel, "test"), { method: "POST" })).text();
1558
+ const response = await fetch(withTraePoolAction(panel, "test"), {
1559
+ method: "POST",
1560
+ signal: controller.signal
1561
+ });
1490
1562
  const seen = /* @__PURE__ */ new Set();
1491
- if (!consumeTestNdjson(text, t, (r) => {
1563
+ let sawAny = false;
1564
+ for await (const line of testLinesOf(response)) if (applyTestLine(line, t, (r) => {
1565
+ const fresh = !seen.has(r.accountId);
1492
1566
  setRows((prev) => ({
1493
1567
  ...prev,
1494
1568
  [r.accountId]: r
1495
1569
  }));
1570
+ if (!fresh) return;
1496
1571
  seen.add(r.accountId);
1497
1572
  if (mountRef.current) setTesting({ done: seen.size });
1498
1573
  pushLog(outcomeTone(r.outcome), t("row.poolLogTestRow", {
@@ -1501,13 +1576,15 @@ window.__ModuleLoader__.load({
1501
1576
  }));
1502
1577
  }, (errorText) => {
1503
1578
  pushLog("error", t("row.poolLogBatchFailed", { message: errorText }));
1504
- })) throw new Error(t("row.poolLogGeneric"));
1579
+ })) sawAny = true;
1580
+ if (!sawAny) throw new Error(t("row.poolLogGeneric"));
1505
1581
  pushLog("ok", t("row.poolLogTestDone"));
1506
1582
  onRefresh?.();
1507
1583
  setNow(Date.now());
1508
1584
  } catch (error) {
1509
- pushLog("error", t("row.poolLogBatchFailed", { message: error instanceof Error ? error.message : String(error) }));
1585
+ pushLog("error", t(controller.signal.aborted ? "row.poolLogTimeout" : "row.poolLogBatchFailed", { message: error instanceof Error ? error.message : String(error) }));
1510
1586
  } finally {
1587
+ clearTimeout(timer);
1511
1588
  if (mountRef.current) {
1512
1589
  setBusy(void 0);
1513
1590
  setTesting(void 0);
@@ -1520,12 +1597,24 @@ window.__ModuleLoader__.load({
1520
1597
  setNow(Date.now());
1521
1598
  const targetLabel = targetModelId === "" ? t("row.poolTargetAutoOption") : models.find((m) => m.id === targetModelId)?.name ?? targetModelId;
1522
1599
  pushLog("info", t("row.poolLogRowTesting", { accountName: account.accountName }));
1600
+ const controller = new AbortController();
1601
+ const timer = setTimeout(() => {
1602
+ controller.abort();
1603
+ }, TEST_TIMEOUT_MS);
1523
1604
  try {
1524
- if (!consumeTestNdjson(await (await fetch(withTraePoolAccount(panel, "test", account.accountId), { method: "POST" })).text(), t, (r) => {
1605
+ const response = await fetch(withTraePoolAccount(panel, "test", account.accountId), {
1606
+ method: "POST",
1607
+ signal: controller.signal
1608
+ });
1609
+ let sawAny = false;
1610
+ const logged = /* @__PURE__ */ new Set();
1611
+ for await (const line of testLinesOf(response)) if (applyTestLine(line, t, (r) => {
1525
1612
  setRows((prev) => ({
1526
1613
  ...prev,
1527
1614
  [r.accountId]: r
1528
1615
  }));
1616
+ if (logged.has(r.accountId)) return;
1617
+ logged.add(r.accountId);
1529
1618
  pushLog(outcomeTone(r.outcome), t("row.poolLogTestRow", {
1530
1619
  accountName: r.accountName,
1531
1620
  outcome: outcomeLabel(r.outcome, t),
@@ -1533,48 +1622,149 @@ window.__ModuleLoader__.load({
1533
1622
  }));
1534
1623
  }, (errorText) => {
1535
1624
  pushLog("error", t("row.poolLogBatchFailed", { message: errorText }));
1536
- })) throw new Error(t("row.poolLogGeneric"));
1625
+ })) sawAny = true;
1626
+ if (!sawAny) throw new Error(t("row.poolLogGeneric"));
1537
1627
  onRefresh?.();
1538
1628
  setNow(Date.now());
1539
1629
  } catch (error) {
1540
- pushLog("error", t("row.poolLogBatchFailed", { message: error instanceof Error ? error.message : String(error) }));
1630
+ pushLog("error", t(controller.signal.aborted ? "row.poolLogTimeout" : "row.poolLogBatchFailed", { message: error instanceof Error ? error.message : String(error) }));
1541
1631
  } finally {
1632
+ clearTimeout(timer);
1542
1633
  if (mountRef.current) setBusy(void 0);
1543
1634
  }
1544
1635
  };
1545
- const removeAccount = async (account) => {
1546
- if (busy !== void 0) return;
1547
- setBusy("remove");
1548
- setRemoved((prev) => ({
1549
- ...prev,
1550
- [account.accountId]: false
1551
- }));
1636
+ /**
1637
+ * Persist the removal of ONE account out of this panel's membership.
1638
+ *
1639
+ * The slot written is the SAVED slot with ONLY `memberAccountIds` replaced —
1640
+ * never the draft's switch or target model, because the card's own hint
1641
+ * promises only the in-row actions take effect immediately
1642
+ * (「开关与目标模型是偏好,改完要保存」), and never the DOCUMENT's effective
1643
+ * target model either: that falls back to the free model when the user left
1644
+ * the choice automatic, so writing it back would pin a model they never chose.
1645
+ * The membership written is the DRAFT minus the removed id, so a checkbox the
1646
+ * user already toggled on another row is not silently discarded by the
1647
+ * settings re-read this write triggers.
1648
+ */
1649
+ const persistRemoval = async (accountId) => {
1650
+ const scope = settingsScope;
1651
+ if (scope === void 0) throw new Error(t("row.poolLogSaveFailed"));
1652
+ const base = regionPoolOf(scope.getSnapshot().value, panel);
1653
+ const next = {
1654
+ ...base,
1655
+ memberAccountIds: [...selected].filter((id) => id !== accountId)
1656
+ };
1657
+ await writeSettingsField(scope, "regions", nextRegionPool(scope.getSnapshot().value, panel, next), (readBack) => {
1658
+ const readPool = regionPoolOf(readBack, panel);
1659
+ return !readPool.memberAccountIds?.includes(accountId) && sameIds(readPool.memberAccountIds, next.memberAccountIds) && (readPool.enabled ?? false) === (base.enabled ?? false) && (readPool.targetModelId ?? "") === (base.targetModelId ?? "") && (readPool.probeInputTokens ?? 0) === (base.probeInputTokens ?? 0);
1660
+ });
1661
+ };
1662
+ /** Product names for the desktop apps a delete touched or was blocked by. */
1663
+ const appLabels = (editions) => editions.map((edition) => t(`row.edition${edition === "cn" ? "Cn" : edition === "sg" ? "Sg" : edition === "solo" ? "Solo" : "SoloSg"}`)).join("、");
1664
+ /**
1665
+ * Delete an account from this machine: drop the plugin's own credential copy,
1666
+ * then erase the account's login from Trae's `storage.json`, then clear every
1667
+ * account-holder this card holds (pool probe, settings membership, draft
1668
+ * selection, the serving binding it may have held).
1669
+ *
1670
+ * The app half is what makes the row actually disappear. It has one failure
1671
+ * mode worth its own message: a RUNNING Trae flushes its in-memory state back
1672
+ * over the file, so the Host refuses and names the app, and the card says to
1673
+ * quit it instead of reporting a generic failure the user cannot act on.
1674
+ */
1675
+ const deleteAccount = async (account) => {
1676
+ if (busy !== void 0 || deletingIds.has(account.accountId)) return;
1677
+ setBusy("delete");
1678
+ setDeletingIds((prev) => new Set(prev).add(account.accountId));
1679
+ setDeleteHint(void 0);
1552
1680
  try {
1553
- const response = await fetch(withTraePoolAccount(panel, "remove", account.accountId), { method: "POST" });
1681
+ const response = await fetch(withTraePoolAccount(panel, "delete", account.accountId), { method: "POST" });
1554
1682
  const body = await response.json();
1555
1683
  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 }));
1684
+ const blocked = body?.blockedApps ?? [];
1685
+ if (blocked.length > 0 && body?.deleted !== true) {
1686
+ setDeleteHint({
1687
+ kind: "app-running",
1688
+ accountId: account.accountId,
1689
+ accountName: account.accountName,
1690
+ apps: blocked
1691
+ });
1692
+ pushLog("warn", t("row.poolRowDeleteAppRunning", {
1693
+ accountName: account.accountName,
1694
+ apps: appLabels(blocked)
1695
+ }));
1696
+ setNow(Date.now());
1697
+ return;
1698
+ }
1699
+ if (body?.deleted !== true) throw new Error(t("row.poolRowDeleteFailed"));
1700
+ const wasMember = (saved.memberAccountIds ?? []).includes(account.accountId);
1701
+ let saveFailed = false;
1702
+ if (wasMember) {
1703
+ if (!writable) {
1704
+ saveFailed = true;
1705
+ setSaveError(t("row.poolSaveFailed", { message: t("row.poolLogSaveFailed") }));
1706
+ pushLog("error", t("row.poolLogSaveFailed"));
1707
+ } else try {
1708
+ await persistRemoval(account.accountId);
1709
+ } catch (error) {
1710
+ saveFailed = true;
1711
+ const message = error instanceof Error ? error.message : String(error);
1712
+ setSaveError(t("row.poolSaveFailed", { message }));
1713
+ pushLog("error", t("row.poolLogSaveFailed"));
1714
+ }
1715
+ }
1562
1716
  setSelected((prev) => {
1563
1717
  const next = new Set(prev);
1564
1718
  next.delete(account.accountId);
1565
1719
  return next;
1566
1720
  });
1567
- onRefresh?.();
1721
+ if (!saveFailed) {
1722
+ if (body.stillSignedIn === true) {
1723
+ const hadCopy = body.hadCopy !== false;
1724
+ setDeleteHint({
1725
+ kind: "still-signed-in",
1726
+ accountId: account.accountId,
1727
+ accountName: account.accountName,
1728
+ hadCopy
1729
+ });
1730
+ pushLog("warn", t(hadCopy ? "row.poolRowDeleteStillSigned" : "row.poolRowDeleteNoCopy", { accountName: account.accountName }));
1731
+ } else {
1732
+ const signedOut = body.signedOutApps ?? [];
1733
+ pushLog("ok", signedOut.length === 0 ? t("row.poolRowDeleted", { accountName: account.accountName }) : t("row.poolRowDeletedSignedOut", {
1734
+ accountName: account.accountName,
1735
+ apps: appLabels(signedOut)
1736
+ }));
1737
+ }
1738
+ }
1568
1739
  setNow(Date.now());
1740
+ if (!saveFailed) await onRefresh?.();
1569
1741
  } catch (error) {
1570
- pushLog("error", t("row.poolRowRemoveError", { message: error instanceof Error ? error.message : String(error) }));
1742
+ pushLog("error", t("row.poolRowDeleteError", {
1743
+ accountName: account.accountName,
1744
+ message: error instanceof Error ? error.message : String(error)
1745
+ }));
1571
1746
  } finally {
1572
- if (mountRef.current) setBusy(void 0);
1747
+ if (mountRef.current) {
1748
+ setBusy(void 0);
1749
+ setDeletingIds((prev) => {
1750
+ const next = new Set(prev);
1751
+ next.delete(account.accountId);
1752
+ return next;
1753
+ });
1754
+ }
1573
1755
  }
1574
1756
  };
1575
1757
  const noCandidate = pool.noCandidate;
1576
1758
  const showNoCandidate = pool.enabled && noCandidate !== void 0;
1577
1759
  const serving = servingAccount(pool);
1760
+ /**
1761
+ * The batch buttons are disabled for reasons the user cannot see (they are not
1762
+ * under the pointer, so a `title` only helps after a hover). Name them here.
1763
+ */
1764
+ const actionNotes = [];
1765
+ if (selected.size === 0) actionNotes.push(t("row.poolActionNoteNoMembers"));
1766
+ if (deviceClaimed) actionNotes.push(t("row.poolActionNoteCheckinDevice"));
1767
+ if (pool.targetModelSource === "none") actionNotes.push(t("row.poolActionNoteTestNone"));
1578
1768
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
1579
1769
  className: "dsm-trae-pool",
1580
1770
  "aria-label": t("row.poolTitle"),
@@ -1639,6 +1829,10 @@ window.__ModuleLoader__.load({
1639
1829
  })
1640
1830
  ]
1641
1831
  }),
1832
+ actionNotes.length === 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1833
+ className: "dsm-trae-pool-action-note",
1834
+ children: actionNotes.join(" · ")
1835
+ }),
1642
1836
  showNoCandidate ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(NoCandidateNotice, {
1643
1837
  noCandidate,
1644
1838
  t
@@ -1678,10 +1872,6 @@ window.__ModuleLoader__.load({
1678
1872
  ]
1679
1873
  })]
1680
1874
  }),
1681
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1682
- className: "dsm-trae-pool-card-note",
1683
- children: t("row.poolCardHint")
1684
- }),
1685
1875
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1686
1876
  className: pool.checkinSupported ? "dsm-trae-pool-table" : "dsm-trae-pool-table dsm-trae-pool-table-no-checkin",
1687
1877
  role: "table",
@@ -1698,6 +1888,7 @@ window.__ModuleLoader__.load({
1698
1888
  models,
1699
1889
  rows,
1700
1890
  checked: selected.has(account.accountId),
1891
+ poolEnabled: pool.enabled === true,
1701
1892
  detail: details?.[account.accountId],
1702
1893
  claim: claims?.[account.accountId],
1703
1894
  onClaim,
@@ -1709,10 +1900,11 @@ window.__ModuleLoader__.load({
1709
1900
  },
1710
1901
  t,
1711
1902
  disabled: busy !== void 0 || !writable,
1712
- removing: removed[account.accountId] === true,
1713
- onRemove: () => {
1714
- removeAccount(account);
1903
+ deleting: deletingIds.has(account.accountId),
1904
+ onDelete: () => {
1905
+ deleteAccount(account);
1715
1906
  },
1907
+ canDelete: pool.deleteSupported === true,
1716
1908
  formatNumber: formatNumber$1,
1717
1909
  relativeAt
1718
1910
  }, account.accountId)),
@@ -1725,10 +1917,11 @@ window.__ModuleLoader__.load({
1725
1917
  },
1726
1918
  t,
1727
1919
  disabled: busy !== void 0 || !writable,
1728
- removing: removed[ghost.accountId] === true,
1729
- onRemove: () => {
1730
- removeAccount(ghost);
1731
- }
1920
+ deleting: deletingIds.has(ghost.accountId),
1921
+ onDelete: () => {
1922
+ deleteAccount(ghost);
1923
+ },
1924
+ canDelete: pool.deleteSupported === true
1732
1925
  }, `ghost:${ghost.accountId}`))
1733
1926
  ]
1734
1927
  }),
@@ -1740,6 +1933,19 @@ window.__ModuleLoader__.load({
1740
1933
  names: ghosts.map((g) => g.accountName).join("、")
1741
1934
  })
1742
1935
  }) : null,
1936
+ deleteHint?.kind === "still-signed-in" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1937
+ className: "dsm-trae-pool-delete-hint",
1938
+ role: "status",
1939
+ children: t(deleteHint.hadCopy ? "row.poolDeleteStillSigned" : "row.poolDeleteNoCopy", { accountName: deleteHint.accountName })
1940
+ }) : null,
1941
+ deleteHint?.kind === "app-running" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1942
+ className: "dsm-trae-pool-delete-hint dsm-trae-pool-delete-hint-action",
1943
+ role: "status",
1944
+ children: t("row.poolDeleteAppRunning", {
1945
+ accountName: deleteHint.accountName,
1946
+ apps: appLabels(deleteHint.apps)
1947
+ })
1948
+ }) : null,
1743
1949
  dirty ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1744
1950
  className: "dsm-trae-pool-unsaved",
1745
1951
  role: "status",
@@ -1765,10 +1971,33 @@ window.__ModuleLoader__.load({
1765
1971
  className: "dsm-trae-pool-tools dsm-trae-pool-tools-dashed",
1766
1972
  children: tools
1767
1973
  }),
1974
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1975
+ className: "dsm-trae-pool-warning dsm-trae-pool-warning-workflow",
1976
+ role: "note",
1977
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1978
+ className: "dsm-trae-pool-warning-title",
1979
+ children: t("row.poolMirrorWorkflowTitle")
1980
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1981
+ className: "dsm-trae-pool-warning-hint",
1982
+ children: t("row.poolMirrorWorkflowHint")
1983
+ })]
1984
+ }),
1985
+ pool.copies === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1986
+ className: "dsm-trae-pool-note dsm-trae-pool-copyfile",
1987
+ role: "note",
1988
+ children: t("row.poolCopyFile", {
1989
+ path: pool.copies.path,
1990
+ count: pool.copies.count
1991
+ })
1992
+ }),
1768
1993
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1769
1994
  className: "dsm-trae-pool-note",
1770
1995
  children: t("row.poolSettingsNote")
1771
1996
  }),
1997
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1998
+ className: "dsm-trae-pool-note",
1999
+ children: t("row.poolDeleteHint")
2000
+ }),
1772
2001
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1773
2002
  className: "dsm-trae-pool-settings",
1774
2003
  children: [
@@ -1884,22 +2113,25 @@ window.__ModuleLoader__.load({
1884
2113
  }),
1885
2114
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1886
2115
  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", {
2116
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("a", {
2117
+ className: "dsm-trae-usage-cheer",
2118
+ href: "https://github.com/dingminhua/dsh-connect-trae",
2119
+ target: "_blank",
2120
+ rel: "noreferrer noopener",
2121
+ children: [t("row.cheer"), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2122
+ className: "dsm-trae-usage-cheer-star",
2123
+ "aria-hidden": "true",
2124
+ children: "★"
2125
+ })]
2126
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2127
+ className: "dsm-trae-pool-save-buttons",
2128
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
1896
2129
  type: "button",
1897
2130
  className: "dsm-btn dsm-btn-outline",
1898
2131
  disabled: !dirty || busy !== void 0,
1899
2132
  onClick: discard,
1900
2133
  children: t("row.poolDiscard")
1901
- }),
1902
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
2134
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
1903
2135
  type: "button",
1904
2136
  className: "dsm-btn dsm-btn-primary",
1905
2137
  disabled: !dirty || busy !== void 0,
@@ -1907,15 +2139,11 @@ window.__ModuleLoader__.load({
1907
2139
  save();
1908
2140
  },
1909
2141
  children: busy === "save" ? t("row.poolSaving") : t("row.poolSaved")
1910
- })
1911
- ]
2142
+ })]
2143
+ })]
1912
2144
  })
1913
2145
  ]
1914
2146
  }),
1915
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1916
- className: "dsm-trae-pool-note",
1917
- children: t("row.poolRegionNote")
1918
- }),
1919
2147
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ActivityLog, {
1920
2148
  log,
1921
2149
  t,
@@ -1970,8 +2198,18 @@ window.__ModuleLoader__.load({
1970
2198
  ]
1971
2199
  });
1972
2200
  }
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;
2201
+ function MemberRow({ account, models, rows, checked, onToggle, onTest, t, disabled, deleting, onDelete, canDelete, formatNumber, relativeAt, detail, claim, onClaim, showCheckin, deviceClaimed, poolEnabled }) {
2202
+ /**
2203
+ * Whether this row is the one billing the next request — and, with the pool on,
2204
+ * whether that claim is the pool's own.
2205
+ *
2206
+ * `account.current` alone is not enough: with an empty (or fully excluded) pool
2207
+ * the Host deliberately falls back to the saved selection, so a removed account
2208
+ * stays `current` and the pill would keep announcing it as the pool's pick.
2209
+ * A row can only be the rotation's answer while it is a member.
2210
+ */
2211
+ const isCurrent = account.current === true && (poolEnabled !== true || account.member === true);
2212
+ const hasCopy = account.reachable;
1975
2213
  const credit = account.generalAvailable;
1976
2214
  const excluded = account.excludedBy;
1977
2215
  const checkin = account.checkedInToday;
@@ -2008,10 +2246,10 @@ window.__ModuleLoader__.load({
2008
2246
  className: "dsm-trae-pool-account-name",
2009
2247
  children: [
2010
2248
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2011
- className: `dsm-trae-pool-dot dsm-trae-pool-dot-${excluded ? "excluded" : "ok"}`,
2249
+ className: `dsm-trae-pool-dot dsm-trae-pool-dot-${account.reachable === false ? "ghost" : excluded ? "excluded" : "ok"}`,
2012
2250
  "aria-hidden": "true"
2013
2251
  }),
2014
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("b", { children: account.accountName }),
2252
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("b", { children: account.accountName !== "" ? account.accountName : account.accountId }),
2015
2253
  detail?.meta === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2016
2254
  className: "dsm-trae-pool-meta",
2017
2255
  children: detail.meta
@@ -2034,6 +2272,11 @@ window.__ModuleLoader__.load({
2034
2272
  className: checked ? "dsm-trae-pool-member" : "dsm-trae-pool-not-member",
2035
2273
  children: checked ? t("row.poolMember") : t("row.poolNotMember")
2036
2274
  }),
2275
+ account.reachable === false ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2276
+ className: "dsm-trae-pool-tag dsm-trae-pool-tag-ghost",
2277
+ role: "status",
2278
+ children: t("row.poolNoCopyTag")
2279
+ }) : null,
2037
2280
  excluded && !probeOnScreen ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2038
2281
  className: "dsm-trae-pool-tag dsm-trae-pool-tag-excluded",
2039
2282
  children: exclusionLabel(excluded, t)
@@ -2091,7 +2334,7 @@ window.__ModuleLoader__.load({
2091
2334
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2092
2335
  className: `dsm-trae-pool-checkin-state dsm-trae-pool-checkin-${checkin === void 0 ? "unknown" : checkin ? "ok" : device === true || deviceClaimed ? "device" : "no"}`,
2093
2336
  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", {
2337
+ }), claim === void 0 || onClaim === void 0 || claim.claimable === false ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
2095
2338
  type: "button",
2096
2339
  className: "dsm-btn dsm-btn-outline dsm-trae-pool-claim",
2097
2340
  disabled: claim.disabled || disabled,
@@ -2107,22 +2350,23 @@ window.__ModuleLoader__.load({
2107
2350
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
2108
2351
  type: "button",
2109
2352
  className: "dsm-btn dsm-btn-outline",
2110
- disabled,
2353
+ disabled: disabled || hasCopy === false,
2354
+ title: hasCopy === false ? t("row.poolNoCopyTestHint") : void 0,
2111
2355
  onClick: onTest,
2112
2356
  children: t("row.poolRowTest")
2113
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
2357
+ }), canDelete ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
2114
2358
  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
- })]
2359
+ className: "dsm-btn dsm-btn-outline-danger dsm-trae-pool-delete",
2360
+ disabled: disabled || deleting,
2361
+ "aria-label": t("row.poolRowDeleteAria", { accountName: account.accountName }),
2362
+ onClick: onDelete,
2363
+ children: deleting ? t("row.poolRowDeleting") : t("row.poolRowDelete")
2364
+ }) : null]
2121
2365
  })
2122
2366
  ]
2123
2367
  });
2124
2368
  }
2125
- function MemberGhostRow({ ghost, checked, onToggle, t, disabled, removing, onRemove, showCheckin }) {
2369
+ function MemberGhostRow({ ghost, checked, onToggle, t, disabled, deleting, onDelete, canDelete, showCheckin }) {
2126
2370
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2127
2371
  className: "dsm-trae-pool-row dsm-trae-pool-row-ghost",
2128
2372
  role: "row",
@@ -2183,14 +2427,14 @@ window.__ModuleLoader__.load({
2183
2427
  }) : null,
2184
2428
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2185
2429
  className: "dsm-trae-pool-row-actions",
2186
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
2430
+ children: canDelete ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
2187
2431
  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
- })
2432
+ className: "dsm-btn dsm-btn-outline-danger dsm-trae-pool-delete",
2433
+ disabled: disabled || deleting,
2434
+ "aria-label": t("row.poolGhostDeleteAria", { accountName: ghost.accountName }),
2435
+ onClick: onDelete,
2436
+ children: deleting ? t("row.poolRowDeleting") : t("row.poolRowDelete")
2437
+ }) : null
2194
2438
  })
2195
2439
  ]
2196
2440
  });
@@ -2305,7 +2549,7 @@ window.__ModuleLoader__.load({
2305
2549
  setTimeout(resolve, timeoutMs);
2306
2550
  })]);
2307
2551
  }
2308
- function AccountTransfer({ t, region, accounts, onChanged }) {
2552
+ function AccountTransfer({ t, region, accounts, onChanged, hideLabel = false }) {
2309
2553
  const [phase, setPhase] = (0, react.useState)({ kind: "idle" });
2310
2554
  const [dialog, setDialog] = (0, react.useState)(void 0);
2311
2555
  const [selected, setSelected] = (0, react.useState)(/* @__PURE__ */ new Set());
@@ -2456,7 +2700,7 @@ window.__ModuleLoader__.load({
2456
2700
  const busy = phase.kind === "exporting" || phase.kind === "importing" || phase.kind === "refreshing";
2457
2701
  const veilMessage = phase.kind === "exporting" ? t("row.transferExporting") : phase.kind === "importing" || phase.kind === "refreshing" ? t("row.transferImporting") : "";
2458
2702
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
2459
- className: "dsm-trae-transfer",
2703
+ className: hideLabel ? "dsm-trae-transfer dsm-trae-transfer-flat" : "dsm-trae-transfer",
2460
2704
  "aria-label": t("row.transferTitle"),
2461
2705
  children: [
2462
2706
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)(BusyOverlay, {
@@ -2464,7 +2708,7 @@ window.__ModuleLoader__.load({
2464
2708
  message: veilMessage,
2465
2709
  ...busy ? { hint: t("row.transferVeilHint") } : {}
2466
2710
  }),
2467
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
2711
+ hideLabel ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
2468
2712
  className: "dsm-trae-transfer-label",
2469
2713
  children: t("row.transferTitle")
2470
2714
  }),
@@ -3801,11 +4045,22 @@ window.__ModuleLoader__.load({
3801
4045
  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
4046
  title,
3803
4047
  disabled: !usable || claimed || deviceSpent || spending,
3804
- busy: spending
4048
+ busy: spending,
4049
+ /**
4050
+ * False when there is nothing left to DO: a disabled "already claimed"
4051
+ * button restates the status line above it, in grey, forever. Callers
4052
+ * drop the control instead of buying it a permanent disabled state.
4053
+ */
4054
+ claimable: !claimed && !deviceSpent,
4055
+ settled: claimed ? "claimed" : deviceSpent ? "device" : void 0
3805
4056
  };
3806
4057
  };
3807
4058
  const checkinControl = (account, state) => {
3808
4059
  const view = claimViewOf(account.id, state);
4060
+ if (!view.claimable) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
4061
+ className: "dsm-trae-account-checkin-state",
4062
+ children: view.settled === "claimed" ? t("row.poolCheckedIn") : t("row.poolDeviceUsed")
4063
+ });
3809
4064
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
3810
4065
  type: "button",
3811
4066
  className: "dsm-trae-account-checkin",
@@ -4217,6 +4472,7 @@ window.__ModuleLoader__.load({
4217
4472
  children: t("row.poolToolsLabel")
4218
4473
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(AccountTransfer, {
4219
4474
  t,
4475
+ hideLabel: true,
4220
4476
  region: activeRegion === "ai" ? "ai" : "cn",
4221
4477
  accounts: status.accounts.map((account) => ({
4222
4478
  id: account.id,
@@ -5243,22 +5499,22 @@ window.__ModuleLoader__.load({
5243
5499
  "row.refreshCredits": "Refresh usage",
5244
5500
  "row.refreshing": "Refreshing…",
5245
5501
  "row.accountsTitle": "Trae account",
5246
- "row.accountsHint": "Sign-ins detected on this machine. Tokens are never shown or saved.",
5502
+ "row.accountsHint": "Copies this plugin keeps of every Trae sign-in on this machine (mirrored on refresh). Refresh after signing in, or Trae's next sign-in overwrites that login. Tokens are never shown.",
5247
5503
  "row.transferTitle": "Import & export accounts",
5248
5504
  "row.transferExport": "Export selected",
5249
5505
  "row.transferImport": "Import from file",
5250
5506
  "row.transferCancel": "Cancel",
5251
5507
  "row.transferSelectAll": "Select all",
5252
- "row.transferExportPick": "Choose which of the {total} accounts to export.",
5508
+ "row.transferExportPick": "Choose which of the {total} account copies to export.",
5253
5509
  "row.transferExportConfirm": "Export {count}",
5254
- "row.transferExported": "Exported {count} account(s) to {fileName}.",
5510
+ "row.transferExported": "Exported {count} account copy/copies to {fileName}.",
5255
5511
  "row.transferExporting": "Preparing the export…",
5256
- "row.transferImportPick": "This file holds {total} record(s). Untick anything you do not want to add.",
5512
+ "row.transferImportPick": "This file holds {total} record(s). Ticked records are added as plugin-owned copies; the Trae app is not touched.",
5257
5513
  "row.transferImportConfirm": "Import {count}",
5258
- "row.transferImported": "Imported {imported} account(s), skipped {skipped}.",
5514
+ "row.transferImported": "Imported {imported} account copy/copies, skipped {skipped}.",
5259
5515
  "row.transferImporting": "Importing…",
5260
5516
  "row.transferReading": "Reading the file…",
5261
- "row.transferHint": "The exported file contains live tokens — treat it as a password. Records belonging to the other region are skipped.",
5517
+ "row.transferHint": "The exported file holds this plugin's own copies, including live tokens — treat it as a password. Records belonging to the other region are skipped.",
5262
5518
  "row.transferError": "Import/export failed: {message}",
5263
5519
  "row.transferVeilHint": "The account list refreshes when this finishes.",
5264
5520
  "row.transferUnnamed": "Unnamed account",
@@ -5370,14 +5626,18 @@ window.__ModuleLoader__.load({
5370
5626
  "row.poolNoCandidateUnusable": "Every selected member is unavailable right now.",
5371
5627
  "row.poolNoCandidateHint": "Traffic keeps landing on the saved selection until one recovers.",
5372
5628
  "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
5629
  "row.poolRescan": "Re-detect accounts",
5375
5630
  "row.poolToolsLabel": "Export / import accounts",
5376
5631
  "row.poolCreditExpiry": "expires {when}",
5377
5632
  "row.poolProbeMeasured": "measured {when}",
5378
5633
  "row.poolProbeAt": "tested {when}",
5634
+ "row.poolMirrorWorkflowTitle": "Sign in one account, then refresh",
5635
+ "row.poolMirrorWorkflowHint": "The Trae app holds only one sign-in. After signing in to an account, refresh this card (or click “Detect accounts again”) so the plugin saves that login as its own copy. Switch accounts without refreshing and the previous login is not saved — the new one overwrites it in Trae.",
5379
5636
  "row.poolSettingsNote": "Preferences below take effect when you save; test results are written by the plugin and are not affected by that button.",
5380
5637
  "row.poolSelectedCount": "Selected {count} / {total}",
5638
+ "row.poolNoCopyTag": "no copy here",
5639
+ "row.poolCopyFile": "Copies live in {path} — {count} right now. Switching accounts in Trae adds a copy; it never overwrites an existing one.",
5640
+ "row.poolNoCopyTestHint": "This machine holds no sign-in for this account, so it cannot be tested. Sign in to Trae with it again to recreate a copy, or delete the row.",
5381
5641
  "row.poolSelectAll": "All",
5382
5642
  "row.poolSelectNone": "None",
5383
5643
  "row.poolColumnAccount": "Account",
@@ -5398,6 +5658,9 @@ window.__ModuleLoader__.load({
5398
5658
  "row.poolTargetStaleClear": "Back to automatic",
5399
5659
  "row.poolTargetNone": "No free model available for this panel",
5400
5660
  "row.poolTargetNoneHint": "The pool can only pick a free model; none resolves right now, so tests are disabled.",
5661
+ "row.poolActionNoteCheckinDevice": "This machine already used today's check-in (Trae allows one per device per day) — check-in is unavailable until tomorrow.",
5662
+ "row.poolActionNoteTestNone": "No free model resolves in this panel — pick a target model before testing.",
5663
+ "row.poolActionNoteNoMembers": "Tick the accounts you want in the pool and save first.",
5401
5664
  "row.poolTargetAutoOption": "Automatic (free model)",
5402
5665
  "row.poolTargetLabel": "Target test model",
5403
5666
  "row.poolTargetHint": "The model a probe test uses.",
@@ -5405,7 +5668,6 @@ window.__ModuleLoader__.load({
5405
5668
  "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
5669
  "row.poolProbeSizeDefault": "Minimal (default)",
5407
5670
  "row.poolProbeSizeOption": "{wan}0k tokens",
5408
- "row.poolStar": "Star the repo",
5409
5671
  "row.poolSettingsTitle": "Pool settings",
5410
5672
  "row.poolManualAccountLabel": "Manually billed account",
5411
5673
  "row.poolManualAccountHint": "Used while the pool is off. With it on, the Host picks by credits.",
@@ -5416,14 +5678,8 @@ window.__ModuleLoader__.load({
5416
5678
  "row.poolDiscard": "Discard",
5417
5679
  "row.poolSaveFailed": "Save failed: {message}",
5418
5680
  "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
5681
  "row.poolUnsavedMembers": "Unsaved membership changes — Save below to apply.",
5422
5682
  "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
5683
  "row.poolLogTitle": "Activity",
5428
5684
  "row.poolLogClear": "Clear",
5429
5685
  "row.poolLogEmpty": "No pool activity yet.",
@@ -5437,7 +5693,7 @@ window.__ModuleLoader__.load({
5437
5693
  "row.poolLogTestRowMalformed": "{accountName}: malformed result.",
5438
5694
  "row.poolLogTestDone": "Test finished.",
5439
5695
  "row.poolLogBatchFailed": "Pool action failed: {message}",
5440
- "row.poolRegionNote": "Each panel has its own pool; enterprise shares the CN account's sign-ins.",
5696
+ "row.poolLogTimeout": "The test did not answer in time — it was cancelled, so the button works again. Check the network and retry: {message}",
5441
5697
  "row.poolSelectAria": "Include {accountName} in the pool",
5442
5698
  "row.poolCheckedIn": "Checked in",
5443
5699
  "row.poolNotCheckedIn": "Not checked in",
@@ -5486,8 +5742,7 @@ window.__ModuleLoader__.load({
5486
5742
  "row.poolErrConflict": "the pool refused the request",
5487
5743
  "row.poolErrGeneric": "pool request failed (HTTP {status})",
5488
5744
  "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",
5745
+ "row.poolSettingsHint": "The switch and the target model are preferences — save them. Test, check in and delete act at once.",
5491
5746
  "row.poolLogSaved": "Pool settings saved.",
5492
5747
  "row.poolLogSaveFailed": "Could not save the pool settings.",
5493
5748
  "row.poolLogNoMembers": "Nothing to do — no pool members are selected.",
@@ -5495,8 +5750,21 @@ window.__ModuleLoader__.load({
5495
5750
  "row.poolLogGeneric": "the pool answered nothing usable",
5496
5751
  "row.poolLogRowTesting": "Testing {accountName}…",
5497
5752
  "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}",
5753
+ "row.poolRowDelete": "Delete",
5754
+ "row.poolRowDeleteAria": "Delete {accountName} from this machine: the plugin's copy and its Trae app login (this will sign it out of Trae)",
5755
+ "row.poolRowDeleting": "Deleting…",
5756
+ "row.poolGhostDeleteAria": "Delete the saved but unreachable account {accountName} from this machine",
5757
+ "row.poolRowDeleteError": "Could not delete {accountName}: {message}",
5758
+ "row.poolRowDeleteFailed": "the deletion was not confirmed",
5759
+ "row.poolRowDeleted": "Deleted the credential copy of {accountName}.",
5760
+ "row.poolDeleteStillSigned": "Deleted this plugin's copy of {accountName}, but it is still signed in to Trae, so the account itself is not gone. Sign out inside Trae to remove it from this machine.",
5761
+ "row.poolRowDeletedSignedOut": "Deleted {accountName} and signed it out of {apps}.",
5762
+ "row.poolRowDeleteAppRunning": "Could not delete {accountName}: {apps} is running and would restore the login. Quit {apps} completely, then delete again.",
5763
+ "row.poolDeleteAppRunning": "Nothing was changed: {apps} is running, and it would write the login back over the file on exit. Quit {apps} completely, then delete again.",
5764
+ "row.poolDeleteNoCopy": "There was no plugin copy of {accountName} left to delete — it only exists as a Trae app login, and that sign-out did not take effect.",
5765
+ "row.poolRowDeleteStillSigned": "removed this copy, but {accountName} is still signed in to Trae",
5766
+ "row.poolRowDeleteNoCopy": "{accountName} had no plugin copy — only its Trae app login",
5767
+ "row.poolDeleteHint": "The plugin keeps its own copy of every local Trae login (a copy is created on refresh unless one already exists) and only ever shows and bills those copies. Delete removes the copy AND signs that account out of Trae itself, leaving a backup of every file it rewrites; a deleted account is not re-imported while it stays signed in Trae. A running Trae would flush its own state back over the file, so quit it first — the card names the app it needs closed.",
5500
5768
  "row.cheer": "Star on GitHub"
5501
5769
  };
5502
5770
  const zh = {
@@ -5560,22 +5828,22 @@ window.__ModuleLoader__.load({
5560
5828
  "row.refreshCredits": "刷新用量",
5561
5829
  "row.refreshing": "正在刷新…",
5562
5830
  "row.accountsTitle": "Trae 账号",
5563
- "row.accountsHint": "本机检测到的登录账号;Token 不会显示或保存。",
5831
+ "row.accountsHint": "本插件保管的账号副本:本机每个 Trae 登录都会在刷新时补建一份副本。登录新号后要刷新一次,否则它会被 Trae 的下一次登录覆盖。Token 不会显示。",
5564
5832
  "row.transferTitle": "导入 / 导出账号",
5565
5833
  "row.transferExport": "导出所选",
5566
5834
  "row.transferImport": "从文件导入",
5567
5835
  "row.transferCancel": "取消",
5568
5836
  "row.transferSelectAll": "全选",
5569
- "row.transferExportPick": "从这 {total} 个账号中选择要导出的。",
5837
+ "row.transferExportPick": "从这 {total} 个账号副本中选择要导出的。",
5570
5838
  "row.transferExportConfirm": "导出 {count} 个",
5571
- "row.transferExported": "已导出 {count} 个账号到 {fileName}。",
5839
+ "row.transferExported": "已导出 {count} 个账号副本到 {fileName}。",
5572
5840
  "row.transferExporting": "正在准备导出…",
5573
- "row.transferImportPick": "该文件包含 {total} 条记录,取消勾选不想添加的。",
5841
+ "row.transferImportPick": "该文件包含 {total} 条记录;勾选的会作为插件副本导入,不会写入 Trae 应用。",
5574
5842
  "row.transferImportConfirm": "导入 {count} 个",
5575
- "row.transferImported": "已导入 {imported} 个账号,跳过 {skipped} 个。",
5843
+ "row.transferImported": "已导入 {imported} 个账号副本,跳过 {skipped} 个。",
5576
5844
  "row.transferImporting": "正在导入…",
5577
5845
  "row.transferReading": "正在读取文件…",
5578
- "row.transferHint": "导出的文件里是有效 Token,请当作密码保管。属于另一区域的记录会被跳过。",
5846
+ "row.transferHint": "导出的文件里是插件保管的账号副本(含有效 Token),请当作密码保管。属于另一区域的记录会被跳过。",
5579
5847
  "row.transferError": "导入/导出失败:{message}",
5580
5848
  "row.transferVeilHint": "完成后账号列表会自动刷新。",
5581
5849
  "row.transferUnnamed": "未命名账号",
@@ -5687,14 +5955,18 @@ window.__ModuleLoader__.load({
5687
5955
  "row.poolNoCandidateUnusable": "所有已选成员当前均不可用。",
5688
5956
  "row.poolNoCandidateHint": "请求会继续落在已保存的账号上,直到有成员恢复可用。",
5689
5957
  "row.poolMembersTitle": "本池包含的账号",
5690
- "row.poolCardHint": "只有勾选的账号才会被签到、测试和轮换。默认一个都不勾 —— 请勾选你要的账号,然后保存。",
5691
5958
  "row.poolRescan": "重新检测账号",
5692
5959
  "row.poolToolsLabel": "导出 / 导入账号",
5693
5960
  "row.poolCreditExpiry": "{when} 过期",
5694
5961
  "row.poolProbeMeasured": "测得 {when}",
5695
5962
  "row.poolProbeAt": "测试于 {when}",
5963
+ "row.poolMirrorWorkflowTitle": "每登录一个账号,就刷新一次",
5964
+ "row.poolMirrorWorkflowHint": "Trae 应用只有一个登录位:每登录一个账号,就回到这里刷新一次(打开卡片或点「重新检测账号」),插件会把这次登录保存成一份自己的副本。没刷新就换号,上一个账号不会被保存——它会被 Trae 里的新登录覆盖。",
5696
5965
  "row.poolSettingsNote": "下面是偏好设置:点保存才生效;测试结果由插件自动写入,不受这个保存按钮影响。",
5697
5966
  "row.poolSelectedCount": "已选 {count} / {total}",
5967
+ "row.poolNoCopyTag": "本机无副本",
5968
+ "row.poolCopyFile": "副本就存在 {path},当前 {count} 份。在 Trae 里换号只会新增一份,不会覆盖已有的副本。",
5969
+ "row.poolNoCopyTestHint": "本机没有这个账号的登录,无法测速。需要它请在 Trae 里重新登录以重建副本,或删除这一行。",
5698
5970
  "row.poolSelectAll": "全部",
5699
5971
  "row.poolSelectNone": "清空",
5700
5972
  "row.poolColumnAccount": "账号",
@@ -5715,6 +5987,9 @@ window.__ModuleLoader__.load({
5715
5987
  "row.poolTargetStaleClear": "改回自动",
5716
5988
  "row.poolTargetNone": "本面板暂无可用免费模型",
5717
5989
  "row.poolTargetNoneHint": "账号池只会挑免费模型;当前无法解析出可用模型,测速暂被禁用。",
5990
+ "row.poolActionNoteCheckinDevice": "本机今日的签到已用掉(Trae 按每台设备每天一次限制),明天才能再签。",
5991
+ "row.poolActionNoteTestNone": "本面板没有解析出免费模型——先指定一个目标模型再测速。",
5992
+ "row.poolActionNoteNoMembers": "先勾选要入池的账号并保存。",
5718
5993
  "row.poolTargetAutoOption": "自动(免费模型)",
5719
5994
  "row.poolTargetLabel": "目标测速模型",
5720
5995
  "row.poolTargetHint": "探针测速所用的模型。",
@@ -5722,7 +5997,6 @@ window.__ModuleLoader__.load({
5722
5997
  "row.poolProbeSizeHint": "测试时发送多少文本。只发一个词的探针,只能证明「这个账号能回答一个词」——如果上游拒绝长请求,问题只会在你真实对话时才出现。填得越多越接近真实对话,每个账号每次测试消耗的积分也越多。",
5723
5998
  "row.poolProbeSizeDefault": "最小请求(默认)",
5724
5999
  "row.poolProbeSizeOption": "{wan} 万 token",
5725
- "row.poolStar": "鼓励一下",
5726
6000
  "row.poolSettingsTitle": "账号池设置",
5727
6001
  "row.poolManualAccountLabel": "手动计费账号",
5728
6002
  "row.poolManualAccountHint": "账号池关闭时由它计费;开启后由 Host 按积分挑选。",
@@ -5733,14 +6007,8 @@ window.__ModuleLoader__.load({
5733
6007
  "row.poolDiscard": "放弃",
5734
6008
  "row.poolSaveFailed": "保存失败:{message}",
5735
6009
  "row.poolGhostMembers": "{count} 个已保存成员不再对应本机登录:{names}",
5736
- "row.poolGhostRemove": "移除 {account}",
5737
- "row.poolGhostRemoveHint": "忘记本面板对 {account} 的绑定。",
5738
6010
  "row.poolUnsavedMembers": "成员改动尚未保存——请点击下方保存。",
5739
6011
  "row.poolRowTest": "测速",
5740
- "row.poolRowRemove": "移除",
5741
- "row.poolRowRemoveAria": "从本面板移除 {accountName}",
5742
- "row.poolRowRemoving": "移除中…",
5743
- "row.poolRowRemoved": "已从本面板移除 {accountName}",
5744
6012
  "row.poolLogTitle": "活动记录",
5745
6013
  "row.poolLogClear": "清空",
5746
6014
  "row.poolLogEmpty": "暂无账号池活动。",
@@ -5754,7 +6022,7 @@ window.__ModuleLoader__.load({
5754
6022
  "row.poolLogTestRowMalformed": "{accountName}:结果异常。",
5755
6023
  "row.poolLogTestDone": "测速完成。",
5756
6024
  "row.poolLogBatchFailed": "账号池操作失败:{message}",
5757
- "row.poolRegionNote": "每个面板各有自己的账号池;企业版与国内版共用同一 Trae CN 账号登录。",
6025
+ "row.poolLogTimeout": "测速超时,已自动取消(按钮已恢复可用)。请检查网络后重试:{message}",
5758
6026
  "row.poolSelectAria": "把 {accountName} 加入账号池",
5759
6027
  "row.poolCheckedIn": "已签到",
5760
6028
  "row.poolNotCheckedIn": "未签到",
@@ -5803,8 +6071,7 @@ window.__ModuleLoader__.load({
5803
6071
  "row.poolErrConflict": "账号池拒绝了该请求",
5804
6072
  "row.poolErrGeneric": "账号池请求失败(HTTP {status})",
5805
6073
  "row.poolCheckingIn": "签到中…",
5806
- "row.poolSettingsHint": "开关与目标模型是偏好,改完要保存;行内的测速、签到与移除立即生效。",
5807
- "row.poolGhostRemoveAria": "把已保存但本机无法登录的账号 {accountName} 移出本面板",
6074
+ "row.poolSettingsHint": "开关与目标模型是偏好,改完要保存;行内的测速、签到与删除立即生效。",
5808
6075
  "row.poolLogSaved": "账号池设置已保存。",
5809
6076
  "row.poolLogSaveFailed": "无法保存账号池设置。",
5810
6077
  "row.poolLogNoMembers": "没有可处理的内容——尚未勾选任何池成员。",
@@ -5812,8 +6079,21 @@ window.__ModuleLoader__.load({
5812
6079
  "row.poolLogGeneric": "账号池未返回可用的结果",
5813
6080
  "row.poolLogRowTesting": "正在测速 {accountName}…",
5814
6081
  "row.poolLogTargetCleared": "测速目标已改回自动。",
5815
- "row.poolRowRemoveFailed": "账号池未确认移除",
5816
- "row.poolRowRemoveError": "无法移除 {accountName}:{message}",
6082
+ "row.poolRowDelete": "删除",
6083
+ "row.poolRowDeleteAria": "从本机删除 {accountName}:插件的副本连同它在 Trae 应用里的登录(会把它从 Trae 退出登录)",
6084
+ "row.poolRowDeleting": "删除中…",
6085
+ "row.poolGhostDeleteAria": "从本机删除已保存但无法登录的账号 {accountName}",
6086
+ "row.poolRowDeleteError": "无法删除 {accountName}:{message}",
6087
+ "row.poolRowDeleteFailed": "删除未得到确认",
6088
+ "row.poolRowDeleted": "已删除 {accountName} 的凭据副本。",
6089
+ "row.poolDeleteStillSigned": "已删除本插件对 {accountName} 的副本,但它仍登录在 Trae 里,所以账号本身还在。要彻底从本机移除,请在 Trae 内退出登录。",
6090
+ "row.poolRowDeletedSignedOut": "已删除 {accountName},并将它从 {apps} 中退出登录。",
6091
+ "row.poolRowDeleteAppRunning": "无法删除 {accountName}:{apps} 正在运行,退出时会把登录写回去。请先完全退出 {apps},再重新删除。",
6092
+ "row.poolDeleteAppRunning": "这次没有改动任何文件:{apps} 正在运行,退出时会把自己的状态回写到文件、覆盖这次修改。请先完全退出 {apps},再点「删除」。",
6093
+ "row.poolDeleteNoCopy": "{accountName} 本来就没有插件副本可删——它只存在于 Trae 应用登录里,而这次退出登录没有生效。",
6094
+ "row.poolRowDeleteStillSigned": "已删除副本,但 {accountName} 仍登录在 Trae 中",
6095
+ "row.poolRowDeleteNoCopy": "{accountName} 没有插件副本——它只存在于 Trae 应用登录里",
6096
+ "row.poolDeleteHint": "本插件会为本机每个 Trae 登录各创建一份自己的副本(刷新时自动创建,已有副本则不动),卡片只显示、也只使用这些副本。「删除」会同时删掉副本并把该账号从 Trae 应用中退出登录,改写的每个文件都留一份备份;只要该账号还登录在 Trae 里,就不会被重新收录。Trae 正在运行时会把自己的状态回写到文件,所以要先完全退出——卡片会点名是哪个应用。",
5817
6097
  "row.cheer": "鼓励一下"
5818
6098
  };
5819
6099
  //#endregion