@mars-sea/dsh-commandcode-provider 0.11.11 → 0.11.13
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 +20 -0
- package/README.md +34 -31
- package/README.zh-CN.md +28 -31
- package/lib/client.js +2073 -1837
- package/lib/client.js.map +1 -1
- package/lib/index.d.ts +55 -86
- package/lib/index.js +350 -390
- package/lib/index.js.map +1 -1
- package/package.json +40 -48
package/lib/client.js
CHANGED
|
@@ -471,7 +471,10 @@ window.__ModuleLoader__.load({
|
|
|
471
471
|
* so the control only reports whether one is configured.
|
|
472
472
|
* - API base -> the `llm-commandcode` settings namespace (`apiBase`), same
|
|
473
473
|
* namespace the Models page card addresses.
|
|
474
|
-
* -
|
|
474
|
+
* - Timeouts, behavior switches, model visibility -> the same namespace,
|
|
475
|
+
* staged and written on save.
|
|
476
|
+
* - Accounts -> `accounts`, `activeAccount`, `modelAccountRules` and the
|
|
477
|
+
* per-account credentials, committed immediately.
|
|
475
478
|
*
|
|
476
479
|
* The controller mirrors the plugin-card pattern from the harness's own
|
|
477
480
|
* settings UI: it binds the `llm-commandcode` namespace through a
|
|
@@ -539,6 +542,24 @@ window.__ModuleLoader__.load({
|
|
|
539
542
|
}
|
|
540
543
|
};
|
|
541
544
|
}
|
|
545
|
+
/** A field limited to fixed string choices; an empty draft clears it. */
|
|
546
|
+
function choiceField(field, choices) {
|
|
547
|
+
return {
|
|
548
|
+
field,
|
|
549
|
+
format: (value) => typeof value === "string" && choices.includes(value) ? value : "",
|
|
550
|
+
parse: (text) => {
|
|
551
|
+
const trimmed = text.trim();
|
|
552
|
+
if (trimmed === "") return { kind: "clear" };
|
|
553
|
+
return choices.includes(trimmed) ? {
|
|
554
|
+
kind: "set",
|
|
555
|
+
value: trimmed
|
|
556
|
+
} : {
|
|
557
|
+
kind: "invalid",
|
|
558
|
+
reason: "format"
|
|
559
|
+
};
|
|
560
|
+
}
|
|
561
|
+
};
|
|
562
|
+
}
|
|
542
563
|
/**
|
|
543
564
|
* A boolean field, staged as the strings `'true'`/`'false'` (an empty draft
|
|
544
565
|
* clears it). The component renders a toggle and only ever stages these two
|
|
@@ -567,10 +588,17 @@ window.__ModuleLoader__.load({
|
|
|
567
588
|
};
|
|
568
589
|
}
|
|
569
590
|
const MAX_TIMEOUT_MS = 2147483647;
|
|
591
|
+
/** The command guard's auto-approve levels, strictest first. */
|
|
592
|
+
const COMMAND_GUARD_LEVEL_CHOICES = [
|
|
593
|
+
"high",
|
|
594
|
+
"medium",
|
|
595
|
+
"low"
|
|
596
|
+
];
|
|
597
|
+
/** The level an unset `commandGuardLevel` reads as on the Host. */
|
|
598
|
+
const COMMAND_GUARD_DEFAULT_LEVEL_CHOICE = "medium";
|
|
570
599
|
/** The fields this page edits inside the `llm-commandcode` namespace. */
|
|
571
600
|
const SECTION_FIELDS = [
|
|
572
601
|
textField("apiBase"),
|
|
573
|
-
textField("workingDir"),
|
|
574
602
|
numberField$1("requestTimeoutMs", {
|
|
575
603
|
min: 1,
|
|
576
604
|
max: MAX_TIMEOUT_MS
|
|
@@ -587,16 +615,8 @@ window.__ModuleLoader__.load({
|
|
|
587
615
|
booleanField$1("webSearch"),
|
|
588
616
|
booleanField$1("showSidebarQuota"),
|
|
589
617
|
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")
|
|
618
|
+
choiceField("commandGuardLevel", COMMAND_GUARD_LEVEL_CHOICES),
|
|
619
|
+
booleanField$1("zdr")
|
|
600
620
|
];
|
|
601
621
|
/** Whether two model-id lists are equal as sets (order-insensitive). */
|
|
602
622
|
function sameModels(a, b) {
|
|
@@ -605,17 +625,47 @@ window.__ModuleLoader__.load({
|
|
|
605
625
|
return b.every((id) => set.has(id));
|
|
606
626
|
}
|
|
607
627
|
/**
|
|
608
|
-
*
|
|
609
|
-
*
|
|
610
|
-
*
|
|
628
|
+
* Fold routing rules into one model list per account with the runtime's
|
|
629
|
+
* first-match-wins order (`matchModelRule()` in src/accounts.ts): a model
|
|
630
|
+
* claimed by an earlier rule is ignored by every later one. Account order is
|
|
631
|
+
* first appearance, which keeps a rewrite's rule order stable.
|
|
611
632
|
*/
|
|
612
|
-
function
|
|
613
|
-
|
|
633
|
+
function accountModelMap(rules) {
|
|
634
|
+
const claimed = /* @__PURE__ */ new Set();
|
|
635
|
+
const map = /* @__PURE__ */ new Map();
|
|
636
|
+
for (const rule of rules) {
|
|
637
|
+
const list = map.get(rule.account) ?? [];
|
|
638
|
+
for (const model of rule.models) {
|
|
639
|
+
if (claimed.has(model)) continue;
|
|
640
|
+
claimed.add(model);
|
|
641
|
+
list.push(model);
|
|
642
|
+
}
|
|
643
|
+
map.set(rule.account, list);
|
|
644
|
+
}
|
|
645
|
+
return map;
|
|
646
|
+
}
|
|
647
|
+
/** Serialize a per-account model map back into `modelAccountRules`. */
|
|
648
|
+
function rulesFromMap(map) {
|
|
649
|
+
const out = [];
|
|
650
|
+
for (const [account, models] of map) if (models.length > 0) out.push({
|
|
651
|
+
models: [...models],
|
|
652
|
+
account
|
|
653
|
+
});
|
|
654
|
+
return out;
|
|
614
655
|
}
|
|
615
656
|
/**
|
|
616
657
|
* Controller bridging the `llm-commandcode` scope and the credentials domain
|
|
617
|
-
* onto the page.
|
|
618
|
-
*
|
|
658
|
+
* onto the page.
|
|
659
|
+
*
|
|
660
|
+
* Two write paths with different contracts:
|
|
661
|
+
* - The staged form (connection, behavior and model-visibility fields, plus
|
|
662
|
+
* the Models-page card's default key draft) lands on `save()`.
|
|
663
|
+
* - Account management (create, rename, remove, key replacement, pinning and
|
|
664
|
+
* per-account models) commits IMMEDIATELY and serially. A new account had to
|
|
665
|
+
* be saved before browser sign-in could target it (the Host refuses a login
|
|
666
|
+
* for a reference the stored `accounts` list does not name), which turned
|
|
667
|
+
* "add an account" into edit → save → sign in; committing each operation
|
|
668
|
+
* removes that dance and keeps unrelated staged edits out of it.
|
|
619
669
|
*/
|
|
620
670
|
var CommandCodeSettingsController = class {
|
|
621
671
|
scope;
|
|
@@ -625,39 +675,10 @@ window.__ModuleLoader__.load({
|
|
|
625
675
|
listeners = /* @__PURE__ */ new Set();
|
|
626
676
|
disposers = [];
|
|
627
677
|
disposed = false;
|
|
628
|
-
defaultWorkingDir;
|
|
629
678
|
/** The credential reference the default account resolves. */
|
|
630
679
|
credentialRef = DEFAULT_API_KEY_REF;
|
|
631
680
|
/** Host-reported configured/writable state per credential reference. */
|
|
632
681
|
credentialStates = /* @__PURE__ */ new Map();
|
|
633
|
-
/** Staged account additions (not yet saved). */
|
|
634
|
-
addedAccounts = [];
|
|
635
|
-
/** Staged removals of stored extra accounts, by credential reference. */
|
|
636
|
-
removedRefs = /* @__PURE__ */ new Set();
|
|
637
|
-
/** Staged label drafts, by credential reference. */
|
|
638
|
-
labelDrafts = /* @__PURE__ */ new Map();
|
|
639
|
-
/** Staged key drafts, by credential reference (blank = keep stored key). */
|
|
640
|
-
keyDrafts = /* @__PURE__ */ new Map();
|
|
641
|
-
/** Credential references staged for removal on the next save. */
|
|
642
|
-
keyClears = /* @__PURE__ */ new Set();
|
|
643
|
-
/** Staged model→account routing rules (not yet saved). */
|
|
644
|
-
addedRules = [];
|
|
645
|
-
/** Staged edits to stored routing rules, by stored row id. */
|
|
646
|
-
ruleDrafts = /* @__PURE__ */ new Map();
|
|
647
|
-
/**
|
|
648
|
-
* Stored routing rule rows staged for removal, by stored row id with a
|
|
649
|
-
* content snapshot. The snapshot makes reconcile content-based: stored
|
|
650
|
-
* row ids are positional (`rule-N`) and shift after any write, so an
|
|
651
|
-
* id-only check would misread a landed removal as pending (and a retry
|
|
652
|
-
* would delete the wrong row).
|
|
653
|
-
*/
|
|
654
|
-
removedRuleIds = /* @__PURE__ */ new Map();
|
|
655
|
-
/**
|
|
656
|
-
* Fingerprint of the stored routing rules when the last `save()` started;
|
|
657
|
-
* `undefined` outside a save. Lets reconcile distinguish a landed rules
|
|
658
|
-
* write from a save that failed before reaching it.
|
|
659
|
-
*/
|
|
660
|
-
rulesBeforeSave = void 0;
|
|
661
682
|
/** Staged visible-model allowlist (undefined = no draft). */
|
|
662
683
|
visibleModelsDraft = void 0;
|
|
663
684
|
/** The catalog the model editors offer (Host-side). */
|
|
@@ -666,13 +687,15 @@ window.__ModuleLoader__.load({
|
|
|
666
687
|
saving = false;
|
|
667
688
|
failed = false;
|
|
668
689
|
savedCount = 0;
|
|
690
|
+
/** Tail of the serial account-operation queue. */
|
|
691
|
+
accountQueue = Promise.resolve();
|
|
692
|
+
accountPending = 0;
|
|
693
|
+
accountFailed = void 0;
|
|
669
694
|
/**
|
|
670
695
|
* @param scope - bound scope for the `llm-commandcode` namespace.
|
|
671
696
|
* @param api - credentials wire face.
|
|
672
|
-
* @param hostDescription - the Host-description observable whose `cwd` is
|
|
673
|
-
* shown as the placeholder a blank `workingDir` field resolves to.
|
|
674
697
|
*/
|
|
675
|
-
constructor(scope, api
|
|
698
|
+
constructor(scope, api) {
|
|
676
699
|
this.scope = scope;
|
|
677
700
|
this.api = api;
|
|
678
701
|
this.disposers.push(scope.subscribe(() => {
|
|
@@ -680,17 +703,6 @@ window.__ModuleLoader__.load({
|
|
|
680
703
|
this.describeAll();
|
|
681
704
|
this.publish();
|
|
682
705
|
}));
|
|
683
|
-
if (hostDescription !== void 0) {
|
|
684
|
-
this.defaultWorkingDir = hostDescription.getSnapshot()?.cwd;
|
|
685
|
-
this.disposers.push(hostDescription.subscribe(() => {
|
|
686
|
-
if (this.disposed) return;
|
|
687
|
-
const cwd = hostDescription.getSnapshot()?.cwd;
|
|
688
|
-
if (cwd !== this.defaultWorkingDir) {
|
|
689
|
-
this.defaultWorkingDir = cwd;
|
|
690
|
-
this.publish();
|
|
691
|
-
}
|
|
692
|
-
}));
|
|
693
|
-
}
|
|
694
706
|
this.recomputeCredentialRef();
|
|
695
707
|
this.describeAll();
|
|
696
708
|
this.refreshCatalog();
|
|
@@ -705,9 +717,9 @@ window.__ModuleLoader__.load({
|
|
|
705
717
|
}
|
|
706
718
|
/**
|
|
707
719
|
* The credential reference the section names, or the provider default. A
|
|
708
|
-
* user who renamed `apiKeyEnv` in
|
|
709
|
-
*
|
|
710
|
-
*
|
|
720
|
+
* user who renamed `apiKeyEnv` in the profile Config gets a page that
|
|
721
|
+
* addresses the renamed ref instead of silently writing the default —
|
|
722
|
+
* mirroring the Models page's `refFor()`.
|
|
711
723
|
*/
|
|
712
724
|
recomputeCredentialRef() {
|
|
713
725
|
const snapshot = this.scope.getSnapshot();
|
|
@@ -727,6 +739,7 @@ window.__ModuleLoader__.load({
|
|
|
727
739
|
const plan = this.plan();
|
|
728
740
|
const credential = this.credentialStates.get(this.credentialRef);
|
|
729
741
|
const accounts = this.effectiveAccounts();
|
|
742
|
+
const active = this.sectionValue("activeAccount");
|
|
730
743
|
return {
|
|
731
744
|
available: snapshot.status === "ready",
|
|
732
745
|
writable: snapshot.writable,
|
|
@@ -740,10 +753,7 @@ window.__ModuleLoader__.load({
|
|
|
740
753
|
invalid: false,
|
|
741
754
|
invalidReason: void 0
|
|
742
755
|
},
|
|
743
|
-
apiKeyClearStaged: this.keyClears.has(this.credentialRef),
|
|
744
756
|
apiBase: this.field("apiBase"),
|
|
745
|
-
workingDir: this.field("workingDir"),
|
|
746
|
-
defaultWorkingDir: this.defaultWorkingDir,
|
|
747
757
|
requestTimeoutMs: this.field("requestTimeoutMs"),
|
|
748
758
|
streamIdleTimeoutMs: this.field("streamIdleTimeoutMs"),
|
|
749
759
|
transportMaxRetries: this.field("transportMaxRetries"),
|
|
@@ -751,164 +761,31 @@ window.__ModuleLoader__.load({
|
|
|
751
761
|
webSearch: this.field("webSearch"),
|
|
752
762
|
showSidebarQuota: this.field("showSidebarQuota"),
|
|
753
763
|
commandGuard: this.field("commandGuard"),
|
|
754
|
-
|
|
755
|
-
commandGuardTimeoutMs: this.field("commandGuardTimeoutMs"),
|
|
764
|
+
commandGuardLevel: this.field("commandGuardLevel"),
|
|
756
765
|
zdr: this.field("zdr"),
|
|
757
766
|
sidebarQuota: this.sectionValue("showSidebarQuota") === true,
|
|
758
|
-
activeAccount:
|
|
767
|
+
activeAccount: typeof active === "string" ? active : "",
|
|
759
768
|
accounts,
|
|
760
|
-
|
|
761
|
-
|
|
769
|
+
accountModels: Object.fromEntries(accountModelMap(this.storedRules())),
|
|
770
|
+
accountBusy: this.accountPending > 0,
|
|
771
|
+
accountFailed: this.accountFailed,
|
|
762
772
|
visibleModels: this.effectiveVisibleModels(),
|
|
763
773
|
catalogModels: this.catalogModels,
|
|
764
774
|
catalogFailed: this.catalogFailed,
|
|
765
|
-
dirty: plan.length > 0 || this.
|
|
775
|
+
dirty: plan.length > 0 || this.visibleModelsDirty(),
|
|
766
776
|
invalid: plan.some((item) => item.run === void 0),
|
|
767
777
|
saving: this.saving,
|
|
768
778
|
failed: this.failed,
|
|
769
779
|
savedCount: this.savedCount
|
|
770
780
|
};
|
|
771
781
|
}
|
|
772
|
-
/** Stage a new extra account (saved on the next `save()`). */
|
|
773
|
-
addAccount() {
|
|
774
|
-
const used = /* @__PURE__ */ new Set([
|
|
775
|
-
this.credentialRef,
|
|
776
|
-
...this.storedExtras().map((extra) => extra.ref),
|
|
777
|
-
...this.addedAccounts.map((extra) => extra.ref)
|
|
778
|
-
]);
|
|
779
|
-
let n = 2;
|
|
780
|
-
while (used.has(`${this.credentialRef}_${n}`)) n += 1;
|
|
781
|
-
const index = this.storedExtras().length + this.addedAccounts.length + 2;
|
|
782
|
-
this.addedAccounts.push({
|
|
783
|
-
label: `Account ${index}`,
|
|
784
|
-
ref: `${this.credentialRef}_${n}`
|
|
785
|
-
});
|
|
786
|
-
this.failed = false;
|
|
787
|
-
this.describeAll();
|
|
788
|
-
this.publish();
|
|
789
|
-
}
|
|
790
|
-
/** Stage one extra account's removal (or drop an unsaved addition). */
|
|
791
|
-
removeAccount(id) {
|
|
792
|
-
const addedIndex = this.addedAccounts.findIndex((extra) => extra.ref === id);
|
|
793
|
-
if (addedIndex >= 0) this.addedAccounts.splice(addedIndex, 1);
|
|
794
|
-
else this.removedRefs.add(id);
|
|
795
|
-
this.labelDrafts.delete(id);
|
|
796
|
-
this.keyDrafts.delete(id);
|
|
797
|
-
const stagedActive = this.staged.get("activeAccount");
|
|
798
|
-
if ((stagedActive !== void 0 ? stagedActive.clear ? "" : stagedActive.text : typeof this.sectionValue("activeAccount") === "string" ? this.sectionValue("activeAccount") : "") === id) this.staged.set("activeAccount", {
|
|
799
|
-
text: "",
|
|
800
|
-
clear: true
|
|
801
|
-
});
|
|
802
|
-
this.failed = false;
|
|
803
|
-
this.publish();
|
|
804
|
-
}
|
|
805
|
-
/** Stage one extra account's label draft. */
|
|
806
|
-
editAccountLabel(id, text) {
|
|
807
|
-
this.labelDrafts.set(id, text);
|
|
808
|
-
this.failed = false;
|
|
809
|
-
this.publish();
|
|
810
|
-
}
|
|
811
|
-
/** Stage one extra account's key draft (blank keeps the stored key). */
|
|
812
|
-
editAccountKey(id, text) {
|
|
813
|
-
this.keyDrafts.set(id, text);
|
|
814
|
-
this.keyClears.delete(id);
|
|
815
|
-
this.failed = false;
|
|
816
|
-
this.publish();
|
|
817
|
-
}
|
|
818
|
-
/**
|
|
819
|
-
* Toggle the staged removal of one account's stored key: the next save
|
|
820
|
-
* unsets the credential so the account reports unconfigured and falls back
|
|
821
|
-
* to its other key sources. Only meaningful while a key is actually
|
|
822
|
-
* stored. `target` is `'default'` (the implicit first account) or an extra
|
|
823
|
-
* account's credential reference.
|
|
824
|
-
*/
|
|
825
|
-
toggleKeyClear(target) {
|
|
826
|
-
const ref = target === "default" ? this.credentialRef : target;
|
|
827
|
-
if (this.keyClears.has(ref)) this.keyClears.delete(ref);
|
|
828
|
-
else {
|
|
829
|
-
if (this.credentialStates.get(ref)?.configured !== true) return;
|
|
830
|
-
this.keyDrafts.delete(ref);
|
|
831
|
-
if (ref === this.credentialRef) this.staged.delete("apiKey");
|
|
832
|
-
this.keyClears.add(ref);
|
|
833
|
-
}
|
|
834
|
-
this.failed = false;
|
|
835
|
-
this.describeAll();
|
|
836
|
-
this.publish();
|
|
837
|
-
}
|
|
838
|
-
/** Stage a new model → account routing rule (saved on the next `save()`). */
|
|
839
|
-
addRule() {
|
|
840
|
-
this.addedRules.push({
|
|
841
|
-
models: [],
|
|
842
|
-
account: "default"
|
|
843
|
-
});
|
|
844
|
-
this.failed = false;
|
|
845
|
-
this.publish();
|
|
846
|
-
}
|
|
847
|
-
/** Stage one routing rule's removal (or drop an unsaved addition). */
|
|
848
|
-
removeRule(id) {
|
|
849
|
-
const addedIndex = this.addedRules.findIndex((_, index) => `new-${index}` === id);
|
|
850
|
-
if (addedIndex >= 0) this.addedRules.splice(addedIndex, 1);
|
|
851
|
-
else {
|
|
852
|
-
const stored = this.storedRules().find((rule) => rule.id === id);
|
|
853
|
-
this.removedRuleIds.set(id, stored === void 0 ? {
|
|
854
|
-
models: [],
|
|
855
|
-
account: ""
|
|
856
|
-
} : {
|
|
857
|
-
models: [...stored.models],
|
|
858
|
-
account: stored.account
|
|
859
|
-
});
|
|
860
|
-
}
|
|
861
|
-
this.ruleDrafts.delete(id);
|
|
862
|
-
this.failed = false;
|
|
863
|
-
this.publish();
|
|
864
|
-
}
|
|
865
|
-
/** Stage one routing rule's selected model ids (multi-select). */
|
|
866
|
-
editRuleModels(id, models) {
|
|
867
|
-
const addedIndex = this.addedRules.findIndex((_, index) => `new-${index}` === id);
|
|
868
|
-
if (addedIndex >= 0) this.addedRules[addedIndex] = {
|
|
869
|
-
...this.addedRules[addedIndex],
|
|
870
|
-
models
|
|
871
|
-
};
|
|
872
|
-
else {
|
|
873
|
-
const current = this.ruleDrafts.get(id) ?? this.storedRules().find((rule) => rule.id === id) ?? {
|
|
874
|
-
models: [],
|
|
875
|
-
account: "default"
|
|
876
|
-
};
|
|
877
|
-
this.ruleDrafts.set(id, {
|
|
878
|
-
...current,
|
|
879
|
-
models
|
|
880
|
-
});
|
|
881
|
-
}
|
|
882
|
-
this.failed = false;
|
|
883
|
-
this.publish();
|
|
884
|
-
}
|
|
885
|
-
/** Stage one routing rule's target account draft. */
|
|
886
|
-
editRuleAccount(id, text) {
|
|
887
|
-
const addedIndex = this.addedRules.findIndex((_, index) => `new-${index}` === id);
|
|
888
|
-
if (addedIndex >= 0) this.addedRules[addedIndex] = {
|
|
889
|
-
...this.addedRules[addedIndex],
|
|
890
|
-
account: text
|
|
891
|
-
};
|
|
892
|
-
else {
|
|
893
|
-
const current = this.ruleDrafts.get(id) ?? this.storedRules().find((rule) => rule.id === id) ?? {
|
|
894
|
-
models: [],
|
|
895
|
-
account: "default"
|
|
896
|
-
};
|
|
897
|
-
this.ruleDrafts.set(id, {
|
|
898
|
-
...current,
|
|
899
|
-
account: text
|
|
900
|
-
});
|
|
901
|
-
}
|
|
902
|
-
this.failed = false;
|
|
903
|
-
this.publish();
|
|
904
|
-
}
|
|
905
782
|
/** Stage one field's draft text. */
|
|
906
783
|
edit(field, text) {
|
|
784
|
+
if (field !== "apiKey") this.spec(field);
|
|
907
785
|
this.staged.set(field, {
|
|
908
786
|
text,
|
|
909
787
|
clear: false
|
|
910
788
|
});
|
|
911
|
-
if (field === "apiKey") this.keyClears.delete(this.credentialRef);
|
|
912
789
|
this.failed = false;
|
|
913
790
|
this.publish();
|
|
914
791
|
}
|
|
@@ -930,11 +807,9 @@ window.__ModuleLoader__.load({
|
|
|
930
807
|
}
|
|
931
808
|
/** Discard every staged edit. */
|
|
932
809
|
discard() {
|
|
933
|
-
if (this.staged.size === 0 &&
|
|
810
|
+
if (this.staged.size === 0 && this.visibleModelsDraft === void 0 && !this.failed) return;
|
|
934
811
|
this.staged.clear();
|
|
935
|
-
this.
|
|
936
|
-
this.clearRuleStaging();
|
|
937
|
-
this.clearVisibleModelsStaging();
|
|
812
|
+
this.visibleModelsDraft = void 0;
|
|
938
813
|
this.failed = false;
|
|
939
814
|
this.publish();
|
|
940
815
|
}
|
|
@@ -949,10 +824,8 @@ window.__ModuleLoader__.load({
|
|
|
949
824
|
/** Write every staged edit, then re-read the Host's accepted state. */
|
|
950
825
|
async save() {
|
|
951
826
|
const plan = this.plan();
|
|
952
|
-
const accountRuns = this.accountPlan();
|
|
953
|
-
const ruleRuns = this.rulesPlan();
|
|
954
827
|
const visibleRuns = this.visibleModelsPlan();
|
|
955
|
-
if (plan.length === 0 &&
|
|
828
|
+
if (plan.length === 0 && visibleRuns.length === 0 || this.saving) return;
|
|
956
829
|
const runs = [];
|
|
957
830
|
for (const item of plan) {
|
|
958
831
|
if (item.run === void 0) return;
|
|
@@ -960,15 +833,9 @@ window.__ModuleLoader__.load({
|
|
|
960
833
|
}
|
|
961
834
|
this.saving = true;
|
|
962
835
|
this.failed = false;
|
|
963
|
-
this.rulesBeforeSave = ruleFingerprint(this.storedRules());
|
|
964
836
|
this.publish();
|
|
965
837
|
let landed = true;
|
|
966
|
-
for (const run of [
|
|
967
|
-
...runs,
|
|
968
|
-
...accountRuns,
|
|
969
|
-
...ruleRuns,
|
|
970
|
-
...visibleRuns
|
|
971
|
-
]) {
|
|
838
|
+
for (const run of [...runs, ...visibleRuns]) {
|
|
972
839
|
let ok = false;
|
|
973
840
|
try {
|
|
974
841
|
ok = await run();
|
|
@@ -985,32 +852,148 @@ window.__ModuleLoader__.load({
|
|
|
985
852
|
if (landed) {
|
|
986
853
|
this.savedCount += 1;
|
|
987
854
|
this.staged.clear();
|
|
988
|
-
this.
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
855
|
+
this.visibleModelsDraft = void 0;
|
|
856
|
+
} else this.reconcileStaging();
|
|
857
|
+
this.publish();
|
|
858
|
+
}
|
|
859
|
+
/** Stage the visible-model allowlist (multi-select). */
|
|
860
|
+
editVisibleModels(models) {
|
|
861
|
+
this.visibleModelsDraft = [...models];
|
|
862
|
+
this.failed = false;
|
|
863
|
+
this.publish();
|
|
864
|
+
}
|
|
865
|
+
/** Stage "show all models" (clears the allowlist). */
|
|
866
|
+
clearVisibleModels() {
|
|
867
|
+
this.visibleModelsDraft = [];
|
|
868
|
+
this.failed = false;
|
|
996
869
|
this.publish();
|
|
997
870
|
}
|
|
998
871
|
/**
|
|
999
|
-
*
|
|
1000
|
-
*
|
|
1001
|
-
*
|
|
1002
|
-
*
|
|
1003
|
-
* the accounts write that failed.
|
|
872
|
+
* Create one extra account now and return its credential reference, or
|
|
873
|
+
* undefined when the account could not be stored. With `key` the key lands
|
|
874
|
+
* first, so a stored row never names a reference whose key write failed;
|
|
875
|
+
* without it the row is stored keyless so browser sign-in can target it.
|
|
1004
876
|
*/
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
this.
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
877
|
+
async createAccount(input) {
|
|
878
|
+
let created;
|
|
879
|
+
await this.runAccountOp("create", async () => {
|
|
880
|
+
const ref = this.nextAccountRef();
|
|
881
|
+
const key = input.key?.trim() ?? "";
|
|
882
|
+
if (key !== "" && !await this.writeKeyTo(ref, key)) return false;
|
|
883
|
+
const label = input.label.trim() === "" ? ref : input.label.trim();
|
|
884
|
+
let ok = false;
|
|
885
|
+
try {
|
|
886
|
+
ok = await this.writeAccountList([...this.rawStoredAccounts(), {
|
|
887
|
+
label,
|
|
888
|
+
apiKeyEnv: ref
|
|
889
|
+
}]);
|
|
890
|
+
} catch {
|
|
891
|
+
ok = false;
|
|
892
|
+
}
|
|
893
|
+
if (!ok) {
|
|
894
|
+
if (key !== "") await this.unsetKey(ref);
|
|
895
|
+
return false;
|
|
896
|
+
}
|
|
897
|
+
created = ref;
|
|
898
|
+
return true;
|
|
899
|
+
});
|
|
900
|
+
return created;
|
|
901
|
+
}
|
|
902
|
+
/** Rename one stored extra account now. */
|
|
903
|
+
renameAccount(ref, label) {
|
|
904
|
+
const next = label.trim();
|
|
905
|
+
return this.runAccountOp("rename", async () => {
|
|
906
|
+
if (next === "") return false;
|
|
907
|
+
const list = this.rawStoredAccounts().map((entry) => entry.apiKeyEnv === ref ? {
|
|
908
|
+
...entry,
|
|
909
|
+
label: next
|
|
910
|
+
} : { ...entry });
|
|
911
|
+
return this.writeAccountList(list);
|
|
912
|
+
});
|
|
913
|
+
}
|
|
914
|
+
/**
|
|
915
|
+
* Remove one stored extra account now: its stored key, its dedicated
|
|
916
|
+
* models and a pin naming it go with it, so nothing orphaned remains.
|
|
917
|
+
*/
|
|
918
|
+
removeAccount(ref) {
|
|
919
|
+
return this.runAccountOp("remove", async () => {
|
|
920
|
+
if (this.credentialStates.get(ref)?.configured === true && !await this.unsetKey(ref)) return false;
|
|
921
|
+
const list = this.rawStoredAccounts().filter((entry) => entry.apiKeyEnv !== ref).map((entry) => ({ ...entry }));
|
|
922
|
+
if (!await this.writeAccountList(list)) return false;
|
|
923
|
+
const map = accountModelMap(this.storedRules());
|
|
924
|
+
if (map.has(ref)) {
|
|
925
|
+
map.delete(ref);
|
|
926
|
+
if (!await this.writeRules(rulesFromMap(map))) return false;
|
|
927
|
+
}
|
|
928
|
+
if (this.sectionValue("activeAccount") === ref) return this.clear("activeAccount");
|
|
929
|
+
return true;
|
|
930
|
+
});
|
|
931
|
+
}
|
|
932
|
+
/** Store a replacement key now; `target` is `'default'` or an extra account's reference. */
|
|
933
|
+
setAccountKey(target, key) {
|
|
934
|
+
const value = key.trim();
|
|
935
|
+
return this.runAccountOp("key", async () => {
|
|
936
|
+
if (value === "") return false;
|
|
937
|
+
return this.writeKeyTo(this.refFor(target), value);
|
|
938
|
+
});
|
|
939
|
+
}
|
|
940
|
+
/** Remove a stored key now; `target` is `'default'` or an extra account's reference. */
|
|
941
|
+
clearAccountKey(target) {
|
|
942
|
+
return this.runAccountOp("key", () => this.unsetKey(this.refFor(target)));
|
|
943
|
+
}
|
|
944
|
+
/** Pin the serving account now (`''` returns to automatic rotation). */
|
|
945
|
+
setActiveAccount(id) {
|
|
946
|
+
return this.runAccountOp("active", () => id === "" ? this.clear("activeAccount") : this.store("activeAccount", id));
|
|
947
|
+
}
|
|
948
|
+
/**
|
|
949
|
+
* Replace one account's dedicated models now. A model belongs to one
|
|
950
|
+
* account at a time, so each selected model is moved out of any other
|
|
951
|
+
* account's list — the stored rules then carry no shadowed entries.
|
|
952
|
+
*/
|
|
953
|
+
setAccountModels(target, models) {
|
|
954
|
+
return this.runAccountOp("models", async () => {
|
|
955
|
+
const chosen = [...new Set(models.filter((id) => id !== ""))];
|
|
956
|
+
const taken = new Set(chosen);
|
|
957
|
+
const map = accountModelMap(this.storedRules());
|
|
958
|
+
for (const [account, list] of map) if (account !== target) map.set(account, list.filter((id) => !taken.has(id)));
|
|
959
|
+
map.set(target, chosen);
|
|
960
|
+
return this.writeRules(rulesFromMap(map));
|
|
961
|
+
});
|
|
962
|
+
}
|
|
963
|
+
/** Run one account operation after every earlier one, tracking busy/failure. */
|
|
964
|
+
runAccountOp(op, run) {
|
|
965
|
+
this.accountPending += 1;
|
|
966
|
+
this.accountFailed = void 0;
|
|
967
|
+
this.publish();
|
|
968
|
+
const result = this.accountQueue.then(async () => {
|
|
969
|
+
if (!this.scope.getSnapshot().writable) return false;
|
|
970
|
+
try {
|
|
971
|
+
return await run();
|
|
972
|
+
} catch {
|
|
973
|
+
return false;
|
|
974
|
+
}
|
|
975
|
+
});
|
|
976
|
+
this.accountQueue = result;
|
|
977
|
+
return result.then((ok) => {
|
|
978
|
+
this.accountPending -= 1;
|
|
979
|
+
if (!ok) this.accountFailed = op;
|
|
980
|
+
this.publish();
|
|
981
|
+
return ok;
|
|
982
|
+
});
|
|
983
|
+
}
|
|
984
|
+
refFor(target) {
|
|
985
|
+
return target === "default" ? this.credentialRef : target;
|
|
986
|
+
}
|
|
987
|
+
/**
|
|
988
|
+
* The first free `<credentialRef>_<n>` reference. Derived from the default
|
|
989
|
+
* reference's name, so a renamed `apiKeyEnv` yields `MY_KEY_2`-style refs
|
|
990
|
+
* consistent with the default slot.
|
|
991
|
+
*/
|
|
992
|
+
nextAccountRef() {
|
|
993
|
+
const used = /* @__PURE__ */ new Set([this.credentialRef, ...this.storedExtras().map((extra) => extra.ref)]);
|
|
994
|
+
let n = 2;
|
|
995
|
+
while (used.has(`${this.credentialRef}_${n}`)) n += 1;
|
|
996
|
+
return `${this.credentialRef}_${n}`;
|
|
1014
997
|
}
|
|
1015
998
|
spec(field) {
|
|
1016
999
|
const spec = this.specs.get(field);
|
|
@@ -1063,7 +1046,7 @@ window.__ModuleLoader__.load({
|
|
|
1063
1046
|
const value = staged.text.trim();
|
|
1064
1047
|
if (value !== "") plan.push({
|
|
1065
1048
|
field,
|
|
1066
|
-
run: () => this.
|
|
1049
|
+
run: () => this.writeKeyTo(this.credentialRef, value)
|
|
1067
1050
|
});
|
|
1068
1051
|
continue;
|
|
1069
1052
|
}
|
|
@@ -1092,7 +1075,16 @@ window.__ModuleLoader__.load({
|
|
|
1092
1075
|
}
|
|
1093
1076
|
return plan;
|
|
1094
1077
|
}
|
|
1078
|
+
/** Drop staged drafts a partially landed save already stored. */
|
|
1079
|
+
reconcileStaging() {
|
|
1080
|
+
for (const [field, staged] of [...this.staged]) {
|
|
1081
|
+
if (field === "apiKey" || staged.clear) continue;
|
|
1082
|
+
if (staged.text === this.spec(field).format(this.sectionValue(field))) this.staged.delete(field);
|
|
1083
|
+
}
|
|
1084
|
+
if (this.visibleModelsDraft !== void 0 && sameModels(this.visibleModelsDraft, this.storedVisibleModels())) this.visibleModelsDraft = void 0;
|
|
1085
|
+
}
|
|
1095
1086
|
async clear(field) {
|
|
1087
|
+
if (!this.stored(field)) return true;
|
|
1096
1088
|
await this.scope.unset(field);
|
|
1097
1089
|
return !this.stored(field);
|
|
1098
1090
|
}
|
|
@@ -1100,10 +1092,6 @@ window.__ModuleLoader__.load({
|
|
|
1100
1092
|
await this.scope.set(field, value);
|
|
1101
1093
|
return this.userLayer()?.[field] === value;
|
|
1102
1094
|
}
|
|
1103
|
-
/** Write the staged default key, then re-read whether the Host holds it. */
|
|
1104
|
-
async writeKey(value) {
|
|
1105
|
-
return this.writeKeyTo(this.credentialRef, value);
|
|
1106
|
-
}
|
|
1107
1095
|
/** Write one account's key, then re-read the Host's credential states. */
|
|
1108
1096
|
async writeKeyTo(ref, value) {
|
|
1109
1097
|
try {
|
|
@@ -1111,16 +1099,29 @@ window.__ModuleLoader__.load({
|
|
|
1111
1099
|
} catch {
|
|
1112
1100
|
return false;
|
|
1113
1101
|
}
|
|
1114
|
-
await this.describeAll();
|
|
1102
|
+
await this.describeAll([ref]);
|
|
1115
1103
|
return this.credentialStates.get(ref)?.configured ?? false;
|
|
1116
1104
|
}
|
|
1117
|
-
/**
|
|
1118
|
-
async
|
|
1119
|
-
|
|
1105
|
+
/** Unset one stored credential, then re-read the Host's credential states. */
|
|
1106
|
+
async unsetKey(ref) {
|
|
1107
|
+
try {
|
|
1108
|
+
if (!(await this.api.credentials.unset(ref)).ok) return false;
|
|
1109
|
+
} catch {
|
|
1110
|
+
return false;
|
|
1111
|
+
}
|
|
1112
|
+
await this.describeAll([ref]);
|
|
1113
|
+
return this.credentialStates.get(ref)?.configured !== true;
|
|
1114
|
+
}
|
|
1115
|
+
/**
|
|
1116
|
+
* Ask the credentials domain about every reference this page writes, plus
|
|
1117
|
+
* `extra` — a key written for an account whose row is not stored yet.
|
|
1118
|
+
*/
|
|
1119
|
+
async describeAll(extra = []) {
|
|
1120
|
+
const refs = [.../* @__PURE__ */ new Set([
|
|
1120
1121
|
this.credentialRef,
|
|
1121
|
-
...this.storedExtras().map((
|
|
1122
|
-
...
|
|
1123
|
-
];
|
|
1122
|
+
...this.storedExtras().map((account) => account.ref),
|
|
1123
|
+
...extra
|
|
1124
|
+
])];
|
|
1124
1125
|
let response;
|
|
1125
1126
|
try {
|
|
1126
1127
|
response = await this.api.credentials.describe(refs);
|
|
@@ -1183,20 +1184,19 @@ window.__ModuleLoader__.load({
|
|
|
1183
1184
|
return raw.filter((entry) => typeof entry === "object" && entry !== null && !Array.isArray(entry));
|
|
1184
1185
|
}
|
|
1185
1186
|
/**
|
|
1186
|
-
* The stored extra accounts
|
|
1187
|
-
*
|
|
1188
|
-
*
|
|
1189
|
-
*
|
|
1190
|
-
*
|
|
1191
|
-
* {@link rawStoredAccounts} and only rewrites the reference-carrying entries
|
|
1192
|
-
* it manages (see the note there), so a literal-key entry stays in the
|
|
1193
|
-
* document and simply has no row.
|
|
1187
|
+
* The stored extra accounts this page can address: the rows carrying a
|
|
1188
|
+
* credential reference. Entries it cannot name (a composition entry with a
|
|
1189
|
+
* literal `apiKey`, or an unknown shape) are never dropped — every list
|
|
1190
|
+
* write starts from {@link rawStoredAccounts} and copies them verbatim,
|
|
1191
|
+
* because the settings layer replaces the whole array.
|
|
1194
1192
|
*/
|
|
1195
1193
|
storedExtras() {
|
|
1196
1194
|
const out = [];
|
|
1195
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1197
1196
|
for (const record of this.rawStoredAccounts()) {
|
|
1198
1197
|
const ref = record.apiKeyEnv;
|
|
1199
|
-
if (typeof ref !== "string" || ref === "") continue;
|
|
1198
|
+
if (typeof ref !== "string" || ref === "" || seen.has(ref)) continue;
|
|
1199
|
+
seen.add(ref);
|
|
1200
1200
|
const label = record.label;
|
|
1201
1201
|
out.push({
|
|
1202
1202
|
label: typeof label === "string" && label !== "" ? label : ref,
|
|
@@ -1205,197 +1205,43 @@ window.__ModuleLoader__.load({
|
|
|
1205
1205
|
}
|
|
1206
1206
|
return out;
|
|
1207
1207
|
}
|
|
1208
|
-
/** Every extra account row: stored (minus staged removals) + staged adds. */
|
|
1209
1208
|
effectiveAccounts() {
|
|
1210
|
-
|
|
1211
|
-
...extra,
|
|
1212
|
-
added: false
|
|
1213
|
-
}));
|
|
1214
|
-
const added = this.addedAccounts.map((extra) => ({
|
|
1215
|
-
...extra,
|
|
1216
|
-
added: true
|
|
1217
|
-
}));
|
|
1218
|
-
return [...stored, ...added].map((extra) => ({
|
|
1209
|
+
return this.storedExtras().map((extra) => ({
|
|
1219
1210
|
id: extra.ref,
|
|
1220
1211
|
ref: extra.ref,
|
|
1221
|
-
label:
|
|
1222
|
-
keyText: this.keyDrafts.get(extra.ref) ?? "",
|
|
1212
|
+
label: extra.label,
|
|
1223
1213
|
configured: this.credentialStates.get(extra.ref)?.configured ?? false,
|
|
1224
|
-
writable: this.credentialStates.get(extra.ref)?.writable ?? true
|
|
1225
|
-
added: extra.added,
|
|
1226
|
-
clearStaged: this.keyClears.has(extra.ref)
|
|
1214
|
+
writable: this.credentialStates.get(extra.ref)?.writable ?? true
|
|
1227
1215
|
}));
|
|
1228
1216
|
}
|
|
1229
|
-
/**
|
|
1230
|
-
|
|
1231
|
-
return this.addedAccounts.length > 0 || this.removedRefs.size > 0 || this.labelDrafts.size > 0 || this.keyDrafts.size > 0 || this.keyClears.size > 0;
|
|
1232
|
-
}
|
|
1233
|
-
/** Whether the staged account edits differ from the stored section. */
|
|
1234
|
-
accountsDirty() {
|
|
1235
|
-
if (this.addedAccounts.length > 0 || this.removedRefs.size > 0) return true;
|
|
1236
|
-
for (const [ref, text] of this.labelDrafts) {
|
|
1237
|
-
const base = this.storedExtras().find((extra) => extra.ref === ref)?.label;
|
|
1238
|
-
if (base !== void 0 && text.trim() !== "" && text !== base) return true;
|
|
1239
|
-
}
|
|
1240
|
-
for (const text of this.keyDrafts.values()) if (text.trim() !== "") return true;
|
|
1241
|
-
for (const ref of this.keyClears) if (this.credentialStates.get(ref)?.configured === true) return true;
|
|
1242
|
-
return false;
|
|
1243
|
-
}
|
|
1244
|
-
/** Reset every account-level staged edit. */
|
|
1245
|
-
clearAccountStaging() {
|
|
1246
|
-
this.addedAccounts = [];
|
|
1247
|
-
this.removedRefs.clear();
|
|
1248
|
-
this.labelDrafts.clear();
|
|
1249
|
-
this.keyDrafts.clear();
|
|
1250
|
-
this.keyClears.clear();
|
|
1251
|
-
}
|
|
1252
|
-
/** Unset one stored credential, then re-read the Host's credential states. */
|
|
1253
|
-
async unsetKey(ref) {
|
|
1254
|
-
try {
|
|
1255
|
-
if (!(await this.api.credentials.unset(ref)).ok) return false;
|
|
1256
|
-
} catch {
|
|
1257
|
-
return false;
|
|
1258
|
-
}
|
|
1259
|
-
await this.describeAll();
|
|
1260
|
-
return this.credentialStates.get(ref)?.configured !== true;
|
|
1261
|
-
}
|
|
1262
|
-
/** The account-level writes a save performs (empty when nothing staged). */
|
|
1263
|
-
accountPlan() {
|
|
1264
|
-
if (!this.accountsDirty()) return [];
|
|
1265
|
-
const runs = [];
|
|
1266
|
-
for (const ref of this.keyClears) if (this.credentialStates.get(ref)?.configured === true) runs.push(() => this.unsetKey(ref));
|
|
1267
|
-
for (const [ref, text] of this.keyDrafts) {
|
|
1268
|
-
const value = text.trim();
|
|
1269
|
-
if (value !== "" && !this.removedRefs.has(ref) && !this.keyClears.has(ref)) runs.push(() => this.writeKeyTo(ref, value));
|
|
1270
|
-
}
|
|
1271
|
-
runs.push(() => this.writeAccounts());
|
|
1272
|
-
return runs;
|
|
1273
|
-
}
|
|
1274
|
-
/**
|
|
1275
|
-
* Persist the staged accounts list into the settings section.
|
|
1276
|
-
*
|
|
1277
|
-
* The stored list is the base — NOT a list rebuilt from this page's rows.
|
|
1278
|
-
* A composition-config entry may carry a literal `apiKey` (or a shape this
|
|
1279
|
-
* page does not know), and the settings layer replaces the whole array, so a
|
|
1280
|
-
* rebuilt list would silently delete every entry the page cannot name along
|
|
1281
|
-
* with the literal keys of the entries it can. Entries are therefore carried
|
|
1282
|
-
* over verbatim and only the reference-carrying rows are rewritten (label
|
|
1283
|
-
* draft applied, staged removals dropped, staged additions appended).
|
|
1284
|
-
*/
|
|
1285
|
-
async writeAccounts() {
|
|
1286
|
-
const removed = this.removedRefs;
|
|
1287
|
-
const written = /* @__PURE__ */ new Set();
|
|
1288
|
-
const list = [];
|
|
1289
|
-
for (const entry of this.rawStoredAccounts()) {
|
|
1290
|
-
const ref = entry.apiKeyEnv;
|
|
1291
|
-
if (typeof ref !== "string" || ref === "") {
|
|
1292
|
-
list.push({ ...entry });
|
|
1293
|
-
continue;
|
|
1294
|
-
}
|
|
1295
|
-
if (removed.has(ref) || written.has(ref)) continue;
|
|
1296
|
-
written.add(ref);
|
|
1297
|
-
list.push(this.accountEntry(ref, entry));
|
|
1298
|
-
}
|
|
1299
|
-
for (const extra of this.addedAccounts) {
|
|
1300
|
-
if (written.has(extra.ref)) continue;
|
|
1301
|
-
written.add(extra.ref);
|
|
1302
|
-
list.push(this.accountEntry(extra.ref, extra));
|
|
1303
|
-
}
|
|
1217
|
+
/** Persist a full accounts list and verify the Host stored it. */
|
|
1218
|
+
async writeAccountList(list) {
|
|
1304
1219
|
await this.scope.set("accounts", list);
|
|
1305
1220
|
const after = this.rawStoredAccounts();
|
|
1306
|
-
return after.length === list.length && list.every((item, index) => after[index]?.apiKeyEnv === item.apiKeyEnv);
|
|
1307
|
-
}
|
|
1308
|
-
/**
|
|
1309
|
-
* One written account entry: the stored/added facts plus the label draft.
|
|
1310
|
-
* `fallback` contributes the non-managed fields (a stored entry's literal
|
|
1311
|
-
* `apiKey`, or any future field) so a rewrite never strips them.
|
|
1312
|
-
*/
|
|
1313
|
-
accountEntry(ref, fallback) {
|
|
1314
|
-
const draft = this.labelDrafts.get(ref)?.trim();
|
|
1315
|
-
const base = "ref" in fallback ? { label: fallback.label } : { ...fallback };
|
|
1316
|
-
const storedLabel = base.label;
|
|
1317
|
-
return {
|
|
1318
|
-
...base,
|
|
1319
|
-
label: draft !== void 0 && draft !== "" ? draft : typeof storedLabel === "string" && storedLabel !== "" ? storedLabel : ref,
|
|
1320
|
-
apiKeyEnv: ref
|
|
1321
|
-
};
|
|
1221
|
+
return after.length === list.length && list.every((item, index) => after[index]?.apiKeyEnv === item.apiKeyEnv && after[index]?.label === item.label);
|
|
1322
1222
|
}
|
|
1323
1223
|
/** The stored routing rules from the settings section (`modelAccountRules`). */
|
|
1324
1224
|
storedRules() {
|
|
1325
1225
|
const raw = this.scope.getSnapshot().value?.modelAccountRules;
|
|
1326
1226
|
if (!Array.isArray(raw)) return [];
|
|
1327
1227
|
const out = [];
|
|
1328
|
-
for (const
|
|
1228
|
+
for (const entry of raw) {
|
|
1329
1229
|
if (typeof entry !== "object" || entry === null || Array.isArray(entry)) continue;
|
|
1330
1230
|
const record = entry;
|
|
1331
|
-
const models = record.models;
|
|
1332
|
-
|
|
1333
|
-
const modelsList = Array.isArray(models) && models.every((m) => typeof m === "string") ? models.filter((m) => m !== "") : [];
|
|
1334
|
-
if (modelsList.length === 0) continue;
|
|
1231
|
+
const models = Array.isArray(record.models) ? record.models.filter((m) => typeof m === "string" && m !== "") : [];
|
|
1232
|
+
if (models.length === 0) continue;
|
|
1335
1233
|
out.push({
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
account: typeof account === "string" && account !== "" ? account : "default"
|
|
1234
|
+
models,
|
|
1235
|
+
account: typeof record.account === "string" && record.account !== "" ? record.account : "default"
|
|
1339
1236
|
});
|
|
1340
1237
|
}
|
|
1341
1238
|
return out;
|
|
1342
1239
|
}
|
|
1343
|
-
/**
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
id: rule.id,
|
|
1349
|
-
models: draft?.models ?? rule.models,
|
|
1350
|
-
account: draft?.account ?? rule.account,
|
|
1351
|
-
added: false
|
|
1352
|
-
};
|
|
1353
|
-
});
|
|
1354
|
-
const added = this.addedRules.map((rule, index) => ({
|
|
1355
|
-
id: `new-${index}`,
|
|
1356
|
-
models: rule.models,
|
|
1357
|
-
account: rule.account,
|
|
1358
|
-
added: true
|
|
1359
|
-
}));
|
|
1360
|
-
return [...stored, ...added];
|
|
1361
|
-
}
|
|
1362
|
-
/** Whether any routing-rule staging (add/remove/edit) exists. */
|
|
1363
|
-
rulesStaged() {
|
|
1364
|
-
return this.addedRules.length > 0 || this.removedRuleIds.size > 0 || this.ruleDrafts.size > 0;
|
|
1365
|
-
}
|
|
1366
|
-
/** Whether the staged routing rules differ from the stored section. */
|
|
1367
|
-
rulesDirty() {
|
|
1368
|
-
if (this.addedRules.length > 0 || this.removedRuleIds.size > 0) return true;
|
|
1369
|
-
for (const [id, draft] of this.ruleDrafts) {
|
|
1370
|
-
const base = this.storedRules().find((rule) => rule.id === id);
|
|
1371
|
-
if (base === void 0) continue;
|
|
1372
|
-
if (draft.models.length > 0 && !sameModels(draft.models, base.models)) return true;
|
|
1373
|
-
if (draft.account !== base.account) return true;
|
|
1374
|
-
}
|
|
1375
|
-
return false;
|
|
1376
|
-
}
|
|
1377
|
-
/** Reset every routing-rule staged edit. */
|
|
1378
|
-
clearRuleStaging() {
|
|
1379
|
-
this.addedRules = [];
|
|
1380
|
-
this.ruleDrafts.clear();
|
|
1381
|
-
this.removedRuleIds.clear();
|
|
1382
|
-
}
|
|
1383
|
-
/**
|
|
1384
|
-
* Drop rule staging the stored section already reflects (partial-save
|
|
1385
|
-
* retry). Stored row ids are positional (`rule-N`) and shift after any
|
|
1386
|
-
* write, so every check here is content-based, never id-based:
|
|
1387
|
-
* - landed additions (in `addedRules`, already in stored) are dropped, or
|
|
1388
|
-
* a retry would persist them twice;
|
|
1389
|
-
* - staged removals whose snapshot row is gone from stored have landed
|
|
1390
|
-
* (drop them); a removal whose snapshot still matches a stored row is
|
|
1391
|
-
* still pending (keep it).
|
|
1392
|
-
*/
|
|
1393
|
-
reconcileRuleStaging() {
|
|
1394
|
-
const stored = this.storedRules();
|
|
1395
|
-
this.addedRules = this.addedRules.filter((added) => !stored.some((rule) => sameModels(added.models, rule.models) && added.account === rule.account));
|
|
1396
|
-
for (const [id, snapshot] of [...this.removedRuleIds]) if (!stored.some((rule) => sameModels(snapshot.models, rule.models) && snapshot.account === rule.account)) this.removedRuleIds.delete(id);
|
|
1397
|
-
if (this.rulesBeforeSave !== void 0 && ruleFingerprint(stored) === this.rulesBeforeSave) return;
|
|
1398
|
-
this.ruleDrafts.clear();
|
|
1240
|
+
/** Persist routing rules and verify the Host stored them. */
|
|
1241
|
+
async writeRules(list) {
|
|
1242
|
+
await this.scope.set("modelAccountRules", list);
|
|
1243
|
+
const after = this.storedRules();
|
|
1244
|
+
return after.length === list.length && list.every((item, index) => after[index] !== void 0 && sameModels(after[index].models, item.models) && after[index].account === item.account);
|
|
1399
1245
|
}
|
|
1400
1246
|
/** The stored visible-model allowlist (`visibleModels`); empty = show all. */
|
|
1401
1247
|
storedVisibleModels() {
|
|
@@ -1403,79 +1249,19 @@ window.__ModuleLoader__.load({
|
|
|
1403
1249
|
if (!Array.isArray(raw)) return [];
|
|
1404
1250
|
return raw.filter((m) => typeof m === "string" && m !== "");
|
|
1405
1251
|
}
|
|
1406
|
-
/** Effective visible-model allowlist: staged draft or stored value. */
|
|
1407
1252
|
effectiveVisibleModels() {
|
|
1408
1253
|
return this.visibleModelsDraft ?? this.storedVisibleModels();
|
|
1409
1254
|
}
|
|
1410
|
-
/** Whether the staged visible-model selection differs from stored. */
|
|
1411
1255
|
visibleModelsDirty() {
|
|
1412
1256
|
return this.visibleModelsDraft !== void 0 && !sameModels(this.visibleModelsDraft, this.storedVisibleModels());
|
|
1413
1257
|
}
|
|
1414
|
-
/** Whether any visible-model staging exists. */
|
|
1415
|
-
visibleModelsStaged() {
|
|
1416
|
-
return this.visibleModelsDraft !== void 0;
|
|
1417
|
-
}
|
|
1418
|
-
/** Reset the visible-model staged edit. */
|
|
1419
|
-
clearVisibleModelsStaging() {
|
|
1420
|
-
this.visibleModelsDraft = void 0;
|
|
1421
|
-
}
|
|
1422
|
-
/** Drop visible-model staging the stored section already reflects. */
|
|
1423
|
-
reconcileVisibleModelsStaging() {
|
|
1424
|
-
if (this.visibleModelsDraft !== void 0 && sameModels(this.visibleModelsDraft, this.storedVisibleModels())) this.visibleModelsDraft = void 0;
|
|
1425
|
-
}
|
|
1426
|
-
/** The visible-model writes a save performs (empty when nothing staged). */
|
|
1427
1258
|
visibleModelsPlan() {
|
|
1428
1259
|
if (!this.visibleModelsDirty()) return [];
|
|
1429
|
-
return [() =>
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
await this.scope.set("visibleModels", list);
|
|
1435
|
-
return sameModels(this.storedVisibleModels(), list);
|
|
1436
|
-
}
|
|
1437
|
-
/** Stage the visible-model allowlist (multi-select). */
|
|
1438
|
-
editVisibleModels(models) {
|
|
1439
|
-
this.visibleModelsDraft = [...models];
|
|
1440
|
-
this.failed = false;
|
|
1441
|
-
this.publish();
|
|
1442
|
-
}
|
|
1443
|
-
/** Stage "show all models" (clears the allowlist). */
|
|
1444
|
-
clearVisibleModels() {
|
|
1445
|
-
this.visibleModelsDraft = [];
|
|
1446
|
-
this.failed = false;
|
|
1447
|
-
this.publish();
|
|
1448
|
-
}
|
|
1449
|
-
/** The routing-rule writes a save performs (empty when nothing staged). */
|
|
1450
|
-
rulesPlan() {
|
|
1451
|
-
if (!this.rulesDirty()) return [];
|
|
1452
|
-
return [() => this.writeRules()];
|
|
1453
|
-
}
|
|
1454
|
-
/** Persist the staged routing rules into the settings section. */
|
|
1455
|
-
async writeRules() {
|
|
1456
|
-
const list = [...this.storedRules().filter((rule) => {
|
|
1457
|
-
const snapshot = this.removedRuleIds.get(rule.id);
|
|
1458
|
-
return snapshot === void 0 || !sameModels(snapshot.models, rule.models) || snapshot.account !== rule.account;
|
|
1459
|
-
}).map((rule) => {
|
|
1460
|
-
const draft = this.ruleDrafts.get(rule.id);
|
|
1461
|
-
return {
|
|
1462
|
-
models: draft !== void 0 && draft.models.length > 0 ? draft.models : rule.models,
|
|
1463
|
-
account: draft?.account !== void 0 && draft.account !== "" ? draft.account : rule.account
|
|
1464
|
-
};
|
|
1465
|
-
}), ...this.addedRules.map((rule) => ({
|
|
1466
|
-
models: rule.models,
|
|
1467
|
-
account: rule.account
|
|
1468
|
-
}))].filter((rule) => rule.models.length > 0);
|
|
1469
|
-
const seen = /* @__PURE__ */ new Set();
|
|
1470
|
-
const deduped = list.filter((rule) => {
|
|
1471
|
-
const key = `${rule.account}\u0001${[...rule.models].sort().join("")}`;
|
|
1472
|
-
if (seen.has(key)) return false;
|
|
1473
|
-
seen.add(key);
|
|
1474
|
-
return true;
|
|
1475
|
-
});
|
|
1476
|
-
await this.scope.set("modelAccountRules", deduped);
|
|
1477
|
-
const after = this.storedRules();
|
|
1478
|
-
return after.length === deduped.length && deduped.every((item, index) => after[index] !== void 0 && sameModels(after[index].models, item.models) && after[index].account === item.account);
|
|
1260
|
+
return [async () => {
|
|
1261
|
+
const list = this.visibleModelsDraft ?? [];
|
|
1262
|
+
await this.scope.set("visibleModels", list);
|
|
1263
|
+
return sameModels(this.storedVisibleModels(), list);
|
|
1264
|
+
}];
|
|
1479
1265
|
}
|
|
1480
1266
|
publish() {
|
|
1481
1267
|
if (this.disposed) return;
|
|
@@ -1483,47 +1269,6 @@ window.__ModuleLoader__.load({
|
|
|
1483
1269
|
}
|
|
1484
1270
|
};
|
|
1485
1271
|
//#endregion
|
|
1486
|
-
//#region src/client/legacy-credentials.ts
|
|
1487
|
-
/** Convert a legacy credentials ApiProxy into the current settings-page face. */
|
|
1488
|
-
function adaptLegacyCredentials(legacy) {
|
|
1489
|
-
if (legacy === void 0) return void 0;
|
|
1490
|
-
return { credentials: {
|
|
1491
|
-
describe: async (refs) => {
|
|
1492
|
-
const response = await legacy.describe({ refs });
|
|
1493
|
-
return response.result.ok ? {
|
|
1494
|
-
ok: true,
|
|
1495
|
-
value: response.result.value.credentials
|
|
1496
|
-
} : {
|
|
1497
|
-
ok: false,
|
|
1498
|
-
error: response.result.error
|
|
1499
|
-
};
|
|
1500
|
-
},
|
|
1501
|
-
set: async (ref, value) => {
|
|
1502
|
-
const response = await legacy.set({
|
|
1503
|
-
ref,
|
|
1504
|
-
value
|
|
1505
|
-
});
|
|
1506
|
-
return response.result.ok ? {
|
|
1507
|
-
ok: true,
|
|
1508
|
-
value: void 0
|
|
1509
|
-
} : {
|
|
1510
|
-
ok: false,
|
|
1511
|
-
error: response.result.error
|
|
1512
|
-
};
|
|
1513
|
-
},
|
|
1514
|
-
unset: async (ref) => {
|
|
1515
|
-
const response = await legacy.unset({ ref });
|
|
1516
|
-
return response.result.ok ? {
|
|
1517
|
-
ok: true,
|
|
1518
|
-
value: void 0
|
|
1519
|
-
} : {
|
|
1520
|
-
ok: false,
|
|
1521
|
-
error: response.result.error
|
|
1522
|
-
};
|
|
1523
|
-
}
|
|
1524
|
-
} };
|
|
1525
|
-
}
|
|
1526
|
-
//#endregion
|
|
1527
1272
|
//#region src/client/usage.ts
|
|
1528
1273
|
const IDLE$1 = {
|
|
1529
1274
|
status: "idle",
|
|
@@ -2129,7 +1874,6 @@ window.__ModuleLoader__.load({
|
|
|
2129
1874
|
return {
|
|
2130
1875
|
mode: "strict",
|
|
2131
1876
|
typeSymbol,
|
|
2132
|
-
schema,
|
|
2133
1877
|
create: () => schema
|
|
2134
1878
|
};
|
|
2135
1879
|
}
|
|
@@ -2141,7 +1885,6 @@ window.__ModuleLoader__.load({
|
|
|
2141
1885
|
* only its own facts.
|
|
2142
1886
|
*/
|
|
2143
1887
|
function makeRemoteDescriptor(endpoint, method, typeSymbol, schema) {
|
|
2144
|
-
const result = makeStrictCodec(typeSymbol, schema);
|
|
2145
1888
|
return {
|
|
2146
1889
|
id: `${REMOTE_PACKAGE}#${endpoint}`,
|
|
2147
1890
|
service: REMOTE_SERVICE,
|
|
@@ -2149,7 +1892,7 @@ window.__ModuleLoader__.load({
|
|
|
2149
1892
|
method,
|
|
2150
1893
|
invocation: { kind: "direct" },
|
|
2151
1894
|
parameters: [],
|
|
2152
|
-
result
|
|
1895
|
+
result: makeStrictCodec(typeSymbol, schema)
|
|
2153
1896
|
};
|
|
2154
1897
|
}
|
|
2155
1898
|
//#endregion
|
|
@@ -2439,55 +2182,6 @@ window.__ModuleLoader__.load({
|
|
|
2439
2182
|
]
|
|
2440
2183
|
};
|
|
2441
2184
|
//#endregion
|
|
2442
|
-
//#region src/client/login-row.tsx
|
|
2443
|
-
/** The sign-in alternative to pasting a key (settings page + Models card). */
|
|
2444
|
-
function LoginRow({ state, targetRef, disabled, disabledHint, t, onBegin, onCancel }) {
|
|
2445
|
-
const { visible: visibleState, busyElsewhere } = loginStateForTarget(state, targetRef);
|
|
2446
|
-
const busy = visibleState.phase === "starting" || visibleState.phase === "waiting";
|
|
2447
|
-
const hint = loginHint(visibleState, t);
|
|
2448
|
-
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2449
|
-
className: "cc-field",
|
|
2450
|
-
children: [
|
|
2451
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2452
|
-
className: "cc-fieldHead",
|
|
2453
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2454
|
-
className: "cc-label",
|
|
2455
|
-
children: t("loginTitle")
|
|
2456
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2457
|
-
className: "cc-badges",
|
|
2458
|
-
children: busy ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2459
|
-
type: "button",
|
|
2460
|
-
className: "cc-reset",
|
|
2461
|
-
onClick: onCancel,
|
|
2462
|
-
children: t("loginCancel")
|
|
2463
|
-
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2464
|
-
type: "button",
|
|
2465
|
-
className: "cc-reset",
|
|
2466
|
-
disabled: disabled || busyElsewhere,
|
|
2467
|
-
onClick: onBegin,
|
|
2468
|
-
children: t("loginButton")
|
|
2469
|
-
})
|
|
2470
|
-
})]
|
|
2471
|
-
}),
|
|
2472
|
-
visibleState.authUrl !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
2473
|
-
className: "cc-hint",
|
|
2474
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("a", {
|
|
2475
|
-
className: "cc-loginLink",
|
|
2476
|
-
href: visibleState.authUrl,
|
|
2477
|
-
target: "_blank",
|
|
2478
|
-
rel: "noreferrer",
|
|
2479
|
-
children: t("loginOpenLink")
|
|
2480
|
-
})
|
|
2481
|
-
}) : null,
|
|
2482
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
2483
|
-
className: hint.className,
|
|
2484
|
-
title: hint.title,
|
|
2485
|
-
children: disabled && !busy && disabledHint !== void 0 ? disabledHint : hint.text
|
|
2486
|
-
})
|
|
2487
|
-
]
|
|
2488
|
-
});
|
|
2489
|
-
}
|
|
2490
|
-
//#endregion
|
|
2491
2185
|
//#region src/client/model-select.ts
|
|
2492
2186
|
/**
|
|
2493
2187
|
* Model-select helpers for the settings page's model editors (browser half).
|
|
@@ -2631,7 +2325,7 @@ window.__ModuleLoader__.load({
|
|
|
2631
2325
|
}
|
|
2632
2326
|
//#endregion
|
|
2633
2327
|
//#region package.json
|
|
2634
|
-
var version = "0.11.
|
|
2328
|
+
var version = "0.11.13";
|
|
2635
2329
|
var repository = {
|
|
2636
2330
|
"type": "git",
|
|
2637
2331
|
"url": "git+https://github.com/Mars-Sea/dsh-commandcode-provider.git"
|
|
@@ -2817,69 +2511,112 @@ window.__ModuleLoader__.load({
|
|
|
2817
2511
|
* `settings.commandcode` locale namespace; all state comes from the
|
|
2818
2512
|
* `CommandCodeSettingsController` injected by the slot registration.
|
|
2819
2513
|
*
|
|
2820
|
-
*
|
|
2821
|
-
*
|
|
2822
|
-
*
|
|
2823
|
-
*
|
|
2824
|
-
*
|
|
2825
|
-
*
|
|
2826
|
-
* (
|
|
2514
|
+
* Layout, top to bottom:
|
|
2515
|
+
* 1. Accounts — one card per account with its status, quota, actions and
|
|
2516
|
+
* dedicated models. Every account operation commits immediately.
|
|
2517
|
+
* 2. Models — the plan filter and the visible-model allowlist.
|
|
2518
|
+
* 3. Privacy & security — zero data retention and the command guard.
|
|
2519
|
+
* 4. Integrations & display — web search and the sidebar quota card.
|
|
2520
|
+
* 5. Advanced (collapsed) — API base and the network limits.
|
|
2521
|
+
* Sections 2–5 are flat groups of rows (title and description left, control
|
|
2522
|
+
* right, as on the harness's own settings pages), a staged form written by
|
|
2523
|
+
* the floating save bar.
|
|
2524
|
+
*
|
|
2525
|
+
* Styles are injected once by the client entry (see src/client/index.ts) and
|
|
2526
|
+
* class-prefixed `cc-` to stay local.
|
|
2827
2527
|
*/
|
|
2828
2528
|
const ADVANCED_FIELDS = [
|
|
2829
2529
|
"apiBase",
|
|
2830
|
-
"workingDir",
|
|
2831
2530
|
"requestTimeoutMs",
|
|
2832
2531
|
"streamIdleTimeoutMs",
|
|
2833
|
-
"transportMaxRetries"
|
|
2834
|
-
"filterModelsByPlan",
|
|
2835
|
-
"webSearch",
|
|
2836
|
-
"showSidebarQuota",
|
|
2837
|
-
"commandGuard",
|
|
2838
|
-
"commandGuardThreshold",
|
|
2839
|
-
"commandGuardTimeoutMs",
|
|
2840
|
-
"zdr"
|
|
2532
|
+
"transportMaxRetries"
|
|
2841
2533
|
];
|
|
2842
|
-
/**
|
|
2843
|
-
function
|
|
2534
|
+
/** The "customized" tag and reset link a staged field shows once it differs from the default. */
|
|
2535
|
+
function FieldOverride({ label, state, disabled, t, onReset }) {
|
|
2536
|
+
if (!state.overridden && !state.clear) return null;
|
|
2537
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2538
|
+
type: "button",
|
|
2539
|
+
className: "cc-linkButton",
|
|
2540
|
+
disabled,
|
|
2541
|
+
onClick: onReset,
|
|
2542
|
+
"aria-label": `${label} — ${t("reset")}`,
|
|
2543
|
+
children: t("reset")
|
|
2544
|
+
});
|
|
2545
|
+
}
|
|
2546
|
+
/**
|
|
2547
|
+
* One settings row, laid out like the harness's own General page: the title
|
|
2548
|
+
* and description on the left, the control on the right, a hairline between
|
|
2549
|
+
* rows.
|
|
2550
|
+
*/
|
|
2551
|
+
function SettingRow({ title, titleFor, titleId, tag, description, error, control, className }) {
|
|
2844
2552
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2845
|
-
className: "cc-
|
|
2846
|
-
children: [
|
|
2847
|
-
|
|
2848
|
-
|
|
2849
|
-
|
|
2850
|
-
className: "cc-
|
|
2851
|
-
|
|
2852
|
-
|
|
2853
|
-
|
|
2854
|
-
|
|
2855
|
-
|
|
2856
|
-
|
|
2857
|
-
|
|
2858
|
-
|
|
2859
|
-
|
|
2860
|
-
|
|
2861
|
-
|
|
2862
|
-
|
|
2863
|
-
|
|
2864
|
-
|
|
2865
|
-
|
|
2866
|
-
|
|
2867
|
-
|
|
2868
|
-
|
|
2869
|
-
|
|
2870
|
-
|
|
2871
|
-
|
|
2872
|
-
|
|
2873
|
-
|
|
2874
|
-
|
|
2875
|
-
|
|
2876
|
-
|
|
2877
|
-
|
|
2878
|
-
|
|
2879
|
-
|
|
2880
|
-
|
|
2881
|
-
|
|
2882
|
-
|
|
2553
|
+
className: className === void 0 ? "cc-row" : `cc-row ${className}`,
|
|
2554
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2555
|
+
className: "cc-rowText",
|
|
2556
|
+
children: [
|
|
2557
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2558
|
+
className: "cc-rowTitleLine",
|
|
2559
|
+
children: [titleFor !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("label", {
|
|
2560
|
+
className: "cc-rowTitle",
|
|
2561
|
+
id: titleId,
|
|
2562
|
+
htmlFor: titleFor,
|
|
2563
|
+
children: title
|
|
2564
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2565
|
+
className: "cc-rowTitle",
|
|
2566
|
+
id: titleId,
|
|
2567
|
+
children: title
|
|
2568
|
+
}), tag]
|
|
2569
|
+
}),
|
|
2570
|
+
error !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
2571
|
+
className: "cc-rowError",
|
|
2572
|
+
children: error
|
|
2573
|
+
}) : null,
|
|
2574
|
+
error === void 0 && description !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
2575
|
+
className: "cc-rowDesc",
|
|
2576
|
+
children: description
|
|
2577
|
+
}) : null
|
|
2578
|
+
]
|
|
2579
|
+
}), control !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
2580
|
+
className: "cc-rowControl",
|
|
2581
|
+
children: control
|
|
2582
|
+
}) : null]
|
|
2583
|
+
});
|
|
2584
|
+
}
|
|
2585
|
+
/** The tag a row title carries while its field overrides the default. */
|
|
2586
|
+
function OverrideTag({ state, t }) {
|
|
2587
|
+
return state.overridden ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2588
|
+
className: "cc-badge",
|
|
2589
|
+
children: t("overridden")
|
|
2590
|
+
}) : null;
|
|
2591
|
+
}
|
|
2592
|
+
/** One text/number field row. */
|
|
2593
|
+
function Field({ id, label, hint, state, disabled, numeric, wide, placeholder, onEdit, onReset, t }) {
|
|
2594
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SettingRow, {
|
|
2595
|
+
title: label,
|
|
2596
|
+
titleFor: id,
|
|
2597
|
+
tag: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(OverrideTag, {
|
|
2598
|
+
state,
|
|
2599
|
+
t
|
|
2600
|
+
}),
|
|
2601
|
+
description: hint,
|
|
2602
|
+
error: state.invalid ? invalidCopy(state.invalidReason, t) : void 0,
|
|
2603
|
+
control: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(FieldOverride, {
|
|
2604
|
+
label,
|
|
2605
|
+
state,
|
|
2606
|
+
disabled,
|
|
2607
|
+
t,
|
|
2608
|
+
onReset
|
|
2609
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
2610
|
+
id,
|
|
2611
|
+
className: `cc-input ${wide ? "cc-rowInputWide" : "cc-rowInput"}${state.invalid ? " cc-inputInvalid" : ""}`,
|
|
2612
|
+
type: "text",
|
|
2613
|
+
inputMode: numeric ? "numeric" : void 0,
|
|
2614
|
+
"aria-invalid": state.invalid || void 0,
|
|
2615
|
+
value: state.text,
|
|
2616
|
+
placeholder,
|
|
2617
|
+
disabled,
|
|
2618
|
+
onChange: (event) => onEdit(event.target.value)
|
|
2619
|
+
})] })
|
|
2883
2620
|
});
|
|
2884
2621
|
}
|
|
2885
2622
|
/** The per-field error copy for a staged draft's failure reason. */
|
|
@@ -2889,300 +2626,82 @@ window.__ModuleLoader__.load({
|
|
|
2889
2626
|
return t("invalidNumber");
|
|
2890
2627
|
}
|
|
2891
2628
|
/**
|
|
2892
|
-
*
|
|
2893
|
-
* plan filter, the web-search backend and the sidebar quota card live here so
|
|
2894
|
-
* the page leads with the facts a user actually touches. Starts collapsed on
|
|
2895
|
-
* every visit; expands on demand. While collapsed, a badge names the
|
|
2896
|
-
* customized count so a nonzero override stays visible (a number field's error
|
|
2897
|
-
* blocks save and must be reachable).
|
|
2898
|
-
*/
|
|
2899
|
-
function AdvancedSection({ state, disabled, t, onEdit, onReset }) {
|
|
2900
|
-
const [expanded, setExpanded] = (0, react.useState)(false);
|
|
2901
|
-
const overridden = ADVANCED_FIELDS.filter((field) => state[field].overridden).length;
|
|
2902
|
-
const invalid = ADVANCED_FIELDS.some((field) => state[field].invalid);
|
|
2903
|
-
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2904
|
-
className: "cc-card",
|
|
2905
|
-
children: [
|
|
2906
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
2907
|
-
type: "button",
|
|
2908
|
-
className: "cc-advancedHead",
|
|
2909
|
-
"aria-expanded": expanded,
|
|
2910
|
-
"aria-controls": "cc-advanced-body",
|
|
2911
|
-
onClick: () => setExpanded((value) => !value),
|
|
2912
|
-
children: [
|
|
2913
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2914
|
-
className: "cc-advancedTitle",
|
|
2915
|
-
children: t("advancedSettings")
|
|
2916
|
-
}),
|
|
2917
|
-
overridden > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2918
|
-
className: "cc-badge",
|
|
2919
|
-
children: overridden === 1 ? t("advancedOverriddenOne") : t("advancedOverriddenMany", { count: overridden })
|
|
2920
|
-
}) : null,
|
|
2921
|
-
!expanded && invalid ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2922
|
-
className: "cc-badge cc-badgeWarn",
|
|
2923
|
-
children: t("advancedInvalid")
|
|
2924
|
-
}) : null,
|
|
2925
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: "cc-advancedSpacer" }),
|
|
2926
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2927
|
-
className: expanded ? "cc-chevron cc-chevronUp" : "cc-chevron",
|
|
2928
|
-
"aria-hidden": "true"
|
|
2929
|
-
})
|
|
2930
|
-
]
|
|
2931
|
-
}),
|
|
2932
|
-
expanded ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2933
|
-
id: "cc-advanced-body",
|
|
2934
|
-
className: "cc-advancedBody",
|
|
2935
|
-
children: [
|
|
2936
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
2937
|
-
className: "cc-hint",
|
|
2938
|
-
children: t("advancedSettingsHint")
|
|
2939
|
-
}),
|
|
2940
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Field, {
|
|
2941
|
-
id: "cc-api-base",
|
|
2942
|
-
label: t("apiBase"),
|
|
2943
|
-
hint: t("apiBaseHint"),
|
|
2944
|
-
state: state.apiBase,
|
|
2945
|
-
disabled,
|
|
2946
|
-
onEdit: (text) => onEdit("apiBase", text),
|
|
2947
|
-
onReset: () => onReset("apiBase"),
|
|
2948
|
-
t
|
|
2949
|
-
}),
|
|
2950
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Field, {
|
|
2951
|
-
id: "cc-working-dir",
|
|
2952
|
-
label: t("workingDir"),
|
|
2953
|
-
hint: t("workingDirHint"),
|
|
2954
|
-
state: state.workingDir,
|
|
2955
|
-
disabled,
|
|
2956
|
-
placeholder: state.defaultWorkingDir,
|
|
2957
|
-
onEdit: (text) => onEdit("workingDir", text),
|
|
2958
|
-
onReset: () => onReset("workingDir"),
|
|
2959
|
-
t
|
|
2960
|
-
}),
|
|
2961
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Field, {
|
|
2962
|
-
id: "cc-request-timeout",
|
|
2963
|
-
label: t("requestTimeoutMs"),
|
|
2964
|
-
hint: t("requestTimeoutMsHint"),
|
|
2965
|
-
state: state.requestTimeoutMs,
|
|
2966
|
-
disabled,
|
|
2967
|
-
numeric: true,
|
|
2968
|
-
onEdit: (text) => onEdit("requestTimeoutMs", text),
|
|
2969
|
-
onReset: () => onReset("requestTimeoutMs"),
|
|
2970
|
-
t
|
|
2971
|
-
}),
|
|
2972
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Field, {
|
|
2973
|
-
id: "cc-stream-idle-timeout",
|
|
2974
|
-
label: t("streamIdleTimeoutMs"),
|
|
2975
|
-
hint: t("streamIdleTimeoutMsHint"),
|
|
2976
|
-
state: state.streamIdleTimeoutMs,
|
|
2977
|
-
disabled,
|
|
2978
|
-
numeric: true,
|
|
2979
|
-
onEdit: (text) => onEdit("streamIdleTimeoutMs", text),
|
|
2980
|
-
onReset: () => onReset("streamIdleTimeoutMs"),
|
|
2981
|
-
t
|
|
2982
|
-
}),
|
|
2983
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Field, {
|
|
2984
|
-
id: "cc-transport-max-retries",
|
|
2985
|
-
label: t("transportMaxRetries"),
|
|
2986
|
-
hint: t("transportMaxRetriesHint"),
|
|
2987
|
-
state: state.transportMaxRetries,
|
|
2988
|
-
disabled,
|
|
2989
|
-
numeric: true,
|
|
2990
|
-
onEdit: (text) => onEdit("transportMaxRetries", text),
|
|
2991
|
-
onReset: () => onReset("transportMaxRetries"),
|
|
2992
|
-
t
|
|
2993
|
-
}),
|
|
2994
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(ToggleField, {
|
|
2995
|
-
id: "cc-filter-models-by-plan",
|
|
2996
|
-
label: t("filterModelsByPlan"),
|
|
2997
|
-
hint: t("filterModelsByPlanHint"),
|
|
2998
|
-
state: state.filterModelsByPlan,
|
|
2999
|
-
disabled,
|
|
3000
|
-
defaultChecked: true,
|
|
3001
|
-
onEdit: (text) => onEdit("filterModelsByPlan", text),
|
|
3002
|
-
onReset: () => onReset("filterModelsByPlan"),
|
|
3003
|
-
t
|
|
3004
|
-
}),
|
|
3005
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(ToggleField, {
|
|
3006
|
-
id: "cc-web-search",
|
|
3007
|
-
label: t("webSearch"),
|
|
3008
|
-
hint: t("webSearchHint"),
|
|
3009
|
-
state: state.webSearch,
|
|
3010
|
-
disabled,
|
|
3011
|
-
defaultChecked: true,
|
|
3012
|
-
onEdit: (text) => onEdit("webSearch", text),
|
|
3013
|
-
onReset: () => onReset("webSearch"),
|
|
3014
|
-
t
|
|
3015
|
-
}),
|
|
3016
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(ToggleField, {
|
|
3017
|
-
id: "cc-show-sidebar-quota",
|
|
3018
|
-
label: t("showSidebarQuota"),
|
|
3019
|
-
hint: t("showSidebarQuotaHint"),
|
|
3020
|
-
state: state.showSidebarQuota,
|
|
3021
|
-
disabled,
|
|
3022
|
-
defaultChecked: false,
|
|
3023
|
-
onEdit: (text) => onEdit("showSidebarQuota", text),
|
|
3024
|
-
onReset: () => onReset("showSidebarQuota"),
|
|
3025
|
-
t
|
|
3026
|
-
}),
|
|
3027
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(ToggleField, {
|
|
3028
|
-
id: "cc-command-guard",
|
|
3029
|
-
label: t("commandGuard"),
|
|
3030
|
-
hint: t("commandGuardHint"),
|
|
3031
|
-
state: state.commandGuard,
|
|
3032
|
-
disabled,
|
|
3033
|
-
defaultChecked: false,
|
|
3034
|
-
onEdit: (text) => onEdit("commandGuard", text),
|
|
3035
|
-
onReset: () => onReset("commandGuard"),
|
|
3036
|
-
t
|
|
3037
|
-
}),
|
|
3038
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Field, {
|
|
3039
|
-
id: "cc-command-guard-threshold",
|
|
3040
|
-
label: t("commandGuardThreshold"),
|
|
3041
|
-
hint: t("commandGuardThresholdHint"),
|
|
3042
|
-
state: state.commandGuardThreshold,
|
|
3043
|
-
disabled,
|
|
3044
|
-
onEdit: (text) => onEdit("commandGuardThreshold", text),
|
|
3045
|
-
onReset: () => onReset("commandGuardThreshold"),
|
|
3046
|
-
t
|
|
3047
|
-
}),
|
|
3048
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Field, {
|
|
3049
|
-
id: "cc-command-guard-timeout",
|
|
3050
|
-
label: t("commandGuardTimeoutMs"),
|
|
3051
|
-
hint: t("commandGuardTimeoutMsHint"),
|
|
3052
|
-
state: state.commandGuardTimeoutMs,
|
|
3053
|
-
disabled,
|
|
3054
|
-
numeric: true,
|
|
3055
|
-
onEdit: (text) => onEdit("commandGuardTimeoutMs", text),
|
|
3056
|
-
onReset: () => onReset("commandGuardTimeoutMs"),
|
|
3057
|
-
t
|
|
3058
|
-
}),
|
|
3059
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(ToggleField, {
|
|
3060
|
-
id: "cc-zdr",
|
|
3061
|
-
label: t("zdr"),
|
|
3062
|
-
hint: t("zdrHint"),
|
|
3063
|
-
state: state.zdr,
|
|
3064
|
-
disabled,
|
|
3065
|
-
defaultChecked: false,
|
|
3066
|
-
onEdit: (text) => onEdit("zdr", text),
|
|
3067
|
-
onReset: () => onReset("zdr"),
|
|
3068
|
-
t
|
|
3069
|
-
})
|
|
3070
|
-
]
|
|
3071
|
-
}) : null,
|
|
3072
|
-
expanded && invalid ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
3073
|
-
className: "cc-invalid",
|
|
3074
|
-
role: "status",
|
|
3075
|
-
children: t("advancedInvalid")
|
|
3076
|
-
}) : null
|
|
3077
|
-
]
|
|
3078
|
-
});
|
|
3079
|
-
}
|
|
3080
|
-
/**
|
|
3081
|
-
* One boolean field row rendered as a toggle. The staged text is `'true'` /
|
|
2629
|
+
* One boolean field row rendered as a switch. The staged text is `'true'` /
|
|
3082
2630
|
* `'false'` / `''` (unset → `defaultChecked`); toggling stages the string the
|
|
3083
2631
|
* boolean field spec parses back into a real boolean on save.
|
|
3084
2632
|
*/
|
|
3085
2633
|
function ToggleField({ id, label, hint, state, disabled, defaultChecked, onEdit, onReset, t }) {
|
|
3086
2634
|
const checked = state.text === "" ? defaultChecked : state.text === "true";
|
|
3087
|
-
return /* @__PURE__ */ (0, react_jsx_runtime.
|
|
3088
|
-
|
|
3089
|
-
|
|
3090
|
-
|
|
3091
|
-
|
|
3092
|
-
|
|
3093
|
-
|
|
3094
|
-
|
|
3095
|
-
|
|
3096
|
-
|
|
3097
|
-
|
|
3098
|
-
|
|
3099
|
-
|
|
3100
|
-
|
|
3101
|
-
|
|
3102
|
-
|
|
3103
|
-
|
|
3104
|
-
|
|
3105
|
-
|
|
3106
|
-
|
|
3107
|
-
|
|
3108
|
-
|
|
3109
|
-
})
|
|
3110
|
-
className: "cc-toggleRow",
|
|
3111
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
3112
|
-
id,
|
|
3113
|
-
className: "cc-toggle",
|
|
3114
|
-
type: "checkbox",
|
|
3115
|
-
role: "switch",
|
|
3116
|
-
checked,
|
|
3117
|
-
disabled,
|
|
3118
|
-
onChange: (event) => onEdit(event.target.checked ? "true" : "false")
|
|
3119
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3120
|
-
className: "cc-hint",
|
|
3121
|
-
children: hint
|
|
3122
|
-
})]
|
|
3123
|
-
})]
|
|
2635
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SettingRow, {
|
|
2636
|
+
title: label,
|
|
2637
|
+
titleFor: id,
|
|
2638
|
+
tag: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(OverrideTag, {
|
|
2639
|
+
state,
|
|
2640
|
+
t
|
|
2641
|
+
}),
|
|
2642
|
+
description: hint,
|
|
2643
|
+
control: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(FieldOverride, {
|
|
2644
|
+
label,
|
|
2645
|
+
state,
|
|
2646
|
+
disabled,
|
|
2647
|
+
t,
|
|
2648
|
+
onReset
|
|
2649
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
2650
|
+
id,
|
|
2651
|
+
className: "cc-toggle",
|
|
2652
|
+
type: "checkbox",
|
|
2653
|
+
role: "switch",
|
|
2654
|
+
checked,
|
|
2655
|
+
disabled,
|
|
2656
|
+
onChange: (event) => onEdit(event.target.checked ? "true" : "false")
|
|
2657
|
+
})] })
|
|
3124
2658
|
});
|
|
3125
2659
|
}
|
|
3126
2660
|
/**
|
|
3127
|
-
*
|
|
3128
|
-
*
|
|
3129
|
-
*
|
|
3130
|
-
* staged for removal (the next save unsets it) when it is bad or unwanted.
|
|
2661
|
+
* One fixed-choice field rendered as a segmented control. The staged text is
|
|
2662
|
+
* one of `options`' values or `''` (unset → `defaultValue`); picking the
|
|
2663
|
+
* default while unset stages nothing new.
|
|
3131
2664
|
*/
|
|
3132
|
-
function
|
|
3133
|
-
const
|
|
3134
|
-
|
|
3135
|
-
|
|
3136
|
-
|
|
3137
|
-
|
|
3138
|
-
|
|
3139
|
-
|
|
3140
|
-
|
|
3141
|
-
|
|
3142
|
-
|
|
3143
|
-
|
|
3144
|
-
|
|
3145
|
-
|
|
3146
|
-
|
|
3147
|
-
|
|
3148
|
-
|
|
3149
|
-
|
|
3150
|
-
|
|
3151
|
-
|
|
3152
|
-
|
|
3153
|
-
|
|
3154
|
-
|
|
3155
|
-
|
|
3156
|
-
|
|
3157
|
-
|
|
3158
|
-
|
|
3159
|
-
|
|
3160
|
-
|
|
3161
|
-
|
|
3162
|
-
|
|
3163
|
-
|
|
3164
|
-
|
|
3165
|
-
onClick: () => setVisible((value) => !value),
|
|
3166
|
-
children: visible ? hideLabel : showLabel
|
|
3167
|
-
})
|
|
3168
|
-
]
|
|
3169
|
-
})]
|
|
3170
|
-
}),
|
|
3171
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
3172
|
-
id: "cc-api-key",
|
|
3173
|
-
className: "cc-input",
|
|
3174
|
-
type: visible ? "text" : "password",
|
|
3175
|
-
autoComplete: "off",
|
|
3176
|
-
spellCheck: false,
|
|
3177
|
-
value: state.text,
|
|
2665
|
+
function SegmentedField({ label, hint, state, disabled, options, defaultValue, className, onEdit, onReset, t }) {
|
|
2666
|
+
const current = state.text === "" ? defaultValue : state.text;
|
|
2667
|
+
const index = Math.max(0, options.findIndex((option) => option.value === current));
|
|
2668
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SettingRow, {
|
|
2669
|
+
title: label,
|
|
2670
|
+
tag: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(OverrideTag, {
|
|
2671
|
+
state,
|
|
2672
|
+
t
|
|
2673
|
+
}),
|
|
2674
|
+
description: hint,
|
|
2675
|
+
...className === void 0 ? {} : { className },
|
|
2676
|
+
control: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(FieldOverride, {
|
|
2677
|
+
label,
|
|
2678
|
+
state,
|
|
2679
|
+
disabled,
|
|
2680
|
+
t,
|
|
2681
|
+
onReset
|
|
2682
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2683
|
+
className: "cc-segmented",
|
|
2684
|
+
role: "radiogroup",
|
|
2685
|
+
"aria-label": label,
|
|
2686
|
+
style: {
|
|
2687
|
+
"--cc-segment-count": options.length,
|
|
2688
|
+
"--cc-segment-index": index
|
|
2689
|
+
},
|
|
2690
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2691
|
+
className: "cc-segmentIndicator",
|
|
2692
|
+
"aria-hidden": "true"
|
|
2693
|
+
}), options.map((option) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2694
|
+
type: "button",
|
|
2695
|
+
role: "radio",
|
|
2696
|
+
"aria-checked": current === option.value,
|
|
2697
|
+
className: "cc-segment",
|
|
3178
2698
|
disabled,
|
|
3179
|
-
|
|
3180
|
-
|
|
3181
|
-
|
|
3182
|
-
|
|
3183
|
-
|
|
3184
|
-
|
|
3185
|
-
]
|
|
2699
|
+
onClick: () => {
|
|
2700
|
+
if (current !== option.value) onEdit(option.value);
|
|
2701
|
+
},
|
|
2702
|
+
children: option.label
|
|
2703
|
+
}, option.value))]
|
|
2704
|
+
})] })
|
|
3186
2705
|
});
|
|
3187
2706
|
}
|
|
3188
2707
|
/** One stat tile in the account card's summary grid. */
|
|
@@ -3253,35 +2772,11 @@ window.__ModuleLoader__.load({
|
|
|
3253
2772
|
]
|
|
3254
2773
|
});
|
|
3255
2774
|
}
|
|
3256
|
-
/** One account's rotation state as a short badge next to its label. */
|
|
3257
|
-
function AccountMark({ entry, t }) {
|
|
3258
|
-
if (entry.active) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3259
|
-
className: "cc-usagePlan",
|
|
3260
|
-
children: t("usageActive")
|
|
3261
|
-
});
|
|
3262
|
-
if (entry.mark === "invalid-credential") return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3263
|
-
className: "cc-usagePlanStatus",
|
|
3264
|
-
children: t("usageInvalidKey")
|
|
3265
|
-
});
|
|
3266
|
-
if (entry.cooldownUntil > 0) return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
3267
|
-
className: "cc-usagePlanStatus",
|
|
3268
|
-
children: [
|
|
3269
|
-
t("usageCooldown"),
|
|
3270
|
-
" ",
|
|
3271
|
-
formatResetAt(entry.cooldownUntil)
|
|
3272
|
-
]
|
|
3273
|
-
});
|
|
3274
|
-
if (entry.mark === "rate-limit") return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3275
|
-
className: "cc-usagePlanStatus",
|
|
3276
|
-
children: t("usageCooldown")
|
|
3277
|
-
});
|
|
3278
|
-
return null;
|
|
3279
|
-
}
|
|
3280
2775
|
/**
|
|
3281
2776
|
* One pool account's facts (identity, totals, credits, window limits)
|
|
3282
|
-
* rendered inside
|
|
2777
|
+
* rendered inside an account row's details.
|
|
3283
2778
|
*/
|
|
3284
|
-
function AccountReport({ entry, fetchedAt, t,
|
|
2779
|
+
function AccountReport({ entry, fetchedAt, t, summaryOnly = false, headless = false }) {
|
|
3285
2780
|
const report = entry.report;
|
|
3286
2781
|
const account = report.account;
|
|
3287
2782
|
const accountName = account === void 0 ? "" : account.userName || account.name;
|
|
@@ -3294,17 +2789,13 @@ window.__ModuleLoader__.load({
|
|
|
3294
2789
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3295
2790
|
className: "cc-accountReport",
|
|
3296
2791
|
children: [
|
|
3297
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2792
|
+
!headless ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3298
2793
|
className: "cc-usageHead",
|
|
3299
2794
|
children: [
|
|
3300
2795
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h4", {
|
|
3301
2796
|
className: "cc-usageTitle",
|
|
3302
2797
|
children: entry.label
|
|
3303
2798
|
}),
|
|
3304
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(AccountMark, {
|
|
3305
|
-
entry,
|
|
3306
|
-
t
|
|
3307
|
-
}),
|
|
3308
2799
|
accountName !== "" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3309
2800
|
className: "cc-usageAccount",
|
|
3310
2801
|
children: accountName
|
|
@@ -3312,20 +2803,13 @@ window.__ModuleLoader__.load({
|
|
|
3312
2803
|
planName !== "" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3313
2804
|
className: "cc-usagePlan",
|
|
3314
2805
|
children: planName
|
|
3315
|
-
}) : null,
|
|
3316
|
-
planStatus !== "" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3317
|
-
className: "cc-usagePlanStatus",
|
|
3318
|
-
children: planStatus
|
|
3319
|
-
}) : null,
|
|
3320
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: "cc-usageMetaSpacer" }),
|
|
3321
|
-
onRemove !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
3322
|
-
type: "button",
|
|
3323
|
-
className: "cc-usageRefresh",
|
|
3324
|
-
onClick: onRemove,
|
|
3325
|
-
children: t("accountRemove")
|
|
3326
2806
|
}) : null
|
|
3327
2807
|
]
|
|
3328
|
-
}),
|
|
2808
|
+
}) : null,
|
|
2809
|
+
planStatus !== "" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
2810
|
+
className: "cc-usagePlanStatus",
|
|
2811
|
+
children: planStatus
|
|
2812
|
+
}) : null,
|
|
3329
2813
|
!entry.configured ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
3330
2814
|
className: "cc-usageHint",
|
|
3331
2815
|
children: t("usageUnconfigured")
|
|
@@ -3349,8 +2833,8 @@ window.__ModuleLoader__.load({
|
|
|
3349
2833
|
}) : null
|
|
3350
2834
|
]
|
|
3351
2835
|
}) : null,
|
|
3352
|
-
report.usage !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3353
|
-
className: "cc-usageStats",
|
|
2836
|
+
!summaryOnly && report.usage !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2837
|
+
className: "cc-usageStats cc-usageStatsActivity",
|
|
3354
2838
|
children: [
|
|
3355
2839
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(UsageStat, {
|
|
3356
2840
|
label: t("usageRequests"),
|
|
@@ -3373,8 +2857,8 @@ window.__ModuleLoader__.load({
|
|
|
3373
2857
|
})
|
|
3374
2858
|
]
|
|
3375
2859
|
}) : null,
|
|
3376
|
-
credits !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3377
|
-
className: "cc-usageStats",
|
|
2860
|
+
!summaryOnly && credits !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2861
|
+
className: "cc-usageStats cc-usageStatsBalance",
|
|
3378
2862
|
children: [
|
|
3379
2863
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(UsageStat, {
|
|
3380
2864
|
label: t("usageMonthly"),
|
|
@@ -3446,360 +2930,6 @@ window.__ModuleLoader__.load({
|
|
|
3446
2930
|
if (reason === "invalid-response") return t("usageInvalidResponseHint");
|
|
3447
2931
|
return t("usageNetworkHint");
|
|
3448
2932
|
}
|
|
3449
|
-
/** The status dot on an account tab: cooling/invalid warn, everything else ok. */
|
|
3450
|
-
function AccountTabDot({ entry }) {
|
|
3451
|
-
const cls = entry.mark === "invalid-credential" ? "cc-tabDot cc-tabDotError" : entry.mark !== "" || entry.cooldownUntil > 0 ? "cc-tabDot cc-tabDotWarn" : "cc-tabDot cc-tabDotOk";
|
|
3452
|
-
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3453
|
-
className: cls,
|
|
3454
|
-
"aria-hidden": "true"
|
|
3455
|
-
});
|
|
3456
|
-
}
|
|
3457
|
-
/**
|
|
3458
|
-
* The account-usage card: the `/commandcode` dashboard's facts rendered as
|
|
3459
|
-
* a native settings card. With several accounts the card is a carousel — a
|
|
3460
|
-
* tab strip (label + status dot) switches between accounts so the page stays
|
|
3461
|
-
* short; each account's report carries its own remove affordance (the
|
|
3462
|
-
* default account is not removable). Accounts staged for removal in the
|
|
3463
|
-
* management card are hidden here immediately. Data arrives through the
|
|
3464
|
-
* `commandcode/report` Remote; the API keys never leave the Host.
|
|
3465
|
-
*/
|
|
3466
|
-
function UsageCard({ t, usage, removingIds, removableIds, canManage, onRefresh, onRemoveAccount }) {
|
|
3467
|
-
const { loading, shouldRefresh, noKey } = usageCardState(usage);
|
|
3468
|
-
(0, react.useEffect)(() => {
|
|
3469
|
-
if (shouldRefresh) onRefresh();
|
|
3470
|
-
}, [shouldRefresh, onRefresh]);
|
|
3471
|
-
const report = usage.report;
|
|
3472
|
-
const [locallyRemoved, setLocallyRemoved] = (0, react.useState)([]);
|
|
3473
|
-
(0, react.useEffect)(() => {
|
|
3474
|
-
setLocallyRemoved([]);
|
|
3475
|
-
}, [usage.fetchedAt]);
|
|
3476
|
-
const hidden = /* @__PURE__ */ new Set([...removingIds, ...locallyRemoved]);
|
|
3477
|
-
const seenIds = /* @__PURE__ */ new Set();
|
|
3478
|
-
const entries = (report?.accounts ?? []).filter((entry) => {
|
|
3479
|
-
if (hidden.has(entry.id)) return false;
|
|
3480
|
-
if (seenIds.has(entry.id)) return false;
|
|
3481
|
-
seenIds.add(entry.id);
|
|
3482
|
-
return true;
|
|
3483
|
-
});
|
|
3484
|
-
const [selectedId, setSelectedId] = (0, react.useState)(void 0);
|
|
3485
|
-
const selected = entries.find((entry) => entry.id === selectedId) ?? entries.find((entry) => entry.active) ?? entries[0];
|
|
3486
|
-
const removeSelected = canManage && selected !== void 0 && removableIds.includes(selected.id) ? () => {
|
|
3487
|
-
const id = selected.id;
|
|
3488
|
-
setLocallyRemoved((prev) => [...prev, id]);
|
|
3489
|
-
onRemoveAccount(id);
|
|
3490
|
-
} : void 0;
|
|
3491
|
-
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3492
|
-
className: "cc-usageCard",
|
|
3493
|
-
"aria-label": t("usageTitle"),
|
|
3494
|
-
children: [
|
|
3495
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3496
|
-
className: "cc-usageHead",
|
|
3497
|
-
children: [
|
|
3498
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", {
|
|
3499
|
-
className: "cc-usageTitle",
|
|
3500
|
-
children: t("usageTitle")
|
|
3501
|
-
}),
|
|
3502
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: "cc-usageMetaSpacer" }),
|
|
3503
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
3504
|
-
type: "button",
|
|
3505
|
-
className: "cc-usageRefresh",
|
|
3506
|
-
disabled: loading,
|
|
3507
|
-
onClick: onRefresh,
|
|
3508
|
-
children: loading ? t("usageRefreshing") : t("usageRefresh")
|
|
3509
|
-
})
|
|
3510
|
-
]
|
|
3511
|
-
}),
|
|
3512
|
-
noKey ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
3513
|
-
className: "cc-usageHint",
|
|
3514
|
-
children: t("usageNoKey")
|
|
3515
|
-
}) : null,
|
|
3516
|
-
report === void 0 && loading ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
3517
|
-
className: "cc-usageHint",
|
|
3518
|
-
children: t("usageLoading")
|
|
3519
|
-
}) : null,
|
|
3520
|
-
usage.status === "error" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
3521
|
-
className: "cc-usageError",
|
|
3522
|
-
role: "status",
|
|
3523
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: [t("usageError"), usage.error !== void 0 && usage.error !== "" ? ` — ${usage.error}` : ""] })
|
|
3524
|
-
}) : null,
|
|
3525
|
-
entries.length > 1 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
3526
|
-
className: "cc-tabs",
|
|
3527
|
-
"aria-label": t("accountsTitle"),
|
|
3528
|
-
children: entries.map((entry) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
3529
|
-
type: "button",
|
|
3530
|
-
"aria-pressed": selected?.id === entry.id,
|
|
3531
|
-
className: selected?.id === entry.id ? "cc-tab cc-tabActive" : "cc-tab",
|
|
3532
|
-
onClick: () => setSelectedId(entry.id),
|
|
3533
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(AccountTabDot, { entry }), entry.label]
|
|
3534
|
-
}, entry.id))
|
|
3535
|
-
}) : null,
|
|
3536
|
-
selected !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(AccountReport, {
|
|
3537
|
-
entry: selected,
|
|
3538
|
-
fetchedAt: usage.fetchedAt,
|
|
3539
|
-
t,
|
|
3540
|
-
onRemove: removeSelected
|
|
3541
|
-
}, selected.id) : null
|
|
3542
|
-
]
|
|
3543
|
-
});
|
|
3544
|
-
}
|
|
3545
|
-
/**
|
|
3546
|
-
* One extra account row: label, key, configured badge. Saved accounts are
|
|
3547
|
-
* removed from the usage card above; a NOT-YET-SAVED addition never appears
|
|
3548
|
-
* there (the usage report is Host-side), so it keeps its own remove button —
|
|
3549
|
-
* otherwise the only way to undo a mistaken Add would be discarding every
|
|
3550
|
-
* other staged edit.
|
|
3551
|
-
*/
|
|
3552
|
-
function AccountRow({ account, disabled, t, onLabel, onKey, onToggleClear, onRemove }) {
|
|
3553
|
-
const locked = !account.writable;
|
|
3554
|
-
const [keyVisible, setKeyVisible] = (0, react.useState)(false);
|
|
3555
|
-
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3556
|
-
className: "cc-field",
|
|
3557
|
-
children: [
|
|
3558
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3559
|
-
className: "cc-fieldHead",
|
|
3560
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("label", {
|
|
3561
|
-
className: "cc-label",
|
|
3562
|
-
htmlFor: `cc-account-label-${account.id}`,
|
|
3563
|
-
children: t("accountLabel")
|
|
3564
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
3565
|
-
className: "cc-badges",
|
|
3566
|
-
children: [
|
|
3567
|
-
account.added ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3568
|
-
className: "cc-badge",
|
|
3569
|
-
children: t("unsaved")
|
|
3570
|
-
}) : null,
|
|
3571
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3572
|
-
className: account.configured ? "cc-badge" : "cc-badgeMuted",
|
|
3573
|
-
children: account.configured ? t("apiKeySet") : t("apiKeyUnset")
|
|
3574
|
-
}),
|
|
3575
|
-
account.clearStaged ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3576
|
-
className: "cc-badge cc-badgeWarn",
|
|
3577
|
-
children: t("usageKeyClearStaged")
|
|
3578
|
-
}) : null,
|
|
3579
|
-
account.configured && !account.clearStaged ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
3580
|
-
type: "button",
|
|
3581
|
-
className: "cc-reset",
|
|
3582
|
-
disabled,
|
|
3583
|
-
onClick: onToggleClear,
|
|
3584
|
-
children: t("usageKeyClear")
|
|
3585
|
-
}) : null,
|
|
3586
|
-
account.clearStaged ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
3587
|
-
type: "button",
|
|
3588
|
-
className: "cc-reset",
|
|
3589
|
-
disabled,
|
|
3590
|
-
onClick: onToggleClear,
|
|
3591
|
-
children: t("usageUndoKeyClear")
|
|
3592
|
-
}) : null,
|
|
3593
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
3594
|
-
type: "button",
|
|
3595
|
-
className: "cc-reset",
|
|
3596
|
-
disabled,
|
|
3597
|
-
onClick: () => setKeyVisible((value) => !value),
|
|
3598
|
-
children: keyVisible ? t("hide") : t("show")
|
|
3599
|
-
}),
|
|
3600
|
-
account.added ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
3601
|
-
type: "button",
|
|
3602
|
-
className: "cc-reset",
|
|
3603
|
-
disabled,
|
|
3604
|
-
onClick: onRemove,
|
|
3605
|
-
children: t("accountRemove")
|
|
3606
|
-
}) : null
|
|
3607
|
-
]
|
|
3608
|
-
})]
|
|
3609
|
-
}),
|
|
3610
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
3611
|
-
id: `cc-account-label-${account.id}`,
|
|
3612
|
-
className: "cc-input",
|
|
3613
|
-
type: "text",
|
|
3614
|
-
value: account.label,
|
|
3615
|
-
disabled,
|
|
3616
|
-
onChange: (event) => onLabel(event.target.value)
|
|
3617
|
-
}),
|
|
3618
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
3619
|
-
id: `cc-account-key-${account.id}`,
|
|
3620
|
-
className: "cc-input",
|
|
3621
|
-
type: keyVisible ? "text" : "password",
|
|
3622
|
-
autoComplete: "off",
|
|
3623
|
-
spellCheck: false,
|
|
3624
|
-
placeholder: t("accountKey"),
|
|
3625
|
-
value: account.keyText,
|
|
3626
|
-
disabled: disabled || locked,
|
|
3627
|
-
onChange: (event) => onKey(event.target.value)
|
|
3628
|
-
}),
|
|
3629
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
3630
|
-
className: "cc-hint",
|
|
3631
|
-
children: locked ? t("apiKeyLocked") : t("accountKeyHint")
|
|
3632
|
-
})
|
|
3633
|
-
]
|
|
3634
|
-
});
|
|
3635
|
-
}
|
|
3636
|
-
/** The multi-account card: the active-account selector + extra accounts in rotation order + add button. */
|
|
3637
|
-
function AccountsCard({ t, state, login, disabled, onAdd, onRemove, onLabel, onKey, onToggleClear, onActive, onActiveReset, onBeginLogin, onCancelLogin }) {
|
|
3638
|
-
const active = state.activeAccount;
|
|
3639
|
-
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3640
|
-
className: "cc-card",
|
|
3641
|
-
"aria-label": t("accountsTitle"),
|
|
3642
|
-
children: [
|
|
3643
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3644
|
-
className: "cc-field",
|
|
3645
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3646
|
-
className: "cc-fieldHead",
|
|
3647
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("label", {
|
|
3648
|
-
className: "cc-label",
|
|
3649
|
-
children: t("accountsTitle")
|
|
3650
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3651
|
-
className: "cc-badges",
|
|
3652
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
3653
|
-
type: "button",
|
|
3654
|
-
className: "cc-reset",
|
|
3655
|
-
disabled,
|
|
3656
|
-
onClick: onAdd,
|
|
3657
|
-
children: t("accountAdd")
|
|
3658
|
-
})
|
|
3659
|
-
})]
|
|
3660
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
3661
|
-
className: "cc-hint",
|
|
3662
|
-
children: t("accountsHint")
|
|
3663
|
-
})]
|
|
3664
|
-
}),
|
|
3665
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3666
|
-
className: "cc-field",
|
|
3667
|
-
children: [
|
|
3668
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3669
|
-
className: "cc-fieldHead",
|
|
3670
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("label", {
|
|
3671
|
-
className: "cc-label",
|
|
3672
|
-
htmlFor: "cc-active-account",
|
|
3673
|
-
children: t("activeAccount")
|
|
3674
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
3675
|
-
className: "cc-badges",
|
|
3676
|
-
children: [active.overridden ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3677
|
-
className: "cc-badge",
|
|
3678
|
-
children: t("overridden")
|
|
3679
|
-
}) : null, /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
3680
|
-
type: "button",
|
|
3681
|
-
className: "cc-reset",
|
|
3682
|
-
disabled,
|
|
3683
|
-
onClick: onActiveReset,
|
|
3684
|
-
children: t("reset")
|
|
3685
|
-
})]
|
|
3686
|
-
})]
|
|
3687
|
-
}),
|
|
3688
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
|
|
3689
|
-
id: "cc-active-account",
|
|
3690
|
-
className: "cc-input",
|
|
3691
|
-
value: active.text,
|
|
3692
|
-
disabled,
|
|
3693
|
-
onChange: (event) => onActive(event.target.value),
|
|
3694
|
-
children: [
|
|
3695
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
3696
|
-
value: "",
|
|
3697
|
-
children: t("activeAccountAuto")
|
|
3698
|
-
}),
|
|
3699
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
3700
|
-
value: "default",
|
|
3701
|
-
children: t("accountDefault")
|
|
3702
|
-
}),
|
|
3703
|
-
state.accounts.filter((account) => !account.added).map((account) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
3704
|
-
value: account.ref,
|
|
3705
|
-
children: account.label
|
|
3706
|
-
}, account.id))
|
|
3707
|
-
]
|
|
3708
|
-
}),
|
|
3709
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
3710
|
-
className: "cc-hint",
|
|
3711
|
-
children: t("activeAccountHint")
|
|
3712
|
-
})
|
|
3713
|
-
]
|
|
3714
|
-
}),
|
|
3715
|
-
state.accounts.map((account) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3716
|
-
className: "cc-accountEditor",
|
|
3717
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(AccountRow, {
|
|
3718
|
-
account,
|
|
3719
|
-
disabled: disabled || login.targetRef === account.ref && (login.phase === "starting" || login.phase === "waiting"),
|
|
3720
|
-
t,
|
|
3721
|
-
onLabel: (text) => onLabel(account.id, text),
|
|
3722
|
-
onKey: (text) => onKey(account.id, text),
|
|
3723
|
-
onToggleClear: () => onToggleClear(account.id),
|
|
3724
|
-
onRemove: () => onRemove(account.id)
|
|
3725
|
-
}), account.added ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
3726
|
-
className: "cc-accountLoginHint",
|
|
3727
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
3728
|
-
className: "cc-hint",
|
|
3729
|
-
children: t("accountLoginAfterSave")
|
|
3730
|
-
})
|
|
3731
|
-
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(LoginRow, {
|
|
3732
|
-
state: login,
|
|
3733
|
-
targetRef: account.ref,
|
|
3734
|
-
disabled: disabled || !account.writable || state.dirty,
|
|
3735
|
-
disabledHint: state.dirty ? t("loginSaveBefore") : void 0,
|
|
3736
|
-
t,
|
|
3737
|
-
onBegin: () => onBeginLogin(account.ref),
|
|
3738
|
-
onCancel: onCancelLogin
|
|
3739
|
-
})]
|
|
3740
|
-
}, account.id))
|
|
3741
|
-
]
|
|
3742
|
-
});
|
|
3743
|
-
}
|
|
3744
|
-
/** One model → account routing rule row. */
|
|
3745
|
-
function RuleRow({ rule, accounts, catalog, disabled, t, onModels, onAccount, onRemove }) {
|
|
3746
|
-
const targets = [{
|
|
3747
|
-
value: "default",
|
|
3748
|
-
label: t("accountDefault")
|
|
3749
|
-
}, ...accounts.filter((account) => !account.added).map((account) => ({
|
|
3750
|
-
value: account.ref,
|
|
3751
|
-
label: account.label
|
|
3752
|
-
}))];
|
|
3753
|
-
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3754
|
-
className: "cc-field",
|
|
3755
|
-
children: [
|
|
3756
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3757
|
-
className: "cc-fieldHead",
|
|
3758
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("label", {
|
|
3759
|
-
className: "cc-label",
|
|
3760
|
-
htmlFor: `cc-rule-model-${rule.id}`,
|
|
3761
|
-
children: t("ruleModel")
|
|
3762
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
3763
|
-
className: "cc-badges",
|
|
3764
|
-
children: [rule.added ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3765
|
-
className: "cc-badge",
|
|
3766
|
-
children: t("unsaved")
|
|
3767
|
-
}) : null, /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
3768
|
-
type: "button",
|
|
3769
|
-
className: "cc-reset",
|
|
3770
|
-
disabled,
|
|
3771
|
-
onClick: onRemove,
|
|
3772
|
-
children: t("ruleRemove")
|
|
3773
|
-
})]
|
|
3774
|
-
})]
|
|
3775
|
-
}),
|
|
3776
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(ModelMultiSelect, {
|
|
3777
|
-
id: `cc-rule-model-${rule.id}`,
|
|
3778
|
-
selected: rule.models,
|
|
3779
|
-
catalog,
|
|
3780
|
-
disabled,
|
|
3781
|
-
t,
|
|
3782
|
-
onSelect: onModels
|
|
3783
|
-
}),
|
|
3784
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("select", {
|
|
3785
|
-
id: `cc-rule-account-${rule.id}`,
|
|
3786
|
-
className: "cc-input",
|
|
3787
|
-
value: rule.account,
|
|
3788
|
-
disabled,
|
|
3789
|
-
onChange: (event) => onAccount(event.target.value),
|
|
3790
|
-
"aria-label": t("ruleAccount"),
|
|
3791
|
-
children: targets.map((target) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
3792
|
-
value: target.value,
|
|
3793
|
-
children: target.label
|
|
3794
|
-
}, target.value))
|
|
3795
|
-
}),
|
|
3796
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
3797
|
-
className: "cc-hint",
|
|
3798
|
-
children: t("ruleHint")
|
|
3799
|
-
})
|
|
3800
|
-
]
|
|
3801
|
-
});
|
|
3802
|
-
}
|
|
3803
2933
|
/**
|
|
3804
2934
|
* A checkbox multi-select dropdown for picking catalog models (the
|
|
3805
2935
|
* routing-rule rows and the visible-models filter share it). The trigger
|
|
@@ -3811,13 +2941,26 @@ window.__ModuleLoader__.load({
|
|
|
3811
2941
|
* longer carries still render — flagged stale — so a saved selection never
|
|
3812
2942
|
* silently loses an entry, and the VisibleModelsCard offers a one-click
|
|
3813
2943
|
* cleanup.
|
|
2944
|
+
*
|
|
2945
|
+
* With `deferCommit` the toggles collect in a local draft that is handed to
|
|
2946
|
+
* `onSelect` once, when the menu closes: the per-account picker writes
|
|
2947
|
+
* immediately, and one write per click would be a burst of document writes.
|
|
3814
2948
|
*/
|
|
3815
|
-
function ModelMultiSelect({ id, selected, catalog, disabled, t, onSelect }) {
|
|
2949
|
+
function ModelMultiSelect({ id, selected: committed, catalog, disabled, deferCommit = false, ownerOf, t, onSelect }) {
|
|
3816
2950
|
const [open, setOpen] = (0, react.useState)(false);
|
|
3817
2951
|
const [query, setQuery] = (0, react.useState)("");
|
|
2952
|
+
const [draft, setDraft] = (0, react.useState)(void 0);
|
|
2953
|
+
const selected = draft ?? committed;
|
|
3818
2954
|
(0, react.useEffect)(() => {
|
|
3819
2955
|
if (open) setQuery("");
|
|
3820
2956
|
}, [open]);
|
|
2957
|
+
const close = () => {
|
|
2958
|
+
setOpen(false);
|
|
2959
|
+
if (draft !== void 0) {
|
|
2960
|
+
setDraft(void 0);
|
|
2961
|
+
if (draft.length !== committed.length || draft.some((modelId) => !committed.includes(modelId))) onSelect(draft);
|
|
2962
|
+
}
|
|
2963
|
+
};
|
|
3821
2964
|
const tiers = (0, react.useMemo)(() => Object.fromEntries(catalog.flatMap((model) => model.tier === void 0 ? [] : [[model.id, model.tier]])), [catalog]);
|
|
3822
2965
|
const options = buildModelSelectOptions(catalog, selected, query);
|
|
3823
2966
|
const items = groupModelSelectOptions(options, (modelId) => tierHeadingFor(modelId, tiers)).flatMap((group) => [...group.heading === void 0 ? [] : [{
|
|
@@ -3843,15 +2986,21 @@ window.__ModuleLoader__.load({
|
|
|
3843
2986
|
option.stale ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3844
2987
|
className: "cc-badge",
|
|
3845
2988
|
children: t("modelStale")
|
|
2989
|
+
}) : null,
|
|
2990
|
+
!selected.includes(option.value) && ownerOf?.(option.value) !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2991
|
+
className: "cc-badgeMuted",
|
|
2992
|
+
children: t("accountModelOwner", { name: ownerOf(option.value) })
|
|
3846
2993
|
}) : null
|
|
3847
2994
|
]
|
|
3848
2995
|
})
|
|
3849
2996
|
}))]);
|
|
3850
2997
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Menu, {
|
|
3851
2998
|
open,
|
|
3852
|
-
onClose:
|
|
2999
|
+
onClose: close,
|
|
3853
3000
|
onSelect: (modelId) => {
|
|
3854
|
-
|
|
3001
|
+
const next = toggleModelSelection(selected, modelId);
|
|
3002
|
+
if (deferCommit) setDraft(next);
|
|
3003
|
+
else onSelect(next);
|
|
3855
3004
|
},
|
|
3856
3005
|
selectedIds: selected,
|
|
3857
3006
|
items,
|
|
@@ -3866,14 +3015,14 @@ window.__ModuleLoader__.load({
|
|
|
3866
3015
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
3867
3016
|
id,
|
|
3868
3017
|
type: "button",
|
|
3869
|
-
className: "cc-
|
|
3018
|
+
className: "cc-selector",
|
|
3870
3019
|
disabled: disabled || catalog.length === 0,
|
|
3871
|
-
onClick: () =>
|
|
3020
|
+
onClick: () => open ? close() : setOpen(true),
|
|
3872
3021
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3873
|
-
className: "cc-
|
|
3874
|
-
children: selected.length === 0 ? t("
|
|
3022
|
+
className: "cc-selectorText",
|
|
3023
|
+
children: selected.length === 0 ? t("modelPick") : t("modelCount", { count: selected.length })
|
|
3875
3024
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3876
|
-
className: "cc-
|
|
3025
|
+
className: "cc-selectorCaret",
|
|
3877
3026
|
"aria-hidden": "true"
|
|
3878
3027
|
})]
|
|
3879
3028
|
}), open ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
@@ -3889,133 +3038,1014 @@ window.__ModuleLoader__.load({
|
|
|
3889
3038
|
})
|
|
3890
3039
|
});
|
|
3891
3040
|
}
|
|
3892
|
-
/** The model
|
|
3893
|
-
function
|
|
3894
|
-
|
|
3895
|
-
|
|
3896
|
-
"
|
|
3041
|
+
/** The visible-model filter row: an allowlist over the catalog. Empty = show all. */
|
|
3042
|
+
function VisibleModelsRow({ t, state, disabled, onSelect, onClear }) {
|
|
3043
|
+
const count = state.visibleModels.length;
|
|
3044
|
+
const pickT = (key, params) => {
|
|
3045
|
+
if (key === "modelPick") return t("visibleModelsPick");
|
|
3046
|
+
if (key === "modelCount") return t("visibleModelsCount", params);
|
|
3047
|
+
return t(key, params);
|
|
3048
|
+
};
|
|
3049
|
+
const readiness = {
|
|
3050
|
+
catalogIds: state.catalogModels.map((model) => model.id),
|
|
3051
|
+
catalogFailed: state.catalogFailed
|
|
3052
|
+
};
|
|
3053
|
+
const staleIds = staleModelIds(state.visibleModels, readiness);
|
|
3054
|
+
const catalogReady = catalogIsReady(readiness);
|
|
3055
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SettingRow, {
|
|
3056
|
+
title: t("visibleModelsTitle"),
|
|
3057
|
+
titleFor: "cc-visible-models",
|
|
3058
|
+
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] }),
|
|
3059
|
+
error: state.catalogFailed ? t("modelCatalogFailed") : void 0,
|
|
3060
|
+
control: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
|
|
3061
|
+
catalogReady && staleIds.length > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
3062
|
+
type: "button",
|
|
3063
|
+
className: "cc-linkButton",
|
|
3064
|
+
disabled,
|
|
3065
|
+
onClick: () => onSelect(state.visibleModels.filter((id) => !staleIds.includes(id))),
|
|
3066
|
+
children: t("visibleModelsCleanStale", { count: staleIds.length })
|
|
3067
|
+
}) : null,
|
|
3068
|
+
count > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
3069
|
+
type: "button",
|
|
3070
|
+
className: "cc-linkButton",
|
|
3071
|
+
disabled,
|
|
3072
|
+
onClick: onClear,
|
|
3073
|
+
children: t("visibleModelsShowAll")
|
|
3074
|
+
}) : null,
|
|
3075
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(ModelMultiSelect, {
|
|
3076
|
+
id: "cc-visible-models",
|
|
3077
|
+
selected: state.visibleModels,
|
|
3078
|
+
catalog: state.catalogModels,
|
|
3079
|
+
disabled,
|
|
3080
|
+
t: pickT,
|
|
3081
|
+
onSelect
|
|
3082
|
+
})
|
|
3083
|
+
] })
|
|
3084
|
+
});
|
|
3085
|
+
}
|
|
3086
|
+
/**
|
|
3087
|
+
* One titled group of rows. No card surface: like the harness's own settings
|
|
3088
|
+
* pages, a group is a heading over hairline-separated rows.
|
|
3089
|
+
*/
|
|
3090
|
+
function SettingsGroup({ title, action, children }) {
|
|
3091
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
|
|
3092
|
+
className: "cc-group",
|
|
3093
|
+
"aria-label": title,
|
|
3094
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3095
|
+
className: "cc-groupHead",
|
|
3096
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", {
|
|
3097
|
+
className: "cc-groupTitle",
|
|
3098
|
+
children: title
|
|
3099
|
+
}), action]
|
|
3100
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
3101
|
+
className: "cc-rows",
|
|
3102
|
+
children
|
|
3103
|
+
})]
|
|
3104
|
+
});
|
|
3105
|
+
}
|
|
3106
|
+
/**
|
|
3107
|
+
* Models: the plan filter and the allowlist sit together because both decide
|
|
3108
|
+
* what the model picker lists (the allowlist applies after the plan filter).
|
|
3109
|
+
*/
|
|
3110
|
+
function ModelsCard({ state, disabled, t, onEdit, onReset, onSelect, onClear }) {
|
|
3111
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(SettingsGroup, {
|
|
3112
|
+
title: t("modelsTitle"),
|
|
3113
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(ToggleField, {
|
|
3114
|
+
id: "cc-filter-models-by-plan",
|
|
3115
|
+
label: t("filterModelsByPlan"),
|
|
3116
|
+
hint: t("filterModelsByPlanHint"),
|
|
3117
|
+
state: state.filterModelsByPlan,
|
|
3118
|
+
disabled,
|
|
3119
|
+
defaultChecked: true,
|
|
3120
|
+
onEdit: (text) => onEdit("filterModelsByPlan", text),
|
|
3121
|
+
onReset: () => onReset("filterModelsByPlan"),
|
|
3122
|
+
t
|
|
3123
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(VisibleModelsRow, {
|
|
3124
|
+
t,
|
|
3125
|
+
state,
|
|
3126
|
+
disabled,
|
|
3127
|
+
onSelect,
|
|
3128
|
+
onClear
|
|
3129
|
+
})]
|
|
3130
|
+
});
|
|
3131
|
+
}
|
|
3132
|
+
/**
|
|
3133
|
+
* Privacy & security: the two opt-ins that change where data goes. ZDR
|
|
3134
|
+
* restricts which upstream serves a request; the command guard sends command
|
|
3135
|
+
* text to a decision model with no ZDR upstream and can skip an approval the
|
|
3136
|
+
* user would otherwise have seen, so both start off.
|
|
3137
|
+
*/
|
|
3138
|
+
function PrivacyCard({ state, disabled, t, onEdit, onReset }) {
|
|
3139
|
+
const showGuardLevel = state.commandGuard.text === "true" || state.commandGuardLevel.overridden || state.commandGuardLevel.clear;
|
|
3140
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(SettingsGroup, {
|
|
3141
|
+
title: t("privacyTitle"),
|
|
3142
|
+
children: [
|
|
3143
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(ToggleField, {
|
|
3144
|
+
id: "cc-zdr",
|
|
3145
|
+
label: t("zdr"),
|
|
3146
|
+
hint: t("zdrHint"),
|
|
3147
|
+
state: state.zdr,
|
|
3148
|
+
disabled,
|
|
3149
|
+
defaultChecked: false,
|
|
3150
|
+
onEdit: (text) => onEdit("zdr", text),
|
|
3151
|
+
onReset: () => onReset("zdr"),
|
|
3152
|
+
t
|
|
3153
|
+
}),
|
|
3154
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(ToggleField, {
|
|
3155
|
+
id: "cc-command-guard",
|
|
3156
|
+
label: t("commandGuard"),
|
|
3157
|
+
hint: t("commandGuardHint"),
|
|
3158
|
+
state: state.commandGuard,
|
|
3159
|
+
disabled,
|
|
3160
|
+
defaultChecked: false,
|
|
3161
|
+
onEdit: (text) => onEdit("commandGuard", text),
|
|
3162
|
+
onReset: () => onReset("commandGuard"),
|
|
3163
|
+
t
|
|
3164
|
+
}),
|
|
3165
|
+
showGuardLevel ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SegmentedField, {
|
|
3166
|
+
className: "cc-rowNested",
|
|
3167
|
+
label: t("commandGuardLevel"),
|
|
3168
|
+
hint: t("commandGuardLevelHint"),
|
|
3169
|
+
state: state.commandGuardLevel,
|
|
3170
|
+
disabled,
|
|
3171
|
+
options: [
|
|
3172
|
+
{
|
|
3173
|
+
value: "high",
|
|
3174
|
+
label: t("commandGuardLevelHigh")
|
|
3175
|
+
},
|
|
3176
|
+
{
|
|
3177
|
+
value: "medium",
|
|
3178
|
+
label: t("commandGuardLevelMedium")
|
|
3179
|
+
},
|
|
3180
|
+
{
|
|
3181
|
+
value: "low",
|
|
3182
|
+
label: t("commandGuardLevelLow")
|
|
3183
|
+
}
|
|
3184
|
+
],
|
|
3185
|
+
defaultValue: COMMAND_GUARD_DEFAULT_LEVEL_CHOICE,
|
|
3186
|
+
onEdit: (text) => onEdit("commandGuardLevel", text),
|
|
3187
|
+
onReset: () => onReset("commandGuardLevel"),
|
|
3188
|
+
t
|
|
3189
|
+
}) : null
|
|
3190
|
+
]
|
|
3191
|
+
});
|
|
3192
|
+
}
|
|
3193
|
+
/** Integrations & display: surfaces outside chat that reuse this provider. */
|
|
3194
|
+
function IntegrationsCard({ state, disabled, t, onEdit, onReset }) {
|
|
3195
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(SettingsGroup, {
|
|
3196
|
+
title: t("integrationsTitle"),
|
|
3197
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(ToggleField, {
|
|
3198
|
+
id: "cc-web-search",
|
|
3199
|
+
label: t("webSearch"),
|
|
3200
|
+
hint: t("webSearchHint"),
|
|
3201
|
+
state: state.webSearch,
|
|
3202
|
+
disabled,
|
|
3203
|
+
defaultChecked: true,
|
|
3204
|
+
onEdit: (text) => onEdit("webSearch", text),
|
|
3205
|
+
onReset: () => onReset("webSearch"),
|
|
3206
|
+
t
|
|
3207
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ToggleField, {
|
|
3208
|
+
id: "cc-show-sidebar-quota",
|
|
3209
|
+
label: t("showSidebarQuota"),
|
|
3210
|
+
hint: t("showSidebarQuotaHint"),
|
|
3211
|
+
state: state.showSidebarQuota,
|
|
3212
|
+
disabled,
|
|
3213
|
+
defaultChecked: false,
|
|
3214
|
+
onEdit: (text) => onEdit("showSidebarQuota", text),
|
|
3215
|
+
onReset: () => onReset("showSidebarQuota"),
|
|
3216
|
+
t
|
|
3217
|
+
})]
|
|
3218
|
+
});
|
|
3219
|
+
}
|
|
3220
|
+
/**
|
|
3221
|
+
* The collapsed "Advanced" card: the API base and the network limits. Starts
|
|
3222
|
+
* collapsed on every visit; while collapsed, a badge names the customized
|
|
3223
|
+
* count and an invalid number (which blocks save) is surfaced on the header.
|
|
3224
|
+
*/
|
|
3225
|
+
function AdvancedSection({ state, disabled, t, onEdit, onReset }) {
|
|
3226
|
+
const [expanded, setExpanded] = (0, react.useState)(false);
|
|
3227
|
+
const overridden = ADVANCED_FIELDS.filter((field) => state[field].overridden).length;
|
|
3228
|
+
const invalid = ADVANCED_FIELDS.some((field) => state[field].invalid);
|
|
3229
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
|
|
3230
|
+
className: "cc-group",
|
|
3231
|
+
"aria-label": t("advancedSettings"),
|
|
3232
|
+
children: [
|
|
3233
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
3234
|
+
type: "button",
|
|
3235
|
+
className: "cc-groupHead cc-disclosure",
|
|
3236
|
+
"aria-expanded": expanded,
|
|
3237
|
+
"aria-controls": "cc-advanced-body",
|
|
3238
|
+
onClick: () => setExpanded((value) => !value),
|
|
3239
|
+
children: [
|
|
3240
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3241
|
+
className: "cc-groupTitle",
|
|
3242
|
+
children: t("advancedSettings")
|
|
3243
|
+
}),
|
|
3244
|
+
overridden > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3245
|
+
className: "cc-badge",
|
|
3246
|
+
children: overridden === 1 ? t("advancedOverriddenOne") : t("advancedOverriddenMany", { count: overridden })
|
|
3247
|
+
}) : null,
|
|
3248
|
+
!expanded && invalid ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3249
|
+
className: "cc-badge cc-badgeWarn",
|
|
3250
|
+
children: t("advancedInvalid")
|
|
3251
|
+
}) : null,
|
|
3252
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: "cc-spacer" }),
|
|
3253
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3254
|
+
className: expanded ? "cc-chevron cc-chevronUp" : "cc-chevron",
|
|
3255
|
+
"aria-hidden": "true"
|
|
3256
|
+
})
|
|
3257
|
+
]
|
|
3258
|
+
}),
|
|
3259
|
+
expanded ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3260
|
+
id: "cc-advanced-body",
|
|
3261
|
+
className: "cc-rows",
|
|
3262
|
+
children: [
|
|
3263
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
3264
|
+
className: "cc-groupDesc",
|
|
3265
|
+
children: t("advancedSettingsHint")
|
|
3266
|
+
}),
|
|
3267
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Field, {
|
|
3268
|
+
id: "cc-api-base",
|
|
3269
|
+
label: t("apiBase"),
|
|
3270
|
+
hint: t("apiBaseHint"),
|
|
3271
|
+
state: state.apiBase,
|
|
3272
|
+
disabled,
|
|
3273
|
+
wide: true,
|
|
3274
|
+
onEdit: (text) => onEdit("apiBase", text),
|
|
3275
|
+
onReset: () => onReset("apiBase"),
|
|
3276
|
+
t
|
|
3277
|
+
}),
|
|
3278
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Field, {
|
|
3279
|
+
id: "cc-request-timeout",
|
|
3280
|
+
label: t("requestTimeoutMs"),
|
|
3281
|
+
hint: t("requestTimeoutMsHint"),
|
|
3282
|
+
state: state.requestTimeoutMs,
|
|
3283
|
+
disabled,
|
|
3284
|
+
numeric: true,
|
|
3285
|
+
onEdit: (text) => onEdit("requestTimeoutMs", text),
|
|
3286
|
+
onReset: () => onReset("requestTimeoutMs"),
|
|
3287
|
+
t
|
|
3288
|
+
}),
|
|
3289
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Field, {
|
|
3290
|
+
id: "cc-stream-idle-timeout",
|
|
3291
|
+
label: t("streamIdleTimeoutMs"),
|
|
3292
|
+
hint: t("streamIdleTimeoutMsHint"),
|
|
3293
|
+
state: state.streamIdleTimeoutMs,
|
|
3294
|
+
disabled,
|
|
3295
|
+
numeric: true,
|
|
3296
|
+
onEdit: (text) => onEdit("streamIdleTimeoutMs", text),
|
|
3297
|
+
onReset: () => onReset("streamIdleTimeoutMs"),
|
|
3298
|
+
t
|
|
3299
|
+
}),
|
|
3300
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Field, {
|
|
3301
|
+
id: "cc-transport-max-retries",
|
|
3302
|
+
label: t("transportMaxRetries"),
|
|
3303
|
+
hint: t("transportMaxRetriesHint"),
|
|
3304
|
+
state: state.transportMaxRetries,
|
|
3305
|
+
disabled,
|
|
3306
|
+
numeric: true,
|
|
3307
|
+
onEdit: (text) => onEdit("transportMaxRetries", text),
|
|
3308
|
+
onReset: () => onReset("transportMaxRetries"),
|
|
3309
|
+
t
|
|
3310
|
+
})
|
|
3311
|
+
]
|
|
3312
|
+
}) : null,
|
|
3313
|
+
expanded && invalid ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
3314
|
+
className: "cc-rowError",
|
|
3315
|
+
role: "status",
|
|
3316
|
+
children: t("advancedInvalid")
|
|
3317
|
+
}) : null
|
|
3318
|
+
]
|
|
3319
|
+
});
|
|
3320
|
+
}
|
|
3321
|
+
/**
|
|
3322
|
+
* Merge the controller's accounts with the usage report. The default slot is
|
|
3323
|
+
* always first; report entries this page cannot manage (literal keys from the
|
|
3324
|
+
* composition config) still render, read-only, so the list names every
|
|
3325
|
+
* account the pool rotates through.
|
|
3326
|
+
*/
|
|
3327
|
+
function accountRows(state, usage, t) {
|
|
3328
|
+
const report = new Map((usage.report?.accounts ?? []).map((entry) => [entry.id, entry]));
|
|
3329
|
+
const defaultUsage = report.get("default");
|
|
3330
|
+
const rows = [{
|
|
3331
|
+
id: "default",
|
|
3332
|
+
label: t("accountDefault"),
|
|
3333
|
+
ref: void 0,
|
|
3334
|
+
isDefault: true,
|
|
3335
|
+
managed: true,
|
|
3336
|
+
configured: state.apiKeyConfigured || defaultUsage?.configured === true,
|
|
3337
|
+
storedKey: state.apiKeyConfigured,
|
|
3338
|
+
keyWritable: state.apiKeyWritable,
|
|
3339
|
+
usage: defaultUsage
|
|
3340
|
+
}];
|
|
3341
|
+
for (const account of state.accounts) {
|
|
3342
|
+
const entry = report.get(account.id);
|
|
3343
|
+
rows.push({
|
|
3344
|
+
id: account.id,
|
|
3345
|
+
label: account.label,
|
|
3346
|
+
ref: account.ref,
|
|
3347
|
+
isDefault: false,
|
|
3348
|
+
managed: true,
|
|
3349
|
+
configured: account.configured || entry?.configured === true,
|
|
3350
|
+
storedKey: account.configured,
|
|
3351
|
+
keyWritable: account.writable,
|
|
3352
|
+
usage: entry
|
|
3353
|
+
});
|
|
3354
|
+
}
|
|
3355
|
+
const known = new Set(rows.map((row) => row.id));
|
|
3356
|
+
for (const entry of usage.report?.accounts ?? []) {
|
|
3357
|
+
if (known.has(entry.id)) continue;
|
|
3358
|
+
known.add(entry.id);
|
|
3359
|
+
rows.push({
|
|
3360
|
+
id: entry.id,
|
|
3361
|
+
label: entry.label,
|
|
3362
|
+
ref: void 0,
|
|
3363
|
+
isDefault: false,
|
|
3364
|
+
managed: false,
|
|
3365
|
+
configured: entry.configured,
|
|
3366
|
+
storedKey: false,
|
|
3367
|
+
keyWritable: false,
|
|
3368
|
+
usage: entry
|
|
3369
|
+
});
|
|
3370
|
+
}
|
|
3371
|
+
return rows;
|
|
3372
|
+
}
|
|
3373
|
+
/** The row's status dot: error for a rejected key, warning while cooling down. */
|
|
3374
|
+
function statusDotClass(row) {
|
|
3375
|
+
const entry = row.usage;
|
|
3376
|
+
if (!row.configured || entry?.mark === "invalid-credential") return "cc-tabDot cc-tabDotError";
|
|
3377
|
+
if (entry !== void 0 && (entry.mark !== "" || entry.cooldownUntil > 0)) return "cc-tabDot cc-tabDotWarn";
|
|
3378
|
+
return "cc-tabDot cc-tabDotOk";
|
|
3379
|
+
}
|
|
3380
|
+
/** One compact quota meter for a row's summary line. */
|
|
3381
|
+
function MiniMeter({ label, limit }) {
|
|
3382
|
+
const ratio = windowRatio(limit.used, limit.cap);
|
|
3383
|
+
const percent = limit.cap > 0 ? `${Math.round(ratio * 100)}%` : formatMoney(limit.used);
|
|
3384
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
3385
|
+
className: "cc-miniMeter",
|
|
3386
|
+
title: limit.cap > 0 ? `${formatMoney(limit.used)} / ${formatMoney(limit.cap)}` : void 0,
|
|
3387
|
+
children: [
|
|
3388
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3389
|
+
className: "cc-miniMeterLabel",
|
|
3390
|
+
children: label
|
|
3391
|
+
}),
|
|
3392
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3393
|
+
className: "cc-miniMeterTrack",
|
|
3394
|
+
"aria-hidden": "true",
|
|
3395
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3396
|
+
className: limit.exceeded ? "cc-miniMeterFill cc-usageBarFillWarn" : "cc-miniMeterFill",
|
|
3397
|
+
style: { width: `${ratio * 100}%` }
|
|
3398
|
+
})
|
|
3399
|
+
}),
|
|
3400
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3401
|
+
className: "cc-miniMeterValue",
|
|
3402
|
+
children: percent
|
|
3403
|
+
})
|
|
3404
|
+
]
|
|
3405
|
+
});
|
|
3406
|
+
}
|
|
3407
|
+
/**
|
|
3408
|
+
* The monthly credit balance for a row's summary line: the billing endpoint's
|
|
3409
|
+
* `monthlyCredits` is what is LEFT, shown as the amount alone.
|
|
3410
|
+
*/
|
|
3411
|
+
function MonthlyBalance({ label, remaining }) {
|
|
3412
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
3413
|
+
className: "cc-miniMeter",
|
|
3414
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3415
|
+
className: "cc-miniMeterLabel",
|
|
3416
|
+
children: label
|
|
3417
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3418
|
+
className: "cc-miniMeterValue",
|
|
3419
|
+
children: formatMoney(Math.max(0, remaining))
|
|
3420
|
+
})]
|
|
3421
|
+
});
|
|
3422
|
+
}
|
|
3423
|
+
/** A single-input inline form (paste key, rename) with confirm/cancel. */
|
|
3424
|
+
function InlineInput({ id, label, secret, initial, placeholder, confirmLabel, disabled, t, onSubmit, onCancel }) {
|
|
3425
|
+
const [value, setValue] = (0, react.useState)(initial ?? "");
|
|
3426
|
+
const [visible, setVisible] = (0, react.useState)(false);
|
|
3427
|
+
const ready = value.trim() !== "";
|
|
3428
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("form", {
|
|
3429
|
+
className: "cc-inlineForm",
|
|
3430
|
+
onSubmit: (event) => {
|
|
3431
|
+
event.preventDefault();
|
|
3432
|
+
if (ready && !disabled) onSubmit(value);
|
|
3433
|
+
},
|
|
3434
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
3435
|
+
id,
|
|
3436
|
+
className: "cc-input",
|
|
3437
|
+
type: secret && !visible ? "password" : "text",
|
|
3438
|
+
"aria-label": label,
|
|
3439
|
+
autoComplete: "off",
|
|
3440
|
+
spellCheck: false,
|
|
3441
|
+
autoFocus: true,
|
|
3442
|
+
placeholder,
|
|
3443
|
+
value,
|
|
3444
|
+
disabled,
|
|
3445
|
+
onChange: (event) => setValue(event.target.value),
|
|
3446
|
+
onKeyDown: (event) => {
|
|
3447
|
+
if (event.key === "Escape") onCancel();
|
|
3448
|
+
}
|
|
3449
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3450
|
+
className: "cc-inlineActions",
|
|
3451
|
+
children: [
|
|
3452
|
+
secret && value !== "" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
3453
|
+
type: "button",
|
|
3454
|
+
className: "cc-reset",
|
|
3455
|
+
onClick: () => setVisible((shown) => !shown),
|
|
3456
|
+
children: visible ? t("hide") : t("show")
|
|
3457
|
+
}) : null,
|
|
3458
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: "cc-spacer" }),
|
|
3459
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
3460
|
+
variant: "ghost",
|
|
3461
|
+
size: "sm",
|
|
3462
|
+
type: "button",
|
|
3463
|
+
onClick: onCancel,
|
|
3464
|
+
children: t("cancel")
|
|
3465
|
+
}),
|
|
3466
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
3467
|
+
variant: "primary",
|
|
3468
|
+
size: "sm",
|
|
3469
|
+
type: "submit",
|
|
3470
|
+
disabled: !ready || disabled,
|
|
3471
|
+
children: confirmLabel
|
|
3472
|
+
})
|
|
3473
|
+
]
|
|
3474
|
+
})]
|
|
3475
|
+
});
|
|
3476
|
+
}
|
|
3477
|
+
/** A destructive confirmation bar shown in place of a menu action. */
|
|
3478
|
+
function ConfirmBar({ text, confirmLabel, disabled, t, onConfirm, onCancel }) {
|
|
3479
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3480
|
+
className: "cc-confirmBar",
|
|
3481
|
+
role: "alertdialog",
|
|
3482
|
+
"aria-label": text,
|
|
3483
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
3484
|
+
className: "cc-confirmText",
|
|
3485
|
+
children: text
|
|
3486
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3487
|
+
className: "cc-inlineActions",
|
|
3488
|
+
children: [
|
|
3489
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: "cc-spacer" }),
|
|
3490
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
3491
|
+
variant: "ghost",
|
|
3492
|
+
size: "sm",
|
|
3493
|
+
onClick: onCancel,
|
|
3494
|
+
children: t("cancel")
|
|
3495
|
+
}),
|
|
3496
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
3497
|
+
variant: "outline",
|
|
3498
|
+
size: "sm",
|
|
3499
|
+
className: "cc-dangerButton",
|
|
3500
|
+
disabled,
|
|
3501
|
+
onClick: onConfirm,
|
|
3502
|
+
children: confirmLabel
|
|
3503
|
+
})
|
|
3504
|
+
]
|
|
3505
|
+
})]
|
|
3506
|
+
});
|
|
3507
|
+
}
|
|
3508
|
+
/** One account row: identity, status, quota summary, actions and expandable details. */
|
|
3509
|
+
function AccountItem({ row, rows, state, usage, login, disabled, pinned, t, actions }) {
|
|
3510
|
+
const [expanded, setExpanded] = (0, react.useState)(false);
|
|
3511
|
+
const [menuOpen, setMenuOpen] = (0, react.useState)(false);
|
|
3512
|
+
const [mode, setMode] = (0, react.useState)(void 0);
|
|
3513
|
+
const entry = row.usage;
|
|
3514
|
+
const report = entry?.report;
|
|
3515
|
+
const accountName = report?.account === void 0 ? "" : report.account.userName || report.account.name;
|
|
3516
|
+
const planName = report?.plan?.name ?? "";
|
|
3517
|
+
const credits = report?.credits;
|
|
3518
|
+
const monthlyReported = credits !== void 0 && credits.monthlyReported !== false;
|
|
3519
|
+
const multi = rows.length > 1;
|
|
3520
|
+
const loginTarget = row.ref;
|
|
3521
|
+
const { visible: loginState, busyElsewhere } = loginStateForTarget(login, loginTarget);
|
|
3522
|
+
const loginBusy = loginState.phase === "starting" || loginState.phase === "waiting";
|
|
3523
|
+
const showLogin = loginState.phase !== "idle";
|
|
3524
|
+
const locked = disabled || !row.managed;
|
|
3525
|
+
const keyActions = !locked && row.keyWritable;
|
|
3526
|
+
const items = [];
|
|
3527
|
+
if (row.managed && multi) items.push(pinned ? {
|
|
3528
|
+
id: "unpin",
|
|
3529
|
+
label: t("accountActionUnpin"),
|
|
3530
|
+
disabled: locked
|
|
3531
|
+
} : {
|
|
3532
|
+
id: "pin",
|
|
3533
|
+
label: t("accountActionPin"),
|
|
3534
|
+
disabled: locked || !row.configured
|
|
3535
|
+
});
|
|
3536
|
+
if (row.managed) {
|
|
3537
|
+
if (items.length > 0) items.push({
|
|
3538
|
+
type: "separator",
|
|
3539
|
+
id: "sep-key"
|
|
3540
|
+
});
|
|
3541
|
+
items.push({
|
|
3542
|
+
id: "login",
|
|
3543
|
+
label: t("accountActionLogin"),
|
|
3544
|
+
disabled: !keyActions || loginBusy || busyElsewhere
|
|
3545
|
+
}, {
|
|
3546
|
+
id: "key",
|
|
3547
|
+
label: t("accountActionKey"),
|
|
3548
|
+
disabled: !keyActions
|
|
3549
|
+
});
|
|
3550
|
+
if (!row.isDefault) items.push({
|
|
3551
|
+
id: "rename",
|
|
3552
|
+
label: t("accountActionRename"),
|
|
3553
|
+
disabled: locked
|
|
3554
|
+
});
|
|
3555
|
+
if (row.isDefault && row.storedKey) items.push({
|
|
3556
|
+
type: "separator",
|
|
3557
|
+
id: "sep-danger"
|
|
3558
|
+
}, {
|
|
3559
|
+
id: "clearKey",
|
|
3560
|
+
label: t("accountActionClearKey"),
|
|
3561
|
+
disabled: !keyActions,
|
|
3562
|
+
danger: true
|
|
3563
|
+
});
|
|
3564
|
+
if (!row.isDefault) items.push({
|
|
3565
|
+
type: "separator",
|
|
3566
|
+
id: "sep-danger"
|
|
3567
|
+
}, {
|
|
3568
|
+
id: "remove",
|
|
3569
|
+
label: t("accountActionRemove"),
|
|
3570
|
+
disabled: locked,
|
|
3571
|
+
danger: true
|
|
3572
|
+
});
|
|
3573
|
+
}
|
|
3574
|
+
const onAction = (id) => {
|
|
3575
|
+
setMenuOpen(false);
|
|
3576
|
+
if (id === "pin") actions.setActive(row.id);
|
|
3577
|
+
else if (id === "unpin") actions.setActive("");
|
|
3578
|
+
else if (id === "login") actions.beginLogin(loginTarget);
|
|
3579
|
+
else if (id === "key" || id === "rename" || id === "remove" || id === "clearKey") setMode(id);
|
|
3580
|
+
};
|
|
3581
|
+
const models = state.accountModels[row.id] ?? [];
|
|
3582
|
+
const ownerOf = (modelId) => {
|
|
3583
|
+
for (const [account, list] of Object.entries(state.accountModels)) {
|
|
3584
|
+
if (account === row.id || !list.includes(modelId)) continue;
|
|
3585
|
+
return rows.find((candidate) => candidate.id === account)?.label ?? account;
|
|
3586
|
+
}
|
|
3587
|
+
};
|
|
3588
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3589
|
+
className: row.usage?.active ? "cc-accountItem cc-accountItemActive" : "cc-accountItem",
|
|
3590
|
+
children: [
|
|
3591
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3592
|
+
className: "cc-accountHead",
|
|
3593
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
3594
|
+
type: "button",
|
|
3595
|
+
className: "cc-accountToggle",
|
|
3596
|
+
"aria-expanded": expanded,
|
|
3597
|
+
"aria-controls": `cc-account-${row.id}-details`,
|
|
3598
|
+
onClick: () => setExpanded((value) => !value),
|
|
3599
|
+
children: [
|
|
3600
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3601
|
+
className: statusDotClass(row),
|
|
3602
|
+
"aria-hidden": "true"
|
|
3603
|
+
}),
|
|
3604
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3605
|
+
className: "cc-accountName",
|
|
3606
|
+
children: row.label
|
|
3607
|
+
}),
|
|
3608
|
+
accountName !== "" && accountName !== row.label ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3609
|
+
className: "cc-usageAccount",
|
|
3610
|
+
children: accountName
|
|
3611
|
+
}) : null,
|
|
3612
|
+
planName !== "" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3613
|
+
className: "cc-usagePlan",
|
|
3614
|
+
children: planName
|
|
3615
|
+
}) : null,
|
|
3616
|
+
entry?.active && multi ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3617
|
+
className: "cc-badge",
|
|
3618
|
+
children: t("usageActive")
|
|
3619
|
+
}) : null,
|
|
3620
|
+
pinned ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3621
|
+
className: "cc-badge",
|
|
3622
|
+
children: t("accountStatusPinned")
|
|
3623
|
+
}) : null,
|
|
3624
|
+
!row.configured ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3625
|
+
className: "cc-badgeMuted",
|
|
3626
|
+
children: t("apiKeyUnset")
|
|
3627
|
+
}) : null,
|
|
3628
|
+
entry?.mark === "invalid-credential" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3629
|
+
className: "cc-usagePlanStatus",
|
|
3630
|
+
children: t("usageInvalidKey")
|
|
3631
|
+
}) : null,
|
|
3632
|
+
entry !== void 0 && entry.mark !== "invalid-credential" && (entry.cooldownUntil > 0 || entry.mark === "rate-limit") ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
3633
|
+
className: "cc-usagePlanStatus",
|
|
3634
|
+
children: [t("usageCooldown"), entry.cooldownUntil > 0 ? ` ${formatResetAt(entry.cooldownUntil)}` : ""]
|
|
3635
|
+
}) : null,
|
|
3636
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: "cc-spacer" }),
|
|
3637
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3638
|
+
className: expanded ? "cc-chevron cc-chevronUp" : "cc-chevron",
|
|
3639
|
+
"aria-hidden": "true"
|
|
3640
|
+
})
|
|
3641
|
+
]
|
|
3642
|
+
}), items.length > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Menu, {
|
|
3643
|
+
open: menuOpen,
|
|
3644
|
+
onClose: () => setMenuOpen(false),
|
|
3645
|
+
onSelect: onAction,
|
|
3646
|
+
items,
|
|
3647
|
+
align: "end",
|
|
3648
|
+
portal: true,
|
|
3649
|
+
anchor: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
3650
|
+
type: "button",
|
|
3651
|
+
className: "cc-iconButton",
|
|
3652
|
+
"aria-label": `${row.label} — ${t("accountActions")}`,
|
|
3653
|
+
title: t("accountActions"),
|
|
3654
|
+
"aria-haspopup": "menu",
|
|
3655
|
+
"aria-expanded": menuOpen,
|
|
3656
|
+
onClick: () => setMenuOpen((value) => !value),
|
|
3657
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3658
|
+
className: "cc-kebab",
|
|
3659
|
+
"aria-hidden": "true"
|
|
3660
|
+
})
|
|
3661
|
+
})
|
|
3662
|
+
}) : null]
|
|
3663
|
+
}),
|
|
3664
|
+
credits !== void 0 && (credits.fiveHour !== void 0 || credits.weekly !== void 0 || monthlyReported) ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3665
|
+
className: "cc-accountMeters",
|
|
3666
|
+
children: [
|
|
3667
|
+
credits.fiveHour !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(MiniMeter, {
|
|
3668
|
+
label: t("usageFiveHour"),
|
|
3669
|
+
limit: credits.fiveHour
|
|
3670
|
+
}) : null,
|
|
3671
|
+
credits.weekly !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(MiniMeter, {
|
|
3672
|
+
label: t("usageWeekly"),
|
|
3673
|
+
limit: credits.weekly
|
|
3674
|
+
}) : null,
|
|
3675
|
+
monthlyReported ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(MonthlyBalance, {
|
|
3676
|
+
label: t("usageMonthlyLeft"),
|
|
3677
|
+
remaining: credits.monthlyCredits
|
|
3678
|
+
}) : null
|
|
3679
|
+
]
|
|
3680
|
+
}) : null,
|
|
3681
|
+
!row.configured && row.managed && mode === void 0 && !showLogin ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3682
|
+
className: "cc-accountSetup",
|
|
3683
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
3684
|
+
className: "cc-hint",
|
|
3685
|
+
children: row.keyWritable ? t("accountNoKeyHint") : t("apiKeyLocked")
|
|
3686
|
+
}), keyActions ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3687
|
+
className: "cc-inlineActions",
|
|
3688
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
3689
|
+
variant: "primary",
|
|
3690
|
+
size: "sm",
|
|
3691
|
+
disabled: loginBusy || busyElsewhere,
|
|
3692
|
+
onClick: () => actions.beginLogin(loginTarget),
|
|
3693
|
+
children: t("accountAddLogin")
|
|
3694
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
3695
|
+
variant: "ghost",
|
|
3696
|
+
size: "sm",
|
|
3697
|
+
onClick: () => setMode("key"),
|
|
3698
|
+
children: t("accountAddPaste")
|
|
3699
|
+
})]
|
|
3700
|
+
}) : null]
|
|
3701
|
+
}) : null,
|
|
3702
|
+
showLogin ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(LoginStatus, {
|
|
3703
|
+
state: loginState,
|
|
3704
|
+
t,
|
|
3705
|
+
onCancel: actions.cancelLogin
|
|
3706
|
+
}) : null,
|
|
3707
|
+
mode === "key" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(InlineInput, {
|
|
3708
|
+
id: `cc-account-${row.id}-key`,
|
|
3709
|
+
label: t("accountKeyPlaceholder"),
|
|
3710
|
+
secret: true,
|
|
3711
|
+
placeholder: t("accountKeyPlaceholder"),
|
|
3712
|
+
confirmLabel: t("accountApply"),
|
|
3713
|
+
disabled: disabled || state.accountBusy,
|
|
3714
|
+
t,
|
|
3715
|
+
onCancel: () => setMode(void 0),
|
|
3716
|
+
onSubmit: (value) => {
|
|
3717
|
+
actions.setKey(row.id, value).then((ok) => {
|
|
3718
|
+
if (ok) setMode(void 0);
|
|
3719
|
+
});
|
|
3720
|
+
}
|
|
3721
|
+
}) : null,
|
|
3722
|
+
mode === "rename" && row.ref !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(InlineInput, {
|
|
3723
|
+
id: `cc-account-${row.id}-rename`,
|
|
3724
|
+
label: t("accountActionRename"),
|
|
3725
|
+
initial: row.label,
|
|
3726
|
+
confirmLabel: t("accountApply"),
|
|
3727
|
+
disabled: disabled || state.accountBusy,
|
|
3728
|
+
t,
|
|
3729
|
+
onCancel: () => setMode(void 0),
|
|
3730
|
+
onSubmit: (value) => {
|
|
3731
|
+
actions.rename(row.ref, value).then((ok) => {
|
|
3732
|
+
if (ok) setMode(void 0);
|
|
3733
|
+
});
|
|
3734
|
+
}
|
|
3735
|
+
}) : null,
|
|
3736
|
+
mode === "remove" && row.ref !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ConfirmBar, {
|
|
3737
|
+
text: t("accountRemoveConfirm", { name: row.label }),
|
|
3738
|
+
confirmLabel: t("accountConfirmRemove"),
|
|
3739
|
+
disabled: disabled || state.accountBusy,
|
|
3740
|
+
t,
|
|
3741
|
+
onCancel: () => setMode(void 0),
|
|
3742
|
+
onConfirm: () => {
|
|
3743
|
+
actions.remove(row.ref).then((ok) => {
|
|
3744
|
+
if (ok) setMode(void 0);
|
|
3745
|
+
});
|
|
3746
|
+
}
|
|
3747
|
+
}) : null,
|
|
3748
|
+
mode === "clearKey" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ConfirmBar, {
|
|
3749
|
+
text: t("accountClearKeyConfirm"),
|
|
3750
|
+
confirmLabel: t("accountConfirmClear"),
|
|
3751
|
+
disabled: disabled || state.accountBusy,
|
|
3752
|
+
t,
|
|
3753
|
+
onCancel: () => setMode(void 0),
|
|
3754
|
+
onConfirm: () => {
|
|
3755
|
+
actions.clearKey(row.id).then((ok) => {
|
|
3756
|
+
if (ok) setMode(void 0);
|
|
3757
|
+
});
|
|
3758
|
+
}
|
|
3759
|
+
}) : null,
|
|
3760
|
+
expanded ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3761
|
+
id: `cc-account-${row.id}-details`,
|
|
3762
|
+
className: "cc-accountDetails",
|
|
3763
|
+
children: [entry !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(AccountReport, {
|
|
3764
|
+
entry,
|
|
3765
|
+
fetchedAt: usage.fetchedAt,
|
|
3766
|
+
t,
|
|
3767
|
+
headless: true
|
|
3768
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
3769
|
+
className: "cc-usageHint",
|
|
3770
|
+
children: usage.status === "loading" ? t("usageLoading") : t("usageUnconfigured")
|
|
3771
|
+
}), multi && row.managed ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SettingRow, {
|
|
3772
|
+
className: "cc-rowFlush",
|
|
3773
|
+
title: t("accountModels"),
|
|
3774
|
+
titleFor: `cc-account-${row.id}-models`,
|
|
3775
|
+
description: t("accountModelsHint"),
|
|
3776
|
+
error: state.catalogFailed ? t("modelCatalogFailed") : void 0,
|
|
3777
|
+
control: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ModelMultiSelect, {
|
|
3778
|
+
id: `cc-account-${row.id}-models`,
|
|
3779
|
+
selected: models,
|
|
3780
|
+
catalog: state.catalogModels,
|
|
3781
|
+
disabled: disabled || state.accountBusy,
|
|
3782
|
+
deferCommit: true,
|
|
3783
|
+
ownerOf,
|
|
3784
|
+
t,
|
|
3785
|
+
onSelect: (ids) => void actions.setModels(row.id, ids)
|
|
3786
|
+
})
|
|
3787
|
+
}) : null]
|
|
3788
|
+
}) : null
|
|
3789
|
+
]
|
|
3790
|
+
});
|
|
3791
|
+
}
|
|
3792
|
+
/** The live status of one browser sign-in: link while waiting, outcome after. */
|
|
3793
|
+
function LoginStatus({ state, t, onCancel }) {
|
|
3794
|
+
const hint = loginHint(state, t);
|
|
3795
|
+
const busy = state.phase === "starting" || state.phase === "waiting";
|
|
3796
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3797
|
+
className: "cc-loginStatus",
|
|
3798
|
+
role: "status",
|
|
3799
|
+
children: [
|
|
3800
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
3801
|
+
className: hint.className,
|
|
3802
|
+
title: hint.title,
|
|
3803
|
+
children: hint.text
|
|
3804
|
+
}),
|
|
3805
|
+
state.authUrl !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("a", {
|
|
3806
|
+
className: "cc-loginLink",
|
|
3807
|
+
href: state.authUrl,
|
|
3808
|
+
target: "_blank",
|
|
3809
|
+
rel: "noreferrer",
|
|
3810
|
+
children: t("loginOpenLink")
|
|
3811
|
+
}) : null,
|
|
3812
|
+
busy ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
3813
|
+
type: "button",
|
|
3814
|
+
className: "cc-reset",
|
|
3815
|
+
onClick: onCancel,
|
|
3816
|
+
children: t("loginCancel")
|
|
3817
|
+
}) : null
|
|
3818
|
+
]
|
|
3819
|
+
});
|
|
3820
|
+
}
|
|
3821
|
+
/**
|
|
3822
|
+
* The add-account panel. Both paths store the account at once: pasting a key
|
|
3823
|
+
* writes key then row; browser sign-in stores a keyless row first (the Host
|
|
3824
|
+
* only signs in to a reference the stored list names) and drops it again if
|
|
3825
|
+
* the sign-in does not complete, so a failed attempt leaves no empty account.
|
|
3826
|
+
*/
|
|
3827
|
+
function AddAccountPanel({ state, login, disabled, t, actions, pendingRef, setPendingRef, onClose }) {
|
|
3828
|
+
const [label, setLabel] = (0, react.useState)("");
|
|
3829
|
+
const [pasting, setPasting] = (0, react.useState)(false);
|
|
3830
|
+
const [loginFailure, setLoginFailure] = (0, react.useState)(void 0);
|
|
3831
|
+
const fallbackName = t("accountNameN", { n: state.accounts.length + 2 });
|
|
3832
|
+
const pending = pendingRef === void 0 ? void 0 : loginStateForTarget(login, pendingRef).visible;
|
|
3833
|
+
const busy = state.accountBusy || pending?.phase === "starting" || pending?.phase === "waiting";
|
|
3834
|
+
const loginBusyElsewhere = login.phase === "starting" || login.phase === "waiting";
|
|
3835
|
+
(0, react.useEffect)(() => {
|
|
3836
|
+
if (pendingRef === void 0 || login.targetRef !== pendingRef) return;
|
|
3837
|
+
if (login.phase === "success") {
|
|
3838
|
+
const named = label.trim() === "" && login.userName !== void 0 && login.userName !== "";
|
|
3839
|
+
setPendingRef(void 0);
|
|
3840
|
+
(named ? actions.rename(pendingRef, login.userName) : Promise.resolve(true)).then(onClose);
|
|
3841
|
+
return;
|
|
3842
|
+
}
|
|
3843
|
+
if (login.phase === "failed" || login.phase === "unavailable") {
|
|
3844
|
+
setLoginFailure(loginHint(login, t).text);
|
|
3845
|
+
setPendingRef(void 0);
|
|
3846
|
+
actions.remove(pendingRef);
|
|
3847
|
+
}
|
|
3848
|
+
}, [
|
|
3849
|
+
login,
|
|
3850
|
+
pendingRef,
|
|
3851
|
+
setPendingRef,
|
|
3852
|
+
label,
|
|
3853
|
+
actions,
|
|
3854
|
+
onClose,
|
|
3855
|
+
t
|
|
3856
|
+
]);
|
|
3857
|
+
const startLogin = async () => {
|
|
3858
|
+
setLoginFailure(void 0);
|
|
3859
|
+
const ref = await actions.create({ label: label.trim() === "" ? fallbackName : label });
|
|
3860
|
+
if (ref === void 0) return;
|
|
3861
|
+
setPendingRef(ref);
|
|
3862
|
+
actions.beginLogin(ref);
|
|
3863
|
+
};
|
|
3864
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3865
|
+
className: "cc-addPanel",
|
|
3866
|
+
"aria-label": t("accountAdd"),
|
|
3867
|
+
children: [
|
|
3868
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3869
|
+
className: "cc-panelTitle",
|
|
3870
|
+
children: t("accountAdd")
|
|
3871
|
+
}),
|
|
3872
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
3873
|
+
className: "cc-input",
|
|
3874
|
+
type: "text",
|
|
3875
|
+
"aria-label": t("accountNamePlaceholder"),
|
|
3876
|
+
placeholder: t("accountNamePlaceholder"),
|
|
3877
|
+
value: label,
|
|
3878
|
+
disabled: disabled || busy,
|
|
3879
|
+
onChange: (event) => setLabel(event.target.value)
|
|
3880
|
+
}),
|
|
3881
|
+
pending !== void 0 && pending.phase !== "idle" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(LoginStatus, {
|
|
3882
|
+
state: pending,
|
|
3883
|
+
t,
|
|
3884
|
+
onCancel: actions.cancelLogin
|
|
3885
|
+
}) : null,
|
|
3886
|
+
loginFailure !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
3887
|
+
className: "cc-loginError",
|
|
3888
|
+
children: loginFailure
|
|
3889
|
+
}) : null,
|
|
3890
|
+
pasting ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(InlineInput, {
|
|
3891
|
+
id: "cc-add-account-key",
|
|
3892
|
+
label: t("accountKeyPlaceholder"),
|
|
3893
|
+
secret: true,
|
|
3894
|
+
placeholder: t("accountKeyPlaceholder"),
|
|
3895
|
+
confirmLabel: t("accountAddConfirm"),
|
|
3896
|
+
disabled: disabled || busy,
|
|
3897
|
+
t,
|
|
3898
|
+
onCancel: () => setPasting(false),
|
|
3899
|
+
onSubmit: (key) => {
|
|
3900
|
+
actions.create({
|
|
3901
|
+
label: label.trim() === "" ? fallbackName : label,
|
|
3902
|
+
key
|
|
3903
|
+
}).then((ref) => {
|
|
3904
|
+
if (ref !== void 0) onClose();
|
|
3905
|
+
});
|
|
3906
|
+
}
|
|
3907
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
3908
|
+
className: "cc-hint",
|
|
3909
|
+
children: t("accountAddHint")
|
|
3910
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3911
|
+
className: "cc-inlineActions",
|
|
3912
|
+
children: [
|
|
3913
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
3914
|
+
variant: "primary",
|
|
3915
|
+
size: "sm",
|
|
3916
|
+
disabled: disabled || busy || loginBusyElsewhere,
|
|
3917
|
+
onClick: () => void startLogin(),
|
|
3918
|
+
children: t("accountAddLogin")
|
|
3919
|
+
}),
|
|
3920
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
3921
|
+
variant: "ghost",
|
|
3922
|
+
size: "sm",
|
|
3923
|
+
disabled: disabled || busy,
|
|
3924
|
+
onClick: () => setPasting(true),
|
|
3925
|
+
children: t("accountAddPaste")
|
|
3926
|
+
}),
|
|
3927
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: "cc-spacer" }),
|
|
3928
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
3929
|
+
variant: "ghost",
|
|
3930
|
+
size: "sm",
|
|
3931
|
+
disabled: busy,
|
|
3932
|
+
onClick: onClose,
|
|
3933
|
+
children: t("cancel")
|
|
3934
|
+
})
|
|
3935
|
+
]
|
|
3936
|
+
})] })
|
|
3937
|
+
]
|
|
3938
|
+
});
|
|
3939
|
+
}
|
|
3940
|
+
/**
|
|
3941
|
+
* The unified account card: every account the pool rotates through, one row
|
|
3942
|
+
* each, with its status and quota inline. Replaces the former split between
|
|
3943
|
+
* a credentials card, a usage card and a routing-rules card, so one account's
|
|
3944
|
+
* facts no longer have to be cross-read across three surfaces.
|
|
3945
|
+
*/
|
|
3946
|
+
function AccountsCard({ t, state, usage, login, disabled, actions, onRefresh }) {
|
|
3947
|
+
const [adding, setAdding] = (0, react.useState)(false);
|
|
3948
|
+
const [pendingRef, setPendingRef] = (0, react.useState)(void 0);
|
|
3949
|
+
const closeAdd = (0, react.useCallback)(() => setAdding(false), []);
|
|
3950
|
+
const { loading, shouldRefresh } = usageCardState(usage);
|
|
3951
|
+
(0, react.useEffect)(() => {
|
|
3952
|
+
if (shouldRefresh) onRefresh();
|
|
3953
|
+
}, [shouldRefresh, onRefresh]);
|
|
3954
|
+
const rows = accountRows(state, usage, t).filter((row) => row.ref === void 0 || row.ref !== pendingRef);
|
|
3955
|
+
const multi = rows.length > 1;
|
|
3956
|
+
const pinnedRow = rows.find((row) => row.id === state.activeAccount);
|
|
3957
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
|
|
3958
|
+
className: "cc-group",
|
|
3959
|
+
"aria-label": t("accountsTitle"),
|
|
3897
3960
|
children: [
|
|
3898
3961
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3899
|
-
className: "cc-
|
|
3962
|
+
className: "cc-groupHead",
|
|
3900
3963
|
children: [
|
|
3901
|
-
/* @__PURE__ */ (0, react_jsx_runtime.
|
|
3902
|
-
className: "cc-
|
|
3903
|
-
children:
|
|
3904
|
-
className: "cc-label",
|
|
3905
|
-
children: t("rulesTitle")
|
|
3906
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3907
|
-
className: "cc-badges",
|
|
3908
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
3909
|
-
type: "button",
|
|
3910
|
-
className: "cc-reset",
|
|
3911
|
-
disabled,
|
|
3912
|
-
onClick: onAdd,
|
|
3913
|
-
children: t("ruleAdd")
|
|
3914
|
-
})
|
|
3915
|
-
})]
|
|
3916
|
-
}),
|
|
3917
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
3918
|
-
className: "cc-hint",
|
|
3919
|
-
children: t("rulesHint")
|
|
3964
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", {
|
|
3965
|
+
className: "cc-groupTitle",
|
|
3966
|
+
children: t("accountsTitle")
|
|
3920
3967
|
}),
|
|
3921
|
-
|
|
3922
|
-
|
|
3923
|
-
|
|
3924
|
-
|
|
3968
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: "cc-spacer" }),
|
|
3969
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
3970
|
+
type: "button",
|
|
3971
|
+
className: "cc-linkButton",
|
|
3972
|
+
disabled: loading,
|
|
3973
|
+
onClick: onRefresh,
|
|
3974
|
+
children: loading ? t("usageRefreshing") : t("usageRefresh")
|
|
3975
|
+
})
|
|
3925
3976
|
]
|
|
3926
3977
|
}),
|
|
3927
|
-
|
|
3928
|
-
|
|
3929
|
-
|
|
3930
|
-
|
|
3978
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
3979
|
+
className: "cc-groupDesc",
|
|
3980
|
+
children: multi ? t("accountsRotationHint") : t("accountsHint")
|
|
3981
|
+
}),
|
|
3982
|
+
multi ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3983
|
+
className: "cc-accountMode",
|
|
3984
|
+
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", {
|
|
3985
|
+
type: "button",
|
|
3986
|
+
className: "cc-reset",
|
|
3987
|
+
disabled: disabled || state.accountBusy,
|
|
3988
|
+
onClick: () => void actions.setActive(""),
|
|
3989
|
+
children: t("accountActionUnpin")
|
|
3990
|
+
}) : null]
|
|
3991
|
+
}) : null,
|
|
3992
|
+
usage.status === "error" ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("p", {
|
|
3993
|
+
className: "cc-rowError",
|
|
3994
|
+
role: "status",
|
|
3995
|
+
children: [t("usageError"), usage.error !== void 0 && usage.error !== "" ? ` — ${usage.error}` : ""]
|
|
3996
|
+
}) : null,
|
|
3997
|
+
state.accountFailed !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
3998
|
+
className: "cc-rowError",
|
|
3999
|
+
role: "status",
|
|
4000
|
+
children: t("accountOpFailed")
|
|
4001
|
+
}) : null,
|
|
4002
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
4003
|
+
className: "cc-accountList",
|
|
4004
|
+
children: rows.map((row) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(AccountItem, {
|
|
4005
|
+
row,
|
|
4006
|
+
rows,
|
|
4007
|
+
state,
|
|
4008
|
+
usage,
|
|
4009
|
+
login,
|
|
4010
|
+
disabled,
|
|
4011
|
+
pinned: state.activeAccount === row.id,
|
|
4012
|
+
t,
|
|
4013
|
+
actions
|
|
4014
|
+
}, row.id))
|
|
4015
|
+
}),
|
|
4016
|
+
adding ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(AddAccountPanel, {
|
|
4017
|
+
state,
|
|
4018
|
+
login,
|
|
3931
4019
|
disabled,
|
|
3932
4020
|
t,
|
|
3933
|
-
|
|
3934
|
-
|
|
3935
|
-
|
|
3936
|
-
|
|
3937
|
-
|
|
3938
|
-
|
|
3939
|
-
|
|
3940
|
-
|
|
4021
|
+
actions,
|
|
4022
|
+
pendingRef,
|
|
4023
|
+
setPendingRef,
|
|
4024
|
+
onClose: closeAdd
|
|
4025
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
4026
|
+
type: "button",
|
|
4027
|
+
className: "cc-addButton",
|
|
4028
|
+
disabled,
|
|
4029
|
+
onClick: () => setAdding(true),
|
|
4030
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
4031
|
+
className: "cc-addGlyph",
|
|
4032
|
+
"aria-hidden": "true"
|
|
4033
|
+
}), t("accountAdd")]
|
|
4034
|
+
})
|
|
3941
4035
|
]
|
|
3942
4036
|
});
|
|
3943
4037
|
}
|
|
3944
|
-
/** The visible-model filter card: an allowlist over the catalog. Empty = show all. */
|
|
3945
|
-
function VisibleModelsCard({ t, state, disabled, onSelect, onClear }) {
|
|
3946
|
-
const count = state.visibleModels.length;
|
|
3947
|
-
const pickT = (key, params) => {
|
|
3948
|
-
if (key === "ruleModelPick") return t("visibleModelsPick");
|
|
3949
|
-
if (key === "ruleModelCount") return t("visibleModelsCount", params);
|
|
3950
|
-
return t(key, params);
|
|
3951
|
-
};
|
|
3952
|
-
const readiness = {
|
|
3953
|
-
catalogIds: state.catalogModels.map((model) => model.id),
|
|
3954
|
-
catalogFailed: state.catalogFailed
|
|
3955
|
-
};
|
|
3956
|
-
const staleIds = staleModelIds(state.visibleModels, readiness);
|
|
3957
|
-
const catalogReady = catalogIsReady(readiness);
|
|
3958
|
-
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
3959
|
-
className: "cc-card",
|
|
3960
|
-
"aria-label": t("visibleModelsTitle"),
|
|
3961
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3962
|
-
className: "cc-field",
|
|
3963
|
-
children: [
|
|
3964
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3965
|
-
className: "cc-fieldHead",
|
|
3966
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("label", {
|
|
3967
|
-
className: "cc-label",
|
|
3968
|
-
children: t("visibleModelsTitle")
|
|
3969
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
3970
|
-
className: "cc-badges",
|
|
3971
|
-
children: [catalogReady && staleIds.length > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
3972
|
-
type: "button",
|
|
3973
|
-
className: "cc-reset",
|
|
3974
|
-
disabled,
|
|
3975
|
-
onClick: () => onSelect(state.visibleModels.filter((id) => !staleIds.includes(id))),
|
|
3976
|
-
children: t("visibleModelsCleanStale", { count: staleIds.length })
|
|
3977
|
-
}) : null, count > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
3978
|
-
type: "button",
|
|
3979
|
-
className: "cc-reset",
|
|
3980
|
-
disabled,
|
|
3981
|
-
onClick: onClear,
|
|
3982
|
-
children: t("visibleModelsShowAll")
|
|
3983
|
-
}) : null]
|
|
3984
|
-
})]
|
|
3985
|
-
}),
|
|
3986
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
3987
|
-
className: "cc-hint",
|
|
3988
|
-
children: t("visibleModelsHint")
|
|
3989
|
-
}),
|
|
3990
|
-
state.catalogFailed ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
3991
|
-
className: "cc-invalid",
|
|
3992
|
-
children: t("rulesCatalogFailed")
|
|
3993
|
-
}) : null,
|
|
3994
|
-
staleIds.length > 0 && catalogReady ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
3995
|
-
className: "cc-hint",
|
|
3996
|
-
children: t("visibleModelsStaleHint", { count: staleIds.length })
|
|
3997
|
-
}) : null,
|
|
3998
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(ModelMultiSelect, {
|
|
3999
|
-
id: "cc-visible-models",
|
|
4000
|
-
selected: state.visibleModels,
|
|
4001
|
-
catalog: state.catalogModels,
|
|
4002
|
-
disabled,
|
|
4003
|
-
t: pickT,
|
|
4004
|
-
onSelect
|
|
4005
|
-
})
|
|
4006
|
-
]
|
|
4007
|
-
})
|
|
4008
|
-
});
|
|
4009
|
-
}
|
|
4010
4038
|
/**
|
|
4011
|
-
* Show the
|
|
4039
|
+
* Show the save bar's "saved" confirmation for a short window after each accepted save.
|
|
4012
4040
|
* The controller only counts saves (`savedCount`); the flash timing lives
|
|
4013
4041
|
* here so the state machine stays timer-free.
|
|
4014
4042
|
*/
|
|
4015
4043
|
function useSavedFlash(tick) {
|
|
4016
4044
|
const [visible, setVisible] = (0, react.useState)(false);
|
|
4045
|
+
const seen = (0, react.useRef)(tick);
|
|
4017
4046
|
(0, react.useEffect)(() => {
|
|
4018
|
-
if (tick ===
|
|
4047
|
+
if (tick === seen.current) return;
|
|
4048
|
+
seen.current = tick;
|
|
4019
4049
|
setVisible(true);
|
|
4020
4050
|
const timer = setTimeout(() => setVisible(false), 2500);
|
|
4021
4051
|
return () => clearTimeout(timer);
|
|
@@ -4045,29 +4075,157 @@ window.__ModuleLoader__.load({
|
|
|
4045
4075
|
}, []);
|
|
4046
4076
|
return available;
|
|
4047
4077
|
}
|
|
4048
|
-
/**
|
|
4078
|
+
/**
|
|
4079
|
+
* What the save bar says. It exists only while there is something to act on
|
|
4080
|
+
* or to confirm: pending edits, a save in flight, a failure, or the brief
|
|
4081
|
+
* confirmation after a save landed. Account operations never reach it, they
|
|
4082
|
+
* commit on their own.
|
|
4083
|
+
*/
|
|
4084
|
+
function saveBarView(state, savedVisible, t) {
|
|
4085
|
+
if (state.failed) return {
|
|
4086
|
+
visible: true,
|
|
4087
|
+
tone: "error",
|
|
4088
|
+
message: t("saveFailed"),
|
|
4089
|
+
actions: state.dirty
|
|
4090
|
+
};
|
|
4091
|
+
if (state.dirty || state.saving) return state.invalid ? {
|
|
4092
|
+
visible: true,
|
|
4093
|
+
tone: "error",
|
|
4094
|
+
message: t("saveInvalid"),
|
|
4095
|
+
actions: true
|
|
4096
|
+
} : {
|
|
4097
|
+
visible: true,
|
|
4098
|
+
tone: "pending",
|
|
4099
|
+
message: t("unsavedChanges"),
|
|
4100
|
+
actions: true
|
|
4101
|
+
};
|
|
4102
|
+
if (savedVisible) return {
|
|
4103
|
+
visible: true,
|
|
4104
|
+
tone: "success",
|
|
4105
|
+
message: t("saved"),
|
|
4106
|
+
actions: false
|
|
4107
|
+
};
|
|
4108
|
+
return {
|
|
4109
|
+
visible: false,
|
|
4110
|
+
tone: "success",
|
|
4111
|
+
message: "",
|
|
4112
|
+
actions: false
|
|
4113
|
+
};
|
|
4114
|
+
}
|
|
4115
|
+
/** The save bar's leading status mark: a soft dot while pending, a glyph once there is an outcome. */
|
|
4116
|
+
function SaveBarIcon({ tone }) {
|
|
4117
|
+
if (tone === "pending") return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
4118
|
+
className: "cc-saveBarIcon",
|
|
4119
|
+
"aria-hidden": "true",
|
|
4120
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: "cc-saveBarPulse" })
|
|
4121
|
+
});
|
|
4122
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
4123
|
+
className: "cc-saveBarIcon",
|
|
4124
|
+
"aria-hidden": "true",
|
|
4125
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("svg", {
|
|
4126
|
+
viewBox: "0 0 16 16",
|
|
4127
|
+
width: "16",
|
|
4128
|
+
height: "16",
|
|
4129
|
+
fill: "none",
|
|
4130
|
+
stroke: "currentColor",
|
|
4131
|
+
strokeWidth: "1.6",
|
|
4132
|
+
strokeLinecap: "round",
|
|
4133
|
+
strokeLinejoin: "round",
|
|
4134
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("circle", {
|
|
4135
|
+
cx: "8",
|
|
4136
|
+
cy: "8",
|
|
4137
|
+
r: "6.5"
|
|
4138
|
+
}), 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" })]
|
|
4139
|
+
})
|
|
4140
|
+
});
|
|
4141
|
+
}
|
|
4142
|
+
/**
|
|
4143
|
+
* The floating save bar: pinned to the bottom of the settings scrollport and
|
|
4144
|
+
* slid out of view when there is nothing to save. It stays mounted so it can
|
|
4145
|
+
* animate out, and keeps showing its last message while it does (the state
|
|
4146
|
+
* that hid it has no message of its own).
|
|
4147
|
+
*/
|
|
4148
|
+
function SaveBar({ view, state, t, onDiscard, onSave }) {
|
|
4149
|
+
const last = (0, react.useRef)(view);
|
|
4150
|
+
if (view.visible) last.current = view;
|
|
4151
|
+
const shown = view.visible ? view : last.current;
|
|
4152
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
4153
|
+
className: "cc-saveBarDock",
|
|
4154
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4155
|
+
className: `cc-saveBar cc-saveBar-${shown.tone}${view.visible ? " cc-saveBarShown" : ""}`,
|
|
4156
|
+
role: "region",
|
|
4157
|
+
"aria-label": t("save"),
|
|
4158
|
+
"aria-hidden": !view.visible,
|
|
4159
|
+
children: [
|
|
4160
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(SaveBarIcon, { tone: shown.tone }),
|
|
4161
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
4162
|
+
className: "cc-saveBarText",
|
|
4163
|
+
role: "status",
|
|
4164
|
+
"aria-live": "polite",
|
|
4165
|
+
title: shown.message,
|
|
4166
|
+
children: shown.message
|
|
4167
|
+
}),
|
|
4168
|
+
shown.actions ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4169
|
+
className: "cc-saveBarActions",
|
|
4170
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
4171
|
+
type: "button",
|
|
4172
|
+
className: "cc-saveBarButton cc-saveBarGhost",
|
|
4173
|
+
disabled: !view.visible || !state.dirty || state.saving,
|
|
4174
|
+
onClick: onDiscard,
|
|
4175
|
+
children: t("discard")
|
|
4176
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
4177
|
+
type: "button",
|
|
4178
|
+
className: "cc-saveBarButton cc-saveBarPrimary",
|
|
4179
|
+
disabled: !view.visible || !state.dirty || state.invalid || state.saving,
|
|
4180
|
+
onClick: onSave,
|
|
4181
|
+
children: t(state.saving ? "saving" : "save")
|
|
4182
|
+
})]
|
|
4183
|
+
}) : null
|
|
4184
|
+
]
|
|
4185
|
+
})
|
|
4186
|
+
});
|
|
4187
|
+
}
|
|
4188
|
+
/** The settings page body: accounts, then the staged provider settings. */
|
|
4049
4189
|
function CommandCodeSettingsPage(props) {
|
|
4050
4190
|
const { t } = props;
|
|
4051
|
-
const footerMarker = (0, react.useRef)(null);
|
|
4052
|
-
const [footerFloating, setFooterFloating] = (0, react.useState)(false);
|
|
4053
4191
|
const state = props.useCommandCodeSettings((snapshot) => snapshot);
|
|
4054
4192
|
const usage = props.useCommandCodeUsage((snapshot) => snapshot);
|
|
4055
4193
|
const login = props.useCommandCodeLogin((snapshot) => snapshot);
|
|
4056
4194
|
const disabled = !state.writable;
|
|
4057
|
-
const keyLocked = !state.apiKeyWritable;
|
|
4058
4195
|
const savedVisible = useSavedFlash(state.savedCount);
|
|
4059
4196
|
const updateVersion = usePluginUpdate();
|
|
4060
|
-
|
|
4061
|
-
|
|
4062
|
-
|
|
4063
|
-
|
|
4064
|
-
|
|
4065
|
-
|
|
4066
|
-
|
|
4067
|
-
|
|
4068
|
-
|
|
4197
|
+
const { createAccount, renameAccount, removeAccount, setAccountKey, clearAccountKey, setActiveAccount, setAccountModels, beginLogin, cancelLogin } = props;
|
|
4198
|
+
const actions = (0, react.useMemo)(() => ({
|
|
4199
|
+
create: createAccount,
|
|
4200
|
+
rename: renameAccount,
|
|
4201
|
+
remove: removeAccount,
|
|
4202
|
+
setKey: setAccountKey,
|
|
4203
|
+
clearKey: clearAccountKey,
|
|
4204
|
+
setActive: setActiveAccount,
|
|
4205
|
+
setModels: setAccountModels,
|
|
4206
|
+
beginLogin,
|
|
4207
|
+
cancelLogin
|
|
4208
|
+
}), [
|
|
4209
|
+
createAccount,
|
|
4210
|
+
renameAccount,
|
|
4211
|
+
removeAccount,
|
|
4212
|
+
setAccountKey,
|
|
4213
|
+
clearAccountKey,
|
|
4214
|
+
setActiveAccount,
|
|
4215
|
+
setAccountModels,
|
|
4216
|
+
beginLogin,
|
|
4217
|
+
cancelLogin
|
|
4218
|
+
]);
|
|
4219
|
+
const bar = saveBarView(state, savedVisible, t);
|
|
4220
|
+
const form = {
|
|
4221
|
+
state,
|
|
4222
|
+
disabled,
|
|
4223
|
+
t,
|
|
4224
|
+
onEdit: props.edit,
|
|
4225
|
+
onReset: props.resetField
|
|
4226
|
+
};
|
|
4069
4227
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
|
|
4070
|
-
className: "cc-section",
|
|
4228
|
+
className: bar.visible ? "cc-section cc-sectionWithBar" : "cc-section",
|
|
4071
4229
|
"aria-label": t("title"),
|
|
4072
4230
|
children: [
|
|
4073
4231
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h2", {
|
|
@@ -4083,117 +4241,23 @@ window.__ModuleLoader__.load({
|
|
|
4083
4241
|
role: "status",
|
|
4084
4242
|
children: t("readOnly")
|
|
4085
4243
|
}) : null,
|
|
4086
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(UsageCard, {
|
|
4087
|
-
t,
|
|
4088
|
-
usage,
|
|
4089
|
-
removingIds: state.accountsRemoving,
|
|
4090
|
-
removableIds: state.accounts.map((account) => account.id),
|
|
4091
|
-
canManage: state.writable,
|
|
4092
|
-
onRefresh: props.refreshUsage,
|
|
4093
|
-
onRemoveAccount: props.removeAccount
|
|
4094
|
-
}),
|
|
4095
4244
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(AccountsCard, {
|
|
4096
4245
|
t,
|
|
4097
4246
|
state,
|
|
4247
|
+
usage,
|
|
4098
4248
|
login,
|
|
4099
4249
|
disabled,
|
|
4100
|
-
|
|
4101
|
-
|
|
4102
|
-
onLabel: props.editAccountLabel,
|
|
4103
|
-
onKey: props.editAccountKey,
|
|
4104
|
-
onToggleClear: (id) => props.toggleKeyClear(id),
|
|
4105
|
-
onActive: (text) => props.edit("activeAccount", text),
|
|
4106
|
-
onActiveReset: () => props.resetField("activeAccount"),
|
|
4107
|
-
onBeginLogin: props.beginLogin,
|
|
4108
|
-
onCancelLogin: props.cancelLogin
|
|
4109
|
-
}),
|
|
4110
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(RulesCard, {
|
|
4111
|
-
t,
|
|
4112
|
-
state,
|
|
4113
|
-
disabled,
|
|
4114
|
-
onAdd: props.addRule,
|
|
4115
|
-
onRemove: props.removeRule,
|
|
4116
|
-
onModels: props.editRuleModels,
|
|
4117
|
-
onAccount: props.editRuleAccount
|
|
4250
|
+
actions,
|
|
4251
|
+
onRefresh: props.refreshUsage
|
|
4118
4252
|
}),
|
|
4119
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(
|
|
4120
|
-
|
|
4121
|
-
state,
|
|
4122
|
-
disabled,
|
|
4253
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(ModelsCard, {
|
|
4254
|
+
...form,
|
|
4123
4255
|
onSelect: props.editVisibleModels,
|
|
4124
4256
|
onClear: props.clearVisibleModels
|
|
4125
4257
|
}),
|
|
4126
|
-
/* @__PURE__ */ (0, react_jsx_runtime.
|
|
4127
|
-
|
|
4128
|
-
|
|
4129
|
-
label: t("apiKey"),
|
|
4130
|
-
hint: keyLocked ? t("apiKeyLocked") : t("apiKeyHint"),
|
|
4131
|
-
state: state.apiKey,
|
|
4132
|
-
disabled: disabled || keyLocked || login.targetRef === void 0 && (login.phase === "starting" || login.phase === "waiting"),
|
|
4133
|
-
configured: state.apiKeyConfigured,
|
|
4134
|
-
configuredLabel: t("apiKeySet"),
|
|
4135
|
-
unconfiguredLabel: t("apiKeyUnset"),
|
|
4136
|
-
clearStaged: state.apiKeyClearStaged,
|
|
4137
|
-
showLabel: t("show"),
|
|
4138
|
-
hideLabel: t("hide"),
|
|
4139
|
-
clearLabel: t("usageKeyClear"),
|
|
4140
|
-
clearStagedLabel: t("usageKeyClearStaged"),
|
|
4141
|
-
undoClearLabel: t("usageUndoKeyClear"),
|
|
4142
|
-
onEdit: (text) => props.edit("apiKey", text),
|
|
4143
|
-
onToggleClear: () => props.toggleKeyClear("default")
|
|
4144
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(LoginRow, {
|
|
4145
|
-
state: login,
|
|
4146
|
-
disabled: disabled || keyLocked || state.dirty,
|
|
4147
|
-
disabledHint: state.dirty ? t("loginSaveBefore") : void 0,
|
|
4148
|
-
t,
|
|
4149
|
-
onBegin: props.beginLogin,
|
|
4150
|
-
onCancel: props.cancelLogin
|
|
4151
|
-
})]
|
|
4152
|
-
}),
|
|
4153
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(AdvancedSection, {
|
|
4154
|
-
state,
|
|
4155
|
-
disabled,
|
|
4156
|
-
t,
|
|
4157
|
-
onEdit: props.edit,
|
|
4158
|
-
onReset: props.resetField
|
|
4159
|
-
}),
|
|
4160
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
4161
|
-
ref: footerMarker,
|
|
4162
|
-
className: "cc-footerMarker",
|
|
4163
|
-
"aria-hidden": "true"
|
|
4164
|
-
}),
|
|
4165
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
4166
|
-
className: `cc-footer cc-footerSticky${footerFloating ? " cc-footerFloating" : ""}`,
|
|
4167
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4168
|
-
className: "cc-footerActions",
|
|
4169
|
-
children: [
|
|
4170
|
-
state.failed ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
4171
|
-
className: "cc-failed",
|
|
4172
|
-
role: "status",
|
|
4173
|
-
children: t("saveFailed")
|
|
4174
|
-
}) : null,
|
|
4175
|
-
savedVisible ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
4176
|
-
className: "cc-saved",
|
|
4177
|
-
role: "status",
|
|
4178
|
-
children: t("saved")
|
|
4179
|
-
}) : null,
|
|
4180
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
4181
|
-
variant: "ghost",
|
|
4182
|
-
size: "sm",
|
|
4183
|
-
disabled: !state.dirty || state.saving,
|
|
4184
|
-
onClick: props.discard,
|
|
4185
|
-
children: t("discard")
|
|
4186
|
-
}),
|
|
4187
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
4188
|
-
variant: "primary",
|
|
4189
|
-
size: "sm",
|
|
4190
|
-
disabled: !state.dirty || state.invalid || state.saving,
|
|
4191
|
-
onClick: props.save,
|
|
4192
|
-
children: t(state.saving ? "saving" : "save")
|
|
4193
|
-
})
|
|
4194
|
-
]
|
|
4195
|
-
})
|
|
4196
|
-
}),
|
|
4258
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(PrivacyCard, { ...form }),
|
|
4259
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(IntegrationsCard, { ...form }),
|
|
4260
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(AdvancedSection, { ...form }),
|
|
4197
4261
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("p", {
|
|
4198
4262
|
className: "cc-version",
|
|
4199
4263
|
children: [
|
|
@@ -4213,6 +4277,62 @@ window.__ModuleLoader__.load({
|
|
|
4213
4277
|
]
|
|
4214
4278
|
})] }) : null
|
|
4215
4279
|
]
|
|
4280
|
+
}),
|
|
4281
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(SaveBar, {
|
|
4282
|
+
view: bar,
|
|
4283
|
+
state,
|
|
4284
|
+
t,
|
|
4285
|
+
onDiscard: props.discard,
|
|
4286
|
+
onSave: props.save
|
|
4287
|
+
})
|
|
4288
|
+
]
|
|
4289
|
+
});
|
|
4290
|
+
}
|
|
4291
|
+
//#endregion
|
|
4292
|
+
//#region src/client/login-row.tsx
|
|
4293
|
+
/** The sign-in alternative to pasting a key (settings page + Models card). */
|
|
4294
|
+
function LoginRow({ state, targetRef, disabled, disabledHint, t, onBegin, onCancel }) {
|
|
4295
|
+
const { visible: visibleState, busyElsewhere } = loginStateForTarget(state, targetRef);
|
|
4296
|
+
const busy = visibleState.phase === "starting" || visibleState.phase === "waiting";
|
|
4297
|
+
const hint = loginHint(visibleState, t);
|
|
4298
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4299
|
+
className: "cc-field",
|
|
4300
|
+
children: [
|
|
4301
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4302
|
+
className: "cc-fieldHead",
|
|
4303
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
4304
|
+
className: "cc-label",
|
|
4305
|
+
children: t("loginTitle")
|
|
4306
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
4307
|
+
className: "cc-badges",
|
|
4308
|
+
children: busy ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
4309
|
+
type: "button",
|
|
4310
|
+
className: "cc-reset",
|
|
4311
|
+
onClick: onCancel,
|
|
4312
|
+
children: t("loginCancel")
|
|
4313
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
4314
|
+
type: "button",
|
|
4315
|
+
className: "cc-reset",
|
|
4316
|
+
disabled: disabled || busyElsewhere,
|
|
4317
|
+
onClick: onBegin,
|
|
4318
|
+
children: t("loginButton")
|
|
4319
|
+
})
|
|
4320
|
+
})]
|
|
4321
|
+
}),
|
|
4322
|
+
visibleState.authUrl !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
4323
|
+
className: "cc-hint",
|
|
4324
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("a", {
|
|
4325
|
+
className: "cc-loginLink",
|
|
4326
|
+
href: visibleState.authUrl,
|
|
4327
|
+
target: "_blank",
|
|
4328
|
+
rel: "noreferrer",
|
|
4329
|
+
children: t("loginOpenLink")
|
|
4330
|
+
})
|
|
4331
|
+
}) : null,
|
|
4332
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
4333
|
+
className: hint.className,
|
|
4334
|
+
title: hint.title,
|
|
4335
|
+
children: disabled && !busy && disabledHint !== void 0 ? disabledHint : hint.text
|
|
4216
4336
|
})
|
|
4217
4337
|
]
|
|
4218
4338
|
});
|
|
@@ -4741,16 +4861,13 @@ window.__ModuleLoader__.load({
|
|
|
4741
4861
|
* Project the shared usage snapshot into the panel's render tree.
|
|
4742
4862
|
*
|
|
4743
4863
|
* Deduplication matches the settings card: hand-edited settings can name one
|
|
4744
|
-
* credential twice
|
|
4745
|
-
* refresh lands.
|
|
4864
|
+
* credential twice.
|
|
4746
4865
|
*/
|
|
4747
4866
|
function buildPanelView(input) {
|
|
4748
4867
|
const { usage } = input;
|
|
4749
4868
|
const t = input.t ?? panelTextEN;
|
|
4750
|
-
const hidden = new Set(input.removingIds ?? []);
|
|
4751
4869
|
const seen = /* @__PURE__ */ new Set();
|
|
4752
4870
|
const entries = (usage.report?.accounts ?? []).filter((entry) => {
|
|
4753
|
-
if (hidden.has(entry.id)) return false;
|
|
4754
4871
|
if (seen.has(entry.id)) return false;
|
|
4755
4872
|
seen.add(entry.id);
|
|
4756
4873
|
return true;
|
|
@@ -4892,12 +5009,9 @@ window.__ModuleLoader__.load({
|
|
|
4892
5009
|
*/
|
|
4893
5010
|
/** The panel's view, recomputed from both seats on every notification. */
|
|
4894
5011
|
function usePanelView(props) {
|
|
4895
|
-
const usage = props.useCommandCodeUsage((state) => state);
|
|
4896
|
-
const settings = props.useCommandCodeSettings((state) => state);
|
|
4897
5012
|
return buildPanelView({
|
|
4898
|
-
usage,
|
|
4899
|
-
apiKeyConfigured:
|
|
4900
|
-
removingIds: settings.accountsRemoving,
|
|
5013
|
+
usage: props.useCommandCodeUsage((state) => state),
|
|
5014
|
+
apiKeyConfigured: props.useCommandCodeSettings((state) => state).anyAccountConfigured,
|
|
4901
5015
|
t: props.t ?? panelTextEN
|
|
4902
5016
|
});
|
|
4903
5017
|
}
|
|
@@ -5790,15 +5904,15 @@ window.__ModuleLoader__.load({
|
|
|
5790
5904
|
/**
|
|
5791
5905
|
* The shipped composer stats row (dsh-client-ui-chat `StatsPills`).
|
|
5792
5906
|
*
|
|
5793
|
-
* A PREFERENCE, not a requirement:
|
|
5794
|
-
*
|
|
5795
|
-
* insisted on it would silently stop finding the row
|
|
5796
|
-
* absent the search falls back to the dock OUTLET — deliberately the outlet
|
|
5797
|
-
* never the outlet's parent, because
|
|
5798
|
-
*
|
|
5799
|
-
*
|
|
5800
|
-
*
|
|
5801
|
-
*
|
|
5907
|
+
* A PREFERENCE, not a requirement: the attribute has already been dropped once
|
|
5908
|
+
* upstream (0.1.6-alpha.2) while the rest of the row's markup stayed identical,
|
|
5909
|
+
* so a lookup that insisted on it would silently stop finding the row. When it
|
|
5910
|
+
* is absent the search falls back to the dock OUTLET — deliberately the outlet
|
|
5911
|
+
* and never the outlet's parent, because the parent is the composer footer,
|
|
5912
|
+
* which holds the `ContextMeter` as well, and that meter's trigger is itself a
|
|
5913
|
+
* `button[aria-haspopup="dialog"]` rendered AFTER the dock. A parent-scoped
|
|
5914
|
+
* "last trigger wins" would therefore append the cost to the context ring
|
|
5915
|
+
* instead of the token pill.
|
|
5802
5916
|
*/
|
|
5803
5917
|
const STATS_ROOT = "[data-composer-stats]";
|
|
5804
5918
|
/**
|
|
@@ -5935,14 +6049,14 @@ window.__ModuleLoader__.load({
|
|
|
5935
6049
|
*
|
|
5936
6050
|
* The scope is the dock OUTLET (`display: contents`, one per slot), which holds
|
|
5937
6051
|
* exactly that slot's entries — the shipped `stats` cell and ours, in their
|
|
5938
|
-
* registration order. {@link STATS_ROOT} narrows that to the stats row
|
|
5939
|
-
*
|
|
5940
|
-
*
|
|
5941
|
-
*
|
|
6052
|
+
* registration order. {@link STATS_ROOT} narrows that to the stats row where
|
|
6053
|
+
* the markup marks it; where it does not, the outlet is already the narrowest
|
|
6054
|
+
* correct container, because the composer footer's `ContextMeter` sits BESIDE
|
|
6055
|
+
* the outlet rather than inside it.
|
|
5942
6056
|
*
|
|
5943
6057
|
* A missing scope is "no pill", never a document-wide search: the outlet is
|
|
5944
6058
|
* what makes this lookup per-composer, and a second composer can be live at
|
|
5945
|
-
* once (
|
|
6059
|
+
* once (the sidebar can mount an embedded Conversation).
|
|
5946
6060
|
*/
|
|
5947
6061
|
resolvePillButton() {
|
|
5948
6062
|
const scope = this.scope();
|
|
@@ -6263,140 +6377,265 @@ window.__ModuleLoader__.load({
|
|
|
6263
6377
|
* that silently drops one stylesheet.
|
|
6264
6378
|
*/
|
|
6265
6379
|
const PAGE_CSS_ID = "@mars-sea/dsh-commandcode-provider/CommandCodeSettingsPage.module.css";
|
|
6266
|
-
/**
|
|
6380
|
+
/**
|
|
6381
|
+
* The settings-page stylesheet.
|
|
6382
|
+
*
|
|
6383
|
+
* The metrics are the harness's own settings pages', read out of the 0.1.7
|
|
6384
|
+
* bundles so the page sits beside General and Models without looking foreign:
|
|
6385
|
+
* a row is 16px of padding over a 0.5px border-l2 hairline, its title 14/22
|
|
6386
|
+
* label-primary and its description 12/18 label-tertiary, the control on the
|
|
6387
|
+
* right. Selectors and buttons are capsules (36px / 28px compact), inputs are
|
|
6388
|
+
* 32px with an 8px radius and a 0.5px border-l4 edge, a card is a 0.5px
|
|
6389
|
+
* border-l4 outline with a 16px radius, and a nested panel is a
|
|
6390
|
+
* bg-module-platform fill with a 12px radius. Errors use state-error-primary,
|
|
6391
|
+
* the platform's error colour (label-error is its text-only sibling).
|
|
6392
|
+
*/
|
|
6267
6393
|
const PAGE_CSS = `
|
|
6268
|
-
.cc-section{max-width:720px;color:var(--dsw-alias-label-primary);flex-direction:column;
|
|
6269
|
-
.cc-title{margin:0;font-size:
|
|
6270
|
-
.cc-intro{color:var(--dsw-alias-label-tertiary);
|
|
6271
|
-
.cc-readOnly{color:var(--dsw-alias-label-tertiary);
|
|
6272
|
-
.cc-
|
|
6394
|
+
.cc-section{max-width:720px;color:var(--dsw-alias-label-primary);flex-direction:column;display:flex}
|
|
6395
|
+
.cc-title{margin:0;color:var(--dsw-alias-label-primary);font-size:16px;font-weight:500;line-height:24px}
|
|
6396
|
+
.cc-intro{margin:4px 0 0;color:var(--dsw-alias-label-tertiary);font-size:14px;line-height:22px}
|
|
6397
|
+
.cc-readOnly{margin:8px 0 0;color:var(--dsw-alias-state-warn-label,var(--dsw-alias-label-tertiary));font-size:12px;line-height:18px}
|
|
6398
|
+
.cc-spacer{flex:1}
|
|
6399
|
+
/* Groups: a heading over hairline-separated rows, no card surface. */
|
|
6400
|
+
.cc-group{flex-direction:column;display:flex;margin-top:28px}
|
|
6401
|
+
.cc-groupHead{align-items:center;gap:8px;display:flex;min-height:28px;padding-bottom:4px}
|
|
6402
|
+
.cc-groupTitle{margin:0;color:var(--dsw-alias-label-primary);font-size:14px;font-weight:500;line-height:22px}
|
|
6403
|
+
.cc-groupDesc{margin:0 0 12px;color:var(--dsw-alias-label-tertiary);font-size:12px;line-height:18px}
|
|
6404
|
+
.cc-rows>.cc-groupDesc{margin:0;padding:4px 0 0}
|
|
6405
|
+
.cc-disclosure{width:100%;padding:0 0 4px;border:0;background:0 0;font:inherit;text-align:left;cursor:pointer;border-radius:6px}
|
|
6406
|
+
.cc-disclosure:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:2px}
|
|
6407
|
+
.cc-rows{flex-direction:column;display:flex}
|
|
6408
|
+
.cc-row{align-items:center;gap:8px;display:flex;padding:16px 0;border-bottom:.5px solid var(--dsw-alias-border-l2)}
|
|
6409
|
+
.cc-rows>.cc-row:last-child{border-bottom:0}
|
|
6410
|
+
.cc-rowNested{padding-left:16px}
|
|
6411
|
+
.cc-rowFlush{padding:0;border-bottom:0}
|
|
6412
|
+
.cc-rowText{flex-direction:column;flex:1;gap:4px;min-width:0;padding-right:32px;display:flex}
|
|
6413
|
+
.cc-rowTitleLine{align-items:center;gap:8px;display:flex;min-width:0}
|
|
6414
|
+
.cc-rowTitle{min-width:0;color:var(--dsw-alias-label-primary);font-size:14px;font-weight:400;line-height:22px}
|
|
6415
|
+
.cc-rowDesc{color:var(--dsw-alias-label-tertiary);font-size:12px;line-height:18px}
|
|
6416
|
+
.cc-rowDesc p{margin:0}
|
|
6417
|
+
.cc-rowDesc p+p{margin-top:4px}
|
|
6418
|
+
.cc-rowError{margin:0;color:var(--dsw-alias-state-error-primary);font-size:12px;line-height:18px}
|
|
6419
|
+
.cc-rowControl{flex:none;align-items:center;justify-content:flex-end;gap:8px;display:inline-flex;max-width:60%}
|
|
6420
|
+
.cc-rowInput{width:200px}
|
|
6421
|
+
.cc-rowInputWide{width:280px}
|
|
6422
|
+
/* The platform's link button: a compact capsule with no fill until hovered. */
|
|
6423
|
+
.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}
|
|
6424
|
+
.cc-linkButton:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-secondary)}
|
|
6425
|
+
.cc-linkButton:disabled{cursor:default;opacity:.4}
|
|
6426
|
+
.cc-linkButton:focus-visible{outline:none;box-shadow:0 0 0 2px var(--dsw-alias-border-l3)}
|
|
6427
|
+
/* Tags: the platform Tag's capsule (11/17, weight 500). */
|
|
6428
|
+
.cc-badges{align-items:center;gap:8px;display:inline-flex}
|
|
6429
|
+
.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}
|
|
6430
|
+
.cc-badge{background:var(--dsw-alias-bg-module-platform);color:var(--dsw-alias-label-secondary)}
|
|
6431
|
+
.cc-badgeMuted{border:.5px solid var(--dsw-alias-border-l4);color:var(--dsw-alias-label-tertiary)}
|
|
6432
|
+
.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))}
|
|
6433
|
+
/* The stacked field (the Models-page provider card and the account forms):
|
|
6434
|
+
* the platform SettingsForm field's metrics. */
|
|
6273
6435
|
.cc-field{flex-direction:column;gap:6px;padding:12px 0;display:flex}
|
|
6274
|
-
.cc-field+.cc-field{border-top
|
|
6436
|
+
.cc-field+.cc-field{border-top:.5px solid var(--dsw-alias-border-l2)}
|
|
6275
6437
|
.cc-fieldHead{align-items:center;gap:8px;display:flex}
|
|
6276
6438
|
.cc-label{min-width:0;color:var(--dsw-alias-label-primary);flex:1;font-size:13px;font-weight:500;line-height:1.5}
|
|
6277
|
-
.cc-
|
|
6278
|
-
.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}
|
|
6279
|
-
.cc-badgeMuted{white-space:nowrap;color:var(--dsw-alias-label-tertiary);border-radius:999px;padding:1px 8px;font-size:11px;line-height:17px}
|
|
6280
|
-
.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}
|
|
6439
|
+
.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}
|
|
6281
6440
|
.cc-reset:hover:not(:disabled){color:var(--dsw-alias-label-primary)}
|
|
6282
|
-
.cc-reset:disabled{cursor:default;opacity:.
|
|
6283
|
-
.cc-input{border:
|
|
6441
|
+
.cc-reset:disabled{cursor:default;opacity:.4}
|
|
6442
|
+
.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}
|
|
6443
|
+
.cc-input::placeholder{color:var(--dsw-alias-label-dimmed)}
|
|
6284
6444
|
.cc-input:focus-visible{border-color:var(--dsw-alias-brand-primary);outline:none}
|
|
6285
|
-
.cc-input:disabled{
|
|
6286
|
-
|
|
6287
|
-
|
|
6288
|
-
|
|
6289
|
-
|
|
6290
|
-
.
|
|
6291
|
-
.cc-
|
|
6292
|
-
.cc-
|
|
6445
|
+
.cc-input:disabled{opacity:.6;cursor:default}
|
|
6446
|
+
.cc-inputInvalid{border-color:var(--dsw-alias-state-error-primary)}
|
|
6447
|
+
.cc-invalid{color:var(--dsw-alias-state-error-primary);margin:0;font-size:12px;line-height:18px}
|
|
6448
|
+
.cc-hint{color:var(--dsw-alias-label-tertiary);margin:0;font-size:12px;line-height:18px}
|
|
6449
|
+
/* The model multi-select trigger: the platform's selector capsule (the
|
|
6450
|
+
* language / permission pickers on General). */
|
|
6451
|
+
.cc-modelSelectAnchor{flex-direction:column;align-items:stretch;gap:6px;display:inline-flex;min-width:160px}
|
|
6452
|
+
.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}
|
|
6453
|
+
.cc-selector:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover)}
|
|
6454
|
+
.cc-selector:disabled{cursor:default;opacity:.4}
|
|
6455
|
+
.cc-selector:focus-visible{outline:none;box-shadow:0 0 0 2px var(--dsw-alias-border-l3)}
|
|
6456
|
+
.cc-selectorText{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
|
6457
|
+
.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)}
|
|
6458
|
+
/* The search box sits inside the Menu anchor, so typing in it never trips
|
|
6459
|
+
* the Menu's outside-click close. */
|
|
6460
|
+
.cc-modelSearch{width:100%}
|
|
6461
|
+
.cc-modelSearch::-webkit-search-cancel-button{cursor:pointer}
|
|
6293
6462
|
.cc-checkRow{align-items:center;gap:8px;display:inline-flex;min-width:0}
|
|
6294
6463
|
.cc-checkRow:hover{cursor:pointer}
|
|
6295
|
-
|
|
6464
|
+
/* A hand-drawn box rather than the platform's accent-color checkbox: the
|
|
6465
|
+
* native control's colours come from color-scheme, which the theme sets once
|
|
6466
|
+
* at BOOT (it is not re-applied when the theme is switched in-session), so a
|
|
6467
|
+
* native box can paint light-chrome white on a dark page. Drawn from tokens it
|
|
6468
|
+
* cannot. The mark rides a brand-primary fill, so it takes the theme's brand
|
|
6469
|
+
* foreground — the platform's pairing for anything painted on that fill, and
|
|
6470
|
+
* the only one that survives the dark mode's near-white brand. The box
|
|
6471
|
+
* outline is a border-l3 hairline: border-l2 (#ffffff1f in dark) leaves an
|
|
6472
|
+
* unchecked box all but invisible against the menu surface. */
|
|
6473
|
+
.cc-check{box-sizing:border-box;appearance:none;flex-shrink:0;width:16px;height:16px;margin:0;border:1px solid var(--dsw-alias-border-l3);border-radius:4px;background:0 0;position:relative}
|
|
6296
6474
|
.cc-check:checked{background:var(--dsw-alias-brand-primary);border-color:var(--dsw-alias-brand-primary)}
|
|
6297
|
-
.cc-check:checked::after{content:'';position:absolute;top:
|
|
6475
|
+
.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)}
|
|
6298
6476
|
.cc-checkName{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
|
6299
|
-
/* The
|
|
6300
|
-
*
|
|
6301
|
-
*
|
|
6302
|
-
|
|
6303
|
-
.cc-
|
|
6304
|
-
.cc-
|
|
6305
|
-
.cc-
|
|
6306
|
-
|
|
6307
|
-
|
|
6308
|
-
|
|
6309
|
-
*
|
|
6310
|
-
*
|
|
6311
|
-
*
|
|
6312
|
-
|
|
6313
|
-
|
|
6314
|
-
|
|
6315
|
-
|
|
6316
|
-
*
|
|
6317
|
-
|
|
6318
|
-
.
|
|
6319
|
-
|
|
6320
|
-
.
|
|
6321
|
-
.cc-
|
|
6322
|
-
.cc-chevronUp{transform:rotate(-135deg);margin-bottom:-3px}
|
|
6323
|
-
.cc-advancedBody{flex-direction:column;display:flex}
|
|
6324
|
-
.cc-advancedBody>.cc-field:first-of-type{border-top:1px solid var(--dsw-alias-border-l2)}
|
|
6325
|
-
.cc-hint{color:var(--dsw-alias-label-tertiary);margin:0;font-size:12px;line-height:1.5}
|
|
6326
|
-
.cc-footer{justify-content:flex-end;align-items:center;gap:8px;display:flex}
|
|
6327
|
-
/* Keep the original footer in flow and inside the settings scrollport. */
|
|
6328
|
-
.cc-footerMarker{height:1px;margin:-6px 0 -7px;pointer-events:none}
|
|
6329
|
-
.cc-footerSticky{position:sticky;bottom:0;z-index:2;flex-shrink:0;min-height:48px;pointer-events:none}
|
|
6330
|
-
.cc-footerActions{align-items:center;gap:8px;display:flex;pointer-events:auto}
|
|
6331
|
-
.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)}
|
|
6332
|
-
.cc-accountEditor{border-top:1px solid var(--dsw-alias-border-l2)}
|
|
6333
|
-
.cc-accountLoginHint{padding:0 0 12px}
|
|
6334
|
-
.cc-toggleRow{align-items:center;gap:8px;cursor:pointer;display:flex}
|
|
6335
|
-
.cc-toggleRow:has(.cc-toggle:disabled){cursor:default}
|
|
6336
|
-
.cc-toggle{appearance:none;flex-shrink:0;background:var(--dsw-alias-border-l2);border-radius:999px;width:30px;height:18px;margin:0;cursor:pointer;position:relative;transition:background .15s ease}
|
|
6477
|
+
/* The platform SegmentedControl: a translucent track and one raised pill that
|
|
6478
|
+
* slides under the picked segment. Segments are equal grid tracks, so the
|
|
6479
|
+
* indicator's width and offset follow from the count and the index alone. */
|
|
6480
|
+
.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)}
|
|
6481
|
+
.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}
|
|
6482
|
+
.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}
|
|
6483
|
+
.cc-segment:hover:not(:disabled),.cc-segment[aria-checked=true]{color:var(--dsw-alias-label-primary)}
|
|
6484
|
+
.cc-segment:disabled{cursor:default;opacity:.4}
|
|
6485
|
+
.cc-segment:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:-2px}
|
|
6486
|
+
/* Modelled on the platform's own Switch primitive (ui-primitives
|
|
6487
|
+
* Switch.module.css): a 36x20 capsule that pads its track by 2px and slides a
|
|
6488
|
+
* 16px thumb across the 32px content box.
|
|
6489
|
+
*
|
|
6490
|
+
* Three things are load-bearing. (1) corner-shape:round on BOTH the track and
|
|
6491
|
+
* the thumb: the theme sets a global superellipse corner shape on every
|
|
6492
|
+
* element, and a superellipse capsule squares its ends off around a round
|
|
6493
|
+
* thumb. (2) No literal colours. --dsw-alias-brand-primary INVERTS between the
|
|
6494
|
+
* two modes (near-black in light, near-white in dark), so a hardcoded white
|
|
6495
|
+
* thumb vanishes on the checked track in dark mode; the brand foreground is the
|
|
6496
|
+
* token the platform paints on that fill. (3) --dsw-alias-border-l3 as the off
|
|
6497
|
+
* track: border-l2 (#ffffff1f in dark) leaves it indistinguishable from the
|
|
6498
|
+
* page. */
|
|
6499
|
+
.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}
|
|
6337
6500
|
.cc-toggle:checked{background:var(--dsw-alias-brand-primary)}
|
|
6338
|
-
.cc-toggle::after{content:'';
|
|
6339
|
-
.
|
|
6501
|
+
.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}
|
|
6502
|
+
/* 32px content box minus the 16px thumb. */
|
|
6503
|
+
.cc-toggle:checked::after{transform:translateX(16px)}
|
|
6504
|
+
.cc-toggle:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:2px}
|
|
6340
6505
|
.cc-toggle:disabled{cursor:default;opacity:.5}
|
|
6341
|
-
.cc-
|
|
6342
|
-
.cc-
|
|
6506
|
+
.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}
|
|
6507
|
+
.cc-chevronUp{transform:rotate(-135deg);margin-bottom:-3px}
|
|
6508
|
+
/* Accounts: one outlined card per account, the Models page's provider card. */
|
|
6509
|
+
.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}
|
|
6510
|
+
.cc-accountList{flex-direction:column;gap:8px;display:flex}
|
|
6511
|
+
.cc-accountList+.cc-addButton,.cc-accountList+.cc-addPanel{margin-top:8px}
|
|
6512
|
+
.cc-accountItem{flex-direction:column;gap:10px;display:flex;padding:12px 14px;border:.5px solid var(--dsw-alias-border-l4);border-radius:16px}
|
|
6513
|
+
.cc-accountItemActive{border-color:var(--dsw-static-neutral-bluish-400,var(--dsw-alias-border-l3))}
|
|
6514
|
+
.cc-accountHead{align-items:center;gap:4px;display:flex;min-height:28px}
|
|
6515
|
+
.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}
|
|
6516
|
+
.cc-accountToggle:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:2px;border-radius:4px}
|
|
6517
|
+
.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}
|
|
6518
|
+
.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}
|
|
6519
|
+
.cc-iconButton:hover{background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-primary)}
|
|
6520
|
+
.cc-iconButton:focus-visible{outline:none;box-shadow:0 0 0 2px var(--dsw-alias-border-l3)}
|
|
6521
|
+
/* Three dots from one element: the dot itself plus two box-shadow copies. */
|
|
6522
|
+
.cc-kebab{width:3px;height:3px;border-radius:50%;corner-shape:round;background:currentColor;box-shadow:0 -5px 0 currentColor,0 5px 0 currentColor}
|
|
6523
|
+
.cc-tabDot{flex-shrink:0;width:8px;height:8px;border-radius:50%;corner-shape:round}
|
|
6524
|
+
.cc-tabDotOk{background:var(--dsw-alias-state-success-primary)}
|
|
6525
|
+
.cc-tabDotWarn{background:var(--dsw-alias-state-warn-primary,#d97706)}
|
|
6526
|
+
.cc-tabDotError{background:var(--dsw-alias-state-error-primary)}
|
|
6527
|
+
.cc-accountMeters{flex-wrap:wrap;gap:6px 20px;display:flex;padding-left:16px}
|
|
6528
|
+
.cc-miniMeter{align-items:center;gap:8px;display:inline-flex;font-size:12px;line-height:18px}
|
|
6529
|
+
.cc-miniMeterLabel{color:var(--dsw-alias-label-tertiary)}
|
|
6530
|
+
.cc-miniMeterTrack{overflow:hidden;background:var(--dsw-alias-bg-module-platform);border-radius:999px;width:64px;height:4px}
|
|
6531
|
+
.cc-miniMeterFill{display:block;background:var(--dsw-alias-brand-primary);border-radius:999px;height:100%}
|
|
6532
|
+
.cc-miniMeterValue{color:var(--dsw-alias-label-secondary);font-variant-numeric:tabular-nums}
|
|
6533
|
+
.cc-accountSetup,.cc-loginStatus{flex-direction:column;gap:8px;display:flex;padding-left:16px}
|
|
6534
|
+
.cc-loginStatus{flex-direction:row;flex-wrap:wrap;align-items:center;gap:4px 12px}
|
|
6535
|
+
.cc-loginStatus>p{margin:0;font-size:12px;line-height:18px}
|
|
6536
|
+
.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}
|
|
6537
|
+
.cc-inlineForm,.cc-confirmBar{flex-direction:column;gap:8px;display:flex}
|
|
6538
|
+
.cc-inlineActions{align-items:center;gap:8px;display:flex}
|
|
6539
|
+
.cc-confirmBar{border:.5px solid var(--dsw-alias-state-error-primary);border-radius:12px;padding:12px 14px}
|
|
6540
|
+
.cc-confirmText{color:var(--dsw-alias-label-primary);margin:0;font-size:14px;line-height:22px}
|
|
6541
|
+
.cc-dangerButton{color:var(--dsw-alias-state-error-primary);border-color:var(--dsw-alias-state-error-primary)}
|
|
6542
|
+
.cc-dangerButton:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover-danger)}
|
|
6543
|
+
/* Adding an account: the Models page's dashed add button, opening a filled
|
|
6544
|
+
* panel in its place. */
|
|
6545
|
+
.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}
|
|
6546
|
+
.cc-addButton:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover)}
|
|
6547
|
+
.cc-addButton:disabled{cursor:default;opacity:.4}
|
|
6548
|
+
.cc-addButton:focus-visible{outline:none;box-shadow:0 0 0 2px var(--dsw-alias-border-l3)}
|
|
6549
|
+
/* A plus from two crossed bars. */
|
|
6550
|
+
.cc-addGlyph{position:relative;width:12px;height:12px}
|
|
6551
|
+
.cc-addGlyph::before,.cc-addGlyph::after{content:'';position:absolute;background:currentColor;border-radius:1px}
|
|
6552
|
+
.cc-addGlyph::before{left:0;right:0;top:5.25px;height:1.5px}
|
|
6553
|
+
.cc-addGlyph::after{top:0;bottom:0;left:5.25px;width:1.5px}
|
|
6554
|
+
.cc-addPanel{flex-direction:column;gap:12px;display:flex;padding:14px 16px;border-radius:12px;background:var(--dsw-alias-bg-module-platform)}
|
|
6555
|
+
.cc-panelTitle{color:var(--dsw-alias-label-primary);font-size:14px;font-weight:500;line-height:22px}
|
|
6556
|
+
/* Account report: stat tiles are filled panels, like the platform's editors. */
|
|
6557
|
+
.cc-accountReport{flex-direction:column;gap:14px;display:flex}
|
|
6343
6558
|
.cc-usageHead{align-items:center;gap:8px;display:flex}
|
|
6344
|
-
.cc-usageTitle{color:var(--dsw-alias-label-primary);flex:1;margin:0;font-size:
|
|
6345
|
-
.cc-usageAccount{max-width:40%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
|
|
6346
|
-
.cc-usagePlan{white-space:nowrap;
|
|
6347
|
-
.cc-usagePlanStatus{white-space:nowrap;color:var(--dsw-alias-
|
|
6348
|
-
.cc-
|
|
6349
|
-
|
|
6350
|
-
.
|
|
6351
|
-
|
|
6352
|
-
.
|
|
6559
|
+
.cc-usageTitle{color:var(--dsw-alias-label-primary);flex:1;margin:0;font-size:14px;font-weight:500;line-height:22px}
|
|
6560
|
+
.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}
|
|
6561
|
+
.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}
|
|
6562
|
+
.cc-usagePlanStatus{flex:none;margin:0;white-space:nowrap;color:var(--dsw-alias-state-error-primary);font-size:12px;line-height:18px}
|
|
6563
|
+
.cc-usageHint{color:var(--dsw-alias-label-tertiary);margin:0;font-size:12px;line-height:18px}
|
|
6564
|
+
/* Two fixed grids instead of one auto-fit track: auto-fit wrapped the four
|
|
6565
|
+
* activity tiles 3 + 1 at the details' width. Each group fills its own row
|
|
6566
|
+
* (four activity tiles, three balances) and the activity row folds to 2 × 2
|
|
6567
|
+
* in a narrow details panel. */
|
|
6353
6568
|
.cc-usageStats{grid-template-columns:repeat(auto-fit,minmax(120px,1fr));gap:8px;display:grid}
|
|
6354
|
-
.cc-
|
|
6355
|
-
.cc-
|
|
6356
|
-
.cc-
|
|
6357
|
-
.cc-
|
|
6358
|
-
.cc-
|
|
6569
|
+
.cc-usageStatsActivity{grid-template-columns:repeat(4,minmax(0,1fr))}
|
|
6570
|
+
.cc-usageStatsBalance{grid-template-columns:repeat(3,minmax(0,1fr))}
|
|
6571
|
+
@container (max-width:460px){.cc-usageStatsActivity{grid-template-columns:repeat(2,minmax(0,1fr))}}
|
|
6572
|
+
.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)}
|
|
6573
|
+
.cc-usageStatLabel{color:var(--dsw-alias-label-tertiary);font-size:12px;line-height:18px}
|
|
6574
|
+
.cc-usageStatValue{color:var(--dsw-alias-label-primary);font-size:16px;font-weight:500;line-height:24px;font-variant-numeric:tabular-nums}
|
|
6575
|
+
.cc-usageStatSub{color:var(--dsw-alias-label-tertiary);font-size:12px;line-height:18px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
|
6576
|
+
.cc-usageWindows{flex-direction:column;gap:14px;display:flex}
|
|
6359
6577
|
.cc-usageWindow{flex-direction:column;gap:6px;display:flex}
|
|
6360
6578
|
.cc-usageWindowHead{align-items:baseline;gap:8px;display:flex}
|
|
6361
|
-
.cc-usageWindowLabel{color:var(--dsw-alias-label-secondary);flex:1;font-size:12px;
|
|
6362
|
-
.cc-usageWindowValue{color:var(--dsw-alias-label-primary);font-size:12px;
|
|
6363
|
-
.cc-usageExceeded{color:var(--dsw-alias-
|
|
6364
|
-
.cc-usageBar{overflow:hidden;background:var(--dsw-alias-bg-
|
|
6579
|
+
.cc-usageWindowLabel{color:var(--dsw-alias-label-secondary);flex:1;font-size:12px;line-height:18px}
|
|
6580
|
+
.cc-usageWindowValue{color:var(--dsw-alias-label-primary);font-size:12px;line-height:18px;font-variant-numeric:tabular-nums}
|
|
6581
|
+
.cc-usageExceeded{color:var(--dsw-alias-state-error-primary);font-size:12px;line-height:18px}
|
|
6582
|
+
.cc-usageBar{overflow:hidden;background:var(--dsw-alias-bg-module-platform);border-radius:999px;height:4px}
|
|
6365
6583
|
.cc-usageBarFill{background:var(--dsw-alias-brand-primary);border-radius:999px;height:100%;transition:width .3s ease}
|
|
6366
|
-
.cc-usageBarFillWarn{background:var(--dsw-alias-
|
|
6367
|
-
|
|
6584
|
+
.cc-usageBarFillWarn{background:var(--dsw-alias-state-error-primary)}
|
|
6585
|
+
.cc-usageWindowReset{color:var(--dsw-alias-label-tertiary);margin:0;font-size:12px;line-height:18px}
|
|
6368
6586
|
.cc-usageMeta{align-items:center;gap:8px;display:flex}
|
|
6369
|
-
.cc-accountReport{flex-direction:column;gap:12px;display:flex}
|
|
6370
|
-
.cc-tabs{flex-wrap:wrap;gap:6px;display:flex}
|
|
6371
|
-
.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}
|
|
6372
|
-
.cc-tab:hover:not(.cc-tabActive){color:var(--dsw-alias-label-primary)}
|
|
6373
|
-
.cc-tabActive{color:var(--dsw-alias-label-primary);border-color:var(--dsw-alias-brand-primary)}
|
|
6374
|
-
.cc-tabDotOk{background:var(--dsw-alias-brand-primary);border-radius:50%;width:6px;height:6px}
|
|
6375
|
-
.cc-tabDotWarn{background:#d97706;border-radius:50%;width:6px;height:6px}
|
|
6376
|
-
.cc-tabDotError{background:var(--dsw-alias-label-error);border-radius:50%;width:6px;height:6px}
|
|
6377
6587
|
.cc-usageMetaSpacer{flex:1}
|
|
6378
|
-
.cc-usageUpdated{color:var(--dsw-alias-label-tertiary);margin:0;font-size:
|
|
6379
|
-
.cc-usagePartial{color:var(--dsw-alias-
|
|
6380
|
-
.cc-usageBlocked{border
|
|
6381
|
-
.cc-usageBlockedTitle{color:var(--dsw-alias-
|
|
6382
|
-
.cc-usageBlockedHint{color:var(--dsw-alias-label-secondary);margin:0;font-size:12px;line-height:
|
|
6383
|
-
.cc-usageBlockedDetail{color:var(--dsw-alias-label-
|
|
6384
|
-
.cc-version{margin:
|
|
6588
|
+
.cc-usageUpdated{color:var(--dsw-alias-label-tertiary);margin:0;font-size:12px;line-height:18px}
|
|
6589
|
+
.cc-usagePartial{color:var(--dsw-alias-state-error-primary);margin:0;font-size:12px;line-height:18px}
|
|
6590
|
+
.cc-usageBlocked{border:.5px solid var(--dsw-alias-state-error-primary);border-radius:12px;padding:12px 14px;display:flex;flex-direction:column;gap:4px}
|
|
6591
|
+
.cc-usageBlockedTitle{color:var(--dsw-alias-state-error-primary);margin:0;font-size:14px;font-weight:500;line-height:22px}
|
|
6592
|
+
.cc-usageBlockedHint{color:var(--dsw-alias-label-secondary);margin:0;font-size:12px;line-height:18px}
|
|
6593
|
+
.cc-usageBlockedDetail{color:var(--dsw-alias-label-tertiary);margin:0;font-size:12px;line-height:18px;word-break:break-word}
|
|
6594
|
+
.cc-version{margin:32px 0 0;text-align:center;color:var(--dsw-alias-label-tertiary);font-size:12px;line-height:18px}
|
|
6595
|
+
/* The update hint rides the footer version line, quiet until hovered. */
|
|
6596
|
+
.cc-versionLink{color:var(--dsw-alias-state-warn-label,var(--dsw-alias-label-secondary));text-decoration:none}
|
|
6597
|
+
.cc-versionLink:hover{color:var(--dsw-alias-label-primary);text-decoration:underline;text-underline-position:under}
|
|
6385
6598
|
/* The Models-page provider panel root: unstyled by design — the row card the
|
|
6386
6599
|
* Models page owns supplies the surface, the panel only stacks its controls. */
|
|
6387
6600
|
.cc-providerCard{flex-direction:column;display:flex}
|
|
6388
|
-
|
|
6389
|
-
|
|
6390
|
-
.
|
|
6391
|
-
.cc-
|
|
6392
|
-
/* The login panel rides the connection card as one more field row; the
|
|
6393
|
-
* authorization link is the only branded element on it. */
|
|
6394
|
-
.cc-loginLink{color:var(--dsw-alias-brand-primary);text-decoration:none;font-size:12px;line-height:1.5}
|
|
6601
|
+
.cc-footer{justify-content:flex-end;align-items:center;gap:8px;display:flex}
|
|
6602
|
+
.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}
|
|
6603
|
+
/* The authorization link is the only link-coloured element on a login row. */
|
|
6604
|
+
.cc-loginLink{color:var(--dsw-alias-link,var(--dsw-alias-brand-primary));text-decoration:none;font-size:12px;line-height:18px}
|
|
6395
6605
|
.cc-loginLink:hover{text-decoration:underline;text-underline-position:under}
|
|
6396
6606
|
.cc-loginDone{color:var(--dsw-alias-state-success-primary,var(--dsw-alias-label-secondary))}
|
|
6397
|
-
.cc-loginError{color:var(--dsw-alias-
|
|
6398
|
-
|
|
6399
|
-
.
|
|
6607
|
+
.cc-loginError{color:var(--dsw-alias-state-error-primary)}
|
|
6608
|
+
/* The floating save bar: a centered capsule pinned to the bottom of the
|
|
6609
|
+
* settings scrollport. The dock is a zero-height sticky strip at the end of
|
|
6610
|
+
* the page, so the bar overlays the scrolling content instead of taking a row
|
|
6611
|
+
* of its own; while it is shown the section reserves room at its end so the
|
|
6612
|
+
* last line can still scroll clear of it. Hidden, it sinks and fades, and
|
|
6613
|
+
* visibility (switched after the transition) takes its buttons out of the tab
|
|
6614
|
+
* order.
|
|
6615
|
+
*
|
|
6616
|
+
* Its surface is an elevated layer (bg-layer-1 over the settings panel's
|
|
6617
|
+
* layer-2) with the platform's prominent elevation and no stroke. The corners
|
|
6618
|
+
* are concentric: a 44px capsule around 36px capsule buttons inset by 4px, so
|
|
6619
|
+
* the gap around each button is even. */
|
|
6620
|
+
.cc-sectionWithBar{padding-bottom:80px}
|
|
6621
|
+
.cc-saveBarDock{position:sticky;bottom:0;z-index:2;height:0;pointer-events:none}
|
|
6622
|
+
.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}
|
|
6623
|
+
.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}
|
|
6624
|
+
.cc-saveBar-error{--cc-saveBar-tone:var(--dsw-alias-state-error-primary)}
|
|
6625
|
+
.cc-saveBar-success{--cc-saveBar-tone:var(--dsw-alias-state-success-primary,#16a34a);padding-right:18px}
|
|
6626
|
+
.cc-saveBarIcon{flex-shrink:0;align-items:center;justify-content:center;display:inline-flex;width:16px;height:16px;color:var(--cc-saveBar-tone)}
|
|
6627
|
+
.cc-saveBarPulse{width:8px;height:8px;border-radius:50%;corner-shape:round;background:var(--cc-saveBar-tone)}
|
|
6628
|
+
.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}
|
|
6629
|
+
.cc-saveBar-error .cc-saveBarText{color:var(--dsw-alias-state-error-primary)}
|
|
6630
|
+
.cc-saveBarActions{flex-shrink:0;align-items:center;gap:4px;display:flex;margin-left:8px}
|
|
6631
|
+
.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}
|
|
6632
|
+
.cc-saveBarButton:focus-visible{outline:none;box-shadow:0 0 0 2px var(--dsw-alias-border-l3)}
|
|
6633
|
+
.cc-saveBarButton:disabled{cursor:default;opacity:.4}
|
|
6634
|
+
.cc-saveBarGhost{background:0 0;color:var(--dsw-alias-label-primary)}
|
|
6635
|
+
.cc-saveBarGhost:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover)}
|
|
6636
|
+
.cc-saveBarPrimary{background:var(--dsw-alias-button-primary-fill,var(--dsw-alias-brand-primary));color:var(--dsw-alias-label-primary-foreground,#fff)}
|
|
6637
|
+
.cc-saveBarPrimary:hover:not(:disabled){background:var(--dsw-alias-button-primary-hover,var(--dsw-alias-button-primary-fill,var(--dsw-alias-brand-primary)))}
|
|
6638
|
+
@media (prefers-reduced-motion:reduce){.cc-chevron,.cc-toggle::after,.cc-usageBarFill,.cc-saveBar,.cc-saveBarShown,.cc-segmentIndicator,.cc-segment{transition:none}}
|
|
6400
6639
|
`;
|
|
6401
6640
|
//#endregion
|
|
6402
6641
|
//#region src/client/panel-styles.ts
|
|
@@ -6580,7 +6819,7 @@ select.cc-input{appearance:none;-webkit-appearance:none;-moz-appearance:none;box
|
|
|
6580
6819
|
const zh = {
|
|
6581
6820
|
nav: "Command Code",
|
|
6582
6821
|
title: "Command Code",
|
|
6583
|
-
intro: "
|
|
6822
|
+
intro: "账户操作立即生效;其余设置修改后点击底部「保存」。API 密钥仅保存在本机凭据服务中,不会回显。",
|
|
6584
6823
|
apiKey: "API 密钥",
|
|
6585
6824
|
apiKeyHint: "在 commandcode.ai 控制台创建。留空保存不会覆盖已存储的密钥。",
|
|
6586
6825
|
apiKeySet: "已配置",
|
|
@@ -6588,8 +6827,6 @@ select.cc-input{appearance:none;-webkit-appearance:none;-moz-appearance:none;box
|
|
|
6588
6827
|
apiKeyLocked: "密钥由只读来源提供",
|
|
6589
6828
|
apiBase: "API 地址",
|
|
6590
6829
|
apiBaseHint: "默认 https://api.commandcode.ai,一般无需修改。",
|
|
6591
|
-
workingDir: "工作目录",
|
|
6592
|
-
workingDirHint: "可选。留空时使用占位符显示的进程工作目录;仅在需要固定路径时填写。",
|
|
6593
6830
|
requestTimeoutMs: "请求超时(毫秒)",
|
|
6594
6831
|
requestTimeoutMsHint: "等待响应首个字节的超时;默认 60000。",
|
|
6595
6832
|
streamIdleTimeoutMs: "流空闲超时(毫秒)",
|
|
@@ -6597,47 +6834,29 @@ select.cc-input{appearance:none;-webkit-appearance:none;-moz-appearance:none;box
|
|
|
6597
6834
|
transportMaxRetries: "网络失败重试次数",
|
|
6598
6835
|
transportMaxRetriesHint: "连接失败时自动重试几次,默认 5(各次等待 0.5+1+2+4+8 秒,合计约 15 秒)。超过后直接报错,不再按指数退避长时间等待;填 0 表示不重试。限流、5xx 等由服务端要求重试的失败不受此项影响。",
|
|
6599
6836
|
advancedSettings: "高级设置",
|
|
6600
|
-
advancedSettingsHint: "API
|
|
6837
|
+
advancedSettingsHint: "API 地址和网络参数,一般无需修改。",
|
|
6601
6838
|
advancedOverriddenOne: "已自定义 1 项",
|
|
6602
6839
|
advancedOverriddenMany: "已自定义 {count} 项",
|
|
6603
6840
|
advancedInvalid: "高级设置中有未填好的数字,请展开修正后再保存。",
|
|
6604
6841
|
filterModelsByPlan: "隐藏套餐外模型",
|
|
6605
|
-
filterModelsByPlanHint: "
|
|
6842
|
+
filterModelsByPlanHint: "只显示套餐内模型;按需余额账户仍显示全部。",
|
|
6606
6843
|
webSearch: "用 Command Code 承载联网搜索",
|
|
6607
|
-
webSearchHint: "
|
|
6844
|
+
webSearchHint: "使用同一密钥承载 dsh 的 web_search;关闭后恢复原搜索后端。",
|
|
6608
6845
|
showSidebarQuota: "在侧边栏显示额度卡片",
|
|
6609
|
-
showSidebarQuotaHint: "
|
|
6846
|
+
showSidebarQuotaHint: "在侧边栏底部显示套餐与额度卡片,点击可打开完整仪表盘。默认关闭,关闭时不会为它后台刷新用量。",
|
|
6610
6847
|
commandGuard: "AI 命令安全预判",
|
|
6611
|
-
commandGuardHint: "命令确认弹窗前,先由 typesafe/jev
|
|
6612
|
-
|
|
6613
|
-
|
|
6614
|
-
|
|
6615
|
-
|
|
6848
|
+
commandGuardHint: "命令确认弹窗前,先由 typesafe/jev 判断它是否安全;沙箱提权还会额外判断范围与必要性。三项都足够确定才自动放行,其余照常弹窗。默认关闭。",
|
|
6849
|
+
commandGuardLevel: "自动放行阈值",
|
|
6850
|
+
commandGuardLevelHint: "阈值越高,需要越确定才放行,弹窗越多。高 = 0.95,中 = 0.9(默认),低 = 0.8。判定超过 3 秒未返回会照常弹窗。",
|
|
6851
|
+
commandGuardLevelHigh: "高",
|
|
6852
|
+
commandGuardLevelMedium: "中",
|
|
6853
|
+
commandGuardLevelLow: "低",
|
|
6616
6854
|
zdr: "零数据保留(ZDR)",
|
|
6617
|
-
zdrHint: "
|
|
6618
|
-
accountsTitle: "
|
|
6619
|
-
accountsHint: "
|
|
6855
|
+
zdrHint: "仅使用不留存、不训练的上游;可用模型可能更少且价格通常更高。",
|
|
6856
|
+
accountsTitle: "账户",
|
|
6857
|
+
accountsHint: "账户操作立即生效,无需保存。可以添加多个账户:当前账户达到用量限额或密钥失效时,请求会自动切换到下一个。",
|
|
6620
6858
|
accountAdd: "添加账户",
|
|
6621
|
-
accountRemove: "移除",
|
|
6622
|
-
accountLabel: "账户备注名",
|
|
6623
|
-
accountKey: "API 密钥",
|
|
6624
|
-
accountKeyHint: "该账户的 API 密钥。留空保存不会覆盖已存储的密钥。",
|
|
6625
|
-
accountLoginAfterSave: "先点击页面底部的「保存」,再点击该账户的「登录 Command Code」通过网页获取密钥。",
|
|
6626
6859
|
accountDefault: "默认账户",
|
|
6627
|
-
activeAccount: "当前使用账户",
|
|
6628
|
-
activeAccountAuto: "自动(第一个可用账户)",
|
|
6629
|
-
activeAccountHint: "手动指定优先使用的账户,保存后下次请求即生效;所选账户耗尽时仍会自动切换到其他可用账户。",
|
|
6630
|
-
rulesTitle: "按模型切换账户",
|
|
6631
|
-
rulesHint: "选择模型并路由到某个账户(可多选)。命中规则的模型且该账户可用时优先使用;账户耗尽或密钥失效时仍自动回落到其他账户。规则按列表顺序匹配,第一条命中生效。",
|
|
6632
|
-
rulesEmpty: "尚未配置规则。",
|
|
6633
|
-
rulesCatalogFailed: "模型目录获取失败,暂时无法选择模型;已保存的规则仍会生效。",
|
|
6634
|
-
ruleAdd: "添加规则",
|
|
6635
|
-
ruleRemove: "移除",
|
|
6636
|
-
ruleModel: "模型",
|
|
6637
|
-
ruleModelPick: "选择模型…",
|
|
6638
|
-
ruleModelCount: "已选 {count} 个模型",
|
|
6639
|
-
ruleAccount: "目标账户",
|
|
6640
|
-
ruleHint: "从下拉列表勾选要路由的模型(可多选),再选择目标账户。",
|
|
6641
6860
|
modelSearchPlaceholder: "搜索模型…",
|
|
6642
6861
|
modelSearchEmpty: "没有匹配的模型。",
|
|
6643
6862
|
modelStale: "已下架",
|
|
@@ -6654,20 +6873,19 @@ select.cc-input{appearance:none;-webkit-appearance:none;-moz-appearance:none;box
|
|
|
6654
6873
|
numberTooSmall: "不能小于 1(毫秒)",
|
|
6655
6874
|
numberTooLarge: "超出允许上限(2147483647 毫秒)",
|
|
6656
6875
|
readOnly: "当前配置为只读。",
|
|
6657
|
-
unsaved: "未保存",
|
|
6658
6876
|
save: "保存",
|
|
6659
6877
|
saving: "保存中",
|
|
6660
|
-
saved: "
|
|
6878
|
+
saved: "设置已保存",
|
|
6661
6879
|
saveFailed: "保存失败,请重试。",
|
|
6880
|
+
unsavedChanges: "有未保存的更改",
|
|
6881
|
+
saveInvalid: "有字段填写不正确,修正后才能保存",
|
|
6662
6882
|
discard: "放弃",
|
|
6663
6883
|
cancel: "取消",
|
|
6664
6884
|
show: "显示",
|
|
6665
6885
|
hide: "隐藏",
|
|
6666
|
-
usageTitle: "账户用量",
|
|
6667
6886
|
usageRefresh: "刷新",
|
|
6668
6887
|
usageRefreshing: "刷新中…",
|
|
6669
6888
|
usageLoading: "正在获取账户用量…",
|
|
6670
|
-
usageNoKey: "配置 API 密钥后,这里会显示账户的用量与额度状态。",
|
|
6671
6889
|
usageError: "用量获取失败",
|
|
6672
6890
|
usageRequests: "请求",
|
|
6673
6891
|
usageFailed: "失败",
|
|
@@ -6681,12 +6899,10 @@ select.cc-input{appearance:none;-webkit-appearance:none;-moz-appearance:none;box
|
|
|
6681
6899
|
usageFree: "赠送",
|
|
6682
6900
|
usageFiveHour: "5 小时窗口",
|
|
6683
6901
|
usageWeekly: "每周窗口",
|
|
6902
|
+
usageMonthlyLeft: "月额度剩余",
|
|
6684
6903
|
usageExceeded: "已超限",
|
|
6685
6904
|
usageReset: "重置于",
|
|
6686
6905
|
usagePartial: "部分端点数据不可用",
|
|
6687
|
-
usageKeyClear: "清除已存密钥",
|
|
6688
|
-
usageKeyClearStaged: "将清除(保存后生效)",
|
|
6689
|
-
usageUndoKeyClear: "撤销清除",
|
|
6690
6906
|
usageKeyInvalid: "API 密钥无效或已过期",
|
|
6691
6907
|
usageKeyInvalidHint: "服务端拒绝了全部请求(401)。请检查该账户配置的密钥,或到 commandcode.ai 控制台重新生成。",
|
|
6692
6908
|
usageServiceUnavailable: "Command Code 服务暂时不可用",
|
|
@@ -6704,8 +6920,7 @@ select.cc-input{appearance:none;-webkit-appearance:none;-moz-appearance:none;box
|
|
|
6704
6920
|
updateAvailable: "可更新",
|
|
6705
6921
|
updateHint: "已发布新版本,点击查看发布说明;更新插件后刷新本页,提示会自动消失。",
|
|
6706
6922
|
loginTitle: "通过官方登录获取密钥",
|
|
6707
|
-
loginHintIdle: "
|
|
6708
|
-
loginSaveBefore: "请先保存本页更改,再通过网页登录此账户。",
|
|
6923
|
+
loginHintIdle: "也可通过网页授权自动获取并保存密钥。",
|
|
6709
6924
|
loginButton: "登录 Command Code",
|
|
6710
6925
|
loginStarting: "正在启动本地回调服务…",
|
|
6711
6926
|
loginWaiting: "等待在浏览器中完成授权…",
|
|
@@ -6723,12 +6938,47 @@ select.cc-input{appearance:none;-webkit-appearance:none;-moz-appearance:none;box
|
|
|
6723
6938
|
cardTitle: "Command Code",
|
|
6724
6939
|
cardRouteActive: "已启用",
|
|
6725
6940
|
cardLoadingHint: "正在读取 Command Code 配置…",
|
|
6726
|
-
cardRegistrationHint: "
|
|
6941
|
+
cardRegistrationHint: "此卡片由 Command Code 插件提供,正在等待设置页装配,稍后即可显示完整内容。",
|
|
6942
|
+
accountsRotationHint: "账户操作立即生效。当前账户达到用量限额或密钥失效时,请求会按列表顺序自动切换到下一个可用账户。",
|
|
6943
|
+
accountModeAuto: "使用方式:自动切换(按列表顺序)",
|
|
6944
|
+
accountModePinned: "使用方式:固定使用「{name}」,不可用时仍会自动切换",
|
|
6945
|
+
accountStatusPinned: "已固定",
|
|
6946
|
+
accountActions: "账户操作",
|
|
6947
|
+
accountActionPin: "固定使用此账户",
|
|
6948
|
+
accountActionUnpin: "恢复自动切换",
|
|
6949
|
+
accountActionLogin: "网页登录获取新密钥",
|
|
6950
|
+
accountActionKey: "粘贴新密钥",
|
|
6951
|
+
accountActionRename: "重命名",
|
|
6952
|
+
accountActionClearKey: "删除密钥",
|
|
6953
|
+
accountActionRemove: "移除账户",
|
|
6954
|
+
accountRemoveConfirm: "移除「{name}」?它保存的密钥和专用模型设置会一起删除。",
|
|
6955
|
+
accountClearKeyConfirm: "删除默认账户保存的密钥?之后会回退到环境变量或官方 CLI 的登录信息。",
|
|
6956
|
+
accountConfirmRemove: "移除",
|
|
6957
|
+
accountConfirmClear: "删除",
|
|
6958
|
+
accountApply: "确定",
|
|
6959
|
+
accountNoKeyHint: "还没有密钥:通过网页登录自动获取,或粘贴在 commandcode.ai 控制台创建的 API 密钥。",
|
|
6960
|
+
accountAddHint: "网页登录会打开 Command Code 授权页,授权完成后账户自动添加;也可以直接粘贴 API 密钥。",
|
|
6961
|
+
accountAddLogin: "网页登录",
|
|
6962
|
+
accountAddPaste: "粘贴密钥",
|
|
6963
|
+
accountAddConfirm: "添加",
|
|
6964
|
+
accountNamePlaceholder: "备注名(可选,网页登录时默认使用账户名)",
|
|
6965
|
+
accountNameN: "账户 {n}",
|
|
6966
|
+
accountKeyPlaceholder: "粘贴 API 密钥",
|
|
6967
|
+
accountOpFailed: "账户操作未能完成,请重试。",
|
|
6968
|
+
accountModels: "专用模型",
|
|
6969
|
+
accountModelsHint: "选中的模型优先由此账户处理,此账户不可用时仍会自动切换。一个模型只属于一个账户,选中会把它从其他账户移过来。",
|
|
6970
|
+
accountModelOwner: "属于 {name}",
|
|
6971
|
+
modelPick: "选择模型…",
|
|
6972
|
+
modelCount: "已选 {count} 个模型",
|
|
6973
|
+
modelCatalogFailed: "模型目录获取失败,暂时无法选择模型;已保存的设置仍会生效。",
|
|
6974
|
+
modelsTitle: "模型",
|
|
6975
|
+
privacyTitle: "隐私与安全",
|
|
6976
|
+
integrationsTitle: "集成与显示"
|
|
6727
6977
|
};
|
|
6728
6978
|
const en = {
|
|
6729
6979
|
nav: "Command Code",
|
|
6730
6980
|
title: "Command Code",
|
|
6731
|
-
intro: "
|
|
6981
|
+
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.",
|
|
6732
6982
|
apiKey: "API key",
|
|
6733
6983
|
apiKeyHint: "Create one in the commandcode.ai console. Saving with this field blank keeps the stored key.",
|
|
6734
6984
|
apiKeySet: "Configured",
|
|
@@ -6736,8 +6986,6 @@ select.cc-input{appearance:none;-webkit-appearance:none;-moz-appearance:none;box
|
|
|
6736
6986
|
apiKeyLocked: "Key provided by a read-only source",
|
|
6737
6987
|
apiBase: "API base URL",
|
|
6738
6988
|
apiBaseHint: "Defaults to https://api.commandcode.ai; usually leave as-is.",
|
|
6739
|
-
workingDir: "Working directory",
|
|
6740
|
-
workingDirHint: "Optional. Leave blank to use the process cwd shown as the placeholder; fill in only to pin a specific path.",
|
|
6741
6989
|
requestTimeoutMs: "Request timeout (ms)",
|
|
6742
6990
|
requestTimeoutMsHint: "Time to wait for the first response byte; default 60000.",
|
|
6743
6991
|
streamIdleTimeoutMs: "Stream idle timeout (ms)",
|
|
@@ -6745,47 +6993,29 @@ select.cc-input{appearance:none;-webkit-appearance:none;-moz-appearance:none;box
|
|
|
6745
6993
|
transportMaxRetries: "Transport retries",
|
|
6746
6994
|
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.",
|
|
6747
6995
|
advancedSettings: "Advanced",
|
|
6748
|
-
advancedSettingsHint: "
|
|
6996
|
+
advancedSettingsHint: "API base URL and network limits; usually leave as-is.",
|
|
6749
6997
|
advancedOverriddenOne: "1 customized",
|
|
6750
6998
|
advancedOverriddenMany: "{count} customized",
|
|
6751
6999
|
advancedInvalid: "A number in Advanced settings is not ready to save; expand to fix it.",
|
|
6752
7000
|
filterModelsByPlan: "Hide out-of-plan models",
|
|
6753
|
-
filterModelsByPlanHint: "
|
|
7001
|
+
filterModelsByPlanHint: "Show only models in your plan; on-demand credit accounts still see the full catalog.",
|
|
6754
7002
|
webSearch: "Serve dsh web search with Command Code",
|
|
6755
|
-
webSearchHint: "
|
|
7003
|
+
webSearchHint: "Use the same key for dsh web search; turning it off restores the previous backend.",
|
|
6756
7004
|
showSidebarQuota: "Show the quota card in the sidebar",
|
|
6757
|
-
showSidebarQuotaHint: "
|
|
7005
|
+
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.",
|
|
6758
7006
|
commandGuard: "AI command guard",
|
|
6759
|
-
commandGuardHint: "Before a shell command asks for approval, typesafe/jev judges whether it is safe
|
|
6760
|
-
|
|
6761
|
-
|
|
6762
|
-
|
|
6763
|
-
|
|
7007
|
+
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.",
|
|
7008
|
+
commandGuardLevel: "Auto-approve threshold",
|
|
7009
|
+
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.",
|
|
7010
|
+
commandGuardLevelHigh: "High",
|
|
7011
|
+
commandGuardLevelMedium: "Medium",
|
|
7012
|
+
commandGuardLevelLow: "Low",
|
|
6764
7013
|
zdr: "Zero data retention (ZDR)",
|
|
6765
|
-
zdrHint: "
|
|
6766
|
-
accountsTitle: "
|
|
6767
|
-
accountsHint: "
|
|
7014
|
+
zdrHint: "Use only upstreams that retain no data and do not train on requests; availability and pricing may differ.",
|
|
7015
|
+
accountsTitle: "Accounts",
|
|
7016
|
+
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.",
|
|
6768
7017
|
accountAdd: "Add account",
|
|
6769
|
-
accountRemove: "Remove",
|
|
6770
|
-
accountLabel: "Account label",
|
|
6771
|
-
accountKey: "API key",
|
|
6772
|
-
accountKeyHint: "This account’s API key. Saving with the field blank keeps the stored key.",
|
|
6773
|
-
accountLoginAfterSave: "Use Save at the bottom of the page, then sign in to this account in your browser to obtain a key.",
|
|
6774
7018
|
accountDefault: "Default account",
|
|
6775
|
-
activeAccount: "Active account",
|
|
6776
|
-
activeAccountAuto: "Auto (first usable account)",
|
|
6777
|
-
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.",
|
|
6778
|
-
rulesTitle: "Route models to accounts",
|
|
6779
|
-
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.",
|
|
6780
|
-
rulesEmpty: "No rules yet.",
|
|
6781
|
-
rulesCatalogFailed: "Could not load the model catalog — selecting models is unavailable; saved rules still apply.",
|
|
6782
|
-
ruleAdd: "Add rule",
|
|
6783
|
-
ruleRemove: "Remove",
|
|
6784
|
-
ruleModel: "Models",
|
|
6785
|
-
ruleModelPick: "Select models…",
|
|
6786
|
-
ruleModelCount: "{count} model(s) selected",
|
|
6787
|
-
ruleAccount: "Target account",
|
|
6788
|
-
ruleHint: "Check the models to route from the dropdown (multi-select), then pick the target account.",
|
|
6789
7019
|
modelSearchPlaceholder: "Search models…",
|
|
6790
7020
|
modelSearchEmpty: "No matching models.",
|
|
6791
7021
|
modelStale: "Retired",
|
|
@@ -6802,20 +7032,19 @@ select.cc-input{appearance:none;-webkit-appearance:none;-moz-appearance:none;box
|
|
|
6802
7032
|
numberTooSmall: "Must be at least 1 (ms)",
|
|
6803
7033
|
numberTooLarge: "Above the allowed maximum (2147483647 ms)",
|
|
6804
7034
|
readOnly: "Settings are read-only.",
|
|
6805
|
-
unsaved: "Unsaved",
|
|
6806
7035
|
save: "Save",
|
|
6807
7036
|
saving: "Saving",
|
|
6808
|
-
saved: "
|
|
7037
|
+
saved: "Settings saved",
|
|
6809
7038
|
saveFailed: "Save failed, please retry.",
|
|
7039
|
+
unsavedChanges: "You have unsaved changes",
|
|
7040
|
+
saveInvalid: "Fix the highlighted field before saving",
|
|
6810
7041
|
discard: "Discard",
|
|
6811
7042
|
cancel: "Cancel",
|
|
6812
7043
|
show: "Show",
|
|
6813
7044
|
hide: "Hide",
|
|
6814
|
-
usageTitle: "Account usage",
|
|
6815
7045
|
usageRefresh: "Refresh",
|
|
6816
7046
|
usageRefreshing: "Refreshing…",
|
|
6817
7047
|
usageLoading: "Fetching account usage…",
|
|
6818
|
-
usageNoKey: "Configure an API key to see this account’s usage and credit state here.",
|
|
6819
7048
|
usageError: "Could not fetch usage",
|
|
6820
7049
|
usageRequests: "Requests",
|
|
6821
7050
|
usageFailed: "failed",
|
|
@@ -6829,12 +7058,10 @@ select.cc-input{appearance:none;-webkit-appearance:none;-moz-appearance:none;box
|
|
|
6829
7058
|
usageFree: "Free",
|
|
6830
7059
|
usageFiveHour: "5-hour window",
|
|
6831
7060
|
usageWeekly: "Weekly window",
|
|
7061
|
+
usageMonthlyLeft: "Monthly left",
|
|
6832
7062
|
usageExceeded: "Exceeded",
|
|
6833
7063
|
usageReset: "Resets",
|
|
6834
7064
|
usagePartial: "Some endpoint data unavailable",
|
|
6835
|
-
usageKeyClear: "Clear stored key",
|
|
6836
|
-
usageKeyClearStaged: "Will be cleared on save",
|
|
6837
|
-
usageUndoKeyClear: "Undo clear",
|
|
6838
7065
|
usageKeyInvalid: "API key invalid or expired",
|
|
6839
7066
|
usageKeyInvalidHint: "The server rejects every request (401). Check the key configured for this account, or generate a new one in the commandcode.ai console.",
|
|
6840
7067
|
usageServiceUnavailable: "The Command Code service is temporarily unavailable",
|
|
@@ -6852,8 +7079,7 @@ select.cc-input{appearance:none;-webkit-appearance:none;-moz-appearance:none;box
|
|
|
6852
7079
|
updateAvailable: "update available",
|
|
6853
7080
|
updateHint: "A newer version has been published; click for release notes. The notice disappears once the plugin is updated.",
|
|
6854
7081
|
loginTitle: "Sign in to fetch a key",
|
|
6855
|
-
loginHintIdle: "
|
|
6856
|
-
loginSaveBefore: "Save page changes before signing in to this account.",
|
|
7082
|
+
loginHintIdle: "Alternatively, sign in to fetch and store a key automatically.",
|
|
6857
7083
|
loginButton: "Sign in to Command Code",
|
|
6858
7084
|
loginStarting: "Starting the local callback server…",
|
|
6859
7085
|
loginWaiting: "Waiting for authorization in your browser…",
|
|
@@ -6871,7 +7097,42 @@ select.cc-input{appearance:none;-webkit-appearance:none;-moz-appearance:none;box
|
|
|
6871
7097
|
cardTitle: "Command Code",
|
|
6872
7098
|
cardRouteActive: "Active",
|
|
6873
7099
|
cardLoadingHint: "Loading the Command Code configuration…",
|
|
6874
|
-
cardRegistrationHint: "This card is contributed by the Command Code plugin
|
|
7100
|
+
cardRegistrationHint: "This card is contributed by the Command Code plugin and is waiting for the settings page to finish assembling.",
|
|
7101
|
+
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.",
|
|
7102
|
+
accountModeAuto: "Mode: switch automatically (list order)",
|
|
7103
|
+
accountModePinned: "Mode: pinned to \"{name}\", still switching when it is unavailable",
|
|
7104
|
+
accountStatusPinned: "Pinned",
|
|
7105
|
+
accountActions: "Account actions",
|
|
7106
|
+
accountActionPin: "Pin this account",
|
|
7107
|
+
accountActionUnpin: "Switch automatically",
|
|
7108
|
+
accountActionLogin: "Sign in for a new key",
|
|
7109
|
+
accountActionKey: "Paste a new key",
|
|
7110
|
+
accountActionRename: "Rename",
|
|
7111
|
+
accountActionClearKey: "Delete key",
|
|
7112
|
+
accountActionRemove: "Remove account",
|
|
7113
|
+
accountRemoveConfirm: "Remove \"{name}\"? Its stored key and dedicated models are deleted with it.",
|
|
7114
|
+
accountClearKeyConfirm: "Delete the default account's stored key? The provider then falls back to the environment or the official CLI sign-in.",
|
|
7115
|
+
accountConfirmRemove: "Remove",
|
|
7116
|
+
accountConfirmClear: "Delete",
|
|
7117
|
+
accountApply: "Apply",
|
|
7118
|
+
accountNoKeyHint: "No key yet: sign in through the browser, or paste an API key created in the commandcode.ai console.",
|
|
7119
|
+
accountAddHint: "Sign-in opens the Command Code authorization page and adds the account once you approve; you can also paste an API key.",
|
|
7120
|
+
accountAddLogin: "Sign in",
|
|
7121
|
+
accountAddPaste: "Paste key",
|
|
7122
|
+
accountAddConfirm: "Add",
|
|
7123
|
+
accountNamePlaceholder: "Name (optional; sign-in uses the account name)",
|
|
7124
|
+
accountNameN: "Account {n}",
|
|
7125
|
+
accountKeyPlaceholder: "Paste an API key",
|
|
7126
|
+
accountOpFailed: "The account change did not complete. Try again.",
|
|
7127
|
+
accountModels: "Dedicated models",
|
|
7128
|
+
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.",
|
|
7129
|
+
accountModelOwner: "on {name}",
|
|
7130
|
+
modelPick: "Choose models…",
|
|
7131
|
+
modelCount: "{count} models selected",
|
|
7132
|
+
modelCatalogFailed: "The model catalog could not be loaded; saved settings still apply.",
|
|
7133
|
+
modelsTitle: "Models",
|
|
7134
|
+
privacyTitle: "Privacy & security",
|
|
7135
|
+
integrationsTitle: "Integrations & display"
|
|
6875
7136
|
};
|
|
6876
7137
|
//#endregion
|
|
6877
7138
|
//#region src/client/index.ts
|
|
@@ -6911,15 +7172,6 @@ select.cc-input{appearance:none;-webkit-appearance:none;-moz-appearance:none;box
|
|
|
6911
7172
|
*/
|
|
6912
7173
|
const PANEL_ID = "commandcode-panel";
|
|
6913
7174
|
/**
|
|
6914
|
-
* ui-conversation's reserved `main` key, used only as the exit fallback for a
|
|
6915
|
-
* layout whose `selectPanel` predates the `null` "show the Conversation"
|
|
6916
|
-
* selection (see `close` in {@link applyClientSurfaces}). Declared locally for
|
|
6917
|
-
* the same reason as {@link PANEL_ID}: ui-conversation is not a dependency of
|
|
6918
|
-
* this bundle, and the key is a published contract of the layout (its README
|
|
6919
|
-
* names `conversation` as reserved for the Conversation).
|
|
6920
|
-
*/
|
|
6921
|
-
const CONVERSATION_PANEL_ID = "conversation";
|
|
6922
|
-
/**
|
|
6923
7175
|
* The composer figure's entry id in `conversation.composer.dock`. Its own id,
|
|
6924
7176
|
* not the shipped `stats` cell's: reusing `stats` would REPLACE the tokens /
|
|
6925
7177
|
* cache-hit / throughput readout rather than inject into it, and that readout
|
|
@@ -6927,12 +7179,10 @@ select.cc-input{appearance:none;-webkit-appearance:none;-moz-appearance:none;box
|
|
|
6927
7179
|
*/
|
|
6928
7180
|
const SESSION_COST_ID = "commandcode-session-cost";
|
|
6929
7181
|
/**
|
|
6930
|
-
* Client plugin body. Gates on
|
|
6931
|
-
*
|
|
6932
|
-
*
|
|
6933
|
-
*
|
|
6934
|
-
* appears; legacy clients mount it from the connection ApiProxy credential
|
|
6935
|
-
* face.
|
|
7182
|
+
* Client plugin body. Gates on `slots`, `locale` and `remote` — never on
|
|
7183
|
+
* `settingsScope`, whose wrapper service the 0.1.7 settings rewrite removed
|
|
7184
|
+
* (the settings scope here speaks the `remote.settings` wire directly). The
|
|
7185
|
+
* page mounts once `remote.credentials` appears.
|
|
6936
7186
|
*/
|
|
6937
7187
|
function apply(ctx) {
|
|
6938
7188
|
injectPageCss();
|
|
@@ -6944,19 +7194,13 @@ select.cc-input{appearance:none;-webkit-appearance:none;-moz-appearance:none;box
|
|
|
6944
7194
|
zh: PANEL_COPY_ZH,
|
|
6945
7195
|
en: PANEL_COPY_EN
|
|
6946
7196
|
}), "dsh-commandcode-provider: panel copy");
|
|
6947
|
-
const connection = ctx.get("connection");
|
|
6948
|
-
const legacyApi = adaptLegacyCredentials(connection?.api?.credentials);
|
|
6949
|
-
if (legacyApi !== void 0) {
|
|
6950
|
-
applyClientSurfaces(ctx, legacyApi, connection?.hostDescription);
|
|
6951
|
-
return;
|
|
6952
|
-
}
|
|
6953
7197
|
ctx.inject(["remote.credentials"], (remoteCtx) => {
|
|
6954
7198
|
const credentials = remoteCtx.remote.credentials;
|
|
6955
7199
|
applyClientSurfaces(remoteCtx, { credentials });
|
|
6956
7200
|
});
|
|
6957
7201
|
}
|
|
6958
|
-
/** Mount the one shared UI implementation
|
|
6959
|
-
function applyClientSurfaces(ctx, api
|
|
7202
|
+
/** Mount the one shared UI implementation. */
|
|
7203
|
+
function applyClientSurfaces(ctx, api) {
|
|
6960
7204
|
let settingsNamespace;
|
|
6961
7205
|
const scope = createSettingsScope(ctx, COMMANDCODE_NS, () => settingsNamespace);
|
|
6962
7206
|
ctx.effect(() => () => {
|
|
@@ -6976,7 +7220,7 @@ select.cc-input{appearance:none;-webkit-appearance:none;-moz-appearance:none;box
|
|
|
6976
7220
|
ok: false,
|
|
6977
7221
|
error: { message: "commandcode/models remote is not mounted" }
|
|
6978
7222
|
})
|
|
6979
|
-
}
|
|
7223
|
+
});
|
|
6980
7224
|
ctx.effect(() => () => controller.dispose(), "dsh-commandcode-provider: settings controller");
|
|
6981
7225
|
const store = createSnapshotStore(controller.state());
|
|
6982
7226
|
controller.subscribe(() => store.set(controller.state()));
|
|
@@ -7103,6 +7347,10 @@ select.cc-input{appearance:none;-webkit-appearance:none;-moz-appearance:none;box
|
|
|
7103
7347
|
lastLoginPhase = phase;
|
|
7104
7348
|
loginStore.set(loginController.state());
|
|
7105
7349
|
});
|
|
7350
|
+
const refreshUsageOn = (ok) => {
|
|
7351
|
+
if (ok) usageController.refresh();
|
|
7352
|
+
return ok;
|
|
7353
|
+
};
|
|
7106
7354
|
const injected = () => ({
|
|
7107
7355
|
hooks: {
|
|
7108
7356
|
commandCodeSettings: store,
|
|
@@ -7119,15 +7367,16 @@ select.cc-input{appearance:none;-webkit-appearance:none;-moz-appearance:none;box
|
|
|
7119
7367
|
refreshUsage: () => void usageController.refresh(),
|
|
7120
7368
|
beginLogin: (targetRef) => void loginController.begin(targetRef),
|
|
7121
7369
|
cancelLogin: () => void loginController.cancel(),
|
|
7122
|
-
|
|
7123
|
-
|
|
7124
|
-
|
|
7125
|
-
|
|
7126
|
-
|
|
7127
|
-
|
|
7128
|
-
|
|
7129
|
-
|
|
7130
|
-
|
|
7370
|
+
createAccount: (input) => controller.createAccount(input).then((ref) => {
|
|
7371
|
+
if (ref !== void 0 && input.key) usageController.refresh();
|
|
7372
|
+
return ref;
|
|
7373
|
+
}),
|
|
7374
|
+
renameAccount: (ref, label) => controller.renameAccount(ref, label),
|
|
7375
|
+
removeAccount: (ref) => controller.removeAccount(ref).then(refreshUsageOn),
|
|
7376
|
+
setAccountKey: (target, key) => controller.setAccountKey(target, key).then(refreshUsageOn),
|
|
7377
|
+
clearAccountKey: (target) => controller.clearAccountKey(target).then(refreshUsageOn),
|
|
7378
|
+
setActiveAccount: (id) => controller.setActiveAccount(id).then(refreshUsageOn),
|
|
7379
|
+
setAccountModels: (target, ids) => controller.setAccountModels(target, ids),
|
|
7131
7380
|
editVisibleModels: (ids) => controller.editVisibleModels(ids),
|
|
7132
7381
|
clearVisibleModels: () => controller.clearVisibleModels()
|
|
7133
7382
|
});
|
|
@@ -7169,21 +7418,10 @@ select.cc-input{appearance:none;-webkit-appearance:none;-moz-appearance:none;box
|
|
|
7169
7418
|
},
|
|
7170
7419
|
startAutoRefresh: () => startPanelAutoRefresh(usageController, () => true),
|
|
7171
7420
|
open: () => {
|
|
7172
|
-
|
|
7173
|
-
if (typeof layout?.selectPanel === "function") layout.selectPanel(PANEL_ID);
|
|
7421
|
+
ctx.get("layout")?.selectPanel(PANEL_ID);
|
|
7174
7422
|
},
|
|
7175
7423
|
close: () => {
|
|
7176
|
-
|
|
7177
|
-
if (typeof layout?.selectPanel !== "function") return;
|
|
7178
|
-
try {
|
|
7179
|
-
layout.selectPanel(null);
|
|
7180
|
-
} catch {
|
|
7181
|
-
try {
|
|
7182
|
-
layout.selectPanel(CONVERSATION_PANEL_ID);
|
|
7183
|
-
} catch (error) {
|
|
7184
|
-
console.error("[dsh-commandcode-provider] could not close the plans & quota panel:", error);
|
|
7185
|
-
}
|
|
7186
|
-
}
|
|
7424
|
+
ctx.get("layout")?.selectPanel(null);
|
|
7187
7425
|
}
|
|
7188
7426
|
});
|
|
7189
7427
|
ctx.effect(() => injectPanelCss(), "dsh-commandcode-provider: panel styles");
|
|
@@ -7198,7 +7436,6 @@ select.cc-input{appearance:none;-webkit-appearance:none;-moz-appearance:none;box
|
|
|
7198
7436
|
console.error("[dsh-commandcode-provider] could not register the plans & quota panel:", error);
|
|
7199
7437
|
}
|
|
7200
7438
|
ctx.inject(["layout"], (layoutCtx) => {
|
|
7201
|
-
if (typeof layoutCtx.get("layout")?.selectPanel !== "function") return;
|
|
7202
7439
|
try {
|
|
7203
7440
|
layoutCtx.slots.inject("sidebar.footer.action", () => layoutCtx.slots.register({
|
|
7204
7441
|
name: "sidebar.footer.action",
|
|
@@ -7225,7 +7462,6 @@ select.cc-input{appearance:none;-webkit-appearance:none;-moz-appearance:none;box
|
|
|
7225
7462
|
const inject = [
|
|
7226
7463
|
"slots",
|
|
7227
7464
|
"locale",
|
|
7228
|
-
"connection",
|
|
7229
7465
|
"remote"
|
|
7230
7466
|
];
|
|
7231
7467
|
//#endregion
|