dsh-connect-trae 4.6.11 → 4.8.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/CHANGELOG.md +381 -0
- package/README.en.md +7 -2
- package/README.md +7 -2
- package/docs/assets/dsh-connect-trae-sidebar-page.png +0 -0
- package/lib/client.js +2157 -972
- package/lib/index.d.ts +132 -1
- package/lib/index.js +343 -89
- package/package.json +2 -1
package/lib/client.js
CHANGED
|
@@ -502,6 +502,123 @@ window.__ModuleLoader__.load({
|
|
|
502
502
|
return options.sort((left, right) => left - right);
|
|
503
503
|
}
|
|
504
504
|
//#endregion
|
|
505
|
+
//#region src/reasoning.ts
|
|
506
|
+
/**
|
|
507
|
+
* The DSH thinking levels this plugin can carry, in ascending order.
|
|
508
|
+
*
|
|
509
|
+
* `max` is the fifth level Trae's own client advertises — its bundle ships the
|
|
510
|
+
* i18n keys `trae-chat-core.model.reasoning_effort.{low,medium,high,extra_high,max}`
|
|
511
|
+
* and `ai.model.reasoning_effort.{low,medium,high,xhigh,max}`, and publishes the
|
|
512
|
+
* per-model list from a remote config (`AI.agent.model.reasoning_effort_level_by_agent_model_v2`).
|
|
513
|
+
* Leaving it out of this list meant a level the vendor could start serving was
|
|
514
|
+
* filtered away at PARSE time, before anything downstream could decide (issue
|
|
515
|
+
* #60). Listing it does not fabricate the level: `supported` is still built
|
|
516
|
+
* only from what the upstream response actually contains, so a model that does
|
|
517
|
+
* not advertise `max` never offers it.
|
|
518
|
+
*
|
|
519
|
+
* `minimal` is kept for the same reason in reverse — it is a DSH level, so the
|
|
520
|
+
* type must be able to represent it even though no CN model has advertised it.
|
|
521
|
+
*/
|
|
522
|
+
const TRAE_REASONING_EFFORTS = [
|
|
523
|
+
"minimal",
|
|
524
|
+
"low",
|
|
525
|
+
"medium",
|
|
526
|
+
"high",
|
|
527
|
+
"xhigh",
|
|
528
|
+
"max"
|
|
529
|
+
];
|
|
530
|
+
//#endregion
|
|
531
|
+
//#region src/default-effort.ts
|
|
532
|
+
/**
|
|
533
|
+
* Which thinking level a model runs at when the caller names none.
|
|
534
|
+
*
|
|
535
|
+
* ## Why this module exists
|
|
536
|
+
*
|
|
537
|
+
* Trae's directory already tells us each model's own default level
|
|
538
|
+
* (`reasoning_effort_config.default_level`, parsed into `reasoning.defaultEffort`
|
|
539
|
+
* by `model-metadata.ts`) — measured 2026-11-03 on a live CN credential:
|
|
540
|
+
* `deepseek-v4.1-flash` says `high`, `glm-5.3` says `xhigh`. The card's meta row
|
|
541
|
+
* names the tiers it accepts, so the fact is known.
|
|
542
|
+
*
|
|
543
|
+
* DSH's model picker nevertheless reads **Default**, because the adapter never
|
|
544
|
+
* reports that level upward (`reasoning.defaultEffort` is absent from what
|
|
545
|
+
* {@link toPiModel} hands the kernel). The picker therefore cannot say which
|
|
546
|
+
* tier a request will run at, and the request itself carries no
|
|
547
|
+
* `reasoning_effort` — the upstream falls back to the same level it already
|
|
548
|
+
* published, so nothing is broken, but nothing is knowable either, and the user
|
|
549
|
+
* has no way to say "always use high on this model, regardless of what Trae
|
|
550
|
+
* ships as its default".
|
|
551
|
+
*
|
|
552
|
+
* ## The three seams that must agree (any one missing and the picker still says Default)
|
|
553
|
+
*
|
|
554
|
+
* 1. **The adapter** must report the level as `LlmResolvedModelInfo.reasoning.defaultEffort`.
|
|
555
|
+
* The kernel materialises that into every request that names no effort
|
|
556
|
+
* (`dsh-llm` `resolveCallWithInfo`: `const effective = requested ?? reasoning.defaultEffort`),
|
|
557
|
+
* and DSH's model picker takes its "current level" from the same metadata.
|
|
558
|
+
* 2. **A per-model saved choice** must survive a settings save. The settings
|
|
559
|
+
* channel rebuilds the whole region document through its schema, so a field
|
|
560
|
+
* absent from that schema is dropped on every save — the failure that once
|
|
561
|
+
* reset `wireConfigName` (see the `modelConfig` schema).
|
|
562
|
+
* 3. **The bridge** must translate the level it receives into Trae's wire name.
|
|
563
|
+
* `solo-bridge.ts` already does exactly that through the model's own
|
|
564
|
+
* `reasoningEfforts` table, and deletes the field when the model does not
|
|
565
|
+
* advertise it — so a level is never sent under a name the vendor never said.
|
|
566
|
+
*
|
|
567
|
+
* ## Precedence
|
|
568
|
+
*
|
|
569
|
+
* The user's explicit choice > the level upstream declares > this model's own
|
|
570
|
+
* middle tier.
|
|
571
|
+
*
|
|
572
|
+
* @module dsh-connect-trae/default-effort
|
|
573
|
+
*/
|
|
574
|
+
/**
|
|
575
|
+
* The tier to fall back to when a model publishes a tier list but names no
|
|
576
|
+
* default of its own and the user has never chosen one.
|
|
577
|
+
*
|
|
578
|
+
* `medium` is tried first because it is the only middle tier shared by the
|
|
579
|
+
* families seen so far. It is deliberately not the only fallback: a model that
|
|
580
|
+
* publishes `low / high` (measured on `Doubao-Seed-2.1-Pro` and
|
|
581
|
+
* `step-5-preview`) has no `medium` at all, and picking its weakest tier instead
|
|
582
|
+
* would silently mean "thinking off" — that is not a fallback, that is the worst
|
|
583
|
+
* possible default.
|
|
584
|
+
*/
|
|
585
|
+
const TRAE_FALLBACK_EFFORT = "medium";
|
|
586
|
+
/**
|
|
587
|
+
* The tiers a model actually declares, filtered to ones DSH can name.
|
|
588
|
+
*
|
|
589
|
+
* Upstream's vocabulary is not DSH's: Trae publishes `light / high / extra_high`
|
|
590
|
+
* and the parser maps them to `low / high / xhigh` (`EFFORT_MAP`). Anything that
|
|
591
|
+
* still fails to land in DSH's six names is dropped rather than passed through —
|
|
592
|
+
* a level the kernel cannot name would be rejected with
|
|
593
|
+
* `UNSUPPORTED_REASONING_EFFORT` and take the whole request with it.
|
|
594
|
+
*
|
|
595
|
+
* The result keeps `TRAE_REASONING_EFFORTS` order, so it reads weakest-first.
|
|
596
|
+
*/
|
|
597
|
+
function declaredEfforts(model) {
|
|
598
|
+
const supported = model.supported ?? [];
|
|
599
|
+
return TRAE_REASONING_EFFORTS.filter((level) => supported.includes(level));
|
|
600
|
+
}
|
|
601
|
+
/**
|
|
602
|
+
* The tier this model should run at when a request names none.
|
|
603
|
+
*
|
|
604
|
+
* @param model - the model, with the tiers it declares and its upstream default.
|
|
605
|
+
* @param saved - the user's per-model choices (model id -> tier). A missing key
|
|
606
|
+
* means the user never chose, which is different from choosing the tier that
|
|
607
|
+
* happens to equal the upstream default.
|
|
608
|
+
* @returns a tier **this model declares**, or `undefined` when it declares none.
|
|
609
|
+
*/
|
|
610
|
+
function defaultEffortFor(model, saved = {}) {
|
|
611
|
+
const declared = declaredEfforts(model);
|
|
612
|
+
if (declared.length === 0) return void 0;
|
|
613
|
+
const asked = saved[model.id];
|
|
614
|
+
const chosen = declared.find((level) => level === asked);
|
|
615
|
+
if (chosen !== void 0) return chosen;
|
|
616
|
+
const upstream = declared.find((level) => level === model.defaultEffort);
|
|
617
|
+
if (upstream !== void 0) return upstream;
|
|
618
|
+
if (declared.includes("medium")) return TRAE_FALLBACK_EFFORT;
|
|
619
|
+
return declared[Math.floor((declared.length - 1) / 2)];
|
|
620
|
+
}
|
|
621
|
+
//#endregion
|
|
505
622
|
//#region src/model-order.ts
|
|
506
623
|
/**
|
|
507
624
|
* Wire name of the default SOLO chat function.
|
|
@@ -1270,6 +1387,16 @@ window.__ModuleLoader__.load({
|
|
|
1270
1387
|
.dsm-trae-model-promo-offPeak{color:var(--dsw-alias-brand-primary,#5686fe);border-color:color-mix(in srgb,var(--dsw-alias-brand-primary,#5686fe) 40%,transparent);background:color-mix(in srgb,var(--dsw-alias-brand-primary,#5686fe) 10%,transparent)}
|
|
1271
1388
|
.dsm-trae-model-meta{display:flex;align-items:center;gap:7px 12px;flex-wrap:wrap;color:var(--dsw-alias-label-tertiary,#999);font-size:11px;line-height:16px}
|
|
1272
1389
|
.dsm-trae-model-options{display:flex;align-items:center;justify-content:flex-end;gap:12px;flex:none}
|
|
1390
|
+
/* The thinking-level picker, on the meta row. Deliberately shaped like the
|
|
1391
|
+
quiet meta text beside it (11px, secondary label) rather than like the
|
|
1392
|
+
options row: it edits a standing preference, and a control that shouts
|
|
1393
|
+
would read as a per-request input the way the image checkbox is. */
|
|
1394
|
+
.dsm-trae-model-effort{display:inline-flex;align-items:center;min-width:0}
|
|
1395
|
+
.dsm-trae-model-effort label{display:inline-flex;align-items:center;gap:4px;cursor:pointer}
|
|
1396
|
+
.dsm-trae-model-effort-select{max-width:112px;padding:0 2px;border:1px solid transparent;border-radius:4px;background:transparent;color:var(--dsw-alias-label-secondary,#c6c9d0);font:inherit;cursor:pointer}
|
|
1397
|
+
.dsm-trae-model-effort-select:hover{border-color:var(--dsw-alias-border-l2,#36373b)}
|
|
1398
|
+
.dsm-trae-model-effort-select:focus-visible{outline:2px solid var(--dsw-alias-brand-primary,#5686fe);outline-offset:1px}
|
|
1399
|
+
.dsm-trae-model-effort-select:disabled{opacity:.6;cursor:not-allowed}
|
|
1273
1400
|
.dsm-trae-model-image{display:inline-flex;align-items:center;gap:4px;color:var(--dsw-alias-label-secondary,#c6c9d0);font-size:11px;line-height:16px;cursor:pointer}
|
|
1274
1401
|
.dsm-trae-model-image input{margin:0;accent-color:var(--dsw-alias-brand-primary,#5686fe)}
|
|
1275
1402
|
.dsm-trae-context-budget{display:flex;align-items:center;justify-content:flex-end;gap:12px;flex:none;margin:0;padding:0;border:0;color:var(--dsw-alias-label-secondary,#c6c9d0);font-size:11px;line-height:16px}
|
|
@@ -1534,6 +1661,29 @@ window.__ModuleLoader__.load({
|
|
|
1534
1661
|
.dsm-trae-pool-log-warn{border-left-color:var(--dsw-alias-state-warn-primary,#b45309)}
|
|
1535
1662
|
.dsm-trae-pool-log-error{border-left-color:var(--dsw-alias-state-error-primary,#d92d20)}
|
|
1536
1663
|
.dsm-trae-pool-log-text{overflow-wrap:anywhere}
|
|
1664
|
+
/* THE OPTIONAL SIDEBAR PAGE (Config.sidebarPanel).
|
|
1665
|
+
*
|
|
1666
|
+
* The same card body, drawn as a full page in the centre column: no card border
|
|
1667
|
+
* or header, its own heading, and a scrolling column so a long pool log cannot
|
|
1668
|
+
* push the header off screen. Widths are capped at the host's own reading width
|
|
1669
|
+
* rather than the viewport, which is what keeps a wide monitor from stretching
|
|
1670
|
+
* the account table into unreadable rows.
|
|
1671
|
+
*
|
|
1672
|
+
* The page's back control reuses the card's own button shape but not its
|
|
1673
|
+
* dsm-btn class: this one is navigation, not an action. Its caret is drawn in
|
|
1674
|
+
* CSS, for the same reason the card's chevron is — the host primitives' icon
|
|
1675
|
+
* names are not a stable contract across DSH releases. */
|
|
1676
|
+
.dsm-trae-page{box-sizing:border-box;height:100%;display:flex;flex-direction:column;align-items:center;gap:20px;padding:28px clamp(24px,4vw,48px) 48px;overflow:auto;color:var(--dsw-alias-label-primary,#e6e6e6)}
|
|
1677
|
+
.dsm-trae-page>*{width:100%;max-width:860px}
|
|
1678
|
+
.dsm-trae-page-head{display:flex;flex-direction:column;gap:6px}
|
|
1679
|
+
.dsm-trae-page-back{appearance:none;font:inherit;cursor:pointer;border:1px solid var(--dsw-alias-border-l2,#36373b);background:var(--dsw-alias-bg-layer-1,#17181b);color:var(--dsw-alias-label-secondary,#c7c7c7);border-radius:8px;padding:5px 12px;display:inline-flex;align-items:center;gap:6px;align-self:flex-start;font-size:13px;line-height:20px}
|
|
1680
|
+
.dsm-trae-page-back:hover{color:var(--dsw-alias-label-primary,#e6e6e6);border-color:var(--dsw-alias-label-dimmed,#777)}
|
|
1681
|
+
.dsm-trae-page-back-caret{display:inline-block;width:7px;height:7px;border-left:1.6px solid currentColor;border-bottom:1.6px solid currentColor;transform:rotate(45deg);margin-right:2px}
|
|
1682
|
+
.dsm-trae-page-icon{width:40px;height:40px;border-radius:9px;align-self:flex-start;margin-top:14px}
|
|
1683
|
+
.dsm-trae-page-title{margin:0;font-size:20px;font-weight:600;line-height:1.4}
|
|
1684
|
+
.dsm-trae-page-desc{margin:0;font-size:13px;line-height:1.5;color:var(--dsw-alias-label-tertiary,#999)}
|
|
1685
|
+
.dsm-trae-page-body .dsm-trae-usage{padding:20px 20px 8px;border:1px solid var(--dsw-alias-border-l2,#36373b);background:var(--dsw-alias-bg-layer-3,#202126);border-radius:12px}
|
|
1686
|
+
.dsm-trae-panel-icon{display:block;border-radius:4px;flex:none}
|
|
1537
1687
|
`;
|
|
1538
1688
|
/**
|
|
1539
1689
|
* Composer-row readout styles, injected by the readout itself.
|
|
@@ -1570,9 +1720,15 @@ window.__ModuleLoader__.load({
|
|
|
1570
1720
|
/* The panel is portaled to document.body and positioned from the trigger, so it
|
|
1571
1721
|
MUST be fixed 鈥?otherwise the left/top it measures are ignored and it lands in
|
|
1572
1722
|
document flow. All material comes from shell tokens so it follows the theme. */
|
|
1723
|
+
/* 264px, not the 248px this started as (m02477). The header holds a title plus
|
|
1724
|
+
two icon+word controls now, and measured in the browser the English pair
|
|
1725
|
+
("Refresh" + "Settings") needed 233px of content where 248px of panel leaves
|
|
1726
|
+
222 — the settings pill overflowed its own header by 11px. Widening the panel
|
|
1727
|
+
is the honest fix: the alternative is renaming the owner's words, and
|
|
1728
|
+
"Refresh"/「刷新」 are what they asked for. */
|
|
1573
1729
|
.dsm-trae-composer-panel{
|
|
1574
1730
|
position:fixed;z-index:1100;box-sizing:border-box;
|
|
1575
|
-
width:min(
|
|
1731
|
+
width:min(264px,calc(100vw - 24px));padding:12px;
|
|
1576
1732
|
border:1px solid var(--dsw-alias-border-l1,rgba(127,127,127,.28));
|
|
1577
1733
|
border-radius:var(--dsw-radius-lg,14px);
|
|
1578
1734
|
background:var(--dsw-specific-menu,Canvas);
|
|
@@ -1581,19 +1737,128 @@ window.__ModuleLoader__.load({
|
|
|
1581
1737
|
font-size:12px;line-height:18px;color:var(--dsw-alias-label-secondary,#444);
|
|
1582
1738
|
}
|
|
1583
1739
|
.dsm-trae-composer-panel-head{display:flex;align-items:center;justify-content:space-between;gap:8px;margin-bottom:8px}
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1740
|
+
/* nowrap: the header is a title plus an 11px control in a ~250px popover, and
|
|
1741
|
+
without it the TITLE broke across two lines the moment the settings control
|
|
1742
|
+
joined the row — measured on the live Desktop host for the sibling plugin.
|
|
1743
|
+
The title is the one thing that must stay on one line; what follows it is
|
|
1744
|
+
short by construction. */
|
|
1745
|
+
.dsm-trae-composer-panel-title{flex:none;white-space:nowrap;font-size:13px;font-weight:600;color:var(--dsw-alias-label-primary,CanvasText)}
|
|
1746
|
+
/* The header's right-hand control: the link to this plugin's settings.
|
|
1747
|
+
A wrapper rather than an auto margin on the button, because the button is NOT
|
|
1748
|
+
always rendered (a host with no route renders none) and a wrapper that is
|
|
1749
|
+
always present keeps the header layout identical either way. */
|
|
1750
|
+
.dsm-trae-composer-panel-actions{flex:none;margin-left:auto;display:flex;align-items:center;gap:6px}
|
|
1751
|
+
/* THE POOL TEST BUTTON (m02091, restyled m02477), sitting to the settings
|
|
1752
|
+
pill's left.
|
|
1753
|
+
|
|
1754
|
+
A GHOST control: no border, no fill, an icon plus a word — and the FILL
|
|
1755
|
+
appears only on hover. That shape is the owner's call (the reference shows a
|
|
1756
|
+
bare circular arrow and 「刷新」 beside the tinted settings pill) and it also
|
|
1757
|
+
reads correctly: the two controls are different kinds of thing. Settings
|
|
1758
|
+
navigates, so it carries the brand fill and behaves like a pill; this one
|
|
1759
|
+
re-runs a batch in place, so it stays quiet until the pointer arrives.
|
|
1760
|
+
|
|
1761
|
+
What the earlier bordered pill got wrong: a 1px outline at rest made it look
|
|
1762
|
+
like the card's dsm-btn-outline (a save/cancel control), and two bordered or
|
|
1763
|
+
two tinted shapes in a 264px header read as one segmented control instead of
|
|
1764
|
+
two different actions. The icon is a circular arrow, not a gear — same family,
|
|
1765
|
+
different verb. */
|
|
1766
|
+
.dsm-trae-composer-panel-test{
|
|
1767
|
+
appearance:none;flex:none;
|
|
1768
|
+
display:inline-flex;align-items:center;gap:4px;
|
|
1769
|
+
padding:3px 6px;
|
|
1770
|
+
border:1px solid transparent;border-radius:999px;background:transparent;
|
|
1771
|
+
color:var(--dsw-alias-label-secondary,#444);
|
|
1772
|
+
font:inherit;font-size:11px;line-height:16px;
|
|
1773
|
+
white-space:nowrap;cursor:pointer;
|
|
1774
|
+
transition:background-color .12s ease,border-color .12s ease,color .12s ease;
|
|
1775
|
+
}
|
|
1776
|
+
.dsm-trae-composer-panel-test:hover:not(:disabled){
|
|
1777
|
+
background:var(--dsw-alias-interactive-bg-hover,rgba(127,127,127,.10));
|
|
1778
|
+
border-color:transparent;
|
|
1779
|
+
color:var(--dsw-alias-label-primary,CanvasText);
|
|
1780
|
+
}
|
|
1781
|
+
.dsm-trae-composer-panel-test:active{background:rgba(127,127,127,.16)}
|
|
1782
|
+
.dsm-trae-composer-panel-test:focus-visible{outline:2px solid var(--dsw-alias-brand-primary,#5686fe);outline-offset:1px}
|
|
1783
|
+
.dsm-trae-composer-panel-test:disabled{opacity:.5;cursor:default}
|
|
1784
|
+
/* The arrow does not shrink when the label is a longer word ("Refresh" vs
|
|
1785
|
+
「刷新」): a flex item defaults to shrinking, which squashes the 12px icon into
|
|
1786
|
+
an oval on a narrow popover — the same trap the gear beside it documents. */
|
|
1787
|
+
.dsm-trae-composer-panel-test-icon{flex:none;display:block}
|
|
1788
|
+
/* While the batch runs the arrow TURNS — 900ms a revolution. A disabled button
|
|
1789
|
+
whose icon sits still reads as broken, and this batch is serial: the slowest
|
|
1790
|
+
account decides when it ends, so "did my press land?" is a real question for
|
|
1791
|
+
as long as the panel is open. Fast enough to read as motion, slow enough not
|
|
1792
|
+
to look like a rendering fault. */
|
|
1793
|
+
.dsm-trae-composer-panel-test-icon-spin{animation:dsm-trae-panel-test-spin .9s linear infinite}
|
|
1794
|
+
@keyframes dsm-trae-panel-test-spin{to{transform:rotate(360deg)}}
|
|
1795
|
+
/* The label never wraps. Two lines in a 16px-tall control would push the header
|
|
1796
|
+
taller and unstick the table below it. */
|
|
1797
|
+
.dsm-trae-composer-panel-test-label{flex:none;white-space:nowrap}
|
|
1798
|
+
/* Reduced motion: the colour transition is decoration, and the hover state is
|
|
1799
|
+
still fully visible without it. */
|
|
1800
|
+
@media (prefers-reduced-motion:reduce){
|
|
1801
|
+
.dsm-trae-composer-panel-test{transition:none}
|
|
1802
|
+
/* The motion is dropped but the STATE SIGNAL must not be: the icon keeps a
|
|
1803
|
+
distinct dimmed look, and the label already switches to 「刷新中…」. Without
|
|
1804
|
+
this the spinning arrow would simply freeze at an arbitrary angle and read
|
|
1805
|
+
as a static icon — i.e. exactly the "did my press land?" silence that the
|
|
1806
|
+
rotation was added to fix. */
|
|
1807
|
+
.dsm-trae-composer-panel-test-icon-spin{animation:none;opacity:.5}
|
|
1808
|
+
}
|
|
1809
|
+
/* The batch's own outcome line: a NOTE (it succeeded but not everything
|
|
1810
|
+
answered), not an error. Sized like the footer so it reads as a report. */
|
|
1811
|
+
.dsm-trae-composer-panel-note{
|
|
1812
|
+
margin:8px 0 0;font-size:11px;line-height:16px;
|
|
1591
1813
|
color:var(--dsw-alias-label-secondary,#444);
|
|
1592
|
-
font:inherit;font-size:11px;line-height:16px;cursor:pointer;
|
|
1593
1814
|
}
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
.
|
|
1815
|
+
/* PORTED VERBATIM IN SHAPE from the sibling dsh-connect-workbuddy
|
|
1816
|
+
(ComposerCredits), so the two connectors' popovers agree: a quiet tinted
|
|
1817
|
+
PILL, not a bordered button and not an underlined link.
|
|
1818
|
+
|
|
1819
|
+
Why not the bordered button this started as: navigating away is not the
|
|
1820
|
+
header's primary action, and a bordered box beside a 13px title reads as a
|
|
1821
|
+
toolbar. Why not an underlined link: a one-word underline reads as body text
|
|
1822
|
+
that happens to be clickable — in a 13px header the eye does not register it
|
|
1823
|
+
as a control at all. The tinted fill plus the gear give it a SHAPE at rest,
|
|
1824
|
+
which is where the affordance is actually needed (the underline only appeared
|
|
1825
|
+
on hover, i.e. after the user already knew). The fill is the brand colour at
|
|
1826
|
+
10%, so it reads as "secondary action in this product" rather than as a fourth
|
|
1827
|
+
grey chip beside the title.
|
|
1828
|
+
|
|
1829
|
+
Self-contained: the dsm-btn primitive lives in TRAE_CARD_CSS, which the
|
|
1830
|
+
composer never loads. */
|
|
1831
|
+
.dsm-trae-composer-panel-settings{
|
|
1832
|
+
appearance:none;flex:none;
|
|
1833
|
+
display:inline-flex;align-items:center;gap:4px;
|
|
1834
|
+
padding:3px 8px 3px 6px;
|
|
1835
|
+
border:1px solid transparent;border-radius:999px;
|
|
1836
|
+
background:rgba(86,134,254,.10);
|
|
1837
|
+
color:var(--dsw-alias-brand-primary,#5686fe);
|
|
1838
|
+
font:inherit;font-size:11px;line-height:16px;font-weight:500;
|
|
1839
|
+
cursor:pointer;
|
|
1840
|
+
transition:background-color .12s ease,border-color .12s ease,color .12s ease;
|
|
1841
|
+
}
|
|
1842
|
+
.dsm-trae-composer-panel-settings:hover:not(:disabled){
|
|
1843
|
+
background:rgba(86,134,254,.18);
|
|
1844
|
+
border-color:rgba(86,134,254,.34);
|
|
1845
|
+
color:var(--dsw-alias-brand-primary,#5686fe);
|
|
1846
|
+
}
|
|
1847
|
+
.dsm-trae-composer-panel-settings:active{background:rgba(86,134,254,.26)}
|
|
1848
|
+
.dsm-trae-composer-panel-settings:focus-visible{outline:2px solid var(--dsw-alias-brand-primary,#5686fe);outline-offset:1px}
|
|
1849
|
+
.dsm-trae-composer-panel-settings:disabled{opacity:.5;cursor:default}
|
|
1850
|
+
/* The gear does not shrink when the label is a longer word ("Settings" vs
|
|
1851
|
+
"设置"): a flex item defaults to shrinking, which would squash the icon into
|
|
1852
|
+
an oval on a narrow popover. */
|
|
1853
|
+
.dsm-trae-composer-panel-settings-icon{flex:none;display:block}
|
|
1854
|
+
/* The label never wraps. Two lines in a 16px-tall pill would push the header
|
|
1855
|
+
taller and unstick the table below it. */
|
|
1856
|
+
.dsm-trae-composer-panel-settings-label{flex:none;white-space:nowrap}
|
|
1857
|
+
/* Reduced motion: the colour transition is decoration, and the hover state is
|
|
1858
|
+
still fully visible without it. */
|
|
1859
|
+
@media (prefers-reduced-motion:reduce){
|
|
1860
|
+
.dsm-trae-composer-panel-settings{transition:none}
|
|
1861
|
+
}
|
|
1597
1862
|
/* The panel is a TABLE: one row per account in the region, one figure (the
|
|
1598
1863
|
general balance) per row, and a row click switches accounts.
|
|
1599
1864
|
|
|
@@ -1758,6 +2023,94 @@ window.__ModuleLoader__.load({
|
|
|
1758
2023
|
});
|
|
1759
2024
|
}
|
|
1760
2025
|
//#endregion
|
|
2026
|
+
//#region src/client/pool-test.ts
|
|
2027
|
+
/**
|
|
2028
|
+
* How long one `test` request may take before the caller gives up.
|
|
2029
|
+
*
|
|
2030
|
+
* A probe has its own upstream deadline, but a stalled connection (a restarting
|
|
2031
|
+
* Host, a dropped keep-alive) has none: without this the run button stays
|
|
2032
|
+
* disabled until the page is reloaded, which reads as "测速卡住了". Sized for a
|
|
2033
|
+
* full batch — a serial run with a gap between accounts — rather than for one
|
|
2034
|
+
* account, because aborting a healthy batch half way through would report the
|
|
2035
|
+
* members it never reached as failures.
|
|
2036
|
+
*/
|
|
2037
|
+
const POOL_TEST_TIMEOUT_MS = 18e4;
|
|
2038
|
+
/**
|
|
2039
|
+
* Handle ONE NDJSON line of a `test` answer. A non-JSON line is skipped; a
|
|
2040
|
+
* `{reason:'pool-failed'}` line reports the failure and counts as "saw
|
|
2041
|
+
* something". Returns whether the line was usable, so the caller can tell "the
|
|
2042
|
+
* route answered nothing parseable" (a bug to surface) from "not finished yet".
|
|
2043
|
+
*/
|
|
2044
|
+
function applyTestLine(line, t, onRow, onFailure) {
|
|
2045
|
+
if (line.trim() === "") return false;
|
|
2046
|
+
let parsed;
|
|
2047
|
+
try {
|
|
2048
|
+
parsed = JSON.parse(line);
|
|
2049
|
+
} catch {
|
|
2050
|
+
return false;
|
|
2051
|
+
}
|
|
2052
|
+
if (parsed.row !== void 0) {
|
|
2053
|
+
onRow(parsed.row);
|
|
2054
|
+
return true;
|
|
2055
|
+
}
|
|
2056
|
+
if (parsed.rows !== void 0) {
|
|
2057
|
+
for (const r of parsed.rows) onRow(r);
|
|
2058
|
+
return true;
|
|
2059
|
+
}
|
|
2060
|
+
if (parsed.reason === "pool-failed") {
|
|
2061
|
+
onFailure(parsed.error ?? t("row.poolLogGeneric"));
|
|
2062
|
+
return true;
|
|
2063
|
+
}
|
|
2064
|
+
return false;
|
|
2065
|
+
}
|
|
2066
|
+
/**
|
|
2067
|
+
* Read the answer line by line AS IT ARRIVES.
|
|
2068
|
+
*
|
|
2069
|
+
* The Host streams one line per account, but `response.text()` waits for the
|
|
2070
|
+
* whole body: with a slow probe the surface sat on "逐个处理账号中…" and looked
|
|
2071
|
+
* frozen (a single member could take half a minute). Reading the body stream
|
|
2072
|
+
* makes each finished account appear immediately, and an empty/failed stream
|
|
2073
|
+
* still ends the loop instead of hanging the busy state forever.
|
|
2074
|
+
*/
|
|
2075
|
+
async function* testLinesOf(response) {
|
|
2076
|
+
const body = response.body;
|
|
2077
|
+
if (body === null) {
|
|
2078
|
+
for (const line of (await response.text()).split(/\r?\n/)) yield line;
|
|
2079
|
+
return;
|
|
2080
|
+
}
|
|
2081
|
+
const reader = body.getReader();
|
|
2082
|
+
const decoder = new TextDecoder();
|
|
2083
|
+
let buffer = "";
|
|
2084
|
+
for (;;) {
|
|
2085
|
+
const { done, value } = await reader.read();
|
|
2086
|
+
if (done) break;
|
|
2087
|
+
buffer += decoder.decode(value, { stream: true });
|
|
2088
|
+
let cut = buffer.indexOf("\n");
|
|
2089
|
+
while (cut >= 0) {
|
|
2090
|
+
yield buffer.slice(0, cut);
|
|
2091
|
+
buffer = buffer.slice(cut + 1);
|
|
2092
|
+
cut = buffer.indexOf("\n");
|
|
2093
|
+
}
|
|
2094
|
+
}
|
|
2095
|
+
buffer += decoder.decode();
|
|
2096
|
+
if (buffer.trim() !== "") yield buffer;
|
|
2097
|
+
}
|
|
2098
|
+
/** A human label for every value of {@link TraeWebPoolOutcome}. */
|
|
2099
|
+
function outcomeLabel(outcome, t) {
|
|
2100
|
+
switch (outcome) {
|
|
2101
|
+
case "ok": return t("row.poolProbeOk");
|
|
2102
|
+
case "rate-limited": return t("row.poolProbeRateLimited");
|
|
2103
|
+
case "model-busy": return t("row.poolProbeModelBusy");
|
|
2104
|
+
case "out-of-credit": return t("row.poolProbeOutOfCredit");
|
|
2105
|
+
case "credential-rejected": return t("row.poolProbeCredentialRejected");
|
|
2106
|
+
case "policy-rejected": return t("row.poolProbePolicyRejected");
|
|
2107
|
+
case "plan-gated": return t("row.poolProbePlanGated");
|
|
2108
|
+
case "unavailable": return t("row.poolProbeUnavailable");
|
|
2109
|
+
case "not-found": return t("row.poolProbeNotFound");
|
|
2110
|
+
case "failed": return t("row.poolProbeFailed");
|
|
2111
|
+
}
|
|
2112
|
+
}
|
|
2113
|
+
//#endregion
|
|
1761
2114
|
//#region src/client/AccountPool.tsx
|
|
1762
2115
|
/**
|
|
1763
2116
|
* The account-pool card UI for Trae, ported from the WorkBuddy sibling.
|
|
@@ -1828,21 +2181,6 @@ window.__ModuleLoader__.load({
|
|
|
1828
2181
|
if (hours < 24) return t("row.poolAgoHour", { n: hours });
|
|
1829
2182
|
return t("row.poolAgoDay", { n: Math.round(hours / 24) });
|
|
1830
2183
|
}
|
|
1831
|
-
/** A human label for every value of {@link TraeWebPoolOutcome}. */
|
|
1832
|
-
function outcomeLabel(outcome, t) {
|
|
1833
|
-
switch (outcome) {
|
|
1834
|
-
case "ok": return t("row.poolProbeOk");
|
|
1835
|
-
case "rate-limited": return t("row.poolProbeRateLimited");
|
|
1836
|
-
case "model-busy": return t("row.poolProbeModelBusy");
|
|
1837
|
-
case "out-of-credit": return t("row.poolProbeOutOfCredit");
|
|
1838
|
-
case "credential-rejected": return t("row.poolProbeCredentialRejected");
|
|
1839
|
-
case "policy-rejected": return t("row.poolProbePolicyRejected");
|
|
1840
|
-
case "plan-gated": return t("row.poolProbePlanGated");
|
|
1841
|
-
case "unavailable": return t("row.poolProbeUnavailable");
|
|
1842
|
-
case "not-found": return t("row.poolProbeNotFound");
|
|
1843
|
-
case "failed": return t("row.poolProbeFailed");
|
|
1844
|
-
}
|
|
1845
|
-
}
|
|
1846
2184
|
/** A human label for every value of {@link TraeWebPoolExclusion}. */
|
|
1847
2185
|
function exclusionLabel(exclusion, t) {
|
|
1848
2186
|
switch (exclusion) {
|
|
@@ -1893,75 +2231,7 @@ window.__ModuleLoader__.load({
|
|
|
1893
2231
|
}
|
|
1894
2232
|
return t("row.poolErrGeneric", { status: String(status) });
|
|
1895
2233
|
}
|
|
1896
|
-
/**
|
|
1897
|
-
* Handle ONE NDJSON line of a `test` answer. A non-JSON line is skipped; a
|
|
1898
|
-
* `{reason:'pool-failed'}` line reports the failure and counts as "saw
|
|
1899
|
-
* something". Returns whether the line was usable, so the caller can tell "the
|
|
1900
|
-
* route answered nothing parseable" (a bug to surface) from "not finished yet".
|
|
1901
|
-
*/
|
|
1902
|
-
function applyTestLine(line, t, onRow, onFailure) {
|
|
1903
|
-
if (line.trim() === "") return false;
|
|
1904
|
-
let parsed;
|
|
1905
|
-
try {
|
|
1906
|
-
parsed = JSON.parse(line);
|
|
1907
|
-
} catch {
|
|
1908
|
-
return false;
|
|
1909
|
-
}
|
|
1910
|
-
if (parsed.row !== void 0) {
|
|
1911
|
-
onRow(parsed.row);
|
|
1912
|
-
return true;
|
|
1913
|
-
}
|
|
1914
|
-
if (parsed.rows !== void 0) {
|
|
1915
|
-
for (const r of parsed.rows) onRow(r);
|
|
1916
|
-
return true;
|
|
1917
|
-
}
|
|
1918
|
-
if (parsed.reason === "pool-failed") {
|
|
1919
|
-
onFailure(parsed.error ?? t("row.poolLogGeneric"));
|
|
1920
|
-
return true;
|
|
1921
|
-
}
|
|
1922
|
-
return false;
|
|
1923
|
-
}
|
|
1924
|
-
/**
|
|
1925
|
-
* Read the answer line by line AS IT ARRIVES.
|
|
1926
|
-
*
|
|
1927
|
-
* The Host streams one line per account, but `response.text()` waits for the
|
|
1928
|
-
* whole body: with a slow probe the card sat on "逐个处理账号中…" and looked
|
|
1929
|
-
* frozen (a single member could take half a minute). Reading the body stream
|
|
1930
|
-
* makes each finished account appear immediately, and an empty/failed stream
|
|
1931
|
-
* still ends the loop instead of hanging the busy state forever.
|
|
1932
|
-
*/
|
|
1933
|
-
async function* testLinesOf(response) {
|
|
1934
|
-
const body = response.body;
|
|
1935
|
-
if (body === null) {
|
|
1936
|
-
for (const line of (await response.text()).split(/\r?\n/)) yield line;
|
|
1937
|
-
return;
|
|
1938
|
-
}
|
|
1939
|
-
const reader = body.getReader();
|
|
1940
|
-
const decoder = new TextDecoder();
|
|
1941
|
-
let buffer = "";
|
|
1942
|
-
for (;;) {
|
|
1943
|
-
const { done, value } = await reader.read();
|
|
1944
|
-
if (done) break;
|
|
1945
|
-
buffer += decoder.decode(value, { stream: true });
|
|
1946
|
-
let cut = buffer.indexOf("\n");
|
|
1947
|
-
while (cut >= 0) {
|
|
1948
|
-
yield buffer.slice(0, cut);
|
|
1949
|
-
buffer = buffer.slice(cut + 1);
|
|
1950
|
-
cut = buffer.indexOf("\n");
|
|
1951
|
-
}
|
|
1952
|
-
}
|
|
1953
|
-
buffer += decoder.decode();
|
|
1954
|
-
if (buffer.trim() !== "") yield buffer;
|
|
1955
|
-
}
|
|
1956
|
-
/**
|
|
1957
|
-
* How long one `test` request may take before the card gives up.
|
|
1958
|
-
*
|
|
1959
|
-
* A probe has its own upstream deadline, but a stalled connection (a restarting
|
|
1960
|
-
* Host, a dropped keep-alive) has none: without this the run button stays
|
|
1961
|
-
* disabled until the page is reloaded, which reads as "测速卡住了". Sized for a
|
|
1962
|
-
* batch: a few seconds per account plus room for the slowest upstream.
|
|
1963
|
-
*/
|
|
1964
|
-
const TEST_TIMEOUT_MS = 18e4;
|
|
2234
|
+
/** Whether one row is a saved member (dictates the in-pool tag / ghost badge). */
|
|
1965
2235
|
function AccountPool({ panel, pool, models, t, settingsScope, onChanged, onRefresh, tools, topActions, details, claims, onClaim, deviceClaimed = false, onBind, bindingBusy }) {
|
|
1966
2236
|
const [settingsTick, setSettingsTick] = (0, react.useState)(0);
|
|
1967
2237
|
const saved = (0, react.useMemo)(() => {
|
|
@@ -2134,7 +2404,7 @@ window.__ModuleLoader__.load({
|
|
|
2134
2404
|
const controller = new AbortController();
|
|
2135
2405
|
const timer = setTimeout(() => {
|
|
2136
2406
|
controller.abort();
|
|
2137
|
-
},
|
|
2407
|
+
}, POOL_TEST_TIMEOUT_MS);
|
|
2138
2408
|
try {
|
|
2139
2409
|
const response = await fetch(withTraePoolAction(panel, "test"), {
|
|
2140
2410
|
method: "POST",
|
|
@@ -2181,7 +2451,7 @@ window.__ModuleLoader__.load({
|
|
|
2181
2451
|
const controller = new AbortController();
|
|
2182
2452
|
const timer = setTimeout(() => {
|
|
2183
2453
|
controller.abort();
|
|
2184
|
-
},
|
|
2454
|
+
}, POOL_TEST_TIMEOUT_MS);
|
|
2185
2455
|
try {
|
|
2186
2456
|
const response = await fetch(withTraePoolAccount(panel, "test", account.accountId), {
|
|
2187
2457
|
method: "POST",
|
|
@@ -3635,7 +3905,7 @@ window.__ModuleLoader__.load({
|
|
|
3635
3905
|
* is precisely why this section has no 「保存」 of its own — the model catalog,
|
|
3636
3906
|
* which does, lives in its own section.
|
|
3637
3907
|
*/
|
|
3638
|
-
function TraeAccountPanel({ t, settingsScope, panel, status, statusLabel, credentialRegionIsAi, regionOn, showPointsInMainUi, onToggleShowPoints, creditPosition, onCreditPositionChange, savingPreferences, creditsLabelId, creditsHintId, rows, busy, rescanNote, onRescan, switchingAccount, onSwitchAccount, onClaim, onTransferChanged, poolDetails, poolClaims, models, deviceClaimed, onPoolChanged, onPoolRefresh, creditsUnavailable, creditsExpiry, alternatives, onSwitchToAlternative }) {
|
|
3908
|
+
function TraeAccountPanel({ t, settingsScope, panel, status, statusLabel, credentialRegionIsAi, regionOn, showPointsInMainUi, onToggleShowPoints, creditPosition, onCreditPositionChange, sidebarPanelOn, sidebarPanelBusy, onToggleSidebarPanel, savingPreferences, creditsLabelId, creditsHintId, rows, busy, rescanNote, onRescan, switchingAccount, onSwitchAccount, onClaim, onTransferChanged, poolDetails, poolClaims, models, deviceClaimed, onPoolChanged, onPoolRefresh, creditsUnavailable, creditsExpiry, alternatives, onSwitchToAlternative }) {
|
|
3639
3909
|
const writable = settingsScope?.getSnapshot().writable === true;
|
|
3640
3910
|
const signedIn = status.status === "signed-in";
|
|
3641
3911
|
const accounts = signedIn ? status.accounts : [];
|
|
@@ -3643,62 +3913,87 @@ window.__ModuleLoader__.load({
|
|
|
3643
3913
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
|
|
3644
3914
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3645
3915
|
className: "dsm-trae-prefs",
|
|
3646
|
-
children: [
|
|
3647
|
-
|
|
3648
|
-
|
|
3649
|
-
|
|
3650
|
-
|
|
3651
|
-
|
|
3652
|
-
|
|
3653
|
-
|
|
3654
|
-
|
|
3655
|
-
|
|
3656
|
-
|
|
3657
|
-
|
|
3658
|
-
|
|
3659
|
-
|
|
3660
|
-
|
|
3661
|
-
className: "dsm-trae-pref-label",
|
|
3662
|
-
id: creditsLabelId,
|
|
3663
|
-
children: t("row.showPointsInMainUi")
|
|
3664
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3665
|
-
className: "dsm-trae-pref-hint",
|
|
3666
|
-
id: creditsHintId,
|
|
3667
|
-
children: t(credentialRegionIsAi ? "row.showPointsInMainUiHintAi" : "row.showPointsInMainUiHint")
|
|
3668
|
-
})]
|
|
3669
|
-
})]
|
|
3670
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("label", {
|
|
3671
|
-
className: "dsm-trae-pref dsm-trae-credit-position-pref",
|
|
3672
|
-
title: t("row.creditPositionHint"),
|
|
3673
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
3674
|
-
className: "dsm-trae-pref-copy",
|
|
3675
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
3676
|
-
className: "dsm-trae-pref-lead",
|
|
3916
|
+
children: [
|
|
3917
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
3918
|
+
className: "dsm-trae-pref",
|
|
3919
|
+
title: t(credentialRegionIsAi ? "row.showPointsInMainUiHintAi" : "row.showPointsInMainUiHint"),
|
|
3920
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
3921
|
+
type: "checkbox",
|
|
3922
|
+
className: "dsm-trae-pref-check",
|
|
3923
|
+
checked: showPointsInMainUi,
|
|
3924
|
+
disabled: !writable || savingPreferences,
|
|
3925
|
+
"aria-describedby": creditsHintId,
|
|
3926
|
+
onChange: (event) => {
|
|
3927
|
+
onToggleShowPoints(event.currentTarget.checked);
|
|
3928
|
+
}
|
|
3929
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
3930
|
+
className: "dsm-trae-pref-copy",
|
|
3677
3931
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3678
3932
|
className: "dsm-trae-pref-label",
|
|
3679
|
-
|
|
3680
|
-
|
|
3681
|
-
|
|
3682
|
-
|
|
3683
|
-
|
|
3684
|
-
|
|
3685
|
-
|
|
3686
|
-
|
|
3687
|
-
|
|
3688
|
-
|
|
3689
|
-
|
|
3690
|
-
|
|
3691
|
-
|
|
3692
|
-
|
|
3693
|
-
|
|
3933
|
+
id: creditsLabelId,
|
|
3934
|
+
children: t("row.showPointsInMainUi")
|
|
3935
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3936
|
+
className: "dsm-trae-pref-hint",
|
|
3937
|
+
id: creditsHintId,
|
|
3938
|
+
children: t(credentialRegionIsAi ? "row.showPointsInMainUiHintAi" : "row.showPointsInMainUiHint")
|
|
3939
|
+
})]
|
|
3940
|
+
})]
|
|
3941
|
+
}),
|
|
3942
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("label", {
|
|
3943
|
+
className: "dsm-trae-pref dsm-trae-credit-position-pref",
|
|
3944
|
+
title: t("row.creditPositionHint"),
|
|
3945
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
3946
|
+
className: "dsm-trae-pref-copy",
|
|
3947
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
3948
|
+
className: "dsm-trae-pref-lead",
|
|
3949
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3950
|
+
className: "dsm-trae-pref-label",
|
|
3951
|
+
children: t("row.creditPositionLabel")
|
|
3952
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
|
|
3953
|
+
className: "dsm-trae-credit-position",
|
|
3954
|
+
value: regionOn ? creditPosition : "composer",
|
|
3955
|
+
disabled: !regionOn || !writable || savingPreferences,
|
|
3956
|
+
"aria-label": t("row.creditPositionAria", { region: panel === "cn" ? t("row.tabCn") : panel === "enterprise" ? t("row.tabEnterprise") : t("row.tabAi") }),
|
|
3957
|
+
onChange: (event) => {
|
|
3958
|
+
onCreditPositionChange(event.currentTarget.value === "stats" ? "stats" : "composer");
|
|
3959
|
+
},
|
|
3960
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
3961
|
+
value: "composer",
|
|
3962
|
+
children: t("row.creditPositionComposer")
|
|
3963
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
3964
|
+
value: "stats",
|
|
3965
|
+
children: t("row.creditPositionStats")
|
|
3966
|
+
})]
|
|
3694
3967
|
})]
|
|
3968
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3969
|
+
className: "dsm-trae-pref-hint",
|
|
3970
|
+
children: t("row.creditPositionHint")
|
|
3971
|
+
})]
|
|
3972
|
+
})
|
|
3973
|
+
}),
|
|
3974
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
3975
|
+
className: "dsm-trae-pref",
|
|
3976
|
+
title: t("row.sidebarPanelHint"),
|
|
3977
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
3978
|
+
type: "checkbox",
|
|
3979
|
+
className: "dsm-trae-pref-check",
|
|
3980
|
+
checked: sidebarPanelOn,
|
|
3981
|
+
disabled: sidebarPanelBusy || savingPreferences,
|
|
3982
|
+
onChange: (event) => {
|
|
3983
|
+
onToggleSidebarPanel(event.currentTarget.checked);
|
|
3984
|
+
}
|
|
3985
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
3986
|
+
className: "dsm-trae-pref-copy",
|
|
3987
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3988
|
+
className: "dsm-trae-pref-label",
|
|
3989
|
+
children: t("row.sidebarPanelLabel")
|
|
3990
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3991
|
+
className: "dsm-trae-pref-hint",
|
|
3992
|
+
children: t("row.sidebarPanelHint")
|
|
3695
3993
|
})]
|
|
3696
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3697
|
-
className: "dsm-trae-pref-hint",
|
|
3698
|
-
children: t("row.creditPositionHint")
|
|
3699
3994
|
})]
|
|
3700
3995
|
})
|
|
3701
|
-
|
|
3996
|
+
]
|
|
3702
3997
|
}),
|
|
3703
3998
|
pool !== void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
|
|
3704
3999
|
className: "dsm-trae-accounts",
|
|
@@ -3959,6 +4254,105 @@ window.__ModuleLoader__.load({
|
|
|
3959
4254
|
return verdict.tier === void 0 ? t(verdict.label) : verdict.tier;
|
|
3960
4255
|
}
|
|
3961
4256
|
//#endregion
|
|
4257
|
+
//#region src/client/panel-switch.ts
|
|
4258
|
+
/**
|
|
4259
|
+
* Whether the OPTIONAL sidebar panel is switched on, as the browser half sees
|
|
4260
|
+
* it, plus the tiny subscription the registration controller needs.
|
|
4261
|
+
*
|
|
4262
|
+
* Ported from the sibling `dsh-connect-workbuddy` plugin (its PR #188, off by
|
|
4263
|
+
* default since), because the same two facts decide the shape here:
|
|
4264
|
+
*
|
|
4265
|
+
* ## Why this is a module-level store and not component state
|
|
4266
|
+
*
|
|
4267
|
+
* The `sidebar.panellist` row is built from ENTRY METADATA, not from the
|
|
4268
|
+
* component: the host's sidebar maps the slot's entries into a row button whose
|
|
4269
|
+
* label comes from `options.label` and whose glyph is `renderSlot(...)`. A
|
|
4270
|
+
* component that returns `null` therefore does not hide the row — it renders a
|
|
4271
|
+
* BLANK, still-clickable nav button. That is why the panel cannot use the
|
|
4272
|
+
* `ComposerPointsGate` trick the credit readout uses (that seat renders a list
|
|
4273
|
+
* of components, so `null` means absent). The row has to be registered only
|
|
4274
|
+
* while the switch is on, and unregistered when it goes off.
|
|
4275
|
+
*
|
|
4276
|
+
* Registering conditionally in turn means something must OWN the re-register on
|
|
4277
|
+
* a settings change, and that something lives in the entry (`apply`), not in a
|
|
4278
|
+
* component. This module is the seam between the two: the card publishes a
|
|
4279
|
+
* value here after it has verified a write landed, and the entry subscribes to
|
|
4280
|
+
* register or dispose.
|
|
4281
|
+
*
|
|
4282
|
+
* ## Why the Host is the seed
|
|
4283
|
+
*
|
|
4284
|
+
* The value is DELIVERED by the Host on the status document
|
|
4285
|
+
* (`sidebarPanelEnabled`), for the reason every other switch in this plugin is
|
|
4286
|
+
* Host-carried: this namespace's browser mirror is documented as stale on the
|
|
4287
|
+
* affected 0.1.7 deployments. Reading only the settings scope at startup would
|
|
4288
|
+
* let a stale `false` hide a row the Host believes it is serving — with nothing
|
|
4289
|
+
* to re-evaluate it afterwards, the panel would then be gone for the session.
|
|
4290
|
+
*
|
|
4291
|
+
* Turning the panel OFF while the page is open needs no special handling here:
|
|
4292
|
+
* the host's layout service drops an `activePanelId` whose `main` key no longer
|
|
4293
|
+
* exists, so the centre column returns to the conversation by itself.
|
|
4294
|
+
*/
|
|
4295
|
+
/** Listeners waiting for a change to the opt-in state. */
|
|
4296
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
4297
|
+
/**
|
|
4298
|
+
* The current answer, defaulting to OFF.
|
|
4299
|
+
*
|
|
4300
|
+
* OFF is the whole point of this switch: an absent or unreadable value must
|
|
4301
|
+
* never turn the row on. See `Config.sidebarPanel` in `src/index.ts`.
|
|
4302
|
+
*/
|
|
4303
|
+
let wanted = false;
|
|
4304
|
+
/** Whether the sidebar panel should currently be registered. */
|
|
4305
|
+
function sidebarPanelWanted() {
|
|
4306
|
+
return wanted;
|
|
4307
|
+
}
|
|
4308
|
+
/**
|
|
4309
|
+
* Set the opt-in state and notify the registration controller.
|
|
4310
|
+
*
|
|
4311
|
+
* Idempotent: the controller re-registers on every call, and a settings
|
|
4312
|
+
* subscription can legitimately report the same value twice. Callers that
|
|
4313
|
+
* cannot tell whether the value changed need not check first.
|
|
4314
|
+
*
|
|
4315
|
+
* @param next - the desired state.
|
|
4316
|
+
*/
|
|
4317
|
+
function setSidebarPanelWanted(next) {
|
|
4318
|
+
if (wanted === next) return;
|
|
4319
|
+
wanted = next;
|
|
4320
|
+
for (const listener of [...listeners]) try {
|
|
4321
|
+
listener();
|
|
4322
|
+
} catch {}
|
|
4323
|
+
}
|
|
4324
|
+
/**
|
|
4325
|
+
* Observe changes to the opt-in state.
|
|
4326
|
+
*
|
|
4327
|
+
* @param listener - invoked after each change.
|
|
4328
|
+
* @returns the disposer removing this listener.
|
|
4329
|
+
*/
|
|
4330
|
+
function subscribeSidebarPanelWanted(listener) {
|
|
4331
|
+
listeners.add(listener);
|
|
4332
|
+
return () => {
|
|
4333
|
+
listeners.delete(listener);
|
|
4334
|
+
};
|
|
4335
|
+
}
|
|
4336
|
+
/**
|
|
4337
|
+
* Read the opt-in switch out of a settings snapshot.
|
|
4338
|
+
*
|
|
4339
|
+
* Only an explicit `true` is ON, matching the schema's `default(false)` and
|
|
4340
|
+
* every other switch this plugin owns: the row claims a GLOBAL sidebar seat
|
|
4341
|
+
* shared by every installed bundle, so an absent or malformed value must read
|
|
4342
|
+
* as "did not ask for this", never as consent.
|
|
4343
|
+
*
|
|
4344
|
+
* The value is deep-unwrapped because DSH 0.1.7 hands volatile-marked fields
|
|
4345
|
+
* back as `{get(): T}` live references, which are truthy non-booleans.
|
|
4346
|
+
*
|
|
4347
|
+
* @param configured - the whole settings snapshot (or any object carrying the
|
|
4348
|
+
* field).
|
|
4349
|
+
* @returns whether the panel row should exist.
|
|
4350
|
+
*/
|
|
4351
|
+
function sidebarPanelEnabledOf(configured) {
|
|
4352
|
+
const value = configured?.["sidebarPanel"];
|
|
4353
|
+
return (value !== null && typeof value === "object" && typeof value.get === "function" ? value.get() : value) === true;
|
|
4354
|
+
}
|
|
4355
|
+
//#endregion
|
|
3962
4356
|
//#region src/client/TraeUsageCard.tsx
|
|
3963
4357
|
/**
|
|
3964
4358
|
* Trae usage card contributed to Harness Plugin configuration.
|
|
@@ -4141,9 +4535,17 @@ window.__ModuleLoader__.load({
|
|
|
4141
4535
|
});
|
|
4142
4536
|
}
|
|
4143
4537
|
/** Render Trae sign-in state and the total usage summary as one expandable card. */
|
|
4144
|
-
function TraeUsageCard({ t, settingsScope, view }) {
|
|
4538
|
+
function TraeUsageCard({ t, settingsScope, view, surface, backToConversation }) {
|
|
4145
4539
|
if (t === void 0) throw new Error("Trae usage card requires its translation function");
|
|
4146
|
-
|
|
4540
|
+
/**
|
|
4541
|
+
* A PAGE surface (the optional sidebar row's `main` panel) has no card chrome
|
|
4542
|
+
* to collapse: it is always expanded, and the collapse button is not rendered,
|
|
4543
|
+
* so `setOpen` is simply unused there. Keeping ONE `open` value lets every
|
|
4544
|
+
* polling effect below stay untouched.
|
|
4545
|
+
*/
|
|
4546
|
+
const pageSurface = surface === "page";
|
|
4547
|
+
const [cardOpen, setCardOpen] = (0, react.useState)(view === "page");
|
|
4548
|
+
const open = pageSurface || cardOpen;
|
|
4147
4549
|
/** The region whose tab is on screen; each tab is its own provider stack. */
|
|
4148
4550
|
const [activeRegion, setActiveRegion] = (0, react.useState)("cn");
|
|
4149
4551
|
/**
|
|
@@ -4305,6 +4707,18 @@ window.__ModuleLoader__.load({
|
|
|
4305
4707
|
* the control would flip and then quietly snap back on the next snapshot.
|
|
4306
4708
|
*/
|
|
4307
4709
|
const [writeError, setWriteError] = (0, react.useState)(void 0);
|
|
4710
|
+
/**
|
|
4711
|
+
* The optional sidebar row's switch (see `Config.sidebarPanel`).
|
|
4712
|
+
*
|
|
4713
|
+
* `undefined` means "no local flip in flight": the box then renders the
|
|
4714
|
+
* committed value from the settings document, or — when that namespace is not
|
|
4715
|
+
* served yet — the Host's own answer on the usage document. Both are read here
|
|
4716
|
+
* rather than echoed from local state for the reason every other switch in
|
|
4717
|
+
* this card reads them: a write made elsewhere (the DSH settings form, another
|
|
4718
|
+
* window) must show up instead of being shadowed by a stale copy.
|
|
4719
|
+
*/
|
|
4720
|
+
const [sidebarPanelEdit, setSidebarPanelEdit] = (0, react.useState)(void 0);
|
|
4721
|
+
const [sidebarPanelBusy, setSidebarPanelBusy] = (0, react.useState)(false);
|
|
4308
4722
|
const mounted = (0, react.useRef)(true);
|
|
4309
4723
|
(0, react.useEffect)(() => {
|
|
4310
4724
|
mounted.current = true;
|
|
@@ -4380,15 +4794,55 @@ window.__ModuleLoader__.load({
|
|
|
4380
4794
|
}
|
|
4381
4795
|
};
|
|
4382
4796
|
/**
|
|
4383
|
-
* Flip
|
|
4797
|
+
* Flip the OPTIONAL sidebar row on or off (`Config.sidebarPanel`).
|
|
4384
4798
|
*
|
|
4385
|
-
*
|
|
4386
|
-
* `regions.<panel>`, because it is per-panel consent and the Host's tool
|
|
4387
|
-
* resolver reads it per panel. `nextRegionDrawImage` carries the rest of that
|
|
4388
|
-
* slot through, so flipping this cannot drop the model directory, budgets,
|
|
4389
|
-
* pool or credit seat — the failure #44 fixed for the pool.
|
|
4799
|
+
* Two things happen on a verified write, and BOTH are required:
|
|
4390
4800
|
*
|
|
4391
|
-
*
|
|
4801
|
+
* 1. the settings document is updated through `writeSettingsField`, which
|
|
4802
|
+
* read-back checks — a switch that only *looks* on while the Host
|
|
4803
|
+
* disagrees would leave a nav row that vanishes on the next reload;
|
|
4804
|
+
* 2. {@link setSidebarPanelWanted} publishes the value, which is what makes
|
|
4805
|
+
* the registration appear or disappear WITHOUT a reload. The row is built
|
|
4806
|
+
* from slot ENTRY METADATA, so it cannot be hidden from inside the
|
|
4807
|
+
* component — the entry has to register or dispose, and it subscribes to
|
|
4808
|
+
* that store.
|
|
4809
|
+
*
|
|
4810
|
+
* Serialized by its own `sidebarPanelBusy` rather than `savingSettings`: this
|
|
4811
|
+
* write touches one scalar, and making it share the credit switch's veil would
|
|
4812
|
+
* grey the whole card out for a nav-row toggle.
|
|
4813
|
+
*
|
|
4814
|
+
* A NOT-writable scope still flips the store. That is deliberate: the row is a
|
|
4815
|
+
* client-side registration, and a read-only host (no settings form bound yet)
|
|
4816
|
+
* would otherwise leave the user no way to open the page at all. The box reads
|
|
4817
|
+
* committed-or-Host value, so the next snapshot is what the user sees.
|
|
4818
|
+
*/
|
|
4819
|
+
const writeSidebarPanel = async (value) => {
|
|
4820
|
+
if (sidebarPanelBusy) return;
|
|
4821
|
+
setSidebarPanelBusy(true);
|
|
4822
|
+
setWriteError(void 0);
|
|
4823
|
+
setSidebarPanelEdit(value);
|
|
4824
|
+
try {
|
|
4825
|
+
if (settingsScope !== void 0 && settingsScope.getSnapshot().writable === true) await writeSettingsField(settingsScope, "sidebarPanel", value, (readBack) => sidebarPanelEnabledOf(readBack) === value);
|
|
4826
|
+
setSidebarPanelWanted(value);
|
|
4827
|
+
} catch (error) {
|
|
4828
|
+
if (mounted.current) setWriteError(error instanceof Error ? error.message : t("row.requestFailed"));
|
|
4829
|
+
} finally {
|
|
4830
|
+
if (mounted.current) {
|
|
4831
|
+
setSidebarPanelEdit(void 0);
|
|
4832
|
+
setSidebarPanelBusy(false);
|
|
4833
|
+
}
|
|
4834
|
+
}
|
|
4835
|
+
};
|
|
4836
|
+
/**
|
|
4837
|
+
* Flip ONE panel's 「允许模型自主生图」 switch (`drawImageEnabled`).
|
|
4838
|
+
*
|
|
4839
|
+
* Not a top-level preference like the two above: the flag lives INSIDE
|
|
4840
|
+
* `regions.<panel>`, because it is per-panel consent and the Host's tool
|
|
4841
|
+
* resolver reads it per panel. `nextRegionDrawImage` carries the rest of that
|
|
4842
|
+
* slot through, so flipping this cannot drop the model directory, budgets,
|
|
4843
|
+
* pool or credit seat — the failure #44 fixed for the pool.
|
|
4844
|
+
*
|
|
4845
|
+
* Serialized by `savingSettings` with the sibling writers: they all write the
|
|
4392
4846
|
* same settings document. The read-back re-reads the document, so a write that
|
|
4393
4847
|
* silently reverted cannot leave the box claiming a grant the Host never got —
|
|
4394
4848
|
* the one failure that matters here, because a box that lies about consent
|
|
@@ -5011,6 +5465,16 @@ window.__ModuleLoader__.load({
|
|
|
5011
5465
|
const slotContextBudgets = typeof regionSlot?.contextBudgets === "object" && regionSlot.contextBudgets !== null ? regionSlot.contextBudgets : void 0;
|
|
5012
5466
|
const legacyContextBudgets = legacySlot && typeof settingsValue === "object" && settingsValue !== null && typeof settingsValue.contextBudgets === "object" && settingsValue.contextBudgets !== null ? settingsValue.contextBudgets : void 0;
|
|
5013
5467
|
const savedContextBudgets = slotContextBudgets ?? legacyContextBudgets ?? {};
|
|
5468
|
+
/**
|
|
5469
|
+
* The saved thinking-level choice per model for this panel.
|
|
5470
|
+
*
|
|
5471
|
+
* A missing key is a real statement — "follow this model's own default" — so
|
|
5472
|
+
* it is preserved everywhere below as an absent entry rather than being
|
|
5473
|
+
* filled in with the level the model would run at anyway. Writing that level
|
|
5474
|
+
* here would freeze today's upstream default into the config, and the model
|
|
5475
|
+
* would then ignore upstream when it changed its mind.
|
|
5476
|
+
*/
|
|
5477
|
+
const savedThinkingEfforts = typeof regionSlot?.thinkingEfforts === "object" && regionSlot.thinkingEfforts !== null ? Object.fromEntries(Object.entries(regionSlot.thinkingEfforts).filter((entry) => typeof entry[1] === "string" && entry[1] !== "")) : {};
|
|
5014
5478
|
const slotImageIds = Array.isArray(regionSlot?.imageModelIds) ? regionSlot.imageModelIds.filter((id) => typeof id === "string") : void 0;
|
|
5015
5479
|
const legacyImageIds = legacySlot && typeof settingsValue === "object" && settingsValue !== null && Array.isArray(settingsValue.imageModelIds) ? settingsValue.imageModelIds.filter((id) => typeof id === "string") : [];
|
|
5016
5480
|
const savedImageIds = new Set(slotImageIds ?? legacyImageIds);
|
|
@@ -5132,6 +5596,7 @@ window.__ModuleLoader__.load({
|
|
|
5132
5596
|
* the single surface.
|
|
5133
5597
|
*/
|
|
5134
5598
|
const activeContextBudgets = draft?.contextBudgets ?? savedContextBudgets;
|
|
5599
|
+
const activeThinkingEfforts = draft?.thinkingEfforts ?? savedThinkingEfforts;
|
|
5135
5600
|
const dirty = draft !== void 0;
|
|
5136
5601
|
/** Apply an edit to the ACTIVE tab's draft, seeding it from the current view. */
|
|
5137
5602
|
const editDraft = (edit) => {
|
|
@@ -5141,7 +5606,8 @@ window.__ModuleLoader__.load({
|
|
|
5141
5606
|
models: [...visibleModels],
|
|
5142
5607
|
enabledIds: new Set(activeEnabledIds),
|
|
5143
5608
|
imageIds: new Set(activeImageIds),
|
|
5144
|
-
contextBudgets: { ...activeContextBudgets }
|
|
5609
|
+
contextBudgets: { ...activeContextBudgets },
|
|
5610
|
+
thinkingEfforts: { ...activeThinkingEfforts }
|
|
5145
5611
|
})
|
|
5146
5612
|
}));
|
|
5147
5613
|
};
|
|
@@ -5177,6 +5643,25 @@ window.__ModuleLoader__.load({
|
|
|
5177
5643
|
});
|
|
5178
5644
|
};
|
|
5179
5645
|
/**
|
|
5646
|
+
* Record one model's thinking level; an empty value means "follow the
|
|
5647
|
+
* upstream default" and therefore DELETES the key.
|
|
5648
|
+
*
|
|
5649
|
+
* Deletion is the point. Keeping the level that happens to equal today's
|
|
5650
|
+
* default would look identical on screen while quietly pinning it — the model
|
|
5651
|
+
* would stop following upstream the moment Trae changed its own default.
|
|
5652
|
+
*/
|
|
5653
|
+
const setThinkingEffort = (modelId, effort) => {
|
|
5654
|
+
editDraft((current) => {
|
|
5655
|
+
const next = { ...current.thinkingEfforts };
|
|
5656
|
+
if (effort === "") delete next[modelId];
|
|
5657
|
+
else next[modelId] = effort;
|
|
5658
|
+
return {
|
|
5659
|
+
...current,
|
|
5660
|
+
thinkingEfforts: next
|
|
5661
|
+
};
|
|
5662
|
+
});
|
|
5663
|
+
};
|
|
5664
|
+
/**
|
|
5180
5665
|
* Bulk context-budget switch (issue #42 side request). The per-model radios
|
|
5181
5666
|
* stay exactly as they are; these two act on the whole region draft at once.
|
|
5182
5667
|
* The reporter counted 18 CN models whose Max window differs from the
|
|
@@ -5252,11 +5737,15 @@ window.__ModuleLoader__.load({
|
|
|
5252
5737
|
})),
|
|
5253
5738
|
enabledModelIds: [...activeEnabledIds],
|
|
5254
5739
|
imageModelIds: [...activeImageIds].filter((id) => activeEnabledIds.has(id)),
|
|
5255
|
-
contextBudgets: activeContextBudgets
|
|
5740
|
+
contextBudgets: activeContextBudgets,
|
|
5741
|
+
thinkingEfforts: activeThinkingEfforts
|
|
5256
5742
|
};
|
|
5257
5743
|
await writeSettingsField(settingsScope, "regions", nextRegionDirectory(configuredRegions, activeRegion, nextSlot), (readBack) => {
|
|
5258
|
-
const
|
|
5259
|
-
|
|
5744
|
+
const slot = regionSlotsOf(readBack)[activeRegion];
|
|
5745
|
+
const ids = slot?.enabledModelIds;
|
|
5746
|
+
if (!Array.isArray(ids) || ids.length !== nextSlot.enabledModelIds.length || !nextSlot.enabledModelIds.every((id) => ids.includes(id))) return false;
|
|
5747
|
+
const savedLevels = slot?.thinkingEfforts;
|
|
5748
|
+
return (typeof savedLevels === "object" && savedLevels !== null ? Object.keys(savedLevels).filter((id) => activeThinkingEfforts[id] !== void 0) : []).length === Object.keys(activeThinkingEfforts).length;
|
|
5260
5749
|
});
|
|
5261
5750
|
discardModels();
|
|
5262
5751
|
await refreshUsage(activeRegion);
|
|
@@ -5266,6 +5755,49 @@ window.__ModuleLoader__.load({
|
|
|
5266
5755
|
if (mounted.current) setSaving(false);
|
|
5267
5756
|
}
|
|
5268
5757
|
};
|
|
5758
|
+
/**
|
|
5759
|
+
* The thinking-level picker for one model row, or nothing when Trae published
|
|
5760
|
+
* no tier list for it.
|
|
5761
|
+
*
|
|
5762
|
+
* The first option is deliberately the interesting one: it names the level
|
|
5763
|
+
* this model will actually run at with no choice made. A picker that only
|
|
5764
|
+
* listed tiers would leave "nothing chosen" unlabelled, so the reader cannot
|
|
5765
|
+
* tell what they are overriding — and the previous UI could not state it at
|
|
5766
|
+
* all, which is why DSH's own picker said `Default`.
|
|
5767
|
+
*
|
|
5768
|
+
* Options are the model's OWN tiers, unlocalised: `low / high / xhigh` need no
|
|
5769
|
+
* translation, and a shared ladder across models would list levels that cannot
|
|
5770
|
+
* be selected.
|
|
5771
|
+
*/
|
|
5772
|
+
const effortPicker = (model) => {
|
|
5773
|
+
const efforts = model.reasoning?.supported ?? [];
|
|
5774
|
+
if (efforts.length === 0) return null;
|
|
5775
|
+
const chosen = activeThinkingEfforts[model.id] ?? "";
|
|
5776
|
+
const upstream = model.reasoning?.defaultEffort;
|
|
5777
|
+
const effective = defaultEffortFor({
|
|
5778
|
+
id: model.id,
|
|
5779
|
+
supported: efforts,
|
|
5780
|
+
defaultEffort: upstream
|
|
5781
|
+
}, activeThinkingEfforts);
|
|
5782
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
5783
|
+
className: "dsm-trae-model-effort",
|
|
5784
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("row.modelReasoningLabel") }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
|
|
5785
|
+
className: "dsm-trae-model-effort-select",
|
|
5786
|
+
disabled: settingsScope?.getSnapshot().writable !== true || saving,
|
|
5787
|
+
value: chosen,
|
|
5788
|
+
onChange: (event) => {
|
|
5789
|
+
setThinkingEffort(model.id, event.target.value);
|
|
5790
|
+
},
|
|
5791
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
5792
|
+
value: "",
|
|
5793
|
+
children: effective === void 0 ? t("row.modelReasoningFollowUpstream") : t("row.modelReasoningFollowUpstreamNamed", { effort: effective })
|
|
5794
|
+
}), efforts.map((effort) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
5795
|
+
value: effort,
|
|
5796
|
+
children: effort
|
|
5797
|
+
}, effort))]
|
|
5798
|
+
})] })
|
|
5799
|
+
});
|
|
5800
|
+
};
|
|
5269
5801
|
const title = t("row.title");
|
|
5270
5802
|
/**
|
|
5271
5803
|
* Row labels for the account table.
|
|
@@ -5431,787 +5963,862 @@ window.__ModuleLoader__.load({
|
|
|
5431
5963
|
}];
|
|
5432
5964
|
}));
|
|
5433
5965
|
const label = loading ? t("row.accountsLoading") : status.status === "signed-in" ? t("row.signedIn", { accountName: status.accountName }) : status.status === "error" ? t("row.requestFailed") : t("row.signedOut");
|
|
5434
|
-
|
|
5435
|
-
|
|
5966
|
+
/**
|
|
5967
|
+
* Whether the sidebar row's box is ticked.
|
|
5968
|
+
*
|
|
5969
|
+
* Precedence: an in-flight flip, then the committed settings value when this
|
|
5970
|
+
* namespace is actually served, then the HOST's answer on the usage document.
|
|
5971
|
+
* The Host is the fallback rather than the first source because the card can
|
|
5972
|
+
* be bound before the mirror settles; it is consulted at all because that
|
|
5973
|
+
* mirror is documented as stale on the affected 0.1.7 deployments, so a stale
|
|
5974
|
+
* `false` alone would make a served row look switched off.
|
|
5975
|
+
*/
|
|
5976
|
+
const sidebarPanelOn = sidebarPanelEdit ?? (settingsScope === void 0 ? void 0 : sidebarPanelEnabledOf(unwrapVolatileDeep(settingsScope.getSnapshot().value))) ?? loaded?.sidebarPanelEnabled === true;
|
|
5977
|
+
/**
|
|
5978
|
+
* The body BOTH surfaces draw: the region tabs, the section tabs and every
|
|
5979
|
+
* section below them.
|
|
5980
|
+
*
|
|
5981
|
+
* Kept as one value because a page duplicate would be a copy that drifts —
|
|
5982
|
+
* the SURFACE changes the chrome around this tree, nothing inside it.
|
|
5983
|
+
*/
|
|
5984
|
+
const cardBody = /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
5985
|
+
className: "dsm-trae-usage",
|
|
5436
5986
|
children: [
|
|
5437
|
-
/* @__PURE__ */ (0, react_jsx_runtime.
|
|
5438
|
-
|
|
5439
|
-
|
|
5440
|
-
"aria-
|
|
5441
|
-
|
|
5442
|
-
|
|
5443
|
-
|
|
5444
|
-
|
|
5987
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
5988
|
+
className: "dsm-trae-tabs",
|
|
5989
|
+
role: "tablist",
|
|
5990
|
+
"aria-label": title,
|
|
5991
|
+
children: TRAE_PANELS.map((item) => {
|
|
5992
|
+
const regionStatus = statusByRegion[item];
|
|
5993
|
+
const on = regionOn(item);
|
|
5994
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
5995
|
+
className: "dsm-trae-tab-cell",
|
|
5996
|
+
role: "presentation",
|
|
5997
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
5998
|
+
type: "button",
|
|
5999
|
+
role: "tab",
|
|
6000
|
+
"aria-selected": item === activeRegion,
|
|
6001
|
+
className: `dsm-trae-tab${item === activeRegion ? " dsm-trae-tab-active" : ""}${on ? "" : " dsm-trae-tab-off"}`,
|
|
6002
|
+
onClick: () => {
|
|
6003
|
+
setActiveRegion(item);
|
|
6004
|
+
},
|
|
6005
|
+
children: [regionStatus === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
6006
|
+
"aria-hidden": "true",
|
|
6007
|
+
className: "dsm-trae-tab-dot",
|
|
6008
|
+
style: dotStyle(regionStatus.status)
|
|
6009
|
+
}), item === "cn" ? t("row.tabCn") : item === "enterprise" ? t("row.tabEnterprise") : t("row.tabAi")]
|
|
6010
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("label", {
|
|
6011
|
+
className: "dsm-trae-switch dsm-trae-tab-switch",
|
|
6012
|
+
title: t("row.tabSwitchHint"),
|
|
6013
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
6014
|
+
type: "checkbox",
|
|
6015
|
+
checked: on,
|
|
6016
|
+
disabled: settingsScope?.getSnapshot().writable !== true || togglingRegion !== void 0,
|
|
6017
|
+
"aria-label": t("row.tabSwitchAria", { region: item === "cn" ? t("row.tabCn") : item === "enterprise" ? t("row.tabEnterprise") : t("row.tabAi") }),
|
|
6018
|
+
onChange: (event) => {
|
|
6019
|
+
toggleRegion(item, event.currentTarget.checked);
|
|
6020
|
+
}
|
|
6021
|
+
})
|
|
6022
|
+
})]
|
|
6023
|
+
}, item);
|
|
6024
|
+
})
|
|
6025
|
+
}),
|
|
6026
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
6027
|
+
className: "dsm-trae-tab-hint",
|
|
6028
|
+
children: t("row.tabHint")
|
|
6029
|
+
}),
|
|
6030
|
+
writeError === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
6031
|
+
className: "dsm-trae-usage-error",
|
|
6032
|
+
role: "alert",
|
|
6033
|
+
children: t("row.settingsWriteFailed", { message: writeError })
|
|
6034
|
+
}),
|
|
6035
|
+
regionOn(activeRegion) ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
6036
|
+
className: "dsm-trae-tab-off-notice",
|
|
6037
|
+
role: "status",
|
|
6038
|
+
children: t("row.tabOffNotice")
|
|
6039
|
+
}),
|
|
6040
|
+
signedIn ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
6041
|
+
className: "dsm-trae-panels",
|
|
6042
|
+
role: "tablist",
|
|
6043
|
+
"aria-label": t("row.panelAria"),
|
|
5445
6044
|
children: [
|
|
5446
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("
|
|
5447
|
-
|
|
5448
|
-
|
|
5449
|
-
|
|
6045
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
6046
|
+
type: "button",
|
|
6047
|
+
role: "tab",
|
|
6048
|
+
id: "dsm-trae-tab-accounts",
|
|
6049
|
+
"aria-controls": "dsm-trae-panel-accounts",
|
|
6050
|
+
"aria-selected": panel === "accounts",
|
|
6051
|
+
className: `dsm-trae-panel-tab${panel === "accounts" ? " dsm-trae-panel-tab-active" : ""}`,
|
|
6052
|
+
onClick: () => {
|
|
6053
|
+
setPanel("accounts");
|
|
6054
|
+
},
|
|
6055
|
+
children: t("row.panelAccounts")
|
|
5450
6056
|
}),
|
|
5451
|
-
/* @__PURE__ */ (0, react_jsx_runtime.
|
|
5452
|
-
|
|
5453
|
-
|
|
5454
|
-
|
|
5455
|
-
|
|
5456
|
-
|
|
5457
|
-
|
|
5458
|
-
|
|
5459
|
-
|
|
6057
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
6058
|
+
type: "button",
|
|
6059
|
+
role: "tab",
|
|
6060
|
+
id: "dsm-trae-tab-models",
|
|
6061
|
+
"aria-controls": "dsm-trae-panel-models",
|
|
6062
|
+
"aria-selected": panel === "models",
|
|
6063
|
+
className: `dsm-trae-panel-tab${panel === "models" ? " dsm-trae-panel-tab-active" : ""}`,
|
|
6064
|
+
onClick: () => {
|
|
6065
|
+
setPanel("models");
|
|
6066
|
+
},
|
|
6067
|
+
children: t("row.panelModels")
|
|
5460
6068
|
}),
|
|
5461
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("
|
|
5462
|
-
|
|
5463
|
-
|
|
6069
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
6070
|
+
type: "button",
|
|
6071
|
+
role: "tab",
|
|
6072
|
+
id: "dsm-trae-tab-diag",
|
|
6073
|
+
"aria-controls": "dsm-trae-panel-diag",
|
|
6074
|
+
"aria-selected": panel === "diag",
|
|
6075
|
+
className: `dsm-trae-panel-tab${panel === "diag" ? " dsm-trae-panel-tab-active" : ""}`,
|
|
6076
|
+
onClick: () => {
|
|
6077
|
+
setPanel("diag");
|
|
6078
|
+
},
|
|
6079
|
+
children: t("row.panelDiag")
|
|
5464
6080
|
})
|
|
5465
6081
|
]
|
|
6082
|
+
}) : null,
|
|
6083
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
6084
|
+
className: "dsm-trae-panel",
|
|
6085
|
+
role: "tabpanel",
|
|
6086
|
+
id: "dsm-trae-panel-accounts",
|
|
6087
|
+
"aria-labelledby": "dsm-trae-tab-accounts",
|
|
6088
|
+
hidden: signedIn && panel !== "accounts",
|
|
6089
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(TraeAccountPanel, {
|
|
6090
|
+
t,
|
|
6091
|
+
settingsScope,
|
|
6092
|
+
panel: activeRegion,
|
|
6093
|
+
status,
|
|
6094
|
+
statusLabel: label,
|
|
6095
|
+
credentialRegionIsAi,
|
|
6096
|
+
regionOn: activeRegionOn,
|
|
6097
|
+
showPointsInMainUi: settingsEdits.showPointsInMainUi,
|
|
6098
|
+
onToggleShowPoints: (value) => {
|
|
6099
|
+
writePreference("showPointsInMainUi", value);
|
|
6100
|
+
},
|
|
6101
|
+
creditPosition: creditPositionOn(activeRegion),
|
|
6102
|
+
onCreditPositionChange: (position) => {
|
|
6103
|
+
changeCreditPosition(activeRegion, position);
|
|
6104
|
+
},
|
|
6105
|
+
sidebarPanelOn,
|
|
6106
|
+
sidebarPanelBusy,
|
|
6107
|
+
onToggleSidebarPanel: (value) => {
|
|
6108
|
+
writeSidebarPanel(value);
|
|
6109
|
+
},
|
|
6110
|
+
savingPreferences: savingSettings,
|
|
6111
|
+
creditsLabelId,
|
|
6112
|
+
creditsHintId,
|
|
6113
|
+
rows: accountRows,
|
|
6114
|
+
busy,
|
|
6115
|
+
rescanNote,
|
|
6116
|
+
onRescan: () => {
|
|
6117
|
+
rescanAccounts();
|
|
6118
|
+
},
|
|
6119
|
+
switchingAccount,
|
|
6120
|
+
onSwitchAccount: (accountId) => {
|
|
6121
|
+
switchAccount(accountId);
|
|
6122
|
+
},
|
|
6123
|
+
onClaim: (accountId) => {
|
|
6124
|
+
claimFor(accountId);
|
|
6125
|
+
},
|
|
6126
|
+
onTransferChanged: async () => {
|
|
6127
|
+
const fresh = await refreshUsage(activeRegion);
|
|
6128
|
+
if (fresh !== void 0) setStatusByRegion((current) => ({
|
|
6129
|
+
...current,
|
|
6130
|
+
[activeRegion]: fresh
|
|
6131
|
+
}));
|
|
6132
|
+
},
|
|
6133
|
+
poolDetails,
|
|
6134
|
+
poolClaims: status.status === "signed-in" && status.pool?.checkinSupported === true ? Object.fromEntries(status.accounts.map((account) => [account.id, claimViewOf(account.id, accountStatusByRegion[activeRegion][account.id])])) : void 0,
|
|
6135
|
+
models: visibleModels,
|
|
6136
|
+
deviceClaimed: deviceClaimedToday,
|
|
6137
|
+
onPoolChanged: async () => {
|
|
6138
|
+
const fresh = await refreshUsage(activeRegion);
|
|
6139
|
+
if (fresh !== void 0) setStatusByRegion((current) => ({
|
|
6140
|
+
...current,
|
|
6141
|
+
[activeRegion]: fresh
|
|
6142
|
+
}));
|
|
6143
|
+
},
|
|
6144
|
+
onPoolRefresh: async () => {
|
|
6145
|
+
const fresh = await refreshUsage(activeRegion);
|
|
6146
|
+
if (fresh !== void 0) setStatusByRegion((current) => ({
|
|
6147
|
+
...current,
|
|
6148
|
+
[activeRegion]: fresh
|
|
6149
|
+
}));
|
|
6150
|
+
},
|
|
6151
|
+
creditsUnavailable: status.status === "signed-in" ? status.creditsUnavailable : void 0,
|
|
6152
|
+
creditsExpiry: status.status === "signed-in" ? status.credits?.expiry : void 0,
|
|
6153
|
+
alternatives: status.status === "signed-in" ? status.alternatives : void 0,
|
|
6154
|
+
onSwitchToAlternative: (accountId) => {
|
|
6155
|
+
switchAccount(accountId);
|
|
6156
|
+
}
|
|
6157
|
+
})
|
|
5466
6158
|
}),
|
|
5467
6159
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
5468
|
-
className: "dsm-
|
|
5469
|
-
|
|
5470
|
-
|
|
5471
|
-
|
|
6160
|
+
className: "dsm-trae-panel",
|
|
6161
|
+
role: "tabpanel",
|
|
6162
|
+
id: "dsm-trae-panel-models",
|
|
6163
|
+
"aria-labelledby": "dsm-trae-tab-models",
|
|
6164
|
+
hidden: !signedIn || panel !== "models",
|
|
6165
|
+
children: signedIn ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_jsx_runtime.Fragment, { children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
|
|
6166
|
+
className: "dsm-trae-models",
|
|
6167
|
+
"aria-label": t("row.modelsTitle"),
|
|
5472
6168
|
children: [
|
|
5473
|
-
/* @__PURE__ */ (0, react_jsx_runtime.
|
|
5474
|
-
className: "dsm-trae-
|
|
5475
|
-
|
|
5476
|
-
|
|
5477
|
-
|
|
5478
|
-
|
|
5479
|
-
|
|
5480
|
-
|
|
5481
|
-
|
|
5482
|
-
|
|
5483
|
-
|
|
6169
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
6170
|
+
className: "dsm-trae-models-head",
|
|
6171
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", {
|
|
6172
|
+
className: "dsm-trae-models-title",
|
|
6173
|
+
children: t("row.modelsTitle")
|
|
6174
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
6175
|
+
className: "dsm-trae-models-summary",
|
|
6176
|
+
children: t("row.modelsSummary", { count: activeEnabledIds.size })
|
|
6177
|
+
})] }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
6178
|
+
className: "dsm-trae-models-actions",
|
|
6179
|
+
children: [
|
|
6180
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
6181
|
+
type: "button",
|
|
6182
|
+
className: "dsm-btn dsm-btn-outline",
|
|
6183
|
+
disabled: busy,
|
|
6184
|
+
onClick: () => {
|
|
6185
|
+
refreshModels();
|
|
6186
|
+
},
|
|
6187
|
+
children: busy ? t("row.modelsRefreshing") : t("row.modelsRefresh")
|
|
6188
|
+
}),
|
|
6189
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
5484
6190
|
type: "button",
|
|
5485
|
-
|
|
5486
|
-
|
|
5487
|
-
|
|
6191
|
+
className: "dsm-btn dsm-btn-outline",
|
|
6192
|
+
disabled: busy || testing !== void 0 || activeEnabledIds.size === 0,
|
|
6193
|
+
title: t("row.modelsTestHint"),
|
|
5488
6194
|
onClick: () => {
|
|
5489
|
-
|
|
6195
|
+
testEnabledModels();
|
|
5490
6196
|
},
|
|
5491
|
-
children:
|
|
5492
|
-
|
|
5493
|
-
|
|
5494
|
-
style: dotStyle(regionStatus.status)
|
|
5495
|
-
}), item === "cn" ? t("row.tabCn") : item === "enterprise" ? t("row.tabEnterprise") : t("row.tabAi")]
|
|
5496
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("label", {
|
|
5497
|
-
className: "dsm-trae-switch dsm-trae-tab-switch",
|
|
5498
|
-
title: t("row.tabSwitchHint"),
|
|
5499
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
5500
|
-
type: "checkbox",
|
|
5501
|
-
checked: on,
|
|
5502
|
-
disabled: settingsScope?.getSnapshot().writable !== true || togglingRegion !== void 0,
|
|
5503
|
-
"aria-label": t("row.tabSwitchAria", { region: item === "cn" ? t("row.tabCn") : item === "enterprise" ? t("row.tabEnterprise") : t("row.tabAi") }),
|
|
5504
|
-
onChange: (event) => {
|
|
5505
|
-
toggleRegion(item, event.currentTarget.checked);
|
|
5506
|
-
}
|
|
6197
|
+
children: testing === void 0 ? t("row.modelsTestEnabled") : t("row.modelsTesting", {
|
|
6198
|
+
done: testing.done,
|
|
6199
|
+
total: testing.total
|
|
5507
6200
|
})
|
|
5508
|
-
})
|
|
5509
|
-
|
|
5510
|
-
|
|
6201
|
+
}),
|
|
6202
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
6203
|
+
type: "button",
|
|
6204
|
+
className: "dsm-btn dsm-btn-outline",
|
|
6205
|
+
disabled: busy || testing !== void 0 || visibleModels.length === 0,
|
|
6206
|
+
title: t("row.modelsTestAllHint"),
|
|
6207
|
+
onClick: () => {
|
|
6208
|
+
testAllModels();
|
|
6209
|
+
},
|
|
6210
|
+
children: testing === void 0 ? t("row.modelsTestAll") : t("row.modelsTesting", {
|
|
6211
|
+
done: testing.done,
|
|
6212
|
+
total: testing.total
|
|
6213
|
+
})
|
|
6214
|
+
})
|
|
6215
|
+
]
|
|
6216
|
+
})]
|
|
5511
6217
|
}),
|
|
5512
6218
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
5513
|
-
className: "dsm-trae-
|
|
5514
|
-
children: t("row.
|
|
6219
|
+
className: "dsm-trae-models-hint",
|
|
6220
|
+
children: t("row.modelsTestHint")
|
|
6221
|
+
}),
|
|
6222
|
+
testSummary === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
6223
|
+
className: "dsm-trae-models-summary",
|
|
6224
|
+
children: testSummary
|
|
5515
6225
|
}),
|
|
5516
|
-
|
|
6226
|
+
testError === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
5517
6227
|
className: "dsm-trae-usage-error",
|
|
5518
6228
|
role: "alert",
|
|
5519
|
-
children:
|
|
6229
|
+
children: testError
|
|
5520
6230
|
}),
|
|
5521
|
-
|
|
5522
|
-
className: "dsm-trae-
|
|
5523
|
-
role: "
|
|
5524
|
-
|
|
6231
|
+
orderedModels.length > commonModelCount ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
6232
|
+
className: "dsm-trae-models-view",
|
|
6233
|
+
role: "group",
|
|
6234
|
+
"aria-label": t("row.modelsViewLabel"),
|
|
6235
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
6236
|
+
type: "button",
|
|
6237
|
+
className: "dsm-trae-models-view-opt",
|
|
6238
|
+
"aria-pressed": modelView === "common",
|
|
6239
|
+
title: t("row.modelsViewCommonHint"),
|
|
6240
|
+
onClick: () => {
|
|
6241
|
+
setModelView("common");
|
|
6242
|
+
},
|
|
6243
|
+
children: t("row.modelsViewCommon", { count: commonModelCount })
|
|
6244
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
6245
|
+
type: "button",
|
|
6246
|
+
className: "dsm-trae-models-view-opt",
|
|
6247
|
+
"aria-pressed": modelView === "all",
|
|
6248
|
+
title: t("row.modelsViewAllHint"),
|
|
6249
|
+
onClick: () => {
|
|
6250
|
+
setModelView("all");
|
|
6251
|
+
},
|
|
6252
|
+
children: t("row.modelsViewAll", { count: orderedModels.length })
|
|
6253
|
+
})]
|
|
6254
|
+
}) : null,
|
|
6255
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
6256
|
+
className: "dsm-trae-models-search",
|
|
6257
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
6258
|
+
type: "search",
|
|
6259
|
+
className: "dsm-trae-models-search-input",
|
|
6260
|
+
value: modelQuery,
|
|
6261
|
+
placeholder: t("row.modelsSearchPlaceholder"),
|
|
6262
|
+
"aria-label": t("row.modelsSearch"),
|
|
6263
|
+
onChange: (event) => {
|
|
6264
|
+
setModelQuery(event.target.value);
|
|
6265
|
+
setClosedModelGroups(/* @__PURE__ */ new Set());
|
|
6266
|
+
}
|
|
6267
|
+
}), modelQueryActive ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
6268
|
+
className: "dsm-trae-models-search-count",
|
|
6269
|
+
children: t("row.modelsSearchCount", {
|
|
6270
|
+
shown: shownModels.length,
|
|
6271
|
+
total: scopedModels.length
|
|
6272
|
+
})
|
|
6273
|
+
}) : null]
|
|
5525
6274
|
}),
|
|
5526
|
-
|
|
5527
|
-
className: "dsm-trae-
|
|
5528
|
-
|
|
5529
|
-
|
|
5530
|
-
|
|
5531
|
-
|
|
6275
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
6276
|
+
className: "dsm-trae-model-list",
|
|
6277
|
+
children: [modelGroups.length === 0 ? matchesOutsideView > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("p", {
|
|
6278
|
+
className: "dsm-trae-models-empty",
|
|
6279
|
+
children: [t("row.modelsSearchOutsideView", {
|
|
6280
|
+
query: modelQuery.trim(),
|
|
6281
|
+
count: matchesOutsideView
|
|
6282
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
5532
6283
|
type: "button",
|
|
5533
|
-
|
|
5534
|
-
id: "dsm-trae-tab-accounts",
|
|
5535
|
-
"aria-controls": "dsm-trae-panel-accounts",
|
|
5536
|
-
"aria-selected": panel === "accounts",
|
|
5537
|
-
className: `dsm-trae-panel-tab${panel === "accounts" ? " dsm-trae-panel-tab-active" : ""}`,
|
|
6284
|
+
className: "dsm-trae-models-widen",
|
|
5538
6285
|
onClick: () => {
|
|
5539
|
-
|
|
6286
|
+
setModelView("all");
|
|
6287
|
+
},
|
|
6288
|
+
children: t("row.modelsViewShowAll")
|
|
6289
|
+
})]
|
|
6290
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
6291
|
+
className: "dsm-trae-models-empty",
|
|
6292
|
+
children: t("row.modelsSearchEmpty", { query: modelQuery.trim() })
|
|
6293
|
+
}) : null, modelGroups.map((group) => {
|
|
6294
|
+
const groupKey = `${activeRegion}|${group.channel ?? ""}`;
|
|
6295
|
+
const groupOpen = modelQueryActive || !closedModelGroups.has(groupKey);
|
|
6296
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("details", {
|
|
6297
|
+
className: "dsm-trae-model-group",
|
|
6298
|
+
open: groupOpen,
|
|
6299
|
+
onToggle: (event) => {
|
|
6300
|
+
const open = event.currentTarget.open;
|
|
6301
|
+
setClosedModelGroups((current) => {
|
|
6302
|
+
if (open === !current.has(groupKey)) return current;
|
|
6303
|
+
const next = new Set(current);
|
|
6304
|
+
if (open) next.delete(groupKey);
|
|
6305
|
+
else next.add(groupKey);
|
|
6306
|
+
return next;
|
|
6307
|
+
});
|
|
5540
6308
|
},
|
|
5541
|
-
children:
|
|
6309
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("summary", {
|
|
6310
|
+
className: "dsm-trae-model-group-head",
|
|
6311
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
6312
|
+
className: "dsm-trae-model-group-name",
|
|
6313
|
+
children: modelChannelLabel(group.channel, t)
|
|
6314
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
6315
|
+
className: "dsm-trae-model-group-count",
|
|
6316
|
+
children: t("row.modelsGroupCount", { count: group.rows.length })
|
|
6317
|
+
})]
|
|
6318
|
+
}), group.rows.map((model) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
6319
|
+
className: `dsm-trae-model${activeEnabledIds.has(model.id) ? "" : " dsm-trae-model-disabled"}`,
|
|
6320
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
6321
|
+
className: "dsm-trae-model-head",
|
|
6322
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
6323
|
+
className: "dsm-trae-model-enabled",
|
|
6324
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
6325
|
+
type: "checkbox",
|
|
6326
|
+
checked: activeEnabledIds.has(model.id),
|
|
6327
|
+
disabled: settingsScope?.getSnapshot().writable !== true || saving,
|
|
6328
|
+
onChange: () => {
|
|
6329
|
+
toggleModel(model.id);
|
|
6330
|
+
}
|
|
6331
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
6332
|
+
className: "dsm-trae-model-copy",
|
|
6333
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
6334
|
+
className: "dsm-trae-model-name",
|
|
6335
|
+
children: [
|
|
6336
|
+
model.name,
|
|
6337
|
+
activeRegion === "enterprise" || model.creditMultiplier === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
6338
|
+
className: "dsm-trae-model-name-rate",
|
|
6339
|
+
children: ["· x", model.creditMultiplier.toFixed(2)]
|
|
6340
|
+
}),
|
|
6341
|
+
probeBadge(probeByRegion[activeRegion][model.id], t)
|
|
6342
|
+
]
|
|
6343
|
+
}), promoBadge(model, t, nowForPromo)]
|
|
6344
|
+
})]
|
|
6345
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
6346
|
+
className: "dsm-trae-model-options",
|
|
6347
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
6348
|
+
className: "dsm-trae-model-image",
|
|
6349
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
6350
|
+
type: "checkbox",
|
|
6351
|
+
checked: rowAcceptsImage(model),
|
|
6352
|
+
disabled: rowImageIsAutomatic(model) || settingsScope?.getSnapshot().writable !== true || saving,
|
|
6353
|
+
title: rowImageIsAutomatic(model) ? t("row.modelImageAuto") : void 0,
|
|
6354
|
+
onChange: () => {
|
|
6355
|
+
toggleImage(model.id);
|
|
6356
|
+
}
|
|
6357
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("row.modelImage") })]
|
|
6358
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("fieldset", {
|
|
6359
|
+
className: "dsm-trae-context-budget",
|
|
6360
|
+
"aria-label": t("row.contextBudget"),
|
|
6361
|
+
title: t("row.contextBudgetHint"),
|
|
6362
|
+
children: contextBudgetOptions(model).map((option) => {
|
|
6363
|
+
const selectable = model.maxContextWindow !== void 0;
|
|
6364
|
+
const checked = (activeContextBudgets[model.id] ?? model.contextWindow) === option;
|
|
6365
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
6366
|
+
type: "radio",
|
|
6367
|
+
name: `context-${model.id}`,
|
|
6368
|
+
checked,
|
|
6369
|
+
disabled: !selectable || settingsScope?.getSnapshot().writable !== true || saving,
|
|
6370
|
+
onChange: () => {
|
|
6371
|
+
setContextBudget(model.id, option);
|
|
6372
|
+
}
|
|
6373
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: formatCapacity(option, t("row.modelUnknown")) })] }, option);
|
|
6374
|
+
})
|
|
6375
|
+
})]
|
|
6376
|
+
})]
|
|
6377
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
6378
|
+
className: "dsm-trae-model-meta",
|
|
6379
|
+
children: [
|
|
6380
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("row.modelContext", { context: formatCapacity(model.maxContextWindow ?? model.contextWindow, t("row.modelUnknown")) }) }),
|
|
6381
|
+
model.maxTokens === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("row.modelOutput", { output: formatCapacity(model.maxTokens, t("row.modelUnknown")) }) }),
|
|
6382
|
+
effortPicker(model)
|
|
6383
|
+
]
|
|
6384
|
+
})]
|
|
6385
|
+
}, model.id))]
|
|
6386
|
+
}, groupKey);
|
|
6387
|
+
})]
|
|
6388
|
+
}),
|
|
6389
|
+
enabledOutsideView.length === 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("p", {
|
|
6390
|
+
className: "dsm-trae-models-note",
|
|
6391
|
+
children: [t("row.modelsEnabledOutsideView", { count: enabledOutsideView.length }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
6392
|
+
type: "button",
|
|
6393
|
+
className: "dsm-trae-models-widen",
|
|
6394
|
+
onClick: () => {
|
|
6395
|
+
setModelView("all");
|
|
6396
|
+
},
|
|
6397
|
+
children: t("row.modelsViewShowAll")
|
|
6398
|
+
})]
|
|
6399
|
+
}),
|
|
6400
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
6401
|
+
className: "dsm-trae-context-bulk",
|
|
6402
|
+
children: [
|
|
6403
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
6404
|
+
type: "button",
|
|
6405
|
+
className: "dsm-btn dsm-btn-outline",
|
|
6406
|
+
disabled: busy || saving || settingsScope?.getSnapshot().writable !== true || !visibleModels.some((model) => contextBudgetOptions(model).includes(5e5)),
|
|
6407
|
+
title: t("row.contextAllMidHint"),
|
|
6408
|
+
onClick: setAllContextMid,
|
|
6409
|
+
children: t("row.contextAllMid")
|
|
5542
6410
|
}),
|
|
5543
6411
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
5544
6412
|
type: "button",
|
|
5545
|
-
|
|
5546
|
-
|
|
5547
|
-
|
|
5548
|
-
|
|
5549
|
-
|
|
5550
|
-
onClick: () => {
|
|
5551
|
-
setPanel("models");
|
|
5552
|
-
},
|
|
5553
|
-
children: t("row.panelModels")
|
|
6413
|
+
className: "dsm-btn dsm-btn-outline",
|
|
6414
|
+
disabled: busy || saving || settingsScope?.getSnapshot().writable !== true || !visibleModels.some((model) => model.maxContextWindow !== void 0),
|
|
6415
|
+
title: t("row.contextAllMaxHint"),
|
|
6416
|
+
onClick: setAllContextMax,
|
|
6417
|
+
children: t("row.contextAllMax")
|
|
5554
6418
|
}),
|
|
5555
6419
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
5556
6420
|
type: "button",
|
|
5557
|
-
|
|
5558
|
-
|
|
5559
|
-
|
|
5560
|
-
|
|
5561
|
-
|
|
5562
|
-
onClick: () => {
|
|
5563
|
-
setPanel("diag");
|
|
5564
|
-
},
|
|
5565
|
-
children: t("row.panelDiag")
|
|
6421
|
+
className: "dsm-btn dsm-btn-outline",
|
|
6422
|
+
disabled: busy || saving || settingsScope?.getSnapshot().writable !== true || Object.keys(activeContextBudgets).length === 0,
|
|
6423
|
+
title: t("row.contextAllDefaultHint"),
|
|
6424
|
+
onClick: resetAllContextBudgets,
|
|
6425
|
+
children: t("row.contextAllDefault")
|
|
5566
6426
|
})
|
|
5567
6427
|
]
|
|
5568
|
-
})
|
|
5569
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("
|
|
5570
|
-
className: "dsm-trae-
|
|
5571
|
-
|
|
5572
|
-
|
|
5573
|
-
|
|
5574
|
-
|
|
5575
|
-
|
|
5576
|
-
|
|
5577
|
-
|
|
5578
|
-
|
|
5579
|
-
|
|
5580
|
-
|
|
5581
|
-
|
|
5582
|
-
|
|
5583
|
-
showPointsInMainUi: settingsEdits.showPointsInMainUi,
|
|
5584
|
-
onToggleShowPoints: (value) => {
|
|
5585
|
-
writePreference("showPointsInMainUi", value);
|
|
5586
|
-
},
|
|
5587
|
-
creditPosition: creditPositionOn(activeRegion),
|
|
5588
|
-
onCreditPositionChange: (position) => {
|
|
5589
|
-
changeCreditPosition(activeRegion, position);
|
|
5590
|
-
},
|
|
5591
|
-
savingPreferences: savingSettings,
|
|
5592
|
-
creditsLabelId,
|
|
5593
|
-
creditsHintId,
|
|
5594
|
-
rows: accountRows,
|
|
5595
|
-
busy,
|
|
5596
|
-
rescanNote,
|
|
5597
|
-
onRescan: () => {
|
|
5598
|
-
rescanAccounts();
|
|
5599
|
-
},
|
|
5600
|
-
switchingAccount,
|
|
5601
|
-
onSwitchAccount: (accountId) => {
|
|
5602
|
-
switchAccount(accountId);
|
|
5603
|
-
},
|
|
5604
|
-
onClaim: (accountId) => {
|
|
5605
|
-
claimFor(accountId);
|
|
5606
|
-
},
|
|
5607
|
-
onTransferChanged: async () => {
|
|
5608
|
-
const fresh = await refreshUsage(activeRegion);
|
|
5609
|
-
if (fresh !== void 0) setStatusByRegion((current) => ({
|
|
5610
|
-
...current,
|
|
5611
|
-
[activeRegion]: fresh
|
|
5612
|
-
}));
|
|
5613
|
-
},
|
|
5614
|
-
poolDetails,
|
|
5615
|
-
poolClaims: status.status === "signed-in" && status.pool?.checkinSupported === true ? Object.fromEntries(status.accounts.map((account) => [account.id, claimViewOf(account.id, accountStatusByRegion[activeRegion][account.id])])) : void 0,
|
|
5616
|
-
models: visibleModels,
|
|
5617
|
-
deviceClaimed: deviceClaimedToday,
|
|
5618
|
-
onPoolChanged: async () => {
|
|
5619
|
-
const fresh = await refreshUsage(activeRegion);
|
|
5620
|
-
if (fresh !== void 0) setStatusByRegion((current) => ({
|
|
5621
|
-
...current,
|
|
5622
|
-
[activeRegion]: fresh
|
|
5623
|
-
}));
|
|
5624
|
-
},
|
|
5625
|
-
onPoolRefresh: async () => {
|
|
5626
|
-
const fresh = await refreshUsage(activeRegion);
|
|
5627
|
-
if (fresh !== void 0) setStatusByRegion((current) => ({
|
|
5628
|
-
...current,
|
|
5629
|
-
[activeRegion]: fresh
|
|
5630
|
-
}));
|
|
5631
|
-
},
|
|
5632
|
-
creditsUnavailable: status.status === "signed-in" ? status.creditsUnavailable : void 0,
|
|
5633
|
-
creditsExpiry: status.status === "signed-in" ? status.credits?.expiry : void 0,
|
|
5634
|
-
alternatives: status.status === "signed-in" ? status.alternatives : void 0,
|
|
5635
|
-
onSwitchToAlternative: (accountId) => {
|
|
5636
|
-
switchAccount(accountId);
|
|
6428
|
+
}),
|
|
6429
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
6430
|
+
className: "dsm-trae-model-capability-note",
|
|
6431
|
+
children: t("row.contextBudgetHint")
|
|
6432
|
+
}),
|
|
6433
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
6434
|
+
className: "dsm-trae-draw-image",
|
|
6435
|
+
"data-disabled": drawImageServable ? void 0 : "true",
|
|
6436
|
+
title: drawImageServable ? t("row.drawImageHint") : drawImageUnavailableHint,
|
|
6437
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
6438
|
+
type: "checkbox",
|
|
6439
|
+
checked: drawImageOn,
|
|
6440
|
+
disabled: !drawImageServable || busy || saving || savingSettings || settingsScope?.getSnapshot().writable !== true,
|
|
6441
|
+
onChange: (event) => {
|
|
6442
|
+
writeDrawImage(event.target.checked);
|
|
5637
6443
|
}
|
|
5638
|
-
})
|
|
6444
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: [t("row.drawImage"), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
6445
|
+
className: "dsm-trae-draw-image-hint",
|
|
6446
|
+
children: [" ", drawImageServable ? t("row.drawImageHint") : drawImageUnavailableHint]
|
|
6447
|
+
})] })]
|
|
5639
6448
|
}),
|
|
5640
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("
|
|
5641
|
-
className: "dsm-trae-
|
|
5642
|
-
|
|
5643
|
-
|
|
5644
|
-
|
|
5645
|
-
|
|
5646
|
-
children:
|
|
5647
|
-
className: "dsm-trae-
|
|
5648
|
-
|
|
5649
|
-
|
|
5650
|
-
|
|
5651
|
-
|
|
5652
|
-
|
|
5653
|
-
|
|
5654
|
-
|
|
5655
|
-
|
|
5656
|
-
|
|
5657
|
-
|
|
5658
|
-
|
|
5659
|
-
|
|
5660
|
-
|
|
5661
|
-
|
|
5662
|
-
|
|
5663
|
-
|
|
5664
|
-
|
|
5665
|
-
|
|
5666
|
-
|
|
5667
|
-
|
|
5668
|
-
|
|
5669
|
-
|
|
5670
|
-
|
|
5671
|
-
|
|
5672
|
-
|
|
5673
|
-
|
|
5674
|
-
|
|
5675
|
-
|
|
5676
|
-
|
|
5677
|
-
|
|
5678
|
-
|
|
5679
|
-
|
|
5680
|
-
|
|
5681
|
-
|
|
5682
|
-
|
|
5683
|
-
|
|
5684
|
-
|
|
5685
|
-
|
|
5686
|
-
|
|
5687
|
-
|
|
5688
|
-
|
|
5689
|
-
|
|
5690
|
-
|
|
5691
|
-
|
|
5692
|
-
|
|
5693
|
-
|
|
5694
|
-
|
|
5695
|
-
|
|
5696
|
-
]
|
|
5697
|
-
})]
|
|
5698
|
-
}),
|
|
5699
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
5700
|
-
className: "dsm-trae-models-hint",
|
|
5701
|
-
children: t("row.modelsTestHint")
|
|
5702
|
-
}),
|
|
5703
|
-
testSummary === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
5704
|
-
className: "dsm-trae-models-summary",
|
|
5705
|
-
children: testSummary
|
|
5706
|
-
}),
|
|
5707
|
-
testError === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
5708
|
-
className: "dsm-trae-usage-error",
|
|
5709
|
-
role: "alert",
|
|
5710
|
-
children: testError
|
|
5711
|
-
}),
|
|
5712
|
-
orderedModels.length > commonModelCount ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
5713
|
-
className: "dsm-trae-models-view",
|
|
5714
|
-
role: "group",
|
|
5715
|
-
"aria-label": t("row.modelsViewLabel"),
|
|
5716
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
5717
|
-
type: "button",
|
|
5718
|
-
className: "dsm-trae-models-view-opt",
|
|
5719
|
-
"aria-pressed": modelView === "common",
|
|
5720
|
-
title: t("row.modelsViewCommonHint"),
|
|
5721
|
-
onClick: () => {
|
|
5722
|
-
setModelView("common");
|
|
5723
|
-
},
|
|
5724
|
-
children: t("row.modelsViewCommon", { count: commonModelCount })
|
|
5725
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
5726
|
-
type: "button",
|
|
5727
|
-
className: "dsm-trae-models-view-opt",
|
|
5728
|
-
"aria-pressed": modelView === "all",
|
|
5729
|
-
title: t("row.modelsViewAllHint"),
|
|
5730
|
-
onClick: () => {
|
|
5731
|
-
setModelView("all");
|
|
5732
|
-
},
|
|
5733
|
-
children: t("row.modelsViewAll", { count: orderedModels.length })
|
|
5734
|
-
})]
|
|
5735
|
-
}) : null,
|
|
5736
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
5737
|
-
className: "dsm-trae-models-search",
|
|
5738
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
5739
|
-
type: "search",
|
|
5740
|
-
className: "dsm-trae-models-search-input",
|
|
5741
|
-
value: modelQuery,
|
|
5742
|
-
placeholder: t("row.modelsSearchPlaceholder"),
|
|
5743
|
-
"aria-label": t("row.modelsSearch"),
|
|
5744
|
-
onChange: (event) => {
|
|
5745
|
-
setModelQuery(event.target.value);
|
|
5746
|
-
setClosedModelGroups(/* @__PURE__ */ new Set());
|
|
5747
|
-
}
|
|
5748
|
-
}), modelQueryActive ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
5749
|
-
className: "dsm-trae-models-search-count",
|
|
5750
|
-
children: t("row.modelsSearchCount", {
|
|
5751
|
-
shown: shownModels.length,
|
|
5752
|
-
total: scopedModels.length
|
|
5753
|
-
})
|
|
5754
|
-
}) : null]
|
|
5755
|
-
}),
|
|
5756
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
5757
|
-
className: "dsm-trae-model-list",
|
|
5758
|
-
children: [modelGroups.length === 0 ? matchesOutsideView > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("p", {
|
|
5759
|
-
className: "dsm-trae-models-empty",
|
|
5760
|
-
children: [t("row.modelsSearchOutsideView", {
|
|
5761
|
-
query: modelQuery.trim(),
|
|
5762
|
-
count: matchesOutsideView
|
|
5763
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
5764
|
-
type: "button",
|
|
5765
|
-
className: "dsm-trae-models-widen",
|
|
5766
|
-
onClick: () => {
|
|
5767
|
-
setModelView("all");
|
|
5768
|
-
},
|
|
5769
|
-
children: t("row.modelsViewShowAll")
|
|
5770
|
-
})]
|
|
5771
|
-
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
5772
|
-
className: "dsm-trae-models-empty",
|
|
5773
|
-
children: t("row.modelsSearchEmpty", { query: modelQuery.trim() })
|
|
5774
|
-
}) : null, modelGroups.map((group) => {
|
|
5775
|
-
const groupKey = `${activeRegion}|${group.channel ?? ""}`;
|
|
5776
|
-
const groupOpen = modelQueryActive || !closedModelGroups.has(groupKey);
|
|
5777
|
-
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("details", {
|
|
5778
|
-
className: "dsm-trae-model-group",
|
|
5779
|
-
open: groupOpen,
|
|
5780
|
-
onToggle: (event) => {
|
|
5781
|
-
const open = event.currentTarget.open;
|
|
5782
|
-
setClosedModelGroups((current) => {
|
|
5783
|
-
if (open === !current.has(groupKey)) return current;
|
|
5784
|
-
const next = new Set(current);
|
|
5785
|
-
if (open) next.delete(groupKey);
|
|
5786
|
-
else next.add(groupKey);
|
|
5787
|
-
return next;
|
|
5788
|
-
});
|
|
5789
|
-
},
|
|
5790
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("summary", {
|
|
5791
|
-
className: "dsm-trae-model-group-head",
|
|
5792
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
5793
|
-
className: "dsm-trae-model-group-name",
|
|
5794
|
-
children: modelChannelLabel(group.channel, t)
|
|
5795
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
5796
|
-
className: "dsm-trae-model-group-count",
|
|
5797
|
-
children: t("row.modelsGroupCount", { count: group.rows.length })
|
|
5798
|
-
})]
|
|
5799
|
-
}), group.rows.map((model) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
5800
|
-
className: `dsm-trae-model${activeEnabledIds.has(model.id) ? "" : " dsm-trae-model-disabled"}`,
|
|
5801
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
5802
|
-
className: "dsm-trae-model-head",
|
|
5803
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
5804
|
-
className: "dsm-trae-model-enabled",
|
|
5805
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
5806
|
-
type: "checkbox",
|
|
5807
|
-
checked: activeEnabledIds.has(model.id),
|
|
5808
|
-
disabled: settingsScope?.getSnapshot().writable !== true || saving,
|
|
5809
|
-
onChange: () => {
|
|
5810
|
-
toggleModel(model.id);
|
|
5811
|
-
}
|
|
5812
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
5813
|
-
className: "dsm-trae-model-copy",
|
|
5814
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
5815
|
-
className: "dsm-trae-model-name",
|
|
5816
|
-
children: [
|
|
5817
|
-
model.name,
|
|
5818
|
-
activeRegion === "enterprise" || model.creditMultiplier === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
5819
|
-
className: "dsm-trae-model-name-rate",
|
|
5820
|
-
children: ["· x", model.creditMultiplier.toFixed(2)]
|
|
5821
|
-
}),
|
|
5822
|
-
probeBadge(probeByRegion[activeRegion][model.id], t)
|
|
5823
|
-
]
|
|
5824
|
-
}), promoBadge(model, t, nowForPromo)]
|
|
5825
|
-
})]
|
|
5826
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
5827
|
-
className: "dsm-trae-model-options",
|
|
5828
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
5829
|
-
className: "dsm-trae-model-image",
|
|
5830
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
5831
|
-
type: "checkbox",
|
|
5832
|
-
checked: rowAcceptsImage(model),
|
|
5833
|
-
disabled: rowImageIsAutomatic(model) || settingsScope?.getSnapshot().writable !== true || saving,
|
|
5834
|
-
title: rowImageIsAutomatic(model) ? t("row.modelImageAuto") : void 0,
|
|
5835
|
-
onChange: () => {
|
|
5836
|
-
toggleImage(model.id);
|
|
5837
|
-
}
|
|
5838
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("row.modelImage") })]
|
|
5839
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("fieldset", {
|
|
5840
|
-
className: "dsm-trae-context-budget",
|
|
5841
|
-
"aria-label": t("row.contextBudget"),
|
|
5842
|
-
title: t("row.contextBudgetHint"),
|
|
5843
|
-
children: contextBudgetOptions(model).map((option) => {
|
|
5844
|
-
const selectable = model.maxContextWindow !== void 0;
|
|
5845
|
-
const checked = (activeContextBudgets[model.id] ?? model.contextWindow) === option;
|
|
5846
|
-
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
5847
|
-
type: "radio",
|
|
5848
|
-
name: `context-${model.id}`,
|
|
5849
|
-
checked,
|
|
5850
|
-
disabled: !selectable || settingsScope?.getSnapshot().writable !== true || saving,
|
|
5851
|
-
onChange: () => {
|
|
5852
|
-
setContextBudget(model.id, option);
|
|
5853
|
-
}
|
|
5854
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: formatCapacity(option, t("row.modelUnknown")) })] }, option);
|
|
5855
|
-
})
|
|
5856
|
-
})]
|
|
5857
|
-
})]
|
|
5858
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
5859
|
-
className: "dsm-trae-model-meta",
|
|
5860
|
-
children: [
|
|
5861
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("row.modelContext", { context: formatCapacity(model.maxContextWindow ?? model.contextWindow, t("row.modelUnknown")) }) }),
|
|
5862
|
-
model.maxTokens === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("row.modelOutput", { output: formatCapacity(model.maxTokens, t("row.modelUnknown")) }) }),
|
|
5863
|
-
model.reasoning === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("row.modelReasoning", { efforts: model.reasoning.supported.join(" / ") }) })
|
|
5864
|
-
]
|
|
5865
|
-
})]
|
|
5866
|
-
}, model.id))]
|
|
5867
|
-
}, groupKey);
|
|
5868
|
-
})]
|
|
5869
|
-
}),
|
|
5870
|
-
enabledOutsideView.length === 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("p", {
|
|
5871
|
-
className: "dsm-trae-models-note",
|
|
5872
|
-
children: [t("row.modelsEnabledOutsideView", { count: enabledOutsideView.length }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
5873
|
-
type: "button",
|
|
5874
|
-
className: "dsm-trae-models-widen",
|
|
5875
|
-
onClick: () => {
|
|
5876
|
-
setModelView("all");
|
|
5877
|
-
},
|
|
5878
|
-
children: t("row.modelsViewShowAll")
|
|
5879
|
-
})]
|
|
5880
|
-
}),
|
|
5881
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
5882
|
-
className: "dsm-trae-context-bulk",
|
|
5883
|
-
children: [
|
|
5884
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
5885
|
-
type: "button",
|
|
5886
|
-
className: "dsm-btn dsm-btn-outline",
|
|
5887
|
-
disabled: busy || saving || settingsScope?.getSnapshot().writable !== true || !visibleModels.some((model) => contextBudgetOptions(model).includes(5e5)),
|
|
5888
|
-
title: t("row.contextAllMidHint"),
|
|
5889
|
-
onClick: setAllContextMid,
|
|
5890
|
-
children: t("row.contextAllMid")
|
|
5891
|
-
}),
|
|
5892
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
5893
|
-
type: "button",
|
|
5894
|
-
className: "dsm-btn dsm-btn-outline",
|
|
5895
|
-
disabled: busy || saving || settingsScope?.getSnapshot().writable !== true || !visibleModels.some((model) => model.maxContextWindow !== void 0),
|
|
5896
|
-
title: t("row.contextAllMaxHint"),
|
|
5897
|
-
onClick: setAllContextMax,
|
|
5898
|
-
children: t("row.contextAllMax")
|
|
5899
|
-
}),
|
|
5900
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
5901
|
-
type: "button",
|
|
5902
|
-
className: "dsm-btn dsm-btn-outline",
|
|
5903
|
-
disabled: busy || saving || settingsScope?.getSnapshot().writable !== true || Object.keys(activeContextBudgets).length === 0,
|
|
5904
|
-
title: t("row.contextAllDefaultHint"),
|
|
5905
|
-
onClick: resetAllContextBudgets,
|
|
5906
|
-
children: t("row.contextAllDefault")
|
|
5907
|
-
})
|
|
5908
|
-
]
|
|
5909
|
-
}),
|
|
5910
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
5911
|
-
className: "dsm-trae-model-capability-note",
|
|
5912
|
-
children: t("row.contextBudgetHint")
|
|
5913
|
-
}),
|
|
5914
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
5915
|
-
className: "dsm-trae-draw-image",
|
|
5916
|
-
"data-disabled": drawImageServable ? void 0 : "true",
|
|
5917
|
-
title: drawImageServable ? t("row.drawImageHint") : drawImageUnavailableHint,
|
|
5918
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
5919
|
-
type: "checkbox",
|
|
5920
|
-
checked: drawImageOn,
|
|
5921
|
-
disabled: !drawImageServable || busy || saving || savingSettings || settingsScope?.getSnapshot().writable !== true,
|
|
5922
|
-
onChange: (event) => {
|
|
5923
|
-
writeDrawImage(event.target.checked);
|
|
5924
|
-
}
|
|
5925
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: [t("row.drawImage"), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
5926
|
-
className: "dsm-trae-draw-image-hint",
|
|
5927
|
-
children: [" ", drawImageServable ? t("row.drawImageHint") : drawImageUnavailableHint]
|
|
5928
|
-
})] })]
|
|
5929
|
-
}),
|
|
5930
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
5931
|
-
className: "dsm-trae-model-capability-note",
|
|
5932
|
-
children: t("row.modelCapabilityPending")
|
|
5933
|
-
}),
|
|
5934
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
5935
|
-
className: "dsm-trae-model-actions",
|
|
5936
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("a", {
|
|
5937
|
-
className: "dsm-trae-usage-cheer",
|
|
5938
|
-
href: TRAE_GITHUB_URL,
|
|
5939
|
-
target: "_blank",
|
|
5940
|
-
rel: "noopener noreferrer",
|
|
5941
|
-
children: [t("row.cheer"), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
5942
|
-
className: "dsm-trae-usage-cheer-star",
|
|
5943
|
-
"aria-hidden": "true",
|
|
5944
|
-
children: "★"
|
|
5945
|
-
})]
|
|
5946
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
5947
|
-
className: "dsm-trae-model-actions-buttons",
|
|
5948
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
5949
|
-
type: "button",
|
|
5950
|
-
className: "dsm-btn dsm-btn-outline",
|
|
5951
|
-
disabled: !dirty || saving,
|
|
5952
|
-
onClick: discardModels,
|
|
5953
|
-
children: t("row.discard")
|
|
5954
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
5955
|
-
type: "button",
|
|
5956
|
-
className: "dsm-btn dsm-btn-primary",
|
|
5957
|
-
disabled: !dirty || saving || activeEnabledIds.size === 0,
|
|
5958
|
-
onClick: () => {
|
|
5959
|
-
saveModels();
|
|
5960
|
-
},
|
|
5961
|
-
children: saving ? t("row.saving") : t("row.save")
|
|
5962
|
-
})]
|
|
5963
|
-
})]
|
|
5964
|
-
})
|
|
5965
|
-
]
|
|
5966
|
-
}) }) : null
|
|
6449
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
6450
|
+
className: "dsm-trae-model-capability-note",
|
|
6451
|
+
children: t("row.modelCapabilityPending")
|
|
6452
|
+
}),
|
|
6453
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
6454
|
+
className: "dsm-trae-model-actions",
|
|
6455
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("a", {
|
|
6456
|
+
className: "dsm-trae-usage-cheer",
|
|
6457
|
+
href: TRAE_GITHUB_URL,
|
|
6458
|
+
target: "_blank",
|
|
6459
|
+
rel: "noopener noreferrer",
|
|
6460
|
+
children: [t("row.cheer"), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
6461
|
+
className: "dsm-trae-usage-cheer-star",
|
|
6462
|
+
"aria-hidden": "true",
|
|
6463
|
+
children: "★"
|
|
6464
|
+
})]
|
|
6465
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
6466
|
+
className: "dsm-trae-model-actions-buttons",
|
|
6467
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
6468
|
+
type: "button",
|
|
6469
|
+
className: "dsm-btn dsm-btn-outline",
|
|
6470
|
+
disabled: !dirty || saving,
|
|
6471
|
+
onClick: discardModels,
|
|
6472
|
+
children: t("row.discard")
|
|
6473
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
6474
|
+
type: "button",
|
|
6475
|
+
className: "dsm-btn dsm-btn-primary",
|
|
6476
|
+
disabled: !dirty || saving || activeEnabledIds.size === 0,
|
|
6477
|
+
onClick: () => {
|
|
6478
|
+
saveModels();
|
|
6479
|
+
},
|
|
6480
|
+
children: saving ? t("row.saving") : t("row.save")
|
|
6481
|
+
})]
|
|
6482
|
+
})]
|
|
6483
|
+
})
|
|
6484
|
+
]
|
|
6485
|
+
}) }) : null
|
|
6486
|
+
}),
|
|
6487
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
6488
|
+
className: "dsm-trae-panel",
|
|
6489
|
+
role: "tabpanel",
|
|
6490
|
+
id: "dsm-trae-panel-diag",
|
|
6491
|
+
"aria-labelledby": "dsm-trae-tab-diag",
|
|
6492
|
+
hidden: !signedIn || panel !== "diag",
|
|
6493
|
+
children: status.status !== "signed-in" || panel !== "diag" ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
6494
|
+
className: "dsm-trae-diag",
|
|
6495
|
+
children: [
|
|
6496
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
6497
|
+
className: "dsm-trae-diag-row",
|
|
6498
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
6499
|
+
className: "dsm-trae-diag-label",
|
|
6500
|
+
children: t("row.diagRegion")
|
|
6501
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
6502
|
+
className: "dsm-trae-diag-value",
|
|
6503
|
+
children: activeRegion === "cn" ? t("row.tabCn") : activeRegion === "enterprise" ? t("row.tabEnterprise") : t("row.tabAi")
|
|
6504
|
+
})]
|
|
5967
6505
|
}),
|
|
5968
|
-
/* @__PURE__ */ (0, react_jsx_runtime.
|
|
5969
|
-
className: "dsm-trae-
|
|
5970
|
-
|
|
5971
|
-
|
|
5972
|
-
|
|
5973
|
-
|
|
5974
|
-
|
|
5975
|
-
className: "dsm-trae-diag",
|
|
6506
|
+
status.unsupportedRegion === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
6507
|
+
className: "dsm-trae-diag-row",
|
|
6508
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
6509
|
+
className: "dsm-trae-diag-label",
|
|
6510
|
+
children: t("row.diagUnsupportedRegion")
|
|
6511
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
6512
|
+
className: "dsm-trae-diag-value dsm-trae-diag-bad",
|
|
5976
6513
|
children: [
|
|
5977
|
-
|
|
5978
|
-
|
|
5979
|
-
|
|
5980
|
-
className: "dsm-trae-diag-label",
|
|
5981
|
-
children: t("row.diagRegion")
|
|
5982
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
5983
|
-
className: "dsm-trae-diag-value",
|
|
5984
|
-
children: activeRegion === "cn" ? t("row.tabCn") : activeRegion === "enterprise" ? t("row.tabEnterprise") : t("row.tabAi")
|
|
5985
|
-
})]
|
|
5986
|
-
}),
|
|
5987
|
-
status.unsupportedRegion === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
5988
|
-
className: "dsm-trae-diag-row",
|
|
5989
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
5990
|
-
className: "dsm-trae-diag-label",
|
|
5991
|
-
children: t("row.diagUnsupportedRegion")
|
|
5992
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
5993
|
-
className: "dsm-trae-diag-value dsm-trae-diag-bad",
|
|
5994
|
-
children: [
|
|
5995
|
-
status.unsupportedRegion.region.toUpperCase(),
|
|
5996
|
-
" · ",
|
|
5997
|
-
status.unsupportedRegion.accounts.join(", ")
|
|
5998
|
-
]
|
|
5999
|
-
})]
|
|
6000
|
-
}),
|
|
6001
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
6002
|
-
className: "dsm-trae-diag-row",
|
|
6003
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
6004
|
-
className: "dsm-trae-diag-label",
|
|
6005
|
-
children: t("row.diagProvider")
|
|
6006
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
6007
|
-
className: "dsm-trae-diag-value dsm-trae-diag-mono",
|
|
6008
|
-
children: providerOfPanel(activeRegion)
|
|
6009
|
-
})]
|
|
6010
|
-
}),
|
|
6011
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
6012
|
-
className: "dsm-trae-diag-row",
|
|
6013
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
6014
|
-
className: "dsm-trae-diag-label",
|
|
6015
|
-
children: t("row.diagAccount")
|
|
6016
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
6017
|
-
className: "dsm-trae-diag-value",
|
|
6018
|
-
children: status.accountName
|
|
6019
|
-
})]
|
|
6020
|
-
}),
|
|
6021
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
6022
|
-
className: "dsm-trae-diag-row",
|
|
6023
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
6024
|
-
className: "dsm-trae-diag-label",
|
|
6025
|
-
children: t("row.diagToken")
|
|
6026
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
6027
|
-
className: "dsm-trae-diag-value dsm-trae-diag-mono",
|
|
6028
|
-
children: formatDateTime(status.tokenExpiresAtMs)
|
|
6029
|
-
})]
|
|
6030
|
-
}),
|
|
6031
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
6032
|
-
className: "dsm-trae-diag-row",
|
|
6033
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
6034
|
-
className: "dsm-trae-diag-label",
|
|
6035
|
-
children: t("row.diagRefresh")
|
|
6036
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
6037
|
-
className: "dsm-trae-diag-value dsm-trae-diag-mono",
|
|
6038
|
-
children: status.refreshExpiresAtMs === void 0 ? t("row.diagRefreshNone") : formatDateTime(status.refreshExpiresAtMs)
|
|
6039
|
-
})]
|
|
6040
|
-
}),
|
|
6041
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
6042
|
-
className: "dsm-trae-diag-row",
|
|
6043
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
6044
|
-
className: "dsm-trae-diag-label",
|
|
6045
|
-
children: t("row.diagCatalog")
|
|
6046
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
6047
|
-
className: "dsm-trae-diag-value",
|
|
6048
|
-
children: formatNumber(status.models.length)
|
|
6049
|
-
})]
|
|
6050
|
-
}),
|
|
6051
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
6052
|
-
className: "dsm-trae-diag-row",
|
|
6053
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
6054
|
-
className: "dsm-trae-diag-label",
|
|
6055
|
-
children: t("row.diagEnabled")
|
|
6056
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
6057
|
-
className: "dsm-trae-diag-value",
|
|
6058
|
-
children: formatNumber(status.enabledModelIds.length)
|
|
6059
|
-
})]
|
|
6060
|
-
}),
|
|
6061
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
6062
|
-
className: "dsm-trae-diag-row",
|
|
6063
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
6064
|
-
className: "dsm-trae-diag-label",
|
|
6065
|
-
children: t("row.diagPool")
|
|
6066
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
6067
|
-
className: "dsm-trae-diag-value",
|
|
6068
|
-
children: status.pool === void 0 ? t("row.diagPoolOff") : status.pool.enabled ? t("row.diagPoolOn", { count: status.pool.effectiveMemberAccountIds?.length ?? status.pool.accounts.length }) : t("row.diagPoolOff")
|
|
6069
|
-
})]
|
|
6070
|
-
}),
|
|
6071
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
6072
|
-
className: "dsm-trae-diag-row",
|
|
6073
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
6074
|
-
className: "dsm-trae-diag-label",
|
|
6075
|
-
children: t("row.diagCheckin")
|
|
6076
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
6077
|
-
className: "dsm-trae-diag-value",
|
|
6078
|
-
children: status.checkin !== void 0 || status.pool?.checkinSupported === true ? t("row.diagCheckinOn") : t("row.diagCheckinOff")
|
|
6079
|
-
})]
|
|
6080
|
-
}),
|
|
6081
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
6082
|
-
className: "dsm-trae-diag-row",
|
|
6083
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
6084
|
-
className: "dsm-trae-diag-label",
|
|
6085
|
-
children: t("row.diagRaw")
|
|
6086
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
6087
|
-
className: "dsm-trae-diag-value dsm-trae-diag-mono",
|
|
6088
|
-
children: status.rawChat === void 0 ? t("row.diagRawOff") : status.rawChat.state
|
|
6089
|
-
})]
|
|
6090
|
-
}),
|
|
6091
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
6092
|
-
className: "dsm-trae-diag-row",
|
|
6093
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
6094
|
-
className: "dsm-trae-diag-label",
|
|
6095
|
-
children: t("row.diagFailures")
|
|
6096
|
-
}), (() => {
|
|
6097
|
-
const failures = [];
|
|
6098
|
-
if (status.creditsError !== void 0) failures.push(status.creditsError);
|
|
6099
|
-
if (status.payStatusError !== void 0) failures.push(status.payStatusError);
|
|
6100
|
-
if (status.checkinError !== void 0) failures.push(status.checkinError);
|
|
6101
|
-
if (writeError !== void 0) failures.push(writeError);
|
|
6102
|
-
return failures.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
6103
|
-
className: "dsm-trae-diag-value",
|
|
6104
|
-
children: t("row.diagNoFailures")
|
|
6105
|
-
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
6106
|
-
className: "dsm-trae-diag-value dsm-trae-diag-bad dsm-trae-diag-mono",
|
|
6107
|
-
children: failures.join(" · ")
|
|
6108
|
-
});
|
|
6109
|
-
})()]
|
|
6110
|
-
}),
|
|
6111
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
6112
|
-
className: "dsm-trae-diag-row",
|
|
6113
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
6114
|
-
className: "dsm-trae-diag-label",
|
|
6115
|
-
children: t("row.diagQuarantine")
|
|
6116
|
-
}), status.quarantined === void 0 || status.quarantined.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
6117
|
-
className: "dsm-trae-diag-value",
|
|
6118
|
-
children: t("row.diagQuarantineNone")
|
|
6119
|
-
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
6120
|
-
className: "dsm-trae-diag-value dsm-trae-diag-bad dsm-trae-diag-mono",
|
|
6121
|
-
children: status.quarantined.map((item) => item.quarantinedAs).join(" · ")
|
|
6122
|
-
})]
|
|
6123
|
-
})
|
|
6514
|
+
status.unsupportedRegion.region.toUpperCase(),
|
|
6515
|
+
" · ",
|
|
6516
|
+
status.unsupportedRegion.accounts.join(", ")
|
|
6124
6517
|
]
|
|
6125
|
-
})
|
|
6126
|
-
|
|
6127
|
-
|
|
6128
|
-
|
|
6518
|
+
})]
|
|
6519
|
+
}),
|
|
6520
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
6521
|
+
className: "dsm-trae-diag-row",
|
|
6522
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
6523
|
+
className: "dsm-trae-diag-label",
|
|
6524
|
+
children: t("row.diagProvider")
|
|
6525
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
6526
|
+
className: "dsm-trae-diag-value dsm-trae-diag-mono",
|
|
6527
|
+
children: providerOfPanel(activeRegion)
|
|
6528
|
+
})]
|
|
6529
|
+
}),
|
|
6530
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
6531
|
+
className: "dsm-trae-diag-row",
|
|
6532
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
6533
|
+
className: "dsm-trae-diag-label",
|
|
6534
|
+
children: t("row.diagAccount")
|
|
6535
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
6536
|
+
className: "dsm-trae-diag-value",
|
|
6537
|
+
children: status.accountName
|
|
6538
|
+
})]
|
|
6539
|
+
}),
|
|
6540
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
6541
|
+
className: "dsm-trae-diag-row",
|
|
6542
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
6543
|
+
className: "dsm-trae-diag-label",
|
|
6544
|
+
children: t("row.diagToken")
|
|
6545
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
6546
|
+
className: "dsm-trae-diag-value dsm-trae-diag-mono",
|
|
6547
|
+
children: formatDateTime(status.tokenExpiresAtMs)
|
|
6548
|
+
})]
|
|
6549
|
+
}),
|
|
6550
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
6551
|
+
className: "dsm-trae-diag-row",
|
|
6552
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
6553
|
+
className: "dsm-trae-diag-label",
|
|
6554
|
+
children: t("row.diagRefresh")
|
|
6555
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
6556
|
+
className: "dsm-trae-diag-value dsm-trae-diag-mono",
|
|
6557
|
+
children: status.refreshExpiresAtMs === void 0 ? t("row.diagRefreshNone") : formatDateTime(status.refreshExpiresAtMs)
|
|
6558
|
+
})]
|
|
6559
|
+
}),
|
|
6560
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
6561
|
+
className: "dsm-trae-diag-row",
|
|
6562
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
6563
|
+
className: "dsm-trae-diag-label",
|
|
6564
|
+
children: t("row.diagCatalog")
|
|
6565
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
6566
|
+
className: "dsm-trae-diag-value",
|
|
6567
|
+
children: formatNumber(status.models.length)
|
|
6568
|
+
})]
|
|
6569
|
+
}),
|
|
6570
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
6571
|
+
className: "dsm-trae-diag-row",
|
|
6572
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
6573
|
+
className: "dsm-trae-diag-label",
|
|
6574
|
+
children: t("row.diagEnabled")
|
|
6575
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
6576
|
+
className: "dsm-trae-diag-value",
|
|
6577
|
+
children: formatNumber(status.enabledModelIds.length)
|
|
6578
|
+
})]
|
|
6579
|
+
}),
|
|
6580
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
6581
|
+
className: "dsm-trae-diag-row",
|
|
6582
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
6583
|
+
className: "dsm-trae-diag-label",
|
|
6584
|
+
children: t("row.diagPool")
|
|
6585
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
6586
|
+
className: "dsm-trae-diag-value",
|
|
6587
|
+
children: status.pool === void 0 ? t("row.diagPoolOff") : status.pool.enabled ? t("row.diagPoolOn", { count: status.pool.effectiveMemberAccountIds?.length ?? status.pool.accounts.length }) : t("row.diagPoolOff")
|
|
6588
|
+
})]
|
|
6589
|
+
}),
|
|
6590
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
6591
|
+
className: "dsm-trae-diag-row",
|
|
6592
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
6593
|
+
className: "dsm-trae-diag-label",
|
|
6594
|
+
children: t("row.diagCheckin")
|
|
6595
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
6596
|
+
className: "dsm-trae-diag-value",
|
|
6597
|
+
children: status.checkin !== void 0 || status.pool?.checkinSupported === true ? t("row.diagCheckinOn") : t("row.diagCheckinOff")
|
|
6598
|
+
})]
|
|
6599
|
+
}),
|
|
6600
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
6601
|
+
className: "dsm-trae-diag-row",
|
|
6602
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
6603
|
+
className: "dsm-trae-diag-label",
|
|
6604
|
+
children: t("row.diagRaw")
|
|
6605
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
6606
|
+
className: "dsm-trae-diag-value dsm-trae-diag-mono",
|
|
6607
|
+
children: status.rawChat === void 0 ? t("row.diagRawOff") : status.rawChat.state
|
|
6608
|
+
})]
|
|
6609
|
+
}),
|
|
6610
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
6611
|
+
className: "dsm-trae-diag-row",
|
|
6612
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
6613
|
+
className: "dsm-trae-diag-label",
|
|
6614
|
+
children: t("row.diagFailures")
|
|
6615
|
+
}), (() => {
|
|
6616
|
+
const failures = [];
|
|
6617
|
+
if (status.creditsError !== void 0) failures.push(status.creditsError);
|
|
6618
|
+
if (status.payStatusError !== void 0) failures.push(status.payStatusError);
|
|
6619
|
+
if (status.checkinError !== void 0) failures.push(status.checkinError);
|
|
6620
|
+
if (writeError !== void 0) failures.push(writeError);
|
|
6621
|
+
return failures.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
6622
|
+
className: "dsm-trae-diag-value",
|
|
6623
|
+
children: t("row.diagNoFailures")
|
|
6624
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
6625
|
+
className: "dsm-trae-diag-value dsm-trae-diag-bad dsm-trae-diag-mono",
|
|
6626
|
+
children: failures.join(" · ")
|
|
6627
|
+
});
|
|
6628
|
+
})()]
|
|
6629
|
+
}),
|
|
6630
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
6631
|
+
className: "dsm-trae-diag-row",
|
|
6632
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
6633
|
+
className: "dsm-trae-diag-label",
|
|
6634
|
+
children: t("row.diagQuarantine")
|
|
6635
|
+
}), status.quarantined === void 0 || status.quarantined.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
6636
|
+
className: "dsm-trae-diag-value",
|
|
6637
|
+
children: t("row.diagQuarantineNone")
|
|
6638
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
6639
|
+
className: "dsm-trae-diag-value dsm-trae-diag-bad dsm-trae-diag-mono",
|
|
6640
|
+
children: status.quarantined.map((item) => item.quarantinedAs).join(" · ")
|
|
6641
|
+
})]
|
|
6642
|
+
})
|
|
6643
|
+
]
|
|
6644
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
6645
|
+
className: "dsm-trae-diag-hint",
|
|
6646
|
+
children: t("row.diagHint")
|
|
6647
|
+
})] })
|
|
6648
|
+
}),
|
|
6649
|
+
status.status === "signed-out" && loading ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_jsx_runtime.Fragment, { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
6650
|
+
className: "dsm-trae-usage-text",
|
|
6651
|
+
role: "status",
|
|
6652
|
+
children: t("row.accountsLoading")
|
|
6653
|
+
}) }) : null,
|
|
6654
|
+
status.status === "signed-out" && !loading ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
|
|
6655
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
6656
|
+
className: "dsm-trae-usage-text",
|
|
6657
|
+
children: status.message ?? t("row.signedOutHint")
|
|
6658
|
+
}),
|
|
6659
|
+
status.unsupportedRegion === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("details", {
|
|
6660
|
+
className: "dsm-trae-quarantine",
|
|
6661
|
+
open: true,
|
|
6662
|
+
children: [
|
|
6663
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("summary", { children: t("row.unsupportedRegionTitle") }),
|
|
6664
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
6665
|
+
className: "dsm-trae-quarantine-hint",
|
|
6666
|
+
children: t("row.unsupportedRegionHint")
|
|
6129
6667
|
}),
|
|
6130
|
-
|
|
6131
|
-
className: "dsm-trae-
|
|
6132
|
-
|
|
6133
|
-
|
|
6134
|
-
|
|
6135
|
-
|
|
6136
|
-
|
|
6137
|
-
|
|
6138
|
-
|
|
6139
|
-
|
|
6140
|
-
|
|
6141
|
-
|
|
6142
|
-
|
|
6143
|
-
|
|
6144
|
-
|
|
6145
|
-
|
|
6146
|
-
|
|
6147
|
-
|
|
6148
|
-
|
|
6149
|
-
|
|
6150
|
-
|
|
6151
|
-
|
|
6152
|
-
|
|
6153
|
-
|
|
6154
|
-
}),
|
|
6155
|
-
status.quarantined && status.quarantined.length > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("details", {
|
|
6156
|
-
className: "dsm-trae-quarantine",
|
|
6157
|
-
open: true,
|
|
6668
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("ul", {
|
|
6669
|
+
className: "dsm-trae-quarantine-list",
|
|
6670
|
+
children: status.unsupportedRegion.accounts.map((name) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("li", { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("code", { children: name }) }, name))
|
|
6671
|
+
})
|
|
6672
|
+
]
|
|
6673
|
+
}),
|
|
6674
|
+
status.quarantined && status.quarantined.length > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("details", {
|
|
6675
|
+
className: "dsm-trae-quarantine",
|
|
6676
|
+
open: true,
|
|
6677
|
+
children: [
|
|
6678
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("summary", { children: [
|
|
6679
|
+
t("row.quarantineTitle"),
|
|
6680
|
+
" (",
|
|
6681
|
+
status.quarantined.length,
|
|
6682
|
+
")"
|
|
6683
|
+
] }),
|
|
6684
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
6685
|
+
className: "dsm-trae-quarantine-hint",
|
|
6686
|
+
children: t("row.quarantineHint")
|
|
6687
|
+
}),
|
|
6688
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("ul", {
|
|
6689
|
+
className: "dsm-trae-quarantine-list",
|
|
6690
|
+
children: status.quarantined.map((item) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("li", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("code", { children: item.quarantinedAs }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
6691
|
+
className: "dsm-trae-quarantine-reason",
|
|
6158
6692
|
children: [
|
|
6159
|
-
|
|
6160
|
-
|
|
6161
|
-
|
|
6162
|
-
status.quarantined.length,
|
|
6163
|
-
")"
|
|
6164
|
-
] }),
|
|
6165
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
6166
|
-
className: "dsm-trae-quarantine-hint",
|
|
6167
|
-
children: t("row.quarantineHint")
|
|
6168
|
-
}),
|
|
6169
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("ul", {
|
|
6170
|
-
className: "dsm-trae-quarantine-list",
|
|
6171
|
-
children: status.quarantined.map((item) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("li", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("code", { children: item.quarantinedAs }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
6172
|
-
className: "dsm-trae-quarantine-reason",
|
|
6173
|
-
children: [
|
|
6174
|
-
item.accounts === void 0 ? t("row.quarantineAccountsUnknown") : t("row.quarantineAccounts", { count: item.accounts }),
|
|
6175
|
-
" · ",
|
|
6176
|
-
t("row.quarantineBytes", { bytes: item.bytes })
|
|
6177
|
-
]
|
|
6178
|
-
})] }, item.quarantinedAs))
|
|
6179
|
-
})
|
|
6693
|
+
item.accounts === void 0 ? t("row.quarantineAccountsUnknown") : t("row.quarantineAccounts", { count: item.accounts }),
|
|
6694
|
+
" · ",
|
|
6695
|
+
t("row.quarantineBytes", { bytes: item.bytes })
|
|
6180
6696
|
]
|
|
6181
|
-
})
|
|
6182
|
-
|
|
6183
|
-
|
|
6697
|
+
})] }, item.quarantinedAs))
|
|
6698
|
+
})
|
|
6699
|
+
]
|
|
6700
|
+
}) : null,
|
|
6701
|
+
status.searched && status.searched.length > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("details", {
|
|
6702
|
+
className: "dsm-trae-searched",
|
|
6703
|
+
children: [
|
|
6704
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("summary", { children: [
|
|
6705
|
+
t("row.searchedTitle"),
|
|
6706
|
+
" (",
|
|
6707
|
+
status.searched.length,
|
|
6708
|
+
")"
|
|
6709
|
+
] }),
|
|
6710
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
6711
|
+
className: "dsm-trae-searched-hint",
|
|
6712
|
+
children: t("row.searchedHint")
|
|
6713
|
+
}),
|
|
6714
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("ul", {
|
|
6715
|
+
className: "dsm-trae-searched-list",
|
|
6716
|
+
children: status.searched.map((item) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("li", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("code", { children: item.path }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
6717
|
+
className: "dsm-trae-searched-reason",
|
|
6184
6718
|
children: [
|
|
6185
|
-
|
|
6186
|
-
|
|
6187
|
-
|
|
6188
|
-
status.searched.length,
|
|
6189
|
-
")"
|
|
6190
|
-
] }),
|
|
6191
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
6192
|
-
className: "dsm-trae-searched-hint",
|
|
6193
|
-
children: t("row.searchedHint")
|
|
6194
|
-
}),
|
|
6195
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("ul", {
|
|
6196
|
-
className: "dsm-trae-searched-list",
|
|
6197
|
-
children: status.searched.map((item) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("li", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("code", { children: item.path }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
6198
|
-
className: "dsm-trae-searched-reason",
|
|
6199
|
-
children: [
|
|
6200
|
-
t(item.source === "cli" ? "row.sourceCli" : "row.sourceDesktop"),
|
|
6201
|
-
" · ",
|
|
6202
|
-
t(item.reason === "missing" ? "row.reasonMissing" : item.reason === "unreadable" ? "row.reasonUnreadable" : "row.reasonInvalid")
|
|
6203
|
-
]
|
|
6204
|
-
})] }, `${item.source}:${item.path}`))
|
|
6205
|
-
})
|
|
6719
|
+
t(item.source === "cli" ? "row.sourceCli" : "row.sourceDesktop"),
|
|
6720
|
+
" · ",
|
|
6721
|
+
t(item.reason === "missing" ? "row.reasonMissing" : item.reason === "unreadable" ? "row.reasonUnreadable" : "row.reasonInvalid")
|
|
6206
6722
|
]
|
|
6207
|
-
})
|
|
6208
|
-
|
|
6209
|
-
status.status === "error" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
6210
|
-
className: "dsm-trae-usage-error",
|
|
6211
|
-
children: status.message
|
|
6212
|
-
}) : null
|
|
6723
|
+
})] }, `${item.source}:${item.path}`))
|
|
6724
|
+
})
|
|
6213
6725
|
]
|
|
6214
6726
|
}) : null
|
|
6727
|
+
] }) : null,
|
|
6728
|
+
status.status === "error" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
6729
|
+
className: "dsm-trae-usage-error",
|
|
6730
|
+
children: status.message
|
|
6731
|
+
}) : null
|
|
6732
|
+
]
|
|
6733
|
+
});
|
|
6734
|
+
/**
|
|
6735
|
+
* The page header, drawn only by the sidebar row's `main` panel.
|
|
6736
|
+
*
|
|
6737
|
+
* A settings card has the Plugins page's own chrome around it; a full page has
|
|
6738
|
+
* nothing, so it has to say what it is and how to leave. The back control
|
|
6739
|
+
* reuses the host's `layout.selectPanel(null)` (handed down as
|
|
6740
|
+
* `backToConversation`), which is the only public contract for returning the
|
|
6741
|
+
* centre column to the conversation — and it is omitted, not drawn dead, on a
|
|
6742
|
+
* host that does not declare `layout`.
|
|
6743
|
+
*/
|
|
6744
|
+
const pageHead = pageSurface ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
6745
|
+
className: "dsm-trae-page-head",
|
|
6746
|
+
children: [
|
|
6747
|
+
backToConversation === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
6748
|
+
type: "button",
|
|
6749
|
+
className: "dsm-trae-page-back",
|
|
6750
|
+
onClick: backToConversation,
|
|
6751
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
6752
|
+
"aria-hidden": "true",
|
|
6753
|
+
className: "dsm-trae-page-back-caret"
|
|
6754
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("row.pageBack") })]
|
|
6755
|
+
}),
|
|
6756
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("img", {
|
|
6757
|
+
className: "dsm-trae-page-icon",
|
|
6758
|
+
src: TRAE_PLUGIN_ICON,
|
|
6759
|
+
alt: ""
|
|
6760
|
+
}),
|
|
6761
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
6762
|
+
className: "dsm-trae-page-title",
|
|
6763
|
+
children: title
|
|
6764
|
+
}),
|
|
6765
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
6766
|
+
className: "dsm-trae-page-desc",
|
|
6767
|
+
children: t("row.desc")
|
|
6768
|
+
})
|
|
6769
|
+
]
|
|
6770
|
+
}) : null;
|
|
6771
|
+
if (pageSurface) return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
6772
|
+
className: "dsm-trae-page",
|
|
6773
|
+
children: [
|
|
6774
|
+
pageHead,
|
|
6775
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
6776
|
+
className: "dsm-trae-page-body",
|
|
6777
|
+
children: cardBody
|
|
6778
|
+
}),
|
|
6779
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(BusyOverlay, {
|
|
6780
|
+
visible: saving || savingSettings,
|
|
6781
|
+
message: t("row.veilSaving")
|
|
6782
|
+
})
|
|
6783
|
+
]
|
|
6784
|
+
});
|
|
6785
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("li", {
|
|
6786
|
+
className: `dsm-plugin-card${open ? " dsm-plugin-card-open" : ""}`,
|
|
6787
|
+
children: [
|
|
6788
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
6789
|
+
type: "button",
|
|
6790
|
+
className: "dsm-plugin-card-header",
|
|
6791
|
+
"aria-expanded": open,
|
|
6792
|
+
"aria-label": `${t(open ? "row.collapse" : "row.expand")}: ${title}`,
|
|
6793
|
+
onClick: () => {
|
|
6794
|
+
setCardOpen(!open);
|
|
6795
|
+
},
|
|
6796
|
+
children: [
|
|
6797
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("img", {
|
|
6798
|
+
className: "dsm-plugin-card-icon",
|
|
6799
|
+
src: TRAE_PLUGIN_ICON,
|
|
6800
|
+
alt: ""
|
|
6801
|
+
}),
|
|
6802
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
6803
|
+
className: "dsm-plugin-card-head",
|
|
6804
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
6805
|
+
className: "dsm-plugin-card-title",
|
|
6806
|
+
children: title
|
|
6807
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
6808
|
+
className: "dsm-plugin-card-description",
|
|
6809
|
+
children: t("row.desc")
|
|
6810
|
+
})]
|
|
6811
|
+
}),
|
|
6812
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
6813
|
+
"aria-hidden": "true",
|
|
6814
|
+
className: `dsm-plugin-card-chevron${open ? " dsm-plugin-card-chevron-open" : ""}`
|
|
6815
|
+
})
|
|
6816
|
+
]
|
|
6817
|
+
}),
|
|
6818
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
6819
|
+
className: "dsm-plugin-card-body",
|
|
6820
|
+
hidden: !open,
|
|
6821
|
+
children: open ? cardBody : null
|
|
6215
6822
|
}),
|
|
6216
6823
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(BusyOverlay, {
|
|
6217
6824
|
visible: saving || savingSettings,
|
|
@@ -6350,9 +6957,16 @@ window.__ModuleLoader__.load({
|
|
|
6350
6957
|
* at a time and nothing overlaps.
|
|
6351
6958
|
*
|
|
6352
6959
|
* The row itself is a bare clickable readout — `Trae CN · 657` — with NO
|
|
6353
|
-
*
|
|
6960
|
+
* controls of its own. Clicking it opens an anchored panel: a TABLE with one row
|
|
6354
6961
|
* per account in the region and that account's general balance, where a row
|
|
6355
|
-
* click switches to it.
|
|
6962
|
+
* click switches to it.
|
|
6963
|
+
*
|
|
6964
|
+
* The panel's header carries a SETTINGS button, not a refresh button: the
|
|
6965
|
+
* question a reader actually has at this panel is "where do I change this?", and
|
|
6966
|
+
* the answer used to require closing the popover, finding the Plugins row and
|
|
6967
|
+
* clicking the bundle. Refreshing is not lost — the readout polls every five
|
|
6968
|
+
* minutes and every account switch re-reads — but it no longer needs a button of
|
|
6969
|
+
* its own. One press now lands on this plugin's own page (`./settings-route.ts`).
|
|
6356
6970
|
*
|
|
6357
6971
|
* Two deliberate choices in that panel:
|
|
6358
6972
|
*
|
|
@@ -6479,7 +7093,7 @@ window.__ModuleLoader__.load({
|
|
|
6479
7093
|
});
|
|
6480
7094
|
}
|
|
6481
7095
|
function ComposerPoints(props) {
|
|
6482
|
-
const { t, provider, region, settingsScope } = props;
|
|
7096
|
+
const { t, provider, region, settingsScope, onOpenSettings } = props;
|
|
6483
7097
|
if (t === void 0) throw new Error("Composer points readout requires its translation function");
|
|
6484
7098
|
const owned = provider === void 0 ? void 0 : TRAE_COMPOSER_PROVIDERS[provider];
|
|
6485
7099
|
const activeRegion = region ?? owned;
|
|
@@ -6495,9 +7109,54 @@ window.__ModuleLoader__.load({
|
|
|
6495
7109
|
* open, for the same reason the credit route does.
|
|
6496
7110
|
*/
|
|
6497
7111
|
const [accountPlans, setAccountPlans] = (0, react.useState)(void 0);
|
|
6498
|
-
const [busy, setBusy] = (0, react.useState)(false);
|
|
6499
7112
|
const [switchingId, setSwitchingId] = (0, react.useState)(void 0);
|
|
6500
7113
|
const [failed, setFailed] = (0, react.useState)(false);
|
|
7114
|
+
/**
|
|
7115
|
+
* The settings button was pressed on a host with no route to the page.
|
|
7116
|
+
*
|
|
7117
|
+
* Its own state rather than reusing `failed`: the two say different things
|
|
7118
|
+
* ("the upstream read failed" vs "this host cannot open the settings page"),
|
|
7119
|
+
* and a shared flag would let one press erase the other's explanation.
|
|
7120
|
+
*/
|
|
7121
|
+
const [settingsFailed, setSettingsFailed] = (0, react.useState)(false);
|
|
7122
|
+
/**
|
|
7123
|
+
* The account-pool test batch, driven from this panel's header.
|
|
7124
|
+
*
|
|
7125
|
+
* WHY IT LIVES HERE AT ALL: the rows below ARE the pool's members, and a row's
|
|
7126
|
+
* mark is a MEASUREMENT (`被限流 · 约 6 小时后` is a probe result, not a
|
|
7127
|
+
* setting). Re-reading the usage document cannot improve a stale measurement —
|
|
7128
|
+
* only running it again can — so the question "which of these can serve right
|
|
7129
|
+
* now?" had no answer from the place that asks it. The pool card can do this,
|
|
7130
|
+
* but it is behind the settings page; the reader is already looking at these
|
|
7131
|
+
* accounts.
|
|
7132
|
+
*
|
|
7133
|
+
* `testing` is separate from the panel's other busy states on purpose: this is
|
|
7134
|
+
* a SERIAL batch against one account at a time with a gap (hitting the whole
|
|
7135
|
+
* pool at once is how the upstream's volume limit gets tripped), so it can run
|
|
7136
|
+
* for a minute while a credits read would be merely slow.
|
|
7137
|
+
*
|
|
7138
|
+
* `testFailed` is its own line for the same reason `settingsFailed` is: three
|
|
7139
|
+
* different failures rendered into one slot would take turns overwriting each
|
|
7140
|
+
* other. `testNote` carries the last outcome summary, which is what makes the
|
|
7141
|
+
* press feel answered even when the table's marks are subtle.
|
|
7142
|
+
*/
|
|
7143
|
+
const [testing, setTesting] = (0, react.useState)(false);
|
|
7144
|
+
const [testFailed, setTestFailed] = (0, react.useState)(void 0);
|
|
7145
|
+
const [testNote, setTestNote] = (0, react.useState)(void 0);
|
|
7146
|
+
const testingRef = (0, react.useRef)(false);
|
|
7147
|
+
/**
|
|
7148
|
+
* The usage document, mirrored into a ref.
|
|
7149
|
+
*
|
|
7150
|
+
* `fetchAccountPlans` has to read the roster out of it, and reading it as a
|
|
7151
|
+
* `useCallback` DEPENDENCY made that callback a new function on every poll
|
|
7152
|
+
* (every `fetchUsage` stores a fresh object). The open/region effect calls
|
|
7153
|
+
* the callback by identity, so a new identity re-ran it — which meant every
|
|
7154
|
+
* five-minute poll silently re-read the per-account status of every account,
|
|
7155
|
+
* the exact "read on OPEN, not on a cycle" cost the effect's own comment says
|
|
7156
|
+
* it is avoiding. The ref keeps the callback stable while still reading the
|
|
7157
|
+
* newest document.
|
|
7158
|
+
*/
|
|
7159
|
+
const usageRef = (0, react.useRef)(void 0);
|
|
6501
7160
|
const [lastRefresh, setLastRefresh] = (0, react.useState)(void 0);
|
|
6502
7161
|
const [open, setOpen] = (0, react.useState)(false);
|
|
6503
7162
|
const inFlight = (0, react.useRef)(false);
|
|
@@ -6522,7 +7181,6 @@ window.__ModuleLoader__.load({
|
|
|
6522
7181
|
const fetchUsage = (0, react.useCallback)(async (signal) => {
|
|
6523
7182
|
if (inFlight.current || activeRegion === void 0) return;
|
|
6524
7183
|
inFlight.current = true;
|
|
6525
|
-
if (mounted.current) setBusy(true);
|
|
6526
7184
|
try {
|
|
6527
7185
|
const response = await fetch(withTraeRegion(TRAE_USAGE_PATH, activeRegion), {
|
|
6528
7186
|
headers: { accept: "application/json" },
|
|
@@ -6532,6 +7190,7 @@ window.__ModuleLoader__.load({
|
|
|
6532
7190
|
if (!response.ok) throw new Error(`HTTP ${response.status}`);
|
|
6533
7191
|
const value = await response.json();
|
|
6534
7192
|
if (!mounted.current || signal?.aborted === true) return;
|
|
7193
|
+
usageRef.current = value;
|
|
6535
7194
|
setUsage(value);
|
|
6536
7195
|
setLastRefresh(Date.now());
|
|
6537
7196
|
setFailed(false);
|
|
@@ -6539,7 +7198,6 @@ window.__ModuleLoader__.load({
|
|
|
6539
7198
|
if (mounted.current && signal?.aborted !== true) setFailed(true);
|
|
6540
7199
|
} finally {
|
|
6541
7200
|
inFlight.current = false;
|
|
6542
|
-
if (mounted.current) setBusy(false);
|
|
6543
7201
|
}
|
|
6544
7202
|
}, [activeRegion]);
|
|
6545
7203
|
/**
|
|
@@ -6590,7 +7248,8 @@ window.__ModuleLoader__.load({
|
|
|
6590
7248
|
*/
|
|
6591
7249
|
const fetchAccountPlans = (0, react.useCallback)(async (signal) => {
|
|
6592
7250
|
if (activeRegion !== "ai") return;
|
|
6593
|
-
const
|
|
7251
|
+
const document = usageRef.current;
|
|
7252
|
+
const roster = document?.status === "signed-in" ? document.accounts.filter((account) => account.region === "ai") : [];
|
|
6594
7253
|
if (roster.length === 0) {
|
|
6595
7254
|
setAccountPlans({});
|
|
6596
7255
|
return;
|
|
@@ -6611,7 +7270,7 @@ window.__ModuleLoader__.load({
|
|
|
6611
7270
|
}));
|
|
6612
7271
|
if (!mounted.current || signal?.aborted === true) return;
|
|
6613
7272
|
setAccountPlans(Object.fromEntries(entries));
|
|
6614
|
-
}, [activeRegion
|
|
7273
|
+
}, [activeRegion]);
|
|
6615
7274
|
(0, react.useEffect)(() => {
|
|
6616
7275
|
if (!open || activeRegion === void 0) return void 0;
|
|
6617
7276
|
const controller = new AbortController();
|
|
@@ -6669,6 +7328,100 @@ window.__ModuleLoader__.load({
|
|
|
6669
7328
|
if (mounted.current) setSwitchingId(void 0);
|
|
6670
7329
|
}
|
|
6671
7330
|
};
|
|
7331
|
+
/**
|
|
7332
|
+
* The header's settings control: open this plugin's page, then close.
|
|
7333
|
+
*
|
|
7334
|
+
* CLOSING IS PART OF THE JOB. The panel is portaled to `document.body` and
|
|
7335
|
+
* floats over the composer; navigating the centre column does not remove it,
|
|
7336
|
+
* so leaving it open would park a credits table on top of the page the user
|
|
7337
|
+
* just asked for. It is not reopened on return — a popover that comes back by
|
|
7338
|
+
* itself after navigation is a popover the user cannot get rid of.
|
|
7339
|
+
*
|
|
7340
|
+
* `false` (no route on this host) sets the panel's own error line rather than
|
|
7341
|
+
* doing nothing: a press that changes nothing on screen is indistinguishable
|
|
7342
|
+
* from a broken button, and the honest answer here is "this host has no way
|
|
7343
|
+
* there".
|
|
7344
|
+
*/
|
|
7345
|
+
const openSettings = () => {
|
|
7346
|
+
if (!(onOpenSettings === void 0 ? false : onOpenSettings())) {
|
|
7347
|
+
setSettingsFailed(true);
|
|
7348
|
+
return;
|
|
7349
|
+
}
|
|
7350
|
+
setSettingsFailed(false);
|
|
7351
|
+
setOpen(false);
|
|
7352
|
+
};
|
|
7353
|
+
/**
|
|
7354
|
+
* Run the pool's test batch, then re-read what it changed.
|
|
7355
|
+
*
|
|
7356
|
+
* The route is the SAME one the card's "测试勾选的账号" drives — it tests the
|
|
7357
|
+
* pool's checked-in members, not every account listed in this panel — and the
|
|
7358
|
+
* model comes from the Host's saved target rather than from here, so a press
|
|
7359
|
+
* cannot spend credits on a model the user did not choose. Same for the
|
|
7360
|
+
* outcome vocabulary and the NDJSON fold: both live in `./pool-test.ts` so the
|
|
7361
|
+
* card and this header cannot answer one batch two ways.
|
|
7362
|
+
*
|
|
7363
|
+
* The re-read afterwards is not a courtesy. The probes live in the USAGE
|
|
7364
|
+
* document (`usage.pool.accounts[].probe`), so the marks only move once that
|
|
7365
|
+
* document is fetched again; without it the press would leave the table
|
|
7366
|
+
* showing the state it just measured away.
|
|
7367
|
+
*
|
|
7368
|
+
* A failure keeps the previous measurements: nothing was learned about them,
|
|
7369
|
+
* and blanking the marks would look like the addresses had become unusable.
|
|
7370
|
+
*/
|
|
7371
|
+
const testAccounts = async () => {
|
|
7372
|
+
if (activeRegion === void 0 || testingRef.current) return;
|
|
7373
|
+
testingRef.current = true;
|
|
7374
|
+
setTesting(true);
|
|
7375
|
+
setTestFailed(void 0);
|
|
7376
|
+
setTestNote(void 0);
|
|
7377
|
+
const controller = new AbortController();
|
|
7378
|
+
const timer = setTimeout(() => {
|
|
7379
|
+
controller.abort();
|
|
7380
|
+
}, POOL_TEST_TIMEOUT_MS);
|
|
7381
|
+
try {
|
|
7382
|
+
const response = await fetch(withTraePoolAction(activeRegion, "test"), {
|
|
7383
|
+
method: "POST",
|
|
7384
|
+
headers: { accept: "application/json" },
|
|
7385
|
+
credentials: "same-origin",
|
|
7386
|
+
signal: controller.signal
|
|
7387
|
+
});
|
|
7388
|
+
let failure;
|
|
7389
|
+
let sawAny = false;
|
|
7390
|
+
/**
|
|
7391
|
+
* Counted by ACCOUNT ID, not by line.
|
|
7392
|
+
*
|
|
7393
|
+
* The route streams one `{row}` per account and then a `{rows}` summary
|
|
7394
|
+
* that repeats every one of them, so counting lines would report "4 / 2".
|
|
7395
|
+
* The summary is what guarantees a client that only reads the last line
|
|
7396
|
+
* still gets the whole batch, so it cannot simply be ignored.
|
|
7397
|
+
*/
|
|
7398
|
+
const seen = /* @__PURE__ */ new Map();
|
|
7399
|
+
for await (const line of testLinesOf(response)) if (applyTestLine(line, t, (row) => {
|
|
7400
|
+
seen.set(row.accountId, row.outcome);
|
|
7401
|
+
}, (message) => {
|
|
7402
|
+
failure = message;
|
|
7403
|
+
})) sawAny = true;
|
|
7404
|
+
if (!mounted.current) return;
|
|
7405
|
+
if (failure !== void 0) {
|
|
7406
|
+
setTestFailed(failure);
|
|
7407
|
+
return;
|
|
7408
|
+
}
|
|
7409
|
+
if (!sawAny) throw new Error(t("row.poolLogGeneric"));
|
|
7410
|
+
const outcomes = [...seen.values()];
|
|
7411
|
+
setTestNote(t("composer.testDone", {
|
|
7412
|
+
ok: String(outcomes.filter((outcome) => outcome === "ok").length),
|
|
7413
|
+
total: String(outcomes.length)
|
|
7414
|
+
}));
|
|
7415
|
+
} catch {
|
|
7416
|
+
if (mounted.current && !controller.signal.aborted) setTestFailed(t("composer.testFailed"));
|
|
7417
|
+
else if (mounted.current) setTestFailed(t("composer.testTimedOut"));
|
|
7418
|
+
} finally {
|
|
7419
|
+
clearTimeout(timer);
|
|
7420
|
+
testingRef.current = false;
|
|
7421
|
+
if (mounted.current) setTesting(false);
|
|
7422
|
+
await Promise.all([fetchUsage(), fetchAccountCredits()]);
|
|
7423
|
+
}
|
|
7424
|
+
};
|
|
6672
7425
|
if (owned === void 0) return null;
|
|
6673
7426
|
const signedIn = usage?.status === "signed-in" ? usage : void 0;
|
|
6674
7427
|
const signedOut = usage !== void 0 && usage.status === "signed-out";
|
|
@@ -6711,6 +7464,15 @@ window.__ModuleLoader__.load({
|
|
|
6711
7464
|
};
|
|
6712
7465
|
});
|
|
6713
7466
|
const canSwitch = settingsScope !== void 0 && settingsScope.getSnapshot().writable === true;
|
|
7467
|
+
/**
|
|
7468
|
+
* This panel's account pool, when the Host reports one.
|
|
7469
|
+
*
|
|
7470
|
+
* A `pool` carrying no members is still a pool: the test button is drawn (the
|
|
7471
|
+
* Host answers 409 `no-members` and the panel says so) rather than hidden, so
|
|
7472
|
+
* the control's absence always means "this panel has no pool feature", never
|
|
7473
|
+
* "you have not checked anybody in yet".
|
|
7474
|
+
*/
|
|
7475
|
+
const pool = signedIn?.pool;
|
|
6714
7476
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
6715
7477
|
ref: rootRef,
|
|
6716
7478
|
className: "dsm-trae-composer-points",
|
|
@@ -6743,14 +7505,72 @@ window.__ModuleLoader__.load({
|
|
|
6743
7505
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
6744
7506
|
className: "dsm-trae-composer-panel-title",
|
|
6745
7507
|
children: activeRegion === "ai" ? t("composer.panelTitleGlobal") : t("composer.panelTitle")
|
|
6746
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.
|
|
6747
|
-
|
|
6748
|
-
|
|
6749
|
-
|
|
6750
|
-
|
|
6751
|
-
|
|
6752
|
-
|
|
6753
|
-
|
|
7508
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
7509
|
+
className: "dsm-trae-composer-panel-actions",
|
|
7510
|
+
children: [pool === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
7511
|
+
type: "button",
|
|
7512
|
+
className: "dsm-trae-composer-panel-test",
|
|
7513
|
+
title: t("composer.testHint"),
|
|
7514
|
+
"aria-label": t("composer.testHint"),
|
|
7515
|
+
disabled: testing || switchingId !== void 0,
|
|
7516
|
+
onClick: () => {
|
|
7517
|
+
testAccounts();
|
|
7518
|
+
},
|
|
7519
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("svg", {
|
|
7520
|
+
className: testing ? "dsm-trae-composer-panel-test-icon dsm-trae-composer-panel-test-icon-spin" : "dsm-trae-composer-panel-test-icon",
|
|
7521
|
+
viewBox: "0 0 16 16",
|
|
7522
|
+
width: "12",
|
|
7523
|
+
height: "12",
|
|
7524
|
+
fill: "none",
|
|
7525
|
+
"aria-hidden": "true",
|
|
7526
|
+
focusable: "false",
|
|
7527
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
|
|
7528
|
+
d: "M13.4 8a5.4 5.4 0 1 1-5.4-5.4c1.5 0 2.95.62 3.96 1.63L13.4 5.6",
|
|
7529
|
+
stroke: "currentColor",
|
|
7530
|
+
strokeWidth: "1.5",
|
|
7531
|
+
strokeLinecap: "round",
|
|
7532
|
+
strokeLinejoin: "round"
|
|
7533
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
|
|
7534
|
+
d: "M13.4 2.6v3h-3",
|
|
7535
|
+
stroke: "currentColor",
|
|
7536
|
+
strokeWidth: "1.5",
|
|
7537
|
+
strokeLinecap: "round",
|
|
7538
|
+
strokeLinejoin: "round"
|
|
7539
|
+
})]
|
|
7540
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
7541
|
+
className: "dsm-trae-composer-panel-test-label",
|
|
7542
|
+
children: testing ? t("composer.testing") : t("composer.test")
|
|
7543
|
+
})]
|
|
7544
|
+
}), onOpenSettings === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
7545
|
+
type: "button",
|
|
7546
|
+
className: "dsm-trae-composer-panel-settings",
|
|
7547
|
+
title: t("composer.openSettingsHint"),
|
|
7548
|
+
disabled: switchingId !== void 0,
|
|
7549
|
+
onClick: openSettings,
|
|
7550
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("svg", {
|
|
7551
|
+
className: "dsm-trae-composer-panel-settings-icon",
|
|
7552
|
+
viewBox: "0 0 16 16",
|
|
7553
|
+
width: "12",
|
|
7554
|
+
height: "12",
|
|
7555
|
+
fill: "none",
|
|
7556
|
+
"aria-hidden": "true",
|
|
7557
|
+
focusable: "false",
|
|
7558
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
|
|
7559
|
+
d: "M8 10.2a2.2 2.2 0 1 0 0-4.4 2.2 2.2 0 0 0 0 4.4Z",
|
|
7560
|
+
stroke: "currentColor",
|
|
7561
|
+
strokeWidth: "1.5"
|
|
7562
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
|
|
7563
|
+
d: "M12.9 9.6a1.2 1.2 0 0 0 .24 1.32l.04.04a1.4 1.4 0 1 1-1.98 1.98l-.04-.04a1.2 1.2 0 0 0-1.32-.24 1.2 1.2 0 0 0-.72 1.1v.12a1.4 1.4 0 1 1-2.8 0v-.07a1.2 1.2 0 0 0-.78-1.1 1.2 1.2 0 0 0-1.32.24l-.04.04a1.4 1.4 0 1 1-1.98-1.98l.04-.04a1.2 1.2 0 0 0 .24-1.32 1.2 1.2 0 0 0-1.1-.72h-.12a1.4 1.4 0 1 1 0-2.8h.07a1.2 1.2 0 0 0 1.1-.78 1.2 1.2 0 0 0-.24-1.32l-.04-.04a1.4 1.4 0 1 1 1.98-1.98l.04.04a1.2 1.2 0 0 0 1.32.24h.05a1.2 1.2 0 0 0 .72-1.1v-.12a1.4 1.4 0 1 1 2.8 0v.07a1.2 1.2 0 0 0 .72 1.1 1.2 1.2 0 0 0 1.32-.24l.04-.04a1.4 1.4 0 1 1 1.98 1.98l-.04.04a1.2 1.2 0 0 0-.24 1.32v.05a1.2 1.2 0 0 0 1.1.72h.12a1.4 1.4 0 1 1 0 2.8h-.07a1.2 1.2 0 0 0-1.1.72Z",
|
|
7564
|
+
stroke: "currentColor",
|
|
7565
|
+
strokeWidth: "1.5",
|
|
7566
|
+
strokeLinecap: "round",
|
|
7567
|
+
strokeLinejoin: "round"
|
|
7568
|
+
})]
|
|
7569
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
7570
|
+
className: "dsm-trae-composer-panel-settings-label",
|
|
7571
|
+
children: t("composer.openSettings")
|
|
7572
|
+
})]
|
|
7573
|
+
})]
|
|
6754
7574
|
})]
|
|
6755
7575
|
}),
|
|
6756
7576
|
signedOut ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
@@ -6817,7 +7637,22 @@ window.__ModuleLoader__.load({
|
|
|
6817
7637
|
className: "dsm-trae-composer-panel-error",
|
|
6818
7638
|
role: "status",
|
|
6819
7639
|
children: t("composer.refreshFailed")
|
|
6820
|
-
}) : null
|
|
7640
|
+
}) : null,
|
|
7641
|
+
settingsFailed ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
7642
|
+
className: "dsm-trae-composer-panel-error",
|
|
7643
|
+
role: "status",
|
|
7644
|
+
children: t("composer.openSettingsFailed")
|
|
7645
|
+
}) : null,
|
|
7646
|
+
testNote === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
7647
|
+
className: "dsm-trae-composer-panel-note",
|
|
7648
|
+
role: "status",
|
|
7649
|
+
children: testNote
|
|
7650
|
+
}),
|
|
7651
|
+
testFailed === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
7652
|
+
className: "dsm-trae-composer-panel-error",
|
|
7653
|
+
role: "status",
|
|
7654
|
+
children: testFailed
|
|
7655
|
+
})
|
|
6821
7656
|
]
|
|
6822
7657
|
}), document.body)]
|
|
6823
7658
|
});
|
|
@@ -6911,7 +7746,8 @@ window.__ModuleLoader__.load({
|
|
|
6911
7746
|
t,
|
|
6912
7747
|
provider,
|
|
6913
7748
|
region,
|
|
6914
|
-
...settingsScope === void 0 ? {} : { settingsScope }
|
|
7749
|
+
...settingsScope === void 0 ? {} : { settingsScope },
|
|
7750
|
+
...props.onOpenSettings === void 0 ? {} : { onOpenSettings: props.onOpenSettings }
|
|
6915
7751
|
});
|
|
6916
7752
|
}
|
|
6917
7753
|
//#endregion
|
|
@@ -7100,8 +7936,15 @@ window.__ModuleLoader__.load({
|
|
|
7100
7936
|
"composer.signedOut": "Not signed in",
|
|
7101
7937
|
"composer.points": "Trae CN",
|
|
7102
7938
|
"composer.pointsGlobal": "Trae Global",
|
|
7103
|
-
"composer.
|
|
7104
|
-
"composer.
|
|
7939
|
+
"composer.openSettings": "Settings",
|
|
7940
|
+
"composer.openSettingsHint": "Open this plugin's settings page",
|
|
7941
|
+
"composer.openSettingsFailed": "This host has no route to the plugin settings page",
|
|
7942
|
+
"composer.test": "Refresh",
|
|
7943
|
+
"composer.testHint": "Refresh every account checked into this panel's pool — probes each one in turn (spends a small amount of credits, can take a minute)",
|
|
7944
|
+
"composer.testing": "Refreshing…",
|
|
7945
|
+
"composer.testDone": "Pool tested: {ok} of {total} answered",
|
|
7946
|
+
"composer.testFailed": "Could not test the pool",
|
|
7947
|
+
"composer.testTimedOut": "Testing took too long and was given up on",
|
|
7105
7948
|
"composer.refreshFailed": "Could not refresh credits",
|
|
7106
7949
|
"row.accountsRefreshed": "Accounts refreshed: {count} found{selected}",
|
|
7107
7950
|
"row.accountsSaved": "bound to",
|
|
@@ -7154,8 +7997,10 @@ window.__ModuleLoader__.load({
|
|
|
7154
7997
|
"row.modelImage": "Image",
|
|
7155
7998
|
"row.modelImageAuto": "Enabled automatically: Trae marks this model multimodal.",
|
|
7156
7999
|
"row.modelRate": "{rate}x credits",
|
|
7157
|
-
"row.modelReasoning": "Reasoning: {efforts}",
|
|
7158
8000
|
"row.modelReasoningSupported": "Reasoning: supported",
|
|
8001
|
+
"row.modelReasoningLabel": "Reasoning",
|
|
8002
|
+
"row.modelReasoningFollowUpstream": "Default",
|
|
8003
|
+
"row.modelReasoningFollowUpstreamNamed": "Default ({effort})",
|
|
7159
8004
|
"row.modelUnknown": "Unknown",
|
|
7160
8005
|
"row.modelCapabilityPending": "Image input follows Trae's own multimodal flag, plus anything you tick yourself.",
|
|
7161
8006
|
"row.poolTitle": "Account pool",
|
|
@@ -7307,7 +8152,11 @@ window.__ModuleLoader__.load({
|
|
|
7307
8152
|
"row.poolRowDeleteStillSigned": "removed this copy, but {accountName} is still signed in to Trae",
|
|
7308
8153
|
"row.poolRowDeleteNoCopy": "{accountName} had no plugin copy — only its Trae app login",
|
|
7309
8154
|
"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.",
|
|
7310
|
-
"row.cheer": "Star on GitHub"
|
|
8155
|
+
"row.cheer": "Star on GitHub",
|
|
8156
|
+
"nav.panel": "Trae",
|
|
8157
|
+
"row.pageBack": "Back to conversation",
|
|
8158
|
+
"row.sidebarPanelLabel": "Show Trae in the sidebar",
|
|
8159
|
+
"row.sidebarPanelHint": "Adds a Trae row to the DSH sidebar that opens this card as a full page. Off by default: the row takes a global sidebar seat shared by every installed plugin, and the Plugins page already reaches the same content. Takes effect on the click — no Save needed — and switching it off while the page is open returns you to the conversation."
|
|
7311
8160
|
};
|
|
7312
8161
|
const zh = {
|
|
7313
8162
|
"row.title": "接入使用Trae积分与模型(dsh-connect-trae)",
|
|
@@ -7488,8 +8337,15 @@ window.__ModuleLoader__.load({
|
|
|
7488
8337
|
"composer.signedOut": "未登录",
|
|
7489
8338
|
"composer.points": "Trae CN",
|
|
7490
8339
|
"composer.pointsGlobal": "Trae 国际版",
|
|
7491
|
-
"composer.
|
|
7492
|
-
"composer.
|
|
8340
|
+
"composer.openSettings": "设置",
|
|
8341
|
+
"composer.openSettingsHint": "打开本插件的设置页",
|
|
8342
|
+
"composer.openSettingsFailed": "这个宿主没有通向插件设置页的入口",
|
|
8343
|
+
"composer.test": "刷新",
|
|
8344
|
+
"composer.testHint": "刷新本面板账号池里勾选账号的状态——逐个探测(会消耗少量积分,最长约一分钟)",
|
|
8345
|
+
"composer.testing": "刷新中…",
|
|
8346
|
+
"composer.testDone": "账号池已测:{total} 个里 {ok} 个可用",
|
|
8347
|
+
"composer.testFailed": "账号池测速失败",
|
|
8348
|
+
"composer.testTimedOut": "测速用时过长,已放弃",
|
|
7493
8349
|
"composer.refreshFailed": "积分刷新失败",
|
|
7494
8350
|
"row.accountsRefreshed": "已刷新账号:找到 {count} 个账号{selected}",
|
|
7495
8351
|
"row.accountsSaved": "已绑定",
|
|
@@ -7542,8 +8398,10 @@ window.__ModuleLoader__.load({
|
|
|
7542
8398
|
"row.modelImage": "图片",
|
|
7543
8399
|
"row.modelImageAuto": "已自动开启:Trae 把该模型标为多模态。",
|
|
7544
8400
|
"row.modelRate": "积分 {rate}x",
|
|
7545
|
-
"row.modelReasoning": "推理强度:{efforts}",
|
|
7546
8401
|
"row.modelReasoningSupported": "推理:支持",
|
|
8402
|
+
"row.modelReasoningLabel": "思考强度",
|
|
8403
|
+
"row.modelReasoningFollowUpstream": "默认",
|
|
8404
|
+
"row.modelReasoningFollowUpstreamNamed": "默认({effort})",
|
|
7547
8405
|
"row.modelUnknown": "未知",
|
|
7548
8406
|
"row.modelCapabilityPending": "图片输入跟随 Trae 自身的多模态标注,并叠加你手动勾选的模型。",
|
|
7549
8407
|
"row.poolTitle": "账号池",
|
|
@@ -7695,7 +8553,11 @@ window.__ModuleLoader__.load({
|
|
|
7695
8553
|
"row.poolRowDeleteStillSigned": "已删除副本,但 {accountName} 仍登录在 Trae 中",
|
|
7696
8554
|
"row.poolRowDeleteNoCopy": "{accountName} 没有插件副本——它只存在于 Trae 应用登录里",
|
|
7697
8555
|
"row.poolDeleteHint": "本插件会为本机每个 Trae 登录各创建一份自己的副本(刷新时自动创建,已有副本则不动),卡片只显示、也只使用这些副本。「删除」会同时删掉副本并把该账号从 Trae 应用中退出登录,改写的每个文件都留一份备份;只要该账号还登录在 Trae 里,就不会被重新收录。Trae 正在运行时会把自己的状态回写到文件,所以要先完全退出——卡片会点名是哪个应用。",
|
|
7698
|
-
"row.cheer": "鼓励一下"
|
|
8556
|
+
"row.cheer": "鼓励一下",
|
|
8557
|
+
"nav.panel": "Trae",
|
|
8558
|
+
"row.pageBack": "返回会话",
|
|
8559
|
+
"row.sidebarPanelLabel": "在侧边栏显示 Trae",
|
|
8560
|
+
"row.sidebarPanelHint": "在 DSH 侧边栏加一个 Trae 行,点开就是这张卡片的整页版。默认关闭:这一行占的是所有插件共用的全局侧边栏座位,而「插件」页本来就能看到同样的内容。点一下即生效,不用保存;页面开着时关掉它,会直接回到会话。"
|
|
7699
8561
|
};
|
|
7700
8562
|
//#endregion
|
|
7701
8563
|
//#region src/client/TraeModelLabel.tsx
|
|
@@ -7980,6 +8842,47 @@ window.__ModuleLoader__.load({
|
|
|
7980
8842
|
* @param props - The host's model identity and the placement of this label.
|
|
7981
8843
|
* @returns The catalog name, followed by the promotion chip when one holds.
|
|
7982
8844
|
*/
|
|
8845
|
+
/**
|
|
8846
|
+
* This plugin's seat in the HOST's model-label cell (issue #74).
|
|
8847
|
+
*
|
|
8848
|
+
* `conversation.input.model.label` is a `single` slot: `SlotCore.register()`
|
|
8849
|
+
* THROWS when a second registration claims the same priority, with
|
|
8850
|
+
* "single slot ... already has a registration at priority 0 ... register at a
|
|
8851
|
+
* different priority to shadow it (lowest renders)". Priority 0 is the DEFAULT,
|
|
8852
|
+
* so it is the one value that is guaranteed to be contested.
|
|
8853
|
+
*
|
|
8854
|
+
* WHY `-0.25` AND NOT 0. Measured on the live desktop host (0.2.1-rc.5) the
|
|
8855
|
+
* cell is already shared:
|
|
8856
|
+
*
|
|
8857
|
+
* dsh-connect-qoder-client priority -1 (active — lowest renders)
|
|
8858
|
+
* dsh-connect-workbuddy-client priority -0.5
|
|
8859
|
+
* dsh-connect-trae-client priority 0 (this plugin)
|
|
8860
|
+
*
|
|
8861
|
+
* -1 and -0.5 are the two sibling connectors' seats and must NOT be reused —
|
|
8862
|
+
* two plugins on one priority collide for exactly the reason 0 did. `-0.25`
|
|
8863
|
+
* sits between them, legal, and collides with neither. The value only has to be
|
|
8864
|
+
* DISTINCT from whatever else claims the cell; it deliberately does not read the
|
|
8865
|
+
* slot ledger or negotiate with other plugins (this repository's §零 invariant,
|
|
8866
|
+
* and the host's own shipped README reaches the same conclusion — its documented
|
|
8867
|
+
* remedy for a same-priority conflict is a configuration decision, not a runtime
|
|
8868
|
+
* handshake).
|
|
8869
|
+
*
|
|
8870
|
+
* WHAT WINNING/HOLDING COSTS. This cell is `single`, so the other registrants'
|
|
8871
|
+
* decorations on the rows this plugin claims are not composed. That is the
|
|
8872
|
+
* documented, accepted trade-off, and it is why the component returns the plain
|
|
8873
|
+
* catalog name for any provider that is not ours — the slot's own minimum.
|
|
8874
|
+
*
|
|
8875
|
+
* WHY A DISTINCT PRIORITY IS NOT ENOUGH ON ITS OWN. It removes the cross-plugin
|
|
8876
|
+
* collision, but issue #74's error was a SECOND registration of THIS plugin
|
|
8877
|
+
* against its own first one (the report's stack shows
|
|
8878
|
+
* "registered by dsh-connect-trae-client" colliding with priority 0). That is
|
|
8879
|
+
* the re-entrancy path `inject` allows: the callback runs again whenever the
|
|
8880
|
+
* slot's declaration lifetime collapses and re-declares, so a callback that
|
|
8881
|
+
* registers unconditionally double-registers. `index.tsx` therefore also
|
|
8882
|
+
* disposes its previous entry before every attempt. Priority alone would have
|
|
8883
|
+
* made the throw rarer and harder to read, not absent.
|
|
8884
|
+
*/
|
|
8885
|
+
const MODEL_LABEL_PRIORITY = -.25;
|
|
7983
8886
|
function TraeModelLabel(props) {
|
|
7984
8887
|
const { provider, model } = props;
|
|
7985
8888
|
const panel = panelOfProvider(provider.id);
|
|
@@ -8033,6 +8936,92 @@ window.__ModuleLoader__.load({
|
|
|
8033
8936
|
});
|
|
8034
8937
|
}
|
|
8035
8938
|
//#endregion
|
|
8939
|
+
//#region src/client/settings-route.ts
|
|
8940
|
+
/**
|
|
8941
|
+
* The route from the composer credit readout to THIS plugin's settings page.
|
|
8942
|
+
*
|
|
8943
|
+
* ## Why a module and not three lines inside `apply`
|
|
8944
|
+
*
|
|
8945
|
+
* The entry (`src/client/index.tsx`) imports browser-only DSH packages the Node
|
|
8946
|
+
* test environment cannot load, so anything worth asserting on has to live
|
|
8947
|
+
* outside it — the same seam `panel-switch.ts` provides for the sidebar opt-in.
|
|
8948
|
+
* The spec then exercises the REAL decision instead of a mirror of it.
|
|
8949
|
+
*
|
|
8950
|
+
* ## Why two routes
|
|
8951
|
+
*
|
|
8952
|
+
* On this host line a plugin's settings are not a "page in Settings": the
|
|
8953
|
+
* Plugins page owns them, and each bundle's page (`plugins.bundle.config` /
|
|
8954
|
+
* `plugins.row.config`) IS the configuration surface — Settings keeps only the
|
|
8955
|
+
* read-only built-in inventory. So "open the plugin settings" means "open the
|
|
8956
|
+
* Plugins page on this bundle", and the supported way to ask for that is the
|
|
8957
|
+
* `pluginNavigation` service the Plugins page provides: `openBundle(packageName)`
|
|
8958
|
+
* selects that page without touching the Session.
|
|
8959
|
+
*
|
|
8960
|
+
* `pluginNavigation` is provided by `@deepseek-ai/dsh-client-ui-plugin-manager`,
|
|
8961
|
+
* NOT by the shell. A profile can be composed without it, and plugin load order
|
|
8962
|
+
* is not something a contributor may assume — so the probe happens at CALL time
|
|
8963
|
+
* rather than at registration time. Probing once in `apply` would capture
|
|
8964
|
+
* `undefined` on a page whose Plugins bundle simply applied after ours, and the
|
|
8965
|
+
* button would be dead for the rest of the session with nothing to re-evaluate it.
|
|
8966
|
+
*
|
|
8967
|
+
* The fallback reaches the SAME page through the lower-level `layout` service.
|
|
8968
|
+
* `selectPanel('plugins')` is the shipped main key of the Plugins page (the
|
|
8969
|
+
* plugin-manager bundle registers `sidebar.panellist` id / `main` key
|
|
8970
|
+
* `'plugins'`), so a host without `pluginNavigation` still lands the user where
|
|
8971
|
+
* the settings are, one click from the row. It is a fallback and not the primary
|
|
8972
|
+
* route deliberately: `'plugins'` is another package's private key, while
|
|
8973
|
+
* `openBundle` is that package's published seam. `selectPanel` THROWS when the
|
|
8974
|
+
* key is not registered (Plugins page disabled, trimmed profile), so the call is
|
|
8975
|
+
* guarded rather than trusted.
|
|
8976
|
+
*
|
|
8977
|
+
* ## Why `false` rather than an exception
|
|
8978
|
+
*
|
|
8979
|
+
* The caller is a popover button. There is no supported host line lacking both
|
|
8980
|
+
* routes, but "cannot" must not become a thrown error inside a click handler on
|
|
8981
|
+
* a user's machine; it becomes a `false` the caller reports, and a button that
|
|
8982
|
+
* stays honest about it.
|
|
8983
|
+
*/
|
|
8984
|
+
/**
|
|
8985
|
+
* The package name the Plugins page knows this plugin by.
|
|
8986
|
+
*
|
|
8987
|
+
* `openBundle` matches the inventory's `pkg.name` by EXACT equality, and that
|
|
8988
|
+
* name is the profile's package name — the npm package name — not the Loader
|
|
8989
|
+
* entry id (which on a live Desktop profile can be `include:dsh-connect-trae`).
|
|
8990
|
+
* This is the same reason the card registers under every spelling the host has
|
|
8991
|
+
* been observed to dispatch: the comparison is exact and the deployment picks
|
|
8992
|
+
* the spelling. The inventory side of that comparison is the package name in
|
|
8993
|
+
* every composition observed, so that is what `openBundle` is asked for.
|
|
8994
|
+
*
|
|
8995
|
+
* Deliberately ONE name, not a list: `openBundle` selects a target and an
|
|
8996
|
+
* unknown target renders the LIST, so a second call with a spelling the
|
|
8997
|
+
* inventory does not hold would undo the first one's navigation.
|
|
8998
|
+
*/
|
|
8999
|
+
const TRAE_PACKAGE_NAME = "dsh-connect-trae";
|
|
9000
|
+
/** The shipped `main` / `sidebar.panellist` key of the Plugins page. */
|
|
9001
|
+
const PLUGINS_PANEL_ID = "plugins";
|
|
9002
|
+
/**
|
|
9003
|
+
* Open this plugin's settings page.
|
|
9004
|
+
*
|
|
9005
|
+
* @param get - the entry's soft service probe (`ctx.get`), which returns
|
|
9006
|
+
* `undefined` for an absent service instead of throwing. Called HERE, on every
|
|
9007
|
+
* invocation, so a service that appears after `apply` is still found.
|
|
9008
|
+
* @returns whether a route was taken. `false` means the caller's control should
|
|
9009
|
+
* report that this host has no way there — see the module note.
|
|
9010
|
+
*/
|
|
9011
|
+
function openPluginSettingsPage(get) {
|
|
9012
|
+
const navigation = get("pluginNavigation");
|
|
9013
|
+
if (navigation !== void 0 && typeof navigation.openBundle === "function") try {
|
|
9014
|
+
navigation.openBundle(TRAE_PACKAGE_NAME);
|
|
9015
|
+
return true;
|
|
9016
|
+
} catch {}
|
|
9017
|
+
const layout = get("layout");
|
|
9018
|
+
if (layout !== void 0 && typeof layout.selectPanel === "function") try {
|
|
9019
|
+
layout.selectPanel(PLUGINS_PANEL_ID);
|
|
9020
|
+
return true;
|
|
9021
|
+
} catch {}
|
|
9022
|
+
return false;
|
|
9023
|
+
}
|
|
9024
|
+
//#endregion
|
|
8036
9025
|
//#region src/client/index.tsx
|
|
8037
9026
|
/** Stable browser-plugin name. */
|
|
8038
9027
|
const name = "dsh-connect-trae-client";
|
|
@@ -8046,21 +9035,87 @@ window.__ModuleLoader__.load({
|
|
|
8046
9035
|
* configForms is not served yet.
|
|
8047
9036
|
*/
|
|
8048
9037
|
const inject = ["slots", "locale"];
|
|
9038
|
+
const liveSlotEntries = /* @__PURE__ */ new Map();
|
|
9039
|
+
/**
|
|
9040
|
+
* Release this client's hold on `identity`, if it has one.
|
|
9041
|
+
*
|
|
9042
|
+
* `entryOnly` exists because of a subtle self-collision: the release is called
|
|
9043
|
+
* from INSIDE the `inject` callback as well as from `apply`, and disposing the
|
|
9044
|
+
* inject controller from within its own callback would tear down the effect
|
|
9045
|
+
* that is currently executing. Inside the callback only the ENTRY is released;
|
|
9046
|
+
* from `apply`, both.
|
|
9047
|
+
*/
|
|
9048
|
+
function releaseSlotEntry(identity, entryOnly = false) {
|
|
9049
|
+
const held = liveSlotEntries.get(identity);
|
|
9050
|
+
if (held === void 0) return;
|
|
9051
|
+
liveSlotEntries.delete(identity);
|
|
9052
|
+
if (held.disposeEntry !== void 0) try {
|
|
9053
|
+
held.disposeEntry();
|
|
9054
|
+
} catch {}
|
|
9055
|
+
if (!entryOnly && held.disposeInject !== void 0) try {
|
|
9056
|
+
held.disposeInject();
|
|
9057
|
+
} catch {}
|
|
9058
|
+
}
|
|
8049
9059
|
/**
|
|
8050
|
-
* Register
|
|
8051
|
-
*
|
|
8052
|
-
*
|
|
8053
|
-
*
|
|
8054
|
-
*
|
|
8055
|
-
*
|
|
8056
|
-
* the
|
|
8057
|
-
*
|
|
8058
|
-
*
|
|
8059
|
-
*
|
|
8060
|
-
*
|
|
8061
|
-
*
|
|
8062
|
-
*
|
|
9060
|
+
* Register one slot entry, surviving EVERY way the registry can refuse it.
|
|
9061
|
+
*
|
|
9062
|
+
* WHY THE CATCH IS INSIDE THE CALLBACK, which is the whole point of issue #74.
|
|
9063
|
+
* `slots.inject()` does not necessarily run its callback now: when the slot is
|
|
9064
|
+
* not declared yet it subscribes and runs the callback LATER. A later throw is
|
|
9065
|
+
* re-thrown by the renderer's own `changed()` handler out of a `queueMicrotask`,
|
|
9066
|
+
* where no call-site try/catch can reach it — the user gets a renderer error
|
|
9067
|
+
* banner on the first paint after a restart, and a reload (which re-declares in
|
|
9068
|
+
* a different order) clears it. Moving the catch inside the callback turns every
|
|
9069
|
+
* refusal into a log line, matching this file's "degrade, never take the page
|
|
9070
|
+
* down" contract.
|
|
9071
|
+
*
|
|
9072
|
+
* WHY THE RELEASE COMES FIRST. A `single` slot rejects a second live entry at
|
|
9073
|
+
* the same priority. That refusal is OUR fault and we can fix it rather than
|
|
9074
|
+
* report it, so each run releases whatever this identity already holds.
|
|
9075
|
+
*
|
|
9076
|
+
* @param ctx - The applying plugin's context (module scope has none).
|
|
9077
|
+
* @param identity - Stable key for this seat.
|
|
9078
|
+
* @param slotKey - The host slot to register into.
|
|
9079
|
+
* @param register - Performs the registration, returning its disposer.
|
|
9080
|
+
* @param describe - Names the seat in the failure log.
|
|
8063
9081
|
*/
|
|
9082
|
+
function registerSlotEntry(ctx, identity, slotKey, register, describe) {
|
|
9083
|
+
const hold = {};
|
|
9084
|
+
const fail = (error) => {
|
|
9085
|
+
console.error(`[dsh-connect-trae] ${describe} failed to register (host provider unaffected):`, error);
|
|
9086
|
+
};
|
|
9087
|
+
const run = () => {
|
|
9088
|
+
releaseSlotEntry(identity, true);
|
|
9089
|
+
try {
|
|
9090
|
+
const disposeEntry = register();
|
|
9091
|
+
hold.disposeEntry = disposeEntry;
|
|
9092
|
+
const current = liveSlotEntries.get(identity);
|
|
9093
|
+
if (current === void 0) liveSlotEntries.set(identity, hold);
|
|
9094
|
+
else current.disposeEntry = disposeEntry;
|
|
9095
|
+
return disposeEntry;
|
|
9096
|
+
} catch (error) {
|
|
9097
|
+
fail(error);
|
|
9098
|
+
return () => {};
|
|
9099
|
+
}
|
|
9100
|
+
};
|
|
9101
|
+
try {
|
|
9102
|
+
const result = ctx.slots.inject(slotKey, () => {
|
|
9103
|
+
try {
|
|
9104
|
+
return run();
|
|
9105
|
+
} catch (error) {
|
|
9106
|
+
fail(error);
|
|
9107
|
+
return () => {};
|
|
9108
|
+
}
|
|
9109
|
+
});
|
|
9110
|
+
hold.disposeInject = result;
|
|
9111
|
+
const current = liveSlotEntries.get(identity);
|
|
9112
|
+
if (current === void 0) liveSlotEntries.set(identity, hold);
|
|
9113
|
+
else current.disposeInject = result;
|
|
9114
|
+
} catch (error) {
|
|
9115
|
+
releaseSlotEntry(identity);
|
|
9116
|
+
fail(error);
|
|
9117
|
+
}
|
|
9118
|
+
}
|
|
8064
9119
|
function apply(ctx) {
|
|
8065
9120
|
try {
|
|
8066
9121
|
const namespace = "settings.trae";
|
|
@@ -8070,6 +9125,16 @@ window.__ModuleLoader__.load({
|
|
|
8070
9125
|
}), "dsh-connect-trae: settings copy");
|
|
8071
9126
|
const t = ctx.locale.bind(namespace);
|
|
8072
9127
|
const softGet = (name) => ctx.get(name);
|
|
9128
|
+
/**
|
|
9129
|
+
* The composer panel's route to this plugin's own settings page.
|
|
9130
|
+
*
|
|
9131
|
+
* Wrapped in a closure, not resolved here: `pluginNavigation` belongs to the
|
|
9132
|
+
* Plugins bundle and its apply order is not ours to assume, so probing once
|
|
9133
|
+
* at `apply` could capture `undefined` permanently. The decision itself (and
|
|
9134
|
+
* its `layout` fallback) lives in `settings-route.ts`, where the spec can
|
|
9135
|
+
* exercise the real chain instead of a mirror.
|
|
9136
|
+
*/
|
|
9137
|
+
const onOpenSettings = () => openPluginSettingsPage(softGet);
|
|
8073
9138
|
const forms = softGet("configForms");
|
|
8074
9139
|
/**
|
|
8075
9140
|
* The card's settings scope, bound to whichever namespace the Host actually
|
|
@@ -8140,19 +9205,15 @@ window.__ModuleLoader__.load({
|
|
|
8140
9205
|
return scope;
|
|
8141
9206
|
})();
|
|
8142
9207
|
const registerCard = (slotName, key) => {
|
|
8143
|
-
|
|
8144
|
-
|
|
8145
|
-
|
|
8146
|
-
|
|
8147
|
-
|
|
8148
|
-
|
|
8149
|
-
|
|
8150
|
-
|
|
8151
|
-
|
|
8152
|
-
}, TraeUsageCard));
|
|
8153
|
-
} catch (error) {
|
|
8154
|
-
console.error(`[dsh-connect-trae] card slot "${slotName}" failed to register (host provider unaffected):`, error);
|
|
8155
|
-
}
|
|
9208
|
+
registerSlotEntry(ctx, `${slotName}#${key}`, slotName, () => ctx.slots.register({
|
|
9209
|
+
name: slotName,
|
|
9210
|
+
key,
|
|
9211
|
+
priority: 30,
|
|
9212
|
+
inject: () => ({
|
|
9213
|
+
t,
|
|
9214
|
+
settingsScope
|
|
9215
|
+
})
|
|
9216
|
+
}, TraeUsageCard), `card slot "${slotName}"`);
|
|
8156
9217
|
};
|
|
8157
9218
|
registerCard("plugins.bundle.config", "dsh-connect-trae");
|
|
8158
9219
|
registerCard("plugins.row.config", "dsh-connect-trae#dsh-connect-trae");
|
|
@@ -8183,21 +9244,18 @@ window.__ModuleLoader__.load({
|
|
|
8183
9244
|
* deciding another's.
|
|
8184
9245
|
*/
|
|
8185
9246
|
const registerPointsSlot = (slotName, id, order, position) => {
|
|
8186
|
-
|
|
8187
|
-
|
|
8188
|
-
|
|
8189
|
-
|
|
8190
|
-
|
|
8191
|
-
|
|
8192
|
-
|
|
8193
|
-
|
|
8194
|
-
|
|
8195
|
-
|
|
8196
|
-
|
|
8197
|
-
|
|
8198
|
-
} catch (error) {
|
|
8199
|
-
console.error(`[dsh-connect-trae] points slot "${slotName}" failed to register (host provider unaffected):`, error);
|
|
8200
|
-
}
|
|
9247
|
+
registerSlotEntry(ctx, `${slotName}#${id}`, slotName, () => ctx.slots.register({
|
|
9248
|
+
name: slotName,
|
|
9249
|
+
id,
|
|
9250
|
+
order,
|
|
9251
|
+
locale: namespace,
|
|
9252
|
+
inject: () => ({
|
|
9253
|
+
t,
|
|
9254
|
+
settingsScope,
|
|
9255
|
+
position,
|
|
9256
|
+
onOpenSettings
|
|
9257
|
+
})
|
|
9258
|
+
}, ComposerPointsGate), `points slot "${slotName}"`);
|
|
8201
9259
|
};
|
|
8202
9260
|
registerPointsSlot("conversation.input.right", "dsh-connect-trae-composer-points", 4, "composer");
|
|
8203
9261
|
registerPointsSlot("conversation.composer.dock", "dsh-connect-trae-stats-points", 10, "stats");
|
|
@@ -8218,13 +9276,140 @@ window.__ModuleLoader__.load({
|
|
|
8218
9276
|
* down with it.
|
|
8219
9277
|
*/
|
|
8220
9278
|
const registerModelLabel = () => {
|
|
8221
|
-
|
|
8222
|
-
|
|
8223
|
-
|
|
8224
|
-
|
|
8225
|
-
}
|
|
9279
|
+
registerSlotEntry(ctx, "conversation.input.model.label", "conversation.input.model.label", () => ctx.slots.register({
|
|
9280
|
+
name: "conversation.input.model.label",
|
|
9281
|
+
priority: MODEL_LABEL_PRIORITY
|
|
9282
|
+
}, TraeModelLabel), "model label slot");
|
|
8226
9283
|
};
|
|
8227
9284
|
registerModelLabel();
|
|
9285
|
+
/**
|
|
9286
|
+
* The sidebar glyph for the optional panel row.
|
|
9287
|
+
*
|
|
9288
|
+
* The host renders `sidebar.panellist` registrants with `{ size, active }`
|
|
9289
|
+
* inside a `currentColor`-themed glyph cell, so the brand mark is drawn as a
|
|
9290
|
+
* small `<img>` — the PNG's own colours read as the plugin's identity, and
|
|
9291
|
+
* the row's selection state is the host's to paint around it.
|
|
9292
|
+
*/
|
|
9293
|
+
function TraePanelIcon(props) {
|
|
9294
|
+
const edge = typeof props.size === "number" && props.size > 0 ? props.size : 16;
|
|
9295
|
+
return (0, react.createElement)("img", {
|
|
9296
|
+
className: "dsm-trae-panel-icon",
|
|
9297
|
+
src: TRAE_PLUGIN_ICON,
|
|
9298
|
+
alt: "",
|
|
9299
|
+
width: edge,
|
|
9300
|
+
height: edge,
|
|
9301
|
+
"aria-hidden": "true"
|
|
9302
|
+
});
|
|
9303
|
+
}
|
|
9304
|
+
/**
|
|
9305
|
+
* Back to the conversation, for the page's header.
|
|
9306
|
+
*
|
|
9307
|
+
* Soft-probed: `layout` is a host service this entry deliberately does not
|
|
9308
|
+
* declare in `inject` (an absent service keeps `apply` from running at all,
|
|
9309
|
+
* and the page is useful without the back affordance). `selectPanel(null)` is
|
|
9310
|
+
* the host's own way of handing the centre column back.
|
|
9311
|
+
*/
|
|
9312
|
+
const backToConversation = () => {
|
|
9313
|
+
const layout = softGet("layout");
|
|
9314
|
+
if (layout !== void 0 && typeof layout.selectPanel === "function") layout.selectPanel(null);
|
|
9315
|
+
};
|
|
9316
|
+
/**
|
|
9317
|
+
* The Trae panel in the sidebar (`sidebar.panellist` row + `main` keyed
|
|
9318
|
+
* page), mirroring the sibling `dsh-connect-workbuddy` panel and the host's
|
|
9319
|
+
* own Plugins row.
|
|
9320
|
+
*
|
|
9321
|
+
* OPT-IN (`Config.sidebarPanel`, default false). Registering it is NOT the
|
|
9322
|
+
* same as showing it: the host's sidebar builds the nav row from the slot's
|
|
9323
|
+
* ENTRY METADATA, so a component returning `null` leaves a blank but still
|
|
9324
|
+
* clickable button rather than no button. The row therefore has to be
|
|
9325
|
+
* registered only while the switch is on, which is what
|
|
9326
|
+
* {@link syncPanelRegistration} owns. See `panel-switch.ts` for the store
|
|
9327
|
+
* that decision rides on.
|
|
9328
|
+
*/
|
|
9329
|
+
const PANEL_ID = "dsh-connect-trae";
|
|
9330
|
+
const registerPanel = () => {
|
|
9331
|
+
registerSlotEntry(ctx, "sidebar.panellist#dsh-connect-trae", "sidebar.panellist", () => ctx.slots.register({
|
|
9332
|
+
name: "sidebar.panellist",
|
|
9333
|
+
id: PANEL_ID,
|
|
9334
|
+
order: 2,
|
|
9335
|
+
label: () => t("nav.panel"),
|
|
9336
|
+
locale: namespace
|
|
9337
|
+
}, TraePanelIcon), "sidebar panel row");
|
|
9338
|
+
registerSlotEntry(ctx, `main#${PANEL_ID}`, "main", () => ctx.slots.register({
|
|
9339
|
+
name: "main",
|
|
9340
|
+
key: PANEL_ID,
|
|
9341
|
+
locale: namespace,
|
|
9342
|
+
inject: () => ({
|
|
9343
|
+
t,
|
|
9344
|
+
settingsScope,
|
|
9345
|
+
surface: "page",
|
|
9346
|
+
backToConversation
|
|
9347
|
+
})
|
|
9348
|
+
}, TraeUsageCard), "sidebar panel page");
|
|
9349
|
+
return [() => {
|
|
9350
|
+
releaseSlotEntry("sidebar.panellist#dsh-connect-trae");
|
|
9351
|
+
}, () => {
|
|
9352
|
+
releaseSlotEntry(`main#${PANEL_ID}`);
|
|
9353
|
+
}];
|
|
9354
|
+
};
|
|
9355
|
+
/**
|
|
9356
|
+
* The live panel registrations, kept SEPARATE from the settings slots.
|
|
9357
|
+
*
|
|
9358
|
+
* They are the only slots in this plugin that come and go while the page is
|
|
9359
|
+
* open (the opt-in switch), so owning them apart means flipping the switch
|
|
9360
|
+
* does not dispose and re-create the card, the composer readout and the
|
|
9361
|
+
* model label — which would be a visible flicker for a change that has
|
|
9362
|
+
* nothing to do with them.
|
|
9363
|
+
*/
|
|
9364
|
+
let panelDisposers = [];
|
|
9365
|
+
/**
|
|
9366
|
+
* Bring the panel registrations in line with the opt-in switch.
|
|
9367
|
+
*
|
|
9368
|
+
* Dispose FIRST, then register: a live `key`/`id` cannot be registered
|
|
9369
|
+
* twice (the ledger is keyed), and the throw would be swallowed into "no
|
|
9370
|
+
* entry at all".
|
|
9371
|
+
*/
|
|
9372
|
+
const syncPanelRegistration = () => {
|
|
9373
|
+
if (panelDisposers.length > 0) {
|
|
9374
|
+
const layout = softGet("layout");
|
|
9375
|
+
try {
|
|
9376
|
+
if (layout?.panelInfo?.getSnapshot?.()?.activePanelId === PANEL_ID) layout.selectPanel?.(null);
|
|
9377
|
+
} catch {}
|
|
9378
|
+
}
|
|
9379
|
+
for (const dispose of panelDisposers) try {
|
|
9380
|
+
dispose();
|
|
9381
|
+
} catch {}
|
|
9382
|
+
panelDisposers = [];
|
|
9383
|
+
if (!sidebarPanelWanted()) return;
|
|
9384
|
+
panelDisposers = registerPanel();
|
|
9385
|
+
};
|
|
9386
|
+
/**
|
|
9387
|
+
* Follow the switch, and SEED it from the Host.
|
|
9388
|
+
*
|
|
9389
|
+
* The seed is a status read rather than the settings scope on purpose: this
|
|
9390
|
+
* namespace's browser mirror is documented as stale on the affected 0.1.7
|
|
9391
|
+
* deployments, and here a stale `false` would mean the row never appears —
|
|
9392
|
+
* with no later event to correct it, because the mirror reports namespace
|
|
9393
|
+
* edges, not value changes.
|
|
9394
|
+
*
|
|
9395
|
+
* A failed read is NOT an error to show: OFF is the documented default
|
|
9396
|
+
* answer, so a plugin whose status route is unreachable simply gets no row.
|
|
9397
|
+
*
|
|
9398
|
+
* The SUBSCRIPTION is what makes the switch live: the card calls
|
|
9399
|
+
* `setSidebarPanelWanted` after a verified write, so the row appears and
|
|
9400
|
+
* disappears without a reload. The host's layout service drops an
|
|
9401
|
+
* `activePanelId` whose `main` key has gone, so switching off while the page
|
|
9402
|
+
* is open returns the centre column to the conversation on its own.
|
|
9403
|
+
*/
|
|
9404
|
+
ctx.effect(() => subscribeSidebarPanelWanted(syncPanelRegistration), "dsh-connect-trae: sidebar panel opt-in");
|
|
9405
|
+
syncPanelRegistration();
|
|
9406
|
+
(async () => {
|
|
9407
|
+
try {
|
|
9408
|
+
const response = await fetch(withTraeRegion(TRAE_USAGE_PATH, "cn"), { headers: { accept: "application/json" } });
|
|
9409
|
+
if (!response.ok) return;
|
|
9410
|
+
if ((await response.json()).sidebarPanelEnabled === true) setSidebarPanelWanted(true);
|
|
9411
|
+
} catch {}
|
|
9412
|
+
})();
|
|
8228
9413
|
} catch (error) {
|
|
8229
9414
|
console.error("[dsh-connect-trae] client card failed to load (host provider unaffected):", error);
|
|
8230
9415
|
}
|