dsh-codearts 1.1.3 → 1.2.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 +29 -0
- package/INSTALL.en.md +24 -0
- package/INSTALL.md +22 -0
- package/lib/bin.mjs +2 -2
- package/lib/client/index.d.cts +16 -0
- package/lib/client/index.js +499 -86
- package/lib/estimate-BePoD-Np.mjs +113 -0
- package/lib/index.d.mts +759 -2
- package/lib/index.mjs +116 -15
- package/lib/models-CgKCibpk.mjs +377 -0
- package/lib/openai-completions-Cn5ZQvOO.mjs +19392 -0
- package/lib/rolldown-runtime-B4iAMlE-.mjs +35 -0
- package/lib/{usage-5YK7bW-e.mjs → usage-B0NfG8vZ.mjs} +1 -12
- package/package.json +1 -4
package/lib/client/index.js
CHANGED
|
@@ -33,70 +33,70 @@ if (typeof document !== "undefined" && document.querySelector("style[data-plugin
|
|
|
33
33
|
document.head.appendChild(tag);
|
|
34
34
|
}
|
|
35
35
|
var CodeArtsPluginCard_module_css_default = {
|
|
36
|
-
"
|
|
37
|
-
"
|
|
38
|
-
"
|
|
39
|
-
"
|
|
40
|
-
"
|
|
41
|
-
"
|
|
42
|
-
"
|
|
43
|
-
"bar": "xoziZG_bar",
|
|
44
|
-
"balance": "xoziZG_balance",
|
|
45
|
-
"link": "xoziZG_link",
|
|
46
|
-
"tabPanel": "xoziZG_tabPanel",
|
|
36
|
+
"accountName": "xoziZG_accountName",
|
|
37
|
+
"modelFill": "xoziZG_modelFill",
|
|
38
|
+
"hint": "xoziZG_hint",
|
|
39
|
+
"section": "xoziZG_section",
|
|
40
|
+
"area": "xoziZG_area",
|
|
41
|
+
"updated": "xoziZG_updated",
|
|
42
|
+
"error": "xoziZG_error",
|
|
47
43
|
"headText": "xoziZG_headText",
|
|
44
|
+
"accountCard": "xoziZG_accountCard",
|
|
45
|
+
"barFill": "xoziZG_barFill",
|
|
46
|
+
"choiceRow": "xoziZG_choiceRow",
|
|
47
|
+
"field": "xoziZG_field",
|
|
48
|
+
"body": "xoziZG_body",
|
|
49
|
+
"accountEntry": "xoziZG_accountEntry",
|
|
50
|
+
"modelChart": "xoziZG_modelChart",
|
|
51
|
+
"modelRequests": "xoziZG_modelRequests",
|
|
52
|
+
"link": "xoziZG_link",
|
|
53
|
+
"actions": "xoziZG_actions",
|
|
54
|
+
"creditRows": "xoziZG_creditRows",
|
|
55
|
+
"desc": "xoziZG_desc",
|
|
48
56
|
"balanceRow": "xoziZG_balanceRow",
|
|
49
|
-
"card": "xoziZG_card",
|
|
50
|
-
"manualForm": "xoziZG_manualForm",
|
|
51
|
-
"name": "xoziZG_name",
|
|
52
|
-
"creditRow": "xoziZG_creditRow",
|
|
53
57
|
"creditLink": "xoziZG_creditLink",
|
|
54
|
-
"
|
|
55
|
-
"
|
|
56
|
-
"
|
|
57
|
-
"accountHint": "xoziZG_accountHint",
|
|
58
|
+
"modelName": "xoziZG_modelName",
|
|
59
|
+
"card": "xoziZG_card",
|
|
60
|
+
"bar": "xoziZG_bar",
|
|
58
61
|
"meta": "xoziZG_meta",
|
|
59
|
-
"
|
|
60
|
-
"
|
|
61
|
-
"
|
|
62
|
+
"sectionHead": "xoziZG_sectionHead",
|
|
63
|
+
"balanceMeta": "xoziZG_balanceMeta",
|
|
64
|
+
"modelTrack": "xoziZG_modelTrack",
|
|
65
|
+
"usageFill": "xoziZG_usageFill",
|
|
66
|
+
"choices": "xoziZG_choices",
|
|
67
|
+
"accountRowActions": "xoziZG_accountRowActions",
|
|
68
|
+
"tabPanel": "xoziZG_tabPanel",
|
|
69
|
+
"expiryRow": "xoziZG_expiryRow",
|
|
70
|
+
"tabs": "xoziZG_tabs",
|
|
71
|
+
"fieldLabel": "xoziZG_fieldLabel",
|
|
72
|
+
"manualSummary": "xoziZG_manualSummary",
|
|
73
|
+
"creditRow": "xoziZG_creditRow",
|
|
62
74
|
"creditHint": "xoziZG_creditHint",
|
|
63
|
-
"
|
|
64
|
-
"accountCard": "xoziZG_accountCard",
|
|
75
|
+
"statusLine": "xoziZG_statusLine",
|
|
65
76
|
"danger": "xoziZG_danger",
|
|
77
|
+
"modelRow": "xoziZG_modelRow",
|
|
78
|
+
"claimAllRow": "xoziZG_claimAllRow",
|
|
66
79
|
"accountToolbar": "xoziZG_accountToolbar",
|
|
67
|
-
"
|
|
68
|
-
"
|
|
69
|
-
"
|
|
80
|
+
"accountHint": "xoziZG_accountHint",
|
|
81
|
+
"expiryLabel": "xoziZG_expiryLabel",
|
|
82
|
+
"name": "xoziZG_name",
|
|
70
83
|
"header": "xoziZG_header",
|
|
71
|
-
"sectionHead": "xoziZG_sectionHead",
|
|
72
|
-
"accountEntry": "xoziZG_accountEntry",
|
|
73
|
-
"hint": "xoziZG_hint",
|
|
74
|
-
"error": "xoziZG_error",
|
|
75
|
-
"modelName": "xoziZG_modelName",
|
|
76
|
-
"field": "xoziZG_field",
|
|
77
|
-
"avatar": "xoziZG_avatar",
|
|
78
|
-
"modelTrack": "xoziZG_modelTrack",
|
|
79
|
-
"usageBar": "xoziZG_usageBar",
|
|
80
|
-
"updated": "xoziZG_updated",
|
|
81
|
-
"modelRow": "xoziZG_modelRow",
|
|
82
|
-
"expiryRow": "xoziZG_expiryRow",
|
|
83
|
-
"actions": "xoziZG_actions",
|
|
84
|
-
"accountRowActions": "xoziZG_accountRowActions",
|
|
85
84
|
"accountRow": "xoziZG_accountRow",
|
|
86
|
-
"
|
|
87
|
-
"
|
|
88
|
-
"
|
|
89
|
-
"
|
|
85
|
+
"sectionLabel": "xoziZG_sectionLabel",
|
|
86
|
+
"count": "xoziZG_count",
|
|
87
|
+
"balanceUnit": "xoziZG_balanceUnit",
|
|
88
|
+
"modelTokens": "xoziZG_modelTokens",
|
|
90
89
|
"accountNameRow": "xoziZG_accountNameRow",
|
|
91
|
-
"
|
|
90
|
+
"accountInfo": "xoziZG_accountInfo",
|
|
92
91
|
"creditBlock": "xoziZG_creditBlock",
|
|
93
|
-
"
|
|
94
|
-
"
|
|
95
|
-
"
|
|
96
|
-
"
|
|
97
|
-
"
|
|
98
|
-
"
|
|
99
|
-
"
|
|
92
|
+
"balance": "xoziZG_balance",
|
|
93
|
+
"avatar": "xoziZG_avatar",
|
|
94
|
+
"manual": "xoziZG_manual",
|
|
95
|
+
"usageBar": "xoziZG_usageBar",
|
|
96
|
+
"creditFooter": "xoziZG_creditFooter",
|
|
97
|
+
"check": "xoziZG_check",
|
|
98
|
+
"manualForm": "xoziZG_manualForm",
|
|
99
|
+
"modelShare": "xoziZG_modelShare"
|
|
100
100
|
};
|
|
101
101
|
//#endregion
|
|
102
102
|
//#region src/client/CodeArtsPluginCard.tsx
|
|
@@ -142,6 +142,11 @@ function claimMessagesOf(data, accountIds, t) {
|
|
|
142
142
|
for (const id of accountIds) out[id] = claimText(data, t);
|
|
143
143
|
return out;
|
|
144
144
|
}
|
|
145
|
+
/** 开关的兜底值:后端没这条路由(老版本)时按「都显示」渲染,与原有行为一致。 */
|
|
146
|
+
const DOCK_DEFAULT = {
|
|
147
|
+
credits: true,
|
|
148
|
+
queue: true
|
|
149
|
+
};
|
|
145
150
|
/** 一个 Tab 面板;未选中的用 hidden 留在 DOM 里,草稿和滚动位置都别丢。 */
|
|
146
151
|
function TabPanel({ id, labelledBy, active, children }) {
|
|
147
152
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
@@ -166,7 +171,7 @@ function claimText(data, t) {
|
|
|
166
171
|
if (data?.state === "unavailable") return t("claimUnavailable");
|
|
167
172
|
return t("claimFailed", { message: typeof data?.message === "string" && data.message !== "" ? data.message : "request failed" });
|
|
168
173
|
}
|
|
169
|
-
function formatClock(ts) {
|
|
174
|
+
function formatClock$1(ts) {
|
|
170
175
|
if (ts === void 0 || !Number.isFinite(ts)) return "";
|
|
171
176
|
try {
|
|
172
177
|
return new Date(ts).toLocaleTimeString([], {
|
|
@@ -204,7 +209,7 @@ function CreditBlock({ credits, error, t, onClaim, claiming, claimResult, justCl
|
|
|
204
209
|
used: formatCredits(c.total.used),
|
|
205
210
|
total: formatCredits(c.total.total)
|
|
206
211
|
});
|
|
207
|
-
const updated = formatClock(c?.fetchedAt);
|
|
212
|
+
const updated = formatClock$1(c?.fetchedAt);
|
|
208
213
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
209
214
|
className: CodeArtsPluginCard_module_css_default.creditBlock,
|
|
210
215
|
children: [
|
|
@@ -552,6 +557,56 @@ function CodeArtsPluginCard({ t }) {
|
|
|
552
557
|
const [manualOpen, setManualOpen] = (0, react.useState)(false);
|
|
553
558
|
const [tab, setTab] = (0, react.useState)("accounts");
|
|
554
559
|
const tabIdBase = (0, react.useId)();
|
|
560
|
+
/** 对话底部那一行的两个显隐开关(后端 = 插件 settings 里的两个布尔)。 */
|
|
561
|
+
const [dock, setDock] = (0, react.useState)(DOCK_DEFAULT);
|
|
562
|
+
const [dockWritable, setDockWritable] = (0, react.useState)(false);
|
|
563
|
+
const [dockSaving, setDockSaving] = (0, react.useState)(false);
|
|
564
|
+
const [dockError, setDockError] = (0, react.useState)(null);
|
|
565
|
+
async function refreshDock() {
|
|
566
|
+
try {
|
|
567
|
+
const res = await fetch("/api/codearts/dock", { headers: { Accept: "application/json" } });
|
|
568
|
+
if (!res.ok) return;
|
|
569
|
+
const data = await res.json();
|
|
570
|
+
if (data.dock !== void 0) setDock(data.dock);
|
|
571
|
+
setDockWritable(data.writable === true);
|
|
572
|
+
} catch {}
|
|
573
|
+
}
|
|
574
|
+
/**
|
|
575
|
+
* 改一个开关:先按新值渲染,写失败再退回去。
|
|
576
|
+
*
|
|
577
|
+
* 这里不像模型勾选那样攒草稿再点保存 —— 只有两个布尔,攒草稿反而让用户猜
|
|
578
|
+
* 「我刚才那一下到底生效没有」。
|
|
579
|
+
*/
|
|
580
|
+
async function toggleDock(key, on) {
|
|
581
|
+
const previous = dock;
|
|
582
|
+
setDock({
|
|
583
|
+
...dock,
|
|
584
|
+
[key]: on
|
|
585
|
+
});
|
|
586
|
+
setDockSaving(true);
|
|
587
|
+
setDockError(null);
|
|
588
|
+
try {
|
|
589
|
+
const res = await fetch("/api/codearts/dock", {
|
|
590
|
+
method: "POST",
|
|
591
|
+
headers: {
|
|
592
|
+
"Content-Type": "application/json",
|
|
593
|
+
Accept: "application/json"
|
|
594
|
+
},
|
|
595
|
+
body: JSON.stringify({ [key]: on })
|
|
596
|
+
});
|
|
597
|
+
if (!res.ok) {
|
|
598
|
+
const data = await res.json().catch(() => ({}));
|
|
599
|
+
throw new Error(typeof data.error === "string" ? data.error : `HTTP ${res.status}`);
|
|
600
|
+
}
|
|
601
|
+
const data = await res.json();
|
|
602
|
+
if (data.dock !== void 0) setDock(data.dock);
|
|
603
|
+
} catch (error) {
|
|
604
|
+
setDock(previous);
|
|
605
|
+
setDockError(error instanceof Error ? error.message : String(error));
|
|
606
|
+
} finally {
|
|
607
|
+
setDockSaving(false);
|
|
608
|
+
}
|
|
609
|
+
}
|
|
555
610
|
/**
|
|
556
611
|
* 领每日签到积分。给 id 就只领那个号,不给就逐个号都领一遍。
|
|
557
612
|
*
|
|
@@ -620,6 +675,9 @@ function CodeArtsPluginCard({ t }) {
|
|
|
620
675
|
(0, react.useEffect)(() => {
|
|
621
676
|
refreshSelection();
|
|
622
677
|
}, []);
|
|
678
|
+
(0, react.useEffect)(() => {
|
|
679
|
+
refreshDock();
|
|
680
|
+
}, []);
|
|
623
681
|
(0, react.useEffect)(() => {
|
|
624
682
|
if (status?.status === "signed-in") refreshSelection();
|
|
625
683
|
}, [status?.status]);
|
|
@@ -838,12 +896,19 @@ function CodeArtsPluginCard({ t }) {
|
|
|
838
896
|
label: t("tabUsage"),
|
|
839
897
|
id: tabId("usage"),
|
|
840
898
|
panelId: panelId("usage")
|
|
899
|
+
},
|
|
900
|
+
display: {
|
|
901
|
+
value: "display",
|
|
902
|
+
label: t("tabDisplay"),
|
|
903
|
+
id: tabId("display"),
|
|
904
|
+
panelId: panelId("display")
|
|
841
905
|
}
|
|
842
906
|
};
|
|
843
907
|
const tabs = [
|
|
844
908
|
tabItems.accounts,
|
|
845
909
|
tabItems.models,
|
|
846
|
-
...usageAvailable ? [tabItems.usage] : []
|
|
910
|
+
...usageAvailable ? [tabItems.usage] : [],
|
|
911
|
+
tabItems.display
|
|
847
912
|
];
|
|
848
913
|
const activeTab = tabs.some((item) => item.value === tab) ? tab : "accounts";
|
|
849
914
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
@@ -1138,6 +1203,57 @@ function CodeArtsPluginCard({ t }) {
|
|
|
1138
1203
|
accounts,
|
|
1139
1204
|
t
|
|
1140
1205
|
})
|
|
1206
|
+
}),
|
|
1207
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(TabPanel, {
|
|
1208
|
+
id: panelId("display"),
|
|
1209
|
+
labelledBy: tabId("display"),
|
|
1210
|
+
active: activeTab === "display",
|
|
1211
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1212
|
+
className: CodeArtsPluginCard_module_css_default.section,
|
|
1213
|
+
children: [
|
|
1214
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1215
|
+
className: CodeArtsPluginCard_module_css_default.sectionHead,
|
|
1216
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1217
|
+
className: CodeArtsPluginCard_module_css_default.sectionLabel,
|
|
1218
|
+
children: t("displayTitle")
|
|
1219
|
+
})
|
|
1220
|
+
}),
|
|
1221
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1222
|
+
className: CodeArtsPluginCard_module_css_default.hint,
|
|
1223
|
+
children: t("displayHint")
|
|
1224
|
+
}),
|
|
1225
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1226
|
+
className: CodeArtsPluginCard_module_css_default.choices,
|
|
1227
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1228
|
+
className: CodeArtsPluginCard_module_css_default.choiceRow,
|
|
1229
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Checkbox, {
|
|
1230
|
+
className: CodeArtsPluginCard_module_css_default.check,
|
|
1231
|
+
checked: dock.credits,
|
|
1232
|
+
disabled: !dockWritable || dockSaving,
|
|
1233
|
+
onChange: (on) => void toggleDock("credits", on),
|
|
1234
|
+
label: t("displayCreditsToggle")
|
|
1235
|
+
})
|
|
1236
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1237
|
+
className: CodeArtsPluginCard_module_css_default.choiceRow,
|
|
1238
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Checkbox, {
|
|
1239
|
+
className: CodeArtsPluginCard_module_css_default.check,
|
|
1240
|
+
checked: dock.queue,
|
|
1241
|
+
disabled: !dockWritable || dockSaving,
|
|
1242
|
+
onChange: (on) => void toggleDock("queue", on),
|
|
1243
|
+
label: t("displayQueueToggle")
|
|
1244
|
+
})
|
|
1245
|
+
})]
|
|
1246
|
+
}),
|
|
1247
|
+
!dockWritable && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1248
|
+
className: CodeArtsPluginCard_module_css_default.hint,
|
|
1249
|
+
children: t("displayNotWritable")
|
|
1250
|
+
}),
|
|
1251
|
+
dockError !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1252
|
+
className: CodeArtsPluginCard_module_css_default.error,
|
|
1253
|
+
children: t("displayFailed", { message: dockError })
|
|
1254
|
+
})
|
|
1255
|
+
]
|
|
1256
|
+
})
|
|
1141
1257
|
})
|
|
1142
1258
|
] }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1143
1259
|
className: CodeArtsPluginCard_module_css_default.section,
|
|
@@ -1197,15 +1313,20 @@ function CodeArtsPluginCard({ t }) {
|
|
|
1197
1313
|
//#endregion
|
|
1198
1314
|
//#region src/client/CodeArtsCreditDock.tsx
|
|
1199
1315
|
/**
|
|
1200
|
-
*
|
|
1316
|
+
* 对话底部的积分条 + 点开后的积分弹层。
|
|
1201
1317
|
*
|
|
1202
1318
|
* 用户最初的诉求就是「对话底部要显示积分」—— 这一条挂在
|
|
1203
1319
|
* `conversation.composer.dock` 槽位上(与宿主 session-stats strip 同位)。
|
|
1204
1320
|
* 只在当前 provider 是 codearts 时才拉取并显示一行「积分 N」,别的 provider 下
|
|
1205
1321
|
* 完全不渲染,不抢别人的行。
|
|
1206
1322
|
*
|
|
1323
|
+
* 那一行是个按钮:点开就是逐号明细(多账号下聚合数字本身没意义,得能看出是哪个号)。
|
|
1324
|
+
* 显隐由设置卡片「显示」页那两个开关管着,开关值随 `/status` 一起下发 —— 底部条本来
|
|
1325
|
+
* 就在轮询它,不必为两个布尔开一条新链路。代价是关掉开关最多要等一个轮询周期(60 秒)
|
|
1326
|
+
* 才看得见效果,这个延迟在设置卡片上写明了。
|
|
1327
|
+
*
|
|
1207
1328
|
* 积分数据走插件自己的 `/api/codearts/status`(带 TTL 缓存,见 src/credits.ts),
|
|
1208
|
-
* 不会因为这条 dock
|
|
1329
|
+
* 不会因为这条 dock 让上游多挨一次请求;弹层上那个「刷新积分」是唯一绕缓存的出口。
|
|
1209
1330
|
*/
|
|
1210
1331
|
/** 与 src/adapter.ts 的 CODEARTS_PROVIDER 对齐 —— client 不能 import host 模块。 */
|
|
1211
1332
|
const CODEARTS_PROVIDER = "codearts";
|
|
@@ -1215,6 +1336,12 @@ const QUEUE_PATH = "/api/codearts/queue";
|
|
|
1215
1336
|
const REFRESH_INTERVAL_MS = 6e4;
|
|
1216
1337
|
/** 排队位置变得快:官方客户端也是 5 秒一次;这是本机路由,打不疼上游。 */
|
|
1217
1338
|
const QUEUE_INTERVAL_MS = 5e3;
|
|
1339
|
+
/** 弹层宽度固定,好算它会不会顶出屏幕右边。 */
|
|
1340
|
+
const PANEL_WIDTH = 280;
|
|
1341
|
+
/** 弹层与那一行之间的缝隙。 */
|
|
1342
|
+
const PANEL_GAP = 8;
|
|
1343
|
+
/** 那一行在输入框下方,往上开才够空间;给弹层留的最小高度,不够就翻到下面。 */
|
|
1344
|
+
const PANEL_MIN_ABOVE = 260;
|
|
1218
1345
|
function readSelection(useProjection) {
|
|
1219
1346
|
if (useProjection === void 0) return void 0;
|
|
1220
1347
|
try {
|
|
@@ -1226,48 +1353,118 @@ function readSelection(useProjection) {
|
|
|
1226
1353
|
function isCodeArts(selection) {
|
|
1227
1354
|
return (selection?.next ?? selection?.lastUsed)?.provider === CODEARTS_PROVIDER;
|
|
1228
1355
|
}
|
|
1356
|
+
function formatNumber(value) {
|
|
1357
|
+
return new Intl.NumberFormat().format(Math.round(value));
|
|
1358
|
+
}
|
|
1359
|
+
function formatClock(ts) {
|
|
1360
|
+
if (ts === void 0 || !Number.isFinite(ts)) return "";
|
|
1361
|
+
try {
|
|
1362
|
+
return new Date(ts).toLocaleTimeString([], {
|
|
1363
|
+
hour: "2-digit",
|
|
1364
|
+
minute: "2-digit"
|
|
1365
|
+
});
|
|
1366
|
+
} catch {
|
|
1367
|
+
return "";
|
|
1368
|
+
}
|
|
1369
|
+
}
|
|
1370
|
+
/** 账号名与设置卡片同一套取法:备注 > 用户名 > id。 */
|
|
1371
|
+
function accountName(account) {
|
|
1372
|
+
return account.label?.trim() || account.userName?.trim() || account.id;
|
|
1373
|
+
}
|
|
1374
|
+
/**
|
|
1375
|
+
* 逐号明细。
|
|
1376
|
+
*
|
|
1377
|
+
* 老后端只有聚合的一份 `credits`,那时单账号就退回它 —— 别因为后端没升级就把
|
|
1378
|
+
* 弹层弹成一句「没有数据」。
|
|
1379
|
+
*/
|
|
1380
|
+
function rowsOf(data) {
|
|
1381
|
+
const byId = new Map((data.creditsByAccount ?? []).map((entry) => [entry.id, entry]));
|
|
1382
|
+
return (data.accounts ?? []).map((account) => {
|
|
1383
|
+
const entry = byId.get(account.id) ?? (byId.size === 0 && (data.accounts?.length ?? 0) === 1 ? {
|
|
1384
|
+
id: account.id,
|
|
1385
|
+
credits: data.credits,
|
|
1386
|
+
creditsError: data.creditsError
|
|
1387
|
+
} : void 0);
|
|
1388
|
+
const snapshot = entry?.credits;
|
|
1389
|
+
return {
|
|
1390
|
+
id: account.id,
|
|
1391
|
+
name: accountName(account),
|
|
1392
|
+
remain: snapshot?.credits.total.remain ?? null,
|
|
1393
|
+
expiring: snapshot?.credits.total.expiring ?? 0,
|
|
1394
|
+
...snapshot?.credits.fetchedAt === void 0 ? {} : { fetchedAt: snapshot.credits.fetchedAt },
|
|
1395
|
+
...entry?.creditsError === void 0 ? {} : { error: entry.creditsError }
|
|
1396
|
+
};
|
|
1397
|
+
});
|
|
1398
|
+
}
|
|
1399
|
+
/** 把弹层钉在按钮边上:优先往上开(那一行本来就在输入框下面),放不下就翻到下面。 */
|
|
1400
|
+
function anchorOf(rect) {
|
|
1401
|
+
const above = rect.top > PANEL_MIN_ABOVE;
|
|
1402
|
+
const width = Math.min(PANEL_WIDTH, window.innerWidth - 16);
|
|
1403
|
+
return {
|
|
1404
|
+
left: Math.max(8, Math.min(rect.left, window.innerWidth - width - 8)),
|
|
1405
|
+
top: above ? rect.top - PANEL_GAP : rect.bottom + PANEL_GAP,
|
|
1406
|
+
above
|
|
1407
|
+
};
|
|
1408
|
+
}
|
|
1229
1409
|
function CodeArtsCreditDock({ useProjection, t }) {
|
|
1230
1410
|
const active = isCodeArts(readSelection(useProjection));
|
|
1231
1411
|
const [remain, setRemain] = (0, react.useState)(null);
|
|
1232
1412
|
const [total, setTotal] = (0, react.useState)(0);
|
|
1413
|
+
const [rows, setRows] = (0, react.useState)([]);
|
|
1414
|
+
const [flags, setFlags] = (0, react.useState)({
|
|
1415
|
+
credits: true,
|
|
1416
|
+
queue: true
|
|
1417
|
+
});
|
|
1233
1418
|
const [queued, setQueued] = (0, react.useState)(null);
|
|
1419
|
+
const [anchor, setAnchor] = (0, react.useState)(null);
|
|
1420
|
+
const [refreshing, setRefreshing] = (0, react.useState)(false);
|
|
1234
1421
|
const mounted = (0, react.useRef)(true);
|
|
1422
|
+
const triggerRef = (0, react.useRef)(null);
|
|
1423
|
+
const panelRef = (0, react.useRef)(null);
|
|
1235
1424
|
(0, react.useEffect)(() => {
|
|
1236
1425
|
mounted.current = true;
|
|
1237
1426
|
return () => {
|
|
1238
1427
|
mounted.current = false;
|
|
1239
1428
|
};
|
|
1240
1429
|
}, []);
|
|
1430
|
+
const loadStatus = (0, react.useCallback)(async (force) => {
|
|
1431
|
+
try {
|
|
1432
|
+
const res = await fetch(force ? `${STATUS_PATH}?force=1` : STATUS_PATH, {
|
|
1433
|
+
headers: { Accept: "application/json" },
|
|
1434
|
+
credentials: "same-origin"
|
|
1435
|
+
});
|
|
1436
|
+
if (!res.ok) return;
|
|
1437
|
+
const data = await res.json().catch(() => void 0);
|
|
1438
|
+
if (data === void 0 || !mounted.current) return;
|
|
1439
|
+
setFlags(data.dock ?? {
|
|
1440
|
+
credits: true,
|
|
1441
|
+
queue: true
|
|
1442
|
+
});
|
|
1443
|
+
setRows(rowsOf(data));
|
|
1444
|
+
const snapshots = (data.creditsByAccount ?? []).map((entry) => entry.credits).filter((value) => value !== void 0);
|
|
1445
|
+
const pool = snapshots.length > 0 ? snapshots : data.credits === void 0 ? [] : [data.credits];
|
|
1446
|
+
if (pool.length === 0) {
|
|
1447
|
+
setRemain(null);
|
|
1448
|
+
return;
|
|
1449
|
+
}
|
|
1450
|
+
setRemain(Math.round(pool.reduce((sum, item) => sum + item.credits.total.remain, 0)));
|
|
1451
|
+
setTotal(Math.round(pool.reduce((sum, item) => sum + item.credits.total.total, 0)));
|
|
1452
|
+
} catch {}
|
|
1453
|
+
}, []);
|
|
1241
1454
|
(0, react.useEffect)(() => {
|
|
1242
1455
|
if (!active) {
|
|
1243
1456
|
setRemain(null);
|
|
1457
|
+
setRows([]);
|
|
1458
|
+
setAnchor(null);
|
|
1244
1459
|
return;
|
|
1245
1460
|
}
|
|
1246
1461
|
let timer;
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
const res = await fetch(STATUS_PATH, {
|
|
1250
|
-
headers: { Accept: "application/json" },
|
|
1251
|
-
credentials: "same-origin"
|
|
1252
|
-
});
|
|
1253
|
-
if (!res.ok) return;
|
|
1254
|
-
const data = await res.json().catch(() => void 0);
|
|
1255
|
-
if (data === void 0) return;
|
|
1256
|
-
const snapshots = (data.creditsByAccount ?? []).map((entry) => entry.credits).filter((value) => value !== void 0);
|
|
1257
|
-
const pool = snapshots.length > 0 ? snapshots : data.credits === void 0 ? [] : [data.credits];
|
|
1258
|
-
if (pool.length === 0) return;
|
|
1259
|
-
if (mounted.current) {
|
|
1260
|
-
setRemain(Math.round(pool.reduce((sum, item) => sum + item.credits.total.remain, 0)));
|
|
1261
|
-
setTotal(Math.round(pool.reduce((sum, item) => sum + item.credits.total.total, 0)));
|
|
1262
|
-
}
|
|
1263
|
-
} catch {}
|
|
1264
|
-
};
|
|
1265
|
-
refresh();
|
|
1266
|
-
timer = window.setInterval(refresh, REFRESH_INTERVAL_MS);
|
|
1462
|
+
loadStatus(false);
|
|
1463
|
+
timer = window.setInterval(() => void loadStatus(false), REFRESH_INTERVAL_MS);
|
|
1267
1464
|
return () => {
|
|
1268
1465
|
if (timer !== void 0) window.clearInterval(timer);
|
|
1269
1466
|
};
|
|
1270
|
-
}, [active]);
|
|
1467
|
+
}, [active, loadStatus]);
|
|
1271
1468
|
(0, react.useEffect)(() => {
|
|
1272
1469
|
if (!active) {
|
|
1273
1470
|
setQueued(null);
|
|
@@ -1291,16 +1488,59 @@ function CodeArtsCreditDock({ useProjection, t }) {
|
|
|
1291
1488
|
if (timer !== void 0) window.clearInterval(timer);
|
|
1292
1489
|
};
|
|
1293
1490
|
}, [active]);
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1491
|
+
const close = (0, react.useCallback)(() => setAnchor(null), []);
|
|
1492
|
+
(0, react.useEffect)(() => {
|
|
1493
|
+
if (anchor === null) return;
|
|
1494
|
+
const onPointerDown = (event) => {
|
|
1495
|
+
const target = event.target;
|
|
1496
|
+
if (target === null) return;
|
|
1497
|
+
if (panelRef.current?.contains(target) === true || triggerRef.current?.contains(target) === true) return;
|
|
1498
|
+
close();
|
|
1499
|
+
};
|
|
1500
|
+
const onKeyDown = (event) => {
|
|
1501
|
+
if (event.key === "Escape") close();
|
|
1502
|
+
};
|
|
1503
|
+
window.addEventListener("pointerdown", onPointerDown, true);
|
|
1504
|
+
window.addEventListener("keydown", onKeyDown);
|
|
1505
|
+
window.addEventListener("resize", close);
|
|
1506
|
+
window.addEventListener("scroll", close, true);
|
|
1507
|
+
return () => {
|
|
1508
|
+
window.removeEventListener("pointerdown", onPointerDown, true);
|
|
1509
|
+
window.removeEventListener("keydown", onKeyDown);
|
|
1510
|
+
window.removeEventListener("resize", close);
|
|
1511
|
+
window.removeEventListener("scroll", close, true);
|
|
1512
|
+
};
|
|
1513
|
+
}, [anchor, close]);
|
|
1514
|
+
if (!active) return null;
|
|
1515
|
+
const showCredits = flags.credits && remain !== null;
|
|
1516
|
+
const showQueue = flags.queue && queued !== null;
|
|
1517
|
+
if (!showCredits && !showQueue) return null;
|
|
1518
|
+
const queueTitle = queued === null ? void 0 : t("queueDockTitle", {
|
|
1297
1519
|
model: queued.model,
|
|
1298
1520
|
position: queued.position,
|
|
1299
1521
|
length: queued.length
|
|
1300
|
-
})
|
|
1522
|
+
});
|
|
1523
|
+
const creditsTitle = showCredits ? t("creditsDockTitle", {
|
|
1301
1524
|
remain: remain ?? 0,
|
|
1302
1525
|
total
|
|
1303
|
-
});
|
|
1526
|
+
}) : void 0;
|
|
1527
|
+
const fetchedAt = rows.reduce((latest, row) => Math.max(latest, row.fetchedAt ?? 0), 0);
|
|
1528
|
+
const lastUpdated = fetchedAt > 0 ? formatClock(fetchedAt) : "";
|
|
1529
|
+
const toggle = (event) => {
|
|
1530
|
+
if (anchor !== null) {
|
|
1531
|
+
close();
|
|
1532
|
+
return;
|
|
1533
|
+
}
|
|
1534
|
+
setAnchor(anchorOf(event.currentTarget.getBoundingClientRect()));
|
|
1535
|
+
};
|
|
1536
|
+
const forceRefresh = async () => {
|
|
1537
|
+
setRefreshing(true);
|
|
1538
|
+
try {
|
|
1539
|
+
await loadStatus(true);
|
|
1540
|
+
} finally {
|
|
1541
|
+
setRefreshing(false);
|
|
1542
|
+
}
|
|
1543
|
+
};
|
|
1304
1544
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1305
1545
|
style: {
|
|
1306
1546
|
display: "inline-flex",
|
|
@@ -1314,8 +1554,149 @@ function CodeArtsCreditDock({ useProjection, t }) {
|
|
|
1314
1554
|
flex: "0 0 auto",
|
|
1315
1555
|
minWidth: "max-content"
|
|
1316
1556
|
},
|
|
1317
|
-
title,
|
|
1318
|
-
children: [
|
|
1557
|
+
title: queueTitle ?? creditsTitle,
|
|
1558
|
+
children: [
|
|
1559
|
+
showQueue && queued !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("queueDock", { position: queued.position }) }),
|
|
1560
|
+
showCredits && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
1561
|
+
ref: triggerRef,
|
|
1562
|
+
type: "button",
|
|
1563
|
+
"aria-expanded": anchor !== null,
|
|
1564
|
+
"aria-label": t("creditsDockExpand"),
|
|
1565
|
+
title: creditsTitle,
|
|
1566
|
+
onClick: toggle,
|
|
1567
|
+
style: {
|
|
1568
|
+
display: "inline-flex",
|
|
1569
|
+
alignItems: "center",
|
|
1570
|
+
gap: 4,
|
|
1571
|
+
margin: 0,
|
|
1572
|
+
padding: 0,
|
|
1573
|
+
border: 0,
|
|
1574
|
+
background: "none",
|
|
1575
|
+
color: "inherit",
|
|
1576
|
+
font: "inherit",
|
|
1577
|
+
lineHeight: "inherit",
|
|
1578
|
+
cursor: "pointer"
|
|
1579
|
+
},
|
|
1580
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("creditsDock", { remain: formatNumber(remain ?? 0) }) }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1581
|
+
"aria-hidden": "true",
|
|
1582
|
+
style: {
|
|
1583
|
+
opacity: .6,
|
|
1584
|
+
fontSize: "0.8em"
|
|
1585
|
+
},
|
|
1586
|
+
children: "▾"
|
|
1587
|
+
})]
|
|
1588
|
+
}),
|
|
1589
|
+
anchor !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1590
|
+
ref: panelRef,
|
|
1591
|
+
role: "dialog",
|
|
1592
|
+
"aria-label": t("creditsDockPanelTitle"),
|
|
1593
|
+
style: {
|
|
1594
|
+
position: "fixed",
|
|
1595
|
+
left: anchor.left,
|
|
1596
|
+
top: anchor.top,
|
|
1597
|
+
transform: anchor.above ? "translateY(-100%)" : void 0,
|
|
1598
|
+
width: PANEL_WIDTH,
|
|
1599
|
+
maxWidth: "calc(100vw - 16px)",
|
|
1600
|
+
zIndex: 1e3,
|
|
1601
|
+
display: "flex",
|
|
1602
|
+
flexDirection: "column",
|
|
1603
|
+
gap: 8,
|
|
1604
|
+
padding: 12,
|
|
1605
|
+
boxSizing: "border-box",
|
|
1606
|
+
borderRadius: "var(--dsw-radius-lg, 10px)",
|
|
1607
|
+
border: "0.5px solid var(--dsw-alias-settings-card-stroke, rgba(127, 127, 127, 0.28))",
|
|
1608
|
+
background: "var(--dsw-alias-settings-card-fill, rgba(32, 32, 32, 0.96))",
|
|
1609
|
+
boxShadow: "0 12px 32px rgba(0, 0, 0, 0.28)",
|
|
1610
|
+
color: "var(--dsw-alias-label-primary)",
|
|
1611
|
+
fontSize: "var(--dsh-content-font-size-secondary, 13px)",
|
|
1612
|
+
lineHeight: "18px",
|
|
1613
|
+
whiteSpace: "normal"
|
|
1614
|
+
},
|
|
1615
|
+
children: [
|
|
1616
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1617
|
+
style: {
|
|
1618
|
+
display: "flex",
|
|
1619
|
+
alignItems: "center",
|
|
1620
|
+
justifyContent: "space-between",
|
|
1621
|
+
gap: 8
|
|
1622
|
+
},
|
|
1623
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1624
|
+
style: { fontWeight: 600 },
|
|
1625
|
+
children: t("creditsDockPanelTitle")
|
|
1626
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
1627
|
+
variant: "outline",
|
|
1628
|
+
size: "sm",
|
|
1629
|
+
disabled: refreshing,
|
|
1630
|
+
onClick: () => void forceRefresh(),
|
|
1631
|
+
children: refreshing ? t("creditsDockRefreshing") : t("creditsDockRefresh")
|
|
1632
|
+
})]
|
|
1633
|
+
}),
|
|
1634
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1635
|
+
style: {
|
|
1636
|
+
display: "grid",
|
|
1637
|
+
gridTemplateColumns: "minmax(0, 1fr) auto",
|
|
1638
|
+
gap: "4px 12px",
|
|
1639
|
+
alignItems: "center",
|
|
1640
|
+
borderTop: "0.5px solid var(--dsw-alias-border-l2, rgba(127, 127, 127, 0.22))",
|
|
1641
|
+
paddingTop: 8,
|
|
1642
|
+
color: "var(--dsw-alias-label-tertiary)"
|
|
1643
|
+
},
|
|
1644
|
+
children: [
|
|
1645
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("creditsDockColAccount") }),
|
|
1646
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1647
|
+
style: { justifySelf: "end" },
|
|
1648
|
+
children: t("creditsDockColBalance")
|
|
1649
|
+
}),
|
|
1650
|
+
rows.length === 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1651
|
+
style: { gridColumn: "1 / -1" },
|
|
1652
|
+
children: t("creditsDockEmpty")
|
|
1653
|
+
}),
|
|
1654
|
+
rows.map((row) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1655
|
+
style: { display: "contents" },
|
|
1656
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
1657
|
+
style: {
|
|
1658
|
+
display: "flex",
|
|
1659
|
+
alignItems: "center",
|
|
1660
|
+
gap: 6,
|
|
1661
|
+
minWidth: 0
|
|
1662
|
+
},
|
|
1663
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.StateDot, { state: row.remain === null ? "warning" : "done" }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1664
|
+
style: {
|
|
1665
|
+
overflow: "hidden",
|
|
1666
|
+
textOverflow: "ellipsis",
|
|
1667
|
+
whiteSpace: "nowrap",
|
|
1668
|
+
color: "var(--dsw-alias-label-primary)"
|
|
1669
|
+
},
|
|
1670
|
+
title: row.error === void 0 ? row.name : row.error,
|
|
1671
|
+
children: row.name
|
|
1672
|
+
})]
|
|
1673
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1674
|
+
style: {
|
|
1675
|
+
justifySelf: "end",
|
|
1676
|
+
fontVariantNumeric: "tabular-nums"
|
|
1677
|
+
},
|
|
1678
|
+
children: row.remain === null ? "—" : formatNumber(row.remain)
|
|
1679
|
+
})]
|
|
1680
|
+
}, row.id))
|
|
1681
|
+
]
|
|
1682
|
+
}),
|
|
1683
|
+
rows.filter((row) => row.expiring > 0).map((row) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1684
|
+
style: {
|
|
1685
|
+
margin: 0,
|
|
1686
|
+
color: "var(--dsw-alias-state-warn-label, var(--dsw-alias-label-secondary))"
|
|
1687
|
+
},
|
|
1688
|
+
children: t("creditsDockExpiring", {
|
|
1689
|
+
account: row.name,
|
|
1690
|
+
expiring: formatNumber(row.expiring)
|
|
1691
|
+
})
|
|
1692
|
+
}, `${row.id}-expiring`)),
|
|
1693
|
+
lastUpdated !== "" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1694
|
+
style: { color: "var(--dsw-alias-label-tertiary)" },
|
|
1695
|
+
children: t("creditsDockUpdated", { time: lastUpdated })
|
|
1696
|
+
})
|
|
1697
|
+
]
|
|
1698
|
+
})
|
|
1699
|
+
]
|
|
1319
1700
|
});
|
|
1320
1701
|
}
|
|
1321
1702
|
//#endregion
|
|
@@ -1405,6 +1786,22 @@ const zh = {
|
|
|
1405
1786
|
creditsNeedSignIn: "这个账号需要重新登录",
|
|
1406
1787
|
creditsDock: "积分 {remain}",
|
|
1407
1788
|
creditsDockTitle: "CodeArts 剩余积分 {remain} / {total}",
|
|
1789
|
+
creditsDockExpand: "点开看各账号积分明细",
|
|
1790
|
+
creditsDockPanelTitle: "CodeArts 积分",
|
|
1791
|
+
creditsDockRefresh: "刷新积分",
|
|
1792
|
+
creditsDockRefreshing: "刷新中…",
|
|
1793
|
+
creditsDockColAccount: "账号",
|
|
1794
|
+
creditsDockColBalance: "剩余积分",
|
|
1795
|
+
creditsDockEmpty: "暂时没有积分数据",
|
|
1796
|
+
creditsDockExpiring: "{account}:{expiring} 积分将在本周期结束时作废",
|
|
1797
|
+
creditsDockUpdated: "最近刷新 {time}",
|
|
1798
|
+
tabDisplay: "显示",
|
|
1799
|
+
displayTitle: "对话底部显示",
|
|
1800
|
+
displayHint: "这两项只管输入框下方那一行,设置卡片里的积分明细不受影响。底部那条每 60 秒读一次设置,所以关掉最多要等一分钟才见效。",
|
|
1801
|
+
displayCreditsToggle: "显示剩余积分(点开可看各账号明细)",
|
|
1802
|
+
displayQueueToggle: "显示上游排队位置",
|
|
1803
|
+
displayNotWritable: "这个宿主没给插件开配置存储,开关只能看不能改。",
|
|
1804
|
+
displayFailed: "保存失败:{message}",
|
|
1408
1805
|
queueDock: "排队第 {position} 位",
|
|
1409
1806
|
queueDockTitle: "上游排队:{model} · 第 {position} 位 / 共 {length} 条",
|
|
1410
1807
|
claimButton: "签到领积分",
|
|
@@ -1502,6 +1899,22 @@ const en = {
|
|
|
1502
1899
|
creditsNeedSignIn: "This account needs signing in again",
|
|
1503
1900
|
creditsDock: "Credits {remain}",
|
|
1504
1901
|
creditsDockTitle: "CodeArts credits left {remain} / {total}",
|
|
1902
|
+
creditsDockExpand: "Show per-account credit details",
|
|
1903
|
+
creditsDockPanelTitle: "CodeArts credits",
|
|
1904
|
+
creditsDockRefresh: "Refresh credits",
|
|
1905
|
+
creditsDockRefreshing: "Refreshing…",
|
|
1906
|
+
creditsDockColAccount: "Account",
|
|
1907
|
+
creditsDockColBalance: "Credits left",
|
|
1908
|
+
creditsDockEmpty: "No credit data yet",
|
|
1909
|
+
creditsDockExpiring: "{account}: {expiring} credits expire when this cycle ends",
|
|
1910
|
+
creditsDockUpdated: "Last updated {time}",
|
|
1911
|
+
tabDisplay: "Display",
|
|
1912
|
+
displayTitle: "Under the chat input box",
|
|
1913
|
+
displayHint: "These two only control that line under the input box — the credit details in this card are unaffected. The bar re-reads settings every 60 seconds, so hiding it can take up to a minute.",
|
|
1914
|
+
displayCreditsToggle: "Show remaining credits (click it for per-account details)",
|
|
1915
|
+
displayQueueToggle: "Show the upstream queue position",
|
|
1916
|
+
displayNotWritable: "This host gives the plugin nowhere to persist settings, so the switches are read-only.",
|
|
1917
|
+
displayFailed: "Failed to save: {message}",
|
|
1505
1918
|
queueDock: "Queued #{position}",
|
|
1506
1919
|
queueDockTitle: "Upstream queue: {model} · position {position} of {length}",
|
|
1507
1920
|
claimButton: "Claim daily credits",
|