dsh-connect-workbuddy 3.0.2 → 3.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +190 -0
- package/README.en.md +49 -20
- package/README.md +49 -22
- package/THIRD_PARTY_NOTICES.md +4 -2
- package/docs/WINDOWS.md +1 -0
- package/docs/assets/account-pool-demo.html +207 -262
- package/lib/bin.js +23 -3
- package/lib/client.js +377 -755
- package/lib/{host-heartbeat-Dwmfxuyw.js → host-heartbeat-t93cLCoW.js} +238 -4
- package/lib/index.d.ts +140 -32
- package/lib/index.js +1409 -265
- package/package.json +1 -1
package/lib/client.js
CHANGED
|
@@ -22,12 +22,19 @@ window.__ModuleLoader__.load({
|
|
|
22
22
|
*/
|
|
23
23
|
/** Plugin-owned usage endpoint consumed by its browser half. */
|
|
24
24
|
const WORKBUDDY_USAGE_PATH = "/plugins/dsh-connect-workbuddy/usage";
|
|
25
|
+
/**
|
|
26
|
+
* The project's public repository.
|
|
27
|
+
*
|
|
28
|
+
* Lives here rather than beside one component because TWO sections link to it
|
|
29
|
+
* now — the model section and the account pool — and a second copy of a URL is
|
|
30
|
+
* a second thing to forget when it moves. Node-free and side-effect free, so it
|
|
31
|
+
* costs nothing on the Host side either.
|
|
32
|
+
*/
|
|
33
|
+
const WORKBUDDY_GITHUB_URL = "https://github.com/dingminhua/dsh-connect-workbuddy";
|
|
25
34
|
/** Plugin-owned live model refresh endpoint. */
|
|
26
35
|
const WORKBUDDY_MODELS_REFRESH_PATH = "/plugins/dsh-connect-workbuddy/models/refresh";
|
|
27
36
|
/** Plugin-owned local account rescan endpoint. */
|
|
28
37
|
const WORKBUDDY_ACCOUNTS_REFRESH_PATH = "/plugins/dsh-connect-workbuddy/accounts/refresh";
|
|
29
|
-
/** Plugin-owned daily check-in action endpoint. */
|
|
30
|
-
const WORKBUDDY_CHECKIN_PATH = "/plugins/dsh-connect-workbuddy/checkin";
|
|
31
38
|
/**
|
|
32
39
|
* Plugin-owned model probe endpoint.
|
|
33
40
|
*
|
|
@@ -501,7 +508,7 @@ window.__ModuleLoader__.load({
|
|
|
501
508
|
*
|
|
502
509
|
* @param scope - the bound settings scope for this plugin's namespace.
|
|
503
510
|
* @param region - the region whose pool preferences are written.
|
|
504
|
-
* @param preferences - the
|
|
511
|
+
* @param preferences - the preference fields, as the user set them.
|
|
505
512
|
* @param slot - the region's COMPLETE next slot (caller owns the merge).
|
|
506
513
|
* @throws {WorkBuddySettingsWriteError} when neither writer persists the value.
|
|
507
514
|
*/
|
|
@@ -510,7 +517,7 @@ window.__ModuleLoader__.load({
|
|
|
510
517
|
await writeField(scope, "regions", region, next, (readBack) => {
|
|
511
518
|
const pool = readBack?.pool;
|
|
512
519
|
if (pool === void 0 || pool === null) return false;
|
|
513
|
-
if (pool["enabled"] !== preferences.enabled || pool["
|
|
520
|
+
if (pool["enabled"] !== preferences.enabled || pool["targetModelId"] !== preferences.targetModelId) return false;
|
|
514
521
|
const stored = pool["memberAccountIds"];
|
|
515
522
|
if (!Array.isArray(stored) || stored.length !== preferences.memberAccountIds.length) return false;
|
|
516
523
|
const wanted = new Set(preferences.memberAccountIds);
|
|
@@ -582,28 +589,6 @@ window.__ModuleLoader__.load({
|
|
|
582
589
|
* @module dsh-connect-workbuddy/client/pool-state
|
|
583
590
|
*/
|
|
584
591
|
/**
|
|
585
|
-
* Whether rotation currently owns the region's billing choice, and whether the
|
|
586
|
-
* user has drafted turning it off.
|
|
587
|
-
*
|
|
588
|
-
* `locked` reads the COMMITTED preferences, never the draft. The Host decides
|
|
589
|
-
* who is billed from the saved config, so a draft-based lock let one click of
|
|
590
|
-
* "turn off rotation" re-enable the manual dropdown and hide the conflict
|
|
591
|
-
* notice while the Host kept rotating — the user then picked an account, the
|
|
592
|
-
* card showed it as current, and a different account was billed. That is
|
|
593
|
-
* exactly the failure the lock exists to prevent.
|
|
594
|
-
*
|
|
595
|
-
* `unlockPending` is the honest intermediate state: the draft says "stop
|
|
596
|
-
* rotating" but the Host has not been told yet, so the lock still applies and
|
|
597
|
-
* the UI must say so rather than looking broken.
|
|
598
|
-
*/
|
|
599
|
-
function rotationLockState(saved, active) {
|
|
600
|
-
const locked = saved.enabled && saved.rotateByCredits;
|
|
601
|
-
return {
|
|
602
|
-
locked,
|
|
603
|
-
unlockPending: locked && !(active.enabled && active.rotateByCredits)
|
|
604
|
-
};
|
|
605
|
-
}
|
|
606
|
-
/**
|
|
607
592
|
* The base a draft edit starts from.
|
|
608
593
|
*
|
|
609
594
|
* Resolved at EDIT time rather than captured when the callback was built. The
|
|
@@ -667,7 +652,6 @@ window.__ModuleLoader__.load({
|
|
|
667
652
|
function announcedBatchCount(input) {
|
|
668
653
|
return (input.savedEffective ?? input.savedMembers).length;
|
|
669
654
|
}
|
|
670
|
-
const POOL_INTERVAL_MAX = 1440;
|
|
671
655
|
/**
|
|
672
656
|
* Localize a pool route failure from its structured cause.
|
|
673
657
|
*
|
|
@@ -689,11 +673,11 @@ window.__ModuleLoader__.load({
|
|
|
689
673
|
*/
|
|
690
674
|
function poolErrorText(t, reason, detail) {
|
|
691
675
|
switch (reason) {
|
|
692
|
-
case "pool-disabled": return t("row.poolErrDisabled");
|
|
693
676
|
case "no-members": return t("row.poolErrNoMembers");
|
|
694
677
|
case "no-live-members": return t("row.poolErrNoLiveMembers");
|
|
695
678
|
case "no-free-model": return t("row.poolErrNoFreeModel");
|
|
696
679
|
case "target-model-stale": return t("row.poolErrStaleModel");
|
|
680
|
+
case "checkin-unsupported": return t("row.poolErrNoCheckin");
|
|
697
681
|
case "pool-unavailable": return t("row.poolErrUnavailable");
|
|
698
682
|
case "pool-failed": return t("row.poolErrFailed", { message: detail ?? "" });
|
|
699
683
|
default: return;
|
|
@@ -722,46 +706,6 @@ window.__ModuleLoader__.load({
|
|
|
722
706
|
return poolErrorText(t, input.reason, input.error) ?? input.error ?? t("row.poolErrHttp", { status: String(input.status) });
|
|
723
707
|
}
|
|
724
708
|
/**
|
|
725
|
-
* Parse an in-progress interval edit.
|
|
726
|
-
*
|
|
727
|
-
* Returns `undefined` while the text is not yet a plain non-negative integer —
|
|
728
|
-
* empty, `-`, `12e`, `1.5` — so the caller can leave EXACTLY what the user typed
|
|
729
|
-
* on screen instead of rewriting the control from a parsed number.
|
|
730
|
-
*
|
|
731
|
-
* That rewriting was the defect this replaces: the field clamped on every
|
|
732
|
-
* keystroke and wrote the clamped result back into a controlled `value`, so
|
|
733
|
-
* typing `120` produced `520` (each keystroke's clamped `5` was rendered, and the
|
|
734
|
-
* next digit appended to it) and clearing the field immediately snapped it to
|
|
735
|
-
* `5`. The user's original request was "设置一个时间间隔自动测试" — a field that
|
|
736
|
-
* cannot be typed into is that requirement unmet, not a cosmetic wart.
|
|
737
|
-
*/
|
|
738
|
-
function parseIntervalInput(raw) {
|
|
739
|
-
const trimmed = raw.trim();
|
|
740
|
-
if (!/^[0-9]+$/.test(trimmed)) return void 0;
|
|
741
|
-
const parsed = Number(trimmed);
|
|
742
|
-
if (!Number.isFinite(parsed)) return void 0;
|
|
743
|
-
return Math.min(POOL_INTERVAL_MAX, Math.max(5, Math.round(parsed)));
|
|
744
|
-
}
|
|
745
|
-
/**
|
|
746
|
-
* One keystroke in the interval field: what to DISPLAY, and what to commit.
|
|
747
|
-
*
|
|
748
|
-
* `text` is ALWAYS the raw keystroke, never a re-serialized number — that
|
|
749
|
-
* distinction is the whole defect. A field whose displayed value is derived from
|
|
750
|
-
* the parsed-and-clamped number cannot be typed into: each keystroke's clamped
|
|
751
|
-
* `5` is rendered, and the next digit appends to it (`120` → `520`).
|
|
752
|
-
*
|
|
753
|
-
* `commit` is the clamped number to fold into the draft, or `undefined` while the
|
|
754
|
-
* text is not yet a plain integer (so the draft is left alone until the entry is
|
|
755
|
-
* meaningful). Splitting the two makes the invariant unit-testable without a DOM,
|
|
756
|
-
* which this project has no way to render.
|
|
757
|
-
*/
|
|
758
|
-
function intervalEditOnInput(raw) {
|
|
759
|
-
return {
|
|
760
|
-
text: raw,
|
|
761
|
-
commit: parseIntervalInput(raw)
|
|
762
|
-
};
|
|
763
|
-
}
|
|
764
|
-
/**
|
|
765
709
|
* Pool MEMBERS that can serve right now.
|
|
766
710
|
*
|
|
767
711
|
* Judged over members only. Using the whole account table made the "no usable
|
|
@@ -785,15 +729,15 @@ window.__ModuleLoader__.load({
|
|
|
785
729
|
* 改动:把「账号池」这一整块独立成模块。原因是它有三条别的区块没有的规则,
|
|
786
730
|
* 放在卡片里会被淹没:
|
|
787
731
|
*
|
|
788
|
-
* 1.
|
|
789
|
-
*
|
|
790
|
-
*
|
|
791
|
-
*
|
|
792
|
-
*
|
|
793
|
-
* 把它们混在一起,一次「保存」就会用旧草稿覆盖刚测出的结果。
|
|
794
|
-
* 3. **免费模型可能不存在。** 国内版静态目录不带倍率,所以「自动挑免费模型」
|
|
732
|
+
* 1. **一种状态、两套写入。** 偏好(启用、目标模型、成员)走草稿 + 保存
|
|
733
|
+
* (保存成功才丢弃草稿);**测试结果**是运行时事实,由 Host 在批量测试后
|
|
734
|
+
* 自动落盘,绝不经这个保存按钮。把它们混在一起,一次「保存」就会用旧草稿
|
|
735
|
+
* 覆盖刚测出的结果。
|
|
736
|
+
* 2. **免费模型可能不存在。** 国内版静态目录不带倍率,所以「自动挑免费模型」
|
|
795
737
|
* 在国内版首次刷新前会挑到空。此时**不回退到收费模型**,而是如实说明并
|
|
796
738
|
* 停用测试按钮。
|
|
739
|
+
* 3. **签到只在有它的区域出现。** 海外区域没有签到,按钮与那一列都不渲染;
|
|
740
|
+
* Host 侧同样拒绝该动作,免得留下一个界面看不见、接口却还能打的死角。
|
|
797
741
|
*
|
|
798
742
|
* @module dsh-connect-workbuddy/client/AccountPool
|
|
799
743
|
*/
|
|
@@ -805,12 +749,10 @@ window.__ModuleLoader__.load({
|
|
|
805
749
|
* The card unmounts its whole body when collapsed (`WorkBuddyCard.tsx:832`
|
|
806
750
|
* renders it only while `open`), and again whenever a poll briefly leaves the
|
|
807
751
|
* region unsigned-in. Component state therefore lost the entire log on every
|
|
808
|
-
* collapse
|
|
809
|
-
*
|
|
810
|
-
* keeps the history across remounts and across tab switches.
|
|
752
|
+
* collapse. Holding it here keeps the history across remounts and across tab
|
|
753
|
+
* switches.
|
|
811
754
|
*/
|
|
812
755
|
const logStore = /* @__PURE__ */ new Map();
|
|
813
|
-
const rotationMemory = /* @__PURE__ */ new Map();
|
|
814
756
|
/**
|
|
815
757
|
* Whether two id sets are equal, ignoring order.
|
|
816
758
|
*
|
|
@@ -899,7 +841,7 @@ window.__ModuleLoader__.load({
|
|
|
899
841
|
* section implying the feature exists.
|
|
900
842
|
*/
|
|
901
843
|
function AccountPool(props) {
|
|
902
|
-
const { t, region, pool, settingsScope, siblingBusy, onBusyChange, onSaved, onRefresh,
|
|
844
|
+
const { t, region, pool, settingsScope, siblingBusy, onBusyChange, onSaved, onRefresh, onRescan, rescanning, onSelectAccount, selectingAccount } = props;
|
|
903
845
|
const [draft, setDraft] = (0, react.useState)(null);
|
|
904
846
|
const [saving, setSaving] = (0, react.useState)(false);
|
|
905
847
|
const [saveError, setSaveError] = (0, react.useState)(void 0);
|
|
@@ -908,19 +850,8 @@ window.__ModuleLoader__.load({
|
|
|
908
850
|
const [log, setLog] = (0, react.useState)(() => logStore.get(region) ?? []);
|
|
909
851
|
(0, react.useEffect)(() => {
|
|
910
852
|
setLog(logStore.get(region) ?? []);
|
|
911
|
-
setIntervalText(void 0);
|
|
912
853
|
}, [region]);
|
|
913
854
|
const [draftRegion, setDraftRegion] = (0, react.useState)(void 0);
|
|
914
|
-
/**
|
|
915
|
-
* The interval field's raw text while it is being typed.
|
|
916
|
-
*
|
|
917
|
-
* `undefined` means "not editing" and the control renders the committed
|
|
918
|
-
* number. Keeping the in-progress string here is what makes the field
|
|
919
|
-
* typeable: clamping on every keystroke and writing the result straight back
|
|
920
|
-
* into the controlled `value` turned `120` into `520` and made an empty field
|
|
921
|
-
* snap to `5`. See `parseIntervalInput`.
|
|
922
|
-
*/
|
|
923
|
-
const [intervalText, setIntervalText] = (0, react.useState)(void 0);
|
|
924
855
|
const mounted = (0, react.useRef)(true);
|
|
925
856
|
(0, react.useEffect)(() => {
|
|
926
857
|
mounted.current = true;
|
|
@@ -930,14 +861,11 @@ window.__ModuleLoader__.load({
|
|
|
930
861
|
}, []);
|
|
931
862
|
const saved = {
|
|
932
863
|
enabled: pool?.enabled ?? false,
|
|
933
|
-
rotateByCredits: pool?.rotateByCredits ?? false,
|
|
934
|
-
autoTestIntervalMinutes: pool?.autoTestIntervalMinutes ?? 30,
|
|
935
864
|
targetModelId: pool?.targetModelId || pool?.staleTargetModelId || "",
|
|
936
865
|
memberAccountIds: pool?.memberAccountIds ?? []
|
|
937
866
|
};
|
|
938
867
|
const active = draftRegion === region && draft !== null ? draft : saved;
|
|
939
|
-
const dirty = draftRegion === region && draft !== null && (draft.enabled !== saved.enabled || draft.
|
|
940
|
-
const current = pool?.accounts.find((account) => account.current);
|
|
868
|
+
const dirty = draftRegion === region && draft !== null && (draft.enabled !== saved.enabled || draft.targetModelId !== saved.targetModelId || !sameIds(draft.memberAccountIds, saved.memberAccountIds));
|
|
941
869
|
/**
|
|
942
870
|
* Membership that actually resolves to a local sign-in — what a batch runs on.
|
|
943
871
|
*
|
|
@@ -970,29 +898,7 @@ window.__ModuleLoader__.load({
|
|
|
970
898
|
*/
|
|
971
899
|
const usableMemberSet = new Set(usableMemberIds(pool?.accounts ?? [], effectiveMembers, active.memberAccountIds));
|
|
972
900
|
const usable = (pool?.accounts ?? []).filter((account) => usableMemberSet.has(account.accountId));
|
|
973
|
-
/**
|
|
974
|
-
* Whether rotation currently owns this region's choice — read from the
|
|
975
|
-
* COMMITTED preferences, not the draft.
|
|
976
|
-
*
|
|
977
|
-
* The distinction is the whole point. The Host decides who is billed from the
|
|
978
|
-
* saved config, so a draft-based lock let one click of "turn off rotation"
|
|
979
|
-
* re-enable the manual dropdown and hide the conflict notice while the Host
|
|
980
|
-
* kept rotating — the user then picked an account, the card showed it as
|
|
981
|
-
* current, and a different account was billed. That is exactly the failure
|
|
982
|
-
* the lock exists to prevent.
|
|
983
|
-
*
|
|
984
|
-
* Reading the committed value means the dropdown stays disabled until the
|
|
985
|
-
* change is SAVED, which is honest: until then the Host really is still
|
|
986
|
-
* rotating. The conflict notice says so and offers to save.
|
|
987
|
-
*/
|
|
988
|
-
const { locked: rotationLocked, unlockPending: rotationUnlockPending } = rotationLockState(saved, active);
|
|
989
901
|
const canEditPool = settingsScope !== void 0;
|
|
990
|
-
(0, react.useEffect)(() => {
|
|
991
|
-
onRotationChange?.(rotationLocked);
|
|
992
|
-
return () => {
|
|
993
|
-
onRotationChange?.(false);
|
|
994
|
-
};
|
|
995
|
-
}, [onRotationChange, rotationLocked]);
|
|
996
902
|
(0, react.useEffect)(() => {
|
|
997
903
|
onBusyChange?.(saving);
|
|
998
904
|
return () => {
|
|
@@ -1011,75 +917,6 @@ window.__ModuleLoader__.load({
|
|
|
1011
917
|
});
|
|
1012
918
|
}, [region]);
|
|
1013
919
|
/**
|
|
1014
|
-
* Record a rotation the HOST performed.
|
|
1015
|
-
*
|
|
1016
|
-
* Rotation can happen on a timer with no card interaction, so the browser
|
|
1017
|
-
* cannot infer it — the Host reports which account it moved to and this logs
|
|
1018
|
-
* the transition. Without it "every switch is recorded" was a promise the UI
|
|
1019
|
-
* had no way to keep.
|
|
1020
|
-
*/
|
|
1021
|
-
/**
|
|
1022
|
-
* Record rotation transitions the HOST performed.
|
|
1023
|
-
*
|
|
1024
|
-
* Rotation can happen on a timer with no card interaction, so the browser
|
|
1025
|
-
* cannot infer it — the Host reports which account it moved to and this logs
|
|
1026
|
-
* the change.
|
|
1027
|
-
*
|
|
1028
|
-
* Three defects are fixed here, all from keeping one `useRef` for a value
|
|
1029
|
-
* that is per-region and long-lived:
|
|
1030
|
-
*
|
|
1031
|
-
* 1. **No phantom first record.** The ref used to start `undefined`, so the
|
|
1032
|
-
* first rotation the card ever SAW was reported as a fresh switch ("from
|
|
1033
|
-
* — to X") even when it had happened long before. The first observation
|
|
1034
|
-
* now PRIMES the ref instead of logging.
|
|
1035
|
-
* 2. **Per-region state.** The card keeps ONE component instance across
|
|
1036
|
-
* tabs and only swaps the `region` prop, so a shared ref explained one
|
|
1037
|
-
* region's rotation with another region's account list — producing
|
|
1038
|
-
* records for switches that never happened. State is keyed by region.
|
|
1039
|
-
* 3. **The stop event is logged.** Rotation ending (`undefined`) used to
|
|
1040
|
-
* return early AND leave the ref set, so "rotation stopped, back to your
|
|
1041
|
-
* own account" never appeared, and rotating to the SAME account again
|
|
1042
|
-
* was silently skipped.
|
|
1043
|
-
*/
|
|
1044
|
-
const rotatedTo = pool?.rotatedToAccountId;
|
|
1045
|
-
(0, react.useEffect)(() => {
|
|
1046
|
-
const memory = rotationMemory.get(region) ?? {
|
|
1047
|
-
last: void 0,
|
|
1048
|
-
primed: false
|
|
1049
|
-
};
|
|
1050
|
-
const previous = memory.last;
|
|
1051
|
-
if (!memory.primed) {
|
|
1052
|
-
rotationMemory.set(region, {
|
|
1053
|
-
last: rotatedTo,
|
|
1054
|
-
primed: true
|
|
1055
|
-
});
|
|
1056
|
-
return;
|
|
1057
|
-
}
|
|
1058
|
-
if (rotatedTo === previous) return;
|
|
1059
|
-
if (rotatedTo === void 0) {
|
|
1060
|
-
rotationMemory.set(region, {
|
|
1061
|
-
last: void 0,
|
|
1062
|
-
primed: true
|
|
1063
|
-
});
|
|
1064
|
-
if (previous !== void 0) appendLog(t("row.poolLogRotationStopped", { from: nameOf(previous, pool) || t("row.accountUnnamed") }), "info");
|
|
1065
|
-
return;
|
|
1066
|
-
}
|
|
1067
|
-
rotationMemory.set(region, {
|
|
1068
|
-
last: rotatedTo,
|
|
1069
|
-
primed: true
|
|
1070
|
-
});
|
|
1071
|
-
appendLog(t("row.poolLogRotated", {
|
|
1072
|
-
from: previous === void 0 ? "—" : nameOf(previous, pool) || t("row.accountUnnamed"),
|
|
1073
|
-
to: nameOf(rotatedTo, pool) || t("row.accountUnnamed")
|
|
1074
|
-
}), "info");
|
|
1075
|
-
}, [
|
|
1076
|
-
appendLog,
|
|
1077
|
-
pool,
|
|
1078
|
-
region,
|
|
1079
|
-
rotatedTo,
|
|
1080
|
-
t
|
|
1081
|
-
]);
|
|
1082
|
-
/**
|
|
1083
920
|
* The latest saved preferences, for callbacks that must not capture a stale
|
|
1084
921
|
* copy.
|
|
1085
922
|
*
|
|
@@ -1121,10 +958,9 @@ window.__ModuleLoader__.load({
|
|
|
1121
958
|
const discard = (0, react.useCallback)(() => {
|
|
1122
959
|
setDraft(null);
|
|
1123
960
|
setDraftRegion(void 0);
|
|
1124
|
-
setIntervalText(void 0);
|
|
1125
961
|
setSaveError(void 0);
|
|
1126
962
|
}, []);
|
|
1127
|
-
/** Save the
|
|
963
|
+
/** Save the pool preferences through the plugin's VERIFIED write path. */
|
|
1128
964
|
const save = (0, react.useCallback)(async () => {
|
|
1129
965
|
if (settingsScope === void 0 || !dirty || draft === null) return;
|
|
1130
966
|
setSaving(true);
|
|
@@ -1174,27 +1010,6 @@ window.__ModuleLoader__.load({
|
|
|
1174
1010
|
memberAccountIds: next ? (pool?.accounts ?? []).map((account) => account.accountId) : []
|
|
1175
1011
|
}));
|
|
1176
1012
|
}, [editDraft, pool]);
|
|
1177
|
-
/**
|
|
1178
|
-
* The interval control's two halves, kept apart so the field is typeable.
|
|
1179
|
-
*
|
|
1180
|
-
* `onIntervalInput` records what the user typed and only folds a VALID value
|
|
1181
|
-
* into the draft; `onIntervalCommit` (blur / Enter) forces the text back to
|
|
1182
|
-
* the committed number, clamping an out-of-range or non-numeric entry exactly
|
|
1183
|
-
* once, after the user is done. Committing on every keystroke is what made
|
|
1184
|
-
* `120` land as `520`.
|
|
1185
|
-
*/
|
|
1186
|
-
const onIntervalInput = (0, react.useCallback)((raw) => {
|
|
1187
|
-
const { text, commit } = intervalEditOnInput(raw);
|
|
1188
|
-
setIntervalText(text);
|
|
1189
|
-
if (commit === void 0) return;
|
|
1190
|
-
editDraft((current) => ({
|
|
1191
|
-
...current,
|
|
1192
|
-
autoTestIntervalMinutes: commit
|
|
1193
|
-
}));
|
|
1194
|
-
}, [editDraft]);
|
|
1195
|
-
const onIntervalCommit = (0, react.useCallback)(() => {
|
|
1196
|
-
setIntervalText(void 0);
|
|
1197
|
-
}, []);
|
|
1198
1013
|
/** Run one batch action against the Host. */
|
|
1199
1014
|
const runAction = (0, react.useCallback)(async (action) => {
|
|
1200
1015
|
setBusy(action);
|
|
@@ -1264,32 +1079,61 @@ window.__ModuleLoader__.load({
|
|
|
1264
1079
|
]);
|
|
1265
1080
|
if (pool === void 0) return null;
|
|
1266
1081
|
/**
|
|
1267
|
-
* Where the target model came from.
|
|
1082
|
+
* Where the target model came from, and which model it is.
|
|
1083
|
+
*
|
|
1084
|
+
* Shown INSIDE the 「目标 test model」 setting row (as its description line)
|
|
1085
|
+
* rather than as a note under the buttons, which is where it used to be — one
|
|
1086
|
+
* of two places that stated the target model, the other being the header
|
|
1087
|
+
* subtitle. Both are gone from the header, so this is now the single place the
|
|
1088
|
+
* provenance is stated, right beside the control it describes.
|
|
1268
1089
|
*
|
|
1269
|
-
*
|
|
1270
|
-
*
|
|
1271
|
-
* `targetModelSource` is sent by the Host.
|
|
1090
|
+
* Returns null when there is nothing to say, so the caller can fall back to the
|
|
1091
|
+
* static explanation instead of rendering an empty "current:" prefix.
|
|
1272
1092
|
*/
|
|
1273
|
-
const
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1093
|
+
const targetProvenance = pool.targetModelSource === "preferred" ? t("row.poolTargetCurrentPreferred", { model: pool.targetModelId ?? "" }) : pool.targetModelSource === "free" ? t("row.poolTargetCurrentFree", { model: pool.targetModelId ?? "" }) : pool.targetModelSource === "stale" ? t("row.poolTargetStale", { model: pool.staleTargetModelId ?? "" }) : pool.targetModelSource === "none" ? t("row.poolTargetNone") : null;
|
|
1094
|
+
/**
|
|
1095
|
+
* The account the next request will start from.
|
|
1096
|
+
*
|
|
1097
|
+
* Read from the Host's `current` flag rather than re-derived here: the Host
|
|
1098
|
+
* owns the selection, and a browser-side guess is how the card would start
|
|
1099
|
+
* disagreeing with the account actually billed.
|
|
1100
|
+
*/
|
|
1101
|
+
const currentAccount = pool.accounts.find((account) => account.current);
|
|
1102
|
+
const currentName = currentAccount === void 0 ? void 0 : currentAccount.accountName === "" ? t("row.accountUnnamed") : currentAccount.accountName;
|
|
1103
|
+
return (0, react.createElement)("section", { className: "dsm-workbuddy-pool" }, (0, react.createElement)("div", { className: "dsm-workbuddy-pool-head" }, (0, react.createElement)("h3", { className: "dsm-workbuddy-pool-title" }, t("row.poolTitle")), currentName === void 0 ? null : (0, react.createElement)("span", { className: "dsm-workbuddy-pool-current-badge" }, t("row.poolCurrentHeader", { account: currentName }))), !active.enabled && onSelectAccount !== void 0 && pool.accounts.length > 0 ? (0, react.createElement)("div", { className: "dsm-workbuddy-pool-set" }, (0, react.createElement)("span", { className: "dsm-workbuddy-pool-set-copy" }, (0, react.createElement)("b", null, t("row.poolManualAccountLabel")), (0, react.createElement)("span", null, t("row.poolManualAccountHint"))), (0, react.createElement)("span", { className: "dsm-workbuddy-pool-set-ctl" }, (0, react.createElement)("select", {
|
|
1104
|
+
className: "dsm-workbuddy-pool-select dsm-workbuddy-pool-account-select",
|
|
1105
|
+
value: currentAccount?.accountId ?? "",
|
|
1106
|
+
disabled: selectingAccount === true,
|
|
1107
|
+
onChange: (event) => {
|
|
1108
|
+
onSelectAccount(event.currentTarget.value);
|
|
1109
|
+
}
|
|
1110
|
+
}, pool.accounts.some((account) => account.current) ? null : (0, react.createElement)("option", {
|
|
1111
|
+
value: "",
|
|
1112
|
+
disabled: true
|
|
1113
|
+
}, t("row.poolManualAccountNone")), ...pool.accounts.map((account) => (0, react.createElement)("option", {
|
|
1114
|
+
key: account.accountId,
|
|
1115
|
+
value: account.accountId
|
|
1116
|
+
}, account.accountName === "" ? t("row.accountUnnamed") : account.accountName))))) : null, (0, react.createElement)("div", { className: "dsm-workbuddy-pool-actions" }, (0, react.createElement)("div", { className: "dsm-workbuddy-pool-actions-group" }, pool.checkinSupported ? (0, react.createElement)("button", {
|
|
1278
1117
|
type: "button",
|
|
1279
1118
|
className: "dsm-btn dsm-btn-primary",
|
|
1280
|
-
disabled: busy !== void 0 ||
|
|
1119
|
+
disabled: busy !== void 0 || effectiveMembers.length === 0,
|
|
1281
1120
|
onClick: () => {
|
|
1282
1121
|
runAction("checkin");
|
|
1283
1122
|
}
|
|
1284
|
-
}, busy === "checkin" ? t("row.poolCheckingIn") : t("row.poolCheckinAll")), (0, react.createElement)("button", {
|
|
1123
|
+
}, busy === "checkin" ? t("row.poolCheckingIn") : t("row.poolCheckinAll")) : null, (0, react.createElement)("button", {
|
|
1285
1124
|
type: "button",
|
|
1286
1125
|
className: "dsm-btn dsm-btn-outline",
|
|
1287
|
-
disabled: busy !== void 0 ||
|
|
1126
|
+
disabled: busy !== void 0 || effectiveMembers.length === 0 || pool.targetModelSource === "none" || pool.targetModelSource === "stale",
|
|
1288
1127
|
title: pool.targetModelSource === "none" ? t("row.poolTargetNone") : pool.targetModelSource === "stale" ? t("row.poolTargetStale", { model: pool.staleTargetModelId ?? "" }) : void 0,
|
|
1289
1128
|
onClick: () => {
|
|
1290
1129
|
runAction("test");
|
|
1291
1130
|
}
|
|
1292
|
-
}, busy === "test" ? t("row.poolTesting") : t("row.poolTestAll")), busy === void 0 ? null : (0, react.createElement)("span", { className: "dsm-workbuddy-pool-hint" }, t("row.poolBusyHint"))),
|
|
1131
|
+
}, busy === "test" ? t("row.poolTesting") : t("row.poolTestAll")), busy === void 0 ? null : (0, react.createElement)("span", { className: "dsm-workbuddy-pool-hint" }, t("row.poolBusyHint"))), onRescan === void 0 ? null : (0, react.createElement)("div", { className: "dsm-workbuddy-pool-actions-group" }, (0, react.createElement)("button", {
|
|
1132
|
+
type: "button",
|
|
1133
|
+
className: "dsm-btn dsm-btn-outline",
|
|
1134
|
+
disabled: rescanning === true,
|
|
1135
|
+
onClick: onRescan
|
|
1136
|
+
}, rescanning === true ? t("row.accountsScanning") : t("row.accountsRescan")))), !active.enabled ? (0, react.createElement)("p", { className: "dsm-workbuddy-pool-note" }, t("row.poolEnabledHint")) : null, active.enabled && usable.length === 0 ? (0, react.createElement)("p", {
|
|
1293
1137
|
className: "dsm-workbuddy-pool-warn",
|
|
1294
1138
|
role: "status"
|
|
1295
1139
|
}, `${t("row.poolNoCandidate")} ${t("row.poolNoCandidateHint")}`) : null, (0, react.createElement)("div", { className: "dsm-workbuddy-pool-members" }, (0, react.createElement)("div", { className: "dsm-workbuddy-pool-members-head" }, (0, react.createElement)("div", null, (0, react.createElement)("h4", { className: "dsm-workbuddy-pool-members-title" }, t("row.poolMembersTitle")), (0, react.createElement)("p", { className: "dsm-workbuddy-pool-note" }, t("row.poolMembersHint"))), (0, react.createElement)("div", { className: "dsm-workbuddy-pool-members-actions" }, (0, react.createElement)("span", { className: "dsm-workbuddy-pool-hint" }, t("row.poolSelectedCount", {
|
|
@@ -1305,11 +1149,12 @@ window.__ModuleLoader__.load({
|
|
|
1305
1149
|
className: "dsm-btn dsm-btn-outline dsm-workbuddy-pool-small-btn",
|
|
1306
1150
|
disabled: effectiveMembers.length === 0 || !canEditPool,
|
|
1307
1151
|
onClick: () => selectAll(false)
|
|
1308
|
-
}, t("row.poolSelectNone")))), (0, react.createElement)("div", { className: "dsm-workbuddy-pool-table" }, (0, react.createElement)("div", { className: "dsm-workbuddy-pool-row dsm-workbuddy-pool-row-head" }, (0, react.createElement)("span", null, t("row.poolColumnAccount")), (0, react.createElement)("span", null, t("row.poolColumnCredits")), (0, react.createElement)("span", null, t("row.poolColumnProbe")), (0, react.createElement)("span", null, t("row.poolColumnCheckin"))), ...pool.accounts.map((account) => renderAccountRow({
|
|
1152
|
+
}, t("row.poolSelectNone")))), (0, react.createElement)("div", { className: "dsm-workbuddy-pool-table" }, (0, react.createElement)("div", { className: "dsm-workbuddy-pool-row dsm-workbuddy-pool-row-head" }, (0, react.createElement)("span", null, t("row.poolColumnAccount")), (0, react.createElement)("span", null, t("row.poolColumnCredits")), (0, react.createElement)("span", null, t("row.poolColumnProbe")), pool.checkinSupported ? (0, react.createElement)("span", null, t("row.poolColumnCheckin")) : null), ...pool.accounts.map((account) => renderAccountRow({
|
|
1309
1153
|
t,
|
|
1310
1154
|
account,
|
|
1311
1155
|
checked: active.memberAccountIds.includes(account.accountId),
|
|
1312
1156
|
canEdit: canEditPool,
|
|
1157
|
+
checkinSupported: pool.checkinSupported,
|
|
1313
1158
|
onToggle: (next) => toggleMember(account.accountId, next)
|
|
1314
1159
|
}))), effectiveMembers.length === 0 ? (0, react.createElement)("p", {
|
|
1315
1160
|
className: "dsm-workbuddy-pool-warn",
|
|
@@ -1340,25 +1185,15 @@ window.__ModuleLoader__.load({
|
|
|
1340
1185
|
dirty,
|
|
1341
1186
|
saving,
|
|
1342
1187
|
siblingBusy: siblingBusy === true,
|
|
1343
|
-
rotationLocked,
|
|
1344
|
-
rotationUnlockPending,
|
|
1345
1188
|
saveError,
|
|
1346
1189
|
settingsScope,
|
|
1347
|
-
intervalText,
|
|
1348
|
-
onIntervalInput,
|
|
1349
|
-
onIntervalCommit,
|
|
1350
1190
|
onEdit: editDraft,
|
|
1351
1191
|
onSave: () => {
|
|
1352
1192
|
save();
|
|
1353
1193
|
},
|
|
1354
1194
|
onDiscard: discard,
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
...current,
|
|
1358
|
-
rotateByCredits: false
|
|
1359
|
-
}));
|
|
1360
|
-
},
|
|
1361
|
-
appendLog
|
|
1195
|
+
appendLog,
|
|
1196
|
+
targetProvenance
|
|
1362
1197
|
}), (0, react.createElement)("p", { className: "dsm-workbuddy-pool-note" }, t("row.poolRegionNote")), renderLog({
|
|
1363
1198
|
t,
|
|
1364
1199
|
log,
|
|
@@ -1369,14 +1204,9 @@ window.__ModuleLoader__.load({
|
|
|
1369
1204
|
}
|
|
1370
1205
|
}));
|
|
1371
1206
|
}
|
|
1372
|
-
/** An account's display name by id, or undefined when it is not listed. */
|
|
1373
|
-
function nameOf(accountId, pool) {
|
|
1374
|
-
const account = pool?.accounts.find((entry) => entry.accountId === accountId);
|
|
1375
|
-
return account === void 0 ? void 0 : account.accountName;
|
|
1376
|
-
}
|
|
1377
1207
|
/** One account's row. */
|
|
1378
1208
|
function renderAccountRow(input) {
|
|
1379
|
-
const { t, account, checked, canEdit, onToggle } = input;
|
|
1209
|
+
const { t, account, checked, canEdit, checkinSupported, onToggle } = input;
|
|
1380
1210
|
const name = account.accountName === "" ? t("row.accountUnnamed") : account.accountName;
|
|
1381
1211
|
const excluded = exclusionText(t, account);
|
|
1382
1212
|
const probe = account.probe;
|
|
@@ -1393,14 +1223,14 @@ window.__ModuleLoader__.load({
|
|
|
1393
1223
|
})), (0, react.createElement)("span", {
|
|
1394
1224
|
className: "dsm-workbuddy-usage-dot",
|
|
1395
1225
|
style: { background: stateColor(account) }
|
|
1396
|
-
}), (0, react.createElement)("span", { className: "dsm-workbuddy-pool-account-name" }, (0, react.createElement)("b", null, name), (0, react.createElement)("span", { className: checked ? "dsm-workbuddy-pool-member" : "dsm-workbuddy-pool-not-member" }, checked ? t("row.poolMember") : t("row.poolNotMember")), excluded === void 0 ? probe === void 0 ? null : (0, react.createElement)("span", null, t("row.poolTestedAt", { at: formatShort(probe.atMs) })) : (0, react.createElement)("span", { className: "dsm-workbuddy-pool-excluded" }, excluded))), (0, react.createElement)("span", { className: "dsm-workbuddy-pool-credits" }, account.credits === void 0 ? (0, react.createElement)("span", null, "—") : (0, react.createElement)("b", null, formatNumber$1(account.credits)), account.expiringSoon !== void 0 && account.expiringSoon > 0 ? (0, react.createElement)("span", { className: "dsm-workbuddy-pool-soon" }, t("row.poolCreditSoon", { count: formatNumber$1(account.expiringSoon) })) : account.nearestExpiryMs === void 0 ? null : (0, react.createElement)("span", { className: "dsm-workbuddy-pool-soon-plain" }, t("row.poolCreditNearest", { at: formatShort(account.nearestExpiryMs) }))), (0, react.createElement)("span", { className: "dsm-workbuddy-pool-probe" }, probeLine), (0, react.createElement)("span", { className: "dsm-workbuddy-pool-checkin" }, account.checkedInToday === void 0 ? (0, react.createElement)("span", {
|
|
1226
|
+
}), (0, react.createElement)("span", { className: "dsm-workbuddy-pool-account-name" }, (0, react.createElement)("b", null, name), account.current ? (0, react.createElement)("span", { className: "dsm-workbuddy-pool-current-tag" }, t("row.poolCurrentBadge")) : null, (0, react.createElement)("span", { className: checked ? "dsm-workbuddy-pool-member" : "dsm-workbuddy-pool-not-member" }, checked ? t("row.poolMember") : t("row.poolNotMember")), excluded === void 0 ? probe === void 0 ? null : (0, react.createElement)("span", null, t("row.poolTestedAt", { at: formatShort(probe.atMs) })) : (0, react.createElement)("span", { className: "dsm-workbuddy-pool-excluded" }, excluded))), (0, react.createElement)("span", { className: "dsm-workbuddy-pool-credits" }, account.credits === void 0 ? (0, react.createElement)("span", null, "—") : (0, react.createElement)("b", null, formatNumber$1(account.credits)), account.expiringSoon !== void 0 && account.expiringSoon > 0 ? (0, react.createElement)("span", { className: "dsm-workbuddy-pool-soon" }, t("row.poolCreditSoon", { count: formatNumber$1(account.expiringSoon) })) : account.nearestExpiryMs === void 0 ? null : (0, react.createElement)("span", { className: "dsm-workbuddy-pool-soon-plain" }, t("row.poolCreditNearest", { at: formatShort(account.nearestExpiryMs) }))), (0, react.createElement)("span", { className: "dsm-workbuddy-pool-probe" }, probeLine), checkinSupported ? (0, react.createElement)("span", { className: "dsm-workbuddy-pool-checkin" }, account.checkedInToday === void 0 ? (0, react.createElement)("span", {
|
|
1397
1227
|
className: "dsm-workbuddy-pool-unknown",
|
|
1398
1228
|
title: t("row.poolNeverTested")
|
|
1399
|
-
}, t("row.poolCheckinUnknown")) : account.checkedInToday ? (0, react.createElement)("span", { className: "dsm-workbuddy-pool-checked" }, t("row.poolCheckedIn")) : t("row.poolNotCheckedIn")));
|
|
1229
|
+
}, t("row.poolCheckinUnknown")) : account.checkedInToday ? (0, react.createElement)("span", { className: "dsm-workbuddy-pool-checked" }, t("row.poolCheckedIn")) : t("row.poolNotCheckedIn")) : null);
|
|
1400
1230
|
}
|
|
1401
1231
|
/** The preference editor plus its save/discard actions. */
|
|
1402
1232
|
function renderSettings(input) {
|
|
1403
|
-
const { t, pool, active, dirty, saving, siblingBusy,
|
|
1233
|
+
const { t, pool, active, dirty, saving, siblingBusy, saveError, settingsScope, onEdit, onSave, onDiscard, targetProvenance } = input;
|
|
1404
1234
|
const canEdit = settingsScope !== void 0;
|
|
1405
1235
|
const toggle = (label, hint, checked, onChange, disabled = false) => (0, react.createElement)("div", { className: "dsm-workbuddy-pool-set" }, (0, react.createElement)("span", { className: "dsm-workbuddy-pool-set-copy" }, (0, react.createElement)("b", null, label), (0, react.createElement)("span", null, hint)), (0, react.createElement)("span", { className: "dsm-workbuddy-pool-set-ctl" }, (0, react.createElement)("label", { className: "sw" }, (0, react.createElement)("input", {
|
|
1406
1236
|
type: "checkbox",
|
|
@@ -1411,28 +1241,10 @@ window.__ModuleLoader__.load({
|
|
|
1411
1241
|
return (0, react.createElement)("div", { className: "dsm-workbuddy-pool-settings-wrap" }, (0, react.createElement)("div", { className: "dsm-workbuddy-pool-head" }, (0, react.createElement)("h3", { className: "dsm-workbuddy-pool-title" }, t("row.poolSettingsTitle")), dirty ? (0, react.createElement)("span", { className: "dsm-workbuddy-pool-dirty" }, t("row.poolDirty")) : null), (0, react.createElement)("p", { className: "dsm-workbuddy-pool-note" }, t("row.poolSettingsHint")), (0, react.createElement)("div", { className: "dsm-workbuddy-pool-settings" }, toggle(t("row.poolEnabled"), t("row.poolEnabledHint"), active.enabled, (next) => onEdit((current) => ({
|
|
1412
1242
|
...current,
|
|
1413
1243
|
enabled: next
|
|
1414
|
-
}))),
|
|
1415
|
-
...current,
|
|
1416
|
-
rotateByCredits: next
|
|
1417
|
-
})), !active.enabled), (0, react.createElement)("div", { className: "dsm-workbuddy-pool-set" }, (0, react.createElement)("span", { className: "dsm-workbuddy-pool-set-copy" }, (0, react.createElement)("b", null, t("row.poolInterval")), (0, react.createElement)("span", null, t("row.poolIntervalHint"))), (0, react.createElement)("span", { className: "dsm-workbuddy-pool-set-ctl" }, (0, react.createElement)("input", {
|
|
1418
|
-
className: "dsm-workbuddy-pool-num",
|
|
1419
|
-
type: "number",
|
|
1420
|
-
min: 5,
|
|
1421
|
-
max: POOL_INTERVAL_MAX,
|
|
1422
|
-
step: 5,
|
|
1423
|
-
value: intervalText ?? String(active.autoTestIntervalMinutes),
|
|
1424
|
-
disabled: !active.enabled || !canEdit,
|
|
1425
|
-
onChange: (event) => {
|
|
1426
|
-
onIntervalInput(event.currentTarget.value);
|
|
1427
|
-
},
|
|
1428
|
-
onBlur: () => onIntervalCommit(),
|
|
1429
|
-
onKeyDown: (event) => {
|
|
1430
|
-
if (event.key === "Enter") onIntervalCommit();
|
|
1431
|
-
}
|
|
1432
|
-
}), (0, react.createElement)("span", { className: "dsm-workbuddy-pool-hint" }, t("row.poolIntervalUnit")))), (0, react.createElement)("div", { className: "dsm-workbuddy-pool-set" }, (0, react.createElement)("span", { className: "dsm-workbuddy-pool-set-copy" }, (0, react.createElement)("b", null, t("row.poolTargetLabel")), (0, react.createElement)("span", null, t("row.poolTargetHint"))), (0, react.createElement)("span", { className: "dsm-workbuddy-pool-set-ctl" }, (0, react.createElement)("select", {
|
|
1244
|
+
}))), (0, react.createElement)("div", { className: "dsm-workbuddy-pool-set" }, (0, react.createElement)("span", { className: "dsm-workbuddy-pool-set-copy" }, (0, react.createElement)("b", null, t("row.poolTargetLabel")), (0, react.createElement)("span", null, targetProvenance === null ? null : `${targetProvenance} `, t("row.poolTargetHint"))), (0, react.createElement)("span", { className: "dsm-workbuddy-pool-set-ctl" }, (0, react.createElement)("select", {
|
|
1433
1245
|
className: "dsm-workbuddy-pool-select",
|
|
1434
1246
|
value: active.targetModelId,
|
|
1435
|
-
disabled: !
|
|
1247
|
+
disabled: !canEdit,
|
|
1436
1248
|
onChange: (event) => {
|
|
1437
1249
|
const value = event.currentTarget.value;
|
|
1438
1250
|
onEdit((current) => ({
|
|
@@ -1446,25 +1258,28 @@ window.__ModuleLoader__.load({
|
|
|
1446
1258
|
}, t("row.poolTargetStaleOption", { model: active.targetModelId })) : null, ...(pool.catalog ?? []).map((model) => (0, react.createElement)("option", {
|
|
1447
1259
|
value: model.id,
|
|
1448
1260
|
key: model.id
|
|
1449
|
-
}, model.creditMultiplier === void 0 ? model.name : `${model.name} (x${model.creditMultiplier.toFixed(2)})`)))))), (0, react.createElement)("div", { className: "dsm-workbuddy-pool-save-bar" }, (0, react.createElement)("
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
},
|
|
1261
|
+
}, model.creditMultiplier === void 0 ? model.name : `${model.name} (x${model.creditMultiplier.toFixed(2)})`)))))), (0, react.createElement)("div", { className: "dsm-workbuddy-pool-save-bar" }, (0, react.createElement)("a", {
|
|
1262
|
+
className: "dsm-workbuddy-usage-cheer",
|
|
1263
|
+
href: WORKBUDDY_GITHUB_URL,
|
|
1264
|
+
target: "_blank",
|
|
1265
|
+
rel: "noopener noreferrer"
|
|
1266
|
+
}, t("row.cheer"), (0, react.createElement)("span", {
|
|
1267
|
+
className: "dsm-workbuddy-usage-cheer-star",
|
|
1268
|
+
"aria-hidden": "true"
|
|
1269
|
+
}, "★")), (0, react.createElement)("div", { className: "dsm-workbuddy-pool-save-buttons" }, (0, react.createElement)("button", {
|
|
1455
1270
|
type: "button",
|
|
1456
1271
|
className: "dsm-btn dsm-btn-outline",
|
|
1457
1272
|
disabled: !dirty || saving || siblingBusy,
|
|
1458
1273
|
onClick: onDiscard
|
|
1459
|
-
}, t("row.poolDiscard")), (0, react.createElement)("
|
|
1274
|
+
}, t("row.poolDiscard")), (0, react.createElement)("button", {
|
|
1275
|
+
type: "button",
|
|
1276
|
+
className: "dsm-btn dsm-btn-primary",
|
|
1277
|
+
disabled: !dirty || saving || siblingBusy || !canEdit,
|
|
1278
|
+
onClick: onSave
|
|
1279
|
+
}, saving ? t("row.poolSaving") : t("row.poolSaved")))), saveError === void 0 ? null : (0, react.createElement)("p", {
|
|
1460
1280
|
className: "dsm-workbuddy-pool-error",
|
|
1461
1281
|
role: "alert"
|
|
1462
|
-
}, t("row.poolSaveFailed", { message: saveError }))
|
|
1463
|
-
type: "button",
|
|
1464
|
-
className: "dsm-btn dsm-btn-outline",
|
|
1465
|
-
disabled: !canEdit || rotationUnlockPending,
|
|
1466
|
-
onClick: onUnlock
|
|
1467
|
-
}, rotationUnlockPending ? t("row.poolUnlockSaved") : t("row.poolManualUnlock"))) : null);
|
|
1282
|
+
}, t("row.poolSaveFailed", { message: saveError })));
|
|
1468
1283
|
}
|
|
1469
1284
|
/** The activity log. */
|
|
1470
1285
|
function renderLog(input) {
|
|
@@ -1594,14 +1409,12 @@ window.__ModuleLoader__.load({
|
|
|
1594
1409
|
.dsm-workbuddy-tab-switch input{margin:0;cursor:pointer;accent-color:var(--dsw-alias-brand-primary,#5686fe)}
|
|
1595
1410
|
.dsm-workbuddy-tab-switch input:disabled{opacity:.4;cursor:default}
|
|
1596
1411
|
.dsm-workbuddy-tab-off{opacity:.55}
|
|
1412
|
+
/* Re-detection keeps a copy in the signed-out branch (that branch is where
|
|
1413
|
+
"I just signed in over there" applies), so it needs its own placement: the
|
|
1414
|
+
pool block's rule cannot reach it. */
|
|
1415
|
+
.dsm-workbuddy-usage-account-actions{display:flex;gap:8px;margin:10px 0 0}
|
|
1597
1416
|
.dsm-workbuddy-tab-off-notice{color:var(--dsw-alias-label-tertiary,#999);font-size:12px;line-height:18px;margin:0 0 4px;padding:8px 10px;background:var(--dsw-alias-bg-layer-3,#2a2c33);border:1px solid var(--dsw-alias-border-l2,#36373b);border-radius:8px}
|
|
1598
|
-
.dsm-workbuddy-usage-account{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:12px 14px;border:1px solid var(--dsw-alias-border-l2,#3a3d45);border-radius:14px;background:var(--dsw-alias-bg-layer-2,#24262c)}
|
|
1599
|
-
.dsm-workbuddy-usage-account-copy{display:flex;flex-direction:column;gap:3px;min-width:0}
|
|
1600
|
-
.dsm-workbuddy-usage-expiry{padding-left:19px;color:var(--dsw-alias-label-tertiary,#9aa0a8);font-size:12px;line-height:18px}
|
|
1601
1417
|
.dsm-workbuddy-usage-hint{padding-left:19px;color:var(--dsw-alias-label-tertiary,#9aa0a8);font-size:12px;line-height:18px}
|
|
1602
|
-
.dsm-workbuddy-account-picker{display:flex;flex-direction:column;gap:8px}
|
|
1603
|
-
.dsm-workbuddy-account-state{color:var(--dsw-alias-label-tertiary,#9aa0a8);font-size:12px;line-height:18px}
|
|
1604
|
-
.dsm-workbuddy-account-state:empty{display:none}
|
|
1605
1418
|
.dsm-workbuddy-account-error{color:var(--dsw-alias-state-error-primary,#ef4444);font-size:12px;line-height:18px;white-space:pre-line}
|
|
1606
1419
|
.dsm-workbuddy-usage-select-wrap{position:relative}
|
|
1607
1420
|
.dsm-workbuddy-usage-select{appearance:none;width:100%;font:inherit;padding:10px 34px 10px 12px;border:1px solid var(--dsw-alias-border-l2,#3a3d45);border-radius:10px;color:var(--dsw-alias-label-primary,#e6e6e6);background:var(--dsw-alias-bg-layer-3,#2a2c33);cursor:pointer;transition:border-color .15s,box-shadow .15s}
|
|
@@ -1622,33 +1435,18 @@ window.__ModuleLoader__.load({
|
|
|
1622
1435
|
.dsm-workbuddy-searched-more:hover{text-decoration:underline}
|
|
1623
1436
|
.dsm-workbuddy-usage-error{margin:0;font-size:14px;line-height:22px;color:var(--dsw-alias-state-error-primary,#ef4444)}
|
|
1624
1437
|
.dsm-workbuddy-usage-dot{width:9px;height:9px;border-radius:50%;flex:0 0 auto}
|
|
1625
|
-
.dsm-workbuddy-usage-status{display:flex;align-items:center;gap:10px;font-size:15px;font-weight:500;color:var(--dsw-alias-label-primary,#e6e6e6)}
|
|
1626
|
-
.dsm-workbuddy-credits-panels{display:grid;grid-template-columns:minmax(0,1.6fr) minmax(150px,.8fr);gap:10px}
|
|
1627
|
-
.dsm-workbuddy-credit-panel{display:flex;flex-direction:column;min-width:0;gap:7px;padding:14px;border:1px solid var(--dsw-alias-border-l2,#3a3d45);border-radius:12px;background:var(--dsw-alias-bg-layer-2,#24262c)}
|
|
1628
|
-
.dsm-workbuddy-credit-panel-title{color:var(--dsw-alias-label-tertiary,#999);font-size:12px;line-height:18px}
|
|
1629
|
-
.dsm-workbuddy-credit-panel-value{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--dsw-alias-label-primary,#e6e6e6);font-size:15px;line-height:21px}
|
|
1630
|
-
.dsm-workbuddy-credit-panel-meta,.dsm-workbuddy-credit-panel-empty{color:var(--dsw-alias-label-secondary,#c6c9d0);font-size:12px;line-height:18px}
|
|
1631
|
-
.dsm-workbuddy-credit-monthly-row{position:relative;display:flex;flex-direction:column;gap:3px;margin:-14px -14px 0;padding:11px 14px 10px;border-bottom:1px solid var(--dsw-alias-border-l2,#36373b);border-radius:14px 14px 0 0;background:var(--dsw-alias-bg-layer-2,#24262c);overflow:hidden}
|
|
1632
|
-
.dsm-workbuddy-credit-monthly-row::before{content:"";position:absolute;top:0;left:0;right:0;height:3px;opacity:.9;background:#9ca2aa}
|
|
1633
|
-
.dsm-workbuddy-credit-monthly-name{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--dsw-alias-label-primary,#e6e6e6);font-size:14px;font-weight:600;line-height:20px}
|
|
1634
|
-
.dsm-workbuddy-credit-monthly-meta{color:var(--dsw-alias-label-tertiary,#999);font-size:11.5px;line-height:17px;font-variant-numeric:tabular-nums}
|
|
1635
|
-
.dsm-workbuddy-credit-packages{display:flex;flex-direction:column;gap:5px;margin:0;padding:0;list-style:none}
|
|
1636
|
-
.dsm-workbuddy-credit-packages li{display:flex;align-items:baseline;justify-content:space-between;gap:10px;color:var(--dsw-alias-label-secondary,#c6c9d0);font-size:12px;line-height:18px}
|
|
1637
|
-
.dsm-workbuddy-credit-packages li span:first-child{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
|
1638
|
-
.dsm-workbuddy-credit-packages li span:last-child{flex:none;color:var(--dsw-alias-label-tertiary,#999);font-size:11px}
|
|
1639
|
-
.dsm-workbuddy-credit-soon{display:flex;align-items:baseline;justify-content:space-between;gap:10px;margin-top:9px;padding-top:9px;border-top:1px solid var(--dsw-alias-border-l2,#36373b);color:var(--dsw-alias-label-secondary,#c6c9d0);font-size:12px;line-height:18px}
|
|
1640
|
-
.dsm-workbuddy-credit-soon strong{color:var(--dsw-alias-label-primary,#e6e6e6);font-size:15px;font-variant-numeric:tabular-nums}
|
|
1641
|
-
.dsm-workbuddy-credit-panel-total{position:relative;align-items:center;text-align:center;overflow:hidden}
|
|
1642
|
-
.dsm-workbuddy-credit-panel-total::before{content:"";position:absolute;top:0;left:0;right:0;height:3px;opacity:.9;background:#4d9b6d}
|
|
1643
|
-
.dsm-workbuddy-credit-total-body{flex:1;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:7px;width:100%}
|
|
1644
|
-
.dsm-workbuddy-credit-total-value{color:#3f8d60;font-size:32px;line-height:36px;font-weight:700;letter-spacing:-.5px;white-space:nowrap;font-variant-numeric:tabular-nums}
|
|
1645
|
-
.dsm-workbuddy-checkin{display:flex;flex-direction:column;align-items:center;width:100%;padding-top:12px;border-top:1px solid var(--dsw-alias-border-l2,#36373b)}
|
|
1646
|
-
.dsm-workbuddy-checkin-button{width:100%;padding:4px 10px;font-size:12px}
|
|
1647
|
-
.dsm-workbuddy-checkin-error{color:var(--dsw-alias-state-error-primary,#ef4444);font-size:11px;line-height:16px;text-align:center}
|
|
1648
|
-
@media (max-width:760px){.dsm-workbuddy-credits-panels{grid-template-columns:1fr}.dsm-workbuddy-credit-panel-total{align-items:flex-start;text-align:left}.dsm-workbuddy-credit-total-body{align-items:flex-start}}
|
|
1649
1438
|
.dsm-workbuddy-models{display:flex;flex-direction:column;gap:10px;border-top:1px solid var(--dsw-alias-border-l2,#36373b);padding-top:14px}
|
|
1439
|
+
.dsm-workbuddy-models-fold{display:flex;flex-direction:column;gap:10px}
|
|
1440
|
+
/* A plain header now: the section does not fold, so it is neither clickable nor
|
|
1441
|
+
focusable, and the list-marker rules that hid the native <details> triangle are
|
|
1442
|
+
gone with it. */
|
|
1650
1443
|
.dsm-workbuddy-models-head{display:flex;align-items:center;justify-content:space-between;gap:12px}
|
|
1444
|
+
.dsm-workbuddy-models-title-row{display:flex;align-items:center;gap:6px;min-width:0}
|
|
1651
1445
|
.dsm-workbuddy-models-title{margin:0;color:var(--dsw-alias-label-primary,#e6e6e6);font-size:14px;font-weight:600;line-height:20px}
|
|
1446
|
+
/* Unsaved-changes marker. It sits in the HEADER rather than beside the save
|
|
1447
|
+
buttons at the foot of the list, so a pending edit is visible without
|
|
1448
|
+
scrolling to the end of a long model list. */
|
|
1449
|
+
.dsm-workbuddy-models-dirty{margin-left:8px;padding:1px 6px;border-radius:6px;background:var(--dsw-alias-state-warn-primary,#f59e0b);color:#1b1d22;font-size:11px;font-weight:600;line-height:16px;vertical-align:1px;white-space:nowrap}
|
|
1652
1450
|
.dsm-workbuddy-models-summary{margin:2px 0 0;color:var(--dsw-alias-label-tertiary,#999);font-size:12px;line-height:18px}
|
|
1653
1451
|
.dsm-workbuddy-model-list{display:flex;flex-direction:column;border:1px solid var(--dsw-alias-border-l2,#36373b);border-radius:10px;overflow:hidden}
|
|
1654
1452
|
.dsm-workbuddy-model{display:grid;grid-template-columns:minmax(0,1fr);gap:7px;padding:10px 12px;background:var(--dsw-alias-bg-layer-2,#232529);transition:opacity .16s}
|
|
@@ -1691,11 +1489,23 @@ window.__ModuleLoader__.load({
|
|
|
1691
1489
|
|
|
1692
1490
|
/* ---- Account pool ---- */
|
|
1693
1491
|
.dsm-workbuddy-pool{display:flex;flex-direction:column;gap:12px;margin:0;padding:16px 0 4px;border-top:1px solid var(--dsw-alias-border-l2,#36373b)}
|
|
1694
|
-
.dsm-workbuddy-pool-head{display:flex;align-items:
|
|
1492
|
+
.dsm-workbuddy-pool-head{display:flex;align-items:center;justify-content:space-between;gap:12px;flex-wrap:wrap}
|
|
1493
|
+
.dsm-workbuddy-pool-current-badge{padding:2px 10px;border-radius:999px;background:rgba(86,134,254,.16);color:var(--dsw-alias-brand-primary,#5686fe);font-size:12px;font-weight:600;line-height:18px;white-space:nowrap}
|
|
1494
|
+
/* Sits under the header block, before the batch buttons: account discovery is
|
|
1495
|
+
the first step of using the pool, so it reads above the actions it enables. */
|
|
1496
|
+
/* Manual-mode account picker: shares the settings row layout (.pool-set), and
|
|
1497
|
+
only the select itself needs a hint that it is a PICKER of accounts rather
|
|
1498
|
+
than of models — the two sit near each other and used to be indistinguishable
|
|
1499
|
+
to a selector query as well as to the eye. */
|
|
1500
|
+
.dsm-workbuddy-pool-account-select{min-width:200px}
|
|
1695
1501
|
.dsm-workbuddy-pool-title{margin:0;font-size:15px;font-weight:600;color:var(--dsw-alias-label-primary,#e6e6e6)}
|
|
1696
|
-
.dsm-workbuddy-pool-summary{margin:2px 0 0;font-size:12px;line-height:18px;color:var(--dsw-alias-label-tertiary,#999)}
|
|
1697
1502
|
.dsm-workbuddy-pool-badge{flex:none;padding:2px 9px;border-radius:999px;font-size:12px;line-height:18px;font-weight:500;color:var(--dsw-alias-brand-primary,#5686fe);background:rgba(86,134,254,.12);border:1px solid rgba(86,134,254,.32)}
|
|
1698
|
-
|
|
1503
|
+
/* One row, two clusters: LEFT acts on the accounts you checked, RIGHT re-reads
|
|
1504
|
+
which accounts exist. Before this the rescan button was on its own row pushed
|
|
1505
|
+
right while the batch buttons were on the next row pushed left — two
|
|
1506
|
+
alignments two lines apart, with nothing explaining the split. */
|
|
1507
|
+
.dsm-workbuddy-pool-actions{display:flex;align-items:center;justify-content:space-between;gap:12px;flex-wrap:wrap;border-top:1px solid var(--dsw-alias-border-l2,#36373b);padding-top:12px}
|
|
1508
|
+
.dsm-workbuddy-pool-actions-group{display:flex;align-items:center;gap:8px;flex-wrap:wrap}
|
|
1699
1509
|
.dsm-workbuddy-pool-hint{font-size:12px;line-height:18px;color:var(--dsw-alias-label-tertiary,#999)}
|
|
1700
1510
|
.dsm-workbuddy-pool-note{margin:0;font-size:12px;line-height:17px;color:var(--dsw-alias-label-tertiary,#999)}
|
|
1701
1511
|
.dsm-workbuddy-pool-warn{margin:0;padding:9px 11px;border-radius:9px;font-size:12px;line-height:17px;color:var(--dsw-alias-state-warn-primary,#f59e0b);background:rgba(245,158,11,.09);border:1px solid rgba(245,158,11,.3)}
|
|
@@ -1705,10 +1515,17 @@ window.__ModuleLoader__.load({
|
|
|
1705
1515
|
.dsm-workbuddy-pool-row:first-child{border-top:0}
|
|
1706
1516
|
.dsm-workbuddy-pool-row-head{padding:8px 14px;background:var(--dsw-alias-bg-layer-3,#2a2c33);color:var(--dsw-alias-label-tertiary,#999);font-size:11px;font-weight:600;letter-spacing:.02em;text-transform:uppercase}
|
|
1707
1517
|
.dsm-workbuddy-pool-row-current{background:rgba(86,134,254,.07)}
|
|
1518
|
+
/* The serving row, made findable at a glance: a left accent bar plus the tint.
|
|
1519
|
+
The bar is what distinguishes it in a long table, where a 7%-opacity wash
|
|
1520
|
+
alone reads as an alternating-row stripe. */
|
|
1521
|
+
.dsm-workbuddy-pool-row-current{box-shadow:inset 3px 0 0 var(--dsw-alias-brand-primary,#5686fe)}
|
|
1708
1522
|
.dsm-workbuddy-pool-account{display:flex;align-items:center;gap:8px;min-width:0}
|
|
1709
1523
|
.dsm-workbuddy-pool-account-name{display:flex;flex-direction:column;gap:1px;min-width:0}
|
|
1710
1524
|
.dsm-workbuddy-pool-account-name b{font-weight:500;color:var(--dsw-alias-label-primary,#e6e6e6);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
|
1711
1525
|
.dsm-workbuddy-pool-account-name span{font-size:11px;line-height:16px;color:var(--dsw-alias-label-tertiary,#999)}
|
|
1526
|
+
/* Declared AFTER the blanket span rule so it wins: the badge must not inherit
|
|
1527
|
+
the muted 11px tertiary treatment the membership line uses. */
|
|
1528
|
+
.dsm-workbuddy-pool-account-name .dsm-workbuddy-pool-current-tag{align-self:flex-start;margin-top:2px;padding:1px 6px;border-radius:999px;background:rgba(86,134,254,.16);color:var(--dsw-alias-brand-primary,#5686fe);font-size:11px;font-weight:600;line-height:16px;white-space:nowrap}
|
|
1712
1529
|
.dsm-workbuddy-pool-excluded{color:var(--dsw-alias-state-warn-primary,#f59e0b)}
|
|
1713
1530
|
.dsm-workbuddy-pool-credits{display:flex;flex-direction:column;gap:1px;min-width:0;font-variant-numeric:tabular-nums}
|
|
1714
1531
|
.dsm-workbuddy-pool-credits b{color:var(--dsw-alias-label-primary,#e6e6e6);font-weight:600}
|
|
@@ -1739,7 +1556,8 @@ window.__ModuleLoader__.load({
|
|
|
1739
1556
|
.dsm-workbuddy-pool-num{width:74px;padding:6px 10px;border:1px solid var(--dsw-alias-border-l2,#3a3d45);border-radius:8px;background:var(--dsw-alias-bg-layer-3,#2a2c33);color:var(--dsw-alias-label-primary,#e6e6e6);font:inherit;font-size:13px;text-align:center;font-variant-numeric:tabular-nums}
|
|
1740
1557
|
.dsm-workbuddy-pool-select{max-width:260px;padding:6px 10px;border:1px solid var(--dsw-alias-border-l2,#3a3d45);border-radius:8px;background:var(--dsw-alias-bg-layer-3,#2a2c33);color:var(--dsw-alias-label-primary,#e6e6e6);font:inherit;font-size:13px;cursor:pointer}
|
|
1741
1558
|
.dsm-workbuddy-pool-select:disabled,.dsm-workbuddy-pool-num:disabled{opacity:.5;cursor:default}
|
|
1742
|
-
.dsm-workbuddy-pool-save-bar{display:flex;align-items:center;gap:
|
|
1559
|
+
.dsm-workbuddy-pool-save-bar{display:flex;align-items:center;justify-content:space-between;gap:12px;flex-wrap:wrap;border-top:1px solid var(--dsw-alias-border-l2,#36373b);padding-top:12px}
|
|
1560
|
+
.dsm-workbuddy-pool-save-buttons{display:flex;align-items:center;justify-content:flex-end;gap:8px;flex:none}
|
|
1743
1561
|
.dsm-workbuddy-pool-conflict{display:flex;align-items:flex-start;justify-content:space-between;gap:12px;flex-wrap:wrap;padding:11px 13px;border-radius:10px;background:rgba(245,158,11,.08);border:1px solid var(--dsw-alias-state-warn-primary,#f59e0b)}
|
|
1744
1562
|
.dsm-workbuddy-pool-pending{flex:none;align-self:center;font-size:12px;line-height:17px;color:var(--dsw-alias-state-warn-primary,#f59e0b)}
|
|
1745
1563
|
.dsm-workbuddy-pool-conflict-main{display:flex;flex-direction:column;gap:2px;min-width:0;flex:1}
|
|
@@ -1916,7 +1734,6 @@ window.__ModuleLoader__.load({
|
|
|
1916
1734
|
* @module dsh-connect-workbuddy/client/WorkBuddyCard
|
|
1917
1735
|
*/
|
|
1918
1736
|
const POLL_INTERVAL_MS = 6e4;
|
|
1919
|
-
const WORKBUDDY_GITHUB_URL = "https://github.com/dingminhua/dsh-connect-workbuddy";
|
|
1920
1737
|
/**
|
|
1921
1738
|
* Tooltip for one model's image checkbox. The box is pre-checked only for
|
|
1922
1739
|
* models the vendored vendor table documents as natively multimodal, so the
|
|
@@ -2010,16 +1827,6 @@ window.__ModuleLoader__.load({
|
|
|
2010
1827
|
maximumFractionDigits: 2
|
|
2011
1828
|
}).format(value);
|
|
2012
1829
|
}
|
|
2013
|
-
function formatDateTime(value) {
|
|
2014
|
-
return new Intl.DateTimeFormat(void 0, {
|
|
2015
|
-
year: "numeric",
|
|
2016
|
-
month: "2-digit",
|
|
2017
|
-
day: "2-digit",
|
|
2018
|
-
hour: "2-digit",
|
|
2019
|
-
minute: "2-digit",
|
|
2020
|
-
hourCycle: "h23"
|
|
2021
|
-
}).format(new Date(value));
|
|
2022
|
-
}
|
|
2023
1830
|
/**
|
|
2024
1831
|
* Compact package-date rendering with time, e.g. `08/25 14:44`.
|
|
2025
1832
|
*
|
|
@@ -2130,15 +1937,6 @@ window.__ModuleLoader__.load({
|
|
|
2130
1937
|
const [saveError, setSaveError] = (0, react.useState)(void 0);
|
|
2131
1938
|
const [switchingAccount, setSwitchingAccount] = (0, react.useState)(false);
|
|
2132
1939
|
/**
|
|
2133
|
-
* Whether the account pool's rotation currently owns this region's choice.
|
|
2134
|
-
*
|
|
2135
|
-
* Reported up by the pool section. While true, the manual account dropdown is
|
|
2136
|
-
* disabled: both would write the same slot, and letting them disagree is how
|
|
2137
|
-
* the card ends up showing one account while another is billed. The pool
|
|
2138
|
-
* shows the reason and an inline way to switch rotation off.
|
|
2139
|
-
*/
|
|
2140
|
-
const [poolRotationLocked, setPoolRotationLocked] = (0, react.useState)(false);
|
|
2141
|
-
/**
|
|
2142
1940
|
* Whether the account pool's save is in flight. Both sections write into one
|
|
2143
1941
|
* region slot and the Host merges per-region, so two concurrent saves can
|
|
2144
1942
|
* interleave on a stale base and revert each other while both report success
|
|
@@ -2147,8 +1945,6 @@ window.__ModuleLoader__.load({
|
|
|
2147
1945
|
const [poolBusy, setPoolBusy] = (0, react.useState)(false);
|
|
2148
1946
|
/** A refused account write (silently unpersisted settings on a locked file). */
|
|
2149
1947
|
const [accountError, setAccountError] = (0, react.useState)(void 0);
|
|
2150
|
-
const [checkingIn, setCheckingIn] = (0, react.useState)(false);
|
|
2151
|
-
const [checkinActionError, setCheckinActionError] = (0, react.useState)(void 0);
|
|
2152
1948
|
/** Region whose on/off checkbox write is in flight, so its box can't race. */
|
|
2153
1949
|
const [togglingRegion, setTogglingRegion] = (0, react.useState)(void 0);
|
|
2154
1950
|
/**
|
|
@@ -2369,28 +2165,6 @@ window.__ModuleLoader__.load({
|
|
|
2369
2165
|
if (mounted.current) setTogglingRegion(void 0);
|
|
2370
2166
|
}
|
|
2371
2167
|
};
|
|
2372
|
-
/**
|
|
2373
|
-
* Claim the daily check-in reward for the active region. The action endpoint
|
|
2374
|
-
* is region-scoped; the response only refreshes this tab's check-in state.
|
|
2375
|
-
*/
|
|
2376
|
-
const claimDailyCheckin = async () => {
|
|
2377
|
-
setCheckingIn(true);
|
|
2378
|
-
setCheckinActionError(void 0);
|
|
2379
|
-
try {
|
|
2380
|
-
const response = await fetch(withWorkBuddyRegion(WORKBUDDY_CHECKIN_PATH, activeRegion), {
|
|
2381
|
-
method: "POST",
|
|
2382
|
-
headers: { accept: "application/json" },
|
|
2383
|
-
credentials: "same-origin"
|
|
2384
|
-
});
|
|
2385
|
-
const body = await response.json().catch(() => void 0);
|
|
2386
|
-
if (!response.ok) throw new Error(body?.error ?? `HTTP ${response.status}`);
|
|
2387
|
-
await refreshUsage(activeRegion);
|
|
2388
|
-
} catch (error) {
|
|
2389
|
-
if (mounted.current) setCheckinActionError(error instanceof Error ? error.message : t("row.requestFailed"));
|
|
2390
|
-
} finally {
|
|
2391
|
-
if (mounted.current) setCheckingIn(false);
|
|
2392
|
-
}
|
|
2393
|
-
};
|
|
2394
2168
|
const refreshModels = async () => {
|
|
2395
2169
|
setBusy(true);
|
|
2396
2170
|
try {
|
|
@@ -2587,7 +2361,6 @@ window.__ModuleLoader__.load({
|
|
|
2587
2361
|
* placeholder says the honest thing instead.
|
|
2588
2362
|
*/
|
|
2589
2363
|
const nameOf = (value) => value === "" ? t("row.accountUnnamed") : value;
|
|
2590
|
-
const label = status.status === "signed-in" ? t("row.signedIn", { accountName: nameOf(status.accountName) }) : status.status === "error" ? t("row.requestFailed") : t("row.signedOut");
|
|
2591
2364
|
/** The saved choice no longer matches a local sign-in (tokens are fine). */
|
|
2592
2365
|
const selectionLost = status.status === "signed-out" && status.selectionLost === true;
|
|
2593
2366
|
/**
|
|
@@ -2607,11 +2380,7 @@ window.__ModuleLoader__.load({
|
|
|
2607
2380
|
message: status.status === "signed-out" ? status.message : void 0,
|
|
2608
2381
|
searched
|
|
2609
2382
|
});
|
|
2610
|
-
|
|
2611
|
-
* Whether this region runs a saved choice, per the Host. `undefined` only on
|
|
2612
|
-
* the error branch, which renders no picker and therefore no state line.
|
|
2613
|
-
*/
|
|
2614
|
-
const selectionExplicit = status.status === "error" ? void 0 : status.selectionExplicit;
|
|
2383
|
+
status.status === "error" || status.selectionExplicit;
|
|
2615
2384
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("li", {
|
|
2616
2385
|
className: `dsm-plugin-card${open ? " dsm-plugin-card-open" : ""}`,
|
|
2617
2386
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
@@ -2696,151 +2465,15 @@ window.__ModuleLoader__.load({
|
|
|
2696
2465
|
className: "dsm-workbuddy-tab-off-notice",
|
|
2697
2466
|
children: t("row.tabOffNotice")
|
|
2698
2467
|
}) : null,
|
|
2699
|
-
/* @__PURE__ */ (0, react_jsx_runtime.
|
|
2700
|
-
className: "dsm-workbuddy-usage-
|
|
2701
|
-
children:
|
|
2702
|
-
|
|
2703
|
-
|
|
2704
|
-
|
|
2705
|
-
|
|
2706
|
-
className: "dsm-workbuddy-usage-status",
|
|
2707
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2708
|
-
"aria-hidden": "true",
|
|
2709
|
-
className: "dsm-workbuddy-usage-dot",
|
|
2710
|
-
style: dotStyle(status.status)
|
|
2711
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: label })]
|
|
2712
|
-
}),
|
|
2713
|
-
status.status === "signed-in" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2714
|
-
className: "dsm-workbuddy-usage-expiry",
|
|
2715
|
-
children: t("row.tokenExpiry", { expiresAt: formatDateTime(status.tokenExpiresAtMs) })
|
|
2716
|
-
}) : null,
|
|
2717
|
-
status.status === "error" || status.status === "signed-in" && status.creditsError !== void 0 ? status.status === "signed-in" && status.credentialRejected === true ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2718
|
-
className: "dsm-workbuddy-usage-hint",
|
|
2719
|
-
children: t("row.reloginHint")
|
|
2720
|
-
}) : null,
|
|
2721
|
-
selectionLost ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2722
|
-
className: "dsm-workbuddy-usage-hint",
|
|
2723
|
-
children: t("row.selectionLostHint")
|
|
2724
|
-
}) : null
|
|
2725
|
-
]
|
|
2726
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2727
|
-
type: "button",
|
|
2728
|
-
className: "dsm-btn dsm-btn-outline",
|
|
2729
|
-
disabled: busy,
|
|
2730
|
-
onClick: () => {
|
|
2731
|
-
rescanAccounts();
|
|
2732
|
-
},
|
|
2733
|
-
children: busy ? t("row.accountsScanning") : t("row.accountsRescan")
|
|
2734
|
-
})]
|
|
2735
|
-
}),
|
|
2736
|
-
status.status !== "error" && status.accounts.length > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
|
|
2737
|
-
className: "dsm-workbuddy-account-picker",
|
|
2738
|
-
"aria-label": t("row.accountsTitle"),
|
|
2739
|
-
children: [
|
|
2740
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
2741
|
-
className: "dsm-workbuddy-usage-select-wrap",
|
|
2742
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
|
|
2743
|
-
className: "dsm-workbuddy-usage-select",
|
|
2744
|
-
value: status.accounts.find((account) => account.selected)?.id ?? "",
|
|
2745
|
-
disabled: switchingAccount || !canWrite || poolRotationLocked,
|
|
2746
|
-
onChange: (event) => {
|
|
2747
|
-
switchAccount(event.currentTarget.value);
|
|
2748
|
-
},
|
|
2749
|
-
children: [status.accounts.some((account) => account.selected) ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
2750
|
-
value: "",
|
|
2751
|
-
disabled: true,
|
|
2752
|
-
children: t("row.accountNoneInEffect")
|
|
2753
|
-
}), status.accounts.map((account) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("option", {
|
|
2754
|
-
value: account.id,
|
|
2755
|
-
children: [nameOf(account.accountName), account.domain === "" ? "" : ` · ${account.domain}`]
|
|
2756
|
-
}, account.id))]
|
|
2757
|
-
})
|
|
2758
|
-
}),
|
|
2759
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2760
|
-
className: "dsm-workbuddy-account-state",
|
|
2761
|
-
role: "status",
|
|
2762
|
-
children: selectionExplicit === true ? t("row.accountsSavedChoice") : ""
|
|
2763
|
-
}),
|
|
2764
|
-
accountError === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2765
|
-
className: "dsm-workbuddy-account-error",
|
|
2766
|
-
role: "alert",
|
|
2767
|
-
children: t("row.accountsWriteFailed", { message: accountError })
|
|
2768
|
-
})
|
|
2769
|
-
]
|
|
2468
|
+
status.status === "error" || status.status === "signed-in" && status.creditsError !== void 0 ? status.status === "signed-in" && status.credentialRejected === true ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
2469
|
+
className: "dsm-workbuddy-usage-hint",
|
|
2470
|
+
children: t("row.reloginHint")
|
|
2471
|
+
}) : null,
|
|
2472
|
+
selectionLost ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
2473
|
+
className: "dsm-workbuddy-usage-hint",
|
|
2474
|
+
children: t("row.selectionLostHint")
|
|
2770
2475
|
}) : null,
|
|
2771
2476
|
status.status === "signed-in" ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
|
|
2772
|
-
status.credits === void 0 ? null : (() => {
|
|
2773
|
-
const monthly = [...status.credits.packages].filter((pack) => pack.monthly).sort((left, right) => right.remain - left.remain);
|
|
2774
|
-
const SOON_MS = 2592e5;
|
|
2775
|
-
const now = Date.now();
|
|
2776
|
-
const expiring = [...status.credits.packages].filter((pack) => !pack.monthly && pack.remain > 0 && (pack.expiresAtMs ?? Number.MAX_SAFE_INTEGER) - now <= SOON_MS).sort((left, right) => (left.expiresAtMs ?? Number.MAX_SAFE_INTEGER) - (right.expiresAtMs ?? Number.MAX_SAFE_INTEGER));
|
|
2777
|
-
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2778
|
-
className: "dsm-workbuddy-credits-panels",
|
|
2779
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
|
|
2780
|
-
className: "dsm-workbuddy-credit-panel dsm-workbuddy-credit-panel-activities",
|
|
2781
|
-
children: [
|
|
2782
|
-
monthly.map((pack, index) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2783
|
-
className: "dsm-workbuddy-credit-monthly-row",
|
|
2784
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2785
|
-
className: "dsm-workbuddy-credit-monthly-name",
|
|
2786
|
-
children: pack.packageName
|
|
2787
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2788
|
-
className: "dsm-workbuddy-credit-monthly-meta",
|
|
2789
|
-
children: t("row.creditsMonthlyRemain", {
|
|
2790
|
-
remain: formatNumber(pack.remain),
|
|
2791
|
-
size: formatNumber(pack.size),
|
|
2792
|
-
at: pack.cycleRefreshMs === void 0 ? "" : formatDate(pack.cycleRefreshMs)
|
|
2793
|
-
})
|
|
2794
|
-
})]
|
|
2795
|
-
}, `monthly-${pack.packageName}-${String(index)}`)),
|
|
2796
|
-
expiring.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2797
|
-
className: "dsm-workbuddy-credit-panel-empty",
|
|
2798
|
-
children: t("row.creditsNoSoon")
|
|
2799
|
-
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("ul", {
|
|
2800
|
-
className: "dsm-workbuddy-credit-packages",
|
|
2801
|
-
children: expiring.map((pack, index) => {
|
|
2802
|
-
const at = pack.expiresAtMs;
|
|
2803
|
-
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("li", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: pack.packageName }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: [formatNumber(pack.remain), at === void 0 ? "" : ` · ${formatDate(at)}`] })] }, `${pack.packageName}-${String(index)}`);
|
|
2804
|
-
})
|
|
2805
|
-
}),
|
|
2806
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2807
|
-
className: "dsm-workbuddy-credit-soon",
|
|
2808
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("row.creditsExpiringSoon") }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: formatNumber(status.credits.expiringSoon) })]
|
|
2809
|
-
})
|
|
2810
|
-
]
|
|
2811
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
|
|
2812
|
-
className: "dsm-workbuddy-credit-panel dsm-workbuddy-credit-panel-total",
|
|
2813
|
-
children: [
|
|
2814
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2815
|
-
className: "dsm-workbuddy-credit-total-body",
|
|
2816
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2817
|
-
className: "dsm-workbuddy-credit-panel-title",
|
|
2818
|
-
children: t("row.creditsTotalLabel")
|
|
2819
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", {
|
|
2820
|
-
className: "dsm-workbuddy-credit-total-value",
|
|
2821
|
-
children: formatNumber(status.credits.total)
|
|
2822
|
-
})]
|
|
2823
|
-
}),
|
|
2824
|
-
status.checkin === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
2825
|
-
className: "dsm-workbuddy-checkin",
|
|
2826
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2827
|
-
type: "button",
|
|
2828
|
-
className: "dsm-btn dsm-btn-primary dsm-workbuddy-checkin-button",
|
|
2829
|
-
disabled: !status.checkin.active || status.checkin.todayCheckedIn || checkingIn,
|
|
2830
|
-
onClick: () => {
|
|
2831
|
-
claimDailyCheckin();
|
|
2832
|
-
},
|
|
2833
|
-
children: checkingIn ? t("row.checkinClaiming") : status.checkin.todayCheckedIn ? t("row.checkinClaimed") : status.checkin.claimButtonText ?? t("row.checkinClaim")
|
|
2834
|
-
})
|
|
2835
|
-
}),
|
|
2836
|
-
status.checkinError === void 0 && checkinActionError === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2837
|
-
className: "dsm-workbuddy-checkin-error",
|
|
2838
|
-
children: t("row.checkinError", { message: checkinActionError ?? status.checkinError ?? "" })
|
|
2839
|
-
})
|
|
2840
|
-
]
|
|
2841
|
-
})]
|
|
2842
|
-
});
|
|
2843
|
-
})(),
|
|
2844
2477
|
status.credentialRejected === true ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
|
|
2845
2478
|
className: "dsm-workbuddy-usage-error",
|
|
2846
2479
|
role: "alert",
|
|
@@ -2869,205 +2502,236 @@ window.__ModuleLoader__.load({
|
|
|
2869
2502
|
role: "alert",
|
|
2870
2503
|
children: t("row.requestFailedHint", { message: status.refreshError })
|
|
2871
2504
|
}),
|
|
2872
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
|
|
2873
|
-
className: "dsm-workbuddy-models",
|
|
2874
|
-
"aria-label": t("row.modelsTitle"),
|
|
2875
|
-
children: [
|
|
2876
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2877
|
-
className: "dsm-workbuddy-models-head",
|
|
2878
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", {
|
|
2879
|
-
className: "dsm-workbuddy-models-title",
|
|
2880
|
-
children: t("row.modelsTitle")
|
|
2881
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
2882
|
-
className: "dsm-workbuddy-models-summary",
|
|
2883
|
-
children: t("row.modelsSummary", { count: activeEnabledIds.size })
|
|
2884
|
-
})] }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
2885
|
-
className: "dsm-workbuddy-models-head-actions",
|
|
2886
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2887
|
-
type: "button",
|
|
2888
|
-
className: "dsm-btn dsm-btn-outline",
|
|
2889
|
-
disabled: busy,
|
|
2890
|
-
onClick: () => {
|
|
2891
|
-
refreshModels();
|
|
2892
|
-
},
|
|
2893
|
-
children: busy ? t("row.modelsRefreshing") : t("row.modelsRefresh")
|
|
2894
|
-
})
|
|
2895
|
-
})]
|
|
2896
|
-
}),
|
|
2897
|
-
probeError === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
2898
|
-
className: "dsm-workbuddy-model-probe-result dsm-workbuddy-model-probe-result-bad",
|
|
2899
|
-
role: "alert",
|
|
2900
|
-
children: t("row.probeError", { message: probeError })
|
|
2901
|
-
}),
|
|
2902
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
2903
|
-
className: "dsm-workbuddy-model-list",
|
|
2904
|
-
children: visibleModels.map((model) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2905
|
-
className: `dsm-workbuddy-model${activeEnabledIds.has(model.id) ? "" : " dsm-workbuddy-model-disabled"}`,
|
|
2906
|
-
children: [
|
|
2907
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2908
|
-
className: "dsm-workbuddy-model-head",
|
|
2909
|
-
children: [
|
|
2910
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
2911
|
-
className: "dsm-workbuddy-model-enabled",
|
|
2912
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
2913
|
-
type: "checkbox",
|
|
2914
|
-
checked: activeEnabledIds.has(model.id),
|
|
2915
|
-
disabled: !canWrite || saving,
|
|
2916
|
-
onChange: () => {
|
|
2917
|
-
toggleModel(model.id);
|
|
2918
|
-
}
|
|
2919
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2920
|
-
className: "dsm-workbuddy-model-copy",
|
|
2921
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
2922
|
-
className: "dsm-workbuddy-model-name",
|
|
2923
|
-
children: [model.name, model.creditMultiplier === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
2924
|
-
className: "dsm-workbuddy-model-name-rate",
|
|
2925
|
-
children: [
|
|
2926
|
-
"(",
|
|
2927
|
-
model.creditMultiplier.toFixed(2),
|
|
2928
|
-
"x)"
|
|
2929
|
-
]
|
|
2930
|
-
})]
|
|
2931
|
-
})
|
|
2932
|
-
})]
|
|
2933
|
-
}),
|
|
2934
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
2935
|
-
className: "dsm-workbuddy-model-image",
|
|
2936
|
-
title: imageCheckboxHint(model, t),
|
|
2937
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
2938
|
-
type: "checkbox",
|
|
2939
|
-
checked: activeImageIds.has(model.id),
|
|
2940
|
-
disabled: !canWrite || saving,
|
|
2941
|
-
onChange: () => {
|
|
2942
|
-
toggleImage(model.id);
|
|
2943
|
-
}
|
|
2944
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("row.modelImage") })]
|
|
2945
|
-
}),
|
|
2946
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("fieldset", {
|
|
2947
|
-
className: "dsm-workbuddy-context-budget",
|
|
2948
|
-
"aria-label": t("row.contextBudget"),
|
|
2949
|
-
children: [model.nativeContextWindow > 2e5 ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
2950
|
-
type: "radio",
|
|
2951
|
-
name: `context-${model.id}`,
|
|
2952
|
-
checked: (activeContextBudgets[model.id] ?? 2e5) === 2e5,
|
|
2953
|
-
disabled: !canWrite || saving,
|
|
2954
|
-
onChange: () => {
|
|
2955
|
-
setContextBudget(model.id, 2e5);
|
|
2956
|
-
}
|
|
2957
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: "200K" })] }) : null, /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
2958
|
-
type: "radio",
|
|
2959
|
-
name: `context-${model.id}`,
|
|
2960
|
-
checked: model.nativeContextWindow <= 2e5 || activeContextBudgets[model.id] === model.nativeContextWindow,
|
|
2961
|
-
disabled: model.nativeContextWindow <= 2e5 || !canWrite || saving,
|
|
2962
|
-
onChange: () => {
|
|
2963
|
-
setContextBudget(model.id, model.nativeContextWindow);
|
|
2964
|
-
}
|
|
2965
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: formatCapacity(model.nativeContextWindow, t("row.modelUnknown")) })] })]
|
|
2966
|
-
}),
|
|
2967
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
2968
|
-
className: "dsm-workbuddy-model-actions-buttons",
|
|
2969
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2970
|
-
type: "button",
|
|
2971
|
-
className: "dsm-btn dsm-btn-outline dsm-workbuddy-model-probe",
|
|
2972
|
-
disabled: probing[model.id] === true || status.status !== "signed-in",
|
|
2973
|
-
title: t("row.probeHint"),
|
|
2974
|
-
onClick: () => {
|
|
2975
|
-
probeModels([model.id]);
|
|
2976
|
-
},
|
|
2977
|
-
children: probing[model.id] === true ? t("row.probing") : t("row.probe")
|
|
2978
|
-
})
|
|
2979
|
-
})
|
|
2980
|
-
]
|
|
2981
|
-
}),
|
|
2982
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
2983
|
-
className: "dsm-workbuddy-model-details",
|
|
2984
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2985
|
-
className: "dsm-workbuddy-model-meta",
|
|
2986
|
-
children: [
|
|
2987
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("row.modelContext", { context: formatCapacity(model.nativeContextWindow, t("row.modelUnknown")) }) }),
|
|
2988
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("row.modelOutput", { output: formatCapacity(model.maxTokens, t("row.modelUnknown")) }) }),
|
|
2989
|
-
model.reasoning === void 0 || model.reasoning.supportedEfforts === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("row.modelReasoning", { efforts: model.reasoning.supportedEfforts.join(" / ") }) })
|
|
2990
|
-
]
|
|
2991
|
-
})
|
|
2992
|
-
}),
|
|
2993
|
-
(() => {
|
|
2994
|
-
const result = probes[activeRegion]?.[model.id];
|
|
2995
|
-
if (result === void 0) return null;
|
|
2996
|
-
const view = probeResultView(result, t);
|
|
2997
|
-
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("p", {
|
|
2998
|
-
className: `dsm-workbuddy-model-probe-result dsm-workbuddy-model-probe-result-${view.tone}`,
|
|
2999
|
-
role: "status",
|
|
3000
|
-
children: [view.text, result.message === void 0 ? null : " · " + result.message]
|
|
3001
|
-
});
|
|
3002
|
-
})()
|
|
3003
|
-
]
|
|
3004
|
-
}, model.id))
|
|
3005
|
-
}),
|
|
3006
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
3007
|
-
className: "dsm-workbuddy-model-capability-note",
|
|
3008
|
-
children: t("row.modelCapabilityPending")
|
|
3009
|
-
}),
|
|
3010
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3011
|
-
className: "dsm-workbuddy-model-actions",
|
|
3012
|
-
children: [
|
|
3013
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("a", {
|
|
3014
|
-
className: "dsm-workbuddy-usage-cheer",
|
|
3015
|
-
href: WORKBUDDY_GITHUB_URL,
|
|
3016
|
-
target: "_blank",
|
|
3017
|
-
rel: "noopener noreferrer",
|
|
3018
|
-
children: [t("row.cheer"), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3019
|
-
className: "dsm-workbuddy-usage-cheer-star",
|
|
3020
|
-
"aria-hidden": "true",
|
|
3021
|
-
children: "★"
|
|
3022
|
-
})]
|
|
3023
|
-
}),
|
|
3024
|
-
saveError === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3025
|
-
className: "dsm-workbuddy-model-save-error",
|
|
3026
|
-
children: t("row.saveError", { message: saveError })
|
|
3027
|
-
}),
|
|
3028
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3029
|
-
className: "dsm-workbuddy-model-actions-buttons",
|
|
3030
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
3031
|
-
type: "button",
|
|
3032
|
-
className: "dsm-btn dsm-btn-outline",
|
|
3033
|
-
disabled: !dirty || saving || poolBusy || togglingRegion !== void 0,
|
|
3034
|
-
onClick: discardModels,
|
|
3035
|
-
children: t("row.discard")
|
|
3036
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
3037
|
-
type: "button",
|
|
3038
|
-
className: "dsm-btn dsm-btn-primary",
|
|
3039
|
-
disabled: !dirty || saving || poolBusy || togglingRegion !== void 0 || activeEnabledIds.size === 0,
|
|
3040
|
-
onClick: () => {
|
|
3041
|
-
saveModels();
|
|
3042
|
-
},
|
|
3043
|
-
children: saving ? t("row.saving") : t("row.save")
|
|
3044
|
-
})]
|
|
3045
|
-
})
|
|
3046
|
-
]
|
|
3047
|
-
})
|
|
3048
|
-
]
|
|
3049
|
-
}),
|
|
3050
2505
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(AccountPool, {
|
|
3051
2506
|
t,
|
|
3052
2507
|
region: activeRegion,
|
|
3053
2508
|
...status.pool === void 0 ? {} : { pool: status.pool },
|
|
3054
2509
|
...settingsScope === void 0 ? {} : { settingsScope },
|
|
2510
|
+
onRescan: () => {
|
|
2511
|
+
rescanAccounts();
|
|
2512
|
+
},
|
|
2513
|
+
rescanning: busy,
|
|
2514
|
+
onSelectAccount: (accountId) => {
|
|
2515
|
+
switchAccount(accountId);
|
|
2516
|
+
},
|
|
2517
|
+
selectingAccount: switchingAccount,
|
|
3055
2518
|
siblingBusy: saving || togglingRegion !== void 0,
|
|
3056
2519
|
onBusyChange: setPoolBusy,
|
|
3057
2520
|
onSaved: async () => await refreshUsage(activeRegion) !== void 0,
|
|
3058
2521
|
onRefresh: () => {
|
|
3059
2522
|
refreshUsage(activeRegion);
|
|
3060
|
-
}
|
|
3061
|
-
|
|
2523
|
+
}
|
|
2524
|
+
}),
|
|
2525
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("section", {
|
|
2526
|
+
className: "dsm-workbuddy-models",
|
|
2527
|
+
"aria-label": t("row.modelsTitle"),
|
|
2528
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2529
|
+
className: "dsm-workbuddy-models-fold",
|
|
2530
|
+
children: [
|
|
2531
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2532
|
+
className: "dsm-workbuddy-models-head",
|
|
2533
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
2534
|
+
className: "dsm-workbuddy-models-title-row",
|
|
2535
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("h3", {
|
|
2536
|
+
className: "dsm-workbuddy-models-title",
|
|
2537
|
+
children: [t("row.modelsTitle"), dirty ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2538
|
+
className: "dsm-workbuddy-models-dirty",
|
|
2539
|
+
children: t("row.modelsDirty")
|
|
2540
|
+
}) : null]
|
|
2541
|
+
})
|
|
2542
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
2543
|
+
className: "dsm-workbuddy-models-summary",
|
|
2544
|
+
children: t("row.modelsSummary", { count: activeEnabledIds.size })
|
|
2545
|
+
})] }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
2546
|
+
className: "dsm-workbuddy-models-head-actions",
|
|
2547
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2548
|
+
type: "button",
|
|
2549
|
+
className: "dsm-btn dsm-btn-outline",
|
|
2550
|
+
disabled: busy,
|
|
2551
|
+
onClick: () => {
|
|
2552
|
+
refreshModels();
|
|
2553
|
+
},
|
|
2554
|
+
children: busy ? t("row.modelsRefreshing") : t("row.modelsRefresh")
|
|
2555
|
+
})
|
|
2556
|
+
})]
|
|
2557
|
+
}),
|
|
2558
|
+
probeError === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
2559
|
+
className: "dsm-workbuddy-model-probe-result dsm-workbuddy-model-probe-result-bad",
|
|
2560
|
+
role: "alert",
|
|
2561
|
+
children: t("row.probeError", { message: probeError })
|
|
2562
|
+
}),
|
|
2563
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
2564
|
+
className: "dsm-workbuddy-model-list",
|
|
2565
|
+
children: visibleModels.map((model) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2566
|
+
className: `dsm-workbuddy-model${activeEnabledIds.has(model.id) ? "" : " dsm-workbuddy-model-disabled"}`,
|
|
2567
|
+
children: [
|
|
2568
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2569
|
+
className: "dsm-workbuddy-model-head",
|
|
2570
|
+
children: [
|
|
2571
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
2572
|
+
className: "dsm-workbuddy-model-enabled",
|
|
2573
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
2574
|
+
type: "checkbox",
|
|
2575
|
+
checked: activeEnabledIds.has(model.id),
|
|
2576
|
+
disabled: !canWrite || saving,
|
|
2577
|
+
onChange: () => {
|
|
2578
|
+
toggleModel(model.id);
|
|
2579
|
+
}
|
|
2580
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2581
|
+
className: "dsm-workbuddy-model-copy",
|
|
2582
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
2583
|
+
className: "dsm-workbuddy-model-name",
|
|
2584
|
+
children: [model.name, model.creditMultiplier === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
2585
|
+
className: "dsm-workbuddy-model-name-rate",
|
|
2586
|
+
children: [
|
|
2587
|
+
"(",
|
|
2588
|
+
model.creditMultiplier.toFixed(2),
|
|
2589
|
+
"x)"
|
|
2590
|
+
]
|
|
2591
|
+
})]
|
|
2592
|
+
})
|
|
2593
|
+
})]
|
|
2594
|
+
}),
|
|
2595
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
2596
|
+
className: "dsm-workbuddy-model-image",
|
|
2597
|
+
title: imageCheckboxHint(model, t),
|
|
2598
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
2599
|
+
type: "checkbox",
|
|
2600
|
+
checked: activeImageIds.has(model.id),
|
|
2601
|
+
disabled: !canWrite || saving,
|
|
2602
|
+
onChange: () => {
|
|
2603
|
+
toggleImage(model.id);
|
|
2604
|
+
}
|
|
2605
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("row.modelImage") })]
|
|
2606
|
+
}),
|
|
2607
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("fieldset", {
|
|
2608
|
+
className: "dsm-workbuddy-context-budget",
|
|
2609
|
+
"aria-label": t("row.contextBudget"),
|
|
2610
|
+
children: [model.nativeContextWindow > 2e5 ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
2611
|
+
type: "radio",
|
|
2612
|
+
name: `context-${model.id}`,
|
|
2613
|
+
checked: (activeContextBudgets[model.id] ?? 2e5) === 2e5,
|
|
2614
|
+
disabled: !canWrite || saving,
|
|
2615
|
+
onChange: () => {
|
|
2616
|
+
setContextBudget(model.id, 2e5);
|
|
2617
|
+
}
|
|
2618
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: "200K" })] }) : null, /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
2619
|
+
type: "radio",
|
|
2620
|
+
name: `context-${model.id}`,
|
|
2621
|
+
checked: model.nativeContextWindow <= 2e5 || activeContextBudgets[model.id] === model.nativeContextWindow,
|
|
2622
|
+
disabled: model.nativeContextWindow <= 2e5 || !canWrite || saving,
|
|
2623
|
+
onChange: () => {
|
|
2624
|
+
setContextBudget(model.id, model.nativeContextWindow);
|
|
2625
|
+
}
|
|
2626
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: formatCapacity(model.nativeContextWindow, t("row.modelUnknown")) })] })]
|
|
2627
|
+
}),
|
|
2628
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
2629
|
+
className: "dsm-workbuddy-model-actions-buttons",
|
|
2630
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2631
|
+
type: "button",
|
|
2632
|
+
className: "dsm-btn dsm-btn-outline dsm-workbuddy-model-probe",
|
|
2633
|
+
disabled: probing[model.id] === true || status.status !== "signed-in",
|
|
2634
|
+
title: t("row.probeHint"),
|
|
2635
|
+
onClick: () => {
|
|
2636
|
+
probeModels([model.id]);
|
|
2637
|
+
},
|
|
2638
|
+
children: probing[model.id] === true ? t("row.probing") : t("row.probe")
|
|
2639
|
+
})
|
|
2640
|
+
})
|
|
2641
|
+
]
|
|
2642
|
+
}),
|
|
2643
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
2644
|
+
className: "dsm-workbuddy-model-details",
|
|
2645
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2646
|
+
className: "dsm-workbuddy-model-meta",
|
|
2647
|
+
children: [
|
|
2648
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("row.modelContext", { context: formatCapacity(model.nativeContextWindow, t("row.modelUnknown")) }) }),
|
|
2649
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("row.modelOutput", { output: formatCapacity(model.maxTokens, t("row.modelUnknown")) }) }),
|
|
2650
|
+
model.reasoning === void 0 || model.reasoning.supportedEfforts === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("row.modelReasoning", { efforts: model.reasoning.supportedEfforts.join(" / ") }) })
|
|
2651
|
+
]
|
|
2652
|
+
})
|
|
2653
|
+
}),
|
|
2654
|
+
(() => {
|
|
2655
|
+
const result = probes[activeRegion]?.[model.id];
|
|
2656
|
+
if (result === void 0) return null;
|
|
2657
|
+
const view = probeResultView(result, t);
|
|
2658
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("p", {
|
|
2659
|
+
className: `dsm-workbuddy-model-probe-result dsm-workbuddy-model-probe-result-${view.tone}`,
|
|
2660
|
+
role: "status",
|
|
2661
|
+
children: [view.text, result.message === void 0 ? null : " · " + result.message]
|
|
2662
|
+
});
|
|
2663
|
+
})()
|
|
2664
|
+
]
|
|
2665
|
+
}, model.id))
|
|
2666
|
+
}),
|
|
2667
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
2668
|
+
className: "dsm-workbuddy-model-capability-note",
|
|
2669
|
+
children: t("row.modelCapabilityPending")
|
|
2670
|
+
}),
|
|
2671
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2672
|
+
className: "dsm-workbuddy-model-actions",
|
|
2673
|
+
children: [
|
|
2674
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("a", {
|
|
2675
|
+
className: "dsm-workbuddy-usage-cheer",
|
|
2676
|
+
href: WORKBUDDY_GITHUB_URL,
|
|
2677
|
+
target: "_blank",
|
|
2678
|
+
rel: "noopener noreferrer",
|
|
2679
|
+
children: [t("row.cheer"), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2680
|
+
className: "dsm-workbuddy-usage-cheer-star",
|
|
2681
|
+
"aria-hidden": "true",
|
|
2682
|
+
children: "★"
|
|
2683
|
+
})]
|
|
2684
|
+
}),
|
|
2685
|
+
saveError === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2686
|
+
className: "dsm-workbuddy-model-save-error",
|
|
2687
|
+
children: t("row.saveError", { message: saveError })
|
|
2688
|
+
}),
|
|
2689
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2690
|
+
className: "dsm-workbuddy-model-actions-buttons",
|
|
2691
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2692
|
+
type: "button",
|
|
2693
|
+
className: "dsm-btn dsm-btn-outline",
|
|
2694
|
+
disabled: !dirty || saving || poolBusy || togglingRegion !== void 0,
|
|
2695
|
+
onClick: discardModels,
|
|
2696
|
+
children: t("row.discard")
|
|
2697
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2698
|
+
type: "button",
|
|
2699
|
+
className: "dsm-btn dsm-btn-primary",
|
|
2700
|
+
disabled: !dirty || saving || poolBusy || togglingRegion !== void 0 || activeEnabledIds.size === 0,
|
|
2701
|
+
onClick: () => {
|
|
2702
|
+
saveModels();
|
|
2703
|
+
},
|
|
2704
|
+
children: saving ? t("row.saving") : t("row.save")
|
|
2705
|
+
})]
|
|
2706
|
+
})
|
|
2707
|
+
]
|
|
2708
|
+
})
|
|
2709
|
+
]
|
|
2710
|
+
})
|
|
3062
2711
|
})
|
|
3063
2712
|
] }) : null,
|
|
3064
|
-
status.status === "signed-out" ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
|
|
3065
|
-
|
|
3066
|
-
|
|
3067
|
-
|
|
3068
|
-
|
|
3069
|
-
|
|
3070
|
-
|
|
2713
|
+
status.status === "signed-out" ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
|
|
2714
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
2715
|
+
className: "dsm-workbuddy-usage-text",
|
|
2716
|
+
children: signedOutText(notice, t)
|
|
2717
|
+
}),
|
|
2718
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
2719
|
+
className: "dsm-workbuddy-usage-account-actions",
|
|
2720
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2721
|
+
type: "button",
|
|
2722
|
+
className: "dsm-btn dsm-btn-outline",
|
|
2723
|
+
disabled: busy,
|
|
2724
|
+
onClick: () => {
|
|
2725
|
+
rescanAccounts();
|
|
2726
|
+
},
|
|
2727
|
+
children: busy ? t("row.accountsScanning") : t("row.accountsRescan")
|
|
2728
|
+
})
|
|
2729
|
+
}),
|
|
2730
|
+
searched.length > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SearchedPaths, {
|
|
2731
|
+
items: searched,
|
|
2732
|
+
t
|
|
2733
|
+
}) : null
|
|
2734
|
+
] }) : null,
|
|
3071
2735
|
status.status === "error" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
3072
2736
|
className: "dsm-workbuddy-usage-error",
|
|
3073
2737
|
children: status.message
|
|
@@ -3101,7 +2765,6 @@ window.__ModuleLoader__.load({
|
|
|
3101
2765
|
"row.tabSwitchHint": "Uncheck to switch this provider off. Its models disappear from the DSH model picker; your account, credits, and model settings are kept and return when you check it again.",
|
|
3102
2766
|
"row.tabSwitchAria": "Offer the {region} provider to DSH",
|
|
3103
2767
|
"row.tabOffNotice": "This provider is switched off: none of its models are offered to DSH. Your account, credits, and model settings are kept — check the box above the tab to bring it back.",
|
|
3104
|
-
"row.signedOut": "Not signed in",
|
|
3105
2768
|
"row.signedOutHint": "Sign in once in the WorkBuddy desktop app; this plugin follows that sign-in automatically.",
|
|
3106
2769
|
"row.signedOutWrongRegion": "Your WorkBuddy sign-in was found, but it belongs to the other region tab. Switch tabs above to use it — there is nothing to sign in again.",
|
|
3107
2770
|
"row.signedOutEncrypted": "Your WorkBuddy sign-in is present, but its token fields are encrypted and the plugin could not read the key. Install the WorkBuddy desktop app, or set WORKBUDDY_APP_EXECUTABLE to its executable — signing in again will not change this.",
|
|
@@ -3115,8 +2778,6 @@ window.__ModuleLoader__.load({
|
|
|
3115
2778
|
"row.sourceDesktop": "desktop app",
|
|
3116
2779
|
"row.sourceDsh": "plugin copy",
|
|
3117
2780
|
"row.searchedMore": "Show the {count} path(s) that were not found",
|
|
3118
|
-
"row.signedIn": "Signed in: {accountName}",
|
|
3119
|
-
"row.tokenExpiry": "Access token expires {expiresAt} (auto-renewal)",
|
|
3120
2781
|
"row.reloginHint": "If errors occur, sign back in to the WorkBuddy app.",
|
|
3121
2782
|
"row.credentialRejectedTitle": "WorkBuddy refused this account's token",
|
|
3122
2783
|
"row.credentialRejectedIntro": "The token for \"{accountName}\" is no longer accepted by the upstream, so credits and check-in are unavailable. Signing in again only helps if the problem is the token itself — here is what actually applies:",
|
|
@@ -3129,34 +2790,24 @@ window.__ModuleLoader__.load({
|
|
|
3129
2790
|
"row.poolErrUnavailable": "The account pool is not available in this build (or its batch route is missing). Restart the app; if it persists, the plugin needs reinstalling.",
|
|
3130
2791
|
"row.poolErrFailed": "The batch failed on the host side: {message}",
|
|
3131
2792
|
"row.poolErrHttp": "The batch was refused (HTTP {status}). Check the plugin is up to date and try again.",
|
|
3132
|
-
"row.poolErrDisabled": "The account pool is switched off for this region. Turn it on and save before running a batch.",
|
|
3133
2793
|
"row.poolErrNoMembers": "No account is checked into this pool — the batch had nothing to run. Check at least one account and save.",
|
|
3134
2794
|
"row.poolErrNoLiveMembers": "Every account checked into this pool has lost its local sign-in, so the batch had nothing to run. Sign in again in the WorkBuddy desktop app, or save to drop them.",
|
|
3135
2795
|
"row.poolErrNoFreeModel": "This region has no zero-multiplier model to test with. Refresh the model catalog, or pick a target model yourself.",
|
|
2796
|
+
"row.poolErrNoCheckin": "This region has no daily check-in — the action is not offered here.",
|
|
3136
2797
|
"row.poolErrStaleModel": "The target model is no longer offered by this region. Pick another, or switch back to automatic.",
|
|
3137
|
-
"row.creditsTotalLabel": "Total remaining credits",
|
|
3138
|
-
"row.creditsMonthlyRemain": "Remaining {remain} / {size} · Refreshes {at}",
|
|
3139
|
-
"row.checkinClaim": "Check in",
|
|
3140
|
-
"row.checkinClaiming": "Checking in…",
|
|
3141
|
-
"row.checkinClaimed": "Checked in",
|
|
3142
|
-
"row.checkinError": "Check-in unavailable: {message}",
|
|
3143
2798
|
"row.creditsError": "Credit query unavailable: {message}",
|
|
3144
|
-
"row.creditsNoSoon": "No packages expire within 3 days.",
|
|
3145
|
-
"row.creditsExpiringSoon": "Expiring in 3 days",
|
|
3146
2799
|
"row.refresh": "Refresh",
|
|
3147
2800
|
"row.refreshing": "Refreshing…",
|
|
3148
|
-
"row.accountsTitle": "WorkBuddy account",
|
|
3149
2801
|
"row.accountUnnamed": "Unnamed account",
|
|
3150
2802
|
"row.accountsHint": "Choose from locally detected sign-ins. Tokens are never shown or saved in DSH settings.",
|
|
3151
2803
|
"row.accountsRescan": "Detect accounts again",
|
|
3152
2804
|
"row.accountsScanning": "Detecting…",
|
|
3153
|
-
"row.accountNoneInEffect": "No account in effect",
|
|
3154
|
-
"row.accountsSavedChoice": "Using the account saved for this region.",
|
|
3155
2805
|
"row.accountsWriteFailed": "The account change was not saved: {message} — the active profile's configuration file could not be updated (on Windows it may be locked by an antivirus scanner or a sync client). Your previous choice is still in effect; close whatever holds that file and try again (see docs/WINDOWS.md in the plugin repo for the exact path).",
|
|
3156
2806
|
"row.selectionLostHint": "The saved account no longer exists locally.",
|
|
3157
2807
|
"row.selectionLostMessage": "The account saved for this region is no longer among the local sign-ins (WorkBuddy replaced its login or cleaned up backups). Your other sign-ins are fine — pick a current one from the list above. Signing in again in the desktop app will not fix this.",
|
|
3158
2808
|
"row.modelsTitle": "Models",
|
|
3159
2809
|
"row.modelsSummary": "{count} enabled",
|
|
2810
|
+
"row.modelsDirty": "unsaved changes",
|
|
3160
2811
|
"row.modelsRefresh": "Refresh from WorkBuddy",
|
|
3161
2812
|
"row.modelsRefreshing": "Refreshing models…",
|
|
3162
2813
|
"row.discard": "Discard changes",
|
|
@@ -3192,10 +2843,8 @@ window.__ModuleLoader__.load({
|
|
|
3192
2843
|
"row.probeError": "Could not run the test: {message}",
|
|
3193
2844
|
"row.cheer": "Star on GitHub",
|
|
3194
2845
|
"row.poolTitle": "Account pool",
|
|
3195
|
-
"row.poolSummary": "{count} account(s) in this region · target model {model}",
|
|
3196
2846
|
"row.poolNoCandidate": "No account in this pool can be used right now.",
|
|
3197
2847
|
"row.poolNoCandidateHint": "Every account is rate-limited, out of credits, rejected, unreachable, or in a state this build cannot use. Re-check the accounts, or sign in again in the desktop app.",
|
|
3198
|
-
"row.poolRotating": "Auto-rotating",
|
|
3199
2848
|
"row.poolCheckinAll": "Check in all accounts",
|
|
3200
2849
|
"row.poolCheckingIn": "Checking in…",
|
|
3201
2850
|
"row.poolTestAll": "Test all accounts",
|
|
@@ -3206,6 +2855,8 @@ window.__ModuleLoader__.load({
|
|
|
3206
2855
|
"row.poolTargetNoneShort": "no free model",
|
|
3207
2856
|
"row.poolTargetFree": "Auto-picked free model: {model}",
|
|
3208
2857
|
"row.poolTargetPreferred": "Your chosen model: {model}",
|
|
2858
|
+
"row.poolTargetCurrentPreferred": "Current: {model} (your choice).",
|
|
2859
|
+
"row.poolTargetCurrentFree": "Current: {model} (auto-picked free model).",
|
|
3209
2860
|
"row.poolTargetNone": "This region has no zero-multiplier model yet.",
|
|
3210
2861
|
"row.poolTargetStale": "The target model you saved is no longer offered: {model}",
|
|
3211
2862
|
"row.poolTargetStaleHint": "The region's catalog changed and no longer lists this model, so testing is off rather than running against a model the upstream may not accept. Pick another target, or switch back to automatic.",
|
|
@@ -3230,14 +2881,12 @@ window.__ModuleLoader__.load({
|
|
|
3230
2881
|
"row.poolRetryAt": "Back after {at}",
|
|
3231
2882
|
"row.poolRetryUnknown": "The upstream gave no time for when this frees up",
|
|
3232
2883
|
"row.poolSettingsTitle": "Account pool settings",
|
|
3233
|
-
"row.poolSettingsHint": "These
|
|
2884
|
+
"row.poolSettingsHint": "These are preferences: they take effect when you save (same as the model list). Test results are written automatically and are not affected by this button.",
|
|
3234
2885
|
"row.poolEnabled": "Enable the account pool",
|
|
3235
|
-
"row.poolEnabledHint": "While off, the plugin
|
|
3236
|
-
"row.
|
|
3237
|
-
"row.
|
|
3238
|
-
"row.
|
|
3239
|
-
"row.poolIntervalHint": "Minutes between automatic tests of this region's accounts. Runs only while BOTH the pool and this region's provider are enabled; switching either off stops the timer.",
|
|
3240
|
-
"row.poolIntervalUnit": "minutes",
|
|
2886
|
+
"row.poolEnabledHint": "While on, this region's pool decides who serves every request (usable first, then credits, soonest-expiring, freshest credential) and retries on another account when one fails. While off, the plugin does not pick or switch: the account you selected serves every request and a failure is reported as-is. Check-in and testing are MANUAL actions and stay available either way — the switch only controls whether the plugin decides by itself.",
|
|
2887
|
+
"row.poolManualAccountLabel": "Account in use",
|
|
2888
|
+
"row.poolManualAccountHint": "Which account serves this region while the pool is off. Turn the pool on and the ranking picks instead.",
|
|
2889
|
+
"row.poolManualAccountNone": "No account in effect",
|
|
3241
2890
|
"row.poolTargetLabel": "Target test model",
|
|
3242
2891
|
"row.poolTargetHint": "Auto (default) picks a zero-multiplier model from this region's catalog, preferring the largest context window. The two regions can pick different models. A manual choice always wins.",
|
|
3243
2892
|
"row.poolTargetAutoOption": "Auto — pick a free model",
|
|
@@ -3247,13 +2896,7 @@ window.__ModuleLoader__.load({
|
|
|
3247
2896
|
"row.poolSavedStaleRefresh": "Saved, but the panel could not re-read it. The value is stored; the next refresh will show it.",
|
|
3248
2897
|
"row.poolDiscard": "Discard changes",
|
|
3249
2898
|
"row.poolDirty": "● Unsaved changes",
|
|
3250
|
-
"row.poolSaveIdle": "Change any setting above to save it.",
|
|
3251
|
-
"row.poolSaveDirty": "You have unsaved changes — saving writes them to this region.",
|
|
3252
|
-
"row.poolManualLocked": "Manual selection is disabled: this region is rotating automatically",
|
|
3253
|
-
"row.poolManualLockedHint": "The plugin picks the account by availability, then credits, then expiry, so you cannot pin one manually. The dropdown above is disabled to avoid showing one account while another is billed.",
|
|
3254
2899
|
"row.poolManualUnlock": "Turn off rotation",
|
|
3255
|
-
"row.poolUnlockPending": "Save to apply — until then the plugin is still rotating.",
|
|
3256
|
-
"row.poolUnlockSaved": "Rotation off — save to unlock",
|
|
3257
2900
|
"row.poolLogTitle": "Activity",
|
|
3258
2901
|
"row.poolLogClear": "Clear",
|
|
3259
2902
|
"row.poolLogEmpty": "Nothing yet. Check-ins, tests, and account switches are recorded here.",
|
|
@@ -3267,8 +2910,6 @@ window.__ModuleLoader__.load({
|
|
|
3267
2910
|
"row.poolLogTestRow": "{accountName}: {outcome}",
|
|
3268
2911
|
"row.poolLogTestDone": "Test finished; the pool was re-ranked",
|
|
3269
2912
|
"row.poolLogTestRowMalformed": "{accountName}: the host returned no result for this account",
|
|
3270
|
-
"row.poolLogRotated": "Switched billing from {from} to {to}",
|
|
3271
|
-
"row.poolLogRotationStopped": "Rotation stopped — back to {from}",
|
|
3272
2913
|
"row.poolRegionNote": "The domestic and international pools are fully independent: accounts, switches, intervals, and target models never affect each other.",
|
|
3273
2914
|
"row.poolMembersTitle": "Accounts in this pool",
|
|
3274
2915
|
"row.poolMembersHint": "Only checked accounts are checked in, tested, and used for rotation. Nothing is selected by default — check the ones you want, then save.",
|
|
@@ -3280,6 +2921,8 @@ window.__ModuleLoader__.load({
|
|
|
3280
2921
|
"row.poolGhostMembers": "{count} saved account(s) are no longer signed in on this machine, so they are not counted and not used. Save again to drop them.",
|
|
3281
2922
|
"row.poolSelectAria": "Include {accountName} in this region's account pool",
|
|
3282
2923
|
"row.poolMember": "In pool",
|
|
2924
|
+
"row.poolCurrentBadge": "In use",
|
|
2925
|
+
"row.poolCurrentHeader": "In use: {account}",
|
|
3283
2926
|
"row.poolNotMember": "Not in pool",
|
|
3284
2927
|
"row.poolUnsavedMembers": "Selection changed — save to apply"
|
|
3285
2928
|
};
|
|
@@ -3294,7 +2937,6 @@ window.__ModuleLoader__.load({
|
|
|
3294
2937
|
"row.tabSwitchHint": "取消勾选即关闭该供应商:它的模型不再出现在 DSH 模型选择器里;账号、积分与模型设置都会保留,重新勾选即恢复。",
|
|
3295
2938
|
"row.tabSwitchAria": "是否向 DSH 提供{region}供应商",
|
|
3296
2939
|
"row.tabOffNotice": "该供应商已关闭:它的模型不会提供给 DSH。账号、积分与模型设置均已保留——勾选上方复选框即可恢复。",
|
|
3297
|
-
"row.signedOut": "未登录",
|
|
3298
2940
|
"row.signedOutHint": "在 WorkBuddy 桌面 App 里登录一次即可,插件会自动跟随当前登录的账号。",
|
|
3299
2941
|
"row.signedOutWrongRegion": "找到了你的 WorkBuddy 登录信息,但它属于另一个地区的标签页。切换到上方对应标签页即可使用——不需要重新登录。",
|
|
3300
2942
|
"row.signedOutEncrypted": "你的 WorkBuddy 登录信息在,但其中的 token 字段是加密的,插件拿不到密钥。请安装 WorkBuddy 桌面 App,或用环境变量 WORKBUDDY_APP_EXECUTABLE 指定它的可执行文件——重新登录无法解决这个问题。",
|
|
@@ -3308,8 +2950,6 @@ window.__ModuleLoader__.load({
|
|
|
3308
2950
|
"row.sourceDesktop": "桌面 App",
|
|
3309
2951
|
"row.sourceDsh": "插件副本",
|
|
3310
2952
|
"row.searchedMore": "显示另外 {count} 条「不存在」的路径",
|
|
3311
|
-
"row.signedIn": "已登录:{accountName}",
|
|
3312
|
-
"row.tokenExpiry": "访问令牌 {expiresAt} 过期(自动续期)",
|
|
3313
2953
|
"row.reloginHint": "出现错误,重新登录 WorkBuddy APP 即可。",
|
|
3314
2954
|
"row.credentialRejectedTitle": "WorkBuddy 拒绝了这个账号的 token",
|
|
3315
2955
|
"row.credentialRejectedIntro": "「{accountName}」的 token 已不被上游接受,所以积分与签到都用不了。重新登录只在「确实是 token 的问题」时才有用——当前实际适用的是下面这一条:",
|
|
@@ -3322,34 +2962,24 @@ window.__ModuleLoader__.load({
|
|
|
3322
2962
|
"row.poolErrUnavailable": "本版本没有可用的账号池(或其批量接口缺失)。请重启应用;若仍不行,插件可能需要重新安装。",
|
|
3323
2963
|
"row.poolErrFailed": "批量操作在宿主侧失败:{message}",
|
|
3324
2964
|
"row.poolErrHttp": "批量操作被拒绝(HTTP {status})。请确认插件是最新版本后重试。",
|
|
3325
|
-
"row.poolErrDisabled": "本区域的账号池是关闭的。请先启用并保存,再执行批量操作。",
|
|
3326
2965
|
"row.poolErrNoMembers": "本池没有勾选任何账号,批量操作没有可执行对象。请至少勾选一个账号并保存。",
|
|
3327
2966
|
"row.poolErrNoLiveMembers": "本池勾选的账号在本机都已不再登录,批量操作没有可执行对象。请在 WorkBuddy 桌面端重新登录,或保存以移除它们。",
|
|
3328
2967
|
"row.poolErrNoFreeModel": "本区域没有可用的免费(倍率为 0)模型。请先刷新模型目录,或自己指定一个目标模型。",
|
|
2968
|
+
"row.poolErrNoCheckin": "本区域没有每日签到 —— 这里不提供该操作。",
|
|
3329
2969
|
"row.poolErrStaleModel": "目标模型已不在本区域目录中。请改选一个,或切回自动。",
|
|
3330
|
-
"row.creditsTotalLabel": "总剩余积分",
|
|
3331
|
-
"row.creditsMonthlyRemain": "剩余 {remain} / {size} · {at} 刷新",
|
|
3332
|
-
"row.checkinClaim": "立即签到",
|
|
3333
|
-
"row.checkinClaiming": "签到中…",
|
|
3334
|
-
"row.checkinClaimed": "今日已签到",
|
|
3335
|
-
"row.checkinError": "签到状态获取失败:{message}",
|
|
3336
2970
|
"row.creditsError": "积分查询失败:{message}",
|
|
3337
|
-
"row.creditsNoSoon": "3 天内没有到期礼包。",
|
|
3338
|
-
"row.creditsExpiringSoon": "最近 3 天到期",
|
|
3339
2971
|
"row.refresh": "刷新",
|
|
3340
2972
|
"row.refreshing": "正在刷新…",
|
|
3341
|
-
"row.accountsTitle": "WorkBuddy 账号",
|
|
3342
2973
|
"row.accountUnnamed": "未命名账号",
|
|
3343
2974
|
"row.accountsHint": "选择本机检测到的登录账号;Token 不会显示,也不会保存到 DSH 设置。",
|
|
3344
2975
|
"row.accountsRescan": "重新检测账号",
|
|
3345
2976
|
"row.accountsScanning": "正在检测…",
|
|
3346
|
-
"row.accountNoneInEffect": "当前没有生效的账号",
|
|
3347
|
-
"row.accountsSavedChoice": "正在使用该区域保存的账号。",
|
|
3348
2977
|
"row.accountsWriteFailed": "账号改动未保存:{message} —— 当前 profile 的配置文件写入失败(Windows 上可能被杀毒软件或同步盘占用)。当前仍是原来保存的选择,请先关闭占用该文件的程序再重试(文件的确切路径见插件仓库的 docs/WINDOWS.md)。",
|
|
3349
2978
|
"row.selectionLostHint": "已保存的账号在本机已不存在。",
|
|
3350
2979
|
"row.selectionLostMessage": "该区域保存的账号已不在本机登录列表中(WorkBuddy 更换了登录或清理了备份文件)。本机其他登录仍然可用——请在上方列表中重新选择一个当前账号。重新登录桌面端 App 无法修复此问题。",
|
|
3351
2980
|
"row.modelsTitle": "模型",
|
|
3352
2981
|
"row.modelsSummary": "已启用 {count} 个",
|
|
2982
|
+
"row.modelsDirty": "有未保存的修改",
|
|
3353
2983
|
"row.modelsRefresh": "从 WorkBuddy 刷新",
|
|
3354
2984
|
"row.modelsRefreshing": "正在刷新模型…",
|
|
3355
2985
|
"row.discard": "放弃修改",
|
|
@@ -3385,10 +3015,8 @@ window.__ModuleLoader__.load({
|
|
|
3385
3015
|
"row.probeError": "无法执行测试:{message}",
|
|
3386
3016
|
"row.cheer": "鼓励一下",
|
|
3387
3017
|
"row.poolTitle": "账号池",
|
|
3388
|
-
"row.poolSummary": "本区域 {count} 个账号 · 目标模型 {model}",
|
|
3389
3018
|
"row.poolNoCandidate": "账号池里目前没有可用的账号。",
|
|
3390
3019
|
"row.poolNoCandidateHint": "每个账号要么被限流、要么积分耗尽、要么被上游拒绝、要么连不上上游,或处于本版本无法使用的状态。请重新检测账号,或在桌面端重新登录。",
|
|
3391
|
-
"row.poolRotating": "自动轮换中",
|
|
3392
3020
|
"row.poolCheckinAll": "一键签到所有账号",
|
|
3393
3021
|
"row.poolCheckingIn": "签到中…",
|
|
3394
3022
|
"row.poolTestAll": "一键测试所有账号",
|
|
@@ -3399,6 +3027,8 @@ window.__ModuleLoader__.load({
|
|
|
3399
3027
|
"row.poolTargetNoneShort": "无免费模型",
|
|
3400
3028
|
"row.poolTargetFree": "自动选中的免费模型:{model}",
|
|
3401
3029
|
"row.poolTargetPreferred": "你指定的模型:{model}",
|
|
3030
|
+
"row.poolTargetCurrentPreferred": "当前:{model}(你指定)。",
|
|
3031
|
+
"row.poolTargetCurrentFree": "当前:{model}(自动挑选的免费模型)。",
|
|
3402
3032
|
"row.poolTargetNone": "本区域暂时没有倍率为 0 的免费模型。",
|
|
3403
3033
|
"row.poolTargetStale": "你保存的目标模型已不在目录中:{model}",
|
|
3404
3034
|
"row.poolTargetStaleHint": "本区域的模型目录已变化,不再包含这个模型,因此测试被停用——而不是拿一个上游可能不接受的模型去跑。请改选一个目标模型,或切回自动。",
|
|
@@ -3423,14 +3053,12 @@ window.__ModuleLoader__.load({
|
|
|
3423
3053
|
"row.poolRetryAt": "{at} 之后可再用",
|
|
3424
3054
|
"row.poolRetryUnknown": "上游未给出何时恢复",
|
|
3425
3055
|
"row.poolSettingsTitle": "账号池设置",
|
|
3426
|
-
"row.poolSettingsHint": "
|
|
3056
|
+
"row.poolSettingsHint": "下面是偏好设置:点保存才生效(与「模型管理」一致)。测试结果由插件自动写入,不受这个保存按钮影响。",
|
|
3427
3057
|
"row.poolEnabled": "启用账号池",
|
|
3428
|
-
"row.poolEnabledHint": "
|
|
3429
|
-
"row.
|
|
3430
|
-
"row.
|
|
3431
|
-
"row.
|
|
3432
|
-
"row.poolIntervalHint": "每隔多少分钟自动测试本区域账号。需要「账号池」与本区域的供应商都启用;任一关闭即停止定时器。",
|
|
3433
|
-
"row.poolIntervalUnit": "分钟",
|
|
3058
|
+
"row.poolEnabledHint": "开启后由本区域的账号池自动决定每一次请求用谁(可用优先,其次积分高、快过期的积分包、凭据更新的),失败时自动换下一个可用账号。关闭后插件不替你选、也不替你换:你选的账号服务每一次请求,失败就如实报错。签到与测试是**手动动作**,两种状态下都可用 —— 这个开关只管「插件要不要自己决定」。",
|
|
3059
|
+
"row.poolManualAccountLabel": "使用哪个账号",
|
|
3060
|
+
"row.poolManualAccountHint": "账号池关闭时,由你在这里选定服务本区域的账号。开启账号池后改由排序自动决定。",
|
|
3061
|
+
"row.poolManualAccountNone": "当前没有生效的账号",
|
|
3434
3062
|
"row.poolTargetLabel": "目标测试模型",
|
|
3435
3063
|
"row.poolTargetHint": "「自动」(默认)会从本区域目录里挑一个倍率为 0 的免费模型,多个候选取上下文窗口最大的。国内版与国际版可能挑到不同的模型。手动指定优先于自动。",
|
|
3436
3064
|
"row.poolTargetAutoOption": "自动 —— 挑一个免费模型",
|
|
@@ -3440,13 +3068,7 @@ window.__ModuleLoader__.load({
|
|
|
3440
3068
|
"row.poolSavedStaleRefresh": "已保存,但面板未能重新读取。设置已写入;下次刷新后会显示。",
|
|
3441
3069
|
"row.poolDiscard": "放弃修改",
|
|
3442
3070
|
"row.poolDirty": "● 有未保存的修改",
|
|
3443
|
-
"row.poolSaveIdle": "修改任一设置后即可保存。",
|
|
3444
|
-
"row.poolSaveDirty": "有未保存的修改 —— 保存后写入本区域配置。",
|
|
3445
|
-
"row.poolManualLocked": "手动选择已停用:本区域正在自动轮换",
|
|
3446
|
-
"row.poolManualLockedHint": "插件按「可用性 → 积分 → 可用时间」自动选择账号,因此不能手动指定。上面的下拉已置灰,避免出现\"显示 A 却计费 B\"。",
|
|
3447
3071
|
"row.poolManualUnlock": "关闭自动轮换",
|
|
3448
|
-
"row.poolUnlockPending": "保存后生效 —— 在那之前插件仍在轮换。",
|
|
3449
|
-
"row.poolUnlockSaved": "已关闭轮换 —— 保存后解锁",
|
|
3450
3072
|
"row.poolLogTitle": "活动记录",
|
|
3451
3073
|
"row.poolLogClear": "清空",
|
|
3452
3074
|
"row.poolLogEmpty": "尚无活动。签到、测试与账号切换都会记录在这里。",
|
|
@@ -3460,8 +3082,6 @@ window.__ModuleLoader__.load({
|
|
|
3460
3082
|
"row.poolLogTestRow": "{accountName}:{outcome}",
|
|
3461
3083
|
"row.poolLogTestDone": "测试完成,已重新计算候选账号",
|
|
3462
3084
|
"row.poolLogTestRowMalformed": "{accountName}:Host 未返回该账号的结果",
|
|
3463
|
-
"row.poolLogRotated": "计费账号已从 {from} 切换到 {to}",
|
|
3464
|
-
"row.poolLogRotationStopped": "轮换已停止 —— 回到 {from}",
|
|
3465
3085
|
"row.poolRegionNote": "国内版与国际版是完全独立的两个账号池:账号、开关、间隔、目标模型互不影响。",
|
|
3466
3086
|
"row.poolMembersTitle": "本池包含的账号",
|
|
3467
3087
|
"row.poolMembersHint": "只有勾选的账号才会被签到、测试和轮换。默认一个都不勾 —— 请勾选你要的账号,然后保存。",
|
|
@@ -3473,6 +3093,8 @@ window.__ModuleLoader__.load({
|
|
|
3473
3093
|
"row.poolGhostMembers": "有 {count} 个已保存的账号在本机已不再登录,因此不计入、也不会被使用。再保存一次即可移除它们。",
|
|
3474
3094
|
"row.poolSelectAria": "把 {accountName} 加入本区域的账号池",
|
|
3475
3095
|
"row.poolMember": "已在池中",
|
|
3096
|
+
"row.poolCurrentBadge": "当前使用",
|
|
3097
|
+
"row.poolCurrentHeader": "当前使用:{account}",
|
|
3476
3098
|
"row.poolNotMember": "未入池",
|
|
3477
3099
|
"row.poolUnsavedMembers": "勾选已改动 —— 保存后生效"
|
|
3478
3100
|
};
|