dsh-qoder-connect 0.1.4 → 0.1.8

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/lib/client.js CHANGED
@@ -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 {
@@ -238,6 +249,10 @@ window.__ModuleLoader__.load({
238
249
  values: {
239
250
  sidebarQuotaCN: false,
240
251
  sidebarQuotaGlobal: false,
252
+ autoCheckInCN: false,
253
+ autoCheckInGlobal: false,
254
+ checkInMinuteCN: CHECK_IN_MINUTE_DEFAULT,
255
+ checkInMinuteGlobal: CHECK_IN_MINUTE_DEFAULT,
241
256
  quotaPollMs: POLL_DEFAULT_MS
242
257
  }
243
258
  };
@@ -249,6 +264,10 @@ window.__ModuleLoader__.load({
249
264
  values: {
250
265
  sidebarQuotaCN: value.sidebarQuotaCN === true,
251
266
  sidebarQuotaGlobal: value.sidebarQuotaGlobal === true,
267
+ autoCheckInCN: value.autoCheckInCN === true,
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,
252
271
  quotaPollMs: typeof value.quotaPollMs === "number" ? value.quotaPollMs : POLL_DEFAULT_MS
253
272
  }
254
273
  };
@@ -272,13 +291,17 @@ window.__ModuleLoader__.load({
272
291
  values: {
273
292
  sidebarQuotaCN: false,
274
293
  sidebarQuotaGlobal: false,
294
+ autoCheckInCN: false,
295
+ autoCheckInGlobal: false,
296
+ checkInMinuteCN: CHECK_IN_MINUTE_DEFAULT,
297
+ checkInMinuteGlobal: CHECK_IN_MINUTE_DEFAULT,
275
298
  quotaPollMs: POLL_DEFAULT_MS
276
299
  }
277
300
  };
278
301
  function stableProject(scope) {
279
302
  if (scope === void 0) return UNAVAILABLE;
280
303
  const next = project(scope);
281
- 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.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) {
282
305
  cachedScope = scope;
283
306
  cachedProjection = next;
284
307
  }
@@ -318,6 +341,106 @@ window.__ModuleLoader__.load({
318
341
  })]
319
342
  });
320
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
+ }
321
444
  /** The inner controls for sidebar quota settings. */
322
445
  function QuotaSettingsContent({ t = (key) => key, scope, signedIn }) {
323
446
  const subscribe = (0, react.useCallback)((onStoreChange) => {
@@ -369,6 +492,8 @@ window.__ModuleLoader__.load({
369
492
  const write = (field, value) => {
370
493
  if (field === "sidebarQuotaCN" && value === true && !signed.cn) return;
371
494
  if (field === "sidebarQuotaGlobal" && value === true && !signed.global) return;
495
+ if (field === "autoCheckInCN" && value === true && !signed.cn) return;
496
+ if (field === "autoCheckInGlobal" && value === true && !signed.global) return;
372
497
  scope?.set(field, value);
373
498
  };
374
499
  const minutes = Math.max(POLL_MIN_MS / 6e4, Math.round(projection.values.quotaPollMs / 6e4));
@@ -395,6 +520,36 @@ window.__ModuleLoader__.load({
395
520
  disabledHint: t("quotaSignInRequired"),
396
521
  onToggle: (next) => write("sidebarQuotaGlobal", next)
397
522
  }),
523
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ToggleRow, {
524
+ label: t("autoCheckInCN"),
525
+ hint: t("autoCheckInHintCN"),
526
+ checked: projection.values.autoCheckInCN,
527
+ disabled: !signed.cn,
528
+ disabledHint: t("quotaSignInRequired"),
529
+ onToggle: (next) => write("autoCheckInCN", next)
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
+ }),
538
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ToggleRow, {
539
+ label: t("autoCheckInGlobal"),
540
+ hint: t("autoCheckInHintGlobal"),
541
+ checked: projection.values.autoCheckInGlobal,
542
+ disabled: !signed.global,
543
+ disabledHint: t("quotaSignInRequired"),
544
+ onToggle: (next) => write("autoCheckInGlobal", next)
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
+ }),
398
553
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
399
554
  style: {
400
555
  ...rowStyle$1,
@@ -494,6 +649,20 @@ window.__ModuleLoader__.load({
494
649
  gap: 6,
495
650
  flex: "none"
496
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
+ };
497
666
  const inputStyle = {
498
667
  boxSizing: "border-box",
499
668
  width: 55,
@@ -1040,6 +1209,379 @@ window.__ModuleLoader__.load({
1040
1209
  if (tokens >= 1e3 && tokens % 1e3 === 0) return `${tokens / 1e3}K`;
1041
1210
  return String(tokens);
1042
1211
  }
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 }) {
1452
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1453
+ style: quotaListStyle,
1454
+ children: [
1455
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1456
+ style: {
1457
+ display: "flex",
1458
+ alignItems: "center",
1459
+ justifyContent: "space-between",
1460
+ flexWrap: "wrap",
1461
+ gap: 8
1462
+ },
1463
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", {
1464
+ style: quotaTitleStyle,
1465
+ children: t("tabCheckIn")
1466
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1467
+ style: {
1468
+ display: "flex",
1469
+ alignItems: "center",
1470
+ gap: 6
1471
+ },
1472
+ children: [
1473
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
1474
+ type: "button",
1475
+ style: buttonStyle$1,
1476
+ disabled: disabled || busy || checkingIn,
1477
+ onClick: onCheckIn,
1478
+ children: checkingIn ? t("checkInChecking") : t("checkInNow")
1479
+ }),
1480
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
1481
+ type: "button",
1482
+ style: buttonStyle$1,
1483
+ disabled: busy || checkingIn,
1484
+ onClick: onRefresh,
1485
+ children: busy ? t("checkInRefreshing") : t("checkInRefresh")
1486
+ }),
1487
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
1488
+ type: "button",
1489
+ style: buttonStyle$1,
1490
+ disabled: busy || clearing || !logs || logs.length === 0,
1491
+ onClick: onClear,
1492
+ children: clearing ? t("checkInClearing") : t("checkInClear")
1493
+ })
1494
+ ]
1495
+ })]
1496
+ }),
1497
+ notice === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1498
+ style: bodyStyle,
1499
+ children: notice
1500
+ }),
1501
+ nextRun === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1502
+ style: descriptionStyle,
1503
+ children: t("checkInNextRun", { time: formatTime(nextRun) })
1504
+ }),
1505
+ !logs || logs.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1506
+ style: descriptionStyle,
1507
+ children: t("checkInLogEmpty")
1508
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1509
+ style: {
1510
+ display: "flex",
1511
+ flexDirection: "column",
1512
+ gap: 6
1513
+ },
1514
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1515
+ style: {
1516
+ display: "flex",
1517
+ borderBottom: "1px solid var(--dsw-alias-border-l2, rgba(127,127,127,0.15))",
1518
+ paddingBottom: 6,
1519
+ fontSize: 12,
1520
+ color: "var(--dsw-alias-label-tertiary)"
1521
+ },
1522
+ children: [
1523
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1524
+ style: { flex: 2 },
1525
+ children: t("checkInLogTime")
1526
+ }),
1527
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1528
+ style: { flex: 3 },
1529
+ children: t("checkInLogResult")
1530
+ }),
1531
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1532
+ style: {
1533
+ flex: 1,
1534
+ textAlign: "right"
1535
+ },
1536
+ children: t("checkInLogAmount")
1537
+ })
1538
+ ]
1539
+ }), logs.map((log) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1540
+ style: {
1541
+ display: "flex",
1542
+ alignItems: "center",
1543
+ padding: "6px 0",
1544
+ fontSize: 13,
1545
+ borderBottom: "1px solid var(--dsw-alias-border-l2, rgba(127,127,127,0.08))"
1546
+ },
1547
+ children: [
1548
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1549
+ style: {
1550
+ flex: 2,
1551
+ color: "var(--dsw-alias-label-secondary)"
1552
+ },
1553
+ children: formatTime(log.timestamp)
1554
+ }),
1555
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
1556
+ style: {
1557
+ flex: 3,
1558
+ display: "flex",
1559
+ alignItems: "center",
1560
+ gap: 6
1561
+ },
1562
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { style: {
1563
+ width: 6,
1564
+ height: 6,
1565
+ borderRadius: "50%",
1566
+ flexShrink: 0,
1567
+ background: log.status === "claimed" ? "var(--dsw-alias-status-success, #52c41a)" : log.status === "already-claimed" ? "var(--dsw-alias-status-info, #1890ff)" : log.status === "no-campaign" ? "var(--dsw-alias-label-tertiary, #999)" : "var(--dsw-alias-status-error, #f5222d)"
1568
+ } }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: log.status === "claimed" ? t("autoCheckInStatusClaimed", { amount: log.amount ?? 100 }) : log.status === "already-claimed" ? t("autoCheckInStatusAlready") : log.status === "no-campaign" ? t("autoCheckInStatusNoCampaign") : t("autoCheckInStatusError", { message: log.message ?? "" }) })]
1569
+ }),
1570
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1571
+ style: {
1572
+ flex: 1,
1573
+ textAlign: "right",
1574
+ fontWeight: 600,
1575
+ color: log.amount ? "var(--dsw-alias-brand-primary)" : "inherit"
1576
+ },
1577
+ children: log.amount ? `+${log.amount}` : "-"
1578
+ })
1579
+ ]
1580
+ }, log.id))]
1581
+ })
1582
+ ]
1583
+ });
1584
+ }
1043
1585
  /** Render Qoder PAT state, quota, catalog, and context capacities as one expandable card. */
1044
1586
  function QoderPluginCard(props) {
1045
1587
  const { t, scope, signedIn, variant, unified } = props;
@@ -1062,6 +1604,9 @@ window.__ModuleLoader__.load({
1062
1604
  const [patNotice, setPatNotice] = (0, react.useState)();
1063
1605
  const patInput = (0, react.useRef)(null);
1064
1606
  const [tab, setTab] = (0, react.useState)("status");
1607
+ const [checkingIn, setCheckingIn] = (0, react.useState)(false);
1608
+ const [clearingLogs, setClearingLogs] = (0, react.useState)(false);
1609
+ const [checkInNotice, setCheckInNotice] = (0, react.useState)();
1065
1610
  const mounted = (0, react.useRef)(true);
1066
1611
  const readSeq = (0, react.useRef)(0);
1067
1612
  const manualControllers = (0, react.useRef)(/* @__PURE__ */ new Set());
@@ -1202,6 +1747,83 @@ window.__ModuleLoader__.load({
1202
1747
  t,
1203
1748
  trackController
1204
1749
  ]);
1750
+ const manualCheckIn = (0, react.useCallback)(async () => {
1751
+ const key = status?.status === "signed-in" ? status.probeKey : void 0;
1752
+ if (key === void 0) return;
1753
+ setCheckingIn(true);
1754
+ setCheckInNotice(void 0);
1755
+ const controller = trackController();
1756
+ try {
1757
+ const response = await fetch(currentVariant.probePath, {
1758
+ method: "POST",
1759
+ headers: {
1760
+ "Content-Type": "application/json",
1761
+ "X-Qoder-Probe-Key": key
1762
+ },
1763
+ credentials: "same-origin",
1764
+ signal: controller.signal,
1765
+ body: JSON.stringify({ action: "checkin" })
1766
+ });
1767
+ if (!response.ok) throw new Error(`HTTP ${response.status}`);
1768
+ const result = await response.json();
1769
+ if (result.state === "claimed") setCheckInNotice(t("autoCheckInStatusClaimed", { amount: result.amount ?? 100 }));
1770
+ else if (result.state === "already-claimed") setCheckInNotice(t("autoCheckInStatusAlready"));
1771
+ else if (result.state === "no-campaign") setCheckInNotice(t("autoCheckInStatusNoCampaign"));
1772
+ else if (result.reason) setCheckInNotice(t("autoCheckInStatusError", { message: result.reason }));
1773
+ } catch (error) {
1774
+ if (mounted.current && controller.signal.aborted !== true) setCheckInNotice(error instanceof Error ? error.message : t("requestFailed"));
1775
+ } finally {
1776
+ manualControllers.current.delete(controller);
1777
+ if (mounted.current) setCheckingIn(false);
1778
+ }
1779
+ try {
1780
+ await refresh(controller.signal);
1781
+ } finally {
1782
+ manualControllers.current.delete(controller);
1783
+ }
1784
+ }, [
1785
+ currentVariant.probePath,
1786
+ refresh,
1787
+ status,
1788
+ t,
1789
+ trackController
1790
+ ]);
1791
+ const clearCheckInLogs = (0, react.useCallback)(async () => {
1792
+ const key = status?.status === "signed-in" ? status.probeKey : void 0;
1793
+ if (key === void 0) return;
1794
+ setClearingLogs(true);
1795
+ setCheckInNotice(void 0);
1796
+ const controller = trackController();
1797
+ try {
1798
+ const response = await fetch(currentVariant.probePath, {
1799
+ method: "POST",
1800
+ headers: {
1801
+ "Content-Type": "application/json",
1802
+ "X-Qoder-Probe-Key": key
1803
+ },
1804
+ credentials: "same-origin",
1805
+ signal: controller.signal,
1806
+ body: JSON.stringify({ action: "clear-checkin-logs" })
1807
+ });
1808
+ if (!response.ok) throw new Error(`HTTP ${response.status}`);
1809
+ } catch (error) {
1810
+ if (mounted.current && controller.signal.aborted !== true) setCheckInNotice(error instanceof Error ? error.message : t("requestFailed"));
1811
+ } finally {
1812
+ manualControllers.current.delete(controller);
1813
+ if (mounted.current) setClearingLogs(false);
1814
+ }
1815
+ try {
1816
+ await refresh(controller.signal);
1817
+ } finally {
1818
+ manualControllers.current.delete(controller);
1819
+ }
1820
+ }, [
1821
+ currentVariant.probePath,
1822
+ refresh,
1823
+ status,
1824
+ t,
1825
+ trackController
1826
+ ]);
1205
1827
  /**
1206
1828
  * Run one probe-route control action and refresh the card's state afterwards.
1207
1829
  *
@@ -1230,7 +1852,7 @@ window.__ModuleLoader__.load({
1230
1852
  const message = typeof value === "object" && value !== null && "error" in value ? String(value["error"]) : `HTTP ${response.status}`;
1231
1853
  throw new Error(message);
1232
1854
  }
1233
- 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")) {
1234
1856
  const reason = typeof value === "object" && value !== null && "reason" in value ? String(value["reason"]) : t("requestFailed");
1235
1857
  throw new Error(reason);
1236
1858
  }
@@ -1622,7 +2244,9 @@ window.__ModuleLoader__.load({
1622
2244
  children: [
1623
2245
  "status",
1624
2246
  "context",
1625
- "details"
2247
+ "models",
2248
+ "details",
2249
+ "checkin"
1626
2250
  ].map((id) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
1627
2251
  type: "button",
1628
2252
  role: "tab",
@@ -1634,7 +2258,7 @@ window.__ModuleLoader__.load({
1634
2258
  ...tabStyle,
1635
2259
  ...tab === id ? tabActiveStyle : {}
1636
2260
  },
1637
- children: t(id === "status" ? "tabStatus" : id === "context" ? "tabContext" : "tabDetails")
2261
+ children: t(id === "status" ? "tabStatus" : id === "context" ? "tabContext" : id === "models" ? "tabModels" : id === "details" ? "tabDetails" : "tabCheckIn")
1638
2262
  }, id))
1639
2263
  }),
1640
2264
  tab === "status" ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
@@ -1672,7 +2296,22 @@ window.__ModuleLoader__.load({
1672
2296
  });
1673
2297
  }
1674
2298
  })
1675
- }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
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
+ })
2314
+ }) : tab === "details" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1676
2315
  style: tabPanelStyle,
1677
2316
  children: status.credits === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1678
2317
  style: quotaListStyle,
@@ -1688,6 +2327,27 @@ window.__ModuleLoader__.load({
1688
2327
  t
1689
2328
  }, `${account.packageName}-${String(index)}`))]
1690
2329
  })
2330
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2331
+ style: tabPanelStyle,
2332
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CheckInLogTable, {
2333
+ logs: status.checkIn?.logs,
2334
+ t,
2335
+ busy,
2336
+ checkingIn,
2337
+ clearing: clearingLogs,
2338
+ disabled: status.status !== "signed-in",
2339
+ ...checkInNotice === void 0 ? {} : { notice: checkInNotice },
2340
+ ...status.checkIn?.nextRunAt === void 0 ? {} : { nextRun: status.checkIn.nextRunAt },
2341
+ onCheckIn: () => {
2342
+ manualCheckIn();
2343
+ },
2344
+ onRefresh: () => {
2345
+ manualRefresh();
2346
+ },
2347
+ onClear: () => {
2348
+ clearCheckInLogs();
2349
+ }
2350
+ })
1691
2351
  })
1692
2352
  ] }) : null,
1693
2353
  status?.status === "signed-out" ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
@@ -2957,7 +3617,20 @@ window.__ModuleLoader__.load({
2957
3617
  creditsHeading: "Remaining credit",
2958
3618
  tabStatus: "Status",
2959
3619
  tabContext: "Context window",
3620
+ tabModels: "Model visibility",
2960
3621
  tabDetails: "Credit details",
3622
+ tabCheckIn: "Check-in log",
3623
+ checkInLogTime: "Check-in time",
3624
+ checkInLogResult: "Result",
3625
+ checkInLogAmount: "Credits",
3626
+ checkInLogEmpty: "No check-in logs recorded yet.",
3627
+ checkInNow: "Check in now",
3628
+ checkInChecking: "Checking in…",
3629
+ checkInRefresh: "Refresh",
3630
+ checkInRefreshing: "Refreshing…",
3631
+ checkInClear: "Clear logs",
3632
+ checkInClearing: "Clearing…",
3633
+ checkInNextRun: "Next automatic check-in: {time}",
2961
3634
  creditsDetailHeading: "By package",
2962
3635
  creditsUsed: "Used this cycle: {percent}%",
2963
3636
  creditsTotalUnlimited: "Total: Unlimited",
@@ -2981,6 +3654,19 @@ window.__ModuleLoader__.load({
2981
3654
  statusRefreshFailed: "Refresh failed: {message} — showing the last known state",
2982
3655
  statusResponseInvalid: "Qoder returned an unreadable status reply",
2983
3656
  accountHeading: "Account",
3657
+ modelsHeading: "Model visibility",
3658
+ modelsSubtitle: "Choose which models are exposed to the DeepSeek Harness model selector.",
3659
+ modelsSearchPlaceholder: "Search by model name or ID…",
3660
+ modelsSelectAll: "Select all",
3661
+ modelsDeselectAll: "Deselect all",
3662
+ modelsEnableSelected: "Enable selected",
3663
+ modelsDisableSelected: "Disable selected",
3664
+ modelsEnabled: "Enabled",
3665
+ modelsDisabled: "Disabled",
3666
+ modelsBatchActions: "Batch actions",
3667
+ modelsSelectedCount: "{count} selected",
3668
+ modelsNoModels: "No models available.",
3669
+ modelsNoMatch: "No models matching \"{query}\".",
2984
3670
  contextHeading: "Context window",
2985
3671
  contextUpTo: "up to {size}",
2986
3672
  contextDefault: "default {size}",
@@ -3020,6 +3706,17 @@ window.__ModuleLoader__.load({
3020
3706
  quotaToggleCN: "Show China credit card",
3021
3707
  quotaToggleGlobal: "Show global credit card",
3022
3708
  quotaToggleHint: "Show this account’s remaining credit in the sidebar footer.",
3709
+ autoCheckInCN: "Qoder (China) daily auto check-in (100 Credits)",
3710
+ autoCheckInGlobal: "Qoder Global (International) daily auto check-in",
3711
+ autoCheckInHintCN: "Automatically check in daily to claim 100 Credits for the China account.",
3712
+ autoCheckInHintGlobal: "Automatically check in daily to claim available perks for the Global account.",
3713
+ checkInTimeCN: "Check-in time (China)",
3714
+ checkInTimeGlobal: "Check-in time (Global)",
3715
+ checkInTimeHint: "The moment the automatic check-in runs each day, in UTC+8.",
3716
+ autoCheckInStatusClaimed: "Checked in today (+{amount} Credits)",
3717
+ autoCheckInStatusAlready: "Already checked in today",
3718
+ autoCheckInStatusNoCampaign: "No active benefit campaign today",
3719
+ autoCheckInStatusError: "Auto check-in error: {message}",
3023
3720
  quotaSignInRequired: "Save a PAT for this variant first to enable this card.",
3024
3721
  quotaPollLabel: "Refresh interval",
3025
3722
  quotaPollHint: "Applies to both quota cards. Longer is kinder to the billing endpoint.",
@@ -3091,7 +3788,20 @@ window.__ModuleLoader__.load({
3091
3788
  creditsHeading: "剩余额度",
3092
3789
  tabStatus: "状态",
3093
3790
  tabContext: "上下文窗口",
3791
+ tabModels: "模型开关",
3094
3792
  tabDetails: "额度明细",
3793
+ tabCheckIn: "签到日志",
3794
+ checkInLogTime: "签到时间",
3795
+ checkInLogResult: "签到结果",
3796
+ checkInLogAmount: "获得额度",
3797
+ checkInLogEmpty: "暂无签到日志记录。",
3798
+ checkInNow: "立即签到",
3799
+ checkInChecking: "正在签到…",
3800
+ checkInRefresh: "刷新",
3801
+ checkInRefreshing: "正在刷新…",
3802
+ checkInClear: "清空日志",
3803
+ checkInClearing: "正在清空…",
3804
+ checkInNextRun: "下次自动签到:{time}",
3095
3805
  creditsDetailHeading: "按套餐",
3096
3806
  creditsUsed: "本轮已用 {percent}%",
3097
3807
  creditsTotalUnlimited: "合计:不限额",
@@ -3115,6 +3825,19 @@ window.__ModuleLoader__.load({
3115
3825
  statusRefreshFailed: "刷新失败:{message} — 当前显示的是上次成功获取的状态",
3116
3826
  statusResponseInvalid: "Qoder 返回的状态数据无法识别",
3117
3827
  accountHeading: "账号",
3828
+ modelsHeading: "模型开关",
3829
+ modelsSubtitle: "控制哪些模型在 DeepSeek Harness 模型选择器中可见并可供调用。",
3830
+ modelsSearchPlaceholder: "按模型名称或 ID 搜索…",
3831
+ modelsSelectAll: "全选",
3832
+ modelsDeselectAll: "取消全选",
3833
+ modelsEnableSelected: "批量开启",
3834
+ modelsDisableSelected: "批量关闭",
3835
+ modelsEnabled: "已开启",
3836
+ modelsDisabled: "已关闭",
3837
+ modelsBatchActions: "批量操作",
3838
+ modelsSelectedCount: "已选 {count} 项",
3839
+ modelsNoModels: "暂无可配置的模型。",
3840
+ modelsNoMatch: "未找到匹配“{query}”的模型。",
3118
3841
  contextHeading: "上下文窗口",
3119
3842
  contextUpTo: "最高 {size}",
3120
3843
  contextDefault: "默认 {size}",
@@ -3154,6 +3877,17 @@ window.__ModuleLoader__.load({
3154
3877
  quotaToggleCN: "展示国内版额度",
3155
3878
  quotaToggleGlobal: "展示国际版额度",
3156
3879
  quotaToggleHint: "在侧栏底部展示该账号的剩余额度。",
3880
+ autoCheckInCN: "Qoder(国内版)每日自动签到",
3881
+ autoCheckInGlobal: "Qoder Global(国际版)每日自动签到",
3882
+ autoCheckInHintCN: "每天自动签到,为国内版账号领取 100 Credits 算力额度。",
3883
+ autoCheckInHintGlobal: "每天自动签到,为国际版账号领取可用福利额度。",
3884
+ checkInTimeCN: "签到时间(国内版)",
3885
+ checkInTimeGlobal: "签到时间(国际版)",
3886
+ checkInTimeHint: "每天执行自动签到的时刻,按北京时间(UTC+8)。",
3887
+ autoCheckInStatusClaimed: "今日已自动签到(+{amount} Credits)",
3888
+ autoCheckInStatusAlready: "今日已完成签到",
3889
+ autoCheckInStatusNoCampaign: "今日无可用签到福利活动",
3890
+ autoCheckInStatusError: "自动签到出错:{message}",
3157
3891
  quotaSignInRequired: "请先为该变体保存 PAT,再开启此开关。",
3158
3892
  quotaPollLabel: "刷新间隔",
3159
3893
  quotaPollHint: "对两张额度卡片同时生效。间隔越长对计费接口越友好。",