dsh-qoder-connect 0.1.5 → 0.1.9
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/README.en.md +7 -2
- package/README.md +7 -2
- package/lib/bin.js +1 -1
- package/lib/client.js +696 -30
- package/lib/index.d.ts +58 -4
- package/lib/index.js +1056 -188
- package/lib/{variants-BEiQve5M.js → variants-C8ezztjV.js} +46 -4
- package/package.json +120 -116
package/lib/client.js
CHANGED
|
@@ -213,9 +213,9 @@ window.__ModuleLoader__.load({
|
|
|
213
213
|
* The shared quota-settings card: one card above the two variant cards that
|
|
214
214
|
* configures both sidebar quota widgets.
|
|
215
215
|
*
|
|
216
|
-
* Like the built-in plugin cards, it registers into
|
|
217
|
-
*
|
|
218
|
-
*
|
|
216
|
+
* Like the built-in plugin cards, it registers into the Plugins tab's card
|
|
217
|
+
* list — `settings.plugin.item` on DSH 0.1.5, the shared 《插件设置》 block on
|
|
218
|
+
* 0.1.7, where that list is gone — and writes through the settings face's
|
|
219
219
|
* revision-fenced `set` — the same durable-write path every preference row
|
|
220
220
|
* uses. A toggle commits on click: each click is one explicit user choice,
|
|
221
221
|
* and the scope's ordering makes the last one win, so no staged-draft form is
|
|
@@ -230,6 +230,17 @@ window.__ModuleLoader__.load({
|
|
|
230
230
|
const POLL_DEFAULT_MS = 3e5;
|
|
231
231
|
/** Floor the schema also enforces; mirrored here for immediate UI feedback. */
|
|
232
232
|
const POLL_MIN_MS = 6e4;
|
|
233
|
+
/** 10:00 UTC+8, the moment the upstream resets the daily campaign. */
|
|
234
|
+
const CHECK_IN_MINUTE_DEFAULT = 600;
|
|
235
|
+
/** Minutes past midnight (UTC+8), split for the two number fields. */
|
|
236
|
+
function splitMinutes(minutes) {
|
|
237
|
+
const safe = Number.isFinite(minutes) ? Math.trunc(minutes) : CHECK_IN_MINUTE_DEFAULT;
|
|
238
|
+
const clamped = safe < 0 || safe > 1439 ? CHECK_IN_MINUTE_DEFAULT : safe;
|
|
239
|
+
return {
|
|
240
|
+
hours: Math.floor(clamped / 60),
|
|
241
|
+
minutes: clamped % 60
|
|
242
|
+
};
|
|
243
|
+
}
|
|
233
244
|
/** Read the section values out of a scope snapshot (defaults when absent). */
|
|
234
245
|
function project(scope) {
|
|
235
246
|
if (scope === void 0) return {
|
|
@@ -240,6 +251,8 @@ window.__ModuleLoader__.load({
|
|
|
240
251
|
sidebarQuotaGlobal: false,
|
|
241
252
|
autoCheckInCN: false,
|
|
242
253
|
autoCheckInGlobal: false,
|
|
254
|
+
checkInMinuteCN: CHECK_IN_MINUTE_DEFAULT,
|
|
255
|
+
checkInMinuteGlobal: CHECK_IN_MINUTE_DEFAULT,
|
|
243
256
|
quotaPollMs: POLL_DEFAULT_MS
|
|
244
257
|
}
|
|
245
258
|
};
|
|
@@ -253,6 +266,8 @@ window.__ModuleLoader__.load({
|
|
|
253
266
|
sidebarQuotaGlobal: value.sidebarQuotaGlobal === true,
|
|
254
267
|
autoCheckInCN: value.autoCheckInCN === true,
|
|
255
268
|
autoCheckInGlobal: value.autoCheckInGlobal === true,
|
|
269
|
+
checkInMinuteCN: typeof value.checkInMinuteCN === "number" ? value.checkInMinuteCN : CHECK_IN_MINUTE_DEFAULT,
|
|
270
|
+
checkInMinuteGlobal: typeof value.checkInMinuteGlobal === "number" ? value.checkInMinuteGlobal : CHECK_IN_MINUTE_DEFAULT,
|
|
256
271
|
quotaPollMs: typeof value.quotaPollMs === "number" ? value.quotaPollMs : POLL_DEFAULT_MS
|
|
257
272
|
}
|
|
258
273
|
};
|
|
@@ -278,13 +293,15 @@ window.__ModuleLoader__.load({
|
|
|
278
293
|
sidebarQuotaGlobal: false,
|
|
279
294
|
autoCheckInCN: false,
|
|
280
295
|
autoCheckInGlobal: false,
|
|
296
|
+
checkInMinuteCN: CHECK_IN_MINUTE_DEFAULT,
|
|
297
|
+
checkInMinuteGlobal: CHECK_IN_MINUTE_DEFAULT,
|
|
281
298
|
quotaPollMs: POLL_DEFAULT_MS
|
|
282
299
|
}
|
|
283
300
|
};
|
|
284
301
|
function stableProject(scope) {
|
|
285
302
|
if (scope === void 0) return UNAVAILABLE;
|
|
286
303
|
const next = project(scope);
|
|
287
|
-
if (cachedProjection === void 0 || cachedScope !== scope || cachedProjection.status !== next.status || cachedProjection.writable !== next.writable || cachedProjection.values.sidebarQuotaCN !== next.values.sidebarQuotaCN || cachedProjection.values.sidebarQuotaGlobal !== next.values.sidebarQuotaGlobal || cachedProjection.values.autoCheckInCN !== next.values.autoCheckInCN || cachedProjection.values.autoCheckInGlobal !== next.values.autoCheckInGlobal || cachedProjection.values.quotaPollMs !== next.values.quotaPollMs) {
|
|
304
|
+
if (cachedProjection === void 0 || cachedScope !== scope || cachedProjection.status !== next.status || cachedProjection.writable !== next.writable || cachedProjection.values.sidebarQuotaCN !== next.values.sidebarQuotaCN || cachedProjection.values.sidebarQuotaGlobal !== next.values.sidebarQuotaGlobal || cachedProjection.values.autoCheckInCN !== next.values.autoCheckInCN || cachedProjection.values.autoCheckInGlobal !== next.values.autoCheckInGlobal || cachedProjection.values.checkInMinuteCN !== next.values.checkInMinuteCN || cachedProjection.values.checkInMinuteGlobal !== next.values.checkInMinuteGlobal || cachedProjection.values.quotaPollMs !== next.values.quotaPollMs) {
|
|
288
305
|
cachedScope = scope;
|
|
289
306
|
cachedProjection = next;
|
|
290
307
|
}
|
|
@@ -324,6 +341,106 @@ window.__ModuleLoader__.load({
|
|
|
324
341
|
})]
|
|
325
342
|
});
|
|
326
343
|
}
|
|
344
|
+
/**
|
|
345
|
+
* One time row: the moment a variant checks in, as two typeable number fields.
|
|
346
|
+
*
|
|
347
|
+
* Deliberately NOT `<input type="time">`: that control's segments are spinners,
|
|
348
|
+
* and with an IME active (every Chinese/Japanese keyboard) the segments refuse
|
|
349
|
+
* typed digits, leaving only the tiny stepper arrows — unusable for the one
|
|
350
|
+
* thing this row exists to do. Two plain number fields accept typing under any
|
|
351
|
+
* input method; the value is committed on blur or Enter, so a half-typed
|
|
352
|
+
* field never writes a wrong moment to the host.
|
|
353
|
+
*/
|
|
354
|
+
function TimeRow({ label, hint, value, disabled, onPick }) {
|
|
355
|
+
const split = splitMinutes(value);
|
|
356
|
+
const [hourDraft, setHourDraft] = (0, react.useState)(String(split.hours));
|
|
357
|
+
const [minuteDraft, setMinuteDraft] = (0, react.useState)(String(split.minutes).padStart(2, "0"));
|
|
358
|
+
(0, react.useEffect)(() => {
|
|
359
|
+
const next = splitMinutes(value);
|
|
360
|
+
setHourDraft(String(next.hours));
|
|
361
|
+
setMinuteDraft(String(next.minutes).padStart(2, "0"));
|
|
362
|
+
}, [value]);
|
|
363
|
+
const commit = () => {
|
|
364
|
+
const parsedHours = Number.parseInt(hourDraft, 10);
|
|
365
|
+
const parsedMinutes = Number.parseInt(minuteDraft, 10);
|
|
366
|
+
const hours = Number.isFinite(parsedHours) ? Math.min(23, Math.max(0, parsedHours)) : split.hours;
|
|
367
|
+
const minutes = Number.isFinite(parsedMinutes) ? Math.min(59, Math.max(0, parsedMinutes)) : split.minutes;
|
|
368
|
+
const next = hours * 60 + minutes;
|
|
369
|
+
if (next === value) {
|
|
370
|
+
setHourDraft(String(hours));
|
|
371
|
+
setMinuteDraft(String(minutes).padStart(2, "0"));
|
|
372
|
+
return;
|
|
373
|
+
}
|
|
374
|
+
onPick(next);
|
|
375
|
+
};
|
|
376
|
+
const onKeyDown = (event) => {
|
|
377
|
+
if (event.key === "Enter") {
|
|
378
|
+
event.preventDefault();
|
|
379
|
+
commit();
|
|
380
|
+
}
|
|
381
|
+
};
|
|
382
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
383
|
+
style: rowStyle$1,
|
|
384
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
385
|
+
style: rowTextStyle,
|
|
386
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
387
|
+
style: labelStyle$1,
|
|
388
|
+
children: label
|
|
389
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
390
|
+
style: hintStyle,
|
|
391
|
+
children: hint
|
|
392
|
+
})]
|
|
393
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
394
|
+
style: pollFieldStyle,
|
|
395
|
+
children: [
|
|
396
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
397
|
+
type: "number",
|
|
398
|
+
min: 0,
|
|
399
|
+
max: 23,
|
|
400
|
+
value: hourDraft,
|
|
401
|
+
disabled,
|
|
402
|
+
"aria-label": `${label} — hour`,
|
|
403
|
+
"data-checkin-part": "hour",
|
|
404
|
+
onChange: (event) => {
|
|
405
|
+
setHourDraft(event.target.value);
|
|
406
|
+
},
|
|
407
|
+
onBlur: commit,
|
|
408
|
+
onKeyDown,
|
|
409
|
+
style: {
|
|
410
|
+
...timePartStyle,
|
|
411
|
+
opacity: disabled === true ? .45 : 1
|
|
412
|
+
}
|
|
413
|
+
}),
|
|
414
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
415
|
+
style: labelStyle$1,
|
|
416
|
+
children: ":"
|
|
417
|
+
}),
|
|
418
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
419
|
+
type: "number",
|
|
420
|
+
min: 0,
|
|
421
|
+
max: 59,
|
|
422
|
+
value: minuteDraft,
|
|
423
|
+
disabled,
|
|
424
|
+
"aria-label": `${label} — minute`,
|
|
425
|
+
"data-checkin-part": "minute",
|
|
426
|
+
onChange: (event) => {
|
|
427
|
+
setMinuteDraft(event.target.value);
|
|
428
|
+
},
|
|
429
|
+
onBlur: commit,
|
|
430
|
+
onKeyDown,
|
|
431
|
+
style: {
|
|
432
|
+
...timePartStyle,
|
|
433
|
+
opacity: disabled === true ? .45 : 1
|
|
434
|
+
}
|
|
435
|
+
}),
|
|
436
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
437
|
+
style: hintStyle,
|
|
438
|
+
children: "UTC+8"
|
|
439
|
+
})
|
|
440
|
+
]
|
|
441
|
+
})]
|
|
442
|
+
});
|
|
443
|
+
}
|
|
327
444
|
/** The inner controls for sidebar quota settings. */
|
|
328
445
|
function QuotaSettingsContent({ t = (key) => key, scope, signedIn }) {
|
|
329
446
|
const subscribe = (0, react.useCallback)((onStoreChange) => {
|
|
@@ -411,6 +528,13 @@ window.__ModuleLoader__.load({
|
|
|
411
528
|
disabledHint: t("quotaSignInRequired"),
|
|
412
529
|
onToggle: (next) => write("autoCheckInCN", next)
|
|
413
530
|
}),
|
|
531
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(TimeRow, {
|
|
532
|
+
label: t("checkInTimeCN"),
|
|
533
|
+
hint: t("checkInTimeHint"),
|
|
534
|
+
value: projection.values.checkInMinuteCN,
|
|
535
|
+
disabled: !signed.cn,
|
|
536
|
+
onPick: (next) => write("checkInMinuteCN", next)
|
|
537
|
+
}),
|
|
414
538
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(ToggleRow, {
|
|
415
539
|
label: t("autoCheckInGlobal"),
|
|
416
540
|
hint: t("autoCheckInHintGlobal"),
|
|
@@ -419,6 +543,13 @@ window.__ModuleLoader__.load({
|
|
|
419
543
|
disabledHint: t("quotaSignInRequired"),
|
|
420
544
|
onToggle: (next) => write("autoCheckInGlobal", next)
|
|
421
545
|
}),
|
|
546
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(TimeRow, {
|
|
547
|
+
label: t("checkInTimeGlobal"),
|
|
548
|
+
hint: t("checkInTimeHint"),
|
|
549
|
+
value: projection.values.checkInMinuteGlobal,
|
|
550
|
+
disabled: !signed.global,
|
|
551
|
+
onPick: (next) => write("checkInMinuteGlobal", next)
|
|
552
|
+
}),
|
|
422
553
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
423
554
|
style: {
|
|
424
555
|
...rowStyle$1,
|
|
@@ -518,6 +649,20 @@ window.__ModuleLoader__.load({
|
|
|
518
649
|
gap: 6,
|
|
519
650
|
flex: "none"
|
|
520
651
|
};
|
|
652
|
+
const timePartStyle = {
|
|
653
|
+
boxSizing: "border-box",
|
|
654
|
+
width: 56,
|
|
655
|
+
padding: "5px 8px",
|
|
656
|
+
borderWidth: "1px",
|
|
657
|
+
borderStyle: "solid",
|
|
658
|
+
borderColor: "var(--dsw-alias-border-l2)",
|
|
659
|
+
borderRadius: 8,
|
|
660
|
+
background: "var(--dsw-alias-bg-layer-2)",
|
|
661
|
+
color: "var(--dsw-alias-label-primary)",
|
|
662
|
+
font: "inherit",
|
|
663
|
+
fontSize: 13,
|
|
664
|
+
textAlign: "center"
|
|
665
|
+
};
|
|
521
666
|
const inputStyle = {
|
|
522
667
|
boxSizing: "border-box",
|
|
523
668
|
width: 55,
|
|
@@ -1064,7 +1209,246 @@ window.__ModuleLoader__.load({
|
|
|
1064
1209
|
if (tokens >= 1e3 && tokens % 1e3 === 0) return `${tokens / 1e3}K`;
|
|
1065
1210
|
return String(tokens);
|
|
1066
1211
|
}
|
|
1067
|
-
|
|
1212
|
+
/**
|
|
1213
|
+
* Model visibility table with individual toggle switches and batch enable/disable controls.
|
|
1214
|
+
*/
|
|
1215
|
+
function ModelSwitchTable({ models, disabledModels = [], t, disabled, onSetModelsEnabled }) {
|
|
1216
|
+
const [selected, setSelected] = (0, react.useState)(/* @__PURE__ */ new Set());
|
|
1217
|
+
const [searchQuery, setSearchQuery] = (0, react.useState)("");
|
|
1218
|
+
const disabledSet = (0, react.useMemo)(() => new Set(disabledModels), [disabledModels]);
|
|
1219
|
+
const list = (0, react.useMemo)(() => models ?? [], [models]);
|
|
1220
|
+
const filteredList = (0, react.useMemo)(() => {
|
|
1221
|
+
const q = searchQuery.trim().toLowerCase();
|
|
1222
|
+
if (!q) return list;
|
|
1223
|
+
return list.filter((m) => m.name.toLowerCase().includes(q) || m.id.toLowerCase().includes(q));
|
|
1224
|
+
}, [list, searchQuery]);
|
|
1225
|
+
const allFilteredSelected = filteredList.length > 0 && filteredList.every((m) => selected.has(m.id));
|
|
1226
|
+
const toggleSelectAll = () => {
|
|
1227
|
+
if (allFilteredSelected) {
|
|
1228
|
+
const next = new Set(selected);
|
|
1229
|
+
for (const m of filteredList) next.delete(m.id);
|
|
1230
|
+
setSelected(next);
|
|
1231
|
+
} else {
|
|
1232
|
+
const next = new Set(selected);
|
|
1233
|
+
for (const m of filteredList) next.add(m.id);
|
|
1234
|
+
setSelected(next);
|
|
1235
|
+
}
|
|
1236
|
+
};
|
|
1237
|
+
const toggleSelectOne = (id) => {
|
|
1238
|
+
const next = new Set(selected);
|
|
1239
|
+
if (next.has(id)) next.delete(id);
|
|
1240
|
+
else next.add(id);
|
|
1241
|
+
setSelected(next);
|
|
1242
|
+
};
|
|
1243
|
+
const handleBatch = (enabled) => {
|
|
1244
|
+
const targets = Array.from(selected);
|
|
1245
|
+
if (targets.length === 0) return;
|
|
1246
|
+
onSetModelsEnabled(targets, enabled);
|
|
1247
|
+
setSelected(/* @__PURE__ */ new Set());
|
|
1248
|
+
};
|
|
1249
|
+
if (list.length === 0) return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1250
|
+
style: quotaListStyle,
|
|
1251
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", {
|
|
1252
|
+
style: quotaTitleStyle,
|
|
1253
|
+
children: t("modelsHeading")
|
|
1254
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1255
|
+
style: descriptionStyle,
|
|
1256
|
+
children: t("modelsNoModels")
|
|
1257
|
+
})]
|
|
1258
|
+
});
|
|
1259
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1260
|
+
style: quotaListStyle,
|
|
1261
|
+
children: [
|
|
1262
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1263
|
+
style: {
|
|
1264
|
+
display: "flex",
|
|
1265
|
+
flexDirection: "column",
|
|
1266
|
+
gap: 6
|
|
1267
|
+
},
|
|
1268
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", {
|
|
1269
|
+
style: quotaTitleStyle,
|
|
1270
|
+
children: t("modelsHeading")
|
|
1271
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1272
|
+
style: descriptionStyle,
|
|
1273
|
+
children: t("modelsSubtitle")
|
|
1274
|
+
})]
|
|
1275
|
+
}),
|
|
1276
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1277
|
+
style: {
|
|
1278
|
+
display: "flex",
|
|
1279
|
+
alignItems: "center",
|
|
1280
|
+
gap: 8
|
|
1281
|
+
},
|
|
1282
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
1283
|
+
type: "text",
|
|
1284
|
+
value: searchQuery,
|
|
1285
|
+
placeholder: t("modelsSearchPlaceholder"),
|
|
1286
|
+
onChange: (e) => {
|
|
1287
|
+
setSearchQuery(e.currentTarget.value);
|
|
1288
|
+
},
|
|
1289
|
+
style: patInputStyle
|
|
1290
|
+
}), searchQuery ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1291
|
+
type: "button",
|
|
1292
|
+
style: buttonStyle$1,
|
|
1293
|
+
onClick: () => {
|
|
1294
|
+
setSearchQuery("");
|
|
1295
|
+
},
|
|
1296
|
+
children: t("cancel")
|
|
1297
|
+
}) : null]
|
|
1298
|
+
}),
|
|
1299
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1300
|
+
style: {
|
|
1301
|
+
display: "flex",
|
|
1302
|
+
alignItems: "center",
|
|
1303
|
+
justifyContent: "space-between",
|
|
1304
|
+
flexWrap: "wrap",
|
|
1305
|
+
gap: 8,
|
|
1306
|
+
padding: "6px 0",
|
|
1307
|
+
borderBottom: "1px solid var(--dsw-alias-border-l2)"
|
|
1308
|
+
},
|
|
1309
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
1310
|
+
style: {
|
|
1311
|
+
display: "inline-flex",
|
|
1312
|
+
alignItems: "center",
|
|
1313
|
+
gap: 8,
|
|
1314
|
+
cursor: "pointer",
|
|
1315
|
+
fontSize: 13,
|
|
1316
|
+
userSelect: "none"
|
|
1317
|
+
},
|
|
1318
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
1319
|
+
type: "checkbox",
|
|
1320
|
+
checked: allFilteredSelected,
|
|
1321
|
+
disabled: disabled || filteredList.length === 0,
|
|
1322
|
+
onChange: toggleSelectAll
|
|
1323
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: allFilteredSelected ? t("modelsDeselectAll") : t("modelsSelectAll") })]
|
|
1324
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1325
|
+
style: {
|
|
1326
|
+
display: "flex",
|
|
1327
|
+
alignItems: "center",
|
|
1328
|
+
gap: 8
|
|
1329
|
+
},
|
|
1330
|
+
children: [
|
|
1331
|
+
selected.size > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1332
|
+
style: modelRateStyle,
|
|
1333
|
+
children: t("modelsSelectedCount", { count: selected.size })
|
|
1334
|
+
}) : null,
|
|
1335
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1336
|
+
type: "button",
|
|
1337
|
+
style: buttonStyle$1,
|
|
1338
|
+
disabled: disabled || selected.size === 0,
|
|
1339
|
+
onClick: () => {
|
|
1340
|
+
handleBatch(true);
|
|
1341
|
+
},
|
|
1342
|
+
children: t("modelsEnableSelected")
|
|
1343
|
+
}),
|
|
1344
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1345
|
+
type: "button",
|
|
1346
|
+
style: buttonStyle$1,
|
|
1347
|
+
disabled: disabled || selected.size === 0,
|
|
1348
|
+
onClick: () => {
|
|
1349
|
+
handleBatch(false);
|
|
1350
|
+
},
|
|
1351
|
+
children: t("modelsDisableSelected")
|
|
1352
|
+
})
|
|
1353
|
+
]
|
|
1354
|
+
})]
|
|
1355
|
+
}),
|
|
1356
|
+
filteredList.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1357
|
+
style: descriptionStyle,
|
|
1358
|
+
children: t("modelsNoMatch", { query: searchQuery.trim() })
|
|
1359
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1360
|
+
style: {
|
|
1361
|
+
display: "flex",
|
|
1362
|
+
flexDirection: "column",
|
|
1363
|
+
gap: 8
|
|
1364
|
+
},
|
|
1365
|
+
children: filteredList.map((model) => {
|
|
1366
|
+
const isModelEnabled = !disabledSet.has(model.id);
|
|
1367
|
+
const isChecked = selected.has(model.id);
|
|
1368
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1369
|
+
style: {
|
|
1370
|
+
display: "flex",
|
|
1371
|
+
alignItems: "center",
|
|
1372
|
+
justifyContent: "space-between",
|
|
1373
|
+
padding: "8px 10px",
|
|
1374
|
+
borderRadius: 6,
|
|
1375
|
+
background: "var(--dsw-alias-bg-layer-2)",
|
|
1376
|
+
gap: 12,
|
|
1377
|
+
opacity: isModelEnabled ? 1 : .65
|
|
1378
|
+
},
|
|
1379
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1380
|
+
style: {
|
|
1381
|
+
display: "inline-flex",
|
|
1382
|
+
alignItems: "center",
|
|
1383
|
+
gap: 10,
|
|
1384
|
+
minWidth: 0,
|
|
1385
|
+
flex: 1
|
|
1386
|
+
},
|
|
1387
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
1388
|
+
type: "checkbox",
|
|
1389
|
+
checked: isChecked,
|
|
1390
|
+
disabled,
|
|
1391
|
+
onChange: () => {
|
|
1392
|
+
toggleSelectOne(model.id);
|
|
1393
|
+
}
|
|
1394
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1395
|
+
style: {
|
|
1396
|
+
display: "flex",
|
|
1397
|
+
flexDirection: "column",
|
|
1398
|
+
minWidth: 0
|
|
1399
|
+
},
|
|
1400
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1401
|
+
style: {
|
|
1402
|
+
fontWeight: 500,
|
|
1403
|
+
fontSize: 13,
|
|
1404
|
+
color: "var(--dsw-alias-label-primary)"
|
|
1405
|
+
},
|
|
1406
|
+
children: model.name
|
|
1407
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1408
|
+
style: {
|
|
1409
|
+
fontSize: 11,
|
|
1410
|
+
color: "var(--dsw-alias-label-tertiary)"
|
|
1411
|
+
},
|
|
1412
|
+
children: model.id
|
|
1413
|
+
})]
|
|
1414
|
+
})]
|
|
1415
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1416
|
+
style: {
|
|
1417
|
+
display: "flex",
|
|
1418
|
+
alignItems: "center",
|
|
1419
|
+
gap: 10
|
|
1420
|
+
},
|
|
1421
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
1422
|
+
style: {
|
|
1423
|
+
display: "inline-flex",
|
|
1424
|
+
alignItems: "center",
|
|
1425
|
+
gap: 6,
|
|
1426
|
+
cursor: disabled ? "not-allowed" : "pointer",
|
|
1427
|
+
userSelect: "none"
|
|
1428
|
+
},
|
|
1429
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
1430
|
+
type: "checkbox",
|
|
1431
|
+
checked: isModelEnabled,
|
|
1432
|
+
disabled,
|
|
1433
|
+
onChange: (event) => {
|
|
1434
|
+
onSetModelsEnabled([model.id], event.currentTarget.checked);
|
|
1435
|
+
}
|
|
1436
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1437
|
+
style: {
|
|
1438
|
+
fontSize: 12,
|
|
1439
|
+
color: isModelEnabled ? "var(--dsw-alias-label-primary)" : "var(--dsw-alias-label-tertiary)"
|
|
1440
|
+
},
|
|
1441
|
+
children: isModelEnabled ? t("modelsEnabled") : t("modelsDisabled")
|
|
1442
|
+
})]
|
|
1443
|
+
})
|
|
1444
|
+
})]
|
|
1445
|
+
}, model.id);
|
|
1446
|
+
})
|
|
1447
|
+
})
|
|
1448
|
+
]
|
|
1449
|
+
});
|
|
1450
|
+
}
|
|
1451
|
+
function CheckInLogTable({ logs = [], t, onCheckIn, onRefresh, onClear, busy, checkingIn, clearing, disabled, notice, nextRun }) {
|
|
1068
1452
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1069
1453
|
style: quotaListStyle,
|
|
1070
1454
|
children: [
|
|
@@ -1114,6 +1498,10 @@ window.__ModuleLoader__.load({
|
|
|
1114
1498
|
style: bodyStyle,
|
|
1115
1499
|
children: notice
|
|
1116
1500
|
}),
|
|
1501
|
+
nextRun === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1502
|
+
style: descriptionStyle,
|
|
1503
|
+
children: t("checkInNextRun", { time: formatTime(nextRun) })
|
|
1504
|
+
}),
|
|
1117
1505
|
!logs || logs.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1118
1506
|
style: descriptionStyle,
|
|
1119
1507
|
children: t("checkInLogEmpty")
|
|
@@ -1464,7 +1852,7 @@ window.__ModuleLoader__.load({
|
|
|
1464
1852
|
const message = typeof value === "object" && value !== null && "error" in value ? String(value["error"]) : `HTTP ${response.status}`;
|
|
1465
1853
|
throw new Error(message);
|
|
1466
1854
|
}
|
|
1467
|
-
if (action.action === "set-maximum-context-window" && (typeof value !== "object" || value === null || value["state"] !== "updated")) {
|
|
1855
|
+
if ((action.action === "set-maximum-context-window" || action.action === "set-models-enabled") && (typeof value !== "object" || value === null || value["state"] !== "updated")) {
|
|
1468
1856
|
const reason = typeof value === "object" && value !== null && "reason" in value ? String(value["reason"]) : t("requestFailed");
|
|
1469
1857
|
throw new Error(reason);
|
|
1470
1858
|
}
|
|
@@ -1856,6 +2244,7 @@ window.__ModuleLoader__.load({
|
|
|
1856
2244
|
children: [
|
|
1857
2245
|
"status",
|
|
1858
2246
|
"context",
|
|
2247
|
+
"models",
|
|
1859
2248
|
"details",
|
|
1860
2249
|
"checkin"
|
|
1861
2250
|
].map((id) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
@@ -1869,7 +2258,7 @@ window.__ModuleLoader__.load({
|
|
|
1869
2258
|
...tabStyle,
|
|
1870
2259
|
...tab === id ? tabActiveStyle : {}
|
|
1871
2260
|
},
|
|
1872
|
-
children: t(id === "status" ? "tabStatus" : id === "context" ? "tabContext" : id === "details" ? "tabDetails" : "tabCheckIn")
|
|
2261
|
+
children: t(id === "status" ? "tabStatus" : id === "context" ? "tabContext" : id === "models" ? "tabModels" : id === "details" ? "tabDetails" : "tabCheckIn")
|
|
1873
2262
|
}, id))
|
|
1874
2263
|
}),
|
|
1875
2264
|
tab === "status" ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
@@ -1907,6 +2296,21 @@ window.__ModuleLoader__.load({
|
|
|
1907
2296
|
});
|
|
1908
2297
|
}
|
|
1909
2298
|
})
|
|
2299
|
+
}) : tab === "models" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
2300
|
+
style: tabPanelStyle,
|
|
2301
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ModelSwitchTable, {
|
|
2302
|
+
models: status.models,
|
|
2303
|
+
disabledModels: status.disabledModels,
|
|
2304
|
+
t,
|
|
2305
|
+
disabled: busy,
|
|
2306
|
+
onSetModelsEnabled: (models, enabled) => {
|
|
2307
|
+
control({
|
|
2308
|
+
action: "set-models-enabled",
|
|
2309
|
+
models,
|
|
2310
|
+
enabled
|
|
2311
|
+
});
|
|
2312
|
+
}
|
|
2313
|
+
})
|
|
1910
2314
|
}) : tab === "details" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1911
2315
|
style: tabPanelStyle,
|
|
1912
2316
|
children: status.credits === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
@@ -1933,6 +2337,7 @@ window.__ModuleLoader__.load({
|
|
|
1933
2337
|
clearing: clearingLogs,
|
|
1934
2338
|
disabled: status.status !== "signed-in",
|
|
1935
2339
|
...checkInNotice === void 0 ? {} : { notice: checkInNotice },
|
|
2340
|
+
...status.checkIn?.nextRunAt === void 0 ? {} : { nextRun: status.checkIn.nextRunAt },
|
|
1936
2341
|
onCheckIn: () => {
|
|
1937
2342
|
manualCheckIn();
|
|
1938
2343
|
},
|
|
@@ -2388,6 +2793,115 @@ window.__ModuleLoader__.load({
|
|
|
2388
2793
|
return t("probeNoteUnknown");
|
|
2389
2794
|
}
|
|
2390
2795
|
//#endregion
|
|
2796
|
+
//#region src/client/http-settings-scope.ts
|
|
2797
|
+
/** Base path of the host half's settings face. */
|
|
2798
|
+
const ROUTE_BASE = "/plugins/dsh-qoder-connect";
|
|
2799
|
+
/**
|
|
2800
|
+
* Fetch one settings face document.
|
|
2801
|
+
* @param init - the request init (method, headers, body).
|
|
2802
|
+
* @returns the parsed document.
|
|
2803
|
+
* @throws when the transport or the host refuses the request.
|
|
2804
|
+
*/
|
|
2805
|
+
async function request(init) {
|
|
2806
|
+
const response = await fetch(`${ROUTE_BASE}/settings`, {
|
|
2807
|
+
credentials: "same-origin",
|
|
2808
|
+
...init
|
|
2809
|
+
});
|
|
2810
|
+
const value = await response.json().catch(() => void 0);
|
|
2811
|
+
if (!response.ok) {
|
|
2812
|
+
const error = typeof value === "object" && value !== null && "error" in value ? String(value["error"]) : `HTTP ${response.status}`;
|
|
2813
|
+
throw new Error(error);
|
|
2814
|
+
}
|
|
2815
|
+
if (typeof value !== "object" || value === null || !("value" in value)) throw new Error("qoder: settings face answered an invalid document");
|
|
2816
|
+
return value;
|
|
2817
|
+
}
|
|
2818
|
+
/**
|
|
2819
|
+
* The scope the card was written against, backed by this plugin's own file.
|
|
2820
|
+
*
|
|
2821
|
+
* A write optimistically adopts the value it just sent, then confirms with the
|
|
2822
|
+
* host's answer (which re-reads the file it wrote), so the card never renders a
|
|
2823
|
+
* value the host does not hold.
|
|
2824
|
+
*/
|
|
2825
|
+
var OwnQuotaSettingsScope = class {
|
|
2826
|
+
document;
|
|
2827
|
+
listeners = /* @__PURE__ */ new Set();
|
|
2828
|
+
snapshot = {
|
|
2829
|
+
status: "loading",
|
|
2830
|
+
value: void 0,
|
|
2831
|
+
writable: true
|
|
2832
|
+
};
|
|
2833
|
+
/**
|
|
2834
|
+
* Load the current document. Call once before the card binds; repeated calls
|
|
2835
|
+
* are harmless and re-read the host.
|
|
2836
|
+
* @returns the effective values.
|
|
2837
|
+
*/
|
|
2838
|
+
async load() {
|
|
2839
|
+
try {
|
|
2840
|
+
this.document = await request({ headers: { accept: "application/json" } });
|
|
2841
|
+
} catch {
|
|
2842
|
+
return;
|
|
2843
|
+
}
|
|
2844
|
+
this.publish();
|
|
2845
|
+
return this.document.value;
|
|
2846
|
+
}
|
|
2847
|
+
/** @returns the stable snapshot the card reads (ready once loaded). */
|
|
2848
|
+
getSnapshot() {
|
|
2849
|
+
return this.snapshot;
|
|
2850
|
+
}
|
|
2851
|
+
/** @param listener - invoked after every snapshot change. @returns the disposer. */
|
|
2852
|
+
subscribe(listener) {
|
|
2853
|
+
this.listeners.add(listener);
|
|
2854
|
+
return () => {
|
|
2855
|
+
this.listeners.delete(listener);
|
|
2856
|
+
};
|
|
2857
|
+
}
|
|
2858
|
+
/**
|
|
2859
|
+
* Write one field.
|
|
2860
|
+
* @param field - field name inside the settings document.
|
|
2861
|
+
* @param value - the JSON-shaped value to store.
|
|
2862
|
+
* @returns whether the host accepted the write.
|
|
2863
|
+
*/
|
|
2864
|
+
async set(field, value) {
|
|
2865
|
+
return this.patch({ [field]: value });
|
|
2866
|
+
}
|
|
2867
|
+
/**
|
|
2868
|
+
* Clear one field: the value falls back to the schema default.
|
|
2869
|
+
* @param field - field name inside the settings document.
|
|
2870
|
+
* @returns whether the host accepted the clear.
|
|
2871
|
+
*/
|
|
2872
|
+
async unset(field) {
|
|
2873
|
+
return this.patch({ [field]: null });
|
|
2874
|
+
}
|
|
2875
|
+
/** Send one patch and adopt the host's answer. */
|
|
2876
|
+
async patch(patch) {
|
|
2877
|
+
const key = this.document?.key;
|
|
2878
|
+
if (key === void 0) return false;
|
|
2879
|
+
try {
|
|
2880
|
+
this.document = await request({
|
|
2881
|
+
method: "POST",
|
|
2882
|
+
headers: {
|
|
2883
|
+
"Content-Type": "application/json",
|
|
2884
|
+
"X-Qoder-Settings-Key": key
|
|
2885
|
+
},
|
|
2886
|
+
body: JSON.stringify(patch)
|
|
2887
|
+
});
|
|
2888
|
+
this.publish();
|
|
2889
|
+
return true;
|
|
2890
|
+
} catch {
|
|
2891
|
+
return false;
|
|
2892
|
+
}
|
|
2893
|
+
}
|
|
2894
|
+
/** Rebuild the cached snapshot and notify the subscribers. */
|
|
2895
|
+
publish() {
|
|
2896
|
+
this.snapshot = {
|
|
2897
|
+
status: this.document === void 0 ? "loading" : "ready",
|
|
2898
|
+
value: this.document?.value,
|
|
2899
|
+
writable: true
|
|
2900
|
+
};
|
|
2901
|
+
for (const listener of this.listeners) listener();
|
|
2902
|
+
}
|
|
2903
|
+
};
|
|
2904
|
+
//#endregion
|
|
2391
2905
|
//#region src/client/quota-merge.ts
|
|
2392
2906
|
/**
|
|
2393
2907
|
* Group credit accounts by (packageName, packageEndTime) and sum each group.
|
|
@@ -3212,6 +3726,7 @@ window.__ModuleLoader__.load({
|
|
|
3212
3726
|
creditsHeading: "Remaining credit",
|
|
3213
3727
|
tabStatus: "Status",
|
|
3214
3728
|
tabContext: "Context window",
|
|
3729
|
+
tabModels: "Model visibility",
|
|
3215
3730
|
tabDetails: "Credit details",
|
|
3216
3731
|
tabCheckIn: "Check-in log",
|
|
3217
3732
|
checkInLogTime: "Check-in time",
|
|
@@ -3224,6 +3739,7 @@ window.__ModuleLoader__.load({
|
|
|
3224
3739
|
checkInRefreshing: "Refreshing…",
|
|
3225
3740
|
checkInClear: "Clear logs",
|
|
3226
3741
|
checkInClearing: "Clearing…",
|
|
3742
|
+
checkInNextRun: "Next automatic check-in: {time}",
|
|
3227
3743
|
creditsDetailHeading: "By package",
|
|
3228
3744
|
creditsUsed: "Used this cycle: {percent}%",
|
|
3229
3745
|
creditsTotalUnlimited: "Total: Unlimited",
|
|
@@ -3247,6 +3763,19 @@ window.__ModuleLoader__.load({
|
|
|
3247
3763
|
statusRefreshFailed: "Refresh failed: {message} — showing the last known state",
|
|
3248
3764
|
statusResponseInvalid: "Qoder returned an unreadable status reply",
|
|
3249
3765
|
accountHeading: "Account",
|
|
3766
|
+
modelsHeading: "Model visibility",
|
|
3767
|
+
modelsSubtitle: "Choose which models are exposed to the DeepSeek Harness model selector.",
|
|
3768
|
+
modelsSearchPlaceholder: "Search by model name or ID…",
|
|
3769
|
+
modelsSelectAll: "Select all",
|
|
3770
|
+
modelsDeselectAll: "Deselect all",
|
|
3771
|
+
modelsEnableSelected: "Enable selected",
|
|
3772
|
+
modelsDisableSelected: "Disable selected",
|
|
3773
|
+
modelsEnabled: "Enabled",
|
|
3774
|
+
modelsDisabled: "Disabled",
|
|
3775
|
+
modelsBatchActions: "Batch actions",
|
|
3776
|
+
modelsSelectedCount: "{count} selected",
|
|
3777
|
+
modelsNoModels: "No models available.",
|
|
3778
|
+
modelsNoMatch: "No models matching \"{query}\".",
|
|
3250
3779
|
contextHeading: "Context window",
|
|
3251
3780
|
contextUpTo: "up to {size}",
|
|
3252
3781
|
contextDefault: "default {size}",
|
|
@@ -3288,8 +3817,11 @@ window.__ModuleLoader__.load({
|
|
|
3288
3817
|
quotaToggleHint: "Show this account’s remaining credit in the sidebar footer.",
|
|
3289
3818
|
autoCheckInCN: "Qoder (China) daily auto check-in (100 Credits)",
|
|
3290
3819
|
autoCheckInGlobal: "Qoder Global (International) daily auto check-in",
|
|
3291
|
-
autoCheckInHintCN: "Automatically check in daily
|
|
3292
|
-
autoCheckInHintGlobal: "Automatically check in daily
|
|
3820
|
+
autoCheckInHintCN: "Automatically check in daily to claim 100 Credits for the China account.",
|
|
3821
|
+
autoCheckInHintGlobal: "Automatically check in daily to claim available perks for the Global account.",
|
|
3822
|
+
checkInTimeCN: "Check-in time (China)",
|
|
3823
|
+
checkInTimeGlobal: "Check-in time (Global)",
|
|
3824
|
+
checkInTimeHint: "The moment the automatic check-in runs each day, in UTC+8.",
|
|
3293
3825
|
autoCheckInStatusClaimed: "Checked in today (+{amount} Credits)",
|
|
3294
3826
|
autoCheckInStatusAlready: "Already checked in today",
|
|
3295
3827
|
autoCheckInStatusNoCampaign: "No active benefit campaign today",
|
|
@@ -3365,6 +3897,7 @@ window.__ModuleLoader__.load({
|
|
|
3365
3897
|
creditsHeading: "剩余额度",
|
|
3366
3898
|
tabStatus: "状态",
|
|
3367
3899
|
tabContext: "上下文窗口",
|
|
3900
|
+
tabModels: "模型开关",
|
|
3368
3901
|
tabDetails: "额度明细",
|
|
3369
3902
|
tabCheckIn: "签到日志",
|
|
3370
3903
|
checkInLogTime: "签到时间",
|
|
@@ -3377,6 +3910,7 @@ window.__ModuleLoader__.load({
|
|
|
3377
3910
|
checkInRefreshing: "正在刷新…",
|
|
3378
3911
|
checkInClear: "清空日志",
|
|
3379
3912
|
checkInClearing: "正在清空…",
|
|
3913
|
+
checkInNextRun: "下次自动签到:{time}",
|
|
3380
3914
|
creditsDetailHeading: "按套餐",
|
|
3381
3915
|
creditsUsed: "本轮已用 {percent}%",
|
|
3382
3916
|
creditsTotalUnlimited: "合计:不限额",
|
|
@@ -3400,6 +3934,19 @@ window.__ModuleLoader__.load({
|
|
|
3400
3934
|
statusRefreshFailed: "刷新失败:{message} — 当前显示的是上次成功获取的状态",
|
|
3401
3935
|
statusResponseInvalid: "Qoder 返回的状态数据无法识别",
|
|
3402
3936
|
accountHeading: "账号",
|
|
3937
|
+
modelsHeading: "模型开关",
|
|
3938
|
+
modelsSubtitle: "控制哪些模型在 DeepSeek Harness 模型选择器中可见并可供调用。",
|
|
3939
|
+
modelsSearchPlaceholder: "按模型名称或 ID 搜索…",
|
|
3940
|
+
modelsSelectAll: "全选",
|
|
3941
|
+
modelsDeselectAll: "取消全选",
|
|
3942
|
+
modelsEnableSelected: "批量开启",
|
|
3943
|
+
modelsDisableSelected: "批量关闭",
|
|
3944
|
+
modelsEnabled: "已开启",
|
|
3945
|
+
modelsDisabled: "已关闭",
|
|
3946
|
+
modelsBatchActions: "批量操作",
|
|
3947
|
+
modelsSelectedCount: "已选 {count} 项",
|
|
3948
|
+
modelsNoModels: "暂无可配置的模型。",
|
|
3949
|
+
modelsNoMatch: "未找到匹配“{query}”的模型。",
|
|
3403
3950
|
contextHeading: "上下文窗口",
|
|
3404
3951
|
contextUpTo: "最高 {size}",
|
|
3405
3952
|
contextDefault: "默认 {size}",
|
|
@@ -3441,8 +3988,11 @@ window.__ModuleLoader__.load({
|
|
|
3441
3988
|
quotaToggleHint: "在侧栏底部展示该账号的剩余额度。",
|
|
3442
3989
|
autoCheckInCN: "Qoder(国内版)每日自动签到",
|
|
3443
3990
|
autoCheckInGlobal: "Qoder Global(国际版)每日自动签到",
|
|
3444
|
-
autoCheckInHintCN: "
|
|
3445
|
-
autoCheckInHintGlobal: "
|
|
3991
|
+
autoCheckInHintCN: "每天自动签到,为国内版账号领取 100 Credits 算力额度。",
|
|
3992
|
+
autoCheckInHintGlobal: "每天自动签到,为国际版账号领取可用福利额度。",
|
|
3993
|
+
checkInTimeCN: "签到时间(国内版)",
|
|
3994
|
+
checkInTimeGlobal: "签到时间(国际版)",
|
|
3995
|
+
checkInTimeHint: "每天执行自动签到的时刻,按北京时间(UTC+8)。",
|
|
3446
3996
|
autoCheckInStatusClaimed: "今日已自动签到(+{amount} Credits)",
|
|
3447
3997
|
autoCheckInStatusAlready: "今日已完成签到",
|
|
3448
3998
|
autoCheckInStatusNoCampaign: "今日无可用签到福利活动",
|
|
@@ -3487,23 +4037,29 @@ window.__ModuleLoader__.load({
|
|
|
3487
4037
|
/** Stable browser-plugin name. */
|
|
3488
4038
|
const name = "dsh-qoder-connect-client";
|
|
3489
4039
|
/**
|
|
3490
|
-
* Client services
|
|
4040
|
+
* Client services this bundle requires BEFORE it activates.
|
|
3491
4041
|
*
|
|
3492
4042
|
* DSH 0.1.2 removed `@deepseek-ai/dsh-client-runtime` (the package that used to
|
|
3493
4043
|
* hold the browser `ClientContext` alias and the `slots` service). The services
|
|
3494
4044
|
* this card relies on now come from narrower packages: the `slots` registry
|
|
3495
4045
|
* moved to `@deepseek-ai/dsh-client-ui-renderer`, `locale` stayed in
|
|
3496
|
-
* `@deepseek-ai/dsh-client-locale`, and the
|
|
3497
|
-
*
|
|
3498
|
-
*
|
|
3499
|
-
*
|
|
4046
|
+
* `@deepseek-ai/dsh-client-locale`, and the Plugins tab's card list
|
|
4047
|
+
* (`settings.plugin.item`) is declared by
|
|
4048
|
+
* `@deepseek-ai/dsh-client-ui-settings-plugins`. Those packages are named in
|
|
4049
|
+
* the package's `dsh.client.inject` list.
|
|
4050
|
+
*
|
|
4051
|
+
* NOT declared here: the settings services. `settingsScope` (0.1.5) does not
|
|
4052
|
+
* exist at all on 0.1.7 — a static service dependency on it is what left this
|
|
4053
|
+
* plugin's client activation pending forever — and `configForms` (0.1.7) does
|
|
4054
|
+
* not exist on 0.1.5. Both are waited for with `ctx.inject([...], callback)`
|
|
4055
|
+
* below, where a missing service simply never calls back instead of holding
|
|
4056
|
+
* activation.
|
|
3500
4057
|
*/
|
|
3501
4058
|
const inject = [
|
|
3502
4059
|
"slots",
|
|
3503
4060
|
"locale",
|
|
3504
4061
|
"remote",
|
|
3505
|
-
"remote.session"
|
|
3506
|
-
"settingsScope"
|
|
4062
|
+
"remote.session"
|
|
3507
4063
|
];
|
|
3508
4064
|
/** The status routes per variant id (mirrors the host's locked route table). */
|
|
3509
4065
|
const VARIANT_STATUS = {
|
|
@@ -3511,6 +4067,47 @@ window.__ModuleLoader__.load({
|
|
|
3511
4067
|
"qoder-global": QODER_GLOBAL_STATUS_PATH
|
|
3512
4068
|
};
|
|
3513
4069
|
/**
|
|
4070
|
+
* The shared 《插件设置》 block's slot and entry ids.
|
|
4071
|
+
*
|
|
4072
|
+
* The three connect plugins must agree on these exactly: `settings.section` is
|
|
4073
|
+
* a `list` slot, so many blocks may coexist, but a child slot name may be
|
|
4074
|
+
* declared once and a list entry id may be registered once. The first plugin to
|
|
4075
|
+
* arrive registers the container (declaring the child slot); the others detect
|
|
4076
|
+
* it and attach their own entry, which is why the ids are fixed constants
|
|
4077
|
+
* rather than per-plugin names.
|
|
4078
|
+
*/
|
|
4079
|
+
const SHARED_SECTION_SLOT = "settings.section";
|
|
4080
|
+
const SHARED_SECTION_ID = "plugin-settings";
|
|
4081
|
+
const SHARED_SECTION_ORDER = 900;
|
|
4082
|
+
const SHARED_ITEM_SLOT = "plugin-settings.item";
|
|
4083
|
+
/** This plugin's entry id inside the shared block: its package name (unique). */
|
|
4084
|
+
const SHARED_ITEM_ID = "dsh-qoder-connect";
|
|
4085
|
+
/** The shared block's title, fixed by the contract so all three plugins agree. */
|
|
4086
|
+
const SHARED_SECTION_LABEL = "插件设置";
|
|
4087
|
+
/**
|
|
4088
|
+
* The shared 《插件设置》 section the 0.1.7 card list lives in.
|
|
4089
|
+
*
|
|
4090
|
+
* It renders nothing but its child slot: every connect plugin contributes its
|
|
4091
|
+
* own card as one `plugin-settings.item` entry. The list wrapper mirrors the
|
|
4092
|
+
* list the removed Plugins tab used for the same cards (`gap: 10`, no markers),
|
|
4093
|
+
* so the cards keep the spacing they had on 0.1.5.
|
|
4094
|
+
*
|
|
4095
|
+
* @param props - the section's slot props (owner share plus `renderSlot`).
|
|
4096
|
+
* @returns the ordered card list.
|
|
4097
|
+
*/
|
|
4098
|
+
function PluginSettingsSection(props) {
|
|
4099
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("ul", {
|
|
4100
|
+
style: {
|
|
4101
|
+
display: "flex",
|
|
4102
|
+
flexDirection: "column",
|
|
4103
|
+
gap: 6,
|
|
4104
|
+
padding: 0,
|
|
4105
|
+
listStyle: "none"
|
|
4106
|
+
},
|
|
4107
|
+
children: props.renderSlot(SHARED_ITEM_SLOT, {})
|
|
4108
|
+
});
|
|
4109
|
+
}
|
|
4110
|
+
/**
|
|
3514
4111
|
* Register card copy, the shared quota-settings card, the two variant cards,
|
|
3515
4112
|
* and the sidebar quota cards.
|
|
3516
4113
|
*
|
|
@@ -3542,9 +4139,22 @@ window.__ModuleLoader__.load({
|
|
|
3542
4139
|
en
|
|
3543
4140
|
}), "dsh-qoder-connect: settings copy");
|
|
3544
4141
|
const t = ctx.locale.bind(namespace);
|
|
4142
|
+
/**
|
|
4143
|
+
* The bound quota settings face, filled in as soon as a host configuration
|
|
4144
|
+
* service becomes available (see the two `ctx.inject` callbacks below).
|
|
4145
|
+
*
|
|
4146
|
+
* It is bound as a SERVICE ARRIVES rather than when the settings card's
|
|
4147
|
+
* inject factory first runs: the factory only executes while the settings
|
|
4148
|
+
* page renders, so a fresh page load read no toggles and rendered no sidebar
|
|
4149
|
+
* card until the user opened settings — the exact regression the
|
|
4150
|
+
* commandcode card avoids by reading its STORED fact independently of the
|
|
4151
|
+
* settings page. The face's subscription mirrors every accepted snapshot
|
|
4152
|
+
* (toggles + interval) into the shared store the sidebar cards and the
|
|
4153
|
+
* dashboard read; a deployment with no configuration service never binds,
|
|
4154
|
+
* and the sidebar cards stay hidden.
|
|
4155
|
+
*/
|
|
3545
4156
|
let quotaScope;
|
|
3546
|
-
|
|
3547
|
-
const scope = ctx.settingsScope.bind({ namespace: "qoder-quota" });
|
|
4157
|
+
const adoptQuotaScope = (scope) => {
|
|
3548
4158
|
quotaScope = scope;
|
|
3549
4159
|
const applySnapshot = () => {
|
|
3550
4160
|
const value = scope.getSnapshot().value;
|
|
@@ -3553,20 +4163,76 @@ window.__ModuleLoader__.load({
|
|
|
3553
4163
|
};
|
|
3554
4164
|
applySnapshot();
|
|
3555
4165
|
scope.subscribe(applySnapshot);
|
|
3556
|
-
}
|
|
3557
|
-
|
|
3558
|
-
|
|
3559
|
-
ctx.slots.inject("settings.plugin.item", () => ctx.slots.register({
|
|
3560
|
-
name: "settings.plugin.item",
|
|
3561
|
-
key: "qoder",
|
|
3562
|
-
priority: 50,
|
|
3563
|
-
inject: () => ({
|
|
4166
|
+
};
|
|
4167
|
+
const registerUnifiedCard = (slot) => {
|
|
4168
|
+
const inject = () => ({
|
|
3564
4169
|
t,
|
|
3565
4170
|
scope: quotaScope,
|
|
3566
4171
|
signedIn: () => quotaSignInState(),
|
|
3567
4172
|
unified: true
|
|
3568
|
-
})
|
|
3569
|
-
|
|
4173
|
+
});
|
|
4174
|
+
if (slot === "settings.plugin.item") return ctx.slots.inject(slot, () => ctx.slots.register({
|
|
4175
|
+
name: slot,
|
|
4176
|
+
key: "qoder",
|
|
4177
|
+
priority: 50,
|
|
4178
|
+
inject
|
|
4179
|
+
}, QoderPluginCard));
|
|
4180
|
+
return ctx.slots.inject(slot, () => ctx.slots.register({
|
|
4181
|
+
name: slot,
|
|
4182
|
+
id: SHARED_ITEM_ID,
|
|
4183
|
+
order: 30,
|
|
4184
|
+
inject
|
|
4185
|
+
}, QoderPluginCard));
|
|
4186
|
+
};
|
|
4187
|
+
/**
|
|
4188
|
+
* Attach this plugin's card to the shared 《插件设置》 block, building the
|
|
4189
|
+
* block first if nobody has.
|
|
4190
|
+
*
|
|
4191
|
+
* Backoff protocol (identical in all three connect plugins, on purpose: a
|
|
4192
|
+
* `list` slot takes many entries but only ONE registration per id, and a
|
|
4193
|
+
* child slot name may be declared once, so the container is a rendezvous
|
|
4194
|
+
* rather than a race to be won):
|
|
4195
|
+
* 1. wait for the host to declare `settings.section`;
|
|
4196
|
+
* 2. if an entry with the shared id is already there, only attach;
|
|
4197
|
+
* 3. otherwise register the container — and if a sibling won the race and
|
|
4198
|
+
* the registration throws, fall back to attaching to theirs.
|
|
4199
|
+
*/
|
|
4200
|
+
const joinSharedSettingsBlock = () => {
|
|
4201
|
+
ctx.slots.inject(SHARED_SECTION_SLOT, () => {
|
|
4202
|
+
let disposeContainer;
|
|
4203
|
+
if (!ctx.slots.entries(SHARED_SECTION_SLOT).some((entry) => entry.options?.id === SHARED_SECTION_ID)) try {
|
|
4204
|
+
disposeContainer = ctx.slots.register({
|
|
4205
|
+
name: SHARED_SECTION_SLOT,
|
|
4206
|
+
id: SHARED_SECTION_ID,
|
|
4207
|
+
order: SHARED_SECTION_ORDER,
|
|
4208
|
+
label: () => SHARED_SECTION_LABEL,
|
|
4209
|
+
children: { [SHARED_ITEM_SLOT]: {
|
|
4210
|
+
kind: "list",
|
|
4211
|
+
scope: "root"
|
|
4212
|
+
} }
|
|
4213
|
+
}, PluginSettingsSection);
|
|
4214
|
+
} catch (error) {
|
|
4215
|
+
console.error("[dsh-qoder-connect] shared plugin-settings block lost the race; attaching to the winner:", error);
|
|
4216
|
+
}
|
|
4217
|
+
const disposeItem = registerUnifiedCard(SHARED_ITEM_SLOT);
|
|
4218
|
+
return () => {
|
|
4219
|
+
disposeItem();
|
|
4220
|
+
disposeContainer?.();
|
|
4221
|
+
};
|
|
4222
|
+
});
|
|
4223
|
+
};
|
|
4224
|
+
/**
|
|
4225
|
+
* 插件自有配置(`<profile>/.dsh-qoder-connect/settings.json`):两条宿主线的
|
|
4226
|
+
* 读写都走宿主半的 settings face,不再经过 settingsScope / configForms。
|
|
4227
|
+
*
|
|
4228
|
+
* 0.1.7 的 configForms 写入会整树 reconcile + fiber 热重载(每次约
|
|
4229
|
+
* 1~1.5 秒,且每次保存都刷新所有客户端镜像);自有文件写入是本地毫秒级
|
|
4230
|
+
* 原子写。卡片注册无条件进行:scope 在启动时载入,迟到也不会漏掉 UI。
|
|
4231
|
+
*/
|
|
4232
|
+
const ownQuotaScope = new OwnQuotaSettingsScope();
|
|
4233
|
+
ownQuotaScope.load();
|
|
4234
|
+
adoptQuotaScope(ownQuotaScope);
|
|
4235
|
+
joinSharedSettingsBlock();
|
|
3570
4236
|
const QUOTA_PANEL_ID = "qoder-quota-panel";
|
|
3571
4237
|
const CONVERSATION_PANEL_ID = "conversation";
|
|
3572
4238
|
const dashboardDocuments = {
|