dsh-workbuddy-xdpool 1.0.0 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/client.js CHANGED
@@ -28,8 +28,6 @@ window.__ModuleLoader__.load({
28
28
  const POOL_RESET_COOLDOWN_PATH = "/plugins/dsh-workbuddy-xdpool/cooldowns/reset";
29
29
  /** Plugin-owned daily check-in action endpoint (claim today's reward). */
30
30
  const POOL_CHECKIN_PATH = "/plugins/dsh-workbuddy-xdpool/checkin";
31
- /** Switch one account in or out of the pool (card toggle). */
32
- const POOL_ACCOUNT_DISABLE_PATH = "/plugins/dsh-workbuddy-xdpool/accounts/disabled";
33
31
  //#endregion
34
32
  //#region src/client/icon.ts
35
33
  /**
@@ -130,18 +128,32 @@ window.__ModuleLoader__.load({
130
128
  /* Account list (each account = a labeled subpanel, same as dingminhua). */
131
129
  .dsm-workbuddy-xdpool-accounts{display:flex;flex-direction:column;gap:14px;border-top:1px solid var(--dsw-alias-border-l2,#36373b);padding-top:14px}
132
130
  .dsm-workbuddy-xdpool-accounts-head{display:flex;align-items:center;justify-content:space-between;gap:12px}
131
+ /* "In use now": answers which account is serving without scanning every row. */
132
+ /* A hairline + tint reads as status; a filled bar would read as a call to action. */
133
+ .dsm-workbuddy-xdpool-current{display:flex;align-items:center;gap:9px;flex-wrap:wrap;margin:10px 0 0;padding:9px 13px;border:1px solid color-mix(in oklab, var(--dsw-alias-state-success-primary,#22a06b) 32%, transparent);border-radius:12px;background:color-mix(in oklab, var(--dsw-alias-state-success-primary,#22a06b) 9%, transparent)}
134
+ .dsm-workbuddy-xdpool-current-dot{width:7px;height:7px;border-radius:50%;flex:none;background:var(--dsw-alias-state-success-primary,#22a06b);box-shadow:0 0 0 3px color-mix(in oklab, var(--dsw-alias-state-success-primary,#22a06b) 18%, transparent)}
135
+ .dsm-workbuddy-xdpool-current-label{color:var(--dsw-alias-state-success-primary,#22a06b);font-size:11.5px;line-height:18px;font-weight:600;letter-spacing:.02em;flex:none}
136
+ .dsm-workbuddy-xdpool-current-name{color:var(--dsw-alias-label-primary,#e6e6e6);font-size:13px;line-height:18px;font-weight:600;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
137
+ .dsm-workbuddy-xdpool-current-note{color:var(--dsw-alias-state-warning-primary,#d97706);font-size:11px;line-height:16px}
133
138
  .dsm-workbuddy-xdpool-accounts-title{margin:0;color:var(--dsw-alias-label-primary,#e6e6e6);font-size:14px;font-weight:600;line-height:20px}
134
139
  .dsm-workbuddy-xdpool-accounts-summary{margin:2px 0 0;color:var(--dsw-alias-label-tertiary,#999);font-size:12px;line-height:18px}
135
- .dsm-workbuddy-xdpool-account{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:12px 14px;border:1px solid var(--dsw-alias-border-l2,#3a3d45);border-radius:14px;background:var(--dsw-alias-bg-layer-2,#24262c)}
136
- .dsm-workbuddy-xdpool-account-copy{display:flex;flex-direction:column;gap:3px;min-width:0}
140
+ .dsm-workbuddy-xdpool-account{display:flex;flex-direction:column;gap:0;padding:0;border:1px solid color-mix(in oklab, var(--dsw-alias-border-l2,#3a3d45) 55%, transparent);border-radius:16px;background:var(--dsw-alias-bg-layer-2,#24262c);box-shadow:0 1px 3px rgba(0,0,0,.04);overflow:hidden}
141
+ /* Body row inside a card: identity on the left, credit panels on the right. */
142
+ /* flex-wrap lets the panels drop below on a narrow card instead of squeezing both. */
143
+ .dsm-workbuddy-xdpool-account-body{display:flex;align-items:stretch;gap:0;flex-wrap:wrap}
144
+ .dsm-workbuddy-xdpool-account-copy{display:flex;flex-direction:column;align-items:flex-start;gap:6px;flex:1 1 200px;min-width:0;padding:14px 16px;justify-content:center}
137
145
  .dsm-workbuddy-xdpool-account-label{color:var(--dsw-alias-label-primary,#e6e6e6);font-size:14px;font-weight:600;line-height:20px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
138
- .dsm-workbuddy-xdpool-account-head{display:flex;align-items:center;gap:8px;min-width:0}
146
+ .dsm-workbuddy-xdpool-account-head .dsm-workbuddy-xdpool-account-toggle{margin-left:auto}
147
+ .dsm-workbuddy-xdpool-account-head{display:flex;align-items:center;gap:10px;min-width:0;padding:12px 16px;border-bottom:1px solid color-mix(in oklab, var(--dsw-alias-border-l2,#3a3d45) 45%, transparent)}
139
148
  /* Account switched off on the card: still listed (so it can be turned back on) but visually muted. */
140
149
  .dsm-workbuddy-xdpool-account-off{opacity:.55}
141
150
  /* Small pill switch: "in rotation" vs "off". Native checkbox styled by the label. */
142
- .dsm-workbuddy-xdpool-account-toggle{display:inline-flex;align-items:center;gap:5px;flex:none;cursor:pointer;color:var(--dsw-alias-label-secondary,#c6c9d0);font-size:11px;line-height:16px;white-space:nowrap}
143
- .dsm-workbuddy-xdpool-account-toggle input{margin:0;accent-color:var(--dsw-alias-brand-primary,#5686fe)}
144
- .dsm-workbuddy-xdpool-account-toggle:hover{color:var(--dsw-alias-label-primary,#e6e6e6)}
151
+ .dsm-workbuddy-xdpool-account-toggle{appearance:none;font:inherit;display:inline-flex;align-items:center;gap:5px;cursor:pointer;flex:none;border:1px solid color-mix(in oklab, var(--dsw-alias-border-l2,#3a3d45) 70%, transparent);border-radius:999px;padding:3px 10px;font-size:11px;line-height:17px;background:transparent;color:var(--dsw-alias-label-tertiary,#9aa0a8);transition:color .16s,border-color .16s,background .16s}
152
+ .dsm-workbuddy-xdpool-account-toggle:hover:not(:disabled){color:var(--dsw-alias-label-primary,#e6e6e6);border-color:var(--dsw-alias-label-dimmed,#777)}
153
+ .dsm-workbuddy-xdpool-account-toggle:focus-visible{outline:2px solid var(--dsw-alias-brand-primary,#5686fe);outline-offset:1px}
154
+ .dsm-workbuddy-xdpool-account-toggle:disabled{cursor:default;opacity:.6}
155
+ .dsm-workbuddy-xdpool-account-toggle-on{background:color-mix(in oklab, var(--dsw-alias-state-success-primary,#22a06b) 14%, transparent);border-color:color-mix(in oklab, var(--dsw-alias-state-success-primary,#22a06b) 55%, transparent);color:var(--dsw-alias-state-success-primary,#22a06b);font-weight:600}
156
+ .dsm-workbuddy-xdpool-account-toggle-dot{width:6px;height:6px;border-radius:50%;flex:none;background:currentColor;opacity:.9}
145
157
  .dsm-workbuddy-xdpool-account-tags{display:flex;align-items:center;gap:6px;flex-wrap:wrap}
146
158
  .dsm-workbuddy-xdpool-account-tag{padding:1px 8px;border-radius:999px;font-size:11px;line-height:18px;background:var(--dsw-alias-state-success-subtle,rgba(34,160,107,.12));color:var(--dsw-alias-state-success-primary,#22a06b)}
147
159
  .dsm-workbuddy-xdpool-account-tag-cooling{background:var(--dsw-alias-state-warning-subtle,rgba(217,119,6,.15));color:var(--dsw-alias-state-warning-primary,#d97706)}
@@ -152,9 +164,9 @@ window.__ModuleLoader__.load({
152
164
  .dsm-workbuddy-xdpool-account-error{margin:0;color:var(--dsw-alias-state-error-primary,#ef4444);font-size:13px;line-height:20px}
153
165
 
154
166
  /* Two-column stats: packages on the left, total + check-in on the right. */
155
- .dsm-workbuddy-xdpool-stats{display:grid;grid-template-columns:minmax(0,1.5fr) minmax(170px,.85fr);gap:10px;margin-top:10px}
156
- .dsm-workbuddy-xdpool-panel{display:flex;flex-direction:column;min-width:0;gap:7px;padding:14px;border:1px solid var(--dsw-alias-border-l2,#3a3d45);border-radius:12px;background:var(--dsw-alias-bg-layer-2,#24262c)}
157
- .dsm-workbuddy-xdpool-panel-title{color:var(--dsw-alias-label-tertiary,#999);font-size:12px;line-height:18px}
167
+ .dsm-workbuddy-xdpool-stats{display:grid;grid-template-columns:minmax(0,1.45fr) minmax(168px,.85fr);gap:0;flex:1 1 420px;min-width:0;max-width:660px;border-left:1px solid color-mix(in oklab, var(--dsw-alias-border-l2,#3a3d45) 45%, transparent)}
168
+ .dsm-workbuddy-xdpool-panel{display:flex;flex-direction:column;min-width:0;gap:7px;padding:14px 16px}
169
+ .dsm-workbuddy-xdpool-panel-title{color:var(--dsw-alias-label-tertiary,#999);font-size:11px;line-height:16px;letter-spacing:.03em;text-transform:uppercase;font-weight:600}
158
170
  .dsm-workbuddy-xdpool-panel-empty{color:var(--dsw-alias-label-tertiary,#999);font-size:14px;line-height:20px}
159
171
  .dsm-workbuddy-xdpool-panel-error{color:var(--dsw-alias-state-error-primary,#ef4444);font-size:12px;line-height:18px;word-break:break-word}
160
172
  .dsm-workbuddy-xdpool-panel-foot{display:flex;align-items:baseline;justify-content:space-between;gap:10px;margin-top:9px;padding-top:9px;border-top:1px solid var(--dsw-alias-border-l2,#36373b);color:var(--dsw-alias-label-secondary,#c6c9d0);font-size:12px;line-height:18px}
@@ -172,9 +184,9 @@ window.__ModuleLoader__.load({
172
184
  fold onto their own line so a long package name cannot squeeze them out. */
173
185
  .dsm-workbuddy-xdpool-packages-when{grid-column:1/-1;color:var(--dsw-alias-label-tertiary,#9aa0a8);font-size:11px;line-height:15px;font-variant-numeric:tabular-nums}
174
186
  .dsm-workbuddy-xdpool-packages-when-soon{color:var(--dsw-alias-state-warning-primary,#d97706)}
175
- .dsm-workbuddy-xdpool-panel-total{position:relative;align-items:center;text-align:center;overflow:hidden}
187
+ .dsm-workbuddy-xdpool-panel-total{position:relative;align-items:center;text-align:center;justify-content:center;overflow:hidden;background:color-mix(in oklab, var(--dsw-alias-state-success-primary,#22a06b) 5%, transparent)}
176
188
  .dsm-workbuddy-xdpool-panel-total::before{content:"";position:absolute;top:0;left:0;right:0;height:3px;opacity:.9;background:var(--dsw-alias-state-success-primary,#22a06b)}
177
- .dsm-workbuddy-xdpool-total-value{color:var(--dsw-alias-state-success-primary,#22a06b);font-size:30px;line-height:34px;font-weight:700;letter-spacing:-.5px;white-space:nowrap;font-variant-numeric:tabular-nums}
189
+ .dsm-workbuddy-xdpool-total-value{color:var(--dsw-alias-state-success-primary,#22a06b);font-size:28px;line-height:32px;font-weight:800;letter-spacing:-.02em;white-space:nowrap;font-variant-numeric:tabular-nums}
178
190
 
179
191
  /* Model directory list. */
180
192
  .dsm-workbuddy-xdpool-models{display:flex;flex-direction:column;gap:10px;border-top:1px solid var(--dsw-alias-border-l2,#36373b);padding-top:14px}
@@ -231,6 +243,21 @@ window.__ModuleLoader__.load({
231
243
  .dsm-workbuddy-xdpool-checkin-meta{align-items:flex-start}
232
244
  .dsm-workbuddy-xdpool-checkin-bonus{text-align:left}
233
245
  }
246
+
247
+ /* Automation panel: schedule and last-run summary, collapsed behind a switch. */
248
+ .dsm-workbuddy-xdpool-auto{margin-top:12px;padding:10px 12px;border:1px solid var(--dsw-alias-border-l2,#36373b);border-radius:10px;background:var(--dsw-alias-bg-module-platform,#202126)}
249
+ .dsm-workbuddy-xdpool-auto-head{display:flex;align-items:center;justify-content:space-between;gap:8px}
250
+ .dsm-workbuddy-xdpool-auto-title{color:var(--dsw-alias-label-primary,#e8e8ea);font-size:12px;font-weight:600}
251
+ .dsm-workbuddy-xdpool-auto-switch{padding:3px 12px;border:1px solid var(--dsw-alias-border-l2,#36373b);border-radius:999px;background:transparent;color:var(--dsw-alias-label-dimmed,#8a97b5);font-size:11px;cursor:pointer;transition:color .16s,border-color .16s,background .16s}
252
+ .dsm-workbuddy-xdpool-auto-switch:hover:not(:disabled){border-color:var(--dsw-alias-label-dimmed,#777);color:var(--dsw-alias-label-primary,#e8e8ea)}
253
+ .dsm-workbuddy-xdpool-auto-switch:disabled{opacity:.5;cursor:not-allowed}
254
+ .dsm-workbuddy-xdpool-auto-switch-on{border-color:#28c8b4;color:#28c8b4;background:rgba(40,200,180,.12)}
255
+ .dsm-workbuddy-xdpool-auto-hint{margin:6px 0 0;color:var(--dsw-alias-label-dimmed,#8a97b5);font-size:11px;line-height:1.5}
256
+ .dsm-workbuddy-xdpool-auto-jobs{margin-top:8px;display:flex;flex-direction:column;gap:4px}
257
+ .dsm-workbuddy-xdpool-auto-job{display:flex;align-items:center;gap:8px;font-size:11px;color:var(--dsw-alias-label-dimmed,#8a97b5)}
258
+ .dsm-workbuddy-xdpool-auto-job-name{flex:0 0 72px;color:var(--dsw-alias-label-primary,#e8e8ea)}
259
+ .dsm-workbuddy-xdpool-auto-job-when{flex:1 1 auto;font-variant-numeric:tabular-nums}
260
+ .dsm-workbuddy-xdpool-auto-job-last{flex:0 0 auto;text-align:right;font-variant-numeric:tabular-nums}
234
261
  `.trim();
235
262
  //#endregion
236
263
  //#region src/client/PoolCard.tsx
@@ -291,6 +318,35 @@ window.__ModuleLoader__.load({
291
318
  minute: "2-digit"
292
319
  }).format(new Date(ms));
293
320
  }
321
+ /**
322
+ * The automation jobs, in the order they run.
323
+ *
324
+ * The order is the contract: the report has to land before the task pass, or
325
+ * the task pass reads progress the report would have lit. `hoursOf` reads the
326
+ * matching hour list off the status document so the panel stays in step with
327
+ * whatever schedule the scheduler is actually running on.
328
+ */
329
+ const AUTOMATION_JOBS = [
330
+ "report",
331
+ "tasks",
332
+ "checkin",
333
+ "streak"
334
+ ];
335
+ /** Read one job's configured hours off the status document. */
336
+ function automationHours(status, kind) {
337
+ const automation = status.automation;
338
+ if (automation === void 0) return [];
339
+ switch (kind) {
340
+ case "report": return automation.reportHours;
341
+ case "tasks": return automation.taskHours;
342
+ case "checkin": return automation.checkinHours;
343
+ case "streak": return automation.streakHours;
344
+ }
345
+ }
346
+ /** Read one job's last-run record off the status document. */
347
+ function automationJob(status, kind) {
348
+ return status.automation?.jobs[kind];
349
+ }
294
350
  function dotColor(status) {
295
351
  return status === "ok" ? "var(--dsw-alias-state-success-primary, #22a06b)" : status === "error" ? "var(--dsw-alias-state-error-primary, #ef4444)" : "var(--dsw-alias-label-dimmed, #9aa0a6)";
296
352
  }
@@ -380,6 +436,7 @@ window.__ModuleLoader__.load({
380
436
  const [error, setError] = (0, react.useState)(void 0);
381
437
  const [busy, setBusy] = (0, react.useState)(false);
382
438
  const [cooldownBusy, setCooldownBusy] = (0, react.useState)(false);
439
+ const [automationBusy, setAutomationBusy] = (0, react.useState)(false);
383
440
  const [flash, setFlash] = (0, react.useState)(void 0);
384
441
  /** Account id whose daily claim is currently in flight. */
385
442
  const [checkinBusyId, setCheckinBusyId] = (0, react.useState)(void 0);
@@ -530,27 +587,21 @@ window.__ModuleLoader__.load({
530
587
  * restart and is re-applied after every re-scan.
531
588
  */
532
589
  const toggleAccountDisabled = async (accountId, disabled) => {
590
+ const write = settingsScope?.set;
591
+ if (write === void 0) {
592
+ setError(t?.("row.modelsSaveError", { message: "settings scope is read-only" }) ?? "settings scope is read-only");
593
+ return;
594
+ }
533
595
  setAccountBusyId(accountId);
534
596
  setFlash(void 0);
535
597
  try {
536
- const response = await fetch(POOL_ACCOUNT_DISABLE_PATH, {
537
- method: "POST",
538
- headers: {
539
- accept: "application/json",
540
- "content-type": "application/json"
541
- },
542
- credentials: "same-origin",
543
- body: JSON.stringify({
544
- accountId,
545
- disabled
546
- })
547
- });
548
- const body = await response.json().catch(() => void 0);
549
- if (!response.ok) throw new Error(body?.error ?? `HTTP ${response.status}`);
598
+ const current = (status?.accounts ?? []).filter((account) => account.disabled === true).map((account) => account.id);
599
+ const next = disabled ? current.includes(accountId) ? current : [...current, accountId] : current.filter((id) => id !== accountId);
600
+ await write.call(settingsScope, "disabledAccountIds", next);
550
601
  await refresh(activeRegion);
551
602
  } catch (cause) {
552
603
  const message = cause instanceof Error ? cause.message : String(cause);
553
- if (mounted.current) setError(t?.("row.accountToggleError", { message }) ?? `Could not switch the account: ${message}`);
604
+ if (mounted.current) setError(t?.("row.accountToggleError", { message }) ?? "Could not switch the account: " + message);
554
605
  } finally {
555
606
  if (mounted.current) setAccountBusyId(void 0);
556
607
  }
@@ -640,6 +691,39 @@ window.__ModuleLoader__.load({
640
691
  if (mounted.current) setError(String(cause));
641
692
  }
642
693
  };
694
+ /**
695
+ * Switch the daily-points automation on or off.
696
+ *
697
+ * The whole `automation` object is written as one key, because that is how the
698
+ * settings document stores it: the schedule fields must be carried along, or a
699
+ * save would drop the hour lists the scheduler is running on.
700
+ */
701
+ const setAutomationEnabled = async (enabled) => {
702
+ const write = settingsScope?.set;
703
+ if (write === void 0) {
704
+ setError(t?.("row.modelsSaveError", { message: "settings scope is read-only" }) ?? "settings scope is read-only");
705
+ return;
706
+ }
707
+ const existing = status?.automation;
708
+ setAutomationBusy(true);
709
+ setFlash(void 0);
710
+ try {
711
+ await write.call(settingsScope, "automation", {
712
+ ...existing === void 0 ? {} : {
713
+ checkinHours: [...existing.checkinHours],
714
+ reportHours: [...existing.reportHours],
715
+ taskHours: [...existing.taskHours],
716
+ streakHours: [...existing.streakHours]
717
+ },
718
+ enabled
719
+ });
720
+ await refresh(activeRegion);
721
+ } catch (cause) {
722
+ if (mounted.current) setError(String(cause));
723
+ } finally {
724
+ if (mounted.current) setAutomationBusy(false);
725
+ }
726
+ };
643
727
  const saveModels = async () => {
644
728
  if (draft === void 0 || status === void 0) return;
645
729
  if (enabledCount === 0) {
@@ -812,6 +896,58 @@ window.__ModuleLoader__.load({
812
896
  }) : null]
813
897
  })]
814
898
  }),
899
+ status?.automation === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
900
+ className: "dsm-workbuddy-xdpool-auto",
901
+ "aria-label": t?.("row.autoTitle") ?? "Automation",
902
+ children: [
903
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
904
+ className: "dsm-workbuddy-xdpool-auto-head",
905
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
906
+ className: "dsm-workbuddy-xdpool-auto-title",
907
+ children: t?.("row.autoTitle") ?? "Automation"
908
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
909
+ type: "button",
910
+ role: "switch",
911
+ "aria-checked": status.automation.enabled,
912
+ disabled: !settingsWritable || automationBusy,
913
+ className: `dsm-workbuddy-xdpool-auto-switch${status.automation.enabled ? " dsm-workbuddy-xdpool-auto-switch-on" : ""}`,
914
+ onClick: () => {
915
+ setAutomationEnabled(!status.automation.enabled);
916
+ },
917
+ children: automationBusy ? t?.("row.autoBusy") ?? "Saving…" : status.automation.enabled ? t?.("row.autoOn") ?? "On" : t?.("row.autoOff") ?? "Off"
918
+ })]
919
+ }),
920
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
921
+ className: "dsm-workbuddy-xdpool-auto-hint",
922
+ children: status.automation.enabled ? t?.("row.autoHintOn") ?? "Reports activity, claims task rewards and checks in once a day." : t?.("row.autoHintOff") ?? "Off: no background requests are made for you."
923
+ }),
924
+ status.automation.enabled ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
925
+ className: "dsm-workbuddy-xdpool-auto-jobs",
926
+ children: AUTOMATION_JOBS.map((kind) => {
927
+ const job = automationJob(status, kind);
928
+ const hours = automationHours(status, kind);
929
+ const label = t?.(`row.autoJob_${kind}`) ?? kind;
930
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
931
+ className: "dsm-workbuddy-xdpool-auto-job",
932
+ children: [
933
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
934
+ className: "dsm-workbuddy-xdpool-auto-job-name",
935
+ children: label
936
+ }),
937
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
938
+ className: "dsm-workbuddy-xdpool-auto-job-when",
939
+ children: hours.length === 0 ? t?.("row.autoHourNone") ?? "not scheduled" : hours.map((hour) => `${String(hour).padStart(2, "0")}:00`).join(" · ")
940
+ }),
941
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
942
+ className: "dsm-workbuddy-xdpool-auto-job-last",
943
+ children: job?.lastRunDate === void 0 ? t?.("row.autoNever") ?? "not run yet" : `${job.lastRunDate} · ${job.ok}${job.failed > 0 ? `/${job.failed}` : ""}`
944
+ })
945
+ ]
946
+ }, kind);
947
+ })
948
+ }) : null
949
+ ]
950
+ }),
815
951
  flash === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
816
952
  className: "dsm-workbuddy-xdpool-note",
817
953
  children: flash
@@ -851,31 +987,55 @@ window.__ModuleLoader__.load({
851
987
  accountCount > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
852
988
  className: "dsm-workbuddy-xdpool-accounts",
853
989
  "aria-label": t?.("row.accountsTitle") ?? "Accounts",
854
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
855
- className: "dsm-workbuddy-xdpool-accounts-head",
856
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", {
857
- className: "dsm-workbuddy-xdpool-accounts-title",
858
- children: t?.("row.accountsTitle") ?? "Accounts in the pool"
859
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
860
- className: "dsm-workbuddy-xdpool-accounts-summary",
861
- children: t?.("row.accountsSummary", {
862
- count: accountCount,
863
- cooling
864
- }) ?? `${accountCount} account(s) · ${cooling} cooling`
865
- })]
866
- }), status?.accounts.map((account) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(AccountBlock, {
867
- account,
868
- ...status.activeAccountId === void 0 ? {} : { activeAccountId: status.activeAccountId },
869
- ...checkinBusyId === void 0 ? {} : { checkinBusyId },
870
- onClaimCheckin: (accountId) => {
871
- claimCheckin(accountId);
872
- },
873
- onToggleDisabled: (accountId, disabled) => {
874
- toggleAccountDisabled(accountId, disabled);
875
- },
876
- ...accountBusyId === void 0 ? {} : { accountBusyId },
877
- t
878
- }, account.id))]
990
+ children: [
991
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
992
+ className: "dsm-workbuddy-xdpool-accounts-head",
993
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", {
994
+ className: "dsm-workbuddy-xdpool-accounts-title",
995
+ children: t?.("row.accountsTitle") ?? "Accounts in the pool"
996
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
997
+ className: "dsm-workbuddy-xdpool-accounts-summary",
998
+ children: t?.("row.accountsSummary", {
999
+ count: accountCount,
1000
+ cooling
1001
+ }) ?? `${accountCount} account(s) · ${cooling} cooling`
1002
+ })]
1003
+ }),
1004
+ (() => {
1005
+ const active = status?.activeAccountId === void 0 ? void 0 : status.accounts.find((account) => account.id === status.activeAccountId);
1006
+ if (active === void 0) return null;
1007
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1008
+ className: "dsm-workbuddy-xdpool-current",
1009
+ children: [
1010
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: "dsm-workbuddy-xdpool-current-dot" }),
1011
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1012
+ className: "dsm-workbuddy-xdpool-current-label",
1013
+ children: t?.("row.currentAccount") ?? "In use now"
1014
+ }),
1015
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1016
+ className: "dsm-workbuddy-xdpool-current-name",
1017
+ children: active.label
1018
+ }),
1019
+ active.disabled === true ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1020
+ className: "dsm-workbuddy-xdpool-current-note",
1021
+ children: t?.("row.currentAccountDisabled") ?? "disabled — will switch on the next request"
1022
+ }) : null
1023
+ ]
1024
+ });
1025
+ })(),
1026
+ status?.accounts.map((account) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(AccountBlock, {
1027
+ account,
1028
+ ...checkinBusyId === void 0 ? {} : { checkinBusyId },
1029
+ onClaimCheckin: (accountId) => {
1030
+ claimCheckin(accountId);
1031
+ },
1032
+ onToggleDisabled: (accountId, disabled) => {
1033
+ toggleAccountDisabled(accountId, disabled);
1034
+ },
1035
+ ...accountBusyId === void 0 ? {} : { accountBusyId },
1036
+ t
1037
+ }, account.id))
1038
+ ]
879
1039
  }) : null,
880
1040
  (status?.models.length ?? 0) > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
881
1041
  className: "dsm-workbuddy-xdpool-models",
@@ -934,81 +1094,75 @@ window.__ModuleLoader__.load({
934
1094
  });
935
1095
  }
936
1096
  /** One account block: label + status tag + meta + optional credit panels. */
937
- function AccountBlock({ account, activeAccountId, t, checkinBusyId, onClaimCheckin, accountBusyId, onToggleDisabled }) {
938
- const isActive = account.id === activeAccountId;
1097
+ function AccountBlock({ account, t, checkinBusyId, onClaimCheckin, accountBusyId, onToggleDisabled }) {
939
1098
  const isDisabled = account.disabled === true;
940
1099
  const isCooling = account.cooling === true;
941
1100
  const cooldownUntil = account.cooldownUntil !== void 0 ? Date.parse(account.cooldownUntil) : void 0;
942
1101
  const modelCooldowns = account.modelCooldowns ?? [];
943
- const tag = isActive ? {
944
- text: t?.("row.accountNext") ?? "Next up",
945
- cls: "dsm-workbuddy-xdpool-account-tag"
946
- } : isCooling ? {
1102
+ const tag = isCooling ? {
947
1103
  text: t?.("row.cooling") ?? "Cooling",
948
1104
  cls: "dsm-workbuddy-xdpool-account-tag dsm-workbuddy-xdpool-account-tag-cooling"
949
1105
  } : null;
950
1106
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
951
1107
  className: isDisabled ? "dsm-workbuddy-xdpool-account dsm-workbuddy-xdpool-account-off" : "dsm-workbuddy-xdpool-account",
952
1108
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
953
- className: "dsm-workbuddy-xdpool-account-copy",
1109
+ className: "dsm-workbuddy-xdpool-account-head",
954
1110
  children: [
955
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
956
- className: "dsm-workbuddy-xdpool-account-head",
957
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
958
- className: "dsm-workbuddy-xdpool-account-label",
959
- children: account.label
960
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
961
- className: "dsm-workbuddy-xdpool-account-toggle",
962
- title: t?.("row.accountToggleHint") ?? "Include this account in the pool",
963
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
964
- type: "checkbox",
965
- checked: !isDisabled,
966
- disabled: accountBusyId === account.id,
967
- onChange: (event) => {
968
- onToggleDisabled(account.id, !event.target.checked);
969
- }
970
- }), isDisabled ? t?.("row.accountOff") ?? "Off" : t?.("row.accountInRotation") ?? "In rotation"]
971
- })]
972
- }),
973
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
974
- className: "dsm-workbuddy-xdpool-account-tags",
975
- children: tag === null ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
976
- className: tag.cls,
977
- children: tag.text
978
- })
1111
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1112
+ className: "dsm-workbuddy-xdpool-account-label",
1113
+ children: account.label
979
1114
  }),
980
- account.domain !== "" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
981
- className: "dsm-workbuddy-xdpool-account-meta",
982
- children: account.domain
1115
+ tag === null ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1116
+ className: tag.cls,
1117
+ children: tag.text
983
1118
  }),
984
- account.expiresAt !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
985
- className: "dsm-workbuddy-xdpool-account-meta",
986
- children: t?.("row.tokenExpiry", { time: formatDateTime(account.expiresAt) }) ?? `token ${formatDateTime(account.expiresAt)}`
987
- }) : null,
988
- isCooling && cooldownUntil !== void 0 && !Number.isNaN(cooldownUntil) ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
989
- className: "dsm-workbuddy-xdpool-account-meta",
990
- children: [
991
- t?.("row.cooldownUntil", { time: formatTime(cooldownUntil) }) ?? `until ${formatTime(cooldownUntil)}`,
992
- " · ",
993
- t?.("row.cooldownHits", { hits: account.rateLimitHits ?? 0 }) ?? `${account.rateLimitHits ?? 0} hit(s)`
994
- ]
995
- }) : null,
996
- modelCooldowns.length > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
997
- className: "dsm-workbuddy-xdpool-account-modelcool",
998
- children: modelCooldowns.map((mc) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
999
- className: "dsm-workbuddy-xdpool-account-modelcool-chip",
1000
- children: t?.("row.modelCooling", {
1001
- model: mc.modelId,
1002
- time: formatDateTime(mc.until)
1003
- }) ?? `${mc.modelId} cooling to ${formatDateTime(mc.until)}`
1004
- }, mc.modelId))
1005
- }) : null
1119
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
1120
+ type: "button",
1121
+ role: "switch",
1122
+ "aria-checked": !isDisabled,
1123
+ className: isDisabled ? "dsm-workbuddy-xdpool-account-toggle" : "dsm-workbuddy-xdpool-account-toggle dsm-workbuddy-xdpool-account-toggle-on",
1124
+ title: t?.("row.accountToggleHint") ?? "Enable this account (uncheck to keep it out of the pool)",
1125
+ disabled: accountBusyId === account.id,
1126
+ onClick: () => {
1127
+ onToggleDisabled(account.id, !isDisabled);
1128
+ },
1129
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: "dsm-workbuddy-xdpool-account-toggle-dot" }), isDisabled ? t?.("row.accountOff") ?? "Disabled" : t?.("row.accountInRotation") ?? "Enabled"]
1130
+ })
1006
1131
  ]
1007
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(AccountStats, {
1008
- account,
1009
- t,
1010
- checkinBusy: checkinBusyId === account.id,
1011
- onClaim: onClaimCheckin
1132
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1133
+ className: "dsm-workbuddy-xdpool-account-body",
1134
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1135
+ className: "dsm-workbuddy-xdpool-account-copy",
1136
+ children: [
1137
+ account.expiresAt !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1138
+ className: "dsm-workbuddy-xdpool-account-meta",
1139
+ children: t?.("row.tokenExpiry", { time: formatDateTime(account.expiresAt) }) ?? `token ${formatDateTime(account.expiresAt)}`
1140
+ }) : null,
1141
+ isCooling && cooldownUntil !== void 0 && !Number.isNaN(cooldownUntil) ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
1142
+ className: "dsm-workbuddy-xdpool-account-meta",
1143
+ children: [
1144
+ t?.("row.cooldownUntil", { time: formatTime(cooldownUntil) }) ?? `until ${formatTime(cooldownUntil)}`,
1145
+ " · ",
1146
+ t?.("row.cooldownHits", { hits: account.rateLimitHits ?? 0 }) ?? `${account.rateLimitHits ?? 0} hit(s)`
1147
+ ]
1148
+ }) : null,
1149
+ modelCooldowns.length > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1150
+ className: "dsm-workbuddy-xdpool-account-modelcool",
1151
+ children: modelCooldowns.map((mc) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1152
+ className: "dsm-workbuddy-xdpool-account-modelcool-chip",
1153
+ children: t?.("row.modelCooling", {
1154
+ model: mc.modelId,
1155
+ time: formatDateTime(mc.until)
1156
+ }) ?? `${mc.modelId} cooling to ${formatDateTime(mc.until)}`
1157
+ }, mc.modelId))
1158
+ }) : null
1159
+ ]
1160
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(AccountStats, {
1161
+ account,
1162
+ t,
1163
+ checkinBusy: checkinBusyId === account.id,
1164
+ onClaim: onClaimCheckin
1165
+ })]
1012
1166
  })]
1013
1167
  });
1014
1168
  }
@@ -1254,11 +1408,12 @@ window.__ModuleLoader__.load({
1254
1408
  "row.accountsSummary": "{count} account(s) · {cooling} cooling",
1255
1409
  "row.ok": "Healthy — requests auto-rotate across accounts",
1256
1410
  "row.allCooling": "Every account is rate-limited right now; requests pause until a cooldown lifts.",
1257
- "row.accountNext": "Next up",
1258
- "row.accountInRotation": "In rotation",
1259
- "row.accountOff": "Off",
1260
- "row.accountToggleHint": "Include this account in the pool",
1411
+ "row.accountInRotation": "Enabled",
1412
+ "row.accountOff": "Disabled",
1413
+ "row.accountToggleHint": "Enable this account (uncheck to keep it out of the pool)",
1261
1414
  "row.accountToggleError": "Could not switch the account: {message}",
1415
+ "row.currentAccount": "In use now",
1416
+ "row.currentAccountDisabled": "switched off — the next request moves on",
1262
1417
  "row.cooling": "Cooling (rate-limited)",
1263
1418
  "row.cooldownHits": "{hits} hit(s)",
1264
1419
  "row.cooldownUntil": "until {time}",
@@ -1318,7 +1473,19 @@ window.__ModuleLoader__.load({
1318
1473
  "row.resetCooldownsBusy": "Clearing…",
1319
1474
  "row.resetCooldownsDone": "Cooldowns cleared",
1320
1475
  "row.accountsRescanned": "Detected {count} account(s)",
1321
- "row.error": "Pool status unavailable: {message}"
1476
+ "row.error": "Pool status unavailable: {message}",
1477
+ "row.autoTitle": "Automation",
1478
+ "row.autoOn": "On",
1479
+ "row.autoOff": "Off",
1480
+ "row.autoBusy": "Saving…",
1481
+ "row.autoHintOn": "Reports activity, claims task rewards and checks in once a day.",
1482
+ "row.autoHintOff": "Off: no background requests are made for you.",
1483
+ "row.autoJob_report": "Activity report",
1484
+ "row.autoJob_tasks": "Task rewards",
1485
+ "row.autoJob_checkin": "Daily check-in",
1486
+ "row.autoJob_streak": "Streak bonus",
1487
+ "row.autoHourNone": "not scheduled",
1488
+ "row.autoNever": "not run yet"
1322
1489
  };
1323
1490
  const zh = {
1324
1491
  "row.title": "WorkBuddy 池(dsh-workbuddy-xdpool)",
@@ -1346,11 +1513,12 @@ window.__ModuleLoader__.load({
1346
1513
  "row.accountsSummary": "{count} 个账号 · {cooling} 个冷却中",
1347
1514
  "row.ok": "运行健康 —— 请求会在各账号间自动轮换",
1348
1515
  "row.allCooling": "当前所有账号都处于限流冷却,请求会暂停直到某个冷却结束。",
1349
- "row.accountNext": "下一个",
1350
- "row.accountInRotation": "参与轮换",
1516
+ "row.accountInRotation": "已启用",
1351
1517
  "row.accountOff": "已停用",
1352
- "row.accountToggleHint": "把这个账号计入池子",
1518
+ "row.accountToggleHint": "启用该账号(取消勾选则不参与池子)",
1353
1519
  "row.accountToggleError": "切换账号失败:{message}",
1520
+ "row.currentAccount": "当前使用",
1521
+ "row.currentAccountDisabled": "已停用 —— 下次请求会换号",
1354
1522
  "row.cooling": "冷却中(被限流)",
1355
1523
  "row.cooldownHits": "触发 {hits} 次",
1356
1524
  "row.cooldownUntil": "至 {time}",
@@ -1410,7 +1578,19 @@ window.__ModuleLoader__.load({
1410
1578
  "row.resetCooldownsBusy": "正在清除…",
1411
1579
  "row.resetCooldownsDone": "冷却已清除",
1412
1580
  "row.accountsRescanned": "检测到 {count} 个账号",
1413
- "row.error": "池状态不可用:{message}"
1581
+ "row.error": "池状态不可用:{message}",
1582
+ "row.autoTitle": "积分自动化",
1583
+ "row.autoOn": "已开启",
1584
+ "row.autoOff": "已关闭",
1585
+ "row.autoBusy": "保存中…",
1586
+ "row.autoHintOn": "每天自动活跃上报、领取任务奖励并签到。",
1587
+ "row.autoHintOff": "已关闭:不会替你发起任何后台请求。",
1588
+ "row.autoJob_report": "活跃上报",
1589
+ "row.autoJob_tasks": "任务奖励",
1590
+ "row.autoJob_checkin": "每日签到",
1591
+ "row.autoJob_streak": "连登奖励",
1592
+ "row.autoHourNone": "未排程",
1593
+ "row.autoNever": "尚未执行"
1414
1594
  };
1415
1595
  //#endregion
1416
1596
  //#region src/client/index.tsx