@mars-sea/dsh-commandcode-provider 0.11.12 → 0.11.14
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 +18 -0
- package/README.md +19 -19
- package/README.zh-CN.md +19 -19
- package/lib/client.js +2055 -1770
- package/lib/client.js.map +1 -1
- package/lib/index.d.ts +16 -11
- package/lib/index.js +154 -68
- package/lib/index.js.map +1 -1
- package/package.json +1 -1
package/lib/client.js
CHANGED
|
@@ -37,7 +37,6 @@ window.__ModuleLoader__.load({
|
|
|
37
37
|
}
|
|
38
38
|
//#endregion
|
|
39
39
|
//#region src/client/settings-scope.ts
|
|
40
|
-
const MEMORY_STATUS = "unavailable";
|
|
41
40
|
/**
|
|
42
41
|
* Backoff before re-reading a directory whose FIRST read failed, in millis.
|
|
43
42
|
*
|
|
@@ -89,7 +88,6 @@ window.__ModuleLoader__.load({
|
|
|
89
88
|
resolveRemote;
|
|
90
89
|
timer;
|
|
91
90
|
store;
|
|
92
|
-
persistence;
|
|
93
91
|
inFlight;
|
|
94
92
|
rerun = false;
|
|
95
93
|
generation = 0;
|
|
@@ -99,15 +97,13 @@ window.__ModuleLoader__.load({
|
|
|
99
97
|
disposed = false;
|
|
100
98
|
/**
|
|
101
99
|
* @param resolveRemote - Reads the inject-captured `remote.settings` namespace.
|
|
102
|
-
* @param persistence - Client-selected Host persistence; non-loopback pages may stay process-local.
|
|
103
100
|
* @param timer - Retry timer seam (see {@link SETTINGS_DESCRIBE_RETRY_MS}).
|
|
104
101
|
*/
|
|
105
|
-
constructor(resolveRemote,
|
|
102
|
+
constructor(resolveRemote, timer = REAL_SETTINGS_TIMER) {
|
|
106
103
|
this.resolveRemote = resolveRemote;
|
|
107
104
|
this.timer = timer;
|
|
108
|
-
this.persistence = persistence;
|
|
109
105
|
this.store = createSnapshotStore({
|
|
110
|
-
status:
|
|
106
|
+
status: "idle",
|
|
111
107
|
view: null,
|
|
112
108
|
error: null
|
|
113
109
|
});
|
|
@@ -118,7 +114,7 @@ window.__ModuleLoader__.load({
|
|
|
118
114
|
subscribe(listener) {
|
|
119
115
|
return this.store.subscribe(listener);
|
|
120
116
|
}
|
|
121
|
-
/** The cheap idempotent entry: kick a first read
|
|
117
|
+
/** The cheap idempotent entry: kick a first read. */
|
|
122
118
|
ensure() {
|
|
123
119
|
this.load();
|
|
124
120
|
}
|
|
@@ -128,7 +124,7 @@ window.__ModuleLoader__.load({
|
|
|
128
124
|
* @returns settlement after this call's freshness is reflected.
|
|
129
125
|
*/
|
|
130
126
|
load() {
|
|
131
|
-
if (this.disposed
|
|
127
|
+
if (this.disposed) return Promise.resolve();
|
|
132
128
|
this.clearRetry();
|
|
133
129
|
if (this.inFlight !== void 0) {
|
|
134
130
|
this.rerun = true;
|
|
@@ -242,33 +238,29 @@ window.__ModuleLoader__.load({
|
|
|
242
238
|
store;
|
|
243
239
|
mirror;
|
|
244
240
|
namespace;
|
|
245
|
-
persistence;
|
|
246
241
|
tail = Promise.resolve();
|
|
247
242
|
writeGeneration = 0;
|
|
248
243
|
disposed = false;
|
|
249
244
|
unsubscribe;
|
|
250
245
|
/** Revision answered by a superseded write still ahead of the mirror. */
|
|
251
246
|
pendingRevision;
|
|
252
|
-
constructor(resolveRemote, mirror, namespace
|
|
247
|
+
constructor(resolveRemote, mirror, namespace) {
|
|
253
248
|
this.resolveRemote = resolveRemote;
|
|
254
249
|
this.mirror = mirror;
|
|
255
250
|
this.namespace = namespace;
|
|
256
|
-
this.persistence = persistence;
|
|
257
251
|
this.store = createSnapshotStore({
|
|
258
|
-
status:
|
|
252
|
+
status: "loading",
|
|
259
253
|
value: void 0,
|
|
260
254
|
base: void 0,
|
|
261
255
|
user: void 0,
|
|
262
256
|
revision: void 0,
|
|
263
257
|
writable: false,
|
|
264
|
-
mode:
|
|
258
|
+
mode: "host"
|
|
265
259
|
});
|
|
266
|
-
|
|
267
|
-
this.unsubscribe = mirror.subscribe(() => {
|
|
268
|
-
this.derive();
|
|
269
|
-
});
|
|
260
|
+
this.unsubscribe = mirror.subscribe(() => {
|
|
270
261
|
this.derive();
|
|
271
|
-
}
|
|
262
|
+
});
|
|
263
|
+
this.derive();
|
|
272
264
|
}
|
|
273
265
|
getSnapshot() {
|
|
274
266
|
return this.store.getSnapshot();
|
|
@@ -377,9 +369,9 @@ window.__ModuleLoader__.load({
|
|
|
377
369
|
if (sameSnapshot(candidate, current)) return;
|
|
378
370
|
this.store.set(candidate);
|
|
379
371
|
}
|
|
380
|
-
/** Queue operations one at a time;
|
|
372
|
+
/** Queue operations one at a time; disposal makes the queue inert. */
|
|
381
373
|
enqueue(operation) {
|
|
382
|
-
if (this.
|
|
374
|
+
if (this.disposed) return Promise.resolve();
|
|
383
375
|
const task = this.tail.then(async () => {
|
|
384
376
|
if (this.disposed) return;
|
|
385
377
|
await operation();
|
|
@@ -418,26 +410,22 @@ window.__ModuleLoader__.load({
|
|
|
418
410
|
* @returns The scope plus the refresh/dispose handles the entry wires into its fiber.
|
|
419
411
|
*/
|
|
420
412
|
function createSettingsScope(context, namespace, resolveRemote, timer = REAL_SETTINGS_TIMER) {
|
|
421
|
-
const
|
|
422
|
-
const
|
|
423
|
-
const mirror = new SettingsDescribeMirror(resolveRemote, persistence, timer);
|
|
424
|
-
const scope = new RemoteSettingsScope(resolveRemote, mirror, namespace, persistence);
|
|
413
|
+
const mirror = new SettingsDescribeMirror(resolveRemote, timer);
|
|
414
|
+
const scope = new RemoteSettingsScope(resolveRemote, mirror, namespace);
|
|
425
415
|
const disposers = [];
|
|
426
|
-
if (
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
if (typeof off === "function") disposers.push(off);
|
|
438
|
-
}
|
|
439
|
-
mirror.ensure();
|
|
416
|
+
if (typeof context.remote.$on === "function") {
|
|
417
|
+
const off = context.remote.$on("settings/document-updated", () => {
|
|
418
|
+
mirror.ensure();
|
|
419
|
+
});
|
|
420
|
+
if (typeof off === "function") disposers.push(off);
|
|
421
|
+
}
|
|
422
|
+
if (typeof context.on === "function") {
|
|
423
|
+
const off = context.on("connection/reset", () => {
|
|
424
|
+
mirror.load();
|
|
425
|
+
});
|
|
426
|
+
if (typeof off === "function") disposers.push(off);
|
|
440
427
|
}
|
|
428
|
+
mirror.ensure();
|
|
441
429
|
return {
|
|
442
430
|
getSnapshot: () => scope.getSnapshot(),
|
|
443
431
|
subscribe: (listener) => scope.subscribe(listener),
|
|
@@ -471,7 +459,10 @@ window.__ModuleLoader__.load({
|
|
|
471
459
|
* so the control only reports whether one is configured.
|
|
472
460
|
* - API base -> the `llm-commandcode` settings namespace (`apiBase`), same
|
|
473
461
|
* namespace the Models page card addresses.
|
|
474
|
-
* -
|
|
462
|
+
* - Timeouts, behavior switches, model visibility -> the same namespace,
|
|
463
|
+
* staged and written on save.
|
|
464
|
+
* - Accounts -> `accounts`, `activeAccount`, `modelAccountRules` and the
|
|
465
|
+
* per-account credentials, committed immediately.
|
|
475
466
|
*
|
|
476
467
|
* The controller mirrors the plugin-card pattern from the harness's own
|
|
477
468
|
* settings UI: it binds the `llm-commandcode` namespace through a
|
|
@@ -539,6 +530,24 @@ window.__ModuleLoader__.load({
|
|
|
539
530
|
}
|
|
540
531
|
};
|
|
541
532
|
}
|
|
533
|
+
/** A field limited to fixed string choices; an empty draft clears it. */
|
|
534
|
+
function choiceField(field, choices) {
|
|
535
|
+
return {
|
|
536
|
+
field,
|
|
537
|
+
format: (value) => typeof value === "string" && choices.includes(value) ? value : "",
|
|
538
|
+
parse: (text) => {
|
|
539
|
+
const trimmed = text.trim();
|
|
540
|
+
if (trimmed === "") return { kind: "clear" };
|
|
541
|
+
return choices.includes(trimmed) ? {
|
|
542
|
+
kind: "set",
|
|
543
|
+
value: trimmed
|
|
544
|
+
} : {
|
|
545
|
+
kind: "invalid",
|
|
546
|
+
reason: "format"
|
|
547
|
+
};
|
|
548
|
+
}
|
|
549
|
+
};
|
|
550
|
+
}
|
|
542
551
|
/**
|
|
543
552
|
* A boolean field, staged as the strings `'true'`/`'false'` (an empty draft
|
|
544
553
|
* clears it). The component renders a toggle and only ever stages these two
|
|
@@ -567,10 +576,17 @@ window.__ModuleLoader__.load({
|
|
|
567
576
|
};
|
|
568
577
|
}
|
|
569
578
|
const MAX_TIMEOUT_MS = 2147483647;
|
|
579
|
+
/** The command guard's auto-approve levels, strictest first. */
|
|
580
|
+
const COMMAND_GUARD_LEVEL_CHOICES = [
|
|
581
|
+
"high",
|
|
582
|
+
"medium",
|
|
583
|
+
"low"
|
|
584
|
+
];
|
|
585
|
+
/** The level an unset `commandGuardLevel` reads as on the Host. */
|
|
586
|
+
const COMMAND_GUARD_DEFAULT_LEVEL_CHOICE = "medium";
|
|
570
587
|
/** The fields this page edits inside the `llm-commandcode` namespace. */
|
|
571
588
|
const SECTION_FIELDS = [
|
|
572
589
|
textField("apiBase"),
|
|
573
|
-
textField("workingDir"),
|
|
574
590
|
numberField$1("requestTimeoutMs", {
|
|
575
591
|
min: 1,
|
|
576
592
|
max: MAX_TIMEOUT_MS
|
|
@@ -587,16 +603,8 @@ window.__ModuleLoader__.load({
|
|
|
587
603
|
booleanField$1("webSearch"),
|
|
588
604
|
booleanField$1("showSidebarQuota"),
|
|
589
605
|
booleanField$1("commandGuard"),
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
max: 1
|
|
593
|
-
}),
|
|
594
|
-
numberField$1("commandGuardTimeoutMs", {
|
|
595
|
-
min: 200,
|
|
596
|
-
max: 1e4
|
|
597
|
-
}),
|
|
598
|
-
booleanField$1("zdr"),
|
|
599
|
-
textField("activeAccount")
|
|
606
|
+
choiceField("commandGuardLevel", COMMAND_GUARD_LEVEL_CHOICES),
|
|
607
|
+
booleanField$1("zdr")
|
|
600
608
|
];
|
|
601
609
|
/** Whether two model-id lists are equal as sets (order-insensitive). */
|
|
602
610
|
function sameModels(a, b) {
|
|
@@ -605,17 +613,47 @@ window.__ModuleLoader__.load({
|
|
|
605
613
|
return b.every((id) => set.has(id));
|
|
606
614
|
}
|
|
607
615
|
/**
|
|
608
|
-
*
|
|
609
|
-
*
|
|
610
|
-
*
|
|
616
|
+
* Fold routing rules into one model list per account with the runtime's
|
|
617
|
+
* first-match-wins order (`matchModelRule()` in src/accounts.ts): a model
|
|
618
|
+
* claimed by an earlier rule is ignored by every later one. Account order is
|
|
619
|
+
* first appearance, which keeps a rewrite's rule order stable.
|
|
611
620
|
*/
|
|
612
|
-
function
|
|
613
|
-
|
|
621
|
+
function accountModelMap(rules) {
|
|
622
|
+
const claimed = /* @__PURE__ */ new Set();
|
|
623
|
+
const map = /* @__PURE__ */ new Map();
|
|
624
|
+
for (const rule of rules) {
|
|
625
|
+
const list = map.get(rule.account) ?? [];
|
|
626
|
+
for (const model of rule.models) {
|
|
627
|
+
if (claimed.has(model)) continue;
|
|
628
|
+
claimed.add(model);
|
|
629
|
+
list.push(model);
|
|
630
|
+
}
|
|
631
|
+
map.set(rule.account, list);
|
|
632
|
+
}
|
|
633
|
+
return map;
|
|
634
|
+
}
|
|
635
|
+
/** Serialize a per-account model map back into `modelAccountRules`. */
|
|
636
|
+
function rulesFromMap(map) {
|
|
637
|
+
const out = [];
|
|
638
|
+
for (const [account, models] of map) if (models.length > 0) out.push({
|
|
639
|
+
models: [...models],
|
|
640
|
+
account
|
|
641
|
+
});
|
|
642
|
+
return out;
|
|
614
643
|
}
|
|
615
644
|
/**
|
|
616
645
|
* Controller bridging the `llm-commandcode` scope and the credentials domain
|
|
617
|
-
* onto the page.
|
|
618
|
-
*
|
|
646
|
+
* onto the page.
|
|
647
|
+
*
|
|
648
|
+
* Two write paths with different contracts:
|
|
649
|
+
* - The staged form (connection, behavior and model-visibility fields, plus
|
|
650
|
+
* the Models-page card's default key draft) lands on `save()`.
|
|
651
|
+
* - Account management (create, rename, remove, key replacement, pinning and
|
|
652
|
+
* per-account models) commits IMMEDIATELY and serially. A new account had to
|
|
653
|
+
* be saved before browser sign-in could target it (the Host refuses a login
|
|
654
|
+
* for a reference the stored `accounts` list does not name), which turned
|
|
655
|
+
* "add an account" into edit → save → sign in; committing each operation
|
|
656
|
+
* removes that dance and keeps unrelated staged edits out of it.
|
|
619
657
|
*/
|
|
620
658
|
var CommandCodeSettingsController = class {
|
|
621
659
|
scope;
|
|
@@ -629,34 +667,6 @@ window.__ModuleLoader__.load({
|
|
|
629
667
|
credentialRef = DEFAULT_API_KEY_REF;
|
|
630
668
|
/** Host-reported configured/writable state per credential reference. */
|
|
631
669
|
credentialStates = /* @__PURE__ */ new Map();
|
|
632
|
-
/** Staged account additions (not yet saved). */
|
|
633
|
-
addedAccounts = [];
|
|
634
|
-
/** Staged removals of stored extra accounts, by credential reference. */
|
|
635
|
-
removedRefs = /* @__PURE__ */ new Set();
|
|
636
|
-
/** Staged label drafts, by credential reference. */
|
|
637
|
-
labelDrafts = /* @__PURE__ */ new Map();
|
|
638
|
-
/** Staged key drafts, by credential reference (blank = keep stored key). */
|
|
639
|
-
keyDrafts = /* @__PURE__ */ new Map();
|
|
640
|
-
/** Credential references staged for removal on the next save. */
|
|
641
|
-
keyClears = /* @__PURE__ */ new Set();
|
|
642
|
-
/** Staged model→account routing rules (not yet saved). */
|
|
643
|
-
addedRules = [];
|
|
644
|
-
/** Staged edits to stored routing rules, by stored row id. */
|
|
645
|
-
ruleDrafts = /* @__PURE__ */ new Map();
|
|
646
|
-
/**
|
|
647
|
-
* Stored routing rule rows staged for removal, by stored row id with a
|
|
648
|
-
* content snapshot. The snapshot makes reconcile content-based: stored
|
|
649
|
-
* row ids are positional (`rule-N`) and shift after any write, so an
|
|
650
|
-
* id-only check would misread a landed removal as pending (and a retry
|
|
651
|
-
* would delete the wrong row).
|
|
652
|
-
*/
|
|
653
|
-
removedRuleIds = /* @__PURE__ */ new Map();
|
|
654
|
-
/**
|
|
655
|
-
* Fingerprint of the stored routing rules when the last `save()` started;
|
|
656
|
-
* `undefined` outside a save. Lets reconcile distinguish a landed rules
|
|
657
|
-
* write from a save that failed before reaching it.
|
|
658
|
-
*/
|
|
659
|
-
rulesBeforeSave = void 0;
|
|
660
670
|
/** Staged visible-model allowlist (undefined = no draft). */
|
|
661
671
|
visibleModelsDraft = void 0;
|
|
662
672
|
/** The catalog the model editors offer (Host-side). */
|
|
@@ -665,6 +675,10 @@ window.__ModuleLoader__.load({
|
|
|
665
675
|
saving = false;
|
|
666
676
|
failed = false;
|
|
667
677
|
savedCount = 0;
|
|
678
|
+
/** Tail of the serial account-operation queue. */
|
|
679
|
+
accountQueue = Promise.resolve();
|
|
680
|
+
accountPending = 0;
|
|
681
|
+
accountFailed = void 0;
|
|
668
682
|
/**
|
|
669
683
|
* @param scope - bound scope for the `llm-commandcode` namespace.
|
|
670
684
|
* @param api - credentials wire face.
|
|
@@ -691,9 +705,9 @@ window.__ModuleLoader__.load({
|
|
|
691
705
|
}
|
|
692
706
|
/**
|
|
693
707
|
* The credential reference the section names, or the provider default. A
|
|
694
|
-
* user who renamed `apiKeyEnv` in
|
|
695
|
-
*
|
|
696
|
-
*
|
|
708
|
+
* user who renamed `apiKeyEnv` in the profile Config gets a page that
|
|
709
|
+
* addresses the renamed ref instead of silently writing the default —
|
|
710
|
+
* mirroring the Models page's `refFor()`.
|
|
697
711
|
*/
|
|
698
712
|
recomputeCredentialRef() {
|
|
699
713
|
const snapshot = this.scope.getSnapshot();
|
|
@@ -713,6 +727,7 @@ window.__ModuleLoader__.load({
|
|
|
713
727
|
const plan = this.plan();
|
|
714
728
|
const credential = this.credentialStates.get(this.credentialRef);
|
|
715
729
|
const accounts = this.effectiveAccounts();
|
|
730
|
+
const active = this.sectionValue("activeAccount");
|
|
716
731
|
return {
|
|
717
732
|
available: snapshot.status === "ready",
|
|
718
733
|
writable: snapshot.writable,
|
|
@@ -726,9 +741,7 @@ window.__ModuleLoader__.load({
|
|
|
726
741
|
invalid: false,
|
|
727
742
|
invalidReason: void 0
|
|
728
743
|
},
|
|
729
|
-
apiKeyClearStaged: this.keyClears.has(this.credentialRef),
|
|
730
744
|
apiBase: this.field("apiBase"),
|
|
731
|
-
workingDir: this.field("workingDir"),
|
|
732
745
|
requestTimeoutMs: this.field("requestTimeoutMs"),
|
|
733
746
|
streamIdleTimeoutMs: this.field("streamIdleTimeoutMs"),
|
|
734
747
|
transportMaxRetries: this.field("transportMaxRetries"),
|
|
@@ -736,164 +749,31 @@ window.__ModuleLoader__.load({
|
|
|
736
749
|
webSearch: this.field("webSearch"),
|
|
737
750
|
showSidebarQuota: this.field("showSidebarQuota"),
|
|
738
751
|
commandGuard: this.field("commandGuard"),
|
|
739
|
-
|
|
740
|
-
commandGuardTimeoutMs: this.field("commandGuardTimeoutMs"),
|
|
752
|
+
commandGuardLevel: this.field("commandGuardLevel"),
|
|
741
753
|
zdr: this.field("zdr"),
|
|
742
754
|
sidebarQuota: this.sectionValue("showSidebarQuota") === true,
|
|
743
|
-
activeAccount:
|
|
755
|
+
activeAccount: typeof active === "string" ? active : "",
|
|
744
756
|
accounts,
|
|
745
|
-
|
|
746
|
-
|
|
757
|
+
accountModels: Object.fromEntries(accountModelMap(this.storedRules())),
|
|
758
|
+
accountBusy: this.accountPending > 0,
|
|
759
|
+
accountFailed: this.accountFailed,
|
|
747
760
|
visibleModels: this.effectiveVisibleModels(),
|
|
748
761
|
catalogModels: this.catalogModels,
|
|
749
762
|
catalogFailed: this.catalogFailed,
|
|
750
|
-
dirty: plan.length > 0 || this.
|
|
763
|
+
dirty: plan.length > 0 || this.visibleModelsDirty(),
|
|
751
764
|
invalid: plan.some((item) => item.run === void 0),
|
|
752
765
|
saving: this.saving,
|
|
753
766
|
failed: this.failed,
|
|
754
767
|
savedCount: this.savedCount
|
|
755
768
|
};
|
|
756
769
|
}
|
|
757
|
-
/** Stage a new extra account (saved on the next `save()`). */
|
|
758
|
-
addAccount() {
|
|
759
|
-
const used = /* @__PURE__ */ new Set([
|
|
760
|
-
this.credentialRef,
|
|
761
|
-
...this.storedExtras().map((extra) => extra.ref),
|
|
762
|
-
...this.addedAccounts.map((extra) => extra.ref)
|
|
763
|
-
]);
|
|
764
|
-
let n = 2;
|
|
765
|
-
while (used.has(`${this.credentialRef}_${n}`)) n += 1;
|
|
766
|
-
const index = this.storedExtras().length + this.addedAccounts.length + 2;
|
|
767
|
-
this.addedAccounts.push({
|
|
768
|
-
label: `Account ${index}`,
|
|
769
|
-
ref: `${this.credentialRef}_${n}`
|
|
770
|
-
});
|
|
771
|
-
this.failed = false;
|
|
772
|
-
this.describeAll();
|
|
773
|
-
this.publish();
|
|
774
|
-
}
|
|
775
|
-
/** Stage one extra account's removal (or drop an unsaved addition). */
|
|
776
|
-
removeAccount(id) {
|
|
777
|
-
const addedIndex = this.addedAccounts.findIndex((extra) => extra.ref === id);
|
|
778
|
-
if (addedIndex >= 0) this.addedAccounts.splice(addedIndex, 1);
|
|
779
|
-
else this.removedRefs.add(id);
|
|
780
|
-
this.labelDrafts.delete(id);
|
|
781
|
-
this.keyDrafts.delete(id);
|
|
782
|
-
const stagedActive = this.staged.get("activeAccount");
|
|
783
|
-
if ((stagedActive !== void 0 ? stagedActive.clear ? "" : stagedActive.text : typeof this.sectionValue("activeAccount") === "string" ? this.sectionValue("activeAccount") : "") === id) this.staged.set("activeAccount", {
|
|
784
|
-
text: "",
|
|
785
|
-
clear: true
|
|
786
|
-
});
|
|
787
|
-
this.failed = false;
|
|
788
|
-
this.publish();
|
|
789
|
-
}
|
|
790
|
-
/** Stage one extra account's label draft. */
|
|
791
|
-
editAccountLabel(id, text) {
|
|
792
|
-
this.labelDrafts.set(id, text);
|
|
793
|
-
this.failed = false;
|
|
794
|
-
this.publish();
|
|
795
|
-
}
|
|
796
|
-
/** Stage one extra account's key draft (blank keeps the stored key). */
|
|
797
|
-
editAccountKey(id, text) {
|
|
798
|
-
this.keyDrafts.set(id, text);
|
|
799
|
-
this.keyClears.delete(id);
|
|
800
|
-
this.failed = false;
|
|
801
|
-
this.publish();
|
|
802
|
-
}
|
|
803
|
-
/**
|
|
804
|
-
* Toggle the staged removal of one account's stored key: the next save
|
|
805
|
-
* unsets the credential so the account reports unconfigured and falls back
|
|
806
|
-
* to its other key sources. Only meaningful while a key is actually
|
|
807
|
-
* stored. `target` is `'default'` (the implicit first account) or an extra
|
|
808
|
-
* account's credential reference.
|
|
809
|
-
*/
|
|
810
|
-
toggleKeyClear(target) {
|
|
811
|
-
const ref = target === "default" ? this.credentialRef : target;
|
|
812
|
-
if (this.keyClears.has(ref)) this.keyClears.delete(ref);
|
|
813
|
-
else {
|
|
814
|
-
if (this.credentialStates.get(ref)?.configured !== true) return;
|
|
815
|
-
this.keyDrafts.delete(ref);
|
|
816
|
-
if (ref === this.credentialRef) this.staged.delete("apiKey");
|
|
817
|
-
this.keyClears.add(ref);
|
|
818
|
-
}
|
|
819
|
-
this.failed = false;
|
|
820
|
-
this.describeAll();
|
|
821
|
-
this.publish();
|
|
822
|
-
}
|
|
823
|
-
/** Stage a new model → account routing rule (saved on the next `save()`). */
|
|
824
|
-
addRule() {
|
|
825
|
-
this.addedRules.push({
|
|
826
|
-
models: [],
|
|
827
|
-
account: "default"
|
|
828
|
-
});
|
|
829
|
-
this.failed = false;
|
|
830
|
-
this.publish();
|
|
831
|
-
}
|
|
832
|
-
/** Stage one routing rule's removal (or drop an unsaved addition). */
|
|
833
|
-
removeRule(id) {
|
|
834
|
-
const addedIndex = this.addedRules.findIndex((_, index) => `new-${index}` === id);
|
|
835
|
-
if (addedIndex >= 0) this.addedRules.splice(addedIndex, 1);
|
|
836
|
-
else {
|
|
837
|
-
const stored = this.storedRules().find((rule) => rule.id === id);
|
|
838
|
-
this.removedRuleIds.set(id, stored === void 0 ? {
|
|
839
|
-
models: [],
|
|
840
|
-
account: ""
|
|
841
|
-
} : {
|
|
842
|
-
models: [...stored.models],
|
|
843
|
-
account: stored.account
|
|
844
|
-
});
|
|
845
|
-
}
|
|
846
|
-
this.ruleDrafts.delete(id);
|
|
847
|
-
this.failed = false;
|
|
848
|
-
this.publish();
|
|
849
|
-
}
|
|
850
|
-
/** Stage one routing rule's selected model ids (multi-select). */
|
|
851
|
-
editRuleModels(id, models) {
|
|
852
|
-
const addedIndex = this.addedRules.findIndex((_, index) => `new-${index}` === id);
|
|
853
|
-
if (addedIndex >= 0) this.addedRules[addedIndex] = {
|
|
854
|
-
...this.addedRules[addedIndex],
|
|
855
|
-
models
|
|
856
|
-
};
|
|
857
|
-
else {
|
|
858
|
-
const current = this.ruleDrafts.get(id) ?? this.storedRules().find((rule) => rule.id === id) ?? {
|
|
859
|
-
models: [],
|
|
860
|
-
account: "default"
|
|
861
|
-
};
|
|
862
|
-
this.ruleDrafts.set(id, {
|
|
863
|
-
...current,
|
|
864
|
-
models
|
|
865
|
-
});
|
|
866
|
-
}
|
|
867
|
-
this.failed = false;
|
|
868
|
-
this.publish();
|
|
869
|
-
}
|
|
870
|
-
/** Stage one routing rule's target account draft. */
|
|
871
|
-
editRuleAccount(id, text) {
|
|
872
|
-
const addedIndex = this.addedRules.findIndex((_, index) => `new-${index}` === id);
|
|
873
|
-
if (addedIndex >= 0) this.addedRules[addedIndex] = {
|
|
874
|
-
...this.addedRules[addedIndex],
|
|
875
|
-
account: text
|
|
876
|
-
};
|
|
877
|
-
else {
|
|
878
|
-
const current = this.ruleDrafts.get(id) ?? this.storedRules().find((rule) => rule.id === id) ?? {
|
|
879
|
-
models: [],
|
|
880
|
-
account: "default"
|
|
881
|
-
};
|
|
882
|
-
this.ruleDrafts.set(id, {
|
|
883
|
-
...current,
|
|
884
|
-
account: text
|
|
885
|
-
});
|
|
886
|
-
}
|
|
887
|
-
this.failed = false;
|
|
888
|
-
this.publish();
|
|
889
|
-
}
|
|
890
770
|
/** Stage one field's draft text. */
|
|
891
771
|
edit(field, text) {
|
|
772
|
+
if (field !== "apiKey") this.spec(field);
|
|
892
773
|
this.staged.set(field, {
|
|
893
774
|
text,
|
|
894
775
|
clear: false
|
|
895
776
|
});
|
|
896
|
-
if (field === "apiKey") this.keyClears.delete(this.credentialRef);
|
|
897
777
|
this.failed = false;
|
|
898
778
|
this.publish();
|
|
899
779
|
}
|
|
@@ -915,11 +795,9 @@ window.__ModuleLoader__.load({
|
|
|
915
795
|
}
|
|
916
796
|
/** Discard every staged edit. */
|
|
917
797
|
discard() {
|
|
918
|
-
if (this.staged.size === 0 &&
|
|
798
|
+
if (this.staged.size === 0 && this.visibleModelsDraft === void 0 && !this.failed) return;
|
|
919
799
|
this.staged.clear();
|
|
920
|
-
this.
|
|
921
|
-
this.clearRuleStaging();
|
|
922
|
-
this.clearVisibleModelsStaging();
|
|
800
|
+
this.visibleModelsDraft = void 0;
|
|
923
801
|
this.failed = false;
|
|
924
802
|
this.publish();
|
|
925
803
|
}
|
|
@@ -934,10 +812,8 @@ window.__ModuleLoader__.load({
|
|
|
934
812
|
/** Write every staged edit, then re-read the Host's accepted state. */
|
|
935
813
|
async save() {
|
|
936
814
|
const plan = this.plan();
|
|
937
|
-
const accountRuns = this.accountPlan();
|
|
938
|
-
const ruleRuns = this.rulesPlan();
|
|
939
815
|
const visibleRuns = this.visibleModelsPlan();
|
|
940
|
-
if (plan.length === 0 &&
|
|
816
|
+
if (plan.length === 0 && visibleRuns.length === 0 || this.saving) return;
|
|
941
817
|
const runs = [];
|
|
942
818
|
for (const item of plan) {
|
|
943
819
|
if (item.run === void 0) return;
|
|
@@ -945,15 +821,9 @@ window.__ModuleLoader__.load({
|
|
|
945
821
|
}
|
|
946
822
|
this.saving = true;
|
|
947
823
|
this.failed = false;
|
|
948
|
-
this.rulesBeforeSave = ruleFingerprint(this.storedRules());
|
|
949
824
|
this.publish();
|
|
950
825
|
let landed = true;
|
|
951
|
-
for (const run of [
|
|
952
|
-
...runs,
|
|
953
|
-
...accountRuns,
|
|
954
|
-
...ruleRuns,
|
|
955
|
-
...visibleRuns
|
|
956
|
-
]) {
|
|
826
|
+
for (const run of [...runs, ...visibleRuns]) {
|
|
957
827
|
let ok = false;
|
|
958
828
|
try {
|
|
959
829
|
ok = await run();
|
|
@@ -970,32 +840,148 @@ window.__ModuleLoader__.load({
|
|
|
970
840
|
if (landed) {
|
|
971
841
|
this.savedCount += 1;
|
|
972
842
|
this.staged.clear();
|
|
973
|
-
this.
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
843
|
+
this.visibleModelsDraft = void 0;
|
|
844
|
+
} else this.reconcileStaging();
|
|
845
|
+
this.publish();
|
|
846
|
+
}
|
|
847
|
+
/** Stage the visible-model allowlist (multi-select). */
|
|
848
|
+
editVisibleModels(models) {
|
|
849
|
+
this.visibleModelsDraft = [...models];
|
|
850
|
+
this.failed = false;
|
|
851
|
+
this.publish();
|
|
852
|
+
}
|
|
853
|
+
/** Stage "show all models" (clears the allowlist). */
|
|
854
|
+
clearVisibleModels() {
|
|
855
|
+
this.visibleModelsDraft = [];
|
|
856
|
+
this.failed = false;
|
|
981
857
|
this.publish();
|
|
982
858
|
}
|
|
983
859
|
/**
|
|
984
|
-
*
|
|
985
|
-
*
|
|
986
|
-
*
|
|
987
|
-
*
|
|
988
|
-
* the accounts write that failed.
|
|
860
|
+
* Create one extra account now and return its credential reference, or
|
|
861
|
+
* undefined when the account could not be stored. With `key` the key lands
|
|
862
|
+
* first, so a stored row never names a reference whose key write failed;
|
|
863
|
+
* without it the row is stored keyless so browser sign-in can target it.
|
|
989
864
|
*/
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
this.
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
865
|
+
async createAccount(input) {
|
|
866
|
+
let created;
|
|
867
|
+
await this.runAccountOp("create", async () => {
|
|
868
|
+
const ref = this.nextAccountRef();
|
|
869
|
+
const key = input.key?.trim() ?? "";
|
|
870
|
+
if (key !== "" && !await this.writeKeyTo(ref, key)) return false;
|
|
871
|
+
const label = input.label.trim() === "" ? ref : input.label.trim();
|
|
872
|
+
let ok = false;
|
|
873
|
+
try {
|
|
874
|
+
ok = await this.writeAccountList([...this.rawStoredAccounts(), {
|
|
875
|
+
label,
|
|
876
|
+
apiKeyEnv: ref
|
|
877
|
+
}]);
|
|
878
|
+
} catch {
|
|
879
|
+
ok = false;
|
|
880
|
+
}
|
|
881
|
+
if (!ok) {
|
|
882
|
+
if (key !== "") await this.unsetKey(ref);
|
|
883
|
+
return false;
|
|
884
|
+
}
|
|
885
|
+
created = ref;
|
|
886
|
+
return true;
|
|
887
|
+
});
|
|
888
|
+
return created;
|
|
889
|
+
}
|
|
890
|
+
/** Rename one stored extra account now. */
|
|
891
|
+
renameAccount(ref, label) {
|
|
892
|
+
const next = label.trim();
|
|
893
|
+
return this.runAccountOp("rename", async () => {
|
|
894
|
+
if (next === "") return false;
|
|
895
|
+
const list = this.rawStoredAccounts().map((entry) => entry.apiKeyEnv === ref ? {
|
|
896
|
+
...entry,
|
|
897
|
+
label: next
|
|
898
|
+
} : { ...entry });
|
|
899
|
+
return this.writeAccountList(list);
|
|
900
|
+
});
|
|
901
|
+
}
|
|
902
|
+
/**
|
|
903
|
+
* Remove one stored extra account now: its stored key, its dedicated
|
|
904
|
+
* models and a pin naming it go with it, so nothing orphaned remains.
|
|
905
|
+
*/
|
|
906
|
+
removeAccount(ref) {
|
|
907
|
+
return this.runAccountOp("remove", async () => {
|
|
908
|
+
if (this.credentialStates.get(ref)?.configured === true && !await this.unsetKey(ref)) return false;
|
|
909
|
+
const list = this.rawStoredAccounts().filter((entry) => entry.apiKeyEnv !== ref).map((entry) => ({ ...entry }));
|
|
910
|
+
if (!await this.writeAccountList(list)) return false;
|
|
911
|
+
const map = accountModelMap(this.storedRules());
|
|
912
|
+
if (map.has(ref)) {
|
|
913
|
+
map.delete(ref);
|
|
914
|
+
if (!await this.writeRules(rulesFromMap(map))) return false;
|
|
915
|
+
}
|
|
916
|
+
if (this.sectionValue("activeAccount") === ref) return this.clear("activeAccount");
|
|
917
|
+
return true;
|
|
918
|
+
});
|
|
919
|
+
}
|
|
920
|
+
/** Store a replacement key now; `target` is `'default'` or an extra account's reference. */
|
|
921
|
+
setAccountKey(target, key) {
|
|
922
|
+
const value = key.trim();
|
|
923
|
+
return this.runAccountOp("key", async () => {
|
|
924
|
+
if (value === "") return false;
|
|
925
|
+
return this.writeKeyTo(this.refFor(target), value);
|
|
926
|
+
});
|
|
927
|
+
}
|
|
928
|
+
/** Remove a stored key now; `target` is `'default'` or an extra account's reference. */
|
|
929
|
+
clearAccountKey(target) {
|
|
930
|
+
return this.runAccountOp("key", () => this.unsetKey(this.refFor(target)));
|
|
931
|
+
}
|
|
932
|
+
/** Pin the serving account now (`''` returns to automatic rotation). */
|
|
933
|
+
setActiveAccount(id) {
|
|
934
|
+
return this.runAccountOp("active", () => id === "" ? this.clear("activeAccount") : this.store("activeAccount", id));
|
|
935
|
+
}
|
|
936
|
+
/**
|
|
937
|
+
* Replace one account's dedicated models now. A model belongs to one
|
|
938
|
+
* account at a time, so each selected model is moved out of any other
|
|
939
|
+
* account's list — the stored rules then carry no shadowed entries.
|
|
940
|
+
*/
|
|
941
|
+
setAccountModels(target, models) {
|
|
942
|
+
return this.runAccountOp("models", async () => {
|
|
943
|
+
const chosen = [...new Set(models.filter((id) => id !== ""))];
|
|
944
|
+
const taken = new Set(chosen);
|
|
945
|
+
const map = accountModelMap(this.storedRules());
|
|
946
|
+
for (const [account, list] of map) if (account !== target) map.set(account, list.filter((id) => !taken.has(id)));
|
|
947
|
+
map.set(target, chosen);
|
|
948
|
+
return this.writeRules(rulesFromMap(map));
|
|
949
|
+
});
|
|
950
|
+
}
|
|
951
|
+
/** Run one account operation after every earlier one, tracking busy/failure. */
|
|
952
|
+
runAccountOp(op, run) {
|
|
953
|
+
this.accountPending += 1;
|
|
954
|
+
this.accountFailed = void 0;
|
|
955
|
+
this.publish();
|
|
956
|
+
const result = this.accountQueue.then(async () => {
|
|
957
|
+
if (!this.scope.getSnapshot().writable) return false;
|
|
958
|
+
try {
|
|
959
|
+
return await run();
|
|
960
|
+
} catch {
|
|
961
|
+
return false;
|
|
962
|
+
}
|
|
963
|
+
});
|
|
964
|
+
this.accountQueue = result;
|
|
965
|
+
return result.then((ok) => {
|
|
966
|
+
this.accountPending -= 1;
|
|
967
|
+
if (!ok) this.accountFailed = op;
|
|
968
|
+
this.publish();
|
|
969
|
+
return ok;
|
|
970
|
+
});
|
|
971
|
+
}
|
|
972
|
+
refFor(target) {
|
|
973
|
+
return target === "default" ? this.credentialRef : target;
|
|
974
|
+
}
|
|
975
|
+
/**
|
|
976
|
+
* The first free `<credentialRef>_<n>` reference. Derived from the default
|
|
977
|
+
* reference's name, so a renamed `apiKeyEnv` yields `MY_KEY_2`-style refs
|
|
978
|
+
* consistent with the default slot.
|
|
979
|
+
*/
|
|
980
|
+
nextAccountRef() {
|
|
981
|
+
const used = /* @__PURE__ */ new Set([this.credentialRef, ...this.storedExtras().map((extra) => extra.ref)]);
|
|
982
|
+
let n = 2;
|
|
983
|
+
while (used.has(`${this.credentialRef}_${n}`)) n += 1;
|
|
984
|
+
return `${this.credentialRef}_${n}`;
|
|
999
985
|
}
|
|
1000
986
|
spec(field) {
|
|
1001
987
|
const spec = this.specs.get(field);
|
|
@@ -1048,7 +1034,7 @@ window.__ModuleLoader__.load({
|
|
|
1048
1034
|
const value = staged.text.trim();
|
|
1049
1035
|
if (value !== "") plan.push({
|
|
1050
1036
|
field,
|
|
1051
|
-
run: () => this.
|
|
1037
|
+
run: () => this.writeKeyTo(this.credentialRef, value)
|
|
1052
1038
|
});
|
|
1053
1039
|
continue;
|
|
1054
1040
|
}
|
|
@@ -1077,7 +1063,16 @@ window.__ModuleLoader__.load({
|
|
|
1077
1063
|
}
|
|
1078
1064
|
return plan;
|
|
1079
1065
|
}
|
|
1066
|
+
/** Drop staged drafts a partially landed save already stored. */
|
|
1067
|
+
reconcileStaging() {
|
|
1068
|
+
for (const [field, staged] of [...this.staged]) {
|
|
1069
|
+
if (field === "apiKey" || staged.clear) continue;
|
|
1070
|
+
if (staged.text === this.spec(field).format(this.sectionValue(field))) this.staged.delete(field);
|
|
1071
|
+
}
|
|
1072
|
+
if (this.visibleModelsDraft !== void 0 && sameModels(this.visibleModelsDraft, this.storedVisibleModels())) this.visibleModelsDraft = void 0;
|
|
1073
|
+
}
|
|
1080
1074
|
async clear(field) {
|
|
1075
|
+
if (!this.stored(field)) return true;
|
|
1081
1076
|
await this.scope.unset(field);
|
|
1082
1077
|
return !this.stored(field);
|
|
1083
1078
|
}
|
|
@@ -1085,10 +1080,6 @@ window.__ModuleLoader__.load({
|
|
|
1085
1080
|
await this.scope.set(field, value);
|
|
1086
1081
|
return this.userLayer()?.[field] === value;
|
|
1087
1082
|
}
|
|
1088
|
-
/** Write the staged default key, then re-read whether the Host holds it. */
|
|
1089
|
-
async writeKey(value) {
|
|
1090
|
-
return this.writeKeyTo(this.credentialRef, value);
|
|
1091
|
-
}
|
|
1092
1083
|
/** Write one account's key, then re-read the Host's credential states. */
|
|
1093
1084
|
async writeKeyTo(ref, value) {
|
|
1094
1085
|
try {
|
|
@@ -1096,16 +1087,29 @@ window.__ModuleLoader__.load({
|
|
|
1096
1087
|
} catch {
|
|
1097
1088
|
return false;
|
|
1098
1089
|
}
|
|
1099
|
-
await this.describeAll();
|
|
1090
|
+
await this.describeAll([ref]);
|
|
1100
1091
|
return this.credentialStates.get(ref)?.configured ?? false;
|
|
1101
1092
|
}
|
|
1102
|
-
/**
|
|
1103
|
-
async
|
|
1104
|
-
|
|
1093
|
+
/** Unset one stored credential, then re-read the Host's credential states. */
|
|
1094
|
+
async unsetKey(ref) {
|
|
1095
|
+
try {
|
|
1096
|
+
if (!(await this.api.credentials.unset(ref)).ok) return false;
|
|
1097
|
+
} catch {
|
|
1098
|
+
return false;
|
|
1099
|
+
}
|
|
1100
|
+
await this.describeAll([ref]);
|
|
1101
|
+
return this.credentialStates.get(ref)?.configured !== true;
|
|
1102
|
+
}
|
|
1103
|
+
/**
|
|
1104
|
+
* Ask the credentials domain about every reference this page writes, plus
|
|
1105
|
+
* `extra` — a key written for an account whose row is not stored yet.
|
|
1106
|
+
*/
|
|
1107
|
+
async describeAll(extra = []) {
|
|
1108
|
+
const refs = [.../* @__PURE__ */ new Set([
|
|
1105
1109
|
this.credentialRef,
|
|
1106
|
-
...this.storedExtras().map((
|
|
1107
|
-
...
|
|
1108
|
-
];
|
|
1110
|
+
...this.storedExtras().map((account) => account.ref),
|
|
1111
|
+
...extra
|
|
1112
|
+
])];
|
|
1109
1113
|
let response;
|
|
1110
1114
|
try {
|
|
1111
1115
|
response = await this.api.credentials.describe(refs);
|
|
@@ -1168,20 +1172,19 @@ window.__ModuleLoader__.load({
|
|
|
1168
1172
|
return raw.filter((entry) => typeof entry === "object" && entry !== null && !Array.isArray(entry));
|
|
1169
1173
|
}
|
|
1170
1174
|
/**
|
|
1171
|
-
* The stored extra accounts
|
|
1172
|
-
*
|
|
1173
|
-
*
|
|
1174
|
-
*
|
|
1175
|
-
*
|
|
1176
|
-
* {@link rawStoredAccounts} and only rewrites the reference-carrying entries
|
|
1177
|
-
* it manages (see the note there), so a literal-key entry stays in the
|
|
1178
|
-
* document and simply has no row.
|
|
1175
|
+
* The stored extra accounts this page can address: the rows carrying a
|
|
1176
|
+
* credential reference. Entries it cannot name (a composition entry with a
|
|
1177
|
+
* literal `apiKey`, or an unknown shape) are never dropped — every list
|
|
1178
|
+
* write starts from {@link rawStoredAccounts} and copies them verbatim,
|
|
1179
|
+
* because the settings layer replaces the whole array.
|
|
1179
1180
|
*/
|
|
1180
1181
|
storedExtras() {
|
|
1181
1182
|
const out = [];
|
|
1183
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1182
1184
|
for (const record of this.rawStoredAccounts()) {
|
|
1183
1185
|
const ref = record.apiKeyEnv;
|
|
1184
|
-
if (typeof ref !== "string" || ref === "") continue;
|
|
1186
|
+
if (typeof ref !== "string" || ref === "" || seen.has(ref)) continue;
|
|
1187
|
+
seen.add(ref);
|
|
1185
1188
|
const label = record.label;
|
|
1186
1189
|
out.push({
|
|
1187
1190
|
label: typeof label === "string" && label !== "" ? label : ref,
|
|
@@ -1190,197 +1193,43 @@ window.__ModuleLoader__.load({
|
|
|
1190
1193
|
}
|
|
1191
1194
|
return out;
|
|
1192
1195
|
}
|
|
1193
|
-
/** Every extra account row: stored (minus staged removals) + staged adds. */
|
|
1194
1196
|
effectiveAccounts() {
|
|
1195
|
-
|
|
1196
|
-
...extra,
|
|
1197
|
-
added: false
|
|
1198
|
-
}));
|
|
1199
|
-
const added = this.addedAccounts.map((extra) => ({
|
|
1200
|
-
...extra,
|
|
1201
|
-
added: true
|
|
1202
|
-
}));
|
|
1203
|
-
return [...stored, ...added].map((extra) => ({
|
|
1197
|
+
return this.storedExtras().map((extra) => ({
|
|
1204
1198
|
id: extra.ref,
|
|
1205
1199
|
ref: extra.ref,
|
|
1206
|
-
label:
|
|
1207
|
-
keyText: this.keyDrafts.get(extra.ref) ?? "",
|
|
1200
|
+
label: extra.label,
|
|
1208
1201
|
configured: this.credentialStates.get(extra.ref)?.configured ?? false,
|
|
1209
|
-
writable: this.credentialStates.get(extra.ref)?.writable ?? true
|
|
1210
|
-
added: extra.added,
|
|
1211
|
-
clearStaged: this.keyClears.has(extra.ref)
|
|
1202
|
+
writable: this.credentialStates.get(extra.ref)?.writable ?? true
|
|
1212
1203
|
}));
|
|
1213
1204
|
}
|
|
1214
|
-
/**
|
|
1215
|
-
|
|
1216
|
-
return this.addedAccounts.length > 0 || this.removedRefs.size > 0 || this.labelDrafts.size > 0 || this.keyDrafts.size > 0 || this.keyClears.size > 0;
|
|
1217
|
-
}
|
|
1218
|
-
/** Whether the staged account edits differ from the stored section. */
|
|
1219
|
-
accountsDirty() {
|
|
1220
|
-
if (this.addedAccounts.length > 0 || this.removedRefs.size > 0) return true;
|
|
1221
|
-
for (const [ref, text] of this.labelDrafts) {
|
|
1222
|
-
const base = this.storedExtras().find((extra) => extra.ref === ref)?.label;
|
|
1223
|
-
if (base !== void 0 && text.trim() !== "" && text !== base) return true;
|
|
1224
|
-
}
|
|
1225
|
-
for (const text of this.keyDrafts.values()) if (text.trim() !== "") return true;
|
|
1226
|
-
for (const ref of this.keyClears) if (this.credentialStates.get(ref)?.configured === true) return true;
|
|
1227
|
-
return false;
|
|
1228
|
-
}
|
|
1229
|
-
/** Reset every account-level staged edit. */
|
|
1230
|
-
clearAccountStaging() {
|
|
1231
|
-
this.addedAccounts = [];
|
|
1232
|
-
this.removedRefs.clear();
|
|
1233
|
-
this.labelDrafts.clear();
|
|
1234
|
-
this.keyDrafts.clear();
|
|
1235
|
-
this.keyClears.clear();
|
|
1236
|
-
}
|
|
1237
|
-
/** Unset one stored credential, then re-read the Host's credential states. */
|
|
1238
|
-
async unsetKey(ref) {
|
|
1239
|
-
try {
|
|
1240
|
-
if (!(await this.api.credentials.unset(ref)).ok) return false;
|
|
1241
|
-
} catch {
|
|
1242
|
-
return false;
|
|
1243
|
-
}
|
|
1244
|
-
await this.describeAll();
|
|
1245
|
-
return this.credentialStates.get(ref)?.configured !== true;
|
|
1246
|
-
}
|
|
1247
|
-
/** The account-level writes a save performs (empty when nothing staged). */
|
|
1248
|
-
accountPlan() {
|
|
1249
|
-
if (!this.accountsDirty()) return [];
|
|
1250
|
-
const runs = [];
|
|
1251
|
-
for (const ref of this.keyClears) if (this.credentialStates.get(ref)?.configured === true) runs.push(() => this.unsetKey(ref));
|
|
1252
|
-
for (const [ref, text] of this.keyDrafts) {
|
|
1253
|
-
const value = text.trim();
|
|
1254
|
-
if (value !== "" && !this.removedRefs.has(ref) && !this.keyClears.has(ref)) runs.push(() => this.writeKeyTo(ref, value));
|
|
1255
|
-
}
|
|
1256
|
-
runs.push(() => this.writeAccounts());
|
|
1257
|
-
return runs;
|
|
1258
|
-
}
|
|
1259
|
-
/**
|
|
1260
|
-
* Persist the staged accounts list into the settings section.
|
|
1261
|
-
*
|
|
1262
|
-
* The stored list is the base — NOT a list rebuilt from this page's rows.
|
|
1263
|
-
* A composition-config entry may carry a literal `apiKey` (or a shape this
|
|
1264
|
-
* page does not know), and the settings layer replaces the whole array, so a
|
|
1265
|
-
* rebuilt list would silently delete every entry the page cannot name along
|
|
1266
|
-
* with the literal keys of the entries it can. Entries are therefore carried
|
|
1267
|
-
* over verbatim and only the reference-carrying rows are rewritten (label
|
|
1268
|
-
* draft applied, staged removals dropped, staged additions appended).
|
|
1269
|
-
*/
|
|
1270
|
-
async writeAccounts() {
|
|
1271
|
-
const removed = this.removedRefs;
|
|
1272
|
-
const written = /* @__PURE__ */ new Set();
|
|
1273
|
-
const list = [];
|
|
1274
|
-
for (const entry of this.rawStoredAccounts()) {
|
|
1275
|
-
const ref = entry.apiKeyEnv;
|
|
1276
|
-
if (typeof ref !== "string" || ref === "") {
|
|
1277
|
-
list.push({ ...entry });
|
|
1278
|
-
continue;
|
|
1279
|
-
}
|
|
1280
|
-
if (removed.has(ref) || written.has(ref)) continue;
|
|
1281
|
-
written.add(ref);
|
|
1282
|
-
list.push(this.accountEntry(ref, entry));
|
|
1283
|
-
}
|
|
1284
|
-
for (const extra of this.addedAccounts) {
|
|
1285
|
-
if (written.has(extra.ref)) continue;
|
|
1286
|
-
written.add(extra.ref);
|
|
1287
|
-
list.push(this.accountEntry(extra.ref, extra));
|
|
1288
|
-
}
|
|
1205
|
+
/** Persist a full accounts list and verify the Host stored it. */
|
|
1206
|
+
async writeAccountList(list) {
|
|
1289
1207
|
await this.scope.set("accounts", list);
|
|
1290
1208
|
const after = this.rawStoredAccounts();
|
|
1291
|
-
return after.length === list.length && list.every((item, index) => after[index]?.apiKeyEnv === item.apiKeyEnv);
|
|
1292
|
-
}
|
|
1293
|
-
/**
|
|
1294
|
-
* One written account entry: the stored/added facts plus the label draft.
|
|
1295
|
-
* `fallback` contributes the non-managed fields (a stored entry's literal
|
|
1296
|
-
* `apiKey`, or any future field) so a rewrite never strips them.
|
|
1297
|
-
*/
|
|
1298
|
-
accountEntry(ref, fallback) {
|
|
1299
|
-
const draft = this.labelDrafts.get(ref)?.trim();
|
|
1300
|
-
const base = "ref" in fallback ? { label: fallback.label } : { ...fallback };
|
|
1301
|
-
const storedLabel = base.label;
|
|
1302
|
-
return {
|
|
1303
|
-
...base,
|
|
1304
|
-
label: draft !== void 0 && draft !== "" ? draft : typeof storedLabel === "string" && storedLabel !== "" ? storedLabel : ref,
|
|
1305
|
-
apiKeyEnv: ref
|
|
1306
|
-
};
|
|
1209
|
+
return after.length === list.length && list.every((item, index) => after[index]?.apiKeyEnv === item.apiKeyEnv && after[index]?.label === item.label);
|
|
1307
1210
|
}
|
|
1308
1211
|
/** The stored routing rules from the settings section (`modelAccountRules`). */
|
|
1309
1212
|
storedRules() {
|
|
1310
1213
|
const raw = this.scope.getSnapshot().value?.modelAccountRules;
|
|
1311
1214
|
if (!Array.isArray(raw)) return [];
|
|
1312
1215
|
const out = [];
|
|
1313
|
-
for (const
|
|
1216
|
+
for (const entry of raw) {
|
|
1314
1217
|
if (typeof entry !== "object" || entry === null || Array.isArray(entry)) continue;
|
|
1315
1218
|
const record = entry;
|
|
1316
|
-
const models = record.models;
|
|
1317
|
-
|
|
1318
|
-
const modelsList = Array.isArray(models) && models.every((m) => typeof m === "string") ? models.filter((m) => m !== "") : [];
|
|
1319
|
-
if (modelsList.length === 0) continue;
|
|
1219
|
+
const models = Array.isArray(record.models) ? record.models.filter((m) => typeof m === "string" && m !== "") : [];
|
|
1220
|
+
if (models.length === 0) continue;
|
|
1320
1221
|
out.push({
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
account: typeof account === "string" && account !== "" ? account : "default"
|
|
1222
|
+
models,
|
|
1223
|
+
account: typeof record.account === "string" && record.account !== "" ? record.account : "default"
|
|
1324
1224
|
});
|
|
1325
1225
|
}
|
|
1326
1226
|
return out;
|
|
1327
1227
|
}
|
|
1328
|
-
/**
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
id: rule.id,
|
|
1334
|
-
models: draft?.models ?? rule.models,
|
|
1335
|
-
account: draft?.account ?? rule.account,
|
|
1336
|
-
added: false
|
|
1337
|
-
};
|
|
1338
|
-
});
|
|
1339
|
-
const added = this.addedRules.map((rule, index) => ({
|
|
1340
|
-
id: `new-${index}`,
|
|
1341
|
-
models: rule.models,
|
|
1342
|
-
account: rule.account,
|
|
1343
|
-
added: true
|
|
1344
|
-
}));
|
|
1345
|
-
return [...stored, ...added];
|
|
1346
|
-
}
|
|
1347
|
-
/** Whether any routing-rule staging (add/remove/edit) exists. */
|
|
1348
|
-
rulesStaged() {
|
|
1349
|
-
return this.addedRules.length > 0 || this.removedRuleIds.size > 0 || this.ruleDrafts.size > 0;
|
|
1350
|
-
}
|
|
1351
|
-
/** Whether the staged routing rules differ from the stored section. */
|
|
1352
|
-
rulesDirty() {
|
|
1353
|
-
if (this.addedRules.length > 0 || this.removedRuleIds.size > 0) return true;
|
|
1354
|
-
for (const [id, draft] of this.ruleDrafts) {
|
|
1355
|
-
const base = this.storedRules().find((rule) => rule.id === id);
|
|
1356
|
-
if (base === void 0) continue;
|
|
1357
|
-
if (draft.models.length > 0 && !sameModels(draft.models, base.models)) return true;
|
|
1358
|
-
if (draft.account !== base.account) return true;
|
|
1359
|
-
}
|
|
1360
|
-
return false;
|
|
1361
|
-
}
|
|
1362
|
-
/** Reset every routing-rule staged edit. */
|
|
1363
|
-
clearRuleStaging() {
|
|
1364
|
-
this.addedRules = [];
|
|
1365
|
-
this.ruleDrafts.clear();
|
|
1366
|
-
this.removedRuleIds.clear();
|
|
1367
|
-
}
|
|
1368
|
-
/**
|
|
1369
|
-
* Drop rule staging the stored section already reflects (partial-save
|
|
1370
|
-
* retry). Stored row ids are positional (`rule-N`) and shift after any
|
|
1371
|
-
* write, so every check here is content-based, never id-based:
|
|
1372
|
-
* - landed additions (in `addedRules`, already in stored) are dropped, or
|
|
1373
|
-
* a retry would persist them twice;
|
|
1374
|
-
* - staged removals whose snapshot row is gone from stored have landed
|
|
1375
|
-
* (drop them); a removal whose snapshot still matches a stored row is
|
|
1376
|
-
* still pending (keep it).
|
|
1377
|
-
*/
|
|
1378
|
-
reconcileRuleStaging() {
|
|
1379
|
-
const stored = this.storedRules();
|
|
1380
|
-
this.addedRules = this.addedRules.filter((added) => !stored.some((rule) => sameModels(added.models, rule.models) && added.account === rule.account));
|
|
1381
|
-
for (const [id, snapshot] of [...this.removedRuleIds]) if (!stored.some((rule) => sameModels(snapshot.models, rule.models) && snapshot.account === rule.account)) this.removedRuleIds.delete(id);
|
|
1382
|
-
if (this.rulesBeforeSave !== void 0 && ruleFingerprint(stored) === this.rulesBeforeSave) return;
|
|
1383
|
-
this.ruleDrafts.clear();
|
|
1228
|
+
/** Persist routing rules and verify the Host stored them. */
|
|
1229
|
+
async writeRules(list) {
|
|
1230
|
+
await this.scope.set("modelAccountRules", list);
|
|
1231
|
+
const after = this.storedRules();
|
|
1232
|
+
return after.length === list.length && list.every((item, index) => after[index] !== void 0 && sameModels(after[index].models, item.models) && after[index].account === item.account);
|
|
1384
1233
|
}
|
|
1385
1234
|
/** The stored visible-model allowlist (`visibleModels`); empty = show all. */
|
|
1386
1235
|
storedVisibleModels() {
|
|
@@ -1388,79 +1237,19 @@ window.__ModuleLoader__.load({
|
|
|
1388
1237
|
if (!Array.isArray(raw)) return [];
|
|
1389
1238
|
return raw.filter((m) => typeof m === "string" && m !== "");
|
|
1390
1239
|
}
|
|
1391
|
-
/** Effective visible-model allowlist: staged draft or stored value. */
|
|
1392
1240
|
effectiveVisibleModels() {
|
|
1393
1241
|
return this.visibleModelsDraft ?? this.storedVisibleModels();
|
|
1394
1242
|
}
|
|
1395
|
-
/** Whether the staged visible-model selection differs from stored. */
|
|
1396
1243
|
visibleModelsDirty() {
|
|
1397
1244
|
return this.visibleModelsDraft !== void 0 && !sameModels(this.visibleModelsDraft, this.storedVisibleModels());
|
|
1398
1245
|
}
|
|
1399
|
-
/** Whether any visible-model staging exists. */
|
|
1400
|
-
visibleModelsStaged() {
|
|
1401
|
-
return this.visibleModelsDraft !== void 0;
|
|
1402
|
-
}
|
|
1403
|
-
/** Reset the visible-model staged edit. */
|
|
1404
|
-
clearVisibleModelsStaging() {
|
|
1405
|
-
this.visibleModelsDraft = void 0;
|
|
1406
|
-
}
|
|
1407
|
-
/** Drop visible-model staging the stored section already reflects. */
|
|
1408
|
-
reconcileVisibleModelsStaging() {
|
|
1409
|
-
if (this.visibleModelsDraft !== void 0 && sameModels(this.visibleModelsDraft, this.storedVisibleModels())) this.visibleModelsDraft = void 0;
|
|
1410
|
-
}
|
|
1411
|
-
/** The visible-model writes a save performs (empty when nothing staged). */
|
|
1412
1246
|
visibleModelsPlan() {
|
|
1413
1247
|
if (!this.visibleModelsDirty()) return [];
|
|
1414
|
-
return [() =>
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
await this.scope.set("visibleModels", list);
|
|
1420
|
-
return sameModels(this.storedVisibleModels(), list);
|
|
1421
|
-
}
|
|
1422
|
-
/** Stage the visible-model allowlist (multi-select). */
|
|
1423
|
-
editVisibleModels(models) {
|
|
1424
|
-
this.visibleModelsDraft = [...models];
|
|
1425
|
-
this.failed = false;
|
|
1426
|
-
this.publish();
|
|
1427
|
-
}
|
|
1428
|
-
/** Stage "show all models" (clears the allowlist). */
|
|
1429
|
-
clearVisibleModels() {
|
|
1430
|
-
this.visibleModelsDraft = [];
|
|
1431
|
-
this.failed = false;
|
|
1432
|
-
this.publish();
|
|
1433
|
-
}
|
|
1434
|
-
/** The routing-rule writes a save performs (empty when nothing staged). */
|
|
1435
|
-
rulesPlan() {
|
|
1436
|
-
if (!this.rulesDirty()) return [];
|
|
1437
|
-
return [() => this.writeRules()];
|
|
1438
|
-
}
|
|
1439
|
-
/** Persist the staged routing rules into the settings section. */
|
|
1440
|
-
async writeRules() {
|
|
1441
|
-
const list = [...this.storedRules().filter((rule) => {
|
|
1442
|
-
const snapshot = this.removedRuleIds.get(rule.id);
|
|
1443
|
-
return snapshot === void 0 || !sameModels(snapshot.models, rule.models) || snapshot.account !== rule.account;
|
|
1444
|
-
}).map((rule) => {
|
|
1445
|
-
const draft = this.ruleDrafts.get(rule.id);
|
|
1446
|
-
return {
|
|
1447
|
-
models: draft !== void 0 && draft.models.length > 0 ? draft.models : rule.models,
|
|
1448
|
-
account: draft?.account !== void 0 && draft.account !== "" ? draft.account : rule.account
|
|
1449
|
-
};
|
|
1450
|
-
}), ...this.addedRules.map((rule) => ({
|
|
1451
|
-
models: rule.models,
|
|
1452
|
-
account: rule.account
|
|
1453
|
-
}))].filter((rule) => rule.models.length > 0);
|
|
1454
|
-
const seen = /* @__PURE__ */ new Set();
|
|
1455
|
-
const deduped = list.filter((rule) => {
|
|
1456
|
-
const key = `${rule.account}\u0001${[...rule.models].sort().join("")}`;
|
|
1457
|
-
if (seen.has(key)) return false;
|
|
1458
|
-
seen.add(key);
|
|
1459
|
-
return true;
|
|
1460
|
-
});
|
|
1461
|
-
await this.scope.set("modelAccountRules", deduped);
|
|
1462
|
-
const after = this.storedRules();
|
|
1463
|
-
return after.length === deduped.length && deduped.every((item, index) => after[index] !== void 0 && sameModels(after[index].models, item.models) && after[index].account === item.account);
|
|
1248
|
+
return [async () => {
|
|
1249
|
+
const list = this.visibleModelsDraft ?? [];
|
|
1250
|
+
await this.scope.set("visibleModels", list);
|
|
1251
|
+
return sameModels(this.storedVisibleModels(), list);
|
|
1252
|
+
}];
|
|
1464
1253
|
}
|
|
1465
1254
|
publish() {
|
|
1466
1255
|
if (this.disposed) return;
|
|
@@ -2381,55 +2170,6 @@ window.__ModuleLoader__.load({
|
|
|
2381
2170
|
]
|
|
2382
2171
|
};
|
|
2383
2172
|
//#endregion
|
|
2384
|
-
//#region src/client/login-row.tsx
|
|
2385
|
-
/** The sign-in alternative to pasting a key (settings page + Models card). */
|
|
2386
|
-
function LoginRow({ state, targetRef, disabled, disabledHint, t, onBegin, onCancel }) {
|
|
2387
|
-
const { visible: visibleState, busyElsewhere } = loginStateForTarget(state, targetRef);
|
|
2388
|
-
const busy = visibleState.phase === "starting" || visibleState.phase === "waiting";
|
|
2389
|
-
const hint = loginHint(visibleState, t);
|
|
2390
|
-
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2391
|
-
className: "cc-field",
|
|
2392
|
-
children: [
|
|
2393
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2394
|
-
className: "cc-fieldHead",
|
|
2395
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2396
|
-
className: "cc-label",
|
|
2397
|
-
children: t("loginTitle")
|
|
2398
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2399
|
-
className: "cc-badges",
|
|
2400
|
-
children: busy ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2401
|
-
type: "button",
|
|
2402
|
-
className: "cc-reset",
|
|
2403
|
-
onClick: onCancel,
|
|
2404
|
-
children: t("loginCancel")
|
|
2405
|
-
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2406
|
-
type: "button",
|
|
2407
|
-
className: "cc-reset",
|
|
2408
|
-
disabled: disabled || busyElsewhere,
|
|
2409
|
-
onClick: onBegin,
|
|
2410
|
-
children: t("loginButton")
|
|
2411
|
-
})
|
|
2412
|
-
})]
|
|
2413
|
-
}),
|
|
2414
|
-
visibleState.authUrl !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
2415
|
-
className: "cc-hint",
|
|
2416
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("a", {
|
|
2417
|
-
className: "cc-loginLink",
|
|
2418
|
-
href: visibleState.authUrl,
|
|
2419
|
-
target: "_blank",
|
|
2420
|
-
rel: "noreferrer",
|
|
2421
|
-
children: t("loginOpenLink")
|
|
2422
|
-
})
|
|
2423
|
-
}) : null,
|
|
2424
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
2425
|
-
className: hint.className,
|
|
2426
|
-
title: hint.title,
|
|
2427
|
-
children: disabled && !busy && disabledHint !== void 0 ? disabledHint : hint.text
|
|
2428
|
-
})
|
|
2429
|
-
]
|
|
2430
|
-
});
|
|
2431
|
-
}
|
|
2432
|
-
//#endregion
|
|
2433
2173
|
//#region src/client/model-select.ts
|
|
2434
2174
|
/**
|
|
2435
2175
|
* Model-select helpers for the settings page's model editors (browser half).
|
|
@@ -2573,7 +2313,7 @@ window.__ModuleLoader__.load({
|
|
|
2573
2313
|
}
|
|
2574
2314
|
//#endregion
|
|
2575
2315
|
//#region package.json
|
|
2576
|
-
var version = "0.11.
|
|
2316
|
+
var version = "0.11.14";
|
|
2577
2317
|
var repository = {
|
|
2578
2318
|
"type": "git",
|
|
2579
2319
|
"url": "git+https://github.com/Mars-Sea/dsh-commandcode-provider.git"
|
|
@@ -2759,69 +2499,112 @@ window.__ModuleLoader__.load({
|
|
|
2759
2499
|
* `settings.commandcode` locale namespace; all state comes from the
|
|
2760
2500
|
* `CommandCodeSettingsController` injected by the slot registration.
|
|
2761
2501
|
*
|
|
2762
|
-
*
|
|
2763
|
-
*
|
|
2764
|
-
*
|
|
2765
|
-
*
|
|
2766
|
-
*
|
|
2767
|
-
*
|
|
2768
|
-
* (
|
|
2502
|
+
* Layout, top to bottom:
|
|
2503
|
+
* 1. Accounts — one card per account with its status, quota, actions and
|
|
2504
|
+
* dedicated models. Every account operation commits immediately.
|
|
2505
|
+
* 2. Models — the plan filter and the visible-model allowlist.
|
|
2506
|
+
* 3. Privacy & security — zero data retention and the command guard.
|
|
2507
|
+
* 4. Integrations & display — web search and the sidebar quota card.
|
|
2508
|
+
* 5. Advanced (collapsed) — API base and the network limits.
|
|
2509
|
+
* Sections 2–5 are flat groups of rows (title and description left, control
|
|
2510
|
+
* right, as on the harness's own settings pages), a staged form written by
|
|
2511
|
+
* the floating save bar.
|
|
2512
|
+
*
|
|
2513
|
+
* Styles are injected once by the client entry (see src/client/index.ts) and
|
|
2514
|
+
* class-prefixed `cc-` to stay local.
|
|
2769
2515
|
*/
|
|
2770
2516
|
const ADVANCED_FIELDS = [
|
|
2771
2517
|
"apiBase",
|
|
2772
|
-
"workingDir",
|
|
2773
2518
|
"requestTimeoutMs",
|
|
2774
2519
|
"streamIdleTimeoutMs",
|
|
2775
|
-
"transportMaxRetries"
|
|
2776
|
-
"filterModelsByPlan",
|
|
2777
|
-
"webSearch",
|
|
2778
|
-
"showSidebarQuota",
|
|
2779
|
-
"commandGuard",
|
|
2780
|
-
"commandGuardThreshold",
|
|
2781
|
-
"commandGuardTimeoutMs",
|
|
2782
|
-
"zdr"
|
|
2520
|
+
"transportMaxRetries"
|
|
2783
2521
|
];
|
|
2784
|
-
/**
|
|
2785
|
-
function
|
|
2522
|
+
/** The "customized" tag and reset link a staged field shows once it differs from the default. */
|
|
2523
|
+
function FieldOverride({ label, state, disabled, t, onReset }) {
|
|
2524
|
+
if (!state.overridden && !state.clear) return null;
|
|
2525
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2526
|
+
type: "button",
|
|
2527
|
+
className: "cc-linkButton",
|
|
2528
|
+
disabled,
|
|
2529
|
+
onClick: onReset,
|
|
2530
|
+
"aria-label": `${label} — ${t("reset")}`,
|
|
2531
|
+
children: t("reset")
|
|
2532
|
+
});
|
|
2533
|
+
}
|
|
2534
|
+
/**
|
|
2535
|
+
* One settings row, laid out like the harness's own General page: the title
|
|
2536
|
+
* and description on the left, the control on the right, a hairline between
|
|
2537
|
+
* rows.
|
|
2538
|
+
*/
|
|
2539
|
+
function SettingRow({ title, titleFor, titleId, tag, description, error, control, className }) {
|
|
2786
2540
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2787
|
-
className: "cc-
|
|
2788
|
-
children: [
|
|
2789
|
-
|
|
2790
|
-
|
|
2791
|
-
|
|
2792
|
-
className: "cc-
|
|
2793
|
-
|
|
2794
|
-
|
|
2795
|
-
|
|
2796
|
-
|
|
2797
|
-
|
|
2798
|
-
|
|
2799
|
-
|
|
2800
|
-
|
|
2801
|
-
|
|
2802
|
-
|
|
2803
|
-
|
|
2804
|
-
|
|
2805
|
-
|
|
2806
|
-
|
|
2807
|
-
|
|
2808
|
-
|
|
2809
|
-
|
|
2810
|
-
|
|
2811
|
-
|
|
2812
|
-
|
|
2813
|
-
|
|
2814
|
-
|
|
2815
|
-
|
|
2816
|
-
|
|
2817
|
-
|
|
2818
|
-
|
|
2819
|
-
|
|
2820
|
-
|
|
2821
|
-
|
|
2822
|
-
|
|
2823
|
-
|
|
2824
|
-
|
|
2541
|
+
className: className === void 0 ? "cc-row" : `cc-row ${className}`,
|
|
2542
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2543
|
+
className: "cc-rowText",
|
|
2544
|
+
children: [
|
|
2545
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2546
|
+
className: "cc-rowTitleLine",
|
|
2547
|
+
children: [titleFor !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("label", {
|
|
2548
|
+
className: "cc-rowTitle",
|
|
2549
|
+
id: titleId,
|
|
2550
|
+
htmlFor: titleFor,
|
|
2551
|
+
children: title
|
|
2552
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2553
|
+
className: "cc-rowTitle",
|
|
2554
|
+
id: titleId,
|
|
2555
|
+
children: title
|
|
2556
|
+
}), tag]
|
|
2557
|
+
}),
|
|
2558
|
+
error !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
2559
|
+
className: "cc-rowError",
|
|
2560
|
+
children: error
|
|
2561
|
+
}) : null,
|
|
2562
|
+
error === void 0 && description !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
2563
|
+
className: "cc-rowDesc",
|
|
2564
|
+
children: description
|
|
2565
|
+
}) : null
|
|
2566
|
+
]
|
|
2567
|
+
}), control !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
2568
|
+
className: "cc-rowControl",
|
|
2569
|
+
children: control
|
|
2570
|
+
}) : null]
|
|
2571
|
+
});
|
|
2572
|
+
}
|
|
2573
|
+
/** The tag a row title carries while its field overrides the default. */
|
|
2574
|
+
function OverrideTag({ state, t }) {
|
|
2575
|
+
return state.overridden ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2576
|
+
className: "cc-badge",
|
|
2577
|
+
children: t("overridden")
|
|
2578
|
+
}) : null;
|
|
2579
|
+
}
|
|
2580
|
+
/** One text/number field row. */
|
|
2581
|
+
function Field({ id, label, hint, state, disabled, numeric, wide, placeholder, onEdit, onReset, t }) {
|
|
2582
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SettingRow, {
|
|
2583
|
+
title: label,
|
|
2584
|
+
titleFor: id,
|
|
2585
|
+
tag: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(OverrideTag, {
|
|
2586
|
+
state,
|
|
2587
|
+
t
|
|
2588
|
+
}),
|
|
2589
|
+
description: hint,
|
|
2590
|
+
error: state.invalid ? invalidCopy(state.invalidReason, t) : void 0,
|
|
2591
|
+
control: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(FieldOverride, {
|
|
2592
|
+
label,
|
|
2593
|
+
state,
|
|
2594
|
+
disabled,
|
|
2595
|
+
t,
|
|
2596
|
+
onReset
|
|
2597
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
2598
|
+
id,
|
|
2599
|
+
className: `cc-input ${wide ? "cc-rowInputWide" : "cc-rowInput"}${state.invalid ? " cc-inputInvalid" : ""}`,
|
|
2600
|
+
type: "text",
|
|
2601
|
+
inputMode: numeric ? "numeric" : void 0,
|
|
2602
|
+
"aria-invalid": state.invalid || void 0,
|
|
2603
|
+
value: state.text,
|
|
2604
|
+
placeholder,
|
|
2605
|
+
disabled,
|
|
2606
|
+
onChange: (event) => onEdit(event.target.value)
|
|
2607
|
+
})] })
|
|
2825
2608
|
});
|
|
2826
2609
|
}
|
|
2827
2610
|
/** The per-field error copy for a staged draft's failure reason. */
|
|
@@ -2831,299 +2614,82 @@ window.__ModuleLoader__.load({
|
|
|
2831
2614
|
return t("invalidNumber");
|
|
2832
2615
|
}
|
|
2833
2616
|
/**
|
|
2834
|
-
*
|
|
2835
|
-
* plan filter, the web-search backend and the sidebar quota card live here so
|
|
2836
|
-
* the page leads with the facts a user actually touches. Starts collapsed on
|
|
2837
|
-
* every visit; expands on demand. While collapsed, a badge names the
|
|
2838
|
-
* customized count so a nonzero override stays visible (a number field's error
|
|
2839
|
-
* blocks save and must be reachable).
|
|
2840
|
-
*/
|
|
2841
|
-
function AdvancedSection({ state, disabled, t, onEdit, onReset }) {
|
|
2842
|
-
const [expanded, setExpanded] = (0, react.useState)(false);
|
|
2843
|
-
const overridden = ADVANCED_FIELDS.filter((field) => state[field].overridden).length;
|
|
2844
|
-
const invalid = ADVANCED_FIELDS.some((field) => state[field].invalid);
|
|
2845
|
-
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2846
|
-
className: "cc-card",
|
|
2847
|
-
children: [
|
|
2848
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
2849
|
-
type: "button",
|
|
2850
|
-
className: "cc-advancedHead",
|
|
2851
|
-
"aria-expanded": expanded,
|
|
2852
|
-
"aria-controls": "cc-advanced-body",
|
|
2853
|
-
onClick: () => setExpanded((value) => !value),
|
|
2854
|
-
children: [
|
|
2855
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2856
|
-
className: "cc-advancedTitle",
|
|
2857
|
-
children: t("advancedSettings")
|
|
2858
|
-
}),
|
|
2859
|
-
overridden > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2860
|
-
className: "cc-badge",
|
|
2861
|
-
children: overridden === 1 ? t("advancedOverriddenOne") : t("advancedOverriddenMany", { count: overridden })
|
|
2862
|
-
}) : null,
|
|
2863
|
-
!expanded && invalid ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2864
|
-
className: "cc-badge cc-badgeWarn",
|
|
2865
|
-
children: t("advancedInvalid")
|
|
2866
|
-
}) : null,
|
|
2867
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: "cc-advancedSpacer" }),
|
|
2868
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2869
|
-
className: expanded ? "cc-chevron cc-chevronUp" : "cc-chevron",
|
|
2870
|
-
"aria-hidden": "true"
|
|
2871
|
-
})
|
|
2872
|
-
]
|
|
2873
|
-
}),
|
|
2874
|
-
expanded ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2875
|
-
id: "cc-advanced-body",
|
|
2876
|
-
className: "cc-advancedBody",
|
|
2877
|
-
children: [
|
|
2878
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
2879
|
-
className: "cc-hint",
|
|
2880
|
-
children: t("advancedSettingsHint")
|
|
2881
|
-
}),
|
|
2882
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Field, {
|
|
2883
|
-
id: "cc-api-base",
|
|
2884
|
-
label: t("apiBase"),
|
|
2885
|
-
hint: t("apiBaseHint"),
|
|
2886
|
-
state: state.apiBase,
|
|
2887
|
-
disabled,
|
|
2888
|
-
onEdit: (text) => onEdit("apiBase", text),
|
|
2889
|
-
onReset: () => onReset("apiBase"),
|
|
2890
|
-
t
|
|
2891
|
-
}),
|
|
2892
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Field, {
|
|
2893
|
-
id: "cc-working-dir",
|
|
2894
|
-
label: t("workingDir"),
|
|
2895
|
-
hint: t("workingDirHint"),
|
|
2896
|
-
state: state.workingDir,
|
|
2897
|
-
disabled,
|
|
2898
|
-
onEdit: (text) => onEdit("workingDir", text),
|
|
2899
|
-
onReset: () => onReset("workingDir"),
|
|
2900
|
-
t
|
|
2901
|
-
}),
|
|
2902
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Field, {
|
|
2903
|
-
id: "cc-request-timeout",
|
|
2904
|
-
label: t("requestTimeoutMs"),
|
|
2905
|
-
hint: t("requestTimeoutMsHint"),
|
|
2906
|
-
state: state.requestTimeoutMs,
|
|
2907
|
-
disabled,
|
|
2908
|
-
numeric: true,
|
|
2909
|
-
onEdit: (text) => onEdit("requestTimeoutMs", text),
|
|
2910
|
-
onReset: () => onReset("requestTimeoutMs"),
|
|
2911
|
-
t
|
|
2912
|
-
}),
|
|
2913
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Field, {
|
|
2914
|
-
id: "cc-stream-idle-timeout",
|
|
2915
|
-
label: t("streamIdleTimeoutMs"),
|
|
2916
|
-
hint: t("streamIdleTimeoutMsHint"),
|
|
2917
|
-
state: state.streamIdleTimeoutMs,
|
|
2918
|
-
disabled,
|
|
2919
|
-
numeric: true,
|
|
2920
|
-
onEdit: (text) => onEdit("streamIdleTimeoutMs", text),
|
|
2921
|
-
onReset: () => onReset("streamIdleTimeoutMs"),
|
|
2922
|
-
t
|
|
2923
|
-
}),
|
|
2924
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Field, {
|
|
2925
|
-
id: "cc-transport-max-retries",
|
|
2926
|
-
label: t("transportMaxRetries"),
|
|
2927
|
-
hint: t("transportMaxRetriesHint"),
|
|
2928
|
-
state: state.transportMaxRetries,
|
|
2929
|
-
disabled,
|
|
2930
|
-
numeric: true,
|
|
2931
|
-
onEdit: (text) => onEdit("transportMaxRetries", text),
|
|
2932
|
-
onReset: () => onReset("transportMaxRetries"),
|
|
2933
|
-
t
|
|
2934
|
-
}),
|
|
2935
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(ToggleField, {
|
|
2936
|
-
id: "cc-filter-models-by-plan",
|
|
2937
|
-
label: t("filterModelsByPlan"),
|
|
2938
|
-
hint: t("filterModelsByPlanHint"),
|
|
2939
|
-
state: state.filterModelsByPlan,
|
|
2940
|
-
disabled,
|
|
2941
|
-
defaultChecked: true,
|
|
2942
|
-
onEdit: (text) => onEdit("filterModelsByPlan", text),
|
|
2943
|
-
onReset: () => onReset("filterModelsByPlan"),
|
|
2944
|
-
t
|
|
2945
|
-
}),
|
|
2946
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(ToggleField, {
|
|
2947
|
-
id: "cc-web-search",
|
|
2948
|
-
label: t("webSearch"),
|
|
2949
|
-
hint: t("webSearchHint"),
|
|
2950
|
-
state: state.webSearch,
|
|
2951
|
-
disabled,
|
|
2952
|
-
defaultChecked: true,
|
|
2953
|
-
onEdit: (text) => onEdit("webSearch", text),
|
|
2954
|
-
onReset: () => onReset("webSearch"),
|
|
2955
|
-
t
|
|
2956
|
-
}),
|
|
2957
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(ToggleField, {
|
|
2958
|
-
id: "cc-show-sidebar-quota",
|
|
2959
|
-
label: t("showSidebarQuota"),
|
|
2960
|
-
hint: t("showSidebarQuotaHint"),
|
|
2961
|
-
state: state.showSidebarQuota,
|
|
2962
|
-
disabled,
|
|
2963
|
-
defaultChecked: false,
|
|
2964
|
-
onEdit: (text) => onEdit("showSidebarQuota", text),
|
|
2965
|
-
onReset: () => onReset("showSidebarQuota"),
|
|
2966
|
-
t
|
|
2967
|
-
}),
|
|
2968
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(ToggleField, {
|
|
2969
|
-
id: "cc-command-guard",
|
|
2970
|
-
label: t("commandGuard"),
|
|
2971
|
-
hint: t("commandGuardHint"),
|
|
2972
|
-
state: state.commandGuard,
|
|
2973
|
-
disabled,
|
|
2974
|
-
defaultChecked: false,
|
|
2975
|
-
onEdit: (text) => onEdit("commandGuard", text),
|
|
2976
|
-
onReset: () => onReset("commandGuard"),
|
|
2977
|
-
t
|
|
2978
|
-
}),
|
|
2979
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Field, {
|
|
2980
|
-
id: "cc-command-guard-threshold",
|
|
2981
|
-
label: t("commandGuardThreshold"),
|
|
2982
|
-
hint: t("commandGuardThresholdHint"),
|
|
2983
|
-
state: state.commandGuardThreshold,
|
|
2984
|
-
disabled,
|
|
2985
|
-
onEdit: (text) => onEdit("commandGuardThreshold", text),
|
|
2986
|
-
onReset: () => onReset("commandGuardThreshold"),
|
|
2987
|
-
t
|
|
2988
|
-
}),
|
|
2989
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Field, {
|
|
2990
|
-
id: "cc-command-guard-timeout",
|
|
2991
|
-
label: t("commandGuardTimeoutMs"),
|
|
2992
|
-
hint: t("commandGuardTimeoutMsHint"),
|
|
2993
|
-
state: state.commandGuardTimeoutMs,
|
|
2994
|
-
disabled,
|
|
2995
|
-
numeric: true,
|
|
2996
|
-
onEdit: (text) => onEdit("commandGuardTimeoutMs", text),
|
|
2997
|
-
onReset: () => onReset("commandGuardTimeoutMs"),
|
|
2998
|
-
t
|
|
2999
|
-
}),
|
|
3000
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(ToggleField, {
|
|
3001
|
-
id: "cc-zdr",
|
|
3002
|
-
label: t("zdr"),
|
|
3003
|
-
hint: t("zdrHint"),
|
|
3004
|
-
state: state.zdr,
|
|
3005
|
-
disabled,
|
|
3006
|
-
defaultChecked: false,
|
|
3007
|
-
onEdit: (text) => onEdit("zdr", text),
|
|
3008
|
-
onReset: () => onReset("zdr"),
|
|
3009
|
-
t
|
|
3010
|
-
})
|
|
3011
|
-
]
|
|
3012
|
-
}) : null,
|
|
3013
|
-
expanded && invalid ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
3014
|
-
className: "cc-invalid",
|
|
3015
|
-
role: "status",
|
|
3016
|
-
children: t("advancedInvalid")
|
|
3017
|
-
}) : null
|
|
3018
|
-
]
|
|
3019
|
-
});
|
|
3020
|
-
}
|
|
3021
|
-
/**
|
|
3022
|
-
* One boolean field row rendered as a toggle. The staged text is `'true'` /
|
|
2617
|
+
* One boolean field row rendered as a switch. The staged text is `'true'` /
|
|
3023
2618
|
* `'false'` / `''` (unset → `defaultChecked`); toggling stages the string the
|
|
3024
2619
|
* boolean field spec parses back into a real boolean on save.
|
|
3025
2620
|
*/
|
|
3026
2621
|
function ToggleField({ id, label, hint, state, disabled, defaultChecked, onEdit, onReset, t }) {
|
|
3027
2622
|
const checked = state.text === "" ? defaultChecked : state.text === "true";
|
|
3028
|
-
return /* @__PURE__ */ (0, react_jsx_runtime.
|
|
3029
|
-
|
|
3030
|
-
|
|
3031
|
-
|
|
3032
|
-
|
|
3033
|
-
|
|
3034
|
-
|
|
3035
|
-
|
|
3036
|
-
|
|
3037
|
-
|
|
3038
|
-
|
|
3039
|
-
|
|
3040
|
-
|
|
3041
|
-
|
|
3042
|
-
|
|
3043
|
-
|
|
3044
|
-
|
|
3045
|
-
|
|
3046
|
-
|
|
3047
|
-
|
|
3048
|
-
|
|
3049
|
-
|
|
3050
|
-
})
|
|
3051
|
-
className: "cc-toggleRow",
|
|
3052
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
3053
|
-
id,
|
|
3054
|
-
className: "cc-toggle",
|
|
3055
|
-
type: "checkbox",
|
|
3056
|
-
role: "switch",
|
|
3057
|
-
checked,
|
|
3058
|
-
disabled,
|
|
3059
|
-
onChange: (event) => onEdit(event.target.checked ? "true" : "false")
|
|
3060
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3061
|
-
className: "cc-hint",
|
|
3062
|
-
children: hint
|
|
3063
|
-
})]
|
|
3064
|
-
})]
|
|
2623
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SettingRow, {
|
|
2624
|
+
title: label,
|
|
2625
|
+
titleFor: id,
|
|
2626
|
+
tag: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(OverrideTag, {
|
|
2627
|
+
state,
|
|
2628
|
+
t
|
|
2629
|
+
}),
|
|
2630
|
+
description: hint,
|
|
2631
|
+
control: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(FieldOverride, {
|
|
2632
|
+
label,
|
|
2633
|
+
state,
|
|
2634
|
+
disabled,
|
|
2635
|
+
t,
|
|
2636
|
+
onReset
|
|
2637
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
2638
|
+
id,
|
|
2639
|
+
className: "cc-toggle",
|
|
2640
|
+
type: "checkbox",
|
|
2641
|
+
role: "switch",
|
|
2642
|
+
checked,
|
|
2643
|
+
disabled,
|
|
2644
|
+
onChange: (event) => onEdit(event.target.checked ? "true" : "false")
|
|
2645
|
+
})] })
|
|
3065
2646
|
});
|
|
3066
2647
|
}
|
|
3067
2648
|
/**
|
|
3068
|
-
*
|
|
3069
|
-
*
|
|
3070
|
-
*
|
|
3071
|
-
* staged for removal (the next save unsets it) when it is bad or unwanted.
|
|
2649
|
+
* One fixed-choice field rendered as a segmented control. The staged text is
|
|
2650
|
+
* one of `options`' values or `''` (unset → `defaultValue`); picking the
|
|
2651
|
+
* default while unset stages nothing new.
|
|
3072
2652
|
*/
|
|
3073
|
-
function
|
|
3074
|
-
const
|
|
3075
|
-
|
|
3076
|
-
|
|
3077
|
-
|
|
3078
|
-
|
|
3079
|
-
|
|
3080
|
-
|
|
3081
|
-
|
|
3082
|
-
|
|
3083
|
-
|
|
3084
|
-
|
|
3085
|
-
|
|
3086
|
-
|
|
3087
|
-
|
|
3088
|
-
|
|
3089
|
-
|
|
3090
|
-
|
|
3091
|
-
|
|
3092
|
-
|
|
3093
|
-
|
|
3094
|
-
|
|
3095
|
-
|
|
3096
|
-
|
|
3097
|
-
|
|
3098
|
-
|
|
3099
|
-
|
|
3100
|
-
|
|
3101
|
-
|
|
3102
|
-
|
|
3103
|
-
|
|
3104
|
-
|
|
3105
|
-
|
|
3106
|
-
onClick: () => setVisible((value) => !value),
|
|
3107
|
-
children: visible ? hideLabel : showLabel
|
|
3108
|
-
})
|
|
3109
|
-
]
|
|
3110
|
-
})]
|
|
3111
|
-
}),
|
|
3112
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
3113
|
-
id: "cc-api-key",
|
|
3114
|
-
className: "cc-input",
|
|
3115
|
-
type: visible ? "text" : "password",
|
|
3116
|
-
autoComplete: "off",
|
|
3117
|
-
spellCheck: false,
|
|
3118
|
-
value: state.text,
|
|
2653
|
+
function SegmentedField({ label, hint, state, disabled, options, defaultValue, className, onEdit, onReset, t }) {
|
|
2654
|
+
const current = state.text === "" ? defaultValue : state.text;
|
|
2655
|
+
const index = Math.max(0, options.findIndex((option) => option.value === current));
|
|
2656
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SettingRow, {
|
|
2657
|
+
title: label,
|
|
2658
|
+
tag: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(OverrideTag, {
|
|
2659
|
+
state,
|
|
2660
|
+
t
|
|
2661
|
+
}),
|
|
2662
|
+
description: hint,
|
|
2663
|
+
...className === void 0 ? {} : { className },
|
|
2664
|
+
control: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(FieldOverride, {
|
|
2665
|
+
label,
|
|
2666
|
+
state,
|
|
2667
|
+
disabled,
|
|
2668
|
+
t,
|
|
2669
|
+
onReset
|
|
2670
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2671
|
+
className: "cc-segmented",
|
|
2672
|
+
role: "radiogroup",
|
|
2673
|
+
"aria-label": label,
|
|
2674
|
+
style: {
|
|
2675
|
+
"--cc-segment-count": options.length,
|
|
2676
|
+
"--cc-segment-index": index
|
|
2677
|
+
},
|
|
2678
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2679
|
+
className: "cc-segmentIndicator",
|
|
2680
|
+
"aria-hidden": "true"
|
|
2681
|
+
}), options.map((option) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2682
|
+
type: "button",
|
|
2683
|
+
role: "radio",
|
|
2684
|
+
"aria-checked": current === option.value,
|
|
2685
|
+
className: "cc-segment",
|
|
3119
2686
|
disabled,
|
|
3120
|
-
|
|
3121
|
-
|
|
3122
|
-
|
|
3123
|
-
|
|
3124
|
-
|
|
3125
|
-
|
|
3126
|
-
]
|
|
2687
|
+
onClick: () => {
|
|
2688
|
+
if (current !== option.value) onEdit(option.value);
|
|
2689
|
+
},
|
|
2690
|
+
children: option.label
|
|
2691
|
+
}, option.value))]
|
|
2692
|
+
})] })
|
|
3127
2693
|
});
|
|
3128
2694
|
}
|
|
3129
2695
|
/** One stat tile in the account card's summary grid. */
|
|
@@ -3194,35 +2760,11 @@ window.__ModuleLoader__.load({
|
|
|
3194
2760
|
]
|
|
3195
2761
|
});
|
|
3196
2762
|
}
|
|
3197
|
-
/** One account's rotation state as a short badge next to its label. */
|
|
3198
|
-
function AccountMark({ entry, t }) {
|
|
3199
|
-
if (entry.active) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3200
|
-
className: "cc-usagePlan",
|
|
3201
|
-
children: t("usageActive")
|
|
3202
|
-
});
|
|
3203
|
-
if (entry.mark === "invalid-credential") return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3204
|
-
className: "cc-usagePlanStatus",
|
|
3205
|
-
children: t("usageInvalidKey")
|
|
3206
|
-
});
|
|
3207
|
-
if (entry.cooldownUntil > 0) return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
3208
|
-
className: "cc-usagePlanStatus",
|
|
3209
|
-
children: [
|
|
3210
|
-
t("usageCooldown"),
|
|
3211
|
-
" ",
|
|
3212
|
-
formatResetAt(entry.cooldownUntil)
|
|
3213
|
-
]
|
|
3214
|
-
});
|
|
3215
|
-
if (entry.mark === "rate-limit") return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3216
|
-
className: "cc-usagePlanStatus",
|
|
3217
|
-
children: t("usageCooldown")
|
|
3218
|
-
});
|
|
3219
|
-
return null;
|
|
3220
|
-
}
|
|
3221
2763
|
/**
|
|
3222
2764
|
* One pool account's facts (identity, totals, credits, window limits)
|
|
3223
|
-
* rendered inside
|
|
2765
|
+
* rendered inside an account row's details.
|
|
3224
2766
|
*/
|
|
3225
|
-
function AccountReport({ entry, fetchedAt, t,
|
|
2767
|
+
function AccountReport({ entry, fetchedAt, t, summaryOnly = false, headless = false }) {
|
|
3226
2768
|
const report = entry.report;
|
|
3227
2769
|
const account = report.account;
|
|
3228
2770
|
const accountName = account === void 0 ? "" : account.userName || account.name;
|
|
@@ -3235,17 +2777,13 @@ window.__ModuleLoader__.load({
|
|
|
3235
2777
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3236
2778
|
className: "cc-accountReport",
|
|
3237
2779
|
children: [
|
|
3238
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2780
|
+
!headless ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3239
2781
|
className: "cc-usageHead",
|
|
3240
2782
|
children: [
|
|
3241
2783
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h4", {
|
|
3242
2784
|
className: "cc-usageTitle",
|
|
3243
2785
|
children: entry.label
|
|
3244
2786
|
}),
|
|
3245
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(AccountMark, {
|
|
3246
|
-
entry,
|
|
3247
|
-
t
|
|
3248
|
-
}),
|
|
3249
2787
|
accountName !== "" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3250
2788
|
className: "cc-usageAccount",
|
|
3251
2789
|
children: accountName
|
|
@@ -3253,21 +2791,14 @@ window.__ModuleLoader__.load({
|
|
|
3253
2791
|
planName !== "" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3254
2792
|
className: "cc-usagePlan",
|
|
3255
2793
|
children: planName
|
|
3256
|
-
}) : null,
|
|
3257
|
-
planStatus !== "" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3258
|
-
className: "cc-usagePlanStatus",
|
|
3259
|
-
children: planStatus
|
|
3260
|
-
}) : null,
|
|
3261
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: "cc-usageMetaSpacer" }),
|
|
3262
|
-
onRemove !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
3263
|
-
type: "button",
|
|
3264
|
-
className: "cc-usageRefresh",
|
|
3265
|
-
onClick: onRemove,
|
|
3266
|
-
children: t("accountRemove")
|
|
3267
2794
|
}) : null
|
|
3268
2795
|
]
|
|
3269
|
-
}),
|
|
3270
|
-
|
|
2796
|
+
}) : null,
|
|
2797
|
+
planStatus !== "" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
2798
|
+
className: "cc-usagePlanStatus",
|
|
2799
|
+
children: planStatus
|
|
2800
|
+
}) : null,
|
|
2801
|
+
!entry.configured ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
3271
2802
|
className: "cc-usageHint",
|
|
3272
2803
|
children: t("usageUnconfigured")
|
|
3273
2804
|
}) : null,
|
|
@@ -3290,8 +2821,8 @@ window.__ModuleLoader__.load({
|
|
|
3290
2821
|
}) : null
|
|
3291
2822
|
]
|
|
3292
2823
|
}) : null,
|
|
3293
|
-
report.usage !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3294
|
-
className: "cc-usageStats",
|
|
2824
|
+
!summaryOnly && report.usage !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2825
|
+
className: "cc-usageStats cc-usageStatsActivity",
|
|
3295
2826
|
children: [
|
|
3296
2827
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(UsageStat, {
|
|
3297
2828
|
label: t("usageRequests"),
|
|
@@ -3314,8 +2845,8 @@ window.__ModuleLoader__.load({
|
|
|
3314
2845
|
})
|
|
3315
2846
|
]
|
|
3316
2847
|
}) : null,
|
|
3317
|
-
credits !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3318
|
-
className: "cc-usageStats",
|
|
2848
|
+
!summaryOnly && credits !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2849
|
+
className: "cc-usageStats cc-usageStatsBalance",
|
|
3319
2850
|
children: [
|
|
3320
2851
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(UsageStat, {
|
|
3321
2852
|
label: t("usageMonthly"),
|
|
@@ -3387,360 +2918,6 @@ window.__ModuleLoader__.load({
|
|
|
3387
2918
|
if (reason === "invalid-response") return t("usageInvalidResponseHint");
|
|
3388
2919
|
return t("usageNetworkHint");
|
|
3389
2920
|
}
|
|
3390
|
-
/** The status dot on an account tab: cooling/invalid warn, everything else ok. */
|
|
3391
|
-
function AccountTabDot({ entry }) {
|
|
3392
|
-
const cls = entry.mark === "invalid-credential" ? "cc-tabDot cc-tabDotError" : entry.mark !== "" || entry.cooldownUntil > 0 ? "cc-tabDot cc-tabDotWarn" : "cc-tabDot cc-tabDotOk";
|
|
3393
|
-
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3394
|
-
className: cls,
|
|
3395
|
-
"aria-hidden": "true"
|
|
3396
|
-
});
|
|
3397
|
-
}
|
|
3398
|
-
/**
|
|
3399
|
-
* The account-usage card: the `/commandcode` dashboard's facts rendered as
|
|
3400
|
-
* a native settings card. With several accounts the card is a carousel — a
|
|
3401
|
-
* tab strip (label + status dot) switches between accounts so the page stays
|
|
3402
|
-
* short; each account's report carries its own remove affordance (the
|
|
3403
|
-
* default account is not removable). Accounts staged for removal in the
|
|
3404
|
-
* management card are hidden here immediately. Data arrives through the
|
|
3405
|
-
* `commandcode/report` Remote; the API keys never leave the Host.
|
|
3406
|
-
*/
|
|
3407
|
-
function UsageCard({ t, usage, removingIds, removableIds, canManage, onRefresh, onRemoveAccount }) {
|
|
3408
|
-
const { loading, shouldRefresh, noKey } = usageCardState(usage);
|
|
3409
|
-
(0, react.useEffect)(() => {
|
|
3410
|
-
if (shouldRefresh) onRefresh();
|
|
3411
|
-
}, [shouldRefresh, onRefresh]);
|
|
3412
|
-
const report = usage.report;
|
|
3413
|
-
const [locallyRemoved, setLocallyRemoved] = (0, react.useState)([]);
|
|
3414
|
-
(0, react.useEffect)(() => {
|
|
3415
|
-
setLocallyRemoved([]);
|
|
3416
|
-
}, [usage.fetchedAt]);
|
|
3417
|
-
const hidden = /* @__PURE__ */ new Set([...removingIds, ...locallyRemoved]);
|
|
3418
|
-
const seenIds = /* @__PURE__ */ new Set();
|
|
3419
|
-
const entries = (report?.accounts ?? []).filter((entry) => {
|
|
3420
|
-
if (hidden.has(entry.id)) return false;
|
|
3421
|
-
if (seenIds.has(entry.id)) return false;
|
|
3422
|
-
seenIds.add(entry.id);
|
|
3423
|
-
return true;
|
|
3424
|
-
});
|
|
3425
|
-
const [selectedId, setSelectedId] = (0, react.useState)(void 0);
|
|
3426
|
-
const selected = entries.find((entry) => entry.id === selectedId) ?? entries.find((entry) => entry.active) ?? entries[0];
|
|
3427
|
-
const removeSelected = canManage && selected !== void 0 && removableIds.includes(selected.id) ? () => {
|
|
3428
|
-
const id = selected.id;
|
|
3429
|
-
setLocallyRemoved((prev) => [...prev, id]);
|
|
3430
|
-
onRemoveAccount(id);
|
|
3431
|
-
} : void 0;
|
|
3432
|
-
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3433
|
-
className: "cc-usageCard",
|
|
3434
|
-
"aria-label": t("usageTitle"),
|
|
3435
|
-
children: [
|
|
3436
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3437
|
-
className: "cc-usageHead",
|
|
3438
|
-
children: [
|
|
3439
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", {
|
|
3440
|
-
className: "cc-usageTitle",
|
|
3441
|
-
children: t("usageTitle")
|
|
3442
|
-
}),
|
|
3443
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: "cc-usageMetaSpacer" }),
|
|
3444
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
3445
|
-
type: "button",
|
|
3446
|
-
className: "cc-usageRefresh",
|
|
3447
|
-
disabled: loading,
|
|
3448
|
-
onClick: onRefresh,
|
|
3449
|
-
children: loading ? t("usageRefreshing") : t("usageRefresh")
|
|
3450
|
-
})
|
|
3451
|
-
]
|
|
3452
|
-
}),
|
|
3453
|
-
noKey ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
3454
|
-
className: "cc-usageHint",
|
|
3455
|
-
children: t("usageNoKey")
|
|
3456
|
-
}) : null,
|
|
3457
|
-
report === void 0 && loading ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
3458
|
-
className: "cc-usageHint",
|
|
3459
|
-
children: t("usageLoading")
|
|
3460
|
-
}) : null,
|
|
3461
|
-
usage.status === "error" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
3462
|
-
className: "cc-usageError",
|
|
3463
|
-
role: "status",
|
|
3464
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: [t("usageError"), usage.error !== void 0 && usage.error !== "" ? ` — ${usage.error}` : ""] })
|
|
3465
|
-
}) : null,
|
|
3466
|
-
entries.length > 1 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
3467
|
-
className: "cc-tabs",
|
|
3468
|
-
"aria-label": t("accountsTitle"),
|
|
3469
|
-
children: entries.map((entry) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
3470
|
-
type: "button",
|
|
3471
|
-
"aria-pressed": selected?.id === entry.id,
|
|
3472
|
-
className: selected?.id === entry.id ? "cc-tab cc-tabActive" : "cc-tab",
|
|
3473
|
-
onClick: () => setSelectedId(entry.id),
|
|
3474
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(AccountTabDot, { entry }), entry.label]
|
|
3475
|
-
}, entry.id))
|
|
3476
|
-
}) : null,
|
|
3477
|
-
selected !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(AccountReport, {
|
|
3478
|
-
entry: selected,
|
|
3479
|
-
fetchedAt: usage.fetchedAt,
|
|
3480
|
-
t,
|
|
3481
|
-
onRemove: removeSelected
|
|
3482
|
-
}, selected.id) : null
|
|
3483
|
-
]
|
|
3484
|
-
});
|
|
3485
|
-
}
|
|
3486
|
-
/**
|
|
3487
|
-
* One extra account row: label, key, configured badge. Saved accounts are
|
|
3488
|
-
* removed from the usage card above; a NOT-YET-SAVED addition never appears
|
|
3489
|
-
* there (the usage report is Host-side), so it keeps its own remove button —
|
|
3490
|
-
* otherwise the only way to undo a mistaken Add would be discarding every
|
|
3491
|
-
* other staged edit.
|
|
3492
|
-
*/
|
|
3493
|
-
function AccountRow({ account, disabled, t, onLabel, onKey, onToggleClear, onRemove }) {
|
|
3494
|
-
const locked = !account.writable;
|
|
3495
|
-
const [keyVisible, setKeyVisible] = (0, react.useState)(false);
|
|
3496
|
-
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3497
|
-
className: "cc-field",
|
|
3498
|
-
children: [
|
|
3499
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3500
|
-
className: "cc-fieldHead",
|
|
3501
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("label", {
|
|
3502
|
-
className: "cc-label",
|
|
3503
|
-
htmlFor: `cc-account-label-${account.id}`,
|
|
3504
|
-
children: t("accountLabel")
|
|
3505
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
3506
|
-
className: "cc-badges",
|
|
3507
|
-
children: [
|
|
3508
|
-
account.added ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3509
|
-
className: "cc-badge",
|
|
3510
|
-
children: t("unsaved")
|
|
3511
|
-
}) : null,
|
|
3512
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3513
|
-
className: account.configured ? "cc-badge" : "cc-badgeMuted",
|
|
3514
|
-
children: account.configured ? t("apiKeySet") : t("apiKeyUnset")
|
|
3515
|
-
}),
|
|
3516
|
-
account.clearStaged ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3517
|
-
className: "cc-badge cc-badgeWarn",
|
|
3518
|
-
children: t("usageKeyClearStaged")
|
|
3519
|
-
}) : null,
|
|
3520
|
-
account.configured && !account.clearStaged ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
3521
|
-
type: "button",
|
|
3522
|
-
className: "cc-reset",
|
|
3523
|
-
disabled,
|
|
3524
|
-
onClick: onToggleClear,
|
|
3525
|
-
children: t("usageKeyClear")
|
|
3526
|
-
}) : null,
|
|
3527
|
-
account.clearStaged ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
3528
|
-
type: "button",
|
|
3529
|
-
className: "cc-reset",
|
|
3530
|
-
disabled,
|
|
3531
|
-
onClick: onToggleClear,
|
|
3532
|
-
children: t("usageUndoKeyClear")
|
|
3533
|
-
}) : null,
|
|
3534
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
3535
|
-
type: "button",
|
|
3536
|
-
className: "cc-reset",
|
|
3537
|
-
disabled,
|
|
3538
|
-
onClick: () => setKeyVisible((value) => !value),
|
|
3539
|
-
children: keyVisible ? t("hide") : t("show")
|
|
3540
|
-
}),
|
|
3541
|
-
account.added ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
3542
|
-
type: "button",
|
|
3543
|
-
className: "cc-reset",
|
|
3544
|
-
disabled,
|
|
3545
|
-
onClick: onRemove,
|
|
3546
|
-
children: t("accountRemove")
|
|
3547
|
-
}) : null
|
|
3548
|
-
]
|
|
3549
|
-
})]
|
|
3550
|
-
}),
|
|
3551
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
3552
|
-
id: `cc-account-label-${account.id}`,
|
|
3553
|
-
className: "cc-input",
|
|
3554
|
-
type: "text",
|
|
3555
|
-
value: account.label,
|
|
3556
|
-
disabled,
|
|
3557
|
-
onChange: (event) => onLabel(event.target.value)
|
|
3558
|
-
}),
|
|
3559
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
3560
|
-
id: `cc-account-key-${account.id}`,
|
|
3561
|
-
className: "cc-input",
|
|
3562
|
-
type: keyVisible ? "text" : "password",
|
|
3563
|
-
autoComplete: "off",
|
|
3564
|
-
spellCheck: false,
|
|
3565
|
-
placeholder: t("accountKey"),
|
|
3566
|
-
value: account.keyText,
|
|
3567
|
-
disabled: disabled || locked,
|
|
3568
|
-
onChange: (event) => onKey(event.target.value)
|
|
3569
|
-
}),
|
|
3570
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
3571
|
-
className: "cc-hint",
|
|
3572
|
-
children: locked ? t("apiKeyLocked") : t("accountKeyHint")
|
|
3573
|
-
})
|
|
3574
|
-
]
|
|
3575
|
-
});
|
|
3576
|
-
}
|
|
3577
|
-
/** The multi-account card: the active-account selector + extra accounts in rotation order + add button. */
|
|
3578
|
-
function AccountsCard({ t, state, login, disabled, onAdd, onRemove, onLabel, onKey, onToggleClear, onActive, onActiveReset, onBeginLogin, onCancelLogin }) {
|
|
3579
|
-
const active = state.activeAccount;
|
|
3580
|
-
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3581
|
-
className: "cc-card",
|
|
3582
|
-
"aria-label": t("accountsTitle"),
|
|
3583
|
-
children: [
|
|
3584
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3585
|
-
className: "cc-field",
|
|
3586
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3587
|
-
className: "cc-fieldHead",
|
|
3588
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("label", {
|
|
3589
|
-
className: "cc-label",
|
|
3590
|
-
children: t("accountsTitle")
|
|
3591
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3592
|
-
className: "cc-badges",
|
|
3593
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
3594
|
-
type: "button",
|
|
3595
|
-
className: "cc-reset",
|
|
3596
|
-
disabled,
|
|
3597
|
-
onClick: onAdd,
|
|
3598
|
-
children: t("accountAdd")
|
|
3599
|
-
})
|
|
3600
|
-
})]
|
|
3601
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
3602
|
-
className: "cc-hint",
|
|
3603
|
-
children: t("accountsHint")
|
|
3604
|
-
})]
|
|
3605
|
-
}),
|
|
3606
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3607
|
-
className: "cc-field",
|
|
3608
|
-
children: [
|
|
3609
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3610
|
-
className: "cc-fieldHead",
|
|
3611
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("label", {
|
|
3612
|
-
className: "cc-label",
|
|
3613
|
-
htmlFor: "cc-active-account",
|
|
3614
|
-
children: t("activeAccount")
|
|
3615
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
3616
|
-
className: "cc-badges",
|
|
3617
|
-
children: [active.overridden ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3618
|
-
className: "cc-badge",
|
|
3619
|
-
children: t("overridden")
|
|
3620
|
-
}) : null, /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
3621
|
-
type: "button",
|
|
3622
|
-
className: "cc-reset",
|
|
3623
|
-
disabled,
|
|
3624
|
-
onClick: onActiveReset,
|
|
3625
|
-
children: t("reset")
|
|
3626
|
-
})]
|
|
3627
|
-
})]
|
|
3628
|
-
}),
|
|
3629
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
|
|
3630
|
-
id: "cc-active-account",
|
|
3631
|
-
className: "cc-input",
|
|
3632
|
-
value: active.text,
|
|
3633
|
-
disabled,
|
|
3634
|
-
onChange: (event) => onActive(event.target.value),
|
|
3635
|
-
children: [
|
|
3636
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
3637
|
-
value: "",
|
|
3638
|
-
children: t("activeAccountAuto")
|
|
3639
|
-
}),
|
|
3640
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
3641
|
-
value: "default",
|
|
3642
|
-
children: t("accountDefault")
|
|
3643
|
-
}),
|
|
3644
|
-
state.accounts.filter((account) => !account.added).map((account) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
3645
|
-
value: account.ref,
|
|
3646
|
-
children: account.label
|
|
3647
|
-
}, account.id))
|
|
3648
|
-
]
|
|
3649
|
-
}),
|
|
3650
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
3651
|
-
className: "cc-hint",
|
|
3652
|
-
children: t("activeAccountHint")
|
|
3653
|
-
})
|
|
3654
|
-
]
|
|
3655
|
-
}),
|
|
3656
|
-
state.accounts.map((account) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3657
|
-
className: "cc-accountEditor",
|
|
3658
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(AccountRow, {
|
|
3659
|
-
account,
|
|
3660
|
-
disabled: disabled || login.targetRef === account.ref && (login.phase === "starting" || login.phase === "waiting"),
|
|
3661
|
-
t,
|
|
3662
|
-
onLabel: (text) => onLabel(account.id, text),
|
|
3663
|
-
onKey: (text) => onKey(account.id, text),
|
|
3664
|
-
onToggleClear: () => onToggleClear(account.id),
|
|
3665
|
-
onRemove: () => onRemove(account.id)
|
|
3666
|
-
}), account.added ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
3667
|
-
className: "cc-accountLoginHint",
|
|
3668
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
3669
|
-
className: "cc-hint",
|
|
3670
|
-
children: t("accountLoginAfterSave")
|
|
3671
|
-
})
|
|
3672
|
-
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(LoginRow, {
|
|
3673
|
-
state: login,
|
|
3674
|
-
targetRef: account.ref,
|
|
3675
|
-
disabled: disabled || !account.writable || state.dirty,
|
|
3676
|
-
disabledHint: state.dirty ? t("loginSaveBefore") : void 0,
|
|
3677
|
-
t,
|
|
3678
|
-
onBegin: () => onBeginLogin(account.ref),
|
|
3679
|
-
onCancel: onCancelLogin
|
|
3680
|
-
})]
|
|
3681
|
-
}, account.id))
|
|
3682
|
-
]
|
|
3683
|
-
});
|
|
3684
|
-
}
|
|
3685
|
-
/** One model → account routing rule row. */
|
|
3686
|
-
function RuleRow({ rule, accounts, catalog, disabled, t, onModels, onAccount, onRemove }) {
|
|
3687
|
-
const targets = [{
|
|
3688
|
-
value: "default",
|
|
3689
|
-
label: t("accountDefault")
|
|
3690
|
-
}, ...accounts.filter((account) => !account.added).map((account) => ({
|
|
3691
|
-
value: account.ref,
|
|
3692
|
-
label: account.label
|
|
3693
|
-
}))];
|
|
3694
|
-
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3695
|
-
className: "cc-field",
|
|
3696
|
-
children: [
|
|
3697
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3698
|
-
className: "cc-fieldHead",
|
|
3699
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("label", {
|
|
3700
|
-
className: "cc-label",
|
|
3701
|
-
htmlFor: `cc-rule-model-${rule.id}`,
|
|
3702
|
-
children: t("ruleModel")
|
|
3703
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
3704
|
-
className: "cc-badges",
|
|
3705
|
-
children: [rule.added ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3706
|
-
className: "cc-badge",
|
|
3707
|
-
children: t("unsaved")
|
|
3708
|
-
}) : null, /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
3709
|
-
type: "button",
|
|
3710
|
-
className: "cc-reset",
|
|
3711
|
-
disabled,
|
|
3712
|
-
onClick: onRemove,
|
|
3713
|
-
children: t("ruleRemove")
|
|
3714
|
-
})]
|
|
3715
|
-
})]
|
|
3716
|
-
}),
|
|
3717
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(ModelMultiSelect, {
|
|
3718
|
-
id: `cc-rule-model-${rule.id}`,
|
|
3719
|
-
selected: rule.models,
|
|
3720
|
-
catalog,
|
|
3721
|
-
disabled,
|
|
3722
|
-
t,
|
|
3723
|
-
onSelect: onModels
|
|
3724
|
-
}),
|
|
3725
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("select", {
|
|
3726
|
-
id: `cc-rule-account-${rule.id}`,
|
|
3727
|
-
className: "cc-input",
|
|
3728
|
-
value: rule.account,
|
|
3729
|
-
disabled,
|
|
3730
|
-
onChange: (event) => onAccount(event.target.value),
|
|
3731
|
-
"aria-label": t("ruleAccount"),
|
|
3732
|
-
children: targets.map((target) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
3733
|
-
value: target.value,
|
|
3734
|
-
children: target.label
|
|
3735
|
-
}, target.value))
|
|
3736
|
-
}),
|
|
3737
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
3738
|
-
className: "cc-hint",
|
|
3739
|
-
children: t("ruleHint")
|
|
3740
|
-
})
|
|
3741
|
-
]
|
|
3742
|
-
});
|
|
3743
|
-
}
|
|
3744
2921
|
/**
|
|
3745
2922
|
* A checkbox multi-select dropdown for picking catalog models (the
|
|
3746
2923
|
* routing-rule rows and the visible-models filter share it). The trigger
|
|
@@ -3752,13 +2929,26 @@ window.__ModuleLoader__.load({
|
|
|
3752
2929
|
* longer carries still render — flagged stale — so a saved selection never
|
|
3753
2930
|
* silently loses an entry, and the VisibleModelsCard offers a one-click
|
|
3754
2931
|
* cleanup.
|
|
2932
|
+
*
|
|
2933
|
+
* With `deferCommit` the toggles collect in a local draft that is handed to
|
|
2934
|
+
* `onSelect` once, when the menu closes: the per-account picker writes
|
|
2935
|
+
* immediately, and one write per click would be a burst of document writes.
|
|
3755
2936
|
*/
|
|
3756
|
-
function ModelMultiSelect({ id, selected, catalog, disabled, t, onSelect }) {
|
|
2937
|
+
function ModelMultiSelect({ id, selected: committed, catalog, disabled, deferCommit = false, ownerOf, t, onSelect }) {
|
|
3757
2938
|
const [open, setOpen] = (0, react.useState)(false);
|
|
3758
2939
|
const [query, setQuery] = (0, react.useState)("");
|
|
2940
|
+
const [draft, setDraft] = (0, react.useState)(void 0);
|
|
2941
|
+
const selected = draft ?? committed;
|
|
3759
2942
|
(0, react.useEffect)(() => {
|
|
3760
2943
|
if (open) setQuery("");
|
|
3761
2944
|
}, [open]);
|
|
2945
|
+
const close = () => {
|
|
2946
|
+
setOpen(false);
|
|
2947
|
+
if (draft !== void 0) {
|
|
2948
|
+
setDraft(void 0);
|
|
2949
|
+
if (draft.length !== committed.length || draft.some((modelId) => !committed.includes(modelId))) onSelect(draft);
|
|
2950
|
+
}
|
|
2951
|
+
};
|
|
3762
2952
|
const tiers = (0, react.useMemo)(() => Object.fromEntries(catalog.flatMap((model) => model.tier === void 0 ? [] : [[model.id, model.tier]])), [catalog]);
|
|
3763
2953
|
const options = buildModelSelectOptions(catalog, selected, query);
|
|
3764
2954
|
const items = groupModelSelectOptions(options, (modelId) => tierHeadingFor(modelId, tiers)).flatMap((group) => [...group.heading === void 0 ? [] : [{
|
|
@@ -3784,15 +2974,21 @@ window.__ModuleLoader__.load({
|
|
|
3784
2974
|
option.stale ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3785
2975
|
className: "cc-badge",
|
|
3786
2976
|
children: t("modelStale")
|
|
2977
|
+
}) : null,
|
|
2978
|
+
!selected.includes(option.value) && ownerOf?.(option.value) !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2979
|
+
className: "cc-badgeMuted",
|
|
2980
|
+
children: t("accountModelOwner", { name: ownerOf(option.value) })
|
|
3787
2981
|
}) : null
|
|
3788
2982
|
]
|
|
3789
2983
|
})
|
|
3790
2984
|
}))]);
|
|
3791
2985
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Menu, {
|
|
3792
2986
|
open,
|
|
3793
|
-
onClose:
|
|
2987
|
+
onClose: close,
|
|
3794
2988
|
onSelect: (modelId) => {
|
|
3795
|
-
|
|
2989
|
+
const next = toggleModelSelection(selected, modelId);
|
|
2990
|
+
if (deferCommit) setDraft(next);
|
|
2991
|
+
else onSelect(next);
|
|
3796
2992
|
},
|
|
3797
2993
|
selectedIds: selected,
|
|
3798
2994
|
items,
|
|
@@ -3807,14 +3003,14 @@ window.__ModuleLoader__.load({
|
|
|
3807
3003
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
3808
3004
|
id,
|
|
3809
3005
|
type: "button",
|
|
3810
|
-
className: "cc-
|
|
3006
|
+
className: "cc-selector",
|
|
3811
3007
|
disabled: disabled || catalog.length === 0,
|
|
3812
|
-
onClick: () =>
|
|
3008
|
+
onClick: () => open ? close() : setOpen(true),
|
|
3813
3009
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3814
|
-
className: "cc-
|
|
3815
|
-
children: selected.length === 0 ? t("
|
|
3010
|
+
className: "cc-selectorText",
|
|
3011
|
+
children: selected.length === 0 ? t("modelPick") : t("modelCount", { count: selected.length })
|
|
3816
3012
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3817
|
-
className: "cc-
|
|
3013
|
+
className: "cc-selectorCaret",
|
|
3818
3014
|
"aria-hidden": "true"
|
|
3819
3015
|
})]
|
|
3820
3016
|
}), open ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
@@ -3830,133 +3026,1014 @@ window.__ModuleLoader__.load({
|
|
|
3830
3026
|
})
|
|
3831
3027
|
});
|
|
3832
3028
|
}
|
|
3833
|
-
/** The model
|
|
3834
|
-
function
|
|
3835
|
-
|
|
3836
|
-
|
|
3837
|
-
"
|
|
3029
|
+
/** The visible-model filter row: an allowlist over the catalog. Empty = show all. */
|
|
3030
|
+
function VisibleModelsRow({ t, state, disabled, onSelect, onClear }) {
|
|
3031
|
+
const count = state.visibleModels.length;
|
|
3032
|
+
const pickT = (key, params) => {
|
|
3033
|
+
if (key === "modelPick") return t("visibleModelsPick");
|
|
3034
|
+
if (key === "modelCount") return t("visibleModelsCount", params);
|
|
3035
|
+
return t(key, params);
|
|
3036
|
+
};
|
|
3037
|
+
const readiness = {
|
|
3038
|
+
catalogIds: state.catalogModels.map((model) => model.id),
|
|
3039
|
+
catalogFailed: state.catalogFailed
|
|
3040
|
+
};
|
|
3041
|
+
const staleIds = staleModelIds(state.visibleModels, readiness);
|
|
3042
|
+
const catalogReady = catalogIsReady(readiness);
|
|
3043
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SettingRow, {
|
|
3044
|
+
title: t("visibleModelsTitle"),
|
|
3045
|
+
titleFor: "cc-visible-models",
|
|
3046
|
+
description: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", { children: t("visibleModelsHint") }), staleIds.length > 0 && catalogReady ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", { children: t("visibleModelsStaleHint", { count: staleIds.length }) }) : null] }),
|
|
3047
|
+
error: state.catalogFailed ? t("modelCatalogFailed") : void 0,
|
|
3048
|
+
control: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
|
|
3049
|
+
catalogReady && staleIds.length > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
3050
|
+
type: "button",
|
|
3051
|
+
className: "cc-linkButton",
|
|
3052
|
+
disabled,
|
|
3053
|
+
onClick: () => onSelect(state.visibleModels.filter((id) => !staleIds.includes(id))),
|
|
3054
|
+
children: t("visibleModelsCleanStale", { count: staleIds.length })
|
|
3055
|
+
}) : null,
|
|
3056
|
+
count > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
3057
|
+
type: "button",
|
|
3058
|
+
className: "cc-linkButton",
|
|
3059
|
+
disabled,
|
|
3060
|
+
onClick: onClear,
|
|
3061
|
+
children: t("visibleModelsShowAll")
|
|
3062
|
+
}) : null,
|
|
3063
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(ModelMultiSelect, {
|
|
3064
|
+
id: "cc-visible-models",
|
|
3065
|
+
selected: state.visibleModels,
|
|
3066
|
+
catalog: state.catalogModels,
|
|
3067
|
+
disabled,
|
|
3068
|
+
t: pickT,
|
|
3069
|
+
onSelect
|
|
3070
|
+
})
|
|
3071
|
+
] })
|
|
3072
|
+
});
|
|
3073
|
+
}
|
|
3074
|
+
/**
|
|
3075
|
+
* One titled group of rows. No card surface: like the harness's own settings
|
|
3076
|
+
* pages, a group is a heading over hairline-separated rows.
|
|
3077
|
+
*/
|
|
3078
|
+
function SettingsGroup({ title, action, children }) {
|
|
3079
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
|
|
3080
|
+
className: "cc-group",
|
|
3081
|
+
"aria-label": title,
|
|
3082
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3083
|
+
className: "cc-groupHead",
|
|
3084
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", {
|
|
3085
|
+
className: "cc-groupTitle",
|
|
3086
|
+
children: title
|
|
3087
|
+
}), action]
|
|
3088
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
3089
|
+
className: "cc-rows",
|
|
3090
|
+
children
|
|
3091
|
+
})]
|
|
3092
|
+
});
|
|
3093
|
+
}
|
|
3094
|
+
/**
|
|
3095
|
+
* Models: the plan filter and the allowlist sit together because both decide
|
|
3096
|
+
* what the model picker lists (the allowlist applies after the plan filter).
|
|
3097
|
+
*/
|
|
3098
|
+
function ModelsCard({ state, disabled, t, onEdit, onReset, onSelect, onClear }) {
|
|
3099
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(SettingsGroup, {
|
|
3100
|
+
title: t("modelsTitle"),
|
|
3101
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(ToggleField, {
|
|
3102
|
+
id: "cc-filter-models-by-plan",
|
|
3103
|
+
label: t("filterModelsByPlan"),
|
|
3104
|
+
hint: t("filterModelsByPlanHint"),
|
|
3105
|
+
state: state.filterModelsByPlan,
|
|
3106
|
+
disabled,
|
|
3107
|
+
defaultChecked: true,
|
|
3108
|
+
onEdit: (text) => onEdit("filterModelsByPlan", text),
|
|
3109
|
+
onReset: () => onReset("filterModelsByPlan"),
|
|
3110
|
+
t
|
|
3111
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(VisibleModelsRow, {
|
|
3112
|
+
t,
|
|
3113
|
+
state,
|
|
3114
|
+
disabled,
|
|
3115
|
+
onSelect,
|
|
3116
|
+
onClear
|
|
3117
|
+
})]
|
|
3118
|
+
});
|
|
3119
|
+
}
|
|
3120
|
+
/**
|
|
3121
|
+
* Privacy & security: the two opt-ins that change where data goes. ZDR
|
|
3122
|
+
* restricts which upstream serves a request; the command guard sends command
|
|
3123
|
+
* text to a decision model with no ZDR upstream and can skip an approval the
|
|
3124
|
+
* user would otherwise have seen, so both start off.
|
|
3125
|
+
*/
|
|
3126
|
+
function PrivacyCard({ state, disabled, t, onEdit, onReset }) {
|
|
3127
|
+
const showGuardLevel = state.commandGuard.text === "true" || state.commandGuardLevel.overridden || state.commandGuardLevel.clear;
|
|
3128
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(SettingsGroup, {
|
|
3129
|
+
title: t("privacyTitle"),
|
|
3130
|
+
children: [
|
|
3131
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(ToggleField, {
|
|
3132
|
+
id: "cc-zdr",
|
|
3133
|
+
label: t("zdr"),
|
|
3134
|
+
hint: t("zdrHint"),
|
|
3135
|
+
state: state.zdr,
|
|
3136
|
+
disabled,
|
|
3137
|
+
defaultChecked: false,
|
|
3138
|
+
onEdit: (text) => onEdit("zdr", text),
|
|
3139
|
+
onReset: () => onReset("zdr"),
|
|
3140
|
+
t
|
|
3141
|
+
}),
|
|
3142
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(ToggleField, {
|
|
3143
|
+
id: "cc-command-guard",
|
|
3144
|
+
label: t("commandGuard"),
|
|
3145
|
+
hint: t("commandGuardHint"),
|
|
3146
|
+
state: state.commandGuard,
|
|
3147
|
+
disabled,
|
|
3148
|
+
defaultChecked: false,
|
|
3149
|
+
onEdit: (text) => onEdit("commandGuard", text),
|
|
3150
|
+
onReset: () => onReset("commandGuard"),
|
|
3151
|
+
t
|
|
3152
|
+
}),
|
|
3153
|
+
showGuardLevel ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SegmentedField, {
|
|
3154
|
+
className: "cc-rowNested",
|
|
3155
|
+
label: t("commandGuardLevel"),
|
|
3156
|
+
hint: t("commandGuardLevelHint"),
|
|
3157
|
+
state: state.commandGuardLevel,
|
|
3158
|
+
disabled,
|
|
3159
|
+
options: [
|
|
3160
|
+
{
|
|
3161
|
+
value: "high",
|
|
3162
|
+
label: t("commandGuardLevelHigh")
|
|
3163
|
+
},
|
|
3164
|
+
{
|
|
3165
|
+
value: "medium",
|
|
3166
|
+
label: t("commandGuardLevelMedium")
|
|
3167
|
+
},
|
|
3168
|
+
{
|
|
3169
|
+
value: "low",
|
|
3170
|
+
label: t("commandGuardLevelLow")
|
|
3171
|
+
}
|
|
3172
|
+
],
|
|
3173
|
+
defaultValue: COMMAND_GUARD_DEFAULT_LEVEL_CHOICE,
|
|
3174
|
+
onEdit: (text) => onEdit("commandGuardLevel", text),
|
|
3175
|
+
onReset: () => onReset("commandGuardLevel"),
|
|
3176
|
+
t
|
|
3177
|
+
}) : null
|
|
3178
|
+
]
|
|
3179
|
+
});
|
|
3180
|
+
}
|
|
3181
|
+
/** Integrations & display: surfaces outside chat that reuse this provider. */
|
|
3182
|
+
function IntegrationsCard({ state, disabled, t, onEdit, onReset }) {
|
|
3183
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(SettingsGroup, {
|
|
3184
|
+
title: t("integrationsTitle"),
|
|
3185
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(ToggleField, {
|
|
3186
|
+
id: "cc-web-search",
|
|
3187
|
+
label: t("webSearch"),
|
|
3188
|
+
hint: t("webSearchHint"),
|
|
3189
|
+
state: state.webSearch,
|
|
3190
|
+
disabled,
|
|
3191
|
+
defaultChecked: true,
|
|
3192
|
+
onEdit: (text) => onEdit("webSearch", text),
|
|
3193
|
+
onReset: () => onReset("webSearch"),
|
|
3194
|
+
t
|
|
3195
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ToggleField, {
|
|
3196
|
+
id: "cc-show-sidebar-quota",
|
|
3197
|
+
label: t("showSidebarQuota"),
|
|
3198
|
+
hint: t("showSidebarQuotaHint"),
|
|
3199
|
+
state: state.showSidebarQuota,
|
|
3200
|
+
disabled,
|
|
3201
|
+
defaultChecked: false,
|
|
3202
|
+
onEdit: (text) => onEdit("showSidebarQuota", text),
|
|
3203
|
+
onReset: () => onReset("showSidebarQuota"),
|
|
3204
|
+
t
|
|
3205
|
+
})]
|
|
3206
|
+
});
|
|
3207
|
+
}
|
|
3208
|
+
/**
|
|
3209
|
+
* The collapsed "Advanced" card: the API base and the network limits. Starts
|
|
3210
|
+
* collapsed on every visit; while collapsed, a badge names the customized
|
|
3211
|
+
* count and an invalid number (which blocks save) is surfaced on the header.
|
|
3212
|
+
*/
|
|
3213
|
+
function AdvancedSection({ state, disabled, t, onEdit, onReset }) {
|
|
3214
|
+
const [expanded, setExpanded] = (0, react.useState)(false);
|
|
3215
|
+
const overridden = ADVANCED_FIELDS.filter((field) => state[field].overridden).length;
|
|
3216
|
+
const invalid = ADVANCED_FIELDS.some((field) => state[field].invalid);
|
|
3217
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
|
|
3218
|
+
className: "cc-group",
|
|
3219
|
+
"aria-label": t("advancedSettings"),
|
|
3220
|
+
children: [
|
|
3221
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
3222
|
+
type: "button",
|
|
3223
|
+
className: "cc-groupHead cc-disclosure",
|
|
3224
|
+
"aria-expanded": expanded,
|
|
3225
|
+
"aria-controls": "cc-advanced-body",
|
|
3226
|
+
onClick: () => setExpanded((value) => !value),
|
|
3227
|
+
children: [
|
|
3228
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3229
|
+
className: "cc-groupTitle",
|
|
3230
|
+
children: t("advancedSettings")
|
|
3231
|
+
}),
|
|
3232
|
+
overridden > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3233
|
+
className: "cc-badge",
|
|
3234
|
+
children: overridden === 1 ? t("advancedOverriddenOne") : t("advancedOverriddenMany", { count: overridden })
|
|
3235
|
+
}) : null,
|
|
3236
|
+
!expanded && invalid ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3237
|
+
className: "cc-badge cc-badgeWarn",
|
|
3238
|
+
children: t("advancedInvalid")
|
|
3239
|
+
}) : null,
|
|
3240
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: "cc-spacer" }),
|
|
3241
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3242
|
+
className: expanded ? "cc-chevron cc-chevronUp" : "cc-chevron",
|
|
3243
|
+
"aria-hidden": "true"
|
|
3244
|
+
})
|
|
3245
|
+
]
|
|
3246
|
+
}),
|
|
3247
|
+
expanded ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3248
|
+
id: "cc-advanced-body",
|
|
3249
|
+
className: "cc-rows",
|
|
3250
|
+
children: [
|
|
3251
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
3252
|
+
className: "cc-groupDesc",
|
|
3253
|
+
children: t("advancedSettingsHint")
|
|
3254
|
+
}),
|
|
3255
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Field, {
|
|
3256
|
+
id: "cc-api-base",
|
|
3257
|
+
label: t("apiBase"),
|
|
3258
|
+
hint: t("apiBaseHint"),
|
|
3259
|
+
state: state.apiBase,
|
|
3260
|
+
disabled,
|
|
3261
|
+
wide: true,
|
|
3262
|
+
onEdit: (text) => onEdit("apiBase", text),
|
|
3263
|
+
onReset: () => onReset("apiBase"),
|
|
3264
|
+
t
|
|
3265
|
+
}),
|
|
3266
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Field, {
|
|
3267
|
+
id: "cc-request-timeout",
|
|
3268
|
+
label: t("requestTimeoutMs"),
|
|
3269
|
+
hint: t("requestTimeoutMsHint"),
|
|
3270
|
+
state: state.requestTimeoutMs,
|
|
3271
|
+
disabled,
|
|
3272
|
+
numeric: true,
|
|
3273
|
+
onEdit: (text) => onEdit("requestTimeoutMs", text),
|
|
3274
|
+
onReset: () => onReset("requestTimeoutMs"),
|
|
3275
|
+
t
|
|
3276
|
+
}),
|
|
3277
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Field, {
|
|
3278
|
+
id: "cc-stream-idle-timeout",
|
|
3279
|
+
label: t("streamIdleTimeoutMs"),
|
|
3280
|
+
hint: t("streamIdleTimeoutMsHint"),
|
|
3281
|
+
state: state.streamIdleTimeoutMs,
|
|
3282
|
+
disabled,
|
|
3283
|
+
numeric: true,
|
|
3284
|
+
onEdit: (text) => onEdit("streamIdleTimeoutMs", text),
|
|
3285
|
+
onReset: () => onReset("streamIdleTimeoutMs"),
|
|
3286
|
+
t
|
|
3287
|
+
}),
|
|
3288
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Field, {
|
|
3289
|
+
id: "cc-transport-max-retries",
|
|
3290
|
+
label: t("transportMaxRetries"),
|
|
3291
|
+
hint: t("transportMaxRetriesHint"),
|
|
3292
|
+
state: state.transportMaxRetries,
|
|
3293
|
+
disabled,
|
|
3294
|
+
numeric: true,
|
|
3295
|
+
onEdit: (text) => onEdit("transportMaxRetries", text),
|
|
3296
|
+
onReset: () => onReset("transportMaxRetries"),
|
|
3297
|
+
t
|
|
3298
|
+
})
|
|
3299
|
+
]
|
|
3300
|
+
}) : null,
|
|
3301
|
+
expanded && invalid ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
3302
|
+
className: "cc-rowError",
|
|
3303
|
+
role: "status",
|
|
3304
|
+
children: t("advancedInvalid")
|
|
3305
|
+
}) : null
|
|
3306
|
+
]
|
|
3307
|
+
});
|
|
3308
|
+
}
|
|
3309
|
+
/**
|
|
3310
|
+
* Merge the controller's accounts with the usage report. The default slot is
|
|
3311
|
+
* always first; report entries this page cannot manage (literal keys from the
|
|
3312
|
+
* composition config) still render, read-only, so the list names every
|
|
3313
|
+
* account the pool rotates through.
|
|
3314
|
+
*/
|
|
3315
|
+
function accountRows(state, usage, t) {
|
|
3316
|
+
const report = new Map((usage.report?.accounts ?? []).map((entry) => [entry.id, entry]));
|
|
3317
|
+
const defaultUsage = report.get("default");
|
|
3318
|
+
const rows = [{
|
|
3319
|
+
id: "default",
|
|
3320
|
+
label: t("accountDefault"),
|
|
3321
|
+
ref: void 0,
|
|
3322
|
+
isDefault: true,
|
|
3323
|
+
managed: true,
|
|
3324
|
+
configured: state.apiKeyConfigured || defaultUsage?.configured === true,
|
|
3325
|
+
storedKey: state.apiKeyConfigured,
|
|
3326
|
+
keyWritable: state.apiKeyWritable,
|
|
3327
|
+
usage: defaultUsage
|
|
3328
|
+
}];
|
|
3329
|
+
for (const account of state.accounts) {
|
|
3330
|
+
const entry = report.get(account.id);
|
|
3331
|
+
rows.push({
|
|
3332
|
+
id: account.id,
|
|
3333
|
+
label: account.label,
|
|
3334
|
+
ref: account.ref,
|
|
3335
|
+
isDefault: false,
|
|
3336
|
+
managed: true,
|
|
3337
|
+
configured: account.configured || entry?.configured === true,
|
|
3338
|
+
storedKey: account.configured,
|
|
3339
|
+
keyWritable: account.writable,
|
|
3340
|
+
usage: entry
|
|
3341
|
+
});
|
|
3342
|
+
}
|
|
3343
|
+
const known = new Set(rows.map((row) => row.id));
|
|
3344
|
+
for (const entry of usage.report?.accounts ?? []) {
|
|
3345
|
+
if (known.has(entry.id)) continue;
|
|
3346
|
+
known.add(entry.id);
|
|
3347
|
+
rows.push({
|
|
3348
|
+
id: entry.id,
|
|
3349
|
+
label: entry.label,
|
|
3350
|
+
ref: void 0,
|
|
3351
|
+
isDefault: false,
|
|
3352
|
+
managed: false,
|
|
3353
|
+
configured: entry.configured,
|
|
3354
|
+
storedKey: false,
|
|
3355
|
+
keyWritable: false,
|
|
3356
|
+
usage: entry
|
|
3357
|
+
});
|
|
3358
|
+
}
|
|
3359
|
+
return rows;
|
|
3360
|
+
}
|
|
3361
|
+
/** The row's status dot: error for a rejected key, warning while cooling down. */
|
|
3362
|
+
function statusDotClass(row) {
|
|
3363
|
+
const entry = row.usage;
|
|
3364
|
+
if (!row.configured || entry?.mark === "invalid-credential") return "cc-tabDot cc-tabDotError";
|
|
3365
|
+
if (entry !== void 0 && (entry.mark !== "" || entry.cooldownUntil > 0)) return "cc-tabDot cc-tabDotWarn";
|
|
3366
|
+
return "cc-tabDot cc-tabDotOk";
|
|
3367
|
+
}
|
|
3368
|
+
/** One compact quota meter for a row's summary line. */
|
|
3369
|
+
function MiniMeter({ label, limit }) {
|
|
3370
|
+
const ratio = windowRatio(limit.used, limit.cap);
|
|
3371
|
+
const percent = limit.cap > 0 ? `${Math.round(ratio * 100)}%` : formatMoney(limit.used);
|
|
3372
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
3373
|
+
className: "cc-miniMeter",
|
|
3374
|
+
title: limit.cap > 0 ? `${formatMoney(limit.used)} / ${formatMoney(limit.cap)}` : void 0,
|
|
3375
|
+
children: [
|
|
3376
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3377
|
+
className: "cc-miniMeterLabel",
|
|
3378
|
+
children: label
|
|
3379
|
+
}),
|
|
3380
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3381
|
+
className: "cc-miniMeterTrack",
|
|
3382
|
+
"aria-hidden": "true",
|
|
3383
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3384
|
+
className: limit.exceeded ? "cc-miniMeterFill cc-usageBarFillWarn" : "cc-miniMeterFill",
|
|
3385
|
+
style: { width: `${ratio * 100}%` }
|
|
3386
|
+
})
|
|
3387
|
+
}),
|
|
3388
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3389
|
+
className: "cc-miniMeterValue",
|
|
3390
|
+
children: percent
|
|
3391
|
+
})
|
|
3392
|
+
]
|
|
3393
|
+
});
|
|
3394
|
+
}
|
|
3395
|
+
/**
|
|
3396
|
+
* The monthly credit balance for a row's summary line: the billing endpoint's
|
|
3397
|
+
* `monthlyCredits` is what is LEFT, shown as the amount alone.
|
|
3398
|
+
*/
|
|
3399
|
+
function MonthlyBalance({ label, remaining }) {
|
|
3400
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
3401
|
+
className: "cc-miniMeter",
|
|
3402
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3403
|
+
className: "cc-miniMeterLabel",
|
|
3404
|
+
children: label
|
|
3405
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3406
|
+
className: "cc-miniMeterValue",
|
|
3407
|
+
children: formatMoney(Math.max(0, remaining))
|
|
3408
|
+
})]
|
|
3409
|
+
});
|
|
3410
|
+
}
|
|
3411
|
+
/** A single-input inline form (paste key, rename) with confirm/cancel. */
|
|
3412
|
+
function InlineInput({ id, label, secret, initial, placeholder, confirmLabel, disabled, t, onSubmit, onCancel }) {
|
|
3413
|
+
const [value, setValue] = (0, react.useState)(initial ?? "");
|
|
3414
|
+
const [visible, setVisible] = (0, react.useState)(false);
|
|
3415
|
+
const ready = value.trim() !== "";
|
|
3416
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("form", {
|
|
3417
|
+
className: "cc-inlineForm",
|
|
3418
|
+
onSubmit: (event) => {
|
|
3419
|
+
event.preventDefault();
|
|
3420
|
+
if (ready && !disabled) onSubmit(value);
|
|
3421
|
+
},
|
|
3422
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
3423
|
+
id,
|
|
3424
|
+
className: "cc-input",
|
|
3425
|
+
type: secret && !visible ? "password" : "text",
|
|
3426
|
+
"aria-label": label,
|
|
3427
|
+
autoComplete: "off",
|
|
3428
|
+
spellCheck: false,
|
|
3429
|
+
autoFocus: true,
|
|
3430
|
+
placeholder,
|
|
3431
|
+
value,
|
|
3432
|
+
disabled,
|
|
3433
|
+
onChange: (event) => setValue(event.target.value),
|
|
3434
|
+
onKeyDown: (event) => {
|
|
3435
|
+
if (event.key === "Escape") onCancel();
|
|
3436
|
+
}
|
|
3437
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3438
|
+
className: "cc-inlineActions",
|
|
3439
|
+
children: [
|
|
3440
|
+
secret && value !== "" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
3441
|
+
type: "button",
|
|
3442
|
+
className: "cc-reset",
|
|
3443
|
+
onClick: () => setVisible((shown) => !shown),
|
|
3444
|
+
children: visible ? t("hide") : t("show")
|
|
3445
|
+
}) : null,
|
|
3446
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: "cc-spacer" }),
|
|
3447
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
3448
|
+
variant: "ghost",
|
|
3449
|
+
size: "sm",
|
|
3450
|
+
type: "button",
|
|
3451
|
+
onClick: onCancel,
|
|
3452
|
+
children: t("cancel")
|
|
3453
|
+
}),
|
|
3454
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
3455
|
+
variant: "primary",
|
|
3456
|
+
size: "sm",
|
|
3457
|
+
type: "submit",
|
|
3458
|
+
disabled: !ready || disabled,
|
|
3459
|
+
children: confirmLabel
|
|
3460
|
+
})
|
|
3461
|
+
]
|
|
3462
|
+
})]
|
|
3463
|
+
});
|
|
3464
|
+
}
|
|
3465
|
+
/** A destructive confirmation bar shown in place of a menu action. */
|
|
3466
|
+
function ConfirmBar({ text, confirmLabel, disabled, t, onConfirm, onCancel }) {
|
|
3467
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3468
|
+
className: "cc-confirmBar",
|
|
3469
|
+
role: "alertdialog",
|
|
3470
|
+
"aria-label": text,
|
|
3471
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
3472
|
+
className: "cc-confirmText",
|
|
3473
|
+
children: text
|
|
3474
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3475
|
+
className: "cc-inlineActions",
|
|
3476
|
+
children: [
|
|
3477
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: "cc-spacer" }),
|
|
3478
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
3479
|
+
variant: "ghost",
|
|
3480
|
+
size: "sm",
|
|
3481
|
+
onClick: onCancel,
|
|
3482
|
+
children: t("cancel")
|
|
3483
|
+
}),
|
|
3484
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
3485
|
+
variant: "outline",
|
|
3486
|
+
size: "sm",
|
|
3487
|
+
className: "cc-dangerButton",
|
|
3488
|
+
disabled,
|
|
3489
|
+
onClick: onConfirm,
|
|
3490
|
+
children: confirmLabel
|
|
3491
|
+
})
|
|
3492
|
+
]
|
|
3493
|
+
})]
|
|
3494
|
+
});
|
|
3495
|
+
}
|
|
3496
|
+
/** One account row: identity, status, quota summary, actions and expandable details. */
|
|
3497
|
+
function AccountItem({ row, rows, state, usage, login, disabled, pinned, t, actions }) {
|
|
3498
|
+
const [expanded, setExpanded] = (0, react.useState)(false);
|
|
3499
|
+
const [menuOpen, setMenuOpen] = (0, react.useState)(false);
|
|
3500
|
+
const [mode, setMode] = (0, react.useState)(void 0);
|
|
3501
|
+
const entry = row.usage;
|
|
3502
|
+
const report = entry?.report;
|
|
3503
|
+
const accountName = report?.account === void 0 ? "" : report.account.userName || report.account.name;
|
|
3504
|
+
const planName = report?.plan?.name ?? "";
|
|
3505
|
+
const credits = report?.credits;
|
|
3506
|
+
const monthlyReported = credits !== void 0 && credits.monthlyReported !== false;
|
|
3507
|
+
const multi = rows.length > 1;
|
|
3508
|
+
const loginTarget = row.ref;
|
|
3509
|
+
const { visible: loginState, busyElsewhere } = loginStateForTarget(login, loginTarget);
|
|
3510
|
+
const loginBusy = loginState.phase === "starting" || loginState.phase === "waiting";
|
|
3511
|
+
const showLogin = loginState.phase !== "idle";
|
|
3512
|
+
const locked = disabled || !row.managed;
|
|
3513
|
+
const keyActions = !locked && row.keyWritable;
|
|
3514
|
+
const items = [];
|
|
3515
|
+
if (row.managed && multi) items.push(pinned ? {
|
|
3516
|
+
id: "unpin",
|
|
3517
|
+
label: t("accountActionUnpin"),
|
|
3518
|
+
disabled: locked
|
|
3519
|
+
} : {
|
|
3520
|
+
id: "pin",
|
|
3521
|
+
label: t("accountActionPin"),
|
|
3522
|
+
disabled: locked || !row.configured
|
|
3523
|
+
});
|
|
3524
|
+
if (row.managed) {
|
|
3525
|
+
if (items.length > 0) items.push({
|
|
3526
|
+
type: "separator",
|
|
3527
|
+
id: "sep-key"
|
|
3528
|
+
});
|
|
3529
|
+
items.push({
|
|
3530
|
+
id: "login",
|
|
3531
|
+
label: t("accountActionLogin"),
|
|
3532
|
+
disabled: !keyActions || loginBusy || busyElsewhere
|
|
3533
|
+
}, {
|
|
3534
|
+
id: "key",
|
|
3535
|
+
label: t("accountActionKey"),
|
|
3536
|
+
disabled: !keyActions
|
|
3537
|
+
});
|
|
3538
|
+
if (!row.isDefault) items.push({
|
|
3539
|
+
id: "rename",
|
|
3540
|
+
label: t("accountActionRename"),
|
|
3541
|
+
disabled: locked
|
|
3542
|
+
});
|
|
3543
|
+
if (row.isDefault && row.storedKey) items.push({
|
|
3544
|
+
type: "separator",
|
|
3545
|
+
id: "sep-danger"
|
|
3546
|
+
}, {
|
|
3547
|
+
id: "clearKey",
|
|
3548
|
+
label: t("accountActionClearKey"),
|
|
3549
|
+
disabled: !keyActions,
|
|
3550
|
+
danger: true
|
|
3551
|
+
});
|
|
3552
|
+
if (!row.isDefault) items.push({
|
|
3553
|
+
type: "separator",
|
|
3554
|
+
id: "sep-danger"
|
|
3555
|
+
}, {
|
|
3556
|
+
id: "remove",
|
|
3557
|
+
label: t("accountActionRemove"),
|
|
3558
|
+
disabled: locked,
|
|
3559
|
+
danger: true
|
|
3560
|
+
});
|
|
3561
|
+
}
|
|
3562
|
+
const onAction = (id) => {
|
|
3563
|
+
setMenuOpen(false);
|
|
3564
|
+
if (id === "pin") actions.setActive(row.id);
|
|
3565
|
+
else if (id === "unpin") actions.setActive("");
|
|
3566
|
+
else if (id === "login") actions.beginLogin(loginTarget);
|
|
3567
|
+
else if (id === "key" || id === "rename" || id === "remove" || id === "clearKey") setMode(id);
|
|
3568
|
+
};
|
|
3569
|
+
const models = state.accountModels[row.id] ?? [];
|
|
3570
|
+
const ownerOf = (modelId) => {
|
|
3571
|
+
for (const [account, list] of Object.entries(state.accountModels)) {
|
|
3572
|
+
if (account === row.id || !list.includes(modelId)) continue;
|
|
3573
|
+
return rows.find((candidate) => candidate.id === account)?.label ?? account;
|
|
3574
|
+
}
|
|
3575
|
+
};
|
|
3576
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3577
|
+
className: row.usage?.active ? "cc-accountItem cc-accountItemActive" : "cc-accountItem",
|
|
3578
|
+
children: [
|
|
3579
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3580
|
+
className: "cc-accountHead",
|
|
3581
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
3582
|
+
type: "button",
|
|
3583
|
+
className: "cc-accountToggle",
|
|
3584
|
+
"aria-expanded": expanded,
|
|
3585
|
+
"aria-controls": `cc-account-${row.id}-details`,
|
|
3586
|
+
onClick: () => setExpanded((value) => !value),
|
|
3587
|
+
children: [
|
|
3588
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3589
|
+
className: statusDotClass(row),
|
|
3590
|
+
"aria-hidden": "true"
|
|
3591
|
+
}),
|
|
3592
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3593
|
+
className: "cc-accountName",
|
|
3594
|
+
children: row.label
|
|
3595
|
+
}),
|
|
3596
|
+
accountName !== "" && accountName !== row.label ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3597
|
+
className: "cc-usageAccount",
|
|
3598
|
+
children: accountName
|
|
3599
|
+
}) : null,
|
|
3600
|
+
planName !== "" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3601
|
+
className: "cc-usagePlan",
|
|
3602
|
+
children: planName
|
|
3603
|
+
}) : null,
|
|
3604
|
+
entry?.active && multi ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3605
|
+
className: "cc-badge",
|
|
3606
|
+
children: t("usageActive")
|
|
3607
|
+
}) : null,
|
|
3608
|
+
pinned ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3609
|
+
className: "cc-badge",
|
|
3610
|
+
children: t("accountStatusPinned")
|
|
3611
|
+
}) : null,
|
|
3612
|
+
!row.configured ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3613
|
+
className: "cc-badgeMuted",
|
|
3614
|
+
children: t("apiKeyUnset")
|
|
3615
|
+
}) : null,
|
|
3616
|
+
entry?.mark === "invalid-credential" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3617
|
+
className: "cc-usagePlanStatus",
|
|
3618
|
+
children: t("usageInvalidKey")
|
|
3619
|
+
}) : null,
|
|
3620
|
+
entry !== void 0 && entry.mark !== "invalid-credential" && (entry.cooldownUntil > 0 || entry.mark === "rate-limit") ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
3621
|
+
className: "cc-usagePlanStatus",
|
|
3622
|
+
children: [t("usageCooldown"), entry.cooldownUntil > 0 ? ` ${formatResetAt(entry.cooldownUntil)}` : ""]
|
|
3623
|
+
}) : null,
|
|
3624
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: "cc-spacer" }),
|
|
3625
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3626
|
+
className: expanded ? "cc-chevron cc-chevronUp" : "cc-chevron",
|
|
3627
|
+
"aria-hidden": "true"
|
|
3628
|
+
})
|
|
3629
|
+
]
|
|
3630
|
+
}), items.length > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Menu, {
|
|
3631
|
+
open: menuOpen,
|
|
3632
|
+
onClose: () => setMenuOpen(false),
|
|
3633
|
+
onSelect: onAction,
|
|
3634
|
+
items,
|
|
3635
|
+
align: "end",
|
|
3636
|
+
portal: true,
|
|
3637
|
+
anchor: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
3638
|
+
type: "button",
|
|
3639
|
+
className: "cc-iconButton",
|
|
3640
|
+
"aria-label": `${row.label} — ${t("accountActions")}`,
|
|
3641
|
+
title: t("accountActions"),
|
|
3642
|
+
"aria-haspopup": "menu",
|
|
3643
|
+
"aria-expanded": menuOpen,
|
|
3644
|
+
onClick: () => setMenuOpen((value) => !value),
|
|
3645
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3646
|
+
className: "cc-kebab",
|
|
3647
|
+
"aria-hidden": "true"
|
|
3648
|
+
})
|
|
3649
|
+
})
|
|
3650
|
+
}) : null]
|
|
3651
|
+
}),
|
|
3652
|
+
credits !== void 0 && (credits.fiveHour !== void 0 || credits.weekly !== void 0 || monthlyReported) ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3653
|
+
className: "cc-accountMeters",
|
|
3654
|
+
children: [
|
|
3655
|
+
credits.fiveHour !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(MiniMeter, {
|
|
3656
|
+
label: t("usageFiveHour"),
|
|
3657
|
+
limit: credits.fiveHour
|
|
3658
|
+
}) : null,
|
|
3659
|
+
credits.weekly !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(MiniMeter, {
|
|
3660
|
+
label: t("usageWeekly"),
|
|
3661
|
+
limit: credits.weekly
|
|
3662
|
+
}) : null,
|
|
3663
|
+
monthlyReported ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(MonthlyBalance, {
|
|
3664
|
+
label: t("usageMonthlyLeft"),
|
|
3665
|
+
remaining: credits.monthlyCredits
|
|
3666
|
+
}) : null
|
|
3667
|
+
]
|
|
3668
|
+
}) : null,
|
|
3669
|
+
!row.configured && row.managed && mode === void 0 && !showLogin ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3670
|
+
className: "cc-accountSetup",
|
|
3671
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
3672
|
+
className: "cc-hint",
|
|
3673
|
+
children: row.keyWritable ? t("accountNoKeyHint") : t("apiKeyLocked")
|
|
3674
|
+
}), keyActions ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3675
|
+
className: "cc-inlineActions",
|
|
3676
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
3677
|
+
variant: "primary",
|
|
3678
|
+
size: "sm",
|
|
3679
|
+
disabled: loginBusy || busyElsewhere,
|
|
3680
|
+
onClick: () => actions.beginLogin(loginTarget),
|
|
3681
|
+
children: t("accountAddLogin")
|
|
3682
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
3683
|
+
variant: "ghost",
|
|
3684
|
+
size: "sm",
|
|
3685
|
+
onClick: () => setMode("key"),
|
|
3686
|
+
children: t("accountAddPaste")
|
|
3687
|
+
})]
|
|
3688
|
+
}) : null]
|
|
3689
|
+
}) : null,
|
|
3690
|
+
showLogin ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(LoginStatus, {
|
|
3691
|
+
state: loginState,
|
|
3692
|
+
t,
|
|
3693
|
+
onCancel: actions.cancelLogin
|
|
3694
|
+
}) : null,
|
|
3695
|
+
mode === "key" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(InlineInput, {
|
|
3696
|
+
id: `cc-account-${row.id}-key`,
|
|
3697
|
+
label: t("accountKeyPlaceholder"),
|
|
3698
|
+
secret: true,
|
|
3699
|
+
placeholder: t("accountKeyPlaceholder"),
|
|
3700
|
+
confirmLabel: t("accountApply"),
|
|
3701
|
+
disabled: disabled || state.accountBusy,
|
|
3702
|
+
t,
|
|
3703
|
+
onCancel: () => setMode(void 0),
|
|
3704
|
+
onSubmit: (value) => {
|
|
3705
|
+
actions.setKey(row.id, value).then((ok) => {
|
|
3706
|
+
if (ok) setMode(void 0);
|
|
3707
|
+
});
|
|
3708
|
+
}
|
|
3709
|
+
}) : null,
|
|
3710
|
+
mode === "rename" && row.ref !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(InlineInput, {
|
|
3711
|
+
id: `cc-account-${row.id}-rename`,
|
|
3712
|
+
label: t("accountActionRename"),
|
|
3713
|
+
initial: row.label,
|
|
3714
|
+
confirmLabel: t("accountApply"),
|
|
3715
|
+
disabled: disabled || state.accountBusy,
|
|
3716
|
+
t,
|
|
3717
|
+
onCancel: () => setMode(void 0),
|
|
3718
|
+
onSubmit: (value) => {
|
|
3719
|
+
actions.rename(row.ref, value).then((ok) => {
|
|
3720
|
+
if (ok) setMode(void 0);
|
|
3721
|
+
});
|
|
3722
|
+
}
|
|
3723
|
+
}) : null,
|
|
3724
|
+
mode === "remove" && row.ref !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ConfirmBar, {
|
|
3725
|
+
text: t("accountRemoveConfirm", { name: row.label }),
|
|
3726
|
+
confirmLabel: t("accountConfirmRemove"),
|
|
3727
|
+
disabled: disabled || state.accountBusy,
|
|
3728
|
+
t,
|
|
3729
|
+
onCancel: () => setMode(void 0),
|
|
3730
|
+
onConfirm: () => {
|
|
3731
|
+
actions.remove(row.ref).then((ok) => {
|
|
3732
|
+
if (ok) setMode(void 0);
|
|
3733
|
+
});
|
|
3734
|
+
}
|
|
3735
|
+
}) : null,
|
|
3736
|
+
mode === "clearKey" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ConfirmBar, {
|
|
3737
|
+
text: t("accountClearKeyConfirm"),
|
|
3738
|
+
confirmLabel: t("accountConfirmClear"),
|
|
3739
|
+
disabled: disabled || state.accountBusy,
|
|
3740
|
+
t,
|
|
3741
|
+
onCancel: () => setMode(void 0),
|
|
3742
|
+
onConfirm: () => {
|
|
3743
|
+
actions.clearKey(row.id).then((ok) => {
|
|
3744
|
+
if (ok) setMode(void 0);
|
|
3745
|
+
});
|
|
3746
|
+
}
|
|
3747
|
+
}) : null,
|
|
3748
|
+
expanded ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3749
|
+
id: `cc-account-${row.id}-details`,
|
|
3750
|
+
className: "cc-accountDetails",
|
|
3751
|
+
children: [entry !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(AccountReport, {
|
|
3752
|
+
entry,
|
|
3753
|
+
fetchedAt: usage.fetchedAt,
|
|
3754
|
+
t,
|
|
3755
|
+
headless: true
|
|
3756
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
3757
|
+
className: "cc-usageHint",
|
|
3758
|
+
children: usage.status === "loading" ? t("usageLoading") : t("usageUnconfigured")
|
|
3759
|
+
}), multi && row.managed ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SettingRow, {
|
|
3760
|
+
className: "cc-rowFlush",
|
|
3761
|
+
title: t("accountModels"),
|
|
3762
|
+
titleFor: `cc-account-${row.id}-models`,
|
|
3763
|
+
description: t("accountModelsHint"),
|
|
3764
|
+
error: state.catalogFailed ? t("modelCatalogFailed") : void 0,
|
|
3765
|
+
control: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ModelMultiSelect, {
|
|
3766
|
+
id: `cc-account-${row.id}-models`,
|
|
3767
|
+
selected: models,
|
|
3768
|
+
catalog: state.catalogModels,
|
|
3769
|
+
disabled: disabled || state.accountBusy,
|
|
3770
|
+
deferCommit: true,
|
|
3771
|
+
ownerOf,
|
|
3772
|
+
t,
|
|
3773
|
+
onSelect: (ids) => void actions.setModels(row.id, ids)
|
|
3774
|
+
})
|
|
3775
|
+
}) : null]
|
|
3776
|
+
}) : null
|
|
3777
|
+
]
|
|
3778
|
+
});
|
|
3779
|
+
}
|
|
3780
|
+
/** The live status of one browser sign-in: link while waiting, outcome after. */
|
|
3781
|
+
function LoginStatus({ state, t, onCancel }) {
|
|
3782
|
+
const hint = loginHint(state, t);
|
|
3783
|
+
const busy = state.phase === "starting" || state.phase === "waiting";
|
|
3784
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3785
|
+
className: "cc-loginStatus",
|
|
3786
|
+
role: "status",
|
|
3787
|
+
children: [
|
|
3788
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
3789
|
+
className: hint.className,
|
|
3790
|
+
title: hint.title,
|
|
3791
|
+
children: hint.text
|
|
3792
|
+
}),
|
|
3793
|
+
state.authUrl !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("a", {
|
|
3794
|
+
className: "cc-loginLink",
|
|
3795
|
+
href: state.authUrl,
|
|
3796
|
+
target: "_blank",
|
|
3797
|
+
rel: "noreferrer",
|
|
3798
|
+
children: t("loginOpenLink")
|
|
3799
|
+
}) : null,
|
|
3800
|
+
busy ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
3801
|
+
type: "button",
|
|
3802
|
+
className: "cc-reset",
|
|
3803
|
+
onClick: onCancel,
|
|
3804
|
+
children: t("loginCancel")
|
|
3805
|
+
}) : null
|
|
3806
|
+
]
|
|
3807
|
+
});
|
|
3808
|
+
}
|
|
3809
|
+
/**
|
|
3810
|
+
* The add-account panel. Both paths store the account at once: pasting a key
|
|
3811
|
+
* writes key then row; browser sign-in stores a keyless row first (the Host
|
|
3812
|
+
* only signs in to a reference the stored list names) and drops it again if
|
|
3813
|
+
* the sign-in does not complete, so a failed attempt leaves no empty account.
|
|
3814
|
+
*/
|
|
3815
|
+
function AddAccountPanel({ state, login, disabled, t, actions, pendingRef, setPendingRef, onClose }) {
|
|
3816
|
+
const [label, setLabel] = (0, react.useState)("");
|
|
3817
|
+
const [pasting, setPasting] = (0, react.useState)(false);
|
|
3818
|
+
const [loginFailure, setLoginFailure] = (0, react.useState)(void 0);
|
|
3819
|
+
const fallbackName = t("accountNameN", { n: state.accounts.length + 2 });
|
|
3820
|
+
const pending = pendingRef === void 0 ? void 0 : loginStateForTarget(login, pendingRef).visible;
|
|
3821
|
+
const busy = state.accountBusy || pending?.phase === "starting" || pending?.phase === "waiting";
|
|
3822
|
+
const loginBusyElsewhere = login.phase === "starting" || login.phase === "waiting";
|
|
3823
|
+
(0, react.useEffect)(() => {
|
|
3824
|
+
if (pendingRef === void 0 || login.targetRef !== pendingRef) return;
|
|
3825
|
+
if (login.phase === "success") {
|
|
3826
|
+
const named = label.trim() === "" && login.userName !== void 0 && login.userName !== "";
|
|
3827
|
+
setPendingRef(void 0);
|
|
3828
|
+
(named ? actions.rename(pendingRef, login.userName) : Promise.resolve(true)).then(onClose);
|
|
3829
|
+
return;
|
|
3830
|
+
}
|
|
3831
|
+
if (login.phase === "failed" || login.phase === "unavailable") {
|
|
3832
|
+
setLoginFailure(loginHint(login, t).text);
|
|
3833
|
+
setPendingRef(void 0);
|
|
3834
|
+
actions.remove(pendingRef);
|
|
3835
|
+
}
|
|
3836
|
+
}, [
|
|
3837
|
+
login,
|
|
3838
|
+
pendingRef,
|
|
3839
|
+
setPendingRef,
|
|
3840
|
+
label,
|
|
3841
|
+
actions,
|
|
3842
|
+
onClose,
|
|
3843
|
+
t
|
|
3844
|
+
]);
|
|
3845
|
+
const startLogin = async () => {
|
|
3846
|
+
setLoginFailure(void 0);
|
|
3847
|
+
const ref = await actions.create({ label: label.trim() === "" ? fallbackName : label });
|
|
3848
|
+
if (ref === void 0) return;
|
|
3849
|
+
setPendingRef(ref);
|
|
3850
|
+
actions.beginLogin(ref);
|
|
3851
|
+
};
|
|
3852
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3853
|
+
className: "cc-addPanel",
|
|
3854
|
+
"aria-label": t("accountAdd"),
|
|
3855
|
+
children: [
|
|
3856
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3857
|
+
className: "cc-panelTitle",
|
|
3858
|
+
children: t("accountAdd")
|
|
3859
|
+
}),
|
|
3860
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
3861
|
+
className: "cc-input",
|
|
3862
|
+
type: "text",
|
|
3863
|
+
"aria-label": t("accountNamePlaceholder"),
|
|
3864
|
+
placeholder: t("accountNamePlaceholder"),
|
|
3865
|
+
value: label,
|
|
3866
|
+
disabled: disabled || busy,
|
|
3867
|
+
onChange: (event) => setLabel(event.target.value)
|
|
3868
|
+
}),
|
|
3869
|
+
pending !== void 0 && pending.phase !== "idle" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(LoginStatus, {
|
|
3870
|
+
state: pending,
|
|
3871
|
+
t,
|
|
3872
|
+
onCancel: actions.cancelLogin
|
|
3873
|
+
}) : null,
|
|
3874
|
+
loginFailure !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
3875
|
+
className: "cc-loginError",
|
|
3876
|
+
children: loginFailure
|
|
3877
|
+
}) : null,
|
|
3878
|
+
pasting ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(InlineInput, {
|
|
3879
|
+
id: "cc-add-account-key",
|
|
3880
|
+
label: t("accountKeyPlaceholder"),
|
|
3881
|
+
secret: true,
|
|
3882
|
+
placeholder: t("accountKeyPlaceholder"),
|
|
3883
|
+
confirmLabel: t("accountAddConfirm"),
|
|
3884
|
+
disabled: disabled || busy,
|
|
3885
|
+
t,
|
|
3886
|
+
onCancel: () => setPasting(false),
|
|
3887
|
+
onSubmit: (key) => {
|
|
3888
|
+
actions.create({
|
|
3889
|
+
label: label.trim() === "" ? fallbackName : label,
|
|
3890
|
+
key
|
|
3891
|
+
}).then((ref) => {
|
|
3892
|
+
if (ref !== void 0) onClose();
|
|
3893
|
+
});
|
|
3894
|
+
}
|
|
3895
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
3896
|
+
className: "cc-hint",
|
|
3897
|
+
children: t("accountAddHint")
|
|
3898
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3899
|
+
className: "cc-inlineActions",
|
|
3900
|
+
children: [
|
|
3901
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
3902
|
+
variant: "primary",
|
|
3903
|
+
size: "sm",
|
|
3904
|
+
disabled: disabled || busy || loginBusyElsewhere,
|
|
3905
|
+
onClick: () => void startLogin(),
|
|
3906
|
+
children: t("accountAddLogin")
|
|
3907
|
+
}),
|
|
3908
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
3909
|
+
variant: "ghost",
|
|
3910
|
+
size: "sm",
|
|
3911
|
+
disabled: disabled || busy,
|
|
3912
|
+
onClick: () => setPasting(true),
|
|
3913
|
+
children: t("accountAddPaste")
|
|
3914
|
+
}),
|
|
3915
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: "cc-spacer" }),
|
|
3916
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
3917
|
+
variant: "ghost",
|
|
3918
|
+
size: "sm",
|
|
3919
|
+
disabled: busy,
|
|
3920
|
+
onClick: onClose,
|
|
3921
|
+
children: t("cancel")
|
|
3922
|
+
})
|
|
3923
|
+
]
|
|
3924
|
+
})] })
|
|
3925
|
+
]
|
|
3926
|
+
});
|
|
3927
|
+
}
|
|
3928
|
+
/**
|
|
3929
|
+
* The unified account card: every account the pool rotates through, one row
|
|
3930
|
+
* each, with its status and quota inline. Replaces the former split between
|
|
3931
|
+
* a credentials card, a usage card and a routing-rules card, so one account's
|
|
3932
|
+
* facts no longer have to be cross-read across three surfaces.
|
|
3933
|
+
*/
|
|
3934
|
+
function AccountsCard({ t, state, usage, login, disabled, actions, onRefresh }) {
|
|
3935
|
+
const [adding, setAdding] = (0, react.useState)(false);
|
|
3936
|
+
const [pendingRef, setPendingRef] = (0, react.useState)(void 0);
|
|
3937
|
+
const closeAdd = (0, react.useCallback)(() => setAdding(false), []);
|
|
3938
|
+
const { loading, shouldRefresh } = usageCardState(usage);
|
|
3939
|
+
(0, react.useEffect)(() => {
|
|
3940
|
+
if (shouldRefresh) onRefresh();
|
|
3941
|
+
}, [shouldRefresh, onRefresh]);
|
|
3942
|
+
const rows = accountRows(state, usage, t).filter((row) => row.ref === void 0 || row.ref !== pendingRef);
|
|
3943
|
+
const multi = rows.length > 1;
|
|
3944
|
+
const pinnedRow = rows.find((row) => row.id === state.activeAccount);
|
|
3945
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
|
|
3946
|
+
className: "cc-group",
|
|
3947
|
+
"aria-label": t("accountsTitle"),
|
|
3838
3948
|
children: [
|
|
3839
3949
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3840
|
-
className: "cc-
|
|
3950
|
+
className: "cc-groupHead",
|
|
3841
3951
|
children: [
|
|
3842
|
-
/* @__PURE__ */ (0, react_jsx_runtime.
|
|
3843
|
-
className: "cc-
|
|
3844
|
-
children:
|
|
3845
|
-
className: "cc-label",
|
|
3846
|
-
children: t("rulesTitle")
|
|
3847
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3848
|
-
className: "cc-badges",
|
|
3849
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
3850
|
-
type: "button",
|
|
3851
|
-
className: "cc-reset",
|
|
3852
|
-
disabled,
|
|
3853
|
-
onClick: onAdd,
|
|
3854
|
-
children: t("ruleAdd")
|
|
3855
|
-
})
|
|
3856
|
-
})]
|
|
3857
|
-
}),
|
|
3858
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
3859
|
-
className: "cc-hint",
|
|
3860
|
-
children: t("rulesHint")
|
|
3952
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", {
|
|
3953
|
+
className: "cc-groupTitle",
|
|
3954
|
+
children: t("accountsTitle")
|
|
3861
3955
|
}),
|
|
3862
|
-
|
|
3863
|
-
|
|
3864
|
-
|
|
3865
|
-
|
|
3956
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: "cc-spacer" }),
|
|
3957
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
3958
|
+
type: "button",
|
|
3959
|
+
className: "cc-linkButton",
|
|
3960
|
+
disabled: loading,
|
|
3961
|
+
onClick: onRefresh,
|
|
3962
|
+
children: loading ? t("usageRefreshing") : t("usageRefresh")
|
|
3963
|
+
})
|
|
3866
3964
|
]
|
|
3867
3965
|
}),
|
|
3868
|
-
|
|
3869
|
-
|
|
3870
|
-
|
|
3871
|
-
|
|
3966
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
3967
|
+
className: "cc-groupDesc",
|
|
3968
|
+
children: multi ? t("accountsRotationHint") : t("accountsHint")
|
|
3969
|
+
}),
|
|
3970
|
+
multi ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3971
|
+
className: "cc-accountMode",
|
|
3972
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: pinnedRow !== void 0 ? t("accountModePinned", { name: pinnedRow.label }) : t("accountModeAuto") }), pinnedRow !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
3973
|
+
type: "button",
|
|
3974
|
+
className: "cc-reset",
|
|
3975
|
+
disabled: disabled || state.accountBusy,
|
|
3976
|
+
onClick: () => void actions.setActive(""),
|
|
3977
|
+
children: t("accountActionUnpin")
|
|
3978
|
+
}) : null]
|
|
3979
|
+
}) : null,
|
|
3980
|
+
usage.status === "error" ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("p", {
|
|
3981
|
+
className: "cc-rowError",
|
|
3982
|
+
role: "status",
|
|
3983
|
+
children: [t("usageError"), usage.error !== void 0 && usage.error !== "" ? ` — ${usage.error}` : ""]
|
|
3984
|
+
}) : null,
|
|
3985
|
+
state.accountFailed !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
3986
|
+
className: "cc-rowError",
|
|
3987
|
+
role: "status",
|
|
3988
|
+
children: t("accountOpFailed")
|
|
3989
|
+
}) : null,
|
|
3990
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
3991
|
+
className: "cc-accountList",
|
|
3992
|
+
children: rows.map((row) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(AccountItem, {
|
|
3993
|
+
row,
|
|
3994
|
+
rows,
|
|
3995
|
+
state,
|
|
3996
|
+
usage,
|
|
3997
|
+
login,
|
|
3998
|
+
disabled,
|
|
3999
|
+
pinned: state.activeAccount === row.id,
|
|
4000
|
+
t,
|
|
4001
|
+
actions
|
|
4002
|
+
}, row.id))
|
|
4003
|
+
}),
|
|
4004
|
+
adding ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(AddAccountPanel, {
|
|
4005
|
+
state,
|
|
4006
|
+
login,
|
|
3872
4007
|
disabled,
|
|
3873
4008
|
t,
|
|
3874
|
-
|
|
3875
|
-
|
|
3876
|
-
|
|
3877
|
-
|
|
3878
|
-
|
|
3879
|
-
|
|
3880
|
-
|
|
3881
|
-
|
|
4009
|
+
actions,
|
|
4010
|
+
pendingRef,
|
|
4011
|
+
setPendingRef,
|
|
4012
|
+
onClose: closeAdd
|
|
4013
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
4014
|
+
type: "button",
|
|
4015
|
+
className: "cc-addButton",
|
|
4016
|
+
disabled,
|
|
4017
|
+
onClick: () => setAdding(true),
|
|
4018
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
4019
|
+
className: "cc-addGlyph",
|
|
4020
|
+
"aria-hidden": "true"
|
|
4021
|
+
}), t("accountAdd")]
|
|
4022
|
+
})
|
|
3882
4023
|
]
|
|
3883
4024
|
});
|
|
3884
4025
|
}
|
|
3885
|
-
/** The visible-model filter card: an allowlist over the catalog. Empty = show all. */
|
|
3886
|
-
function VisibleModelsCard({ t, state, disabled, onSelect, onClear }) {
|
|
3887
|
-
const count = state.visibleModels.length;
|
|
3888
|
-
const pickT = (key, params) => {
|
|
3889
|
-
if (key === "ruleModelPick") return t("visibleModelsPick");
|
|
3890
|
-
if (key === "ruleModelCount") return t("visibleModelsCount", params);
|
|
3891
|
-
return t(key, params);
|
|
3892
|
-
};
|
|
3893
|
-
const readiness = {
|
|
3894
|
-
catalogIds: state.catalogModels.map((model) => model.id),
|
|
3895
|
-
catalogFailed: state.catalogFailed
|
|
3896
|
-
};
|
|
3897
|
-
const staleIds = staleModelIds(state.visibleModels, readiness);
|
|
3898
|
-
const catalogReady = catalogIsReady(readiness);
|
|
3899
|
-
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
3900
|
-
className: "cc-card",
|
|
3901
|
-
"aria-label": t("visibleModelsTitle"),
|
|
3902
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3903
|
-
className: "cc-field",
|
|
3904
|
-
children: [
|
|
3905
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3906
|
-
className: "cc-fieldHead",
|
|
3907
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("label", {
|
|
3908
|
-
className: "cc-label",
|
|
3909
|
-
children: t("visibleModelsTitle")
|
|
3910
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
3911
|
-
className: "cc-badges",
|
|
3912
|
-
children: [catalogReady && staleIds.length > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
3913
|
-
type: "button",
|
|
3914
|
-
className: "cc-reset",
|
|
3915
|
-
disabled,
|
|
3916
|
-
onClick: () => onSelect(state.visibleModels.filter((id) => !staleIds.includes(id))),
|
|
3917
|
-
children: t("visibleModelsCleanStale", { count: staleIds.length })
|
|
3918
|
-
}) : null, count > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
3919
|
-
type: "button",
|
|
3920
|
-
className: "cc-reset",
|
|
3921
|
-
disabled,
|
|
3922
|
-
onClick: onClear,
|
|
3923
|
-
children: t("visibleModelsShowAll")
|
|
3924
|
-
}) : null]
|
|
3925
|
-
})]
|
|
3926
|
-
}),
|
|
3927
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
3928
|
-
className: "cc-hint",
|
|
3929
|
-
children: t("visibleModelsHint")
|
|
3930
|
-
}),
|
|
3931
|
-
state.catalogFailed ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
3932
|
-
className: "cc-invalid",
|
|
3933
|
-
children: t("rulesCatalogFailed")
|
|
3934
|
-
}) : null,
|
|
3935
|
-
staleIds.length > 0 && catalogReady ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
3936
|
-
className: "cc-hint",
|
|
3937
|
-
children: t("visibleModelsStaleHint", { count: staleIds.length })
|
|
3938
|
-
}) : null,
|
|
3939
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(ModelMultiSelect, {
|
|
3940
|
-
id: "cc-visible-models",
|
|
3941
|
-
selected: state.visibleModels,
|
|
3942
|
-
catalog: state.catalogModels,
|
|
3943
|
-
disabled,
|
|
3944
|
-
t: pickT,
|
|
3945
|
-
onSelect
|
|
3946
|
-
})
|
|
3947
|
-
]
|
|
3948
|
-
})
|
|
3949
|
-
});
|
|
3950
|
-
}
|
|
3951
4026
|
/**
|
|
3952
|
-
* Show the
|
|
4027
|
+
* Show the save bar's "saved" confirmation for a short window after each accepted save.
|
|
3953
4028
|
* The controller only counts saves (`savedCount`); the flash timing lives
|
|
3954
4029
|
* here so the state machine stays timer-free.
|
|
3955
4030
|
*/
|
|
3956
4031
|
function useSavedFlash(tick) {
|
|
3957
4032
|
const [visible, setVisible] = (0, react.useState)(false);
|
|
4033
|
+
const seen = (0, react.useRef)(tick);
|
|
3958
4034
|
(0, react.useEffect)(() => {
|
|
3959
|
-
if (tick ===
|
|
4035
|
+
if (tick === seen.current) return;
|
|
4036
|
+
seen.current = tick;
|
|
3960
4037
|
setVisible(true);
|
|
3961
4038
|
const timer = setTimeout(() => setVisible(false), 2500);
|
|
3962
4039
|
return () => clearTimeout(timer);
|
|
@@ -3986,29 +4063,157 @@ window.__ModuleLoader__.load({
|
|
|
3986
4063
|
}, []);
|
|
3987
4064
|
return available;
|
|
3988
4065
|
}
|
|
3989
|
-
/**
|
|
4066
|
+
/**
|
|
4067
|
+
* What the save bar says. It exists only while there is something to act on
|
|
4068
|
+
* or to confirm: pending edits, a save in flight, a failure, or the brief
|
|
4069
|
+
* confirmation after a save landed. Account operations never reach it, they
|
|
4070
|
+
* commit on their own.
|
|
4071
|
+
*/
|
|
4072
|
+
function saveBarView(state, savedVisible, t) {
|
|
4073
|
+
if (state.failed) return {
|
|
4074
|
+
visible: true,
|
|
4075
|
+
tone: "error",
|
|
4076
|
+
message: t("saveFailed"),
|
|
4077
|
+
actions: state.dirty
|
|
4078
|
+
};
|
|
4079
|
+
if (state.dirty || state.saving) return state.invalid ? {
|
|
4080
|
+
visible: true,
|
|
4081
|
+
tone: "error",
|
|
4082
|
+
message: t("saveInvalid"),
|
|
4083
|
+
actions: true
|
|
4084
|
+
} : {
|
|
4085
|
+
visible: true,
|
|
4086
|
+
tone: "pending",
|
|
4087
|
+
message: t("unsavedChanges"),
|
|
4088
|
+
actions: true
|
|
4089
|
+
};
|
|
4090
|
+
if (savedVisible) return {
|
|
4091
|
+
visible: true,
|
|
4092
|
+
tone: "success",
|
|
4093
|
+
message: t("saved"),
|
|
4094
|
+
actions: false
|
|
4095
|
+
};
|
|
4096
|
+
return {
|
|
4097
|
+
visible: false,
|
|
4098
|
+
tone: "success",
|
|
4099
|
+
message: "",
|
|
4100
|
+
actions: false
|
|
4101
|
+
};
|
|
4102
|
+
}
|
|
4103
|
+
/** The save bar's leading status mark: a soft dot while pending, a glyph once there is an outcome. */
|
|
4104
|
+
function SaveBarIcon({ tone }) {
|
|
4105
|
+
if (tone === "pending") return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
4106
|
+
className: "cc-saveBarIcon",
|
|
4107
|
+
"aria-hidden": "true",
|
|
4108
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: "cc-saveBarPulse" })
|
|
4109
|
+
});
|
|
4110
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
4111
|
+
className: "cc-saveBarIcon",
|
|
4112
|
+
"aria-hidden": "true",
|
|
4113
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("svg", {
|
|
4114
|
+
viewBox: "0 0 16 16",
|
|
4115
|
+
width: "16",
|
|
4116
|
+
height: "16",
|
|
4117
|
+
fill: "none",
|
|
4118
|
+
stroke: "currentColor",
|
|
4119
|
+
strokeWidth: "1.6",
|
|
4120
|
+
strokeLinecap: "round",
|
|
4121
|
+
strokeLinejoin: "round",
|
|
4122
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("circle", {
|
|
4123
|
+
cx: "8",
|
|
4124
|
+
cy: "8",
|
|
4125
|
+
r: "6.5"
|
|
4126
|
+
}), tone === "success" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { d: "M5.2 8.2l1.9 1.9 3.7-3.9" }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { d: "M8 4.8v3.6M8 11.1v.1" })]
|
|
4127
|
+
})
|
|
4128
|
+
});
|
|
4129
|
+
}
|
|
4130
|
+
/**
|
|
4131
|
+
* The floating save bar: pinned to the bottom of the settings scrollport and
|
|
4132
|
+
* slid out of view when there is nothing to save. It stays mounted so it can
|
|
4133
|
+
* animate out, and keeps showing its last message while it does (the state
|
|
4134
|
+
* that hid it has no message of its own).
|
|
4135
|
+
*/
|
|
4136
|
+
function SaveBar({ view, state, t, onDiscard, onSave }) {
|
|
4137
|
+
const last = (0, react.useRef)(view);
|
|
4138
|
+
if (view.visible) last.current = view;
|
|
4139
|
+
const shown = view.visible ? view : last.current;
|
|
4140
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
4141
|
+
className: "cc-saveBarDock",
|
|
4142
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4143
|
+
className: `cc-saveBar cc-saveBar-${shown.tone}${view.visible ? " cc-saveBarShown" : ""}`,
|
|
4144
|
+
role: "region",
|
|
4145
|
+
"aria-label": t("save"),
|
|
4146
|
+
"aria-hidden": !view.visible,
|
|
4147
|
+
children: [
|
|
4148
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(SaveBarIcon, { tone: shown.tone }),
|
|
4149
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
4150
|
+
className: "cc-saveBarText",
|
|
4151
|
+
role: "status",
|
|
4152
|
+
"aria-live": "polite",
|
|
4153
|
+
title: shown.message,
|
|
4154
|
+
children: shown.message
|
|
4155
|
+
}),
|
|
4156
|
+
shown.actions ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4157
|
+
className: "cc-saveBarActions",
|
|
4158
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
4159
|
+
type: "button",
|
|
4160
|
+
className: "cc-saveBarButton cc-saveBarGhost",
|
|
4161
|
+
disabled: !view.visible || !state.dirty || state.saving,
|
|
4162
|
+
onClick: onDiscard,
|
|
4163
|
+
children: t("discard")
|
|
4164
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
4165
|
+
type: "button",
|
|
4166
|
+
className: "cc-saveBarButton cc-saveBarPrimary",
|
|
4167
|
+
disabled: !view.visible || !state.dirty || state.invalid || state.saving,
|
|
4168
|
+
onClick: onSave,
|
|
4169
|
+
children: t(state.saving ? "saving" : "save")
|
|
4170
|
+
})]
|
|
4171
|
+
}) : null
|
|
4172
|
+
]
|
|
4173
|
+
})
|
|
4174
|
+
});
|
|
4175
|
+
}
|
|
4176
|
+
/** The settings page body: accounts, then the staged provider settings. */
|
|
3990
4177
|
function CommandCodeSettingsPage(props) {
|
|
3991
4178
|
const { t } = props;
|
|
3992
|
-
const footerMarker = (0, react.useRef)(null);
|
|
3993
|
-
const [footerFloating, setFooterFloating] = (0, react.useState)(false);
|
|
3994
4179
|
const state = props.useCommandCodeSettings((snapshot) => snapshot);
|
|
3995
4180
|
const usage = props.useCommandCodeUsage((snapshot) => snapshot);
|
|
3996
4181
|
const login = props.useCommandCodeLogin((snapshot) => snapshot);
|
|
3997
4182
|
const disabled = !state.writable;
|
|
3998
|
-
const keyLocked = !state.apiKeyWritable;
|
|
3999
4183
|
const savedVisible = useSavedFlash(state.savedCount);
|
|
4000
4184
|
const updateVersion = usePluginUpdate();
|
|
4001
|
-
|
|
4002
|
-
|
|
4003
|
-
|
|
4004
|
-
|
|
4005
|
-
|
|
4006
|
-
|
|
4007
|
-
|
|
4008
|
-
|
|
4009
|
-
|
|
4185
|
+
const { createAccount, renameAccount, removeAccount, setAccountKey, clearAccountKey, setActiveAccount, setAccountModels, beginLogin, cancelLogin } = props;
|
|
4186
|
+
const actions = (0, react.useMemo)(() => ({
|
|
4187
|
+
create: createAccount,
|
|
4188
|
+
rename: renameAccount,
|
|
4189
|
+
remove: removeAccount,
|
|
4190
|
+
setKey: setAccountKey,
|
|
4191
|
+
clearKey: clearAccountKey,
|
|
4192
|
+
setActive: setActiveAccount,
|
|
4193
|
+
setModels: setAccountModels,
|
|
4194
|
+
beginLogin,
|
|
4195
|
+
cancelLogin
|
|
4196
|
+
}), [
|
|
4197
|
+
createAccount,
|
|
4198
|
+
renameAccount,
|
|
4199
|
+
removeAccount,
|
|
4200
|
+
setAccountKey,
|
|
4201
|
+
clearAccountKey,
|
|
4202
|
+
setActiveAccount,
|
|
4203
|
+
setAccountModels,
|
|
4204
|
+
beginLogin,
|
|
4205
|
+
cancelLogin
|
|
4206
|
+
]);
|
|
4207
|
+
const bar = saveBarView(state, savedVisible, t);
|
|
4208
|
+
const form = {
|
|
4209
|
+
state,
|
|
4210
|
+
disabled,
|
|
4211
|
+
t,
|
|
4212
|
+
onEdit: props.edit,
|
|
4213
|
+
onReset: props.resetField
|
|
4214
|
+
};
|
|
4010
4215
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
|
|
4011
|
-
className: "cc-section",
|
|
4216
|
+
className: bar.visible ? "cc-section cc-sectionWithBar" : "cc-section",
|
|
4012
4217
|
"aria-label": t("title"),
|
|
4013
4218
|
children: [
|
|
4014
4219
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h2", {
|
|
@@ -4024,117 +4229,23 @@ window.__ModuleLoader__.load({
|
|
|
4024
4229
|
role: "status",
|
|
4025
4230
|
children: t("readOnly")
|
|
4026
4231
|
}) : null,
|
|
4027
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(UsageCard, {
|
|
4028
|
-
t,
|
|
4029
|
-
usage,
|
|
4030
|
-
removingIds: state.accountsRemoving,
|
|
4031
|
-
removableIds: state.accounts.map((account) => account.id),
|
|
4032
|
-
canManage: state.writable,
|
|
4033
|
-
onRefresh: props.refreshUsage,
|
|
4034
|
-
onRemoveAccount: props.removeAccount
|
|
4035
|
-
}),
|
|
4036
4232
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(AccountsCard, {
|
|
4037
4233
|
t,
|
|
4038
4234
|
state,
|
|
4235
|
+
usage,
|
|
4039
4236
|
login,
|
|
4040
4237
|
disabled,
|
|
4041
|
-
|
|
4042
|
-
|
|
4043
|
-
onLabel: props.editAccountLabel,
|
|
4044
|
-
onKey: props.editAccountKey,
|
|
4045
|
-
onToggleClear: (id) => props.toggleKeyClear(id),
|
|
4046
|
-
onActive: (text) => props.edit("activeAccount", text),
|
|
4047
|
-
onActiveReset: () => props.resetField("activeAccount"),
|
|
4048
|
-
onBeginLogin: props.beginLogin,
|
|
4049
|
-
onCancelLogin: props.cancelLogin
|
|
4050
|
-
}),
|
|
4051
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(RulesCard, {
|
|
4052
|
-
t,
|
|
4053
|
-
state,
|
|
4054
|
-
disabled,
|
|
4055
|
-
onAdd: props.addRule,
|
|
4056
|
-
onRemove: props.removeRule,
|
|
4057
|
-
onModels: props.editRuleModels,
|
|
4058
|
-
onAccount: props.editRuleAccount
|
|
4238
|
+
actions,
|
|
4239
|
+
onRefresh: props.refreshUsage
|
|
4059
4240
|
}),
|
|
4060
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(
|
|
4061
|
-
|
|
4062
|
-
state,
|
|
4063
|
-
disabled,
|
|
4241
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(ModelsCard, {
|
|
4242
|
+
...form,
|
|
4064
4243
|
onSelect: props.editVisibleModels,
|
|
4065
4244
|
onClear: props.clearVisibleModels
|
|
4066
4245
|
}),
|
|
4067
|
-
/* @__PURE__ */ (0, react_jsx_runtime.
|
|
4068
|
-
|
|
4069
|
-
|
|
4070
|
-
label: t("apiKey"),
|
|
4071
|
-
hint: keyLocked ? t("apiKeyLocked") : t("apiKeyHint"),
|
|
4072
|
-
state: state.apiKey,
|
|
4073
|
-
disabled: disabled || keyLocked || login.targetRef === void 0 && (login.phase === "starting" || login.phase === "waiting"),
|
|
4074
|
-
configured: state.apiKeyConfigured,
|
|
4075
|
-
configuredLabel: t("apiKeySet"),
|
|
4076
|
-
unconfiguredLabel: t("apiKeyUnset"),
|
|
4077
|
-
clearStaged: state.apiKeyClearStaged,
|
|
4078
|
-
showLabel: t("show"),
|
|
4079
|
-
hideLabel: t("hide"),
|
|
4080
|
-
clearLabel: t("usageKeyClear"),
|
|
4081
|
-
clearStagedLabel: t("usageKeyClearStaged"),
|
|
4082
|
-
undoClearLabel: t("usageUndoKeyClear"),
|
|
4083
|
-
onEdit: (text) => props.edit("apiKey", text),
|
|
4084
|
-
onToggleClear: () => props.toggleKeyClear("default")
|
|
4085
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(LoginRow, {
|
|
4086
|
-
state: login,
|
|
4087
|
-
disabled: disabled || keyLocked || state.dirty,
|
|
4088
|
-
disabledHint: state.dirty ? t("loginSaveBefore") : void 0,
|
|
4089
|
-
t,
|
|
4090
|
-
onBegin: props.beginLogin,
|
|
4091
|
-
onCancel: props.cancelLogin
|
|
4092
|
-
})]
|
|
4093
|
-
}),
|
|
4094
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(AdvancedSection, {
|
|
4095
|
-
state,
|
|
4096
|
-
disabled,
|
|
4097
|
-
t,
|
|
4098
|
-
onEdit: props.edit,
|
|
4099
|
-
onReset: props.resetField
|
|
4100
|
-
}),
|
|
4101
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
4102
|
-
ref: footerMarker,
|
|
4103
|
-
className: "cc-footerMarker",
|
|
4104
|
-
"aria-hidden": "true"
|
|
4105
|
-
}),
|
|
4106
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
4107
|
-
className: `cc-footer cc-footerSticky${footerFloating ? " cc-footerFloating" : ""}`,
|
|
4108
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4109
|
-
className: "cc-footerActions",
|
|
4110
|
-
children: [
|
|
4111
|
-
state.failed ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
4112
|
-
className: "cc-failed",
|
|
4113
|
-
role: "status",
|
|
4114
|
-
children: t("saveFailed")
|
|
4115
|
-
}) : null,
|
|
4116
|
-
savedVisible ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
4117
|
-
className: "cc-saved",
|
|
4118
|
-
role: "status",
|
|
4119
|
-
children: t("saved")
|
|
4120
|
-
}) : null,
|
|
4121
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
4122
|
-
variant: "ghost",
|
|
4123
|
-
size: "sm",
|
|
4124
|
-
disabled: !state.dirty || state.saving,
|
|
4125
|
-
onClick: props.discard,
|
|
4126
|
-
children: t("discard")
|
|
4127
|
-
}),
|
|
4128
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
4129
|
-
variant: "primary",
|
|
4130
|
-
size: "sm",
|
|
4131
|
-
disabled: !state.dirty || state.invalid || state.saving,
|
|
4132
|
-
onClick: props.save,
|
|
4133
|
-
children: t(state.saving ? "saving" : "save")
|
|
4134
|
-
})
|
|
4135
|
-
]
|
|
4136
|
-
})
|
|
4137
|
-
}),
|
|
4246
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(PrivacyCard, { ...form }),
|
|
4247
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(IntegrationsCard, { ...form }),
|
|
4248
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(AdvancedSection, { ...form }),
|
|
4138
4249
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("p", {
|
|
4139
4250
|
className: "cc-version",
|
|
4140
4251
|
children: [
|
|
@@ -4154,6 +4265,62 @@ window.__ModuleLoader__.load({
|
|
|
4154
4265
|
]
|
|
4155
4266
|
})] }) : null
|
|
4156
4267
|
]
|
|
4268
|
+
}),
|
|
4269
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(SaveBar, {
|
|
4270
|
+
view: bar,
|
|
4271
|
+
state,
|
|
4272
|
+
t,
|
|
4273
|
+
onDiscard: props.discard,
|
|
4274
|
+
onSave: props.save
|
|
4275
|
+
})
|
|
4276
|
+
]
|
|
4277
|
+
});
|
|
4278
|
+
}
|
|
4279
|
+
//#endregion
|
|
4280
|
+
//#region src/client/login-row.tsx
|
|
4281
|
+
/** The sign-in alternative to pasting a key (settings page + Models card). */
|
|
4282
|
+
function LoginRow({ state, targetRef, disabled, disabledHint, t, onBegin, onCancel }) {
|
|
4283
|
+
const { visible: visibleState, busyElsewhere } = loginStateForTarget(state, targetRef);
|
|
4284
|
+
const busy = visibleState.phase === "starting" || visibleState.phase === "waiting";
|
|
4285
|
+
const hint = loginHint(visibleState, t);
|
|
4286
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4287
|
+
className: "cc-field",
|
|
4288
|
+
children: [
|
|
4289
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4290
|
+
className: "cc-fieldHead",
|
|
4291
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
4292
|
+
className: "cc-label",
|
|
4293
|
+
children: t("loginTitle")
|
|
4294
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
4295
|
+
className: "cc-badges",
|
|
4296
|
+
children: busy ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
4297
|
+
type: "button",
|
|
4298
|
+
className: "cc-reset",
|
|
4299
|
+
onClick: onCancel,
|
|
4300
|
+
children: t("loginCancel")
|
|
4301
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
4302
|
+
type: "button",
|
|
4303
|
+
className: "cc-reset",
|
|
4304
|
+
disabled: disabled || busyElsewhere,
|
|
4305
|
+
onClick: onBegin,
|
|
4306
|
+
children: t("loginButton")
|
|
4307
|
+
})
|
|
4308
|
+
})]
|
|
4309
|
+
}),
|
|
4310
|
+
visibleState.authUrl !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
4311
|
+
className: "cc-hint",
|
|
4312
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("a", {
|
|
4313
|
+
className: "cc-loginLink",
|
|
4314
|
+
href: visibleState.authUrl,
|
|
4315
|
+
target: "_blank",
|
|
4316
|
+
rel: "noreferrer",
|
|
4317
|
+
children: t("loginOpenLink")
|
|
4318
|
+
})
|
|
4319
|
+
}) : null,
|
|
4320
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
4321
|
+
className: hint.className,
|
|
4322
|
+
title: hint.title,
|
|
4323
|
+
children: disabled && !busy && disabledHint !== void 0 ? disabledHint : hint.text
|
|
4157
4324
|
})
|
|
4158
4325
|
]
|
|
4159
4326
|
});
|
|
@@ -4682,16 +4849,13 @@ window.__ModuleLoader__.load({
|
|
|
4682
4849
|
* Project the shared usage snapshot into the panel's render tree.
|
|
4683
4850
|
*
|
|
4684
4851
|
* Deduplication matches the settings card: hand-edited settings can name one
|
|
4685
|
-
* credential twice
|
|
4686
|
-
* refresh lands.
|
|
4852
|
+
* credential twice.
|
|
4687
4853
|
*/
|
|
4688
4854
|
function buildPanelView(input) {
|
|
4689
4855
|
const { usage } = input;
|
|
4690
4856
|
const t = input.t ?? panelTextEN;
|
|
4691
|
-
const hidden = new Set(input.removingIds ?? []);
|
|
4692
4857
|
const seen = /* @__PURE__ */ new Set();
|
|
4693
4858
|
const entries = (usage.report?.accounts ?? []).filter((entry) => {
|
|
4694
|
-
if (hidden.has(entry.id)) return false;
|
|
4695
4859
|
if (seen.has(entry.id)) return false;
|
|
4696
4860
|
seen.add(entry.id);
|
|
4697
4861
|
return true;
|
|
@@ -4833,12 +4997,9 @@ window.__ModuleLoader__.load({
|
|
|
4833
4997
|
*/
|
|
4834
4998
|
/** The panel's view, recomputed from both seats on every notification. */
|
|
4835
4999
|
function usePanelView(props) {
|
|
4836
|
-
const usage = props.useCommandCodeUsage((state) => state);
|
|
4837
|
-
const settings = props.useCommandCodeSettings((state) => state);
|
|
4838
5000
|
return buildPanelView({
|
|
4839
|
-
usage,
|
|
4840
|
-
apiKeyConfigured:
|
|
4841
|
-
removingIds: settings.accountsRemoving,
|
|
5001
|
+
usage: props.useCommandCodeUsage((state) => state),
|
|
5002
|
+
apiKeyConfigured: props.useCommandCodeSettings((state) => state).anyAccountConfigured,
|
|
4842
5003
|
t: props.t ?? panelTextEN
|
|
4843
5004
|
});
|
|
4844
5005
|
}
|
|
@@ -6204,33 +6365,88 @@ window.__ModuleLoader__.load({
|
|
|
6204
6365
|
* that silently drops one stylesheet.
|
|
6205
6366
|
*/
|
|
6206
6367
|
const PAGE_CSS_ID = "@mars-sea/dsh-commandcode-provider/CommandCodeSettingsPage.module.css";
|
|
6207
|
-
/**
|
|
6368
|
+
/**
|
|
6369
|
+
* The settings-page stylesheet.
|
|
6370
|
+
*
|
|
6371
|
+
* The metrics are the harness's own settings pages', read out of the 0.1.7
|
|
6372
|
+
* bundles so the page sits beside General and Models without looking foreign:
|
|
6373
|
+
* a row is 16px of padding over a 0.5px border-l2 hairline, its title 14/22
|
|
6374
|
+
* label-primary and its description 12/18 label-tertiary, the control on the
|
|
6375
|
+
* right. Selectors and buttons are capsules (36px / 28px compact), inputs are
|
|
6376
|
+
* 32px with an 8px radius and a 0.5px border-l4 edge, a card is a 0.5px
|
|
6377
|
+
* border-l4 outline with a 16px radius, and a nested panel is a
|
|
6378
|
+
* bg-module-platform fill with a 12px radius. Errors use state-error-primary,
|
|
6379
|
+
* the platform's error colour (label-error is its text-only sibling).
|
|
6380
|
+
*/
|
|
6208
6381
|
const PAGE_CSS = `
|
|
6209
|
-
.cc-section{max-width:720px;color:var(--dsw-alias-label-primary);flex-direction:column;
|
|
6210
|
-
.cc-title{margin:0;font-size:
|
|
6211
|
-
.cc-intro{color:var(--dsw-alias-label-tertiary);
|
|
6212
|
-
.cc-readOnly{color:var(--dsw-alias-label-tertiary);
|
|
6213
|
-
.cc-
|
|
6382
|
+
.cc-section{max-width:720px;color:var(--dsw-alias-label-primary);flex-direction:column;display:flex}
|
|
6383
|
+
.cc-title{margin:0;color:var(--dsw-alias-label-primary);font-size:16px;font-weight:500;line-height:24px}
|
|
6384
|
+
.cc-intro{margin:4px 0 0;color:var(--dsw-alias-label-tertiary);font-size:14px;line-height:22px}
|
|
6385
|
+
.cc-readOnly{margin:8px 0 0;color:var(--dsw-alias-state-warn-label,var(--dsw-alias-label-tertiary));font-size:12px;line-height:18px}
|
|
6386
|
+
.cc-spacer{flex:1}
|
|
6387
|
+
/* Groups: a heading over hairline-separated rows, no card surface. */
|
|
6388
|
+
.cc-group{flex-direction:column;display:flex;margin-top:28px}
|
|
6389
|
+
.cc-groupHead{align-items:center;gap:8px;display:flex;min-height:28px;padding-bottom:4px}
|
|
6390
|
+
.cc-groupTitle{margin:0;color:var(--dsw-alias-label-primary);font-size:14px;font-weight:500;line-height:22px}
|
|
6391
|
+
.cc-groupDesc{margin:0 0 12px;color:var(--dsw-alias-label-tertiary);font-size:12px;line-height:18px}
|
|
6392
|
+
.cc-rows>.cc-groupDesc{margin:0;padding:4px 0 0}
|
|
6393
|
+
.cc-disclosure{width:100%;padding:0 0 4px;border:0;background:0 0;font:inherit;text-align:left;cursor:pointer;border-radius:6px}
|
|
6394
|
+
.cc-disclosure:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:2px}
|
|
6395
|
+
.cc-rows{flex-direction:column;display:flex}
|
|
6396
|
+
.cc-row{align-items:center;gap:8px;display:flex;padding:16px 0;border-bottom:.5px solid var(--dsw-alias-border-l2)}
|
|
6397
|
+
.cc-rows>.cc-row:last-child{border-bottom:0}
|
|
6398
|
+
.cc-rowNested{padding-left:16px}
|
|
6399
|
+
.cc-rowFlush{padding:0;border-bottom:0}
|
|
6400
|
+
.cc-rowText{flex-direction:column;flex:1;gap:4px;min-width:0;padding-right:32px;display:flex}
|
|
6401
|
+
.cc-rowTitleLine{align-items:center;gap:8px;display:flex;min-width:0}
|
|
6402
|
+
.cc-rowTitle{min-width:0;color:var(--dsw-alias-label-primary);font-size:14px;font-weight:400;line-height:22px}
|
|
6403
|
+
.cc-rowDesc{color:var(--dsw-alias-label-tertiary);font-size:12px;line-height:18px}
|
|
6404
|
+
.cc-rowDesc p{margin:0}
|
|
6405
|
+
.cc-rowDesc p+p{margin-top:4px}
|
|
6406
|
+
.cc-rowError{margin:0;color:var(--dsw-alias-state-error-primary);font-size:12px;line-height:18px}
|
|
6407
|
+
.cc-rowControl{flex:none;align-items:center;justify-content:flex-end;gap:8px;display:inline-flex;max-width:60%}
|
|
6408
|
+
.cc-rowInput{width:200px}
|
|
6409
|
+
.cc-rowInputWide{width:280px}
|
|
6410
|
+
/* The platform's link button: a compact capsule with no fill until hovered. */
|
|
6411
|
+
.cc-linkButton{box-sizing:border-box;flex:none;align-items:center;display:inline-flex;height:28px;padding:0 10px;border:0;border-radius:14px;background:0 0;color:var(--dsw-alias-label-tertiary);font:inherit;font-size:12px;line-height:18px;white-space:nowrap;cursor:pointer}
|
|
6412
|
+
.cc-linkButton:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-secondary)}
|
|
6413
|
+
.cc-linkButton:disabled{cursor:default;opacity:.4}
|
|
6414
|
+
.cc-linkButton:focus-visible{outline:none;box-shadow:0 0 0 2px var(--dsw-alias-border-l3)}
|
|
6415
|
+
/* Tags: the platform Tag's capsule (11/17, weight 500). */
|
|
6416
|
+
.cc-badges{align-items:center;gap:8px;display:inline-flex}
|
|
6417
|
+
.cc-badge,.cc-badgeMuted{flex:none;align-items:center;display:inline-flex;white-space:nowrap;border-radius:999px;corner-shape:round;padding:1px 8px;font-size:11px;font-weight:500;line-height:17px}
|
|
6418
|
+
.cc-badge{background:var(--dsw-alias-bg-module-platform);color:var(--dsw-alias-label-secondary)}
|
|
6419
|
+
.cc-badgeMuted{border:.5px solid var(--dsw-alias-border-l4);color:var(--dsw-alias-label-tertiary)}
|
|
6420
|
+
.cc-badgeWarn{background:var(--dsw-alias-state-warn-tertiary,var(--dsw-alias-bg-module-platform));color:var(--dsw-alias-state-warn-label,var(--dsw-alias-label-secondary))}
|
|
6421
|
+
/* The stacked field (the Models-page provider card and the account forms):
|
|
6422
|
+
* the platform SettingsForm field's metrics. */
|
|
6214
6423
|
.cc-field{flex-direction:column;gap:6px;padding:12px 0;display:flex}
|
|
6215
|
-
.cc-field+.cc-field{border-top
|
|
6424
|
+
.cc-field+.cc-field{border-top:.5px solid var(--dsw-alias-border-l2)}
|
|
6216
6425
|
.cc-fieldHead{align-items:center;gap:8px;display:flex}
|
|
6217
6426
|
.cc-label{min-width:0;color:var(--dsw-alias-label-primary);flex:1;font-size:13px;font-weight:500;line-height:1.5}
|
|
6218
|
-
.cc-
|
|
6219
|
-
.cc-badge{white-space:nowrap;background:var(--dsw-alias-bg-module-platform);color:var(--dsw-alias-label-secondary);border-radius:999px;padding:1px 8px;font-size:11px;font-weight:500;line-height:17px}
|
|
6220
|
-
.cc-badgeMuted{white-space:nowrap;color:var(--dsw-alias-label-tertiary);border-radius:999px;padding:1px 8px;font-size:11px;line-height:17px}
|
|
6221
|
-
.cc-reset{font:inherit;color:var(--dsw-alias-label-secondary);cursor:pointer;background:0 0;border:none;padding:0;font-size:12px;line-height:1.5}
|
|
6427
|
+
.cc-reset{font:inherit;color:var(--dsw-alias-label-secondary);cursor:pointer;background:0 0;border:none;padding:0;font-size:12px;line-height:18px}
|
|
6222
6428
|
.cc-reset:hover:not(:disabled){color:var(--dsw-alias-label-primary)}
|
|
6223
|
-
.cc-reset:disabled{cursor:default;opacity:.
|
|
6224
|
-
.cc-input{border:
|
|
6429
|
+
.cc-reset:disabled{cursor:default;opacity:.4}
|
|
6430
|
+
.cc-input{box-sizing:border-box;height:32px;min-width:0;padding:0 10px;border:.5px solid var(--dsw-alias-border-l4);border-radius:8px;background:var(--dsw-alias-bg-layer-1);font:inherit;color:var(--dsw-alias-label-primary);font-size:14px;line-height:22px}
|
|
6431
|
+
.cc-input::placeholder{color:var(--dsw-alias-label-dimmed)}
|
|
6225
6432
|
.cc-input:focus-visible{border-color:var(--dsw-alias-brand-primary);outline:none}
|
|
6226
|
-
.cc-input:disabled{
|
|
6227
|
-
|
|
6228
|
-
|
|
6229
|
-
|
|
6230
|
-
|
|
6231
|
-
.
|
|
6232
|
-
.cc-
|
|
6233
|
-
.cc-
|
|
6433
|
+
.cc-input:disabled{opacity:.6;cursor:default}
|
|
6434
|
+
.cc-inputInvalid{border-color:var(--dsw-alias-state-error-primary)}
|
|
6435
|
+
.cc-invalid{color:var(--dsw-alias-state-error-primary);margin:0;font-size:12px;line-height:18px}
|
|
6436
|
+
.cc-hint{color:var(--dsw-alias-label-tertiary);margin:0;font-size:12px;line-height:18px}
|
|
6437
|
+
/* The model multi-select trigger: the platform's selector capsule (the
|
|
6438
|
+
* language / permission pickers on General). */
|
|
6439
|
+
.cc-modelSelectAnchor{flex-direction:column;align-items:stretch;gap:6px;display:inline-flex;min-width:160px}
|
|
6440
|
+
.cc-selector{box-sizing:border-box;align-items:center;gap:12px;display:inline-flex;height:36px;padding:0 14px;border:0;border-radius:18px;background:var(--dsw-alias-bg-module-platform);color:var(--dsw-alias-label-primary);font:inherit;font-size:14px;line-height:22px;text-align:left;cursor:pointer}
|
|
6441
|
+
.cc-selector:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover)}
|
|
6442
|
+
.cc-selector:disabled{cursor:default;opacity:.4}
|
|
6443
|
+
.cc-selector:focus-visible{outline:none;box-shadow:0 0 0 2px var(--dsw-alias-border-l3)}
|
|
6444
|
+
.cc-selectorText{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
|
6445
|
+
.cc-selectorCaret{flex-shrink:0;border-right:1.5px solid var(--dsw-alias-label-tertiary);border-bottom:1.5px solid var(--dsw-alias-label-tertiary);width:6px;height:6px;margin-bottom:3px;transform:rotate(45deg)}
|
|
6446
|
+
/* The search box sits inside the Menu anchor, so typing in it never trips
|
|
6447
|
+
* the Menu's outside-click close. */
|
|
6448
|
+
.cc-modelSearch{width:100%}
|
|
6449
|
+
.cc-modelSearch::-webkit-search-cancel-button{cursor:pointer}
|
|
6234
6450
|
.cc-checkRow{align-items:center;gap:8px;display:inline-flex;min-width:0}
|
|
6235
6451
|
.cc-checkRow:hover{cursor:pointer}
|
|
6236
6452
|
/* A hand-drawn box rather than the platform's accent-color checkbox: the
|
|
@@ -6246,126 +6462,168 @@ window.__ModuleLoader__.load({
|
|
|
6246
6462
|
.cc-check:checked{background:var(--dsw-alias-brand-primary);border-color:var(--dsw-alias-brand-primary)}
|
|
6247
6463
|
.cc-check:checked::after{content:'';position:absolute;top:3px;left:5px;width:3px;height:7px;border:solid var(--dsw-alias-label-primary-foreground,#fff);border-width:0 1.5px 1.5px 0;transform:rotate(45deg)}
|
|
6248
6464
|
.cc-checkName{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
|
6249
|
-
/* The
|
|
6250
|
-
*
|
|
6251
|
-
*
|
|
6252
|
-
|
|
6253
|
-
.cc-
|
|
6254
|
-
.cc-
|
|
6255
|
-
.cc-
|
|
6256
|
-
|
|
6257
|
-
|
|
6258
|
-
* 36px) and forces its own menulist text metrics, so drop the native
|
|
6259
|
-
* chrome entirely (appearance:none), restore content-box so the outer box
|
|
6260
|
-
* matches the inputs again, and draw the chevron ourselves. Longhand
|
|
6261
|
-
* background-* only — the shorthand would reset .cc-input's background. */
|
|
6262
|
-
select.cc-input{appearance:none;-webkit-appearance:none;-moz-appearance:none;box-sizing:content-box;padding-right:32px;background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='7' viewBox='0 0 12 7'%3E%3Cpath d='M1 1l5 5 5-5' fill='none' stroke='%23888f98' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");background-repeat:no-repeat;background-position:right 11px center}
|
|
6263
|
-
.cc-inputInvalid{border-color:var(--dsw-alias-label-error)}
|
|
6264
|
-
.cc-invalid{color:var(--dsw-alias-label-error);margin:0;font-size:12px;line-height:1.5}
|
|
6265
|
-
/* The Advanced card: its header row is one full-width toggle button; the
|
|
6266
|
-
* chevron is two borders on a rotated square (no icon dependency). */
|
|
6267
|
-
.cc-advancedHead{align-items:center;gap:8px;display:flex;width:100%;padding:12px 0;background:0 0;border:none;cursor:pointer;font:inherit;text-align:left}
|
|
6268
|
-
.cc-advancedHead:hover .cc-advancedTitle{color:var(--dsw-alias-label-primary)}
|
|
6269
|
-
.cc-advancedTitle{color:var(--dsw-alias-label-primary);font-size:13px;font-weight:500;line-height:1.5}
|
|
6270
|
-
.cc-advancedSpacer{flex:1}
|
|
6271
|
-
.cc-chevron{flex-shrink:0;border-right:1.5px solid var(--dsw-alias-label-tertiary);border-bottom:1.5px solid var(--dsw-alias-label-tertiary);width:8px;height:8px;margin-right:4px;margin-bottom:2px;transform:rotate(45deg);transition:transform .15s ease}
|
|
6272
|
-
.cc-chevronUp{transform:rotate(-135deg);margin-bottom:-3px}
|
|
6273
|
-
.cc-advancedBody{flex-direction:column;display:flex}
|
|
6274
|
-
.cc-advancedBody>.cc-field:first-of-type{border-top:1px solid var(--dsw-alias-border-l2)}
|
|
6275
|
-
.cc-hint{color:var(--dsw-alias-label-tertiary);margin:0;font-size:12px;line-height:1.5}
|
|
6276
|
-
.cc-footer{justify-content:flex-end;align-items:center;gap:8px;display:flex}
|
|
6277
|
-
/* Keep the original footer in flow and inside the settings scrollport. */
|
|
6278
|
-
.cc-footerMarker{height:1px;margin:-6px 0 -7px;pointer-events:none}
|
|
6279
|
-
.cc-footerSticky{position:sticky;bottom:0;z-index:2;flex-shrink:0;min-height:48px;pointer-events:none}
|
|
6280
|
-
.cc-footerActions{align-items:center;gap:8px;display:flex;pointer-events:auto}
|
|
6281
|
-
.cc-footerFloating .cc-footerActions{padding:6px;border:1px solid var(--dsw-alias-border-l2);border-radius:12px;background:var(--dsw-alias-bg-layer-3,var(--dsw-alias-bg-layer-2));box-shadow:0 8px 24px rgba(0,0,0,.12)}
|
|
6282
|
-
.cc-accountEditor{border-top:1px solid var(--dsw-alias-border-l2)}
|
|
6283
|
-
.cc-accountLoginHint{padding:0 0 12px}
|
|
6284
|
-
.cc-toggleRow{align-items:center;gap:8px;cursor:pointer;display:flex}
|
|
6285
|
-
.cc-toggleRow:has(.cc-toggle:disabled){cursor:default}
|
|
6465
|
+
/* The platform SegmentedControl: a translucent track and one raised pill that
|
|
6466
|
+
* slides under the picked segment. Segments are equal grid tracks, so the
|
|
6467
|
+
* indicator's width and offset follow from the count and the index alone. */
|
|
6468
|
+
.cc-segmented{position:relative;display:inline-grid;grid-auto-flow:column;grid-auto-columns:1fr;gap:2px;padding:3px;border-radius:9px;background:var(--dsw-alias-interactive-bg-hover)}
|
|
6469
|
+
.cc-segmentIndicator{position:absolute;top:3px;left:3px;width:calc((100% - 6px - 2px * (var(--cc-segment-count) - 1)) / var(--cc-segment-count));height:calc(100% - 6px);border-radius:7px;background:var(--dsw-alias-bg-layer-1);box-shadow:var(--dsw-elevation-soft,0 1px 2px rgba(0,0,0,.12));transform:translateX(calc(var(--cc-segment-index) * (100% + 2px)));transition:transform .16s ease;pointer-events:none}
|
|
6470
|
+
.cc-segment{box-sizing:border-box;position:relative;z-index:1;height:28px;padding:0 16px;border:0;border-radius:7px;background:0 0;color:var(--dsw-alias-label-secondary);font:inherit;font-size:13px;font-weight:500;line-height:20px;white-space:nowrap;cursor:pointer;transition:color .12s ease}
|
|
6471
|
+
.cc-segment:hover:not(:disabled),.cc-segment[aria-checked=true]{color:var(--dsw-alias-label-primary)}
|
|
6472
|
+
.cc-segment:disabled{cursor:default;opacity:.4}
|
|
6473
|
+
.cc-segment:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:-2px}
|
|
6286
6474
|
/* Modelled on the platform's own Switch primitive (ui-primitives
|
|
6287
|
-
* Switch.module.css
|
|
6288
|
-
*
|
|
6289
|
-
* same shape, size and motion as every other switch in the app.
|
|
6475
|
+
* Switch.module.css): a 36x20 capsule that pads its track by 2px and slides a
|
|
6476
|
+
* 16px thumb across the 32px content box.
|
|
6290
6477
|
*
|
|
6291
6478
|
* Three things are load-bearing. (1) corner-shape:round on BOTH the track and
|
|
6292
6479
|
* the thumb: the theme sets a global superellipse corner shape on every
|
|
6293
6480
|
* element, and a superellipse capsule squares its ends off around a round
|
|
6294
|
-
* thumb (
|
|
6295
|
-
* (
|
|
6296
|
-
*
|
|
6297
|
-
*
|
|
6298
|
-
*
|
|
6299
|
-
*
|
|
6300
|
-
* (3) --dsw-alias-border-l3 as the off track: --dsw-alias-border-l2
|
|
6301
|
-
* (#ffffff1f in dark) left the unchecked track indistinguishable from the card
|
|
6302
|
-
* — the dark-mode half of the same report. */
|
|
6481
|
+
* thumb. (2) No literal colours. --dsw-alias-brand-primary INVERTS between the
|
|
6482
|
+
* two modes (near-black in light, near-white in dark), so a hardcoded white
|
|
6483
|
+
* thumb vanishes on the checked track in dark mode; the brand foreground is the
|
|
6484
|
+
* token the platform paints on that fill. (3) --dsw-alias-border-l3 as the off
|
|
6485
|
+
* track: border-l2 (#ffffff1f in dark) leaves it indistinguishable from the
|
|
6486
|
+
* page. */
|
|
6303
6487
|
.cc-toggle{box-sizing:border-box;appearance:none;flex-shrink:0;width:36px;height:20px;margin:0;padding:2px;border:0;border-radius:10px;corner-shape:round;background:var(--dsw-alias-border-l3);cursor:pointer;position:relative}
|
|
6304
6488
|
.cc-toggle:checked{background:var(--dsw-alias-brand-primary)}
|
|
6305
|
-
.cc-toggle::after{content:'';display:block;width:16px;height:16px;border-radius:50%;corner-shape:round;background:var(--dsw-alias-label-primary-foreground);
|
|
6489
|
+
.cc-toggle::after{content:'';display:block;width:16px;height:16px;border-radius:50%;corner-shape:round;background:var(--dsw-alias-label-primary-foreground);transition:transform .12s ease}
|
|
6306
6490
|
/* 32px content box minus the 16px thumb. */
|
|
6307
6491
|
.cc-toggle:checked::after{transform:translateX(16px)}
|
|
6308
6492
|
.cc-toggle:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:2px}
|
|
6309
6493
|
.cc-toggle:disabled{cursor:default;opacity:.5}
|
|
6310
|
-
.cc-
|
|
6311
|
-
.cc-
|
|
6494
|
+
.cc-chevron{flex-shrink:0;border-right:1.5px solid var(--dsw-alias-label-tertiary);border-bottom:1.5px solid var(--dsw-alias-label-tertiary);width:7px;height:7px;margin-right:6px;margin-bottom:3px;transform:rotate(45deg);transition:transform .15s ease}
|
|
6495
|
+
.cc-chevronUp{transform:rotate(-135deg);margin-bottom:-3px}
|
|
6496
|
+
/* Accounts: one outlined card per account, the Models page's provider card. */
|
|
6497
|
+
.cc-accountMode{align-items:center;gap:4px;display:flex;margin:-4px 0 12px;color:var(--dsw-alias-label-secondary);font-size:12px;line-height:18px}
|
|
6498
|
+
.cc-accountList{flex-direction:column;gap:8px;display:flex}
|
|
6499
|
+
.cc-accountList+.cc-addButton,.cc-accountList+.cc-addPanel{margin-top:8px}
|
|
6500
|
+
.cc-accountItem{flex-direction:column;gap:10px;display:flex;padding:12px 14px;border:.5px solid var(--dsw-alias-border-l4);border-radius:16px}
|
|
6501
|
+
.cc-accountItemActive{border-color:var(--dsw-static-neutral-bluish-400,var(--dsw-alias-border-l3))}
|
|
6502
|
+
.cc-accountHead{align-items:center;gap:4px;display:flex;min-height:28px}
|
|
6503
|
+
.cc-accountToggle{align-items:center;gap:8px;display:flex;flex:1;min-width:0;padding:0;background:0 0;border:0;color:inherit;cursor:pointer;font:inherit;text-align:left}
|
|
6504
|
+
.cc-accountToggle:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:2px;border-radius:4px}
|
|
6505
|
+
.cc-accountName{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--dsw-alias-label-primary);font-size:14px;font-weight:500;line-height:22px}
|
|
6506
|
+
.cc-iconButton{flex-shrink:0;align-items:center;justify-content:center;display:inline-flex;width:28px;height:28px;padding:0;background:0 0;border:0;border-radius:6px;color:var(--dsw-alias-label-tertiary);cursor:pointer}
|
|
6507
|
+
.cc-iconButton:hover{background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-primary)}
|
|
6508
|
+
.cc-iconButton:focus-visible{outline:none;box-shadow:0 0 0 2px var(--dsw-alias-border-l3)}
|
|
6509
|
+
/* Three dots from one element: the dot itself plus two box-shadow copies. */
|
|
6510
|
+
.cc-kebab{width:3px;height:3px;border-radius:50%;corner-shape:round;background:currentColor;box-shadow:0 -5px 0 currentColor,0 5px 0 currentColor}
|
|
6511
|
+
.cc-tabDot{flex-shrink:0;width:8px;height:8px;border-radius:50%;corner-shape:round}
|
|
6512
|
+
.cc-tabDotOk{background:var(--dsw-alias-state-success-primary)}
|
|
6513
|
+
.cc-tabDotWarn{background:var(--dsw-alias-state-warn-primary,#d97706)}
|
|
6514
|
+
.cc-tabDotError{background:var(--dsw-alias-state-error-primary)}
|
|
6515
|
+
.cc-accountMeters{flex-wrap:wrap;gap:6px 20px;display:flex;padding-left:16px}
|
|
6516
|
+
.cc-miniMeter{align-items:center;gap:8px;display:inline-flex;font-size:12px;line-height:18px}
|
|
6517
|
+
.cc-miniMeterLabel{color:var(--dsw-alias-label-tertiary)}
|
|
6518
|
+
.cc-miniMeterTrack{overflow:hidden;background:var(--dsw-alias-bg-module-platform);border-radius:999px;width:64px;height:4px}
|
|
6519
|
+
.cc-miniMeterFill{display:block;background:var(--dsw-alias-brand-primary);border-radius:999px;height:100%}
|
|
6520
|
+
.cc-miniMeterValue{color:var(--dsw-alias-label-secondary);font-variant-numeric:tabular-nums}
|
|
6521
|
+
.cc-accountSetup,.cc-loginStatus{flex-direction:column;gap:8px;display:flex;padding-left:16px}
|
|
6522
|
+
.cc-loginStatus{flex-direction:row;flex-wrap:wrap;align-items:center;gap:4px 12px}
|
|
6523
|
+
.cc-loginStatus>p{margin:0;font-size:12px;line-height:18px}
|
|
6524
|
+
.cc-accountDetails{container-type:inline-size;border-top:.5px solid var(--dsw-alias-border-l2);flex-direction:column;gap:14px;display:flex;padding-top:12px}
|
|
6525
|
+
.cc-inlineForm,.cc-confirmBar{flex-direction:column;gap:8px;display:flex}
|
|
6526
|
+
.cc-inlineActions{align-items:center;gap:8px;display:flex}
|
|
6527
|
+
.cc-confirmBar{border:.5px solid var(--dsw-alias-state-error-primary);border-radius:12px;padding:12px 14px}
|
|
6528
|
+
.cc-confirmText{color:var(--dsw-alias-label-primary);margin:0;font-size:14px;line-height:22px}
|
|
6529
|
+
.cc-dangerButton{color:var(--dsw-alias-state-error-primary);border-color:var(--dsw-alias-state-error-primary)}
|
|
6530
|
+
.cc-dangerButton:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover-danger)}
|
|
6531
|
+
/* Adding an account: the Models page's dashed add button, opening a filled
|
|
6532
|
+
* panel in its place. */
|
|
6533
|
+
.cc-addButton{box-sizing:border-box;align-items:center;justify-content:center;gap:6px;display:flex;width:100%;height:44px;border:1px dashed var(--dsw-alias-border-l3);border-radius:16px;background:0 0;color:var(--dsw-alias-label-primary);font:inherit;font-size:14px;line-height:22px;cursor:pointer}
|
|
6534
|
+
.cc-addButton:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover)}
|
|
6535
|
+
.cc-addButton:disabled{cursor:default;opacity:.4}
|
|
6536
|
+
.cc-addButton:focus-visible{outline:none;box-shadow:0 0 0 2px var(--dsw-alias-border-l3)}
|
|
6537
|
+
/* A plus from two crossed bars. */
|
|
6538
|
+
.cc-addGlyph{position:relative;width:12px;height:12px}
|
|
6539
|
+
.cc-addGlyph::before,.cc-addGlyph::after{content:'';position:absolute;background:currentColor;border-radius:1px}
|
|
6540
|
+
.cc-addGlyph::before{left:0;right:0;top:5.25px;height:1.5px}
|
|
6541
|
+
.cc-addGlyph::after{top:0;bottom:0;left:5.25px;width:1.5px}
|
|
6542
|
+
.cc-addPanel{flex-direction:column;gap:12px;display:flex;padding:14px 16px;border-radius:12px;background:var(--dsw-alias-bg-module-platform)}
|
|
6543
|
+
.cc-panelTitle{color:var(--dsw-alias-label-primary);font-size:14px;font-weight:500;line-height:22px}
|
|
6544
|
+
/* Account report: stat tiles are filled panels, like the platform's editors. */
|
|
6545
|
+
.cc-accountReport{flex-direction:column;gap:14px;display:flex}
|
|
6312
6546
|
.cc-usageHead{align-items:center;gap:8px;display:flex}
|
|
6313
|
-
.cc-usageTitle{color:var(--dsw-alias-label-primary);flex:1;margin:0;font-size:
|
|
6314
|
-
.cc-usageAccount{max-width:40%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
|
|
6315
|
-
.cc-usagePlan{white-space:nowrap;
|
|
6316
|
-
.cc-usagePlanStatus{white-space:nowrap;color:var(--dsw-alias-
|
|
6317
|
-
.cc-
|
|
6318
|
-
|
|
6319
|
-
.
|
|
6320
|
-
|
|
6321
|
-
.
|
|
6547
|
+
.cc-usageTitle{color:var(--dsw-alias-label-primary);flex:1;margin:0;font-size:14px;font-weight:500;line-height:22px}
|
|
6548
|
+
.cc-usageAccount{max-width:40%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--dsw-alias-label-tertiary);font-size:12px;line-height:18px}
|
|
6549
|
+
.cc-usagePlan{flex:none;white-space:nowrap;border:.5px solid var(--dsw-alias-border-l3);color:var(--dsw-alias-label-secondary);border-radius:999px;corner-shape:round;padding:0 7px;font-size:11px;font-weight:500;line-height:17px}
|
|
6550
|
+
.cc-usagePlanStatus{flex:none;margin:0;white-space:nowrap;color:var(--dsw-alias-state-error-primary);font-size:12px;line-height:18px}
|
|
6551
|
+
.cc-usageHint{color:var(--dsw-alias-label-tertiary);margin:0;font-size:12px;line-height:18px}
|
|
6552
|
+
/* Two fixed grids instead of one auto-fit track: auto-fit wrapped the four
|
|
6553
|
+
* activity tiles 3 + 1 at the details' width. Each group fills its own row
|
|
6554
|
+
* (four activity tiles, three balances) and the activity row folds to 2 × 2
|
|
6555
|
+
* in a narrow details panel. */
|
|
6322
6556
|
.cc-usageStats{grid-template-columns:repeat(auto-fit,minmax(120px,1fr));gap:8px;display:grid}
|
|
6323
|
-
.cc-
|
|
6324
|
-
.cc-
|
|
6325
|
-
.cc-
|
|
6326
|
-
.cc-
|
|
6327
|
-
.cc-
|
|
6557
|
+
.cc-usageStatsActivity{grid-template-columns:repeat(4,minmax(0,1fr))}
|
|
6558
|
+
.cc-usageStatsBalance{grid-template-columns:repeat(3,minmax(0,1fr))}
|
|
6559
|
+
@container (max-width:460px){.cc-usageStatsActivity{grid-template-columns:repeat(2,minmax(0,1fr))}}
|
|
6560
|
+
.cc-usageStat{min-width:0;flex-direction:column;gap:2px;display:flex;padding:10px 12px;border-radius:12px;background:var(--dsw-alias-bg-module-platform)}
|
|
6561
|
+
.cc-usageStatLabel{color:var(--dsw-alias-label-tertiary);font-size:12px;line-height:18px}
|
|
6562
|
+
.cc-usageStatValue{color:var(--dsw-alias-label-primary);font-size:16px;font-weight:500;line-height:24px;font-variant-numeric:tabular-nums}
|
|
6563
|
+
.cc-usageStatSub{color:var(--dsw-alias-label-tertiary);font-size:12px;line-height:18px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
|
6564
|
+
.cc-usageWindows{flex-direction:column;gap:14px;display:flex}
|
|
6328
6565
|
.cc-usageWindow{flex-direction:column;gap:6px;display:flex}
|
|
6329
6566
|
.cc-usageWindowHead{align-items:baseline;gap:8px;display:flex}
|
|
6330
|
-
.cc-usageWindowLabel{color:var(--dsw-alias-label-secondary);flex:1;font-size:12px;
|
|
6331
|
-
.cc-usageWindowValue{color:var(--dsw-alias-label-primary);font-size:12px;
|
|
6332
|
-
.cc-usageExceeded{color:var(--dsw-alias-
|
|
6333
|
-
.cc-usageBar{overflow:hidden;background:var(--dsw-alias-bg-
|
|
6567
|
+
.cc-usageWindowLabel{color:var(--dsw-alias-label-secondary);flex:1;font-size:12px;line-height:18px}
|
|
6568
|
+
.cc-usageWindowValue{color:var(--dsw-alias-label-primary);font-size:12px;line-height:18px;font-variant-numeric:tabular-nums}
|
|
6569
|
+
.cc-usageExceeded{color:var(--dsw-alias-state-error-primary);font-size:12px;line-height:18px}
|
|
6570
|
+
.cc-usageBar{overflow:hidden;background:var(--dsw-alias-bg-module-platform);border-radius:999px;height:4px}
|
|
6334
6571
|
.cc-usageBarFill{background:var(--dsw-alias-brand-primary);border-radius:999px;height:100%;transition:width .3s ease}
|
|
6335
|
-
.cc-usageBarFillWarn{background:var(--dsw-alias-
|
|
6336
|
-
|
|
6572
|
+
.cc-usageBarFillWarn{background:var(--dsw-alias-state-error-primary)}
|
|
6573
|
+
.cc-usageWindowReset{color:var(--dsw-alias-label-tertiary);margin:0;font-size:12px;line-height:18px}
|
|
6337
6574
|
.cc-usageMeta{align-items:center;gap:8px;display:flex}
|
|
6338
|
-
.cc-accountReport{flex-direction:column;gap:12px;display:flex}
|
|
6339
|
-
.cc-tabs{flex-wrap:wrap;gap:6px;display:flex}
|
|
6340
|
-
.cc-tab{align-items:center;font:inherit;color:var(--dsw-alias-label-secondary);cursor:pointer;background:var(--dsw-alias-bg-layer-1);border:1px solid var(--dsw-alias-border-l2);border-radius:999px;padding:2px 10px;font-size:12px;line-height:18px;display:inline-flex;gap:6px}
|
|
6341
|
-
.cc-tab:hover:not(.cc-tabActive){color:var(--dsw-alias-label-primary)}
|
|
6342
|
-
.cc-tabActive{color:var(--dsw-alias-label-primary);border-color:var(--dsw-alias-brand-primary)}
|
|
6343
|
-
.cc-tabDotOk{background:var(--dsw-alias-brand-primary);border-radius:50%;width:6px;height:6px}
|
|
6344
|
-
.cc-tabDotWarn{background:#d97706;border-radius:50%;width:6px;height:6px}
|
|
6345
|
-
.cc-tabDotError{background:var(--dsw-alias-label-error);border-radius:50%;width:6px;height:6px}
|
|
6346
6575
|
.cc-usageMetaSpacer{flex:1}
|
|
6347
|
-
.cc-usageUpdated{color:var(--dsw-alias-label-tertiary);margin:0;font-size:
|
|
6348
|
-
.cc-usagePartial{color:var(--dsw-alias-
|
|
6349
|
-
.cc-usageBlocked{border
|
|
6350
|
-
.cc-usageBlockedTitle{color:var(--dsw-alias-
|
|
6351
|
-
.cc-usageBlockedHint{color:var(--dsw-alias-label-secondary);margin:0;font-size:12px;line-height:
|
|
6352
|
-
.cc-usageBlockedDetail{color:var(--dsw-alias-label-
|
|
6353
|
-
.cc-version{margin:
|
|
6576
|
+
.cc-usageUpdated{color:var(--dsw-alias-label-tertiary);margin:0;font-size:12px;line-height:18px}
|
|
6577
|
+
.cc-usagePartial{color:var(--dsw-alias-state-error-primary);margin:0;font-size:12px;line-height:18px}
|
|
6578
|
+
.cc-usageBlocked{border:.5px solid var(--dsw-alias-state-error-primary);border-radius:12px;padding:12px 14px;display:flex;flex-direction:column;gap:4px}
|
|
6579
|
+
.cc-usageBlockedTitle{color:var(--dsw-alias-state-error-primary);margin:0;font-size:14px;font-weight:500;line-height:22px}
|
|
6580
|
+
.cc-usageBlockedHint{color:var(--dsw-alias-label-secondary);margin:0;font-size:12px;line-height:18px}
|
|
6581
|
+
.cc-usageBlockedDetail{color:var(--dsw-alias-label-tertiary);margin:0;font-size:12px;line-height:18px;word-break:break-word}
|
|
6582
|
+
.cc-version{margin:32px 0 0;text-align:center;color:var(--dsw-alias-label-tertiary);font-size:12px;line-height:18px}
|
|
6583
|
+
/* The update hint rides the footer version line, quiet until hovered. */
|
|
6584
|
+
.cc-versionLink{color:var(--dsw-alias-state-warn-label,var(--dsw-alias-label-secondary));text-decoration:none}
|
|
6585
|
+
.cc-versionLink:hover{color:var(--dsw-alias-label-primary);text-decoration:underline;text-underline-position:under}
|
|
6354
6586
|
/* The Models-page provider panel root: unstyled by design — the row card the
|
|
6355
6587
|
* Models page owns supplies the surface, the panel only stacks its controls. */
|
|
6356
6588
|
.cc-providerCard{flex-direction:column;display:flex}
|
|
6357
|
-
|
|
6358
|
-
|
|
6359
|
-
.
|
|
6360
|
-
.cc-
|
|
6361
|
-
/* The login panel rides the connection card as one more field row; the
|
|
6362
|
-
* authorization link is the only branded element on it. */
|
|
6363
|
-
.cc-loginLink{color:var(--dsw-alias-brand-primary);text-decoration:none;font-size:12px;line-height:1.5}
|
|
6589
|
+
.cc-footer{justify-content:flex-end;align-items:center;gap:8px;display:flex}
|
|
6590
|
+
.cc-failed{display:inline-block;max-width:260px;min-width:0;color:var(--dsw-alias-state-error-primary);margin:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:12px;line-height:18px}
|
|
6591
|
+
/* The authorization link is the only link-coloured element on a login row. */
|
|
6592
|
+
.cc-loginLink{color:var(--dsw-alias-link,var(--dsw-alias-brand-primary));text-decoration:none;font-size:12px;line-height:18px}
|
|
6364
6593
|
.cc-loginLink:hover{text-decoration:underline;text-underline-position:under}
|
|
6365
6594
|
.cc-loginDone{color:var(--dsw-alias-state-success-primary,var(--dsw-alias-label-secondary))}
|
|
6366
|
-
.cc-loginError{color:var(--dsw-alias-
|
|
6367
|
-
|
|
6368
|
-
.
|
|
6595
|
+
.cc-loginError{color:var(--dsw-alias-state-error-primary)}
|
|
6596
|
+
/* The floating save bar: a centered capsule pinned to the bottom of the
|
|
6597
|
+
* settings scrollport. The dock is a zero-height sticky strip at the end of
|
|
6598
|
+
* the page, so the bar overlays the scrolling content instead of taking a row
|
|
6599
|
+
* of its own; while it is shown the section reserves room at its end so the
|
|
6600
|
+
* last line can still scroll clear of it. Hidden, it sinks and fades, and
|
|
6601
|
+
* visibility (switched after the transition) takes its buttons out of the tab
|
|
6602
|
+
* order.
|
|
6603
|
+
*
|
|
6604
|
+
* Its surface is an elevated layer (bg-layer-1 over the settings panel's
|
|
6605
|
+
* layer-2) with the platform's prominent elevation and no stroke. The corners
|
|
6606
|
+
* are concentric: a 44px capsule around 36px capsule buttons inset by 4px, so
|
|
6607
|
+
* the gap around each button is even. */
|
|
6608
|
+
.cc-sectionWithBar{padding-bottom:80px}
|
|
6609
|
+
.cc-saveBarDock{position:sticky;bottom:0;z-index:2;height:0;pointer-events:none}
|
|
6610
|
+
.cc-saveBar{--cc-saveBar-tone:var(--dsw-alias-state-warn-primary,#d97706);position:absolute;left:50%;bottom:20px;box-sizing:border-box;width:max-content;max-width:calc(100% - 24px);align-items:center;gap:10px;display:flex;height:44px;padding:4px 4px 4px 16px;border:0;border-radius:22px;corner-shape:round;background:var(--dsw-alias-bg-layer-1);box-shadow:var(--dsw-elevation-prominent,0 12px 32px -8px rgba(0,0,0,.24),0 2px 8px rgba(0,0,0,.08));opacity:0;visibility:hidden;transform:translate(-50%,12px);transition:opacity .16s ease,transform .16s ease,visibility 0s linear .16s}
|
|
6611
|
+
.cc-saveBarShown{opacity:1;visibility:visible;transform:translate(-50%,0);pointer-events:auto;transition:opacity .2s ease,transform .24s cubic-bezier(.2,.9,.3,1.1),visibility 0s}
|
|
6612
|
+
.cc-saveBar-error{--cc-saveBar-tone:var(--dsw-alias-state-error-primary)}
|
|
6613
|
+
.cc-saveBar-success{--cc-saveBar-tone:var(--dsw-alias-state-success-primary,#16a34a);padding-right:18px}
|
|
6614
|
+
.cc-saveBarIcon{flex-shrink:0;align-items:center;justify-content:center;display:inline-flex;width:16px;height:16px;color:var(--cc-saveBar-tone)}
|
|
6615
|
+
.cc-saveBarPulse{width:8px;height:8px;border-radius:50%;corner-shape:round;background:var(--cc-saveBar-tone)}
|
|
6616
|
+
.cc-saveBarText{min-width:0;margin:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--dsw-alias-label-primary);font-size:14px;line-height:22px}
|
|
6617
|
+
.cc-saveBar-error .cc-saveBarText{color:var(--dsw-alias-state-error-primary)}
|
|
6618
|
+
.cc-saveBarActions{flex-shrink:0;align-items:center;gap:4px;display:flex;margin-left:8px}
|
|
6619
|
+
.cc-saveBarButton{box-sizing:border-box;height:36px;padding:0 16px;border:0;border-radius:18px;corner-shape:round;font:inherit;font-size:14px;line-height:22px;white-space:nowrap;cursor:pointer}
|
|
6620
|
+
.cc-saveBarButton:focus-visible{outline:none;box-shadow:0 0 0 2px var(--dsw-alias-border-l3)}
|
|
6621
|
+
.cc-saveBarButton:disabled{cursor:default;opacity:.4}
|
|
6622
|
+
.cc-saveBarGhost{background:0 0;color:var(--dsw-alias-label-primary)}
|
|
6623
|
+
.cc-saveBarGhost:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover)}
|
|
6624
|
+
.cc-saveBarPrimary{background:var(--dsw-alias-button-primary-fill,var(--dsw-alias-brand-primary));color:var(--dsw-alias-label-primary-foreground,#fff)}
|
|
6625
|
+
.cc-saveBarPrimary:hover:not(:disabled){background:var(--dsw-alias-button-primary-hover,var(--dsw-alias-button-primary-fill,var(--dsw-alias-brand-primary)))}
|
|
6626
|
+
@media (prefers-reduced-motion:reduce){.cc-chevron,.cc-toggle::after,.cc-usageBarFill,.cc-saveBar,.cc-saveBarShown,.cc-segmentIndicator,.cc-segment{transition:none}}
|
|
6369
6627
|
`;
|
|
6370
6628
|
//#endregion
|
|
6371
6629
|
//#region src/client/panel-styles.ts
|
|
@@ -6549,7 +6807,7 @@ select.cc-input{appearance:none;-webkit-appearance:none;-moz-appearance:none;box
|
|
|
6549
6807
|
const zh = {
|
|
6550
6808
|
nav: "Command Code",
|
|
6551
6809
|
title: "Command Code",
|
|
6552
|
-
intro: "
|
|
6810
|
+
intro: "账户操作立即生效;其余设置修改后点击底部「保存」。API 密钥仅保存在本机凭据服务中,不会回显。",
|
|
6553
6811
|
apiKey: "API 密钥",
|
|
6554
6812
|
apiKeyHint: "在 commandcode.ai 控制台创建。留空保存不会覆盖已存储的密钥。",
|
|
6555
6813
|
apiKeySet: "已配置",
|
|
@@ -6557,8 +6815,6 @@ select.cc-input{appearance:none;-webkit-appearance:none;-moz-appearance:none;box
|
|
|
6557
6815
|
apiKeyLocked: "密钥由只读来源提供",
|
|
6558
6816
|
apiBase: "API 地址",
|
|
6559
6817
|
apiBaseHint: "默认 https://api.commandcode.ai,一般无需修改。",
|
|
6560
|
-
workingDir: "工作目录",
|
|
6561
|
-
workingDirHint: "可选。留空时使用占位符显示的进程工作目录;仅在需要固定路径时填写。",
|
|
6562
6818
|
requestTimeoutMs: "请求超时(毫秒)",
|
|
6563
6819
|
requestTimeoutMsHint: "等待响应首个字节的超时;默认 60000。",
|
|
6564
6820
|
streamIdleTimeoutMs: "流空闲超时(毫秒)",
|
|
@@ -6566,47 +6822,29 @@ select.cc-input{appearance:none;-webkit-appearance:none;-moz-appearance:none;box
|
|
|
6566
6822
|
transportMaxRetries: "网络失败重试次数",
|
|
6567
6823
|
transportMaxRetriesHint: "连接失败时自动重试几次,默认 5(各次等待 0.5+1+2+4+8 秒,合计约 15 秒)。超过后直接报错,不再按指数退避长时间等待;填 0 表示不重试。限流、5xx 等由服务端要求重试的失败不受此项影响。",
|
|
6568
6824
|
advancedSettings: "高级设置",
|
|
6569
|
-
advancedSettingsHint: "API
|
|
6825
|
+
advancedSettingsHint: "API 地址和网络参数,一般无需修改。",
|
|
6570
6826
|
advancedOverriddenOne: "已自定义 1 项",
|
|
6571
6827
|
advancedOverriddenMany: "已自定义 {count} 项",
|
|
6572
6828
|
advancedInvalid: "高级设置中有未填好的数字,请展开修正后再保存。",
|
|
6573
6829
|
filterModelsByPlan: "隐藏套餐外模型",
|
|
6574
|
-
filterModelsByPlanHint: "
|
|
6830
|
+
filterModelsByPlanHint: "只显示套餐内模型;按需余额账户仍显示全部。",
|
|
6575
6831
|
webSearch: "用 Command Code 承载联网搜索",
|
|
6576
|
-
webSearchHint: "
|
|
6832
|
+
webSearchHint: "使用同一密钥承载 dsh 的 web_search;关闭后恢复原搜索后端。",
|
|
6577
6833
|
showSidebarQuota: "在侧边栏显示额度卡片",
|
|
6578
|
-
showSidebarQuotaHint: "
|
|
6834
|
+
showSidebarQuotaHint: "在侧边栏底部显示套餐与额度卡片,点击可打开完整仪表盘。默认关闭,关闭时不会为它后台刷新用量。",
|
|
6579
6835
|
commandGuard: "AI 命令安全预判",
|
|
6580
|
-
commandGuardHint: "命令确认弹窗前,先由 typesafe/jev
|
|
6581
|
-
|
|
6582
|
-
|
|
6583
|
-
|
|
6584
|
-
|
|
6836
|
+
commandGuardHint: "命令确认弹窗前,先由 typesafe/jev 判断它是否安全;沙箱提权还会额外判断范围与必要性。三项都足够确定才自动放行,其余照常弹窗。默认关闭。",
|
|
6837
|
+
commandGuardLevel: "自动放行阈值",
|
|
6838
|
+
commandGuardLevelHint: "阈值越高,需要越确定才放行,弹窗越多。高 = 0.95,中 = 0.9(默认),低 = 0.8。判定超过 3 秒未返回会照常弹窗。",
|
|
6839
|
+
commandGuardLevelHigh: "高",
|
|
6840
|
+
commandGuardLevelMedium: "中",
|
|
6841
|
+
commandGuardLevelLow: "低",
|
|
6585
6842
|
zdr: "零数据保留(ZDR)",
|
|
6586
|
-
zdrHint: "
|
|
6587
|
-
accountsTitle: "
|
|
6588
|
-
accountsHint: "
|
|
6843
|
+
zdrHint: "仅使用不留存、不训练的上游;可用模型可能更少且价格通常更高。",
|
|
6844
|
+
accountsTitle: "账户",
|
|
6845
|
+
accountsHint: "账户操作立即生效,无需保存。可以添加多个账户:当前账户达到用量限额或密钥失效时,请求会自动切换到下一个。",
|
|
6589
6846
|
accountAdd: "添加账户",
|
|
6590
|
-
accountRemove: "移除",
|
|
6591
|
-
accountLabel: "账户备注名",
|
|
6592
|
-
accountKey: "API 密钥",
|
|
6593
|
-
accountKeyHint: "该账户的 API 密钥。留空保存不会覆盖已存储的密钥。",
|
|
6594
|
-
accountLoginAfterSave: "先点击页面底部的「保存」,再点击该账户的「登录 Command Code」通过网页获取密钥。",
|
|
6595
6847
|
accountDefault: "默认账户",
|
|
6596
|
-
activeAccount: "当前使用账户",
|
|
6597
|
-
activeAccountAuto: "自动(第一个可用账户)",
|
|
6598
|
-
activeAccountHint: "手动指定优先使用的账户,保存后下次请求即生效;所选账户耗尽时仍会自动切换到其他可用账户。",
|
|
6599
|
-
rulesTitle: "按模型切换账户",
|
|
6600
|
-
rulesHint: "选择模型并路由到某个账户(可多选)。命中规则的模型且该账户可用时优先使用;账户耗尽或密钥失效时仍自动回落到其他账户。规则按列表顺序匹配,第一条命中生效。",
|
|
6601
|
-
rulesEmpty: "尚未配置规则。",
|
|
6602
|
-
rulesCatalogFailed: "模型目录获取失败,暂时无法选择模型;已保存的规则仍会生效。",
|
|
6603
|
-
ruleAdd: "添加规则",
|
|
6604
|
-
ruleRemove: "移除",
|
|
6605
|
-
ruleModel: "模型",
|
|
6606
|
-
ruleModelPick: "选择模型…",
|
|
6607
|
-
ruleModelCount: "已选 {count} 个模型",
|
|
6608
|
-
ruleAccount: "目标账户",
|
|
6609
|
-
ruleHint: "从下拉列表勾选要路由的模型(可多选),再选择目标账户。",
|
|
6610
6848
|
modelSearchPlaceholder: "搜索模型…",
|
|
6611
6849
|
modelSearchEmpty: "没有匹配的模型。",
|
|
6612
6850
|
modelStale: "已下架",
|
|
@@ -6623,20 +6861,19 @@ select.cc-input{appearance:none;-webkit-appearance:none;-moz-appearance:none;box
|
|
|
6623
6861
|
numberTooSmall: "不能小于 1(毫秒)",
|
|
6624
6862
|
numberTooLarge: "超出允许上限(2147483647 毫秒)",
|
|
6625
6863
|
readOnly: "当前配置为只读。",
|
|
6626
|
-
unsaved: "未保存",
|
|
6627
6864
|
save: "保存",
|
|
6628
6865
|
saving: "保存中",
|
|
6629
|
-
saved: "
|
|
6866
|
+
saved: "设置已保存",
|
|
6630
6867
|
saveFailed: "保存失败,请重试。",
|
|
6868
|
+
unsavedChanges: "有未保存的更改",
|
|
6869
|
+
saveInvalid: "有字段填写不正确,修正后才能保存",
|
|
6631
6870
|
discard: "放弃",
|
|
6632
6871
|
cancel: "取消",
|
|
6633
6872
|
show: "显示",
|
|
6634
6873
|
hide: "隐藏",
|
|
6635
|
-
usageTitle: "账户用量",
|
|
6636
6874
|
usageRefresh: "刷新",
|
|
6637
6875
|
usageRefreshing: "刷新中…",
|
|
6638
6876
|
usageLoading: "正在获取账户用量…",
|
|
6639
|
-
usageNoKey: "配置 API 密钥后,这里会显示账户的用量与额度状态。",
|
|
6640
6877
|
usageError: "用量获取失败",
|
|
6641
6878
|
usageRequests: "请求",
|
|
6642
6879
|
usageFailed: "失败",
|
|
@@ -6650,12 +6887,10 @@ select.cc-input{appearance:none;-webkit-appearance:none;-moz-appearance:none;box
|
|
|
6650
6887
|
usageFree: "赠送",
|
|
6651
6888
|
usageFiveHour: "5 小时窗口",
|
|
6652
6889
|
usageWeekly: "每周窗口",
|
|
6890
|
+
usageMonthlyLeft: "月额度剩余",
|
|
6653
6891
|
usageExceeded: "已超限",
|
|
6654
6892
|
usageReset: "重置于",
|
|
6655
6893
|
usagePartial: "部分端点数据不可用",
|
|
6656
|
-
usageKeyClear: "清除已存密钥",
|
|
6657
|
-
usageKeyClearStaged: "将清除(保存后生效)",
|
|
6658
|
-
usageUndoKeyClear: "撤销清除",
|
|
6659
6894
|
usageKeyInvalid: "API 密钥无效或已过期",
|
|
6660
6895
|
usageKeyInvalidHint: "服务端拒绝了全部请求(401)。请检查该账户配置的密钥,或到 commandcode.ai 控制台重新生成。",
|
|
6661
6896
|
usageServiceUnavailable: "Command Code 服务暂时不可用",
|
|
@@ -6673,8 +6908,7 @@ select.cc-input{appearance:none;-webkit-appearance:none;-moz-appearance:none;box
|
|
|
6673
6908
|
updateAvailable: "可更新",
|
|
6674
6909
|
updateHint: "已发布新版本,点击查看发布说明;更新插件后刷新本页,提示会自动消失。",
|
|
6675
6910
|
loginTitle: "通过官方登录获取密钥",
|
|
6676
|
-
loginHintIdle: "
|
|
6677
|
-
loginSaveBefore: "请先保存本页更改,再通过网页登录此账户。",
|
|
6911
|
+
loginHintIdle: "也可通过网页授权自动获取并保存密钥。",
|
|
6678
6912
|
loginButton: "登录 Command Code",
|
|
6679
6913
|
loginStarting: "正在启动本地回调服务…",
|
|
6680
6914
|
loginWaiting: "等待在浏览器中完成授权…",
|
|
@@ -6692,12 +6926,47 @@ select.cc-input{appearance:none;-webkit-appearance:none;-moz-appearance:none;box
|
|
|
6692
6926
|
cardTitle: "Command Code",
|
|
6693
6927
|
cardRouteActive: "已启用",
|
|
6694
6928
|
cardLoadingHint: "正在读取 Command Code 配置…",
|
|
6695
|
-
cardRegistrationHint: "此卡片由 Command Code 插件提供,正在等待设置页装配,稍后即可显示完整内容。"
|
|
6929
|
+
cardRegistrationHint: "此卡片由 Command Code 插件提供,正在等待设置页装配,稍后即可显示完整内容。",
|
|
6930
|
+
accountsRotationHint: "账户操作立即生效。当前账户达到用量限额或密钥失效时,请求会按列表顺序自动切换到下一个可用账户。",
|
|
6931
|
+
accountModeAuto: "使用方式:自动切换(按列表顺序)",
|
|
6932
|
+
accountModePinned: "使用方式:固定使用「{name}」,不可用时仍会自动切换",
|
|
6933
|
+
accountStatusPinned: "已固定",
|
|
6934
|
+
accountActions: "账户操作",
|
|
6935
|
+
accountActionPin: "固定使用此账户",
|
|
6936
|
+
accountActionUnpin: "恢复自动切换",
|
|
6937
|
+
accountActionLogin: "网页登录获取新密钥",
|
|
6938
|
+
accountActionKey: "粘贴新密钥",
|
|
6939
|
+
accountActionRename: "重命名",
|
|
6940
|
+
accountActionClearKey: "删除密钥",
|
|
6941
|
+
accountActionRemove: "移除账户",
|
|
6942
|
+
accountRemoveConfirm: "移除「{name}」?它保存的密钥和专用模型设置会一起删除。",
|
|
6943
|
+
accountClearKeyConfirm: "删除默认账户保存的密钥?之后会回退到环境变量或官方 CLI 的登录信息。",
|
|
6944
|
+
accountConfirmRemove: "移除",
|
|
6945
|
+
accountConfirmClear: "删除",
|
|
6946
|
+
accountApply: "确定",
|
|
6947
|
+
accountNoKeyHint: "还没有密钥:通过网页登录自动获取,或粘贴在 commandcode.ai 控制台创建的 API 密钥。",
|
|
6948
|
+
accountAddHint: "网页登录会打开 Command Code 授权页,授权完成后账户自动添加;也可以直接粘贴 API 密钥。",
|
|
6949
|
+
accountAddLogin: "网页登录",
|
|
6950
|
+
accountAddPaste: "粘贴密钥",
|
|
6951
|
+
accountAddConfirm: "添加",
|
|
6952
|
+
accountNamePlaceholder: "备注名(可选,网页登录时默认使用账户名)",
|
|
6953
|
+
accountNameN: "账户 {n}",
|
|
6954
|
+
accountKeyPlaceholder: "粘贴 API 密钥",
|
|
6955
|
+
accountOpFailed: "账户操作未能完成,请重试。",
|
|
6956
|
+
accountModels: "专用模型",
|
|
6957
|
+
accountModelsHint: "选中的模型优先由此账户处理,此账户不可用时仍会自动切换。一个模型只属于一个账户,选中会把它从其他账户移过来。",
|
|
6958
|
+
accountModelOwner: "属于 {name}",
|
|
6959
|
+
modelPick: "选择模型…",
|
|
6960
|
+
modelCount: "已选 {count} 个模型",
|
|
6961
|
+
modelCatalogFailed: "模型目录获取失败,暂时无法选择模型;已保存的设置仍会生效。",
|
|
6962
|
+
modelsTitle: "模型",
|
|
6963
|
+
privacyTitle: "隐私与安全",
|
|
6964
|
+
integrationsTitle: "集成与显示"
|
|
6696
6965
|
};
|
|
6697
6966
|
const en = {
|
|
6698
6967
|
nav: "Command Code",
|
|
6699
6968
|
title: "Command Code",
|
|
6700
|
-
intro: "
|
|
6969
|
+
intro: "Account actions apply immediately; other settings are written by Save at the bottom. The API key stays in the local credential service and is never echoed.",
|
|
6701
6970
|
apiKey: "API key",
|
|
6702
6971
|
apiKeyHint: "Create one in the commandcode.ai console. Saving with this field blank keeps the stored key.",
|
|
6703
6972
|
apiKeySet: "Configured",
|
|
@@ -6705,8 +6974,6 @@ select.cc-input{appearance:none;-webkit-appearance:none;-moz-appearance:none;box
|
|
|
6705
6974
|
apiKeyLocked: "Key provided by a read-only source",
|
|
6706
6975
|
apiBase: "API base URL",
|
|
6707
6976
|
apiBaseHint: "Defaults to https://api.commandcode.ai; usually leave as-is.",
|
|
6708
|
-
workingDir: "Working directory",
|
|
6709
|
-
workingDirHint: "Optional. Leave blank to use the process cwd shown as the placeholder; fill in only to pin a specific path.",
|
|
6710
6977
|
requestTimeoutMs: "Request timeout (ms)",
|
|
6711
6978
|
requestTimeoutMsHint: "Time to wait for the first response byte; default 60000.",
|
|
6712
6979
|
streamIdleTimeoutMs: "Stream idle timeout (ms)",
|
|
@@ -6714,47 +6981,29 @@ select.cc-input{appearance:none;-webkit-appearance:none;-moz-appearance:none;box
|
|
|
6714
6981
|
transportMaxRetries: "Transport retries",
|
|
6715
6982
|
transportMaxRetriesHint: "How many times a failed connection is retried automatically; default 5 (waits of 0.5+1+2+4+8s, ~15s in total). After that the failure is reported instead of waiting on the exponential backoff. 0 disables the retries. Rate limits and 5xx answers keep their own, longer retry window.",
|
|
6716
6983
|
advancedSettings: "Advanced",
|
|
6717
|
-
advancedSettingsHint: "
|
|
6984
|
+
advancedSettingsHint: "API base URL and network limits; usually leave as-is.",
|
|
6718
6985
|
advancedOverriddenOne: "1 customized",
|
|
6719
6986
|
advancedOverriddenMany: "{count} customized",
|
|
6720
6987
|
advancedInvalid: "A number in Advanced settings is not ready to save; expand to fix it.",
|
|
6721
6988
|
filterModelsByPlan: "Hide out-of-plan models",
|
|
6722
|
-
filterModelsByPlanHint: "
|
|
6989
|
+
filterModelsByPlanHint: "Show only models in your plan; on-demand credit accounts still see the full catalog.",
|
|
6723
6990
|
webSearch: "Serve dsh web search with Command Code",
|
|
6724
|
-
webSearchHint: "
|
|
6991
|
+
webSearchHint: "Use the same key for dsh web search; turning it off restores the previous backend.",
|
|
6725
6992
|
showSidebarQuota: "Show the quota card in the sidebar",
|
|
6726
|
-
showSidebarQuotaHint: "
|
|
6993
|
+
showSidebarQuotaHint: "Show a plan and quota card at the bottom of the sidebar that opens the full dashboard. Off by default; while off, no background usage refresh runs for it.",
|
|
6727
6994
|
commandGuard: "AI command guard",
|
|
6728
|
-
commandGuardHint: "Before a shell command asks for approval, typesafe/jev judges whether it is safe
|
|
6729
|
-
|
|
6730
|
-
|
|
6731
|
-
|
|
6732
|
-
|
|
6995
|
+
commandGuardHint: "Before a shell command asks for approval, typesafe/jev judges whether it is safe. A sandbox escalation also needs separate scope and necessity verdicts; all three must be confident enough to skip the prompt. Off by default.",
|
|
6996
|
+
commandGuardLevel: "Auto-approve threshold",
|
|
6997
|
+
commandGuardLevelHint: "A higher threshold needs more confidence to skip the prompt, so you see more prompts. High = 0.95, Medium = 0.9 (default), Low = 0.8. A judgement that takes longer than 3 seconds shows the normal prompt.",
|
|
6998
|
+
commandGuardLevelHigh: "High",
|
|
6999
|
+
commandGuardLevelMedium: "Medium",
|
|
7000
|
+
commandGuardLevelLow: "Low",
|
|
6733
7001
|
zdr: "Zero data retention (ZDR)",
|
|
6734
|
-
zdrHint: "
|
|
6735
|
-
accountsTitle: "
|
|
6736
|
-
accountsHint: "
|
|
7002
|
+
zdrHint: "Use only upstreams that retain no data and do not train on requests; availability and pricing may differ.",
|
|
7003
|
+
accountsTitle: "Accounts",
|
|
7004
|
+
accountsHint: "Account actions apply immediately, no save needed. Add more accounts and requests switch to the next one when the current account hits its usage limit or its key stops working.",
|
|
6737
7005
|
accountAdd: "Add account",
|
|
6738
|
-
accountRemove: "Remove",
|
|
6739
|
-
accountLabel: "Account label",
|
|
6740
|
-
accountKey: "API key",
|
|
6741
|
-
accountKeyHint: "This account’s API key. Saving with the field blank keeps the stored key.",
|
|
6742
|
-
accountLoginAfterSave: "Use Save at the bottom of the page, then sign in to this account in your browser to obtain a key.",
|
|
6743
7006
|
accountDefault: "Default account",
|
|
6744
|
-
activeAccount: "Active account",
|
|
6745
|
-
activeAccountAuto: "Auto (first usable account)",
|
|
6746
|
-
activeAccountHint: "Pin the preferred account; applies to the next request after saving. If the selected account is exhausted, requests still rotate to another usable account.",
|
|
6747
|
-
rulesTitle: "Route models to accounts",
|
|
6748
|
-
rulesHint: "Pick models (multi-select) and route them to an account. When the request’s model is in a rule and that account is usable, it serves; an exhausted or invalid routed account falls back to the normal rotation. Rules match in list order — the first hit wins.",
|
|
6749
|
-
rulesEmpty: "No rules yet.",
|
|
6750
|
-
rulesCatalogFailed: "Could not load the model catalog — selecting models is unavailable; saved rules still apply.",
|
|
6751
|
-
ruleAdd: "Add rule",
|
|
6752
|
-
ruleRemove: "Remove",
|
|
6753
|
-
ruleModel: "Models",
|
|
6754
|
-
ruleModelPick: "Select models…",
|
|
6755
|
-
ruleModelCount: "{count} model(s) selected",
|
|
6756
|
-
ruleAccount: "Target account",
|
|
6757
|
-
ruleHint: "Check the models to route from the dropdown (multi-select), then pick the target account.",
|
|
6758
7007
|
modelSearchPlaceholder: "Search models…",
|
|
6759
7008
|
modelSearchEmpty: "No matching models.",
|
|
6760
7009
|
modelStale: "Retired",
|
|
@@ -6771,20 +7020,19 @@ select.cc-input{appearance:none;-webkit-appearance:none;-moz-appearance:none;box
|
|
|
6771
7020
|
numberTooSmall: "Must be at least 1 (ms)",
|
|
6772
7021
|
numberTooLarge: "Above the allowed maximum (2147483647 ms)",
|
|
6773
7022
|
readOnly: "Settings are read-only.",
|
|
6774
|
-
unsaved: "Unsaved",
|
|
6775
7023
|
save: "Save",
|
|
6776
7024
|
saving: "Saving",
|
|
6777
|
-
saved: "
|
|
7025
|
+
saved: "Settings saved",
|
|
6778
7026
|
saveFailed: "Save failed, please retry.",
|
|
7027
|
+
unsavedChanges: "You have unsaved changes",
|
|
7028
|
+
saveInvalid: "Fix the highlighted field before saving",
|
|
6779
7029
|
discard: "Discard",
|
|
6780
7030
|
cancel: "Cancel",
|
|
6781
7031
|
show: "Show",
|
|
6782
7032
|
hide: "Hide",
|
|
6783
|
-
usageTitle: "Account usage",
|
|
6784
7033
|
usageRefresh: "Refresh",
|
|
6785
7034
|
usageRefreshing: "Refreshing…",
|
|
6786
7035
|
usageLoading: "Fetching account usage…",
|
|
6787
|
-
usageNoKey: "Configure an API key to see this account’s usage and credit state here.",
|
|
6788
7036
|
usageError: "Could not fetch usage",
|
|
6789
7037
|
usageRequests: "Requests",
|
|
6790
7038
|
usageFailed: "failed",
|
|
@@ -6798,12 +7046,10 @@ select.cc-input{appearance:none;-webkit-appearance:none;-moz-appearance:none;box
|
|
|
6798
7046
|
usageFree: "Free",
|
|
6799
7047
|
usageFiveHour: "5-hour window",
|
|
6800
7048
|
usageWeekly: "Weekly window",
|
|
7049
|
+
usageMonthlyLeft: "Monthly left",
|
|
6801
7050
|
usageExceeded: "Exceeded",
|
|
6802
7051
|
usageReset: "Resets",
|
|
6803
7052
|
usagePartial: "Some endpoint data unavailable",
|
|
6804
|
-
usageKeyClear: "Clear stored key",
|
|
6805
|
-
usageKeyClearStaged: "Will be cleared on save",
|
|
6806
|
-
usageUndoKeyClear: "Undo clear",
|
|
6807
7053
|
usageKeyInvalid: "API key invalid or expired",
|
|
6808
7054
|
usageKeyInvalidHint: "The server rejects every request (401). Check the key configured for this account, or generate a new one in the commandcode.ai console.",
|
|
6809
7055
|
usageServiceUnavailable: "The Command Code service is temporarily unavailable",
|
|
@@ -6821,8 +7067,7 @@ select.cc-input{appearance:none;-webkit-appearance:none;-moz-appearance:none;box
|
|
|
6821
7067
|
updateAvailable: "update available",
|
|
6822
7068
|
updateHint: "A newer version has been published; click for release notes. The notice disappears once the plugin is updated.",
|
|
6823
7069
|
loginTitle: "Sign in to fetch a key",
|
|
6824
|
-
loginHintIdle: "
|
|
6825
|
-
loginSaveBefore: "Save page changes before signing in to this account.",
|
|
7070
|
+
loginHintIdle: "Alternatively, sign in to fetch and store a key automatically.",
|
|
6826
7071
|
loginButton: "Sign in to Command Code",
|
|
6827
7072
|
loginStarting: "Starting the local callback server…",
|
|
6828
7073
|
loginWaiting: "Waiting for authorization in your browser…",
|
|
@@ -6840,7 +7085,42 @@ select.cc-input{appearance:none;-webkit-appearance:none;-moz-appearance:none;box
|
|
|
6840
7085
|
cardTitle: "Command Code",
|
|
6841
7086
|
cardRouteActive: "Active",
|
|
6842
7087
|
cardLoadingHint: "Loading the Command Code configuration…",
|
|
6843
|
-
cardRegistrationHint: "This card is contributed by the Command Code plugin and is waiting for the settings page to finish assembling."
|
|
7088
|
+
cardRegistrationHint: "This card is contributed by the Command Code plugin and is waiting for the settings page to finish assembling.",
|
|
7089
|
+
accountsRotationHint: "Account actions apply immediately. When the current account hits its usage limit or its key stops working, requests switch to the next usable account in list order.",
|
|
7090
|
+
accountModeAuto: "Mode: switch automatically (list order)",
|
|
7091
|
+
accountModePinned: "Mode: pinned to \"{name}\", still switching when it is unavailable",
|
|
7092
|
+
accountStatusPinned: "Pinned",
|
|
7093
|
+
accountActions: "Account actions",
|
|
7094
|
+
accountActionPin: "Pin this account",
|
|
7095
|
+
accountActionUnpin: "Switch automatically",
|
|
7096
|
+
accountActionLogin: "Sign in for a new key",
|
|
7097
|
+
accountActionKey: "Paste a new key",
|
|
7098
|
+
accountActionRename: "Rename",
|
|
7099
|
+
accountActionClearKey: "Delete key",
|
|
7100
|
+
accountActionRemove: "Remove account",
|
|
7101
|
+
accountRemoveConfirm: "Remove \"{name}\"? Its stored key and dedicated models are deleted with it.",
|
|
7102
|
+
accountClearKeyConfirm: "Delete the default account's stored key? The provider then falls back to the environment or the official CLI sign-in.",
|
|
7103
|
+
accountConfirmRemove: "Remove",
|
|
7104
|
+
accountConfirmClear: "Delete",
|
|
7105
|
+
accountApply: "Apply",
|
|
7106
|
+
accountNoKeyHint: "No key yet: sign in through the browser, or paste an API key created in the commandcode.ai console.",
|
|
7107
|
+
accountAddHint: "Sign-in opens the Command Code authorization page and adds the account once you approve; you can also paste an API key.",
|
|
7108
|
+
accountAddLogin: "Sign in",
|
|
7109
|
+
accountAddPaste: "Paste key",
|
|
7110
|
+
accountAddConfirm: "Add",
|
|
7111
|
+
accountNamePlaceholder: "Name (optional; sign-in uses the account name)",
|
|
7112
|
+
accountNameN: "Account {n}",
|
|
7113
|
+
accountKeyPlaceholder: "Paste an API key",
|
|
7114
|
+
accountOpFailed: "The account change did not complete. Try again.",
|
|
7115
|
+
accountModels: "Dedicated models",
|
|
7116
|
+
accountModelsHint: "These models prefer this account and still switch when it is unavailable. A model belongs to one account; selecting it here moves it from any other.",
|
|
7117
|
+
accountModelOwner: "on {name}",
|
|
7118
|
+
modelPick: "Choose models…",
|
|
7119
|
+
modelCount: "{count} models selected",
|
|
7120
|
+
modelCatalogFailed: "The model catalog could not be loaded; saved settings still apply.",
|
|
7121
|
+
modelsTitle: "Models",
|
|
7122
|
+
privacyTitle: "Privacy & security",
|
|
7123
|
+
integrationsTitle: "Integrations & display"
|
|
6844
7124
|
};
|
|
6845
7125
|
//#endregion
|
|
6846
7126
|
//#region src/client/index.ts
|
|
@@ -7055,6 +7335,10 @@ select.cc-input{appearance:none;-webkit-appearance:none;-moz-appearance:none;box
|
|
|
7055
7335
|
lastLoginPhase = phase;
|
|
7056
7336
|
loginStore.set(loginController.state());
|
|
7057
7337
|
});
|
|
7338
|
+
const refreshUsageOn = (ok) => {
|
|
7339
|
+
if (ok) usageController.refresh();
|
|
7340
|
+
return ok;
|
|
7341
|
+
};
|
|
7058
7342
|
const injected = () => ({
|
|
7059
7343
|
hooks: {
|
|
7060
7344
|
commandCodeSettings: store,
|
|
@@ -7071,15 +7355,16 @@ select.cc-input{appearance:none;-webkit-appearance:none;-moz-appearance:none;box
|
|
|
7071
7355
|
refreshUsage: () => void usageController.refresh(),
|
|
7072
7356
|
beginLogin: (targetRef) => void loginController.begin(targetRef),
|
|
7073
7357
|
cancelLogin: () => void loginController.cancel(),
|
|
7074
|
-
|
|
7075
|
-
|
|
7076
|
-
|
|
7077
|
-
|
|
7078
|
-
|
|
7079
|
-
|
|
7080
|
-
|
|
7081
|
-
|
|
7082
|
-
|
|
7358
|
+
createAccount: (input) => controller.createAccount(input).then((ref) => {
|
|
7359
|
+
if (ref !== void 0 && input.key) usageController.refresh();
|
|
7360
|
+
return ref;
|
|
7361
|
+
}),
|
|
7362
|
+
renameAccount: (ref, label) => controller.renameAccount(ref, label),
|
|
7363
|
+
removeAccount: (ref) => controller.removeAccount(ref).then(refreshUsageOn),
|
|
7364
|
+
setAccountKey: (target, key) => controller.setAccountKey(target, key).then(refreshUsageOn),
|
|
7365
|
+
clearAccountKey: (target) => controller.clearAccountKey(target).then(refreshUsageOn),
|
|
7366
|
+
setActiveAccount: (id) => controller.setActiveAccount(id).then(refreshUsageOn),
|
|
7367
|
+
setAccountModels: (target, ids) => controller.setAccountModels(target, ids),
|
|
7083
7368
|
editVisibleModels: (ids) => controller.editVisibleModels(ids),
|
|
7084
7369
|
clearVisibleModels: () => controller.clearVisibleModels()
|
|
7085
7370
|
});
|