dsh-workbuddy-xdpool 1.7.9 → 1.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -4,6 +4,40 @@
4
4
 
5
5
  版本号遵循 [语义化版本](https://semver.org/lang/zh-CN/)。
6
6
 
7
+ ## 1.8.0 — 四种模式,终于排得像个样子了
8
+
9
+ > 上一版加了第四种模式「每对话固定」,功能是好的,排版是坏的。坏得很实在:格子还是按三个排的,第四个只好委屈地掉到下一行,占着半个身位,像个插班生。
10
+
11
+ ### 问题出在哪
12
+
13
+ 「账号使用方式」这一块本来写着 `grid-template-columns: repeat(3, ...)`——三列。模式从三个变成四个之后,没人去改这个数字。
14
+
15
+ 于是:
16
+
17
+ - 第四张卡被挤到第二行,左边空出一大块,右边孤零零一张;
18
+ - 每张卡只剩大约 190 像素宽,中文说明被折成两三行,长短参差不齐,眼睛没法竖着扫;
19
+ - 更尴尬的是,**测试全绿、构建全过**——因为没有任何一个测试会去数 CSS 里的网格列数。这种错只能靠眼睛发现。
20
+
21
+ ### 现在长什么样
22
+
23
+ - **两列排布**,四张卡正好 2×2,方方正正;窄窗口下自动变成一列,每张卡独占一行,说明文字不再折行。
24
+ - **顶部多了一个「当前:每对话固定」的小徽章**,不用在四张卡里自己找哪张亮着。
25
+ - **选中的那张卡明显抬高**:底色提亮一层,左侧加一条竖色条,一眼就能认出来。
26
+ - 「推荐」不再塞进标题文字里(`每对话固定(推荐)`这种写法会让标题长度不齐),改成一个**独立的小绿标**挂在卡片右上角。
27
+
28
+ ### 配色上的一点坚持
29
+
30
+ 这次参考了 `dingminhua/dsh-connect-workbuddy` 的界面,它的思路很清楚:给「当前项」一条竖色条 + 抬高的底色。这套手法我们借来了。
31
+
32
+ 但**颜色没借**。它用的是宿主品牌蓝 `#5686fe`——那是 DSH 自己的颜色,不是这张卡片的。我们的卡片有自己的一套:底色用和上面区域切换按钮选中时同一个「抬升表面」色,竖色条用中性色,和宿主保持一致但不冒充宿主。
33
+
34
+ 绿色只留给「推荐」徽章,因为卡片下方那个绿点已经在表示「账号健康」了,别让两种绿打架。
35
+
36
+ ### 已知边界
37
+
38
+ - 这次只动了样式和排版,**分发逻辑一行没改**——四种模式的行为和 1.7.9 完全一样。
39
+ - 布局测试是**读源码文本**断言的(比如「网格必须是两列」「选中态不许出现 brand-primary」),能防住改错,但防不住视觉上「两列也不好看」。真正的判断还得靠眼睛。
40
+
7
41
  ## 1.7.9 — 一个对话只用一个账号,新对话才换人
8
42
 
9
43
  > 有人反馈:除了「优先模式」,换别的模式都会掉命中率、掉速度。这个观察是对的,而且原因不在你的网络——在账号切换本身。所以这次加了一种新模式,专门解决这件事。
package/lib/client.js CHANGED
@@ -165,17 +165,38 @@ window.__ModuleLoader__.load({
165
165
  .dsm-workbuddy-xdpool-usage-status{display:flex;align-items:center;gap:10px;font-size:15px;font-weight:600;color:var(--dsw-alias-label-primary,#e6e6e6)}
166
166
  .dsm-workbuddy-xdpool-usage-dot{width:9px;height:9px;border-radius:50%;flex:0 0 auto}
167
167
  .dsm-workbuddy-xdpool-usage-hint{margin:0;color:var(--dsw-alias-label-tertiary,#9aa0a8);font-size:12px;line-height:18px}
168
- /* Distribution switch: priority (drain one) vs round-robin (spread). */
169
- .dsm-workbuddy-xdpool-dist{display:flex;flex-direction:column;gap:7px}
168
+ /*
169
+ * Distribution picker: four modes, so the old three-column grid left the
170
+ * fourth card wrapped onto a line of its own. Two columns give every card room
171
+ * for a full hint sentence and keep the reading order 2x2.
172
+ *
173
+ * The selection cue is this card's own: an inset accent bar plus a solid
174
+ * surface, the same "current one is a raised chip" language the account list
175
+ * uses for the serving account. It is deliberately NOT the brand blue (that is
176
+ * the host's, not ours) and not the health green (that already means "this
177
+ * account works" two cards down).
178
+ */
179
+ .dsm-workbuddy-xdpool-dist{display:flex;flex-direction:column;gap:9px}
180
+ .dsm-workbuddy-xdpool-dist-head{display:flex;align-items:center;justify-content:space-between;gap:10px;flex-wrap:wrap}
170
181
  .dsm-workbuddy-xdpool-dist-title{color:var(--dsw-alias-label-tertiary,#9aa0a8);font-size:11px;line-height:16px;letter-spacing:.03em;text-transform:uppercase;font-weight:600}
171
- .dsm-workbuddy-xdpool-dist-option{appearance:none;font:inherit;cursor:pointer;text-align:left;display:flex;flex-direction:column;justify-content:center;gap:2px;min-height:44px;border:1px solid var(--dsw-alias-border-l2,#3a3d45);border-radius:8px;padding:6px 10px;background:transparent;color:var(--dsw-alias-label-tertiary,#9aa0a8);transition:color .16s,border-color .16s,background .16s}
172
- .dsm-workbuddy-xdpool-dist-option:hover:not(:disabled):not(.dsm-workbuddy-xdpool-dist-option-active){color:var(--dsw-alias-label-primary,#e6e6e6);border-color:var(--dsw-alias-label-dimmed,#777)}
182
+ /* "now: X" — repeats the answer in the header so the user does not have to
183
+ scan four cards for the raised one. */
184
+ .dsm-workbuddy-xdpool-dist-now{flex:none;padding:2px 10px;border-radius:999px;border:1px solid color-mix(in oklab, var(--dsw-alias-label-primary,#e6e6e6) 26%, transparent);background:color-mix(in oklab, var(--dsw-alias-label-primary,#e6e6e6) 10%, transparent);color:var(--dsw-alias-label-primary,#e6e6e6);font-size:12px;font-weight:600;line-height:18px;white-space:nowrap}
185
+ .dsm-workbuddy-xdpool-dist-options{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:8px}
186
+ .dsm-workbuddy-xdpool-dist-option{appearance:none;font:inherit;cursor:pointer;text-align:left;display:flex;flex-direction:column;justify-content:center;gap:4px;min-height:62px;border:1px solid color-mix(in oklab, var(--dsw-alias-border-l2,#3a3d45) 80%, transparent);border-radius:12px;padding:9px 13px;background:transparent;color:var(--dsw-alias-label-tertiary,#9aa0a8);transition:color .16s,border-color .16s,background .16s,box-shadow .16s}
187
+ .dsm-workbuddy-xdpool-dist-option:hover:not(:disabled):not(.dsm-workbuddy-xdpool-dist-option-active){color:var(--dsw-alias-label-secondary,#c6c9d0);border-color:var(--dsw-alias-label-dimmed,#777);background:rgba(255,255,255,.03)}
173
188
  .dsm-workbuddy-xdpool-dist-option:focus-visible{outline:2px solid var(--dsw-alias-brand-primary,#5686fe);outline-offset:1px}
174
- .dsm-workbuddy-xdpool-dist-option-active{background:var(--dsw-alias-state-success-subtle,rgba(34,160,107,.14));border-color:var(--dsw-alias-state-success-primary,#22a06b);color:var(--dsw-alias-state-success-primary,#22a06b)}
189
+ /* Selected: raised onto layer-3 (the same surface the region tabs use when
190
+ active) with an inset accent bar. The bar is what survives a glance — a
191
+ tinted wash alone is indistinguishable from hover. */
192
+ .dsm-workbuddy-xdpool-dist-option-active{background:var(--dsw-alias-bg-layer-3,#2a2c33);border-color:color-mix(in oklab, var(--dsw-alias-label-primary,#e6e6e6) 34%, transparent);box-shadow:inset 3px 0 0 var(--dsw-alias-label-primary,#e6e6e6);color:var(--dsw-alias-label-primary,#e6e6e6)}
193
+ .dsm-workbuddy-xdpool-dist-option-active .dsm-workbuddy-xdpool-dist-option-name{color:var(--dsw-alias-label-primary,#e6e6e6)}
175
194
  .dsm-workbuddy-xdpool-dist-option:disabled{cursor:default;opacity:.6}
176
- .dsm-workbuddy-xdpool-dist-option-name{font-size:11px;line-height:16px;font-weight:600}
177
- .dsm-workbuddy-xdpool-dist-option-hint{font-size:10px;line-height:14px;opacity:.8}
178
- .dsm-workbuddy-xdpool-dist-options{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:8px}
195
+ .dsm-workbuddy-xdpool-dist-option-top{display:flex;align-items:center;gap:7px;min-width:0}
196
+ .dsm-workbuddy-xdpool-dist-option-name{font-size:12.5px;line-height:18px;font-weight:600;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
197
+ /* "recommended" chip: quiet, and only on the mode this release argues for. */
198
+ .dsm-workbuddy-xdpool-dist-option-badge{flex:none;margin-left:auto;padding:1px 7px;border-radius:999px;background:var(--dsw-alias-state-success-subtle,rgba(34,160,107,.14));color:var(--dsw-alias-state-success-primary,#22a06b);font-size:10px;font-weight:600;line-height:15px;white-space:nowrap}
199
+ .dsm-workbuddy-xdpool-dist-option-hint{font-size:11px;line-height:16px;opacity:.85}
179
200
  .dsm-workbuddy-xdpool-usage-actions{display:flex;align-items:center;gap:8px;flex-wrap:wrap}
180
201
 
181
202
  /* Account list (each account = a labeled subpanel, same as dingminhua). */
@@ -334,6 +355,10 @@ window.__ModuleLoader__.load({
334
355
  .dsm-workbuddy-xdpool-checkin{align-items:stretch}
335
356
  .dsm-workbuddy-xdpool-checkin-meta{align-items:flex-start}
336
357
  .dsm-workbuddy-xdpool-checkin-bonus{text-align:left}
358
+ /* One mode per row: at this width a two-up grid leaves ~150px per card, and
359
+ the hint sentence wraps to four lines. */
360
+ .dsm-workbuddy-xdpool-dist-options{grid-template-columns:minmax(0,1fr)}
361
+ .dsm-workbuddy-xdpool-dist-option{min-height:0}
337
362
  }
338
363
 
339
364
  /* Automation panel: schedule and last-run summary, collapsed behind a switch. */
@@ -597,6 +622,32 @@ window.__ModuleLoader__.load({
597
622
  if (value >= 1e3 && value % 1e3 === 0) return `${value / 1e3}K`;
598
623
  return String(value);
599
624
  }
625
+ /**
626
+ * The four ways the pool can hand out accounts.
627
+ *
628
+ * Order is the reading order of the picker: the recommended mode first, then
629
+ * the two cache-friendly extremes, then the two spreading modes.
630
+ */
631
+ const DIST_OPTIONS = [
632
+ "sticky",
633
+ "priority",
634
+ "balanced",
635
+ "round-robin"
636
+ ];
637
+ /** Localized name of one distribution mode. */
638
+ function distLabel(option, t) {
639
+ if (option === "sticky") return t?.("row.distSticky") ?? "Per conversation";
640
+ if (option === "priority") return t?.("row.distPriority") ?? "Priority";
641
+ if (option === "balanced") return t?.("row.distBalanced") ?? "Balanced";
642
+ return t?.("row.distRoundRobin") ?? "Round-robin";
643
+ }
644
+ /** One-line explanation of what a mode does to caching and spend. */
645
+ function distHint(option, t) {
646
+ if (option === "sticky") return t?.("row.distStickyHint") ?? "";
647
+ if (option === "priority") return t?.("row.distPriorityHint") ?? "";
648
+ if (option === "balanced") return t?.("row.distBalancedHint") ?? "";
649
+ return t?.("row.distRoundRobinHint") ?? "";
650
+ }
600
651
  /** Build the draft from the server's selection + catalog flags. */
601
652
  function draftFromStatus(status) {
602
653
  const selection = status.selection;
@@ -1298,20 +1349,21 @@ window.__ModuleLoader__.load({
1298
1349
  className: "dsm-workbuddy-xdpool-dist",
1299
1350
  role: "radiogroup",
1300
1351
  "aria-label": t?.("row.distTitle") ?? "Account usage",
1301
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1302
- className: "dsm-workbuddy-xdpool-dist-title",
1303
- children: t?.("row.distTitle") ?? "Account usage"
1352
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1353
+ className: "dsm-workbuddy-xdpool-dist-head",
1354
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1355
+ className: "dsm-workbuddy-xdpool-dist-title",
1356
+ children: t?.("row.distTitle") ?? "Account usage"
1357
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1358
+ className: "dsm-workbuddy-xdpool-dist-now",
1359
+ children: distLabel(status.distribution ?? "priority", t)
1360
+ })]
1304
1361
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1305
1362
  className: "dsm-workbuddy-xdpool-dist-options",
1306
- children: [
1307
- "sticky",
1308
- "priority",
1309
- "balanced",
1310
- "round-robin"
1311
- ].map((option) => {
1363
+ children: DIST_OPTIONS.map((option) => {
1312
1364
  const active = (status.distribution ?? "priority") === option;
1313
- const label = option === "sticky" ? t?.("row.distSticky") ?? "Per conversation" : option === "priority" ? t?.("row.distPriority") ?? "Priority" : option === "balanced" ? t?.("row.distBalanced") ?? "Balanced" : t?.("row.distRoundRobin") ?? "Round-robin";
1314
- const hint = option === "sticky" ? t?.("row.distStickyHint") ?? "" : option === "priority" ? t?.("row.distPriorityHint") ?? "" : option === "balanced" ? t?.("row.distBalancedHint") ?? "" : t?.("row.distRoundRobinHint") ?? "";
1365
+ const label = distLabel(option, t);
1366
+ const hint = distHint(option, t);
1315
1367
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
1316
1368
  type: "button",
1317
1369
  role: "radio",
@@ -1322,9 +1374,15 @@ window.__ModuleLoader__.load({
1322
1374
  onClick: () => {
1323
1375
  setDistribution(option);
1324
1376
  },
1325
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1326
- className: "dsm-workbuddy-xdpool-dist-option-name",
1327
- children: label
1377
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
1378
+ className: "dsm-workbuddy-xdpool-dist-option-top",
1379
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1380
+ className: "dsm-workbuddy-xdpool-dist-option-name",
1381
+ children: label
1382
+ }), option === "sticky" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1383
+ className: "dsm-workbuddy-xdpool-dist-option-badge",
1384
+ children: t?.("row.distRecommended") ?? "Recommended"
1385
+ }) : null]
1328
1386
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1329
1387
  className: "dsm-workbuddy-xdpool-dist-option-hint",
1330
1388
  children: hint
@@ -2285,6 +2343,7 @@ window.__ModuleLoader__.load({
2285
2343
  "row.distBalancedHint": "Draw at random, favouring the account idle longest",
2286
2344
  "row.distSticky": "Per conversation",
2287
2345
  "row.distStickyHint": "One account per chat, new chats take the next account — keeps the prompt cache warm",
2346
+ "row.distRecommended": "Recommended",
2288
2347
  "row.resetCooldowns": "Clear all cooldowns",
2289
2348
  "row.resetCooldownsBusy": "Clearing…",
2290
2349
  "row.resetCooldownsDone": "Cooldowns cleared",
@@ -2437,8 +2496,9 @@ window.__ModuleLoader__.load({
2437
2496
  "row.distRoundRobinHint": "按顺序轮流使用,积分均匀分摊",
2438
2497
  "row.distBalanced": "均衡模式",
2439
2498
  "row.distBalancedHint": "随机抽取,闲置越久的账号被选中概率越高",
2440
- "row.distSticky": "每对话固定(推荐)",
2499
+ "row.distSticky": "每对话固定",
2441
2500
  "row.distStickyHint": "一个对话只用一个账号,新对话自动换下一个账号——保住上游缓存,命中率和速度都更好",
2501
+ "row.distRecommended": "推荐",
2442
2502
  "row.resetCooldowns": "清除所有冷却",
2443
2503
  "row.resetCooldownsBusy": "正在清除…",
2444
2504
  "row.resetCooldownsDone": "冷却已清除",
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "dsh-workbuddy-xdpool",
3
3
  "displayName": "DSH WorkBuddy XD Pool",
4
4
  "description": "Merge every locally signed-in WorkBuddy account into DeepSeek Harness as one auto-failing-over model pool (multi-account rotation, live credits, daily check-in and model catalog).",
5
- "version": "1.7.9",
5
+ "version": "1.8.0",
6
6
  "license": "MIT",
7
7
  "author": "XDTrees",
8
8
  "homepage": "https://github.com/XDTrees/dsh-workbuddy-xdpool#readme",