dsh-codearts 1.1.4 → 1.2.1
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 +64 -0
- package/lib/bin.mjs +2 -2
- package/lib/client/index.d.cts +19 -0
- package/lib/client/index.js +635 -90
- package/lib/index.d.mts +5 -1
- package/lib/index.mjs +142 -25
- package/lib/{usage-B0NfG8vZ.mjs → usage-05PgtUIP.mjs} +7 -0
- package/package.json +1 -1
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
|
-
"
|
|
36
|
+
"modelChart": "xoziZG_modelChart",
|
|
37
|
+
"accountRowActions": "xoziZG_accountRowActions",
|
|
38
|
+
"field": "xoziZG_field",
|
|
37
39
|
"statusLine": "xoziZG_statusLine",
|
|
40
|
+
"meta": "xoziZG_meta",
|
|
41
|
+
"manualForm": "xoziZG_manualForm",
|
|
42
|
+
"usageBar": "xoziZG_usageBar",
|
|
43
|
+
"check": "xoziZG_check",
|
|
38
44
|
"tabs": "xoziZG_tabs",
|
|
45
|
+
"modelTrack": "xoziZG_modelTrack",
|
|
46
|
+
"modelFill": "xoziZG_modelFill",
|
|
47
|
+
"balance": "xoziZG_balance",
|
|
48
|
+
"creditLink": "xoziZG_creditLink",
|
|
39
49
|
"actions": "xoziZG_actions",
|
|
40
|
-
"
|
|
41
|
-
"
|
|
42
|
-
"accountRowActions": "xoziZG_accountRowActions",
|
|
50
|
+
"accountName": "xoziZG_accountName",
|
|
51
|
+
"area": "xoziZG_area",
|
|
43
52
|
"sectionHead": "xoziZG_sectionHead",
|
|
44
|
-
"
|
|
45
|
-
"
|
|
46
|
-
"
|
|
53
|
+
"accountHint": "xoziZG_accountHint",
|
|
54
|
+
"expiryRow": "xoziZG_expiryRow",
|
|
55
|
+
"bar": "xoziZG_bar",
|
|
56
|
+
"accountCard": "xoziZG_accountCard",
|
|
57
|
+
"accountRow": "xoziZG_accountRow",
|
|
58
|
+
"balanceMeta": "xoziZG_balanceMeta",
|
|
59
|
+
"sectionLabel": "xoziZG_sectionLabel",
|
|
60
|
+
"barFill": "xoziZG_barFill",
|
|
61
|
+
"link": "xoziZG_link",
|
|
47
62
|
"manual": "xoziZG_manual",
|
|
63
|
+
"accountNameRow": "xoziZG_accountNameRow",
|
|
48
64
|
"fieldLabel": "xoziZG_fieldLabel",
|
|
49
|
-
"
|
|
50
|
-
"
|
|
65
|
+
"creditFooter": "xoziZG_creditFooter",
|
|
66
|
+
"creditBlock": "xoziZG_creditBlock",
|
|
67
|
+
"error": "xoziZG_error",
|
|
68
|
+
"creditRow": "xoziZG_creditRow",
|
|
69
|
+
"modelRow": "xoziZG_modelRow",
|
|
70
|
+
"updated": "xoziZG_updated",
|
|
71
|
+
"creditHint": "xoziZG_creditHint",
|
|
72
|
+
"balanceRow": "xoziZG_balanceRow",
|
|
51
73
|
"expiryLabel": "xoziZG_expiryLabel",
|
|
52
|
-
"
|
|
53
|
-
"barFill": "xoziZG_barFill",
|
|
54
|
-
"balanceUnit": "xoziZG_balanceUnit",
|
|
55
|
-
"section": "xoziZG_section",
|
|
56
|
-
"usageBar": "xoziZG_usageBar",
|
|
57
|
-
"accountHint": "xoziZG_accountHint",
|
|
58
|
-
"modelTrack": "xoziZG_modelTrack",
|
|
74
|
+
"count": "xoziZG_count",
|
|
59
75
|
"accountInfo": "xoziZG_accountInfo",
|
|
60
|
-
"
|
|
61
|
-
"
|
|
62
|
-
"
|
|
76
|
+
"balanceUnit": "xoziZG_balanceUnit",
|
|
77
|
+
"danger": "xoziZG_danger",
|
|
78
|
+
"choiceRow": "xoziZG_choiceRow",
|
|
79
|
+
"body": "xoziZG_body",
|
|
63
80
|
"claimAllRow": "xoziZG_claimAllRow",
|
|
64
|
-
"modelRow": "xoziZG_modelRow",
|
|
65
|
-
"manualSummary": "xoziZG_manualSummary",
|
|
66
|
-
"accountName": "xoziZG_accountName",
|
|
67
|
-
"avatar": "xoziZG_avatar",
|
|
68
|
-
"choices": "xoziZG_choices",
|
|
69
|
-
"creditHint": "xoziZG_creditHint",
|
|
70
|
-
"creditRow": "xoziZG_creditRow",
|
|
71
|
-
"field": "xoziZG_field",
|
|
72
81
|
"usageFill": "xoziZG_usageFill",
|
|
73
|
-
"updated": "xoziZG_updated",
|
|
74
|
-
"expiryRow": "xoziZG_expiryRow",
|
|
75
|
-
"accountRow": "xoziZG_accountRow",
|
|
76
|
-
"error": "xoziZG_error",
|
|
77
|
-
"bar": "xoziZG_bar",
|
|
78
82
|
"header": "xoziZG_header",
|
|
79
83
|
"tabPanel": "xoziZG_tabPanel",
|
|
80
|
-
"meta": "xoziZG_meta",
|
|
81
84
|
"desc": "xoziZG_desc",
|
|
82
|
-
"
|
|
83
|
-
"modelFill": "xoziZG_modelFill",
|
|
84
|
-
"creditRows": "xoziZG_creditRows",
|
|
85
|
-
"modelChart": "xoziZG_modelChart",
|
|
86
|
-
"headText": "xoziZG_headText",
|
|
87
|
-
"accountToolbar": "xoziZG_accountToolbar",
|
|
88
|
-
"creditBlock": "xoziZG_creditBlock",
|
|
89
|
-
"sectionLabel": "xoziZG_sectionLabel",
|
|
90
|
-
"manualForm": "xoziZG_manualForm",
|
|
91
|
-
"area": "xoziZG_area",
|
|
92
|
-
"balanceMeta": "xoziZG_balanceMeta",
|
|
93
|
-
"accountCard": "xoziZG_accountCard",
|
|
94
|
-
"check": "xoziZG_check",
|
|
95
|
-
"creditLink": "xoziZG_creditLink",
|
|
96
|
-
"link": "xoziZG_link",
|
|
85
|
+
"card": "xoziZG_card",
|
|
97
86
|
"accountEntry": "xoziZG_accountEntry",
|
|
98
|
-
"
|
|
99
|
-
"
|
|
87
|
+
"hint": "xoziZG_hint",
|
|
88
|
+
"accountToolbar": "xoziZG_accountToolbar",
|
|
89
|
+
"headText": "xoziZG_headText",
|
|
90
|
+
"section": "xoziZG_section",
|
|
91
|
+
"modelName": "xoziZG_modelName",
|
|
92
|
+
"name": "xoziZG_name",
|
|
93
|
+
"creditRows": "xoziZG_creditRows",
|
|
94
|
+
"modelShare": "xoziZG_modelShare",
|
|
95
|
+
"modelRequests": "xoziZG_modelRequests",
|
|
96
|
+
"choices": "xoziZG_choices",
|
|
97
|
+
"manualSummary": "xoziZG_manualSummary",
|
|
98
|
+
"avatar": "xoziZG_avatar",
|
|
99
|
+
"modelTokens": "xoziZG_modelTokens"
|
|
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", {
|
|
@@ -1095,6 +1160,10 @@ function CodeArtsPluginCard({ t }) {
|
|
|
1095
1160
|
choice.ratio !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tag, {
|
|
1096
1161
|
tone: "quiet",
|
|
1097
1162
|
children: choice.ratio
|
|
1163
|
+
}),
|
|
1164
|
+
choice.supportsImages === true && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tag, {
|
|
1165
|
+
tone: "quiet",
|
|
1166
|
+
children: t("modelImages")
|
|
1098
1167
|
})
|
|
1099
1168
|
]
|
|
1100
1169
|
}, choice.id))
|
|
@@ -1138,6 +1207,57 @@ function CodeArtsPluginCard({ t }) {
|
|
|
1138
1207
|
accounts,
|
|
1139
1208
|
t
|
|
1140
1209
|
})
|
|
1210
|
+
}),
|
|
1211
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(TabPanel, {
|
|
1212
|
+
id: panelId("display"),
|
|
1213
|
+
labelledBy: tabId("display"),
|
|
1214
|
+
active: activeTab === "display",
|
|
1215
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1216
|
+
className: CodeArtsPluginCard_module_css_default.section,
|
|
1217
|
+
children: [
|
|
1218
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1219
|
+
className: CodeArtsPluginCard_module_css_default.sectionHead,
|
|
1220
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1221
|
+
className: CodeArtsPluginCard_module_css_default.sectionLabel,
|
|
1222
|
+
children: t("displayTitle")
|
|
1223
|
+
})
|
|
1224
|
+
}),
|
|
1225
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1226
|
+
className: CodeArtsPluginCard_module_css_default.hint,
|
|
1227
|
+
children: t("displayHint")
|
|
1228
|
+
}),
|
|
1229
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1230
|
+
className: CodeArtsPluginCard_module_css_default.choices,
|
|
1231
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1232
|
+
className: CodeArtsPluginCard_module_css_default.choiceRow,
|
|
1233
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Checkbox, {
|
|
1234
|
+
className: CodeArtsPluginCard_module_css_default.check,
|
|
1235
|
+
checked: dock.credits,
|
|
1236
|
+
disabled: !dockWritable || dockSaving,
|
|
1237
|
+
onChange: (on) => void toggleDock("credits", on),
|
|
1238
|
+
label: t("displayCreditsToggle")
|
|
1239
|
+
})
|
|
1240
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1241
|
+
className: CodeArtsPluginCard_module_css_default.choiceRow,
|
|
1242
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Checkbox, {
|
|
1243
|
+
className: CodeArtsPluginCard_module_css_default.check,
|
|
1244
|
+
checked: dock.queue,
|
|
1245
|
+
disabled: !dockWritable || dockSaving,
|
|
1246
|
+
onChange: (on) => void toggleDock("queue", on),
|
|
1247
|
+
label: t("displayQueueToggle")
|
|
1248
|
+
})
|
|
1249
|
+
})]
|
|
1250
|
+
}),
|
|
1251
|
+
!dockWritable && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1252
|
+
className: CodeArtsPluginCard_module_css_default.hint,
|
|
1253
|
+
children: t("displayNotWritable")
|
|
1254
|
+
}),
|
|
1255
|
+
dockError !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1256
|
+
className: CodeArtsPluginCard_module_css_default.error,
|
|
1257
|
+
children: t("displayFailed", { message: dockError })
|
|
1258
|
+
})
|
|
1259
|
+
]
|
|
1260
|
+
})
|
|
1141
1261
|
})
|
|
1142
1262
|
] }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1143
1263
|
className: CodeArtsPluginCard_module_css_default.section,
|
|
@@ -1195,17 +1315,102 @@ function CodeArtsPluginCard({ t }) {
|
|
|
1195
1315
|
});
|
|
1196
1316
|
}
|
|
1197
1317
|
//#endregion
|
|
1318
|
+
//#region src/client/dock-line.ts
|
|
1319
|
+
/** 段分隔符 —— 与邻居插件一致,改这里等于改整行的样子。 */
|
|
1320
|
+
const DOCK_SEGMENT_SEPARATOR = " · ";
|
|
1321
|
+
function buildDockLine(parts, t) {
|
|
1322
|
+
const rest = [];
|
|
1323
|
+
rest.push(t("dockProvider", { provider: parts.provider }));
|
|
1324
|
+
rest.push(parts.model === void 0 ? t("dockNoModel") : t("dockModel", { model: parts.model }));
|
|
1325
|
+
if (parts.ratio !== void 0) rest.push(parts.ratio);
|
|
1326
|
+
if (parts.queue !== void 0) rest.push(parts.queue);
|
|
1327
|
+
return {
|
|
1328
|
+
...parts.credits === void 0 ? {} : { credits: parts.credits },
|
|
1329
|
+
rest: rest.join(DOCK_SEGMENT_SEPARATOR)
|
|
1330
|
+
};
|
|
1331
|
+
}
|
|
1332
|
+
//#endregion
|
|
1333
|
+
//#region src/client/dock-suppress.ts
|
|
1334
|
+
/**
|
|
1335
|
+
* 选中我们的模型时,把对话底部那一行里**别人**的条目暂时压掉。
|
|
1336
|
+
*
|
|
1337
|
+
* 别人注册什么我们管不了 —— `conversation.composer.dock` 是 list 槽位,每条注册由
|
|
1338
|
+
* 宿主平铺成同一个 `[data-composer-dock]` 行里的一个直接子节点。所以我们能做的是:
|
|
1339
|
+
* 在自己这条的 effect 里认出兄弟节点、给它们打个属性,再用一条我们注入的 CSS 隐藏。
|
|
1340
|
+
* 切走或卸载时逐个摘掉属性,绝不留下别人的行永久消失。
|
|
1341
|
+
*
|
|
1342
|
+
* 不碰的:宿主自己的轮次/token 统计 pill(带 `data-composer-stat`),以及上下文占用
|
|
1343
|
+
* 那一格 —— 那些不是「别人的 provider 行」,压掉就是抢宿主的 UI。
|
|
1344
|
+
*
|
|
1345
|
+
* 这里刻意不写 `instanceof HTMLElement`:结构够了就能用,逻辑也就能在没装 jsdom 的
|
|
1346
|
+
* 测试环境里用假节点跑(见 test/dock-suppress.spec.ts)。
|
|
1347
|
+
*/
|
|
1348
|
+
/** 标记属性:带上它的节点被我们隐藏。 */
|
|
1349
|
+
const SUPPRESS_ATTR = "data-codearts-dock-suppressed";
|
|
1350
|
+
/** 注入的那条样式表 id。 */
|
|
1351
|
+
const SUPPRESS_STYLE_ID = "dsh-codearts-dock-suppress";
|
|
1352
|
+
/**
|
|
1353
|
+
* 这个兄弟节点该不该被压掉。
|
|
1354
|
+
*
|
|
1355
|
+
* 该压:别人的一条注册(既不是我们自己,也不包含我们,也不是宿主统计 pill)。
|
|
1356
|
+
* 不该压:我们自己、我们自己的子孙(有些宿主会包一层)、宿主的统计 pill。
|
|
1357
|
+
*/
|
|
1358
|
+
function isForeignDockEntry(node, self) {
|
|
1359
|
+
if (node === self) return false;
|
|
1360
|
+
if (node.contains?.(self) === true) return false;
|
|
1361
|
+
if (node.hasAttribute("data-composer-stat")) return false;
|
|
1362
|
+
if (node.querySelector("[data-composer-stat]") !== null) return false;
|
|
1363
|
+
return true;
|
|
1364
|
+
}
|
|
1365
|
+
/**
|
|
1366
|
+
* 扫一遍并给新发现的「别人」打标记。返回**这次**新标记的节点,调用方负责在清理时
|
|
1367
|
+
* 逐个摘掉(只摘自己打的那批,别动别人的状态)。
|
|
1368
|
+
*/
|
|
1369
|
+
function markForeignDockEntries(parent, self) {
|
|
1370
|
+
const marked = [];
|
|
1371
|
+
for (const child of Array.from(parent.children)) {
|
|
1372
|
+
if (!isForeignDockEntry(child, self)) continue;
|
|
1373
|
+
if (child.getAttribute("data-codearts-dock-suppressed") === "") continue;
|
|
1374
|
+
child.setAttribute(SUPPRESS_ATTR, "");
|
|
1375
|
+
marked.push(child);
|
|
1376
|
+
}
|
|
1377
|
+
return marked;
|
|
1378
|
+
}
|
|
1379
|
+
/** 摘掉标记 —— 切走 provider 或组件卸载时调用。 */
|
|
1380
|
+
function releaseDockEntries(nodes) {
|
|
1381
|
+
for (const node of nodes) node.removeAttribute(SUPPRESS_ATTR);
|
|
1382
|
+
}
|
|
1383
|
+
/** 注入一次隐藏规则;重复调用无副作用。没有 document 的环境(SSR/测试)直接跳过。 */
|
|
1384
|
+
function ensureSuppressStyle() {
|
|
1385
|
+
if (typeof document === "undefined") return;
|
|
1386
|
+
if (document.getElementById("dsh-codearts-dock-suppress") !== null) return;
|
|
1387
|
+
const style = document.createElement("style");
|
|
1388
|
+
style.id = SUPPRESS_STYLE_ID;
|
|
1389
|
+
style.textContent = `[${SUPPRESS_ATTR}]{display:none!important}`;
|
|
1390
|
+
document.head.appendChild(style);
|
|
1391
|
+
}
|
|
1392
|
+
//#endregion
|
|
1198
1393
|
//#region src/client/CodeArtsCreditDock.tsx
|
|
1199
1394
|
/**
|
|
1200
|
-
*
|
|
1395
|
+
* 对话底部的「模型 + 积分」条,以及点开后的积分弹层。
|
|
1201
1396
|
*
|
|
1202
|
-
*
|
|
1203
|
-
*
|
|
1204
|
-
*
|
|
1205
|
-
*
|
|
1397
|
+
* 这一条挂在 `conversation.composer.dock` 槽位上(与宿主 session-stats strip 同位)。
|
|
1398
|
+
* 只在当前 provider 是 codearts 时才渲染,别的 provider 下完全不出现,不抢别人的行。
|
|
1399
|
+
*
|
|
1400
|
+
* 第一段是 `Provider CodeArts · <模型名>`,模型名直接用目录里的 `name`
|
|
1401
|
+
* (`catalog.ts` 的 `modelDisplayName` 已经把倍率写进去了,如
|
|
1402
|
+
* `DeepSeek-V4.1-Flash-1017 · x0.18`),所以这一行自带倍率、不必另算。
|
|
1403
|
+
* 这段是**我们自己的**身份行:邻居插件(如 dsh-codebuddy-cli)对任何 provider
|
|
1404
|
+
* 都会画一行 `Provider <id> · Model <id>`,在 codearts 下那行只有模型 id、没有
|
|
1405
|
+
* 倍率;两边都开就会重复,所以优先我们这条,邻居那条应在它自己的设置里关掉。
|
|
1406
|
+
*
|
|
1407
|
+
* 那一行是个按钮:点开就是逐号明细(多账号下聚合数字本身没意义,得能看出是哪个号)。
|
|
1408
|
+
* 显隐由设置卡片「显示」页那两个开关管着,开关值随 `/status` 一起下发 —— 底部条本来
|
|
1409
|
+
* 就在轮询它,不必为两个布尔开一条新链路。代价是关掉开关最多要等一个轮询周期(60 秒)
|
|
1410
|
+
* 才看得见效果,这个延迟在设置卡片上写明了。
|
|
1206
1411
|
*
|
|
1207
1412
|
* 积分数据走插件自己的 `/api/codearts/status`(带 TTL 缓存,见 src/credits.ts),
|
|
1208
|
-
* 不会因为这条 dock
|
|
1413
|
+
* 不会因为这条 dock 让上游多挨一次请求;弹层上那个「刷新积分」是唯一绕缓存的出口。
|
|
1209
1414
|
*/
|
|
1210
1415
|
/** 与 src/adapter.ts 的 CODEARTS_PROVIDER 对齐 —— client 不能 import host 模块。 */
|
|
1211
1416
|
const CODEARTS_PROVIDER = "codearts";
|
|
@@ -1215,6 +1420,12 @@ const QUEUE_PATH = "/api/codearts/queue";
|
|
|
1215
1420
|
const REFRESH_INTERVAL_MS = 6e4;
|
|
1216
1421
|
/** 排队位置变得快:官方客户端也是 5 秒一次;这是本机路由,打不疼上游。 */
|
|
1217
1422
|
const QUEUE_INTERVAL_MS = 5e3;
|
|
1423
|
+
/** 弹层宽度固定,好算它会不会顶出屏幕右边。 */
|
|
1424
|
+
const PANEL_WIDTH = 280;
|
|
1425
|
+
/** 弹层与那一行之间的缝隙。 */
|
|
1426
|
+
const PANEL_GAP = 8;
|
|
1427
|
+
/** 那一行在输入框下方,往上开才够空间;给弹层留的最小高度,不够就翻到下面。 */
|
|
1428
|
+
const PANEL_MIN_ABOVE = 260;
|
|
1218
1429
|
function readSelection(useProjection) {
|
|
1219
1430
|
if (useProjection === void 0) return void 0;
|
|
1220
1431
|
try {
|
|
@@ -1223,51 +1434,138 @@ function readSelection(useProjection) {
|
|
|
1223
1434
|
return;
|
|
1224
1435
|
}
|
|
1225
1436
|
}
|
|
1437
|
+
function currentOf(selection) {
|
|
1438
|
+
return selection?.next ?? selection?.lastUsed;
|
|
1439
|
+
}
|
|
1226
1440
|
function isCodeArts(selection) {
|
|
1227
|
-
return (selection
|
|
1441
|
+
return currentOf(selection)?.provider === CODEARTS_PROVIDER;
|
|
1442
|
+
}
|
|
1443
|
+
function formatNumber(value) {
|
|
1444
|
+
return new Intl.NumberFormat().format(Math.round(value));
|
|
1445
|
+
}
|
|
1446
|
+
function formatClock(ts) {
|
|
1447
|
+
if (ts === void 0 || !Number.isFinite(ts)) return "";
|
|
1448
|
+
try {
|
|
1449
|
+
return new Date(ts).toLocaleTimeString([], {
|
|
1450
|
+
hour: "2-digit",
|
|
1451
|
+
minute: "2-digit"
|
|
1452
|
+
});
|
|
1453
|
+
} catch {
|
|
1454
|
+
return "";
|
|
1455
|
+
}
|
|
1456
|
+
}
|
|
1457
|
+
/** 账号名与设置卡片同一套取法:备注 > 用户名 > id。 */
|
|
1458
|
+
function accountName(account) {
|
|
1459
|
+
return account.label?.trim() || account.userName?.trim() || account.id;
|
|
1460
|
+
}
|
|
1461
|
+
/**
|
|
1462
|
+
* 逐号明细。
|
|
1463
|
+
*
|
|
1464
|
+
* 老后端只有聚合的一份 `credits`,那时单账号就退回它 —— 别因为后端没升级就把
|
|
1465
|
+
* 弹层弹成一句「没有数据」。
|
|
1466
|
+
*/
|
|
1467
|
+
function rowsOf(data) {
|
|
1468
|
+
const byId = new Map((data.creditsByAccount ?? []).map((entry) => [entry.id, entry]));
|
|
1469
|
+
return (data.accounts ?? []).map((account) => {
|
|
1470
|
+
const entry = byId.get(account.id) ?? (byId.size === 0 && (data.accounts?.length ?? 0) === 1 ? {
|
|
1471
|
+
id: account.id,
|
|
1472
|
+
credits: data.credits,
|
|
1473
|
+
creditsError: data.creditsError
|
|
1474
|
+
} : void 0);
|
|
1475
|
+
const snapshot = entry?.credits;
|
|
1476
|
+
return {
|
|
1477
|
+
id: account.id,
|
|
1478
|
+
name: accountName(account),
|
|
1479
|
+
remain: snapshot?.credits.total.remain ?? null,
|
|
1480
|
+
expiring: snapshot?.credits.total.expiring ?? 0,
|
|
1481
|
+
...snapshot?.credits.fetchedAt === void 0 ? {} : { fetchedAt: snapshot.credits.fetchedAt },
|
|
1482
|
+
...entry?.creditsError === void 0 ? {} : { error: entry.creditsError }
|
|
1483
|
+
};
|
|
1484
|
+
});
|
|
1485
|
+
}
|
|
1486
|
+
/** 把弹层钉在按钮边上:优先往上开(那一行本来就在输入框下面),放不下就翻到下面。 */
|
|
1487
|
+
function anchorOf(rect) {
|
|
1488
|
+
const above = rect.top > PANEL_MIN_ABOVE;
|
|
1489
|
+
const width = Math.min(PANEL_WIDTH, window.innerWidth - 16);
|
|
1490
|
+
return {
|
|
1491
|
+
left: Math.max(8, Math.min(rect.left, window.innerWidth - width - 8)),
|
|
1492
|
+
top: above ? rect.top - PANEL_GAP : rect.bottom + PANEL_GAP,
|
|
1493
|
+
above
|
|
1494
|
+
};
|
|
1228
1495
|
}
|
|
1229
1496
|
function CodeArtsCreditDock({ useProjection, t }) {
|
|
1230
|
-
const
|
|
1497
|
+
const selection = readSelection(useProjection);
|
|
1498
|
+
const active = isCodeArts(selection);
|
|
1231
1499
|
const [remain, setRemain] = (0, react.useState)(null);
|
|
1232
1500
|
const [total, setTotal] = (0, react.useState)(0);
|
|
1501
|
+
const [rows, setRows] = (0, react.useState)([]);
|
|
1502
|
+
/**
|
|
1503
|
+
* 模型 id -> 目录条目(显示名 + 已格式化的倍率)。
|
|
1504
|
+
*
|
|
1505
|
+
* 显示名自带 ` · x0.18` 尾巴,倍率单独放一段,所以两个都要拿:
|
|
1506
|
+
* 名字用 `stripModelSuffixes` 剥掉尾巴,倍率直接用服务端算好的 `ratio`。
|
|
1507
|
+
*/
|
|
1508
|
+
const [modelInfo, setModelInfo] = (0, react.useState)({});
|
|
1509
|
+
const [flags, setFlags] = (0, react.useState)({
|
|
1510
|
+
credits: true,
|
|
1511
|
+
queue: true
|
|
1512
|
+
});
|
|
1233
1513
|
const [queued, setQueued] = (0, react.useState)(null);
|
|
1514
|
+
const [anchor, setAnchor] = (0, react.useState)(null);
|
|
1515
|
+
const [refreshing, setRefreshing] = (0, react.useState)(false);
|
|
1234
1516
|
const mounted = (0, react.useRef)(true);
|
|
1517
|
+
const triggerRef = (0, react.useRef)(null);
|
|
1518
|
+
const panelRef = (0, react.useRef)(null);
|
|
1519
|
+
/** 这一行的根节点 —— 它的 parentElement 就是宿主那条共享 flex 行。 */
|
|
1520
|
+
const rootRef = (0, react.useRef)(null);
|
|
1235
1521
|
(0, react.useEffect)(() => {
|
|
1236
1522
|
mounted.current = true;
|
|
1237
1523
|
return () => {
|
|
1238
1524
|
mounted.current = false;
|
|
1239
1525
|
};
|
|
1240
1526
|
}, []);
|
|
1527
|
+
const loadStatus = (0, react.useCallback)(async (force) => {
|
|
1528
|
+
try {
|
|
1529
|
+
const res = await fetch(force ? `${STATUS_PATH}?force=1` : STATUS_PATH, {
|
|
1530
|
+
headers: { Accept: "application/json" },
|
|
1531
|
+
credentials: "same-origin"
|
|
1532
|
+
});
|
|
1533
|
+
if (!res.ok) return;
|
|
1534
|
+
const data = await res.json().catch(() => void 0);
|
|
1535
|
+
if (data === void 0 || !mounted.current) return;
|
|
1536
|
+
setFlags(data.dock ?? {
|
|
1537
|
+
credits: true,
|
|
1538
|
+
queue: true
|
|
1539
|
+
});
|
|
1540
|
+
setRows(rowsOf(data));
|
|
1541
|
+
setModelInfo(Object.fromEntries((data.models ?? []).map((model) => [model.id, {
|
|
1542
|
+
name: model.name,
|
|
1543
|
+
...model.ratio === void 0 ? {} : { ratio: model.ratio }
|
|
1544
|
+
}])));
|
|
1545
|
+
const snapshots = (data.creditsByAccount ?? []).map((entry) => entry.credits).filter((value) => value !== void 0);
|
|
1546
|
+
const pool = snapshots.length > 0 ? snapshots : data.credits === void 0 ? [] : [data.credits];
|
|
1547
|
+
if (pool.length === 0) {
|
|
1548
|
+
setRemain(null);
|
|
1549
|
+
return;
|
|
1550
|
+
}
|
|
1551
|
+
setRemain(Math.round(pool.reduce((sum, item) => sum + item.credits.total.remain, 0)));
|
|
1552
|
+
setTotal(Math.round(pool.reduce((sum, item) => sum + item.credits.total.total, 0)));
|
|
1553
|
+
} catch {}
|
|
1554
|
+
}, []);
|
|
1241
1555
|
(0, react.useEffect)(() => {
|
|
1242
1556
|
if (!active) {
|
|
1243
1557
|
setRemain(null);
|
|
1558
|
+
setRows([]);
|
|
1559
|
+
setAnchor(null);
|
|
1244
1560
|
return;
|
|
1245
1561
|
}
|
|
1246
1562
|
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);
|
|
1563
|
+
loadStatus(false);
|
|
1564
|
+
timer = window.setInterval(() => void loadStatus(false), REFRESH_INTERVAL_MS);
|
|
1267
1565
|
return () => {
|
|
1268
1566
|
if (timer !== void 0) window.clearInterval(timer);
|
|
1269
1567
|
};
|
|
1270
|
-
}, [active]);
|
|
1568
|
+
}, [active, loadStatus]);
|
|
1271
1569
|
(0, react.useEffect)(() => {
|
|
1272
1570
|
if (!active) {
|
|
1273
1571
|
setQueued(null);
|
|
@@ -1291,21 +1589,96 @@ function CodeArtsCreditDock({ useProjection, t }) {
|
|
|
1291
1589
|
if (timer !== void 0) window.clearInterval(timer);
|
|
1292
1590
|
};
|
|
1293
1591
|
}, [active]);
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1592
|
+
const close = (0, react.useCallback)(() => setAnchor(null), []);
|
|
1593
|
+
(0, react.useEffect)(() => {
|
|
1594
|
+
if (anchor === null) return;
|
|
1595
|
+
const onPointerDown = (event) => {
|
|
1596
|
+
const target = event.target;
|
|
1597
|
+
if (target === null) return;
|
|
1598
|
+
if (panelRef.current?.contains(target) === true || triggerRef.current?.contains(target) === true) return;
|
|
1599
|
+
close();
|
|
1600
|
+
};
|
|
1601
|
+
const onKeyDown = (event) => {
|
|
1602
|
+
if (event.key === "Escape") close();
|
|
1603
|
+
};
|
|
1604
|
+
window.addEventListener("pointerdown", onPointerDown, true);
|
|
1605
|
+
window.addEventListener("keydown", onKeyDown);
|
|
1606
|
+
window.addEventListener("resize", close);
|
|
1607
|
+
window.addEventListener("scroll", close, true);
|
|
1608
|
+
return () => {
|
|
1609
|
+
window.removeEventListener("pointerdown", onPointerDown, true);
|
|
1610
|
+
window.removeEventListener("keydown", onKeyDown);
|
|
1611
|
+
window.removeEventListener("resize", close);
|
|
1612
|
+
window.removeEventListener("scroll", close, true);
|
|
1613
|
+
};
|
|
1614
|
+
}, [anchor, close]);
|
|
1615
|
+
(0, react.useEffect)(() => {
|
|
1616
|
+
if (!active) return;
|
|
1617
|
+
const self = rootRef.current;
|
|
1618
|
+
const slot = self?.closest("[data-slot=\"conversation.composer.dock\"]") ?? null;
|
|
1619
|
+
if (self === null || slot === null) return;
|
|
1620
|
+
const node = slot;
|
|
1621
|
+
const me = self;
|
|
1622
|
+
const marked = /* @__PURE__ */ new Set();
|
|
1623
|
+
const sweep = () => {
|
|
1624
|
+
for (const found of markForeignDockEntries(node, me)) marked.add(found);
|
|
1625
|
+
};
|
|
1626
|
+
ensureSuppressStyle();
|
|
1627
|
+
sweep();
|
|
1628
|
+
const observer = new MutationObserver(sweep);
|
|
1629
|
+
observer.observe(slot, { childList: true });
|
|
1630
|
+
return () => {
|
|
1631
|
+
observer.disconnect();
|
|
1632
|
+
releaseDockEntries(marked);
|
|
1633
|
+
marked.clear();
|
|
1634
|
+
};
|
|
1635
|
+
}, [active]);
|
|
1636
|
+
if (!active) return null;
|
|
1637
|
+
const showCredits = flags.credits && remain !== null;
|
|
1638
|
+
const showQueue = flags.queue && queued !== null;
|
|
1639
|
+
const modelId = currentOf(selection)?.model;
|
|
1640
|
+
const entry = modelId === void 0 ? void 0 : modelInfo[modelId];
|
|
1641
|
+
const modelLabel = entry === void 0 ? modelId ?? "" : stripModelSuffixes(entry.name);
|
|
1642
|
+
const ratio = entry?.ratio;
|
|
1643
|
+
const line = buildDockLine({
|
|
1644
|
+
...showCredits ? { credits: t("creditsDock", { remain: formatNumber(remain ?? 0) }) } : {},
|
|
1645
|
+
provider: "CodeArts",
|
|
1646
|
+
...modelLabel === "" ? {} : { model: modelLabel },
|
|
1647
|
+
...ratio === void 0 ? {} : { ratio },
|
|
1648
|
+
...showQueue && queued !== null ? { queue: t("queueDock", { position: queued.position }) } : {}
|
|
1649
|
+
}, t);
|
|
1650
|
+
const queueTitle = queued === null ? void 0 : t("queueDockTitle", {
|
|
1297
1651
|
model: queued.model,
|
|
1298
1652
|
position: queued.position,
|
|
1299
1653
|
length: queued.length
|
|
1300
|
-
})
|
|
1654
|
+
});
|
|
1655
|
+
const creditsTitle = showCredits ? t("creditsDockTitle", {
|
|
1301
1656
|
remain: remain ?? 0,
|
|
1302
1657
|
total
|
|
1303
|
-
});
|
|
1658
|
+
}) : void 0;
|
|
1659
|
+
const fetchedAt = rows.reduce((latest, row) => Math.max(latest, row.fetchedAt ?? 0), 0);
|
|
1660
|
+
const lastUpdated = fetchedAt > 0 ? formatClock(fetchedAt) : "";
|
|
1661
|
+
const toggle = (event) => {
|
|
1662
|
+
if (anchor !== null) {
|
|
1663
|
+
close();
|
|
1664
|
+
return;
|
|
1665
|
+
}
|
|
1666
|
+
setAnchor(anchorOf(event.currentTarget.getBoundingClientRect()));
|
|
1667
|
+
};
|
|
1668
|
+
const forceRefresh = async () => {
|
|
1669
|
+
setRefreshing(true);
|
|
1670
|
+
try {
|
|
1671
|
+
await loadStatus(true);
|
|
1672
|
+
} finally {
|
|
1673
|
+
setRefreshing(false);
|
|
1674
|
+
}
|
|
1675
|
+
};
|
|
1304
1676
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1677
|
+
ref: rootRef,
|
|
1305
1678
|
style: {
|
|
1306
1679
|
display: "inline-flex",
|
|
1307
1680
|
alignItems: "center",
|
|
1308
|
-
gap:
|
|
1681
|
+
gap: 0,
|
|
1309
1682
|
color: "var(--dsw-alias-label-tertiary)",
|
|
1310
1683
|
fontSize: "var(--dsh-content-font-size-secondary, 13px)",
|
|
1311
1684
|
lineHeight: "18px",
|
|
@@ -1314,8 +1687,142 @@ function CodeArtsCreditDock({ useProjection, t }) {
|
|
|
1314
1687
|
flex: "0 0 auto",
|
|
1315
1688
|
minWidth: "max-content"
|
|
1316
1689
|
},
|
|
1317
|
-
title,
|
|
1318
|
-
children: [
|
|
1690
|
+
title: queueTitle ?? creditsTitle,
|
|
1691
|
+
children: [
|
|
1692
|
+
line.credits !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1693
|
+
ref: triggerRef,
|
|
1694
|
+
type: "button",
|
|
1695
|
+
"aria-haspopup": "dialog",
|
|
1696
|
+
"aria-expanded": anchor !== null,
|
|
1697
|
+
"aria-label": t("creditsDockPanelTitle"),
|
|
1698
|
+
title: creditsTitle,
|
|
1699
|
+
onClick: toggle,
|
|
1700
|
+
style: {
|
|
1701
|
+
all: "unset",
|
|
1702
|
+
cursor: "pointer",
|
|
1703
|
+
font: "inherit",
|
|
1704
|
+
color: "inherit",
|
|
1705
|
+
fontVariantNumeric: "inherit",
|
|
1706
|
+
lineHeight: "inherit",
|
|
1707
|
+
whiteSpace: "inherit"
|
|
1708
|
+
},
|
|
1709
|
+
children: line.credits
|
|
1710
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1711
|
+
"aria-hidden": "true",
|
|
1712
|
+
children: " · "
|
|
1713
|
+
})] }),
|
|
1714
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: line.rest }),
|
|
1715
|
+
anchor !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1716
|
+
ref: panelRef,
|
|
1717
|
+
role: "dialog",
|
|
1718
|
+
"aria-label": t("creditsDockPanelTitle"),
|
|
1719
|
+
style: {
|
|
1720
|
+
position: "fixed",
|
|
1721
|
+
left: anchor.left,
|
|
1722
|
+
top: anchor.top,
|
|
1723
|
+
transform: anchor.above ? "translateY(-100%)" : void 0,
|
|
1724
|
+
width: PANEL_WIDTH,
|
|
1725
|
+
maxWidth: "calc(100vw - 16px)",
|
|
1726
|
+
zIndex: 1e3,
|
|
1727
|
+
display: "flex",
|
|
1728
|
+
flexDirection: "column",
|
|
1729
|
+
gap: 8,
|
|
1730
|
+
padding: 12,
|
|
1731
|
+
boxSizing: "border-box",
|
|
1732
|
+
borderRadius: "var(--dsw-radius-lg, 10px)",
|
|
1733
|
+
border: "0.5px solid var(--dsw-alias-settings-card-stroke, rgba(127, 127, 127, 0.28))",
|
|
1734
|
+
background: "var(--dsw-alias-settings-card-fill, rgba(32, 32, 32, 0.96))",
|
|
1735
|
+
boxShadow: "0 12px 32px rgba(0, 0, 0, 0.28)",
|
|
1736
|
+
color: "var(--dsw-alias-label-primary)",
|
|
1737
|
+
fontSize: "var(--dsh-content-font-size-secondary, 13px)",
|
|
1738
|
+
lineHeight: "18px",
|
|
1739
|
+
whiteSpace: "normal"
|
|
1740
|
+
},
|
|
1741
|
+
children: [
|
|
1742
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1743
|
+
style: {
|
|
1744
|
+
display: "flex",
|
|
1745
|
+
alignItems: "center",
|
|
1746
|
+
justifyContent: "space-between",
|
|
1747
|
+
gap: 8
|
|
1748
|
+
},
|
|
1749
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1750
|
+
style: { fontWeight: 600 },
|
|
1751
|
+
children: t("creditsDockPanelTitle")
|
|
1752
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
1753
|
+
variant: "outline",
|
|
1754
|
+
size: "sm",
|
|
1755
|
+
disabled: refreshing,
|
|
1756
|
+
onClick: () => void forceRefresh(),
|
|
1757
|
+
children: refreshing ? t("creditsDockRefreshing") : t("creditsDockRefresh")
|
|
1758
|
+
})]
|
|
1759
|
+
}),
|
|
1760
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1761
|
+
style: {
|
|
1762
|
+
display: "grid",
|
|
1763
|
+
gridTemplateColumns: "minmax(0, 1fr) auto",
|
|
1764
|
+
gap: "4px 12px",
|
|
1765
|
+
alignItems: "center",
|
|
1766
|
+
borderTop: "0.5px solid var(--dsw-alias-border-l2, rgba(127, 127, 127, 0.22))",
|
|
1767
|
+
paddingTop: 8,
|
|
1768
|
+
color: "var(--dsw-alias-label-tertiary)"
|
|
1769
|
+
},
|
|
1770
|
+
children: [
|
|
1771
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("creditsDockColAccount") }),
|
|
1772
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1773
|
+
style: { justifySelf: "end" },
|
|
1774
|
+
children: t("creditsDockColBalance")
|
|
1775
|
+
}),
|
|
1776
|
+
rows.length === 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1777
|
+
style: { gridColumn: "1 / -1" },
|
|
1778
|
+
children: t("creditsDockEmpty")
|
|
1779
|
+
}),
|
|
1780
|
+
rows.map((row) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1781
|
+
style: { display: "contents" },
|
|
1782
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
1783
|
+
style: {
|
|
1784
|
+
display: "flex",
|
|
1785
|
+
alignItems: "center",
|
|
1786
|
+
gap: 6,
|
|
1787
|
+
minWidth: 0
|
|
1788
|
+
},
|
|
1789
|
+
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", {
|
|
1790
|
+
style: {
|
|
1791
|
+
overflow: "hidden",
|
|
1792
|
+
textOverflow: "ellipsis",
|
|
1793
|
+
whiteSpace: "nowrap",
|
|
1794
|
+
color: "var(--dsw-alias-label-primary)"
|
|
1795
|
+
},
|
|
1796
|
+
title: row.error === void 0 ? row.name : row.error,
|
|
1797
|
+
children: row.name
|
|
1798
|
+
})]
|
|
1799
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1800
|
+
style: {
|
|
1801
|
+
justifySelf: "end",
|
|
1802
|
+
fontVariantNumeric: "tabular-nums"
|
|
1803
|
+
},
|
|
1804
|
+
children: row.remain === null ? "—" : formatNumber(row.remain)
|
|
1805
|
+
})]
|
|
1806
|
+
}, row.id))
|
|
1807
|
+
]
|
|
1808
|
+
}),
|
|
1809
|
+
rows.filter((row) => row.expiring > 0).map((row) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1810
|
+
style: {
|
|
1811
|
+
margin: 0,
|
|
1812
|
+
color: "var(--dsw-alias-state-warn-label, var(--dsw-alias-label-secondary))"
|
|
1813
|
+
},
|
|
1814
|
+
children: t("creditsDockExpiring", {
|
|
1815
|
+
account: row.name,
|
|
1816
|
+
expiring: formatNumber(row.expiring)
|
|
1817
|
+
})
|
|
1818
|
+
}, `${row.id}-expiring`)),
|
|
1819
|
+
lastUpdated !== "" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1820
|
+
style: { color: "var(--dsw-alias-label-tertiary)" },
|
|
1821
|
+
children: t("creditsDockUpdated", { time: lastUpdated })
|
|
1822
|
+
})
|
|
1823
|
+
]
|
|
1824
|
+
})
|
|
1825
|
+
]
|
|
1319
1826
|
});
|
|
1320
1827
|
}
|
|
1321
1828
|
//#endregion
|
|
@@ -1360,6 +1867,7 @@ const zh = {
|
|
|
1360
1867
|
benefit: "福利",
|
|
1361
1868
|
benefitBlocked: "福利今日不可用",
|
|
1362
1869
|
benefitBlockedHint: "{n} 个福利模型今天被上游判定没有权益(活动领完或渠道未开),发出去会直接报 benefit not found。",
|
|
1870
|
+
modelImages: "识图",
|
|
1363
1871
|
refreshModels: "刷新模型列表",
|
|
1364
1872
|
refreshing: "刷新中…",
|
|
1365
1873
|
refreshed: "已刷新:{n} 个模型,其中 {benefit} 个走福利路由",
|
|
@@ -1405,6 +1913,24 @@ const zh = {
|
|
|
1405
1913
|
creditsNeedSignIn: "这个账号需要重新登录",
|
|
1406
1914
|
creditsDock: "积分 {remain}",
|
|
1407
1915
|
creditsDockTitle: "CodeArts 剩余积分 {remain} / {total}",
|
|
1916
|
+
creditsDockPanelTitle: "CodeArts 积分",
|
|
1917
|
+
creditsDockRefresh: "刷新积分",
|
|
1918
|
+
creditsDockRefreshing: "刷新中…",
|
|
1919
|
+
creditsDockColAccount: "账号",
|
|
1920
|
+
creditsDockColBalance: "剩余积分",
|
|
1921
|
+
creditsDockEmpty: "暂时没有积分数据",
|
|
1922
|
+
creditsDockExpiring: "{account}:{expiring} 积分将在本周期结束时作废",
|
|
1923
|
+
creditsDockUpdated: "最近刷新 {time}",
|
|
1924
|
+
dockProvider: "Provider {provider}",
|
|
1925
|
+
dockModel: "Model {model}",
|
|
1926
|
+
dockNoModel: "未选择模型",
|
|
1927
|
+
tabDisplay: "显示",
|
|
1928
|
+
displayTitle: "对话底部显示",
|
|
1929
|
+
displayHint: "这两项只管输入框下方那一行的积分与排队两段;中间那段「Provider CodeArts · Model 模型 · 倍率」一直显示,不受开关影响。底部那条每 60 秒读一次设置,所以关掉最多要等一分钟才见效。",
|
|
1930
|
+
displayCreditsToggle: "显示剩余积分(点开可看各账号明细)",
|
|
1931
|
+
displayQueueToggle: "显示上游排队位置",
|
|
1932
|
+
displayNotWritable: "这个宿主没给插件开配置存储,开关只能看不能改。",
|
|
1933
|
+
displayFailed: "保存失败:{message}",
|
|
1408
1934
|
queueDock: "排队第 {position} 位",
|
|
1409
1935
|
queueDockTitle: "上游排队:{model} · 第 {position} 位 / 共 {length} 条",
|
|
1410
1936
|
claimButton: "签到领积分",
|
|
@@ -1457,6 +1983,7 @@ const en = {
|
|
|
1457
1983
|
benefit: "Benefit",
|
|
1458
1984
|
benefitBlocked: "Benefit unavailable today",
|
|
1459
1985
|
benefitBlockedHint: "{n} benefit model(s) were told they have no entitlement today (campaign exhausted or channel not opened); sending to them fails with benefit not found.",
|
|
1986
|
+
modelImages: "Images",
|
|
1460
1987
|
refreshModels: "Refresh models",
|
|
1461
1988
|
refreshing: "Refreshing…",
|
|
1462
1989
|
refreshed: "Refreshed: {n} models, {benefit} on the benefit route",
|
|
@@ -1502,6 +2029,24 @@ const en = {
|
|
|
1502
2029
|
creditsNeedSignIn: "This account needs signing in again",
|
|
1503
2030
|
creditsDock: "Credits {remain}",
|
|
1504
2031
|
creditsDockTitle: "CodeArts credits left {remain} / {total}",
|
|
2032
|
+
creditsDockPanelTitle: "CodeArts credits",
|
|
2033
|
+
creditsDockRefresh: "Refresh credits",
|
|
2034
|
+
creditsDockRefreshing: "Refreshing…",
|
|
2035
|
+
creditsDockColAccount: "Account",
|
|
2036
|
+
creditsDockColBalance: "Credits left",
|
|
2037
|
+
creditsDockEmpty: "No credit data yet",
|
|
2038
|
+
creditsDockExpiring: "{account}: {expiring} credits expire when this cycle ends",
|
|
2039
|
+
creditsDockUpdated: "Last updated {time}",
|
|
2040
|
+
dockProvider: "Provider {provider}",
|
|
2041
|
+
dockModel: "Model {model}",
|
|
2042
|
+
dockNoModel: "No model selected",
|
|
2043
|
+
tabDisplay: "Display",
|
|
2044
|
+
displayTitle: "Under the chat input box",
|
|
2045
|
+
displayHint: "These two only control the credits and queue segments of that line under the input box; the middle “Provider CodeArts · Model … · rate” segments always show. The bar re-reads settings every 60 seconds, so hiding the rest can take up to a minute.",
|
|
2046
|
+
displayCreditsToggle: "Show remaining credits (click it for per-account details)",
|
|
2047
|
+
displayQueueToggle: "Show the upstream queue position",
|
|
2048
|
+
displayNotWritable: "This host gives the plugin nowhere to persist settings, so the switches are read-only.",
|
|
2049
|
+
displayFailed: "Failed to save: {message}",
|
|
1505
2050
|
queueDock: "Queued #{position}",
|
|
1506
2051
|
queueDockTitle: "Upstream queue: {model} · position {position} of {length}",
|
|
1507
2052
|
claimButton: "Claim daily credits",
|