dsh-group-status 0.2.1 → 0.3.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.
Files changed (92) hide show
  1. package/CHANGELOG.md +53 -0
  2. package/README.en.md +36 -18
  3. package/README.md +39 -19
  4. package/lib/client.js +659 -300
  5. package/lib/client.js.map +1 -1
  6. package/lib/index.js +22 -9
  7. package/lib/types/client/WorkspacePicker.d.ts +2 -2
  8. package/lib/types/client/WorkspacePicker.d.ts.map +1 -1
  9. package/lib/types/client/badge-labels.d.ts +92 -0
  10. package/lib/types/client/badge-labels.d.ts.map +1 -0
  11. package/lib/types/client/badge-labels.js +150 -0
  12. package/lib/types/client/badge-labels.js.map +1 -0
  13. package/lib/types/client/badge-settings.d.ts +17 -1
  14. package/lib/types/client/badge-settings.d.ts.map +1 -1
  15. package/lib/types/client/badge-settings.js +12 -0
  16. package/lib/types/client/badge-settings.js.map +1 -1
  17. package/lib/types/client/facet-store.d.ts +46 -0
  18. package/lib/types/client/facet-store.d.ts.map +1 -0
  19. package/lib/types/client/facet-store.js +123 -0
  20. package/lib/types/client/facet-store.js.map +1 -0
  21. package/lib/types/client/facet-visibility.d.ts +17 -0
  22. package/lib/types/client/facet-visibility.d.ts.map +1 -0
  23. package/lib/types/client/facet-visibility.js +18 -0
  24. package/lib/types/client/facet-visibility.js.map +1 -0
  25. package/lib/types/client/facets.d.ts +265 -0
  26. package/lib/types/client/facets.d.ts.map +1 -0
  27. package/lib/types/client/facets.js +209 -0
  28. package/lib/types/client/facets.js.map +1 -0
  29. package/lib/types/client/group-status.d.ts +43 -47
  30. package/lib/types/client/group-status.d.ts.map +1 -1
  31. package/lib/types/client/group-status.js +76 -90
  32. package/lib/types/client/group-status.js.map +1 -1
  33. package/lib/types/client/index.d.ts +25 -0
  34. package/lib/types/client/index.d.ts.map +1 -1
  35. package/lib/types/client/index.js +28 -0
  36. package/lib/types/client/index.js.map +1 -1
  37. package/lib/types/client/legacy/bridge.d.ts +30 -0
  38. package/lib/types/client/legacy/bridge.d.ts.map +1 -0
  39. package/lib/types/client/legacy/bridge.js +58 -0
  40. package/lib/types/client/legacy/bridge.js.map +1 -0
  41. package/lib/types/client/legacy/register-legacy-facets.d.ts +14 -0
  42. package/lib/types/client/legacy/register-legacy-facets.d.ts.map +1 -0
  43. package/lib/types/client/legacy/register-legacy-facets.js +77 -0
  44. package/lib/types/client/legacy/register-legacy-facets.js.map +1 -0
  45. package/lib/types/client/legacy/schedule.d.ts +49 -0
  46. package/lib/types/client/legacy/schedule.d.ts.map +1 -0
  47. package/lib/types/client/legacy/schedule.js +73 -0
  48. package/lib/types/client/legacy/schedule.js.map +1 -0
  49. package/lib/types/client/legacy/store-source.d.ts +78 -0
  50. package/lib/types/client/legacy/store-source.d.ts.map +1 -0
  51. package/lib/types/client/legacy/store-source.js +82 -0
  52. package/lib/types/client/legacy/store-source.js.map +1 -0
  53. package/lib/types/client/legacy/waiting-review.d.ts +85 -0
  54. package/lib/types/client/legacy/waiting-review.d.ts.map +1 -0
  55. package/lib/types/client/legacy/waiting-review.js +179 -0
  56. package/lib/types/client/legacy/waiting-review.js.map +1 -0
  57. package/lib/types/client/locales.d.ts +2 -4
  58. package/lib/types/client/locales.d.ts.map +1 -1
  59. package/lib/types/client/locales.js +5 -7
  60. package/lib/types/client/locales.js.map +1 -1
  61. package/lib/types/client/rows/GroupBadge.d.ts +18 -25
  62. package/lib/types/client/rows/GroupBadge.d.ts.map +1 -1
  63. package/lib/types/client/rows/GroupBadge.js +45 -106
  64. package/lib/types/client/rows/GroupBadge.js.map +1 -1
  65. package/lib/types/client/rows/Rows.d.ts +8 -4
  66. package/lib/types/client/rows/Rows.d.ts.map +1 -1
  67. package/lib/types/client/rows/Rows.js +6 -6
  68. package/lib/types/client/rows/Rows.js.map +1 -1
  69. package/lib/types/client/rows/WorkspaceBrowser.d.ts +1 -1
  70. package/lib/types/client/rows/WorkspaceBrowser.d.ts.map +1 -1
  71. package/lib/types/client/rows/WorkspaceBrowser.js +30 -23
  72. package/lib/types/client/rows/WorkspaceBrowser.js.map +1 -1
  73. package/lib/types/client/session-actions/ArchiveSession.d.ts +3 -3
  74. package/lib/types/client/session-actions/ArchiveSession.d.ts.map +1 -1
  75. package/lib/types/client/session-actions/ForkSession.d.ts +1 -1
  76. package/lib/types/client/session-actions/ForkSession.d.ts.map +1 -1
  77. package/lib/types/client/session-actions/PinSession.d.ts +2 -2
  78. package/lib/types/client/session-actions/PinSession.d.ts.map +1 -1
  79. package/lib/types/client/session-actions/RenameSession.d.ts +2 -2
  80. package/lib/types/client/session-actions/RenameSession.d.ts.map +1 -1
  81. package/lib/types/client/session-actions/RowActionToast.d.ts +1 -1
  82. package/lib/types/client/session-actions/RowActionToast.d.ts.map +1 -1
  83. package/lib/types/client/tree.d.ts +0 -2
  84. package/lib/types/client/tree.d.ts.map +1 -1
  85. package/lib/types/client/tree.js +0 -2
  86. package/lib/types/client/tree.js.map +1 -1
  87. package/lib/types/index.d.ts +56 -1
  88. package/lib/types/index.d.ts.map +1 -1
  89. package/lib/types/index.js +1 -1
  90. package/lib/types/index.js.map +1 -1
  91. package/package.json +1 -5
  92. package/lib/tsconfig.tsbuildinfo +0 -1
package/lib/client.js CHANGED
@@ -228,6 +228,17 @@ window.__ModuleLoader__.load({
228
228
  */
229
229
  /** Loader 条目 id —— 设置页的寻址键,也是宿主 `describe()` 的 ns。 */
230
230
  const ENTRY_ID = "dsh-group-status";
231
+ /**
232
+ * 一个 facet 是否被设置关掉。
233
+ *
234
+ * - 没有 `settingKey` → 始终参与(缺省是"显示",与其余开关的默认值同源)。
235
+ * - 声明了但不是布尔开关键 → 同样按显示处理:拼错的键不该让一个状态整块消失。
236
+ */
237
+ function facetVisibleBySettings(settings, settingKey) {
238
+ if (settingKey === void 0) return true;
239
+ const value = settings[settingKey];
240
+ return typeof value === "boolean" ? value : true;
241
+ }
231
242
  /** 默认值:与 `Config` 各字段的 schema 默认同源(全部显示,数字阈值 2 即 count=1 时省略)。 */
232
243
  const DEFAULT_BADGE_SETTINGS = Object.freeze({
233
244
  showCompletedInBadge: true,
@@ -298,6 +309,288 @@ window.__ModuleLoader__.load({
298
309
  };
299
310
  }
300
311
  //#endregion
312
+ //#region src/client/facets.ts
313
+ /** 本协议文件的协议版本。provider 必须原样填这个常量。 */
314
+ const GROUP_STATUS_FACET_PROTOCOL_VERSION = 1;
315
+ /** tone 之间的优先级(越大越紧急),用于多层合并时取更高档。 */
316
+ const FACET_TONE_RANK = Object.freeze({
317
+ info: 0,
318
+ warn: 1,
319
+ error: 2
320
+ });
321
+ /**
322
+ * 同 id 冲突时**由来源决胜,与注册顺序无关**。
323
+ *
324
+ * 宿主不保证插件 apply 的先后。过渡期内"本插件代 provider 写的适配器"与"provider
325
+ * 自己注册的 facet"会撞同一个 id:若靠顺序决胜,同一份部署今天显示、明天不显示。
326
+ * `provider` 恒胜 `legacy`:所有者一接入,代管方立刻让位,且不重复计数。迁移因此
327
+ * 不需要协商发布顺序。
328
+ */
329
+ const FACET_ORIGIN_RANK = Object.freeze({
330
+ legacy: 0,
331
+ provider: 1
332
+ });
333
+ /** 一个 facet 的来源;缺省按 `provider`(最保守的假设:别人主动注册就是所有者)。 */
334
+ function facetOrigin(facet) {
335
+ return facet.origin === "legacy" ? "legacy" : "provider";
336
+ }
337
+ /**
338
+ * 检查一个 facet 是否符合协议 v1。
339
+ * @returns 拒绝原因;`undefined` 表示可用。
340
+ */
341
+ function facetRejection(facet) {
342
+ if (typeof facet !== "object" || facet === null) return "not-an-object";
343
+ const candidate = facet;
344
+ if (candidate.protocol !== 1) return "protocol-mismatch";
345
+ if (typeof candidate.id !== "string" || candidate.id === "") return "missing-id";
346
+ if (typeof candidate.subscribe !== "function" || typeof candidate.getSnapshot !== "function" || typeof candidate.fold !== "function") return "missing-methods";
347
+ }
348
+ /**
349
+ * 缺省折果合并:数量相加、色档取更紧急的一档、脉冲取或。
350
+ *
351
+ * ⚠️ 它**不**合并 `data`(本插件不知道 `data` 是什么)。需要合并原始事实的 facet
352
+ * 必须自己实现 {@link GroupStatusFacet.merge}。
353
+ */
354
+ function defaultMergeFold(a, b) {
355
+ const toneA = FACET_TONE_RANK[a.tone] ?? -1;
356
+ const toneB = FACET_TONE_RANK[b.tone] ?? -1;
357
+ return {
358
+ count: a.count + b.count,
359
+ tone: toneB > toneA ? b.tone : a.tone,
360
+ pulse: a.pulse || b.pulse
361
+ };
362
+ }
363
+ /** 创建一张登记表面(测试用;生产走模块级单例 {@link registry})。 */
364
+ function createFacetRegistry() {
365
+ const entries = /* @__PURE__ */ new Map();
366
+ const listeners = /* @__PURE__ */ new Set();
367
+ let sequence = 0;
368
+ let sorted = [];
369
+ const resort = () => {
370
+ sorted = [...entries.values()].sort((x, y) => {
371
+ const declaredX = x.facet.order !== void 0;
372
+ const declaredY = y.facet.order !== void 0;
373
+ if (declaredX !== declaredY) return declaredX ? -1 : 1;
374
+ if (declaredX && declaredY) return x.facet.order - y.facet.order;
375
+ return x.sequence - y.sequence;
376
+ });
377
+ };
378
+ let notifying = false;
379
+ let pending = false;
380
+ /**
381
+ * 通知订阅者,并把**重入合并成一轮补跑**。
382
+ *
383
+ * 订阅者在自己的回调里登记/注销 facet 是完全合法的(消费方常在收到变更后立即
384
+ * 补登记一个派生来源)。直接递归会有两个问题:嵌套深度由订阅者数量决定(两个互相
385
+ * 触发的订阅者就是无界递归),而且中间那轮通知看到的登记面是**改到一半**的。
386
+ * 所以内层调用只置脏标志,外层跑完一轮后再补一轮,直到没有新的改动 —— 每个订阅者
387
+ * 都看得到最终状态,递归深度恒为 1。
388
+ */
389
+ const notify = () => {
390
+ if (notifying) {
391
+ pending = true;
392
+ return;
393
+ }
394
+ notifying = true;
395
+ try {
396
+ do {
397
+ pending = false;
398
+ for (const listener of [...listeners]) try {
399
+ listener();
400
+ } catch {}
401
+ } while (pending);
402
+ } finally {
403
+ notifying = false;
404
+ pending = false;
405
+ }
406
+ };
407
+ return {
408
+ register: (facet) => {
409
+ const reason = facetRejection(facet);
410
+ const id = typeof facet?.id === "string" ? facet.id : "";
411
+ if (reason !== void 0) return {
412
+ ok: false,
413
+ id,
414
+ reason
415
+ };
416
+ const existing = entries.get(id);
417
+ const nextRank = FACET_ORIGIN_RANK[facetOrigin(facet)];
418
+ if (existing !== void 0 && FACET_ORIGIN_RANK[facetOrigin(existing.facet)] > nextRank) return {
419
+ ok: false,
420
+ id,
421
+ reason: "superseded"
422
+ };
423
+ sequence += 1;
424
+ entries.set(id, {
425
+ facet,
426
+ sequence
427
+ });
428
+ resort();
429
+ notify();
430
+ return {
431
+ ok: true,
432
+ id
433
+ };
434
+ },
435
+ remove: (id, facet) => {
436
+ const current = entries.get(id);
437
+ if (current === void 0) return false;
438
+ if (facet !== void 0 && current.facet !== facet) return false;
439
+ entries.delete(id);
440
+ resort();
441
+ notify();
442
+ return true;
443
+ },
444
+ list: () => sorted,
445
+ subscribe: (listener) => {
446
+ listeners.add(listener);
447
+ return () => {
448
+ listeners.delete(listener);
449
+ };
450
+ }
451
+ };
452
+ }
453
+ /**
454
+ * 生产用的模块级单例登记表面。
455
+ *
456
+ * 与 {@link createFacetRegistry} 分开,是为了让测试能对着一张干净的表断言协议行为,
457
+ * 而不会被别的用例或别的插件登记的 facet 污染。
458
+ */
459
+ const registry = createFacetRegistry();
460
+ /**
461
+ * 本插件 client 入口对外的登记函数(provider 侧唯一需要认识的符号)。
462
+ *
463
+ * @param facet - 待登记的 facet。
464
+ * @returns 注销函数;**注册失败时返回一个空操作**,同时把原因写进 console——
465
+ * 返回值形状保持稳定,provider 的 dispose 路径不必区分成败。
466
+ */
467
+ function registerGroupStatusFacet(facet) {
468
+ const result = registry.register(facet);
469
+ if (!result.ok) {
470
+ if (!(result.reason === "superseded" && facetOrigin(facet) === "legacy")) reportRejection(result);
471
+ return () => void 0;
472
+ }
473
+ return () => {
474
+ registry.remove(result.id, facet);
475
+ };
476
+ }
477
+ /**
478
+ * 把注册失败写成一条可诊断的日志。
479
+ *
480
+ * 刻意不抛错:状态显示是增强功能,一个插件协议填错不该把宿主界面渲染打断;
481
+ * 但也不静默——"注册了却没显示"必须能查出原因。
482
+ */
483
+ function reportRejection(rejection) {
484
+ const detail = rejection.reason === "protocol-mismatch" ? `本插件只接受 protocol: 1` : rejection.reason === "missing-methods" ? "subscribe / getSnapshot / fold 三个方法必须齐备" : rejection.reason === "missing-id" ? "id 必须是非空字符串,建议 '<包名>.<状态名>'" : rejection.reason === "superseded" ? "同 id 上已有更高来源的登记;两个不同插件用了同一个 id,请各自换名" : "facet 必须是一个对象";
485
+ console.warn(`dsh-group-status: 拒绝登记状态 facet "${rejection.id}"(${rejection.reason}):${detail}`);
486
+ }
487
+ //#endregion
488
+ //#region src/client/facet-store.ts
489
+ /**
490
+ * facet 的**消费侧**:把「登记表面 + 每个 facet 自己的外部存储」压成一份 React 可订阅的
491
+ * 单一存储。
492
+ *
493
+ * ## 为什么必须是一个存储,而不是逐个 hook
494
+ *
495
+ * 见 `./facets.ts` 的文件头:hook 数量在同一个组件里必须逐次渲染恒定,而 facet 是运行
496
+ * 时注册/注销的。所以这里只暴露一个**版本号**(数字,引用天然稳定)给
497
+ * `useSyncExternalStore`,实际的快照读取与折叠都发生在渲染期。
498
+ *
499
+ * ## 快照读取为什么放在渲染期
500
+ *
501
+ * `getSnapshot` 必须返回稳定引用,而「每个 facet 的快照」是一个对象图:为它造一个
502
+ * 缓存就需要判定失效,而判定失效又要读快照本身。改成「版本号变 → 重新渲染 → 现读现折」
503
+ * 之后,缓存问题消失,代价是一次 O(facet × 组内会话) 的纯计算,量级与原有实现相同。
504
+ *
505
+ * @module dsh-group-status/client/facet-store
506
+ */
507
+ /**
508
+ * 创建 facet 存储。
509
+ *
510
+ * 对登记面做一次订阅,并在登记变化时**重新接线**各 facet 的 `subscribe`:provider 热替换
511
+ * 实现(同 id 再注册)或运行中启用/停用插件,都只会走到这里一次,不会留下野订阅。
512
+ *
513
+ * @param source - 登记表面;缺省用模块级单例,测试传入自建实例以隔离。
514
+ */
515
+ function createFacetStore(source = registry) {
516
+ const listeners = /* @__PURE__ */ new Set();
517
+ const links = /* @__PURE__ */ new Map();
518
+ let version = 0;
519
+ let disposed = false;
520
+ /** 任一来源变化都要让消费方重新渲染,因此统一 bump。 */
521
+ const bump = () => {
522
+ if (disposed) return;
523
+ version += 1;
524
+ for (const listener of [...listeners]) try {
525
+ listener();
526
+ } catch {}
527
+ };
528
+ /** 把 links 对齐到当前登记项:新增接线、移除退订、替换重接。 */
529
+ const rewire = () => {
530
+ if (disposed) return;
531
+ const current = new Map(source.list().map((entry) => [entry.facet.id, entry.facet]));
532
+ for (const [id, link] of [...links]) {
533
+ if (current.get(id) === link.facet) continue;
534
+ try {
535
+ link.unsubscribe();
536
+ } catch {}
537
+ links.delete(id);
538
+ }
539
+ for (const [id, facet] of current) {
540
+ if (links.has(id)) continue;
541
+ links.set(id, {
542
+ facet,
543
+ unsubscribe: facet.subscribe(bump)
544
+ });
545
+ }
546
+ };
547
+ const unsubscribeRegistry = source.subscribe(() => {
548
+ rewire();
549
+ bump();
550
+ });
551
+ rewire();
552
+ return {
553
+ subscribe: (listener) => {
554
+ listeners.add(listener);
555
+ return () => {
556
+ listeners.delete(listener);
557
+ };
558
+ },
559
+ getVersion: () => version,
560
+ getServerVersion: () => 0,
561
+ list: () => source.list(),
562
+ dispose: () => {
563
+ if (disposed) return;
564
+ disposed = true;
565
+ for (const link of links.values()) try {
566
+ link.unsubscribe();
567
+ } catch {}
568
+ links.clear();
569
+ unsubscribeRegistry();
570
+ listeners.clear();
571
+ }
572
+ };
573
+ }
574
+ /**
575
+ * 生产用的单例存储:消费侧(`WorkspaceBrowser`)订阅它,
576
+ * `apply` 的 effect 结束时用 {@link disposeFacetStore} 释放全部接线。
577
+ *
578
+ * 单例而非每次渲染新建,是因为它持有**对每个 facet 存储的订阅**;随渲染重建会把
579
+ * provider 的 store 拖进无谓的退订/重订,也让"快照在两个文件夹行之间读数不同"成为可能。
580
+ */
581
+ let singleton;
582
+ /** 取(并首次创建)单例存储。 */
583
+ function facetStore() {
584
+ if (singleton === void 0) singleton = createFacetStore();
585
+ return singleton;
586
+ }
587
+ /** 释放单例存储(插件卸载时调用;下次 {@link facetStore} 会重建)。 */
588
+ function disposeFacetStore() {
589
+ if (singleton === void 0) return;
590
+ singleton.dispose();
591
+ singleton = void 0;
592
+ }
593
+ //#endregion
301
594
  //#region src/client/badge-field.ts
302
595
  /** 卡片字段名(与 `src/index.ts` 的 `Config` 同名同义)。 */
303
596
  const BADGE_FIELDS = {
@@ -365,8 +658,8 @@ window.__ModuleLoader__.load({
365
658
  }
366
659
  ];
367
660
  //#endregion
368
- //#region \0dsh-css:/home/wujue/workspace/dsh-plugins/dsh-group-status/src/client/BadgeSettingsCard.module.css.mjs
369
- const css$4 = ".EgC0Ja_section{min-width:0;padding:16px 0}.EgC0Ja_section:last-child{padding-bottom:0}.EgC0Ja_heading{color:var(--dsw-alias-label-primary);margin:0;font-size:13px;font-weight:600;line-height:1.5}.EgC0Ja_badgeField{gap:6px;padding:12px 0;display:grid}.EgC0Ja_badgeToggleRow{color:var(--dsw-alias-label-primary);justify-content:space-between;align-items:flex-start;gap:16px;font-size:13px;line-height:1.5;display:flex}.EgC0Ja_badgeToggleLabel{flex:1;min-width:0}.EgC0Ja_badgeNumberControl{align-items:center;gap:4px;display:flex}.EgC0Ja_badgeNumberButton{width:28px;height:28px;color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-elevated,#2a2a2a);border:1px solid var(--dsw-alias-border-primary,#555);cursor:pointer;user-select:none;border-radius:6px;justify-content:center;align-items:center;padding:0;font-size:16px;font-weight:600;line-height:1;display:flex}.EgC0Ja_badgeNumberButton:hover:not(:disabled){border-color:var(--dsw-alias-border-focus,#0078d4);background:var(--dsw-alias-bg-hover,#3a3a3a)}.EgC0Ja_badgeNumberButton:active:not(:disabled){background:var(--dsw-alias-bg-active,#4a4a4a)}.EgC0Ja_badgeNumberButton:disabled{opacity:.3;cursor:not-allowed}.EgC0Ja_badgeNumberValue{text-align:center;min-width:24px;color:var(--dsw-alias-label-primary);font-size:13px;font-weight:500}.EgC0Ja_hint{color:var(--dsw-alias-label-tertiary);margin:0;font-size:12px;line-height:1.5}";
661
+ //#region \0dsh-css:src/client/BadgeSettingsCard.module.css.mjs
662
+ const css$4 = ".dhp54G_section{min-width:0;padding:16px 0}.dhp54G_section:last-child{padding-bottom:0}.dhp54G_heading{color:var(--dsw-alias-label-primary);margin:0;font-size:13px;font-weight:600;line-height:1.5}.dhp54G_badgeField{gap:6px;padding:12px 0;display:grid}.dhp54G_badgeToggleRow{color:var(--dsw-alias-label-primary);justify-content:space-between;align-items:flex-start;gap:16px;font-size:13px;line-height:1.5;display:flex}.dhp54G_badgeToggleLabel{flex:1;min-width:0}.dhp54G_badgeNumberControl{align-items:center;gap:4px;display:flex}.dhp54G_badgeNumberButton{width:28px;height:28px;color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-elevated,#2a2a2a);border:1px solid var(--dsw-alias-border-primary,#555);cursor:pointer;user-select:none;border-radius:6px;justify-content:center;align-items:center;padding:0;font-size:16px;font-weight:600;line-height:1;display:flex}.dhp54G_badgeNumberButton:hover:not(:disabled){border-color:var(--dsw-alias-border-focus,#0078d4);background:var(--dsw-alias-bg-hover,#3a3a3a)}.dhp54G_badgeNumberButton:active:not(:disabled){background:var(--dsw-alias-bg-active,#4a4a4a)}.dhp54G_badgeNumberButton:disabled{opacity:.3;cursor:not-allowed}.dhp54G_badgeNumberValue{text-align:center;min-width:24px;color:var(--dsw-alias-label-primary);font-size:13px;font-weight:500}.dhp54G_hint{color:var(--dsw-alias-label-tertiary);margin:0;font-size:12px;line-height:1.5}";
370
663
  const tagId$4 = "dsh-group-status/BadgeSettingsCard.module.css";
371
664
  if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$4) + "]") === null) {
372
665
  const tag = document.createElement("style");
@@ -376,15 +669,15 @@ window.__ModuleLoader__.load({
376
669
  document.head.appendChild(tag);
377
670
  }
378
671
  var BadgeSettingsCard_module_css_default = {
379
- "badgeField": "EgC0Ja_badgeField",
380
- "badgeNumberButton": "EgC0Ja_badgeNumberButton",
381
- "badgeNumberControl": "EgC0Ja_badgeNumberControl",
382
- "badgeNumberValue": "EgC0Ja_badgeNumberValue",
383
- "badgeToggleLabel": "EgC0Ja_badgeToggleLabel",
384
- "badgeToggleRow": "EgC0Ja_badgeToggleRow",
385
- "heading": "EgC0Ja_heading",
386
- "hint": "EgC0Ja_hint",
387
- "section": "EgC0Ja_section"
672
+ "badgeField": "dhp54G_badgeField",
673
+ "badgeNumberButton": "dhp54G_badgeNumberButton",
674
+ "badgeNumberControl": "dhp54G_badgeNumberControl",
675
+ "badgeNumberValue": "dhp54G_badgeNumberValue",
676
+ "badgeToggleLabel": "dhp54G_badgeToggleLabel",
677
+ "badgeToggleRow": "dhp54G_badgeToggleRow",
678
+ "heading": "dhp54G_heading",
679
+ "hint": "dhp54G_hint",
680
+ "section": "dhp54G_section"
388
681
  };
389
682
  //#endregion
390
683
  //#region src/client/BadgeSettingsCard.tsx
@@ -889,8 +1182,6 @@ window.__ModuleLoader__.load({
889
1182
  * @param rowState - registry-global pin and archive sets plus the archived filter.
890
1183
  * @param statuses - unified UI status by Session.
891
1184
  * @param view - local expansion arrays.
892
- * @param waitingReviewSummary - dsh-gitea-dispatch waiting map (session id → waiting entry).
893
- * @param scheduleSummary - dsh-later schedule map (session id → schedule entry).
894
1185
  * @returns group sections in render order.
895
1186
  */
896
1187
  function deriveGroups(list, workspaces, rowState, statuses, view) {
@@ -1469,15 +1760,12 @@ window.__ModuleLoader__.load({
1469
1760
  function workspaceDisplayTitle(title, localizedDefault) {
1470
1761
  return title === "default-workspace" ? localizedDefault : title;
1471
1762
  }
1472
- //#endregion
1473
- //#region src/client/group-status.ts
1474
1763
  /**
1475
1764
  * Zero-valued sentinel for {@link GroupLiveStatus}.
1476
1765
  *
1477
- * Used when a group is expanded (badge is hidden, so aggregation is wasted
1478
- * work) or when no cross-plugin data is available. Kept as a frozen constant
1479
- * so the reference is stable across renders — `Object.is` comparisons in
1480
- * React selectors never trigger spurious re-renders on it.
1766
+ * Used when a group has nothing to report. Kept as a frozen constant so the
1767
+ * reference is stable across renders — `Object.is` comparisons in React selectors
1768
+ * never trigger spurious re-renders on it.
1481
1769
  */
1482
1770
  const EMPTY_LIVE_STATUS = Object.freeze({
1483
1771
  running: 0,
@@ -1486,24 +1774,15 @@ window.__ModuleLoader__.load({
1486
1774
  question: 0,
1487
1775
  done: 0,
1488
1776
  subagents: 0,
1489
- scheduledSessions: 0,
1490
- scheduledCount: 0,
1491
- scheduleState: void 0,
1492
- waitingReviewCount: 0,
1493
- waitingReviewState: void 0
1777
+ facets: /* @__PURE__ */ new Map()
1494
1778
  });
1495
1779
  /**
1496
- * Merge two {@link GroupLiveStatus} objects by summing counters and taking the
1497
- * highest-priority state (overdue > urgent > scheduled for schedule; overdue > waiting
1498
- * for waiting-review).
1780
+ * 合并两份分组折果:本插件自己的计数相加,外部状态来源**按 facet 各自合并**。
1499
1781
  *
1500
- * Used for multi-level aggregation: when a parent group is collapsed, its badge
1501
- * must reflect all descendant sessions, not just direct members.
1782
+ * 用于多层聚合:折叠的父文件夹要覆盖整棵子树,而子树里每一层都已各自折过一遍。
1783
+ * `FacetFold.data` 怎么合归该 facet 自己决定,本模块从不解释。
1502
1784
  */
1503
1785
  function mergeLiveStatus(a, b) {
1504
- const priorityOf = (s) => s === "overdue" ? 3 : s === "urgent" ? 2 : s === "scheduled" ? 1 : 0;
1505
- const scheduleState = priorityOf(a.scheduleState) >= priorityOf(b.scheduleState) ? a.scheduleState : b.scheduleState;
1506
- const waitingReviewState = (a.waitingReviewState === "overdue" ? 2 : a.waitingReviewState === "waiting" ? 1 : 0) >= (b.waitingReviewState === "overdue" ? 2 : b.waitingReviewState === "waiting" ? 1 : 0) ? a.waitingReviewState : b.waitingReviewState;
1507
1786
  return {
1508
1787
  running: a.running + b.running,
1509
1788
  approval: a.approval + b.approval,
@@ -1511,35 +1790,36 @@ window.__ModuleLoader__.load({
1511
1790
  question: a.question + b.question,
1512
1791
  done: a.done + b.done,
1513
1792
  subagents: a.subagents + b.subagents,
1514
- scheduledSessions: a.scheduledSessions + b.scheduledSessions,
1515
- scheduledCount: a.scheduledCount + b.scheduledCount,
1516
- scheduleState,
1517
- waitingReviewCount: a.waitingReviewCount + b.waitingReviewCount,
1518
- waitingReviewState
1793
+ facets: mergeFacets(a.facets, b.facets)
1519
1794
  };
1520
1795
  }
1521
- /**
1522
- * Strip the `session-` / `webhook-` prefix from a Session id.
1523
- *
1524
- * dsh-gitea-dispatch publishes its waiting map keyed by the host SessionId, but
1525
- * its HTTP endpoints key by this normalized form. Accepting both here keeps the
1526
- * badge working across that publisher's conventions without knowing which is in
1527
- * play.
1528
- */
1529
- function stripSessionPrefix(id) {
1530
- return id.replace(/^(?:session|webhook)-/, "");
1796
+ /** 按 facet id 合并两侧折果;只有一侧有的来源原样带过。 */
1797
+ function mergeFacets(a, b) {
1798
+ if (a.size === 0) return b;
1799
+ if (b.size === 0) return a;
1800
+ const merged = new Map(a);
1801
+ for (const [id, entry] of b) {
1802
+ const own = merged.get(id);
1803
+ merged.set(id, own === void 0 ? entry : {
1804
+ facet: own.facet,
1805
+ fold: own.facet.merge?.(own.fold, entry.fold) ?? defaultMergeFold(own.fold, entry.fold)
1806
+ });
1807
+ }
1808
+ return merged;
1531
1809
  }
1532
1810
  /**
1533
1811
  * Fold every member row of a group into counts so a collapsed folder can hint at
1534
1812
  * hidden live sessions. Mirrors the session-row status priority (pending
1535
1813
  * outranks running outranks completed) at the counting level; labels resolve with
1536
1814
  * locale at render time.
1815
+ *
1537
1816
  * @param nodes - member rows of the group.
1538
- * @param sessionIds - the same members' Session ids (schedule and waiting facts are keyed by id).
1539
- * @param waitingReviewSummary - dsh-gitea-dispatch waiting map, when that plugin is loaded.
1540
- * @param scheduleSummary - dsh-later schedule map, when that plugin is loaded.
1817
+ * @param sessionIds - the same members' Session ids (facets address their own data by id).
1818
+ * @param facets - the registered status sources to fold over this group.
1819
+ * @param now - one clock reading for the whole pass, so sibling facets cannot disagree
1820
+ * about which deadlines are still ahead.
1541
1821
  */
1542
- function summarizeGroupStatus(nodes, sessionIds, waitingReviewSummary, scheduleSummary) {
1822
+ function summarizeGroupStatus(nodes, sessionIds, facets = [], now = Date.now()) {
1543
1823
  let running = 0;
1544
1824
  let approval = 0;
1545
1825
  let planReview = 0;
@@ -1548,50 +1828,48 @@ window.__ModuleLoader__.load({
1548
1828
  let subagents = 0;
1549
1829
  for (const n of nodes) {
1550
1830
  if (n.blank || n.archived) continue;
1831
+ const hasSubagents = n.runningSubagentCount > 0;
1551
1832
  if (n.pendingInteraction === "approval") {
1552
1833
  approval += 1;
1834
+ subagents += n.runningSubagentCount;
1553
1835
  continue;
1554
1836
  }
1555
1837
  if (n.pendingInteraction === "plan-review") {
1556
1838
  planReview += 1;
1839
+ subagents += n.runningSubagentCount;
1557
1840
  continue;
1558
1841
  }
1559
1842
  if (n.pendingInteraction === "question") {
1560
1843
  question += 1;
1844
+ subagents += n.runningSubagentCount;
1561
1845
  continue;
1562
1846
  }
1563
- if (n.running) running += 1;
1564
- else if (n.completed) done += 1;
1565
- subagents += n.runningSubagentCount;
1566
- }
1567
- let scheduledSessions = 0;
1568
- let scheduledCount = 0;
1569
- let scheduleState = void 0;
1570
- if (scheduleSummary && sessionIds.length > 0) {
1571
- let totalSessions = 0;
1572
- let totalCount = 0;
1573
- let highestPriority = 0;
1574
- for (const id of sessionIds) {
1575
- const entry = scheduleSummary.get(id);
1576
- if (entry) {
1577
- totalSessions += 1;
1578
- totalCount += entry.count;
1579
- const priority = entry.state === "overdue" ? 3 : entry.state === "urgent" ? 2 : 1;
1580
- if (priority > highestPriority) highestPriority = priority;
1581
- }
1847
+ if (n.running) {
1848
+ running += 1;
1849
+ subagents += n.runningSubagentCount;
1850
+ continue;
1582
1851
  }
1583
- scheduledSessions = totalSessions;
1584
- scheduledCount = totalCount;
1585
- scheduleState = highestPriority === 3 ? "overdue" : highestPriority === 2 ? "urgent" : highestPriority === 1 ? "scheduled" : void 0;
1852
+ if (hasSubagents) {
1853
+ subagents += n.runningSubagentCount;
1854
+ continue;
1855
+ }
1856
+ if (n.completed) done += 1;
1586
1857
  }
1587
- let waitingReviewCount = 0;
1588
- let waitingReviewState = void 0;
1589
- if (waitingReviewSummary && sessionIds.length > 0) for (const id of sessionIds) {
1590
- const entry = waitingReviewSummary.get(id) ?? waitingReviewSummary.get(stripSessionPrefix(id));
1591
- if (entry === void 0) continue;
1592
- waitingReviewCount += 1;
1593
- if (entry.state === "overdue") waitingReviewState = "overdue";
1594
- else if (waitingReviewState === void 0) waitingReviewState = "waiting";
1858
+ const folded = /* @__PURE__ */ new Map();
1859
+ for (const { facet } of facets) {
1860
+ let snapshot;
1861
+ try {
1862
+ snapshot = facet.getSnapshot();
1863
+ } catch {
1864
+ snapshot = void 0;
1865
+ }
1866
+ try {
1867
+ const result = facet.fold(snapshot, sessionIds, now);
1868
+ if (result !== void 0 && result.count > 0) folded.set(facet.id, {
1869
+ facet,
1870
+ fold: result
1871
+ });
1872
+ } catch {}
1595
1873
  }
1596
1874
  return {
1597
1875
  running,
@@ -1600,11 +1878,7 @@ window.__ModuleLoader__.load({
1600
1878
  question,
1601
1879
  done,
1602
1880
  subagents,
1603
- scheduledSessions,
1604
- scheduledCount,
1605
- scheduleState,
1606
- waitingReviewCount,
1607
- waitingReviewState
1881
+ facets: folded
1608
1882
  };
1609
1883
  }
1610
1884
  /**
@@ -1618,12 +1892,12 @@ window.__ModuleLoader__.load({
1618
1892
  * every collapsed parent undercount by the size of its expanded descendants.
1619
1893
  *
1620
1894
  * @param groups - derived groups, each with its member rows already populated.
1621
- * @param waitingReviewSummary - dsh-gitea-dispatch waiting map, when that plugin is loaded.
1622
- * @param scheduleSummary - dsh-later schedule map, when that plugin is loaded.
1895
+ * @param facets - registered sources, already filtered by the visibility settings.
1896
+ * @param now - one clock reading shared by every group in the pass.
1623
1897
  */
1624
- function directStatusesForGroups(groups, waitingReviewSummary, scheduleSummary) {
1898
+ function directStatusesForGroups(groups, facets = [], now = Date.now()) {
1625
1899
  const map = /* @__PURE__ */ new Map();
1626
- for (const group of groups) map.set(group.key, summarizeGroupStatus(group.sessions, group.sessions.map((s) => s.id), waitingReviewSummary, scheduleSummary));
1900
+ for (const group of groups) map.set(group.key, summarizeGroupStatus(group.sessions, group.sessions.map((s) => s.id), facets, now));
1627
1901
  return map;
1628
1902
  }
1629
1903
  /**
@@ -1673,66 +1947,69 @@ window.__ModuleLoader__.load({
1673
1947
  }));
1674
1948
  }
1675
1949
  //#endregion
1676
- //#region \0dsh-css:/home/wujue/workspace/dsh-plugins/dsh-group-status/src/client/rows/Rows.module.css.mjs
1677
- const css$3 = ".eCE9Ta_projectRow,.eCE9Ta_sessionRow{border-radius:var(--dsw-radius-md);padding:0 8px;cursor:pointer;user-select:none;color:var(--dsw-alias-label-primary);align-items:center;gap:6px;padding-inline-start:calc(8px + var(--dsh-workspace-indent,0px));display:flex}.eCE9Ta_projectRow:hover,.eCE9Ta_sessionRow:hover,.eCE9Ta_sessionRow.eCE9Ta_selected{background:var(--dsw-alias-interactive-bg-hover)}.eCE9Ta_searchResultRow{box-sizing:border-box;border-radius:var(--dsw-radius-lg);cursor:pointer;text-align:left;width:100%;min-height:48px;color:var(--dsw-alias-label-primary);background:0 0;border:none;flex-direction:column;align-items:stretch;padding:4px 8px;display:flex}.eCE9Ta_searchResultRow:hover,.eCE9Ta_searchResultRow.eCE9Ta_selected{background:var(--dsw-alias-interactive-bg-hover)}.eCE9Ta_searchResultHeading{align-items:center;min-width:0;display:flex}.eCE9Ta_searchResultTitle{text-overflow:ellipsis;white-space:nowrap;flex:0 auto;min-width:0;margin-left:4px;font-size:14px;line-height:20px;overflow:hidden}.eCE9Ta_searchResultMeta{align-items:center;gap:6px;min-width:0;margin-left:20px;display:flex}.eCE9Ta_searchResultWorkspace,.eCE9Ta_searchResultSnippet{text-overflow:ellipsis;white-space:nowrap;font-size:12px;line-height:17px;overflow:hidden}.eCE9Ta_searchResultWorkspace{max-width:40%;color:var(--dsw-alias-label-tertiary);flex:none}.eCE9Ta_searchResultWorkspace:only-child{max-width:100%}.eCE9Ta_searchResultSnippet{min-width:0;color:var(--dsw-alias-label-secondary);flex:1}.eCE9Ta_projectRow{box-sizing:border-box;align-items:center;height:34px}.eCE9Ta_projectRow .eCE9Ta_rowActions{height:20px}.eCE9Ta_sessionRow{gap:0;height:32px}.eCE9Ta_sessionRow .eCE9Ta_title{margin:0 6px 0 4px}.eCE9Ta_slot{width:16px;height:20px;color:var(--dsw-alias-label-tertiary);flex:none;justify-content:center;align-items:center;display:inline-flex}.eCE9Ta_visuallyHidden{clip:rect(0 0 0 0);white-space:nowrap;width:1px;height:1px;position:absolute;overflow:hidden}.eCE9Ta_folderActive{color:var(--dsw-alias-state-business-primary)}.eCE9Ta_projectRow .eCE9Ta_chevron{display:none}.eCE9Ta_projectRow:hover .eCE9Ta_chevron{display:inline-flex}.eCE9Ta_projectRow:hover .eCE9Ta_folder{display:none}.eCE9Ta_arrow{transition:transform .15s var(--ds-ease-in-out)}.eCE9Ta_arrowOpen{transform:rotate(90deg)}.eCE9Ta_projectText{flex-direction:column;flex:1;gap:2px;min-width:0;display:flex}.eCE9Ta_title{text-overflow:ellipsis;white-space:nowrap;min-width:0;font-size:14px;line-height:20px;overflow:hidden}.eCE9Ta_renameInput{border:.5px solid var(--dsw-alias-border-l4);border-radius:var(--dsw-radius-sm);background:var(--dsw-alias-button-elevated-fill);min-width:0;color:inherit;outline:none;padding:0 2px;font-size:14px;line-height:20px}.eCE9Ta_sessionRow .eCE9Ta_title{flex:1}.eCE9Ta_sessionRow .eCE9Ta_title[data-scrolled]{mask-image:linear-gradient(90deg,#0000,#000 12px)}.eCE9Ta_sessionRow .eCE9Ta_title[data-clipped]{mask-image:linear-gradient(270deg,#0000,#000 12px)}.eCE9Ta_sessionRow .eCE9Ta_title[data-scrolled][data-clipped]{mask-image:linear-gradient(90deg,#0000,#000 12px calc(100% - 12px),#0000)}.eCE9Ta_meta{text-overflow:ellipsis;white-space:nowrap;color:var(--dsw-alias-label-tertiary);font-size:12px;line-height:20px;overflow:hidden}.eCE9Ta_time{color:var(--dsw-alias-label-tertiary);flex:none;font-size:10px;line-height:16px}.eCE9Ta_pinIndicator{width:16px;height:20px;color:var(--dsw-alias-label-caption);flex:none;justify-content:center;align-items:center;margin-left:6px;display:inline-flex}.eCE9Ta_sessionRow.eCE9Ta_archived .eCE9Ta_title,.eCE9Ta_searchResultRow.eCE9Ta_archived .eCE9Ta_searchResultTitle,.eCE9Ta_searchResultRow.eCE9Ta_archived .eCE9Ta_searchResultWorkspace,.eCE9Ta_searchResultRow.eCE9Ta_archived .eCE9Ta_searchResultSnippet{color:var(--dsw-alias-label-caption)}.eCE9Ta_dot{flex:none}.eCE9Ta_rowActions{flex:none;align-items:center;gap:10px;display:none}.eCE9Ta_projectRow:hover .eCE9Ta_rowActions,.eCE9Ta_sessionRow:hover .eCE9Ta_rowActions,.eCE9Ta_searchResultRow:hover .eCE9Ta_rowActions,.eCE9Ta_projectRow.eCE9Ta_menuOpen .eCE9Ta_rowActions,.eCE9Ta_sessionRow.eCE9Ta_menuOpen .eCE9Ta_rowActions{display:inline-flex}.eCE9Ta_searchResultHeading .eCE9Ta_rowActions{margin-left:auto}.eCE9Ta_sessionRow:hover .eCE9Ta_time,.eCE9Ta_sessionRow.eCE9Ta_menuOpen .eCE9Ta_time,.eCE9Ta_sessionRow:hover .eCE9Ta_pinIndicator,.eCE9Ta_sessionRow.eCE9Ta_menuOpen .eCE9Ta_pinIndicator{display:none}@media (hover:hover){.eCE9Ta_sessionRow:hover .eCE9Ta_title,.eCE9Ta_sessionRow.eCE9Ta_menuOpen .eCE9Ta_title{text-overflow:clip}}.eCE9Ta_projectRow.eCE9Ta_menuOpen,.eCE9Ta_sessionRow.eCE9Ta_menuOpen{background:var(--dsw-alias-interactive-bg-hover)}.eCE9Ta_sessionRow.eCE9Ta_dropBefore,.eCE9Ta_sessionRow.eCE9Ta_dropAfter{position:relative}.eCE9Ta_sessionRow.eCE9Ta_dropBefore:before,.eCE9Ta_sessionRow.eCE9Ta_dropAfter:after{content:\"\";z-index:1;background:linear-gradient(55deg, transparent calc(50% - 1px), var(--dsw-alias-state-business-primary) calc(50% - 1px) calc(50% + 1px), transparent calc(50% + 1px)) 0 0 / 5px 7px no-repeat, linear-gradient(125deg, transparent calc(50% - 1px), var(--dsw-alias-state-business-primary) calc(50% - 1px) calc(50% + 1px), transparent calc(50% + 1px)) 0 5px / 5px 7px no-repeat, linear-gradient(var(--dsw-alias-state-business-primary) 0 0) 4px 5px / calc(100% - 4px) 2px no-repeat;pointer-events:none;height:12px;position:absolute;left:0;right:4px}.eCE9Ta_sessionRow.eCE9Ta_dropBefore:before{top:-7px}.eCE9Ta_sessionRow.eCE9Ta_dropAfter:after{bottom:-7px}.eCE9Ta_hoverContent{flex-direction:column;gap:8px;display:flex}.eCE9Ta_hoverTitle{color:var(--dsw-alias-label-primary);overflow-wrap:break-word;font-size:14px;line-height:20px}.eCE9Ta_hoverPath{color:var(--dsw-alias-label-secondary);word-break:break-all;font-size:12px;line-height:16px}.eCE9Ta_hoverTime{color:var(--dsw-alias-label-secondary);font-size:12px;line-height:16px}.eCE9Ta_hoverStatus{color:var(--dsw-alias-label-tertiary);align-items:center;gap:8px;font-size:12px;line-height:20px;display:flex}.eCE9Ta_groupBadge{flex:none;align-items:center;gap:3px;margin-inline-start:4px;display:inline-flex}.eCE9Ta_groupBadgeCount{color:var(--dsw-alias-label-tertiary);border:1px solid;border-radius:6px;padding:0 3px;font-size:9px;line-height:12px}@keyframes eCE9Ta_groupBadgePulse{0%,to{opacity:1}50%{opacity:.6}}.eCE9Ta_groupBadgeUrgent{animation:2s ease-in-out infinite eCE9Ta_groupBadgePulse}.eCE9Ta_hoverArchived svg{flex-shrink:0;margin:0 -4px}.eCE9Ta_iconButton{border-radius:var(--dsw-radius-xs);cursor:pointer;width:16px;height:16px;color:var(--dsw-alias-label-tertiary);background:0 0;border:none;flex:none;justify-content:center;align-items:center;padding:0;display:inline-flex}.eCE9Ta_iconButton:hover{color:var(--dsw-alias-label-primary)}.eCE9Ta_chevron{color:var(--dsw-alias-label-caption)}@media (prefers-reduced-motion:reduce){.eCE9Ta_arrow{transition:none;animation:none}}";
1678
- const tagId$3 = "dsh-group-status/Rows.module.css";
1679
- if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$3) + "]") === null) {
1680
- const tag = document.createElement("style");
1681
- tag.dataset.plugin = "dsh-group-status";
1682
- tag.dataset.pluginCss = tagId$3;
1683
- tag.textContent = css$3;
1684
- document.head.appendChild(tag);
1950
+ //#region src/client/facet-visibility.ts
1951
+ /**
1952
+ * 按用户设置筛掉不参与折叠的状态来源。
1953
+ *
1954
+ * 单独成文是因为它属于**徽标自身**的可见性规则,而不是某个来源的适配:来源只在
1955
+ * `facet.settingKey` 上声明"我受哪个开关管",开关的语义与过滤时机归本插件。
1956
+ *
1957
+ * 过滤发生在**折叠之前**而不是渲染阶段:徽标数字、图标、`aria-label`、悬停明细与
1958
+ * "要不要画徽标"因此天然同源。放到渲染阶段会出现"label 里有、计数里没有",而那正是
1959
+ * 徽标最容易被怀疑撒谎的地方。
1960
+ *
1961
+ * @module dsh-group-status/client/facet-visibility
1962
+ */
1963
+ /** 筛出设置允许显示的登记项;顺序保持登记面的顺序(已按 `order` 排好)。 */
1964
+ function visibleFacets(entries, settings) {
1965
+ return entries.filter((entry) => facetVisibleBySettings(settings, entry.facet.settingKey));
1685
1966
  }
1686
- var Rows_module_css_default = {
1687
- "archived": "eCE9Ta_archived",
1688
- "arrow": "eCE9Ta_arrow",
1689
- "arrowOpen": "eCE9Ta_arrowOpen",
1690
- "chevron": "eCE9Ta_chevron",
1691
- "dot": "eCE9Ta_dot",
1692
- "dropAfter": "eCE9Ta_dropAfter",
1693
- "dropBefore": "eCE9Ta_dropBefore",
1694
- "folder": "eCE9Ta_folder",
1695
- "folderActive": "eCE9Ta_folderActive",
1696
- "groupBadge": "eCE9Ta_groupBadge",
1697
- "groupBadgeCount": "eCE9Ta_groupBadgeCount",
1698
- "groupBadgePulse": "eCE9Ta_groupBadgePulse",
1699
- "groupBadgeUrgent": "eCE9Ta_groupBadgeUrgent",
1700
- "hoverArchived": "eCE9Ta_hoverArchived",
1701
- "hoverContent": "eCE9Ta_hoverContent",
1702
- "hoverPath": "eCE9Ta_hoverPath",
1703
- "hoverStatus": "eCE9Ta_hoverStatus",
1704
- "hoverTime": "eCE9Ta_hoverTime",
1705
- "hoverTitle": "eCE9Ta_hoverTitle",
1706
- "iconButton": "eCE9Ta_iconButton",
1707
- "menuOpen": "eCE9Ta_menuOpen",
1708
- "meta": "eCE9Ta_meta",
1709
- "pinIndicator": "eCE9Ta_pinIndicator",
1710
- "projectRow": "eCE9Ta_projectRow",
1711
- "projectText": "eCE9Ta_projectText",
1712
- "renameInput": "eCE9Ta_renameInput",
1713
- "rowActions": "eCE9Ta_rowActions",
1714
- "searchResultHeading": "eCE9Ta_searchResultHeading",
1715
- "searchResultMeta": "eCE9Ta_searchResultMeta",
1716
- "searchResultRow": "eCE9Ta_searchResultRow",
1717
- "searchResultSnippet": "eCE9Ta_searchResultSnippet",
1718
- "searchResultTitle": "eCE9Ta_searchResultTitle",
1719
- "searchResultWorkspace": "eCE9Ta_searchResultWorkspace",
1720
- "selected": "eCE9Ta_selected",
1721
- "sessionRow": "eCE9Ta_sessionRow",
1722
- "slot": "eCE9Ta_slot",
1723
- "time": "eCE9Ta_time",
1724
- "title": "eCE9Ta_title",
1725
- "visuallyHidden": "eCE9Ta_visuallyHidden"
1726
- };
1727
1967
  //#endregion
1728
- //#region src/client/rows/GroupBadge.tsx
1968
+ //#region src/client/badge-labels.ts
1969
+ /**
1970
+ * 把侧栏行的翻译位交给 facet。
1971
+ *
1972
+ * 这里有一次**故意的放宽**:协议的 `t` 收任意字符串键,而行位只收本插件 `workspace`
1973
+ * 命名空间的键。放宽是安全的——宿主 locale 服务对未注册的键一律**原样回显键名**(不是
1974
+ * 抛错),所以外来 facet 传了自己命名空间的键,最坏读出的是那串键名而不是崩渲染。
1975
+ * 要正确本地化,provider 应当闭包自己的翻译函数、忽略这里传入的 `t`。
1976
+ */
1977
+ function asFacetTranslate(t) {
1978
+ return (key, options) => t(key, options);
1979
+ }
1980
+ /** 该 facet 贡献进徽标数字的条数。 */
1981
+ function facetCountTotal(s) {
1982
+ let total = 0;
1983
+ for (const entry of s.facets.values()) total += entry.fold.count;
1984
+ return total;
1985
+ }
1986
+ /**
1987
+ * 每个外部状态来源的那句话,按登记顺序。
1988
+ *
1989
+ * facet 没实现 `label` 时**不能**退化成 `2 dsh-later.schedule` 这种读法,所以给一句
1990
+ * 通用措辞兜底:只说有几个来源在响,具体语义等该来源自己补文案。
1991
+ */
1992
+ function facetLines(s, t, now) {
1993
+ const translate = asFacetTranslate(t);
1994
+ const lines = [];
1995
+ for (const entry of s.facets.values()) {
1996
+ const text = entry.facet.label?.(entry.fold, {
1997
+ now,
1998
+ t: translate
1999
+ });
2000
+ lines.push(text === void 0 || text === "" ? `${entry.fold.count} ${t("status.needsAttention")}` : text);
2001
+ }
2002
+ return lines;
2003
+ }
1729
2004
  /**
1730
2005
  * Accessible label for a folded group's aggregated status, reusing the
1731
2006
  * session-row locale keys so both surfaces read identically.
1732
2007
  *
1733
- * 按设置过滤:关掉的来源不出现在标签里(aria-label 与徽标视觉一致)。
2008
+ * 按设置过滤:**关掉的外部来源根本不在折果里**(`WorkspaceBrowser` 折叠前就按
2009
+ * `facet.settingKey` 过滤了登记面),所以这里不必再判设置,`aria-label` 与徽标视觉
2010
+ * 天然一致;宿主的行级状态仍按设置逐项过滤。
1734
2011
  */
1735
- function groupLiveLabel(s, t, settings) {
2012
+ function groupLiveLabel(s, t, settings, now = Date.now()) {
1736
2013
  const parts = [];
1737
2014
  if (settings.showPendingInBadge) {
1738
2015
  if (s.approval > 0) parts.push(`${s.approval} ${t("status.waitingApproval")}`);
@@ -1743,80 +2020,50 @@ window.__ModuleLoader__.load({
1743
2020
  if (s.running > 0) parts.push(`${s.running} ${t("status.running")}`);
1744
2021
  if (s.subagents > 0) parts.push(t(s.subagents === 1 ? "status.subagentsRunning.one" : "status.subagentsRunning.other", { n: s.subagents }));
1745
2022
  }
1746
- if (settings.showWaitingReviewInBadge && s.waitingReviewCount > 0) parts.push(`${s.waitingReviewCount} ${t("status.waitingReview")}`);
1747
- if (settings.showScheduledInBadge && s.scheduledSessions > 0) parts.push(`${s.scheduledSessions} ${t("status.scheduled")}`);
2023
+ parts.push(...facetLines(s, t, now));
1748
2024
  if (settings.showCompletedInBadge && s.done > 0) parts.push(`${s.done} ${t("status.completed")}`);
1749
2025
  return parts.join(" · ");
1750
2026
  }
1751
2027
  /**
1752
- * Aggregated live-status badge for a folded folder row. Dots reuse StateDot so
1753
- * colors match session rows (warning = pending, ongoing = running, done = completed).
1754
- * Renders nothing while expanded or when nothing is pending/active/completed/scheduled/waiting.
2028
+ * 每个外部状态来源的明细行,**带它自己的图标**(#24)。
1755
2029
  *
1756
- * Count matches the dots: pending + active + completed + waiting-review + scheduled sessions.
2030
+ * 徽标侧画的是 provider 交出的、已含色档的元素(`rows/GroupBadge.tsx` 的 `facetGlyph`),
2031
+ * 明细侧此前只画一个统一的 `StateDot`,于是同一条状态、同一次悬停里,徽标是红色沙漏、
2032
+ * 明细是黄点——同一行的两个视图各说一套。宿主 `StateDot` 只有 warning / ongoing / done
2033
+ * 三档、没有 error 档,所以色档**只能**由来源自己的图标表达,这也是本函数存在的理由。
1757
2034
  *
1758
- * `settings` 按来源过滤:关掉的来源不画点、不计数、不进 aria-label、不参与「是否显示徽标」
1759
- * 的判断。会话行自身的状态不受影响:那是行级状态,不是分组提示。
2035
+ * provider 没交图标时退回通用点(与徽标侧同一套兜底),陌生来源仍然可见。
2036
+ *
2037
+ * @returns 明细行;`glyph` 为 `undefined` 表示该行用 `StateDot` 渲染。
1760
2038
  */
1761
- function groupLiveBadge(group, t, settings) {
1762
- const s = group.liveStatus;
1763
- if (group.expanded || s === void 0) return null;
1764
- const pending = settings.showPendingInBadge ? s.approval + s.planReview + s.question : 0;
1765
- const active = settings.showRunningInBadge ? s.running + s.subagents : 0;
1766
- const scheduled = settings.showScheduledInBadge ? s.scheduledSessions : 0;
1767
- const hasWaitingReview = settings.showWaitingReviewInBadge && s.waitingReviewCount > 0;
1768
- const completed = settings.showCompletedInBadge ? s.done : 0;
1769
- if (pending === 0 && active === 0 && completed === 0 && scheduled === 0 && !hasWaitingReview) return null;
1770
- const label = groupLiveLabel(s, t, settings);
1771
- const dots = [];
1772
- if (pending > 0) dots.push(/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.StateDot, { state: "warning" }, "warn"));
1773
- if (active > 0) dots.push(/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.StateDot, { state: "ongoing" }, "run"));
1774
- if (completed > 0) dots.push(/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.StateDot, { state: "done" }, "done"));
1775
- if (scheduled > 0) {
1776
- const stateColor = s.scheduleState === "overdue" ? "var(--dsw-alias-state-error-primary)" : s.scheduleState === "urgent" ? "var(--dsw-alias-state-warn-secondary)" : "#8b5cf6";
1777
- dots.push(/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1778
- style: {
1779
- color: stateColor,
1780
- display: "inline-flex",
1781
- alignItems: "center"
1782
- },
1783
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconClockOutlineRegular, { size: 14 })
1784
- }, "schedule"));
1785
- }
1786
- if (hasWaitingReview) {
1787
- const stateColor = s.waitingReviewState === "overdue" ? "var(--dsw-alias-state-error-primary)" : "var(--dsw-alias-state-warn-secondary)";
1788
- dots.push(/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1789
- style: {
1790
- color: stateColor,
1791
- display: "inline-flex",
1792
- alignItems: "center"
1793
- },
1794
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconListPenOutlineRegular, { size: 14 })
1795
- }, "waiting-review"));
2039
+ function facetDetailLines(s, t, now) {
2040
+ const translate = asFacetTranslate(t);
2041
+ const lines = [];
2042
+ for (const entry of s.facets.values()) {
2043
+ const raw = entry.facet.label?.(entry.fold, {
2044
+ now,
2045
+ t: translate
2046
+ });
2047
+ const text = raw === void 0 || raw === "" ? `${entry.fold.count} ${t("status.needsAttention")}` : raw;
2048
+ lines.push({
2049
+ state: "warning",
2050
+ text,
2051
+ glyph: facetGlyph(entry, t, now)
2052
+ });
1796
2053
  }
1797
- const count = pending + active + completed + scheduled + (hasWaitingReview ? s.waitingReviewCount : 0);
1798
- const countLabel = count > 99 ? "99+" : count >= settings.countThreshold ? String(count) : "";
1799
- const badgeClass = s.scheduleState === "overdue" || s.scheduleState === "urgent" || s.waitingReviewState === "overdue" ? `${Rows_module_css_default.groupBadge} ${Rows_module_css_default.groupBadgeUrgent}` : Rows_module_css_default.groupBadge;
1800
- return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
1801
- className: badgeClass,
1802
- role: "img",
1803
- "aria-label": label,
1804
- title: label,
1805
- children: [dots, countLabel && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1806
- className: Rows_module_css_default.groupBadgeCount,
1807
- children: countLabel
1808
- })]
1809
- });
2054
+ return lines;
1810
2055
  }
1811
2056
  /**
1812
2057
  * Build the status detail lines for the workspace hover card.
1813
2058
  *
1814
- * Extracted so `WorkspaceHoverContent` in `Rows.tsx` only needs to map the
1815
- * returned array — the fork-specific status→line logic lives here.
2059
+ * Extracted so `WorkspaceHoverContent` in `Rows.tsx` only needs to map the returned
2060
+ * array — the fork-specific status→line logic lives here.
1816
2061
  *
1817
2062
  * 按设置过滤:关掉的来源不出现在明细行里;`showHoverDetails=false` 时整段返回空。
2063
+ * 明细行的顺序与徽标**完全一致**(外部来源排在会话状态之后、已完成之前):两处顺序
2064
+ * 不一样会让人怀疑它们是两套事实。
1818
2065
  */
1819
- function hoverStatusLines(status, t, settings) {
2066
+ function hoverStatusLines(status, t, settings, now = Date.now()) {
1820
2067
  const lines = [];
1821
2068
  if (status === void 0 || !settings.showHoverDetails) return lines;
1822
2069
  if (settings.showPendingInBadge) {
@@ -1843,20 +2090,146 @@ window.__ModuleLoader__.load({
1843
2090
  text: t(status.subagents === 1 ? "status.subagentsRunning.one" : "status.subagentsRunning.other", { n: status.subagents })
1844
2091
  });
1845
2092
  }
1846
- if (settings.showWaitingReviewInBadge && status.waitingReviewCount > 0) lines.push({
1847
- state: "warning",
1848
- text: `${status.waitingReviewCount} ${t("status.waitingReview")}`
1849
- });
1850
- if (settings.showScheduledInBadge && status.scheduledSessions > 0) lines.push({
1851
- state: "warning",
1852
- text: `${status.scheduledSessions} ${t("status.scheduled")}`
1853
- });
2093
+ lines.push(...facetDetailLines(status, t, now));
1854
2094
  if (settings.showCompletedInBadge && status.done > 0) lines.push({
1855
2095
  state: "done",
1856
2096
  text: `${status.done} ${t("status.completed")}`
1857
2097
  });
1858
2098
  return lines;
1859
2099
  }
2100
+ /**
2101
+ * 某一条外部来源的元素该带哪只 class(#22)。
2102
+ *
2103
+ * 脉冲判据仍由各 facet 自己声明(`FacetFold.pulse`),但**作用对象必须下沉到声明它的那个
2104
+ * 元素**。此前 `groupBadgeIsUrgent()` 只返回一个整徽标的布尔值、class 加在容器上,于是同组
2105
+ * 里任何一家声明 `pulse: true` 都会让别家本该静止的图标跟着呼吸:`pulse: false` 是否生效,
2106
+ * 取决于同组里恰好有没有别人开了闪。判断交出去了,作用对象没交出去——这是修过一次的回归
2107
+ * (见下)。
2108
+ *
2109
+ * 保留这条历史的原因写在所有者那边:dsh-gitea-dispatch 把 `overdue` 画成**静止**(其
2110
+ * `lib/waiting-glyph.js`:「超时后停动画:静止即不再期待」),且它只代表 CI 窗口到期、
2111
+ * 不代表评审失败。同一条等待在行内静止、在父级闪动,就是同一事实的两种说法。
2112
+ */
2113
+ function facetPulseClass(entry, urgentClass) {
2114
+ return entry.fold.pulse ? urgentClass : void 0;
2115
+ }
2116
+ /**
2117
+ * 一个来源的图标。
2118
+ *
2119
+ * 归 provider 画(`facet.renderIcon`):「这个状态长什么样」是它的知识。没实现时本插件
2120
+ * 按色档退回一个通用点(见 `rows/GroupBadge.tsx`),保证陌生来源仍然可见、只是朴素。
2121
+ */
2122
+ function facetGlyph(entry, t, now) {
2123
+ return entry.facet.renderIcon?.(entry.fold, {
2124
+ now,
2125
+ t: asFacetTranslate(t)
2126
+ });
2127
+ }
2128
+ //#endregion
2129
+ //#region \0dsh-css:src/client/rows/Rows.module.css.mjs
2130
+ const css$3 = "._1Jb0BW_projectRow,._1Jb0BW_sessionRow{border-radius:var(--dsw-radius-md);padding:0 8px;cursor:pointer;user-select:none;color:var(--dsw-alias-label-primary);align-items:center;gap:6px;padding-inline-start:calc(8px + var(--dsh-workspace-indent,0px));display:flex}._1Jb0BW_projectRow:hover,._1Jb0BW_sessionRow:hover,._1Jb0BW_sessionRow._1Jb0BW_selected{background:var(--dsw-alias-interactive-bg-hover)}._1Jb0BW_searchResultRow{box-sizing:border-box;border-radius:var(--dsw-radius-lg);cursor:pointer;text-align:left;width:100%;min-height:48px;color:var(--dsw-alias-label-primary);background:0 0;border:none;flex-direction:column;align-items:stretch;padding:4px 8px;display:flex}._1Jb0BW_searchResultRow:hover,._1Jb0BW_searchResultRow._1Jb0BW_selected{background:var(--dsw-alias-interactive-bg-hover)}._1Jb0BW_searchResultHeading{align-items:center;min-width:0;display:flex}._1Jb0BW_searchResultTitle{text-overflow:ellipsis;white-space:nowrap;flex:0 auto;min-width:0;margin-left:4px;font-size:14px;line-height:20px;overflow:hidden}._1Jb0BW_searchResultMeta{align-items:center;gap:6px;min-width:0;margin-left:20px;display:flex}._1Jb0BW_searchResultWorkspace,._1Jb0BW_searchResultSnippet{text-overflow:ellipsis;white-space:nowrap;font-size:12px;line-height:17px;overflow:hidden}._1Jb0BW_searchResultWorkspace{max-width:40%;color:var(--dsw-alias-label-tertiary);flex:none}._1Jb0BW_searchResultWorkspace:only-child{max-width:100%}._1Jb0BW_searchResultSnippet{min-width:0;color:var(--dsw-alias-label-secondary);flex:1}._1Jb0BW_projectRow{box-sizing:border-box;align-items:center;height:34px}._1Jb0BW_projectRow ._1Jb0BW_rowActions{height:20px}._1Jb0BW_sessionRow{gap:0;height:32px}._1Jb0BW_sessionRow ._1Jb0BW_title{margin:0 6px 0 4px}._1Jb0BW_slot{width:16px;height:20px;color:var(--dsw-alias-label-tertiary);flex:none;justify-content:center;align-items:center;display:inline-flex}._1Jb0BW_visuallyHidden{clip:rect(0 0 0 0);white-space:nowrap;width:1px;height:1px;position:absolute;overflow:hidden}._1Jb0BW_folderActive{color:var(--dsw-alias-state-business-primary)}._1Jb0BW_projectRow ._1Jb0BW_chevron{display:none}._1Jb0BW_projectRow:hover ._1Jb0BW_chevron{display:inline-flex}._1Jb0BW_projectRow:hover ._1Jb0BW_folder{display:none}._1Jb0BW_arrow{transition:transform .15s var(--ds-ease-in-out)}._1Jb0BW_arrowOpen{transform:rotate(90deg)}._1Jb0BW_projectText{flex-direction:column;flex:1;gap:2px;min-width:0;display:flex}._1Jb0BW_title{text-overflow:ellipsis;white-space:nowrap;min-width:0;font-size:14px;line-height:20px;overflow:hidden}._1Jb0BW_renameInput{border:.5px solid var(--dsw-alias-border-l4);border-radius:var(--dsw-radius-sm);background:var(--dsw-alias-button-elevated-fill);min-width:0;color:inherit;outline:none;padding:0 2px;font-size:14px;line-height:20px}._1Jb0BW_sessionRow ._1Jb0BW_title{flex:1}._1Jb0BW_sessionRow ._1Jb0BW_title[data-scrolled]{mask-image:linear-gradient(90deg,#0000,#000 12px)}._1Jb0BW_sessionRow ._1Jb0BW_title[data-clipped]{mask-image:linear-gradient(270deg,#0000,#000 12px)}._1Jb0BW_sessionRow ._1Jb0BW_title[data-scrolled][data-clipped]{mask-image:linear-gradient(90deg,#0000,#000 12px calc(100% - 12px),#0000)}._1Jb0BW_meta{text-overflow:ellipsis;white-space:nowrap;color:var(--dsw-alias-label-tertiary);font-size:12px;line-height:20px;overflow:hidden}._1Jb0BW_time{color:var(--dsw-alias-label-tertiary);flex:none;font-size:10px;line-height:16px}._1Jb0BW_pinIndicator{width:16px;height:20px;color:var(--dsw-alias-label-caption);flex:none;justify-content:center;align-items:center;margin-left:6px;display:inline-flex}._1Jb0BW_sessionRow._1Jb0BW_archived ._1Jb0BW_title,._1Jb0BW_searchResultRow._1Jb0BW_archived ._1Jb0BW_searchResultTitle,._1Jb0BW_searchResultRow._1Jb0BW_archived ._1Jb0BW_searchResultWorkspace,._1Jb0BW_searchResultRow._1Jb0BW_archived ._1Jb0BW_searchResultSnippet{color:var(--dsw-alias-label-caption)}._1Jb0BW_dot{flex:none}._1Jb0BW_rowActions{flex:none;align-items:center;gap:10px;display:none}._1Jb0BW_projectRow:hover ._1Jb0BW_rowActions,._1Jb0BW_sessionRow:hover ._1Jb0BW_rowActions,._1Jb0BW_searchResultRow:hover ._1Jb0BW_rowActions,._1Jb0BW_projectRow._1Jb0BW_menuOpen ._1Jb0BW_rowActions,._1Jb0BW_sessionRow._1Jb0BW_menuOpen ._1Jb0BW_rowActions{display:inline-flex}._1Jb0BW_searchResultHeading ._1Jb0BW_rowActions{margin-left:auto}._1Jb0BW_sessionRow:hover ._1Jb0BW_time,._1Jb0BW_sessionRow._1Jb0BW_menuOpen ._1Jb0BW_time,._1Jb0BW_sessionRow:hover ._1Jb0BW_pinIndicator,._1Jb0BW_sessionRow._1Jb0BW_menuOpen ._1Jb0BW_pinIndicator{display:none}@media (hover:hover){._1Jb0BW_sessionRow:hover ._1Jb0BW_title,._1Jb0BW_sessionRow._1Jb0BW_menuOpen ._1Jb0BW_title{text-overflow:clip}}._1Jb0BW_projectRow._1Jb0BW_menuOpen,._1Jb0BW_sessionRow._1Jb0BW_menuOpen{background:var(--dsw-alias-interactive-bg-hover)}._1Jb0BW_sessionRow._1Jb0BW_dropBefore,._1Jb0BW_sessionRow._1Jb0BW_dropAfter{position:relative}._1Jb0BW_sessionRow._1Jb0BW_dropBefore:before,._1Jb0BW_sessionRow._1Jb0BW_dropAfter:after{content:\"\";z-index:1;background:linear-gradient(55deg, transparent calc(50% - 1px), var(--dsw-alias-state-business-primary) calc(50% - 1px) calc(50% + 1px), transparent calc(50% + 1px)) 0 0 / 5px 7px no-repeat, linear-gradient(125deg, transparent calc(50% - 1px), var(--dsw-alias-state-business-primary) calc(50% - 1px) calc(50% + 1px), transparent calc(50% + 1px)) 0 5px / 5px 7px no-repeat, linear-gradient(var(--dsw-alias-state-business-primary) 0 0) 4px 5px / calc(100% - 4px) 2px no-repeat;pointer-events:none;height:12px;position:absolute;left:0;right:4px}._1Jb0BW_sessionRow._1Jb0BW_dropBefore:before{top:-7px}._1Jb0BW_sessionRow._1Jb0BW_dropAfter:after{bottom:-7px}._1Jb0BW_hoverContent{flex-direction:column;gap:8px;display:flex}._1Jb0BW_hoverTitle{color:var(--dsw-alias-label-primary);overflow-wrap:break-word;font-size:14px;line-height:20px}._1Jb0BW_hoverPath{color:var(--dsw-alias-label-secondary);word-break:break-all;font-size:12px;line-height:16px}._1Jb0BW_hoverTime{color:var(--dsw-alias-label-secondary);font-size:12px;line-height:16px}._1Jb0BW_hoverStatus{color:var(--dsw-alias-label-tertiary);align-items:center;gap:8px;font-size:12px;line-height:20px;display:flex}._1Jb0BW_groupBadge{flex:none;align-items:center;gap:3px;margin-inline-start:4px;display:inline-flex}._1Jb0BW_groupBadgeCount{color:var(--dsw-alias-label-tertiary);border:1px solid;border-radius:6px;padding:0 3px;font-size:9px;line-height:12px}@keyframes _1Jb0BW_groupBadgePulse{0%,to{opacity:1}50%{opacity:.6}}._1Jb0BW_groupBadgeUrgent{animation:2s ease-in-out infinite _1Jb0BW_groupBadgePulse}._1Jb0BW_hoverArchived svg{flex-shrink:0;margin:0 -4px}._1Jb0BW_iconButton{border-radius:var(--dsw-radius-xs);cursor:pointer;width:16px;height:16px;color:var(--dsw-alias-label-tertiary);background:0 0;border:none;flex:none;justify-content:center;align-items:center;padding:0;display:inline-flex}._1Jb0BW_iconButton:hover{color:var(--dsw-alias-label-primary)}._1Jb0BW_chevron{color:var(--dsw-alias-label-caption)}@media (prefers-reduced-motion:reduce){._1Jb0BW_arrow{transition:none;animation:none}}";
2131
+ const tagId$3 = "dsh-group-status/Rows.module.css";
2132
+ if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$3) + "]") === null) {
2133
+ const tag = document.createElement("style");
2134
+ tag.dataset.plugin = "dsh-group-status";
2135
+ tag.dataset.pluginCss = tagId$3;
2136
+ tag.textContent = css$3;
2137
+ document.head.appendChild(tag);
2138
+ }
2139
+ var Rows_module_css_default = {
2140
+ "archived": "_1Jb0BW_archived",
2141
+ "arrow": "_1Jb0BW_arrow",
2142
+ "arrowOpen": "_1Jb0BW_arrowOpen",
2143
+ "chevron": "_1Jb0BW_chevron",
2144
+ "dot": "_1Jb0BW_dot",
2145
+ "dropAfter": "_1Jb0BW_dropAfter",
2146
+ "dropBefore": "_1Jb0BW_dropBefore",
2147
+ "folder": "_1Jb0BW_folder",
2148
+ "folderActive": "_1Jb0BW_folderActive",
2149
+ "groupBadge": "_1Jb0BW_groupBadge",
2150
+ "groupBadgeCount": "_1Jb0BW_groupBadgeCount",
2151
+ "groupBadgePulse": "_1Jb0BW_groupBadgePulse",
2152
+ "groupBadgeUrgent": "_1Jb0BW_groupBadgeUrgent",
2153
+ "hoverArchived": "_1Jb0BW_hoverArchived",
2154
+ "hoverContent": "_1Jb0BW_hoverContent",
2155
+ "hoverPath": "_1Jb0BW_hoverPath",
2156
+ "hoverStatus": "_1Jb0BW_hoverStatus",
2157
+ "hoverTime": "_1Jb0BW_hoverTime",
2158
+ "hoverTitle": "_1Jb0BW_hoverTitle",
2159
+ "iconButton": "_1Jb0BW_iconButton",
2160
+ "menuOpen": "_1Jb0BW_menuOpen",
2161
+ "meta": "_1Jb0BW_meta",
2162
+ "pinIndicator": "_1Jb0BW_pinIndicator",
2163
+ "projectRow": "_1Jb0BW_projectRow",
2164
+ "projectText": "_1Jb0BW_projectText",
2165
+ "renameInput": "_1Jb0BW_renameInput",
2166
+ "rowActions": "_1Jb0BW_rowActions",
2167
+ "searchResultHeading": "_1Jb0BW_searchResultHeading",
2168
+ "searchResultMeta": "_1Jb0BW_searchResultMeta",
2169
+ "searchResultRow": "_1Jb0BW_searchResultRow",
2170
+ "searchResultSnippet": "_1Jb0BW_searchResultSnippet",
2171
+ "searchResultTitle": "_1Jb0BW_searchResultTitle",
2172
+ "searchResultWorkspace": "_1Jb0BW_searchResultWorkspace",
2173
+ "selected": "_1Jb0BW_selected",
2174
+ "sessionRow": "_1Jb0BW_sessionRow",
2175
+ "slot": "_1Jb0BW_slot",
2176
+ "time": "_1Jb0BW_time",
2177
+ "title": "_1Jb0BW_title",
2178
+ "visuallyHidden": "_1Jb0BW_visuallyHidden"
2179
+ };
2180
+ //#endregion
2181
+ //#region src/client/rows/GroupBadge.tsx
2182
+ /**
2183
+ * Aggregated live-status badge for a folded folder row. Dots reuse StateDot so
2184
+ * colors match session rows (warning = pending, ongoing = running, done = completed).
2185
+ * Renders nothing while expanded or when nothing is pending/active/completed or any
2186
+ * status source reports a contribution.
2187
+ *
2188
+ * Count matches the dots: host-side sessions plus every registered facet's own count.
2189
+ *
2190
+ * `settings` 按来源过滤:关掉的来源不画点、不计数、不进 aria-label、不参与「是否显示
2191
+ * 徽标」的判断。会话行自身的状态点不受影响:那是行级状态,不是分组提示。**外部**来源的
2192
+ * 过滤发生在折叠之前(`WorkspaceBrowser` 按 `facet.settingKey` 筛登记面),所以到这里
2193
+ * 的折果已经是"该显示的",label 与视觉不可能各说一套。
2194
+ *
2195
+ * `now` 缺省取渲染时刻(与 `WorkspaceBrowser` 为相对时间读的时钟同一姿态):文案里的
2196
+ * 剩余时长以此计算,只在提供方 store 通知时随渲染更新。
2197
+ */
2198
+ function groupLiveBadge(group, t, settings, now = Date.now()) {
2199
+ const s = group.liveStatus;
2200
+ if (group.expanded || s === void 0) return null;
2201
+ const pending = settings.showPendingInBadge ? s.approval + s.planReview + s.question : 0;
2202
+ const active = settings.showRunningInBadge ? s.running + s.subagents : 0;
2203
+ const completed = settings.showCompletedInBadge ? s.done : 0;
2204
+ const facetTotal = facetCountTotal(s);
2205
+ if (pending === 0 && active === 0 && completed === 0 && facetTotal === 0) return null;
2206
+ const label = groupLiveLabel(s, t, settings, now);
2207
+ const dots = [];
2208
+ if (pending > 0) dots.push(/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.StateDot, { state: "warning" }, "warn"));
2209
+ if (active > 0) dots.push(/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.StateDot, { state: "ongoing" }, "run"));
2210
+ const urgentClass = Rows_module_css_default.groupBadgeUrgent ?? "";
2211
+ for (const entry of s.facets.values()) {
2212
+ const glyph = facetGlyph(entry, t, now);
2213
+ const pulseClass = facetPulseClass(entry, urgentClass) || void 0;
2214
+ dots.push(glyph === void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.StateDot, { state: entry.fold.tone === "info" ? "ongoing" : "warning" }, entry.facet.id) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2215
+ className: pulseClass,
2216
+ children: glyph
2217
+ }, entry.facet.id));
2218
+ }
2219
+ if (completed > 0) dots.push(/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.StateDot, { state: "done" }, "done"));
2220
+ const count = pending + active + completed + facetTotal;
2221
+ const countLabel = count > 99 ? "99+" : count >= settings.countThreshold ? String(count) : "";
2222
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
2223
+ className: Rows_module_css_default.groupBadge,
2224
+ role: "img",
2225
+ "aria-label": label,
2226
+ title: label,
2227
+ children: [dots, countLabel && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2228
+ className: Rows_module_css_default.groupBadgeCount,
2229
+ children: countLabel
2230
+ })]
2231
+ });
2232
+ }
1860
2233
  //#endregion
1861
2234
  //#region src/client/rows/Rows.tsx
1862
2235
  /**
@@ -1982,8 +2355,8 @@ window.__ModuleLoader__.load({
1982
2355
  })} ${pad2(d.getHours())}:${pad2(d.getMinutes())}` });
1983
2356
  }
1984
2357
  /** Hover-card body: workspace title, display directory path, absolute creation time. */
1985
- function WorkspaceHoverContent({ label, cwd, createdAt, status, settings, t }) {
1986
- const lines = hoverStatusLines(status, t, settings);
2358
+ function WorkspaceHoverContent({ label, cwd, createdAt, status, settings, now, t }) {
2359
+ const lines = hoverStatusLines(status, t, settings, now);
1987
2360
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1988
2361
  className: Rows_module_css_default.hoverContent,
1989
2362
  children: [
@@ -2001,7 +2374,7 @@ window.__ModuleLoader__.load({
2001
2374
  }),
2002
2375
  lines.map((line) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2003
2376
  className: Rows_module_css_default.hoverStatus,
2004
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.StateDot, { state: line.state }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: line.text })]
2377
+ children: [line.glyph ?? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.StateDot, { state: line.state }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: line.text })]
2005
2378
  }, line.text))
2006
2379
  ]
2007
2380
  });
@@ -2025,7 +2398,7 @@ window.__ModuleLoader__.load({
2025
2398
  * @param props.t - the browser root's locale seat.
2026
2399
  * @returns the row element.
2027
2400
  */
2028
- function ProjectRowItem({ group, containsCurrentDescendant = false, onToggle, onCreate, actions, drag, home, newShortcut, settings, t }) {
2401
+ function ProjectRowItem({ group, containsCurrentDescendant = false, onToggle, onCreate, actions, drag, home, newShortcut, settings, now, t }) {
2029
2402
  const row = group;
2030
2403
  const label = row.workspaceId === void 0 ? t("group.ungrouped") : row.label;
2031
2404
  const active = containsCurrentDescendant || group.expanded && group.containsCurrent;
@@ -2069,7 +2442,7 @@ window.__ModuleLoader__.load({
2069
2442
  children: label
2070
2443
  })
2071
2444
  }),
2072
- groupLiveBadge(group, t, settings),
2445
+ groupLiveBadge(group, t, settings, now),
2073
2446
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
2074
2447
  className: Rows_module_css_default.rowActions,
2075
2448
  children: [actions !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Menu, {
@@ -2127,6 +2500,7 @@ window.__ModuleLoader__.load({
2127
2500
  createdAt: row.createdAt,
2128
2501
  status: row.expanded ? void 0 : row.liveStatus,
2129
2502
  settings,
2503
+ now: now ?? Date.now(),
2130
2504
  t
2131
2505
  }),
2132
2506
  openDelayMs: 800,
@@ -2445,8 +2819,8 @@ window.__ModuleLoader__.load({
2445
2819
  });
2446
2820
  }
2447
2821
  //#endregion
2448
- //#region \0dsh-css:/home/wujue/workspace/dsh-plugins/dsh-group-status/src/client/rows/AnimatedRows.module.css.mjs
2449
- const css$2 = ".dYnUQG_exits{contain:strict;pointer-events:none;position:absolute;inset:0;overflow:clip}";
2822
+ //#region \0dsh-css:src/client/rows/AnimatedRows.module.css.mjs
2823
+ const css$2 = "._8TnAsq_exits{contain:strict;pointer-events:none;position:absolute;inset:0;overflow:clip}";
2450
2824
  const tagId$2 = "dsh-group-status/AnimatedRows.module.css";
2451
2825
  if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$2) + "]") === null) {
2452
2826
  const tag = document.createElement("style");
@@ -2455,7 +2829,7 @@ window.__ModuleLoader__.load({
2455
2829
  tag.textContent = css$2;
2456
2830
  document.head.appendChild(tag);
2457
2831
  }
2458
- var AnimatedRows_module_css_default = { "exits": "dYnUQG_exits" };
2832
+ var AnimatedRows_module_css_default = { "exits": "_8TnAsq_exits" };
2459
2833
  //#endregion
2460
2834
  //#region src/client/rows/AnimatedRows.tsx
2461
2835
  /** React-commit-driven movement and entry/exit fades for the sidebar's keyed rows. */
@@ -2621,8 +2995,8 @@ window.__ModuleLoader__.load({
2621
2995
  }
2622
2996
  };
2623
2997
  //#endregion
2624
- //#region \0dsh-css:/home/wujue/workspace/dsh-plugins/dsh-group-status/src/client/WorkspacePicker.module.css.mjs
2625
- const css$1 = ".ff8vyq_modalAction{min-width:72px}.ff8vyq_modalError,.ff8vyq_menuStatus{margin-top:8px;font-size:12px;line-height:18px}.ff8vyq_modalError{color:var(--dsw-alias-state-error-primary)}.ff8vyq_menuStatus{color:var(--dsw-alias-label-secondary)}";
2998
+ //#region \0dsh-css:src/client/WorkspacePicker.module.css.mjs
2999
+ const css$1 = ".S_JYgq_modalAction{min-width:72px}.S_JYgq_modalError,.S_JYgq_menuStatus{margin-top:8px;font-size:12px;line-height:18px}.S_JYgq_modalError{color:var(--dsw-alias-state-error-primary)}.S_JYgq_menuStatus{color:var(--dsw-alias-label-secondary)}";
2626
3000
  const tagId$1 = "dsh-group-status/WorkspacePicker.module.css";
2627
3001
  if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$1) + "]") === null) {
2628
3002
  const tag = document.createElement("style");
@@ -2632,9 +3006,9 @@ window.__ModuleLoader__.load({
2632
3006
  document.head.appendChild(tag);
2633
3007
  }
2634
3008
  var WorkspacePicker_module_css_default = {
2635
- "menuStatus": "ff8vyq_menuStatus",
2636
- "modalAction": "ff8vyq_modalAction",
2637
- "modalError": "ff8vyq_modalError"
3009
+ "menuStatus": "S_JYgq_menuStatus",
3010
+ "modalAction": "S_JYgq_modalAction",
3011
+ "modalError": "S_JYgq_modalError"
2638
3012
  };
2639
3013
  //#endregion
2640
3014
  //#region src/client/WorkspacePicker.tsx
@@ -2794,8 +3168,8 @@ window.__ModuleLoader__.load({
2794
3168
  });
2795
3169
  }
2796
3170
  //#endregion
2797
- //#region \0dsh-css:/home/wujue/workspace/dsh-plugins/dsh-group-status/src/client/rows/WorkspaceBrowser.module.css.mjs
2798
- const css = ".q9WRdW_root{--dsh-session-list-edge-inset:var(--dsh-sidebar-inline-padding);--dsh-session-list-scrollbar-width:5px;--dsh-session-list-scrollbar-offset:2px;box-sizing:border-box;min-height:0;padding-right:var(--dsh-session-list-edge-inset);flex-direction:column;flex:1;display:flex}.q9WRdW_root.q9WRdW_rail{padding-right:0}.q9WRdW_iconButton{border-radius:var(--dsw-radius-sm);cursor:pointer;width:28px;height:28px;color:var(--dsw-alias-label-secondary);background:0 0;border:none;flex:none;justify-content:center;align-items:center;padding:0;display:inline-flex}.q9WRdW_iconButton:focus-visible,.q9WRdW_searchButton:focus-visible,.q9WRdW_clearButton:focus-visible{outline:var(--dsw-focus-ring-width) solid var(--dsw-focus-ring-color,var(--dsw-alias-state-business-primary));outline-offset:-2px}.q9WRdW_iconButton:hover{background:var(--dsw-alias-interactive-bg-hover)}.q9WRdW_viewOptionsMenu{min-width:200px}.q9WRdW_sectionHeader{box-sizing:border-box;border-radius:var(--dsw-radius-md);height:36px;color:var(--dsw-alias-label-tertiary);flex:none;justify-content:flex-end;align-items:center;gap:4px;margin-bottom:4px;padding-left:4px;display:flex;overflow:hidden}.q9WRdW_root:not(.q9WRdW_rail) .q9WRdW_sectionHeader{margin-top:2px;margin-right:-4px}.q9WRdW_sectionLabel{white-space:nowrap;opacity:1;visibility:visible;min-width:0;max-width:45%;transition:max-width .18s var(--ds-ease-in-out), margin-right .18s var(--ds-ease-in-out), opacity .12s var(--ds-ease-in-out), transform .18s var(--ds-ease-in-out), visibility 0s linear;flex:none;line-height:20px;overflow:hidden}.q9WRdW_sectionLabelHidden{opacity:0;visibility:hidden;max-width:0;margin-right:-4px;transition-delay:0s,0s,0s,0s,.18s;transform:translate(-4px)}.q9WRdW_searchSlot{box-sizing:border-box;min-width:0;max-width:28px;transition:max-width .18s var(--ds-ease-in-out), padding-left .18s var(--ds-ease-in-out);flex:1;align-items:center;margin-left:auto;padding-left:0;display:flex}.q9WRdW_searchSlotExpanded{max-width:100%;padding-left:0}.q9WRdW_headerActions{opacity:1;visibility:visible;max-width:60px;transition:max-width .18s var(--ds-ease-in-out), opacity .12s var(--ds-ease-in-out), transform .18s var(--ds-ease-in-out), visibility 0s linear;flex:none;align-items:center;gap:4px;display:flex;overflow:hidden}.q9WRdW_headerActionsHidden{opacity:0;visibility:hidden;pointer-events:none;max-width:0;transition-delay:0s,0s,0s,.18s;transform:translate(4px)}.q9WRdW_search{box-sizing:border-box;border-radius:var(--dsw-radius-sm);cursor:text;width:100%;height:28px;color:var(--dsw-alias-label-secondary);transition:width .18s var(--ds-ease-in-out), padding .18s var(--ds-ease-in-out), border-color .18s var(--ds-ease-in-out), background-color .18s var(--ds-ease-in-out);background:0 0;border:none;flex:none;align-items:center;gap:0;margin:0;padding:0;display:flex;overflow:hidden}.q9WRdW_searchExpanded{border:.5px solid var(--dsw-alias-border-l4);border-radius:var(--dsw-radius-md);width:calc(100% + 4px);height:30px;color:var(--dsw-alias-label-caption);background:0 0;margin-inline:-2px;padding:0 4px 0 0}.q9WRdW_searchButton{border-radius:var(--dsw-radius-sm);cursor:pointer;width:28px;height:28px;color:inherit;background:0 0;border:none;flex:none;justify-content:center;align-items:center;padding:0;display:inline-flex}.q9WRdW_searchExpanded .q9WRdW_searchButton{width:28px;height:30px}.q9WRdW_searchButton:hover{background:var(--dsw-alias-interactive-bg-hover)}.q9WRdW_searchExpanded .q9WRdW_searchButton:hover{background:0 0}.q9WRdW_searchInput{opacity:0;pointer-events:none;width:0;min-width:0;color:var(--dsw-alias-label-primary);transition:opacity .12s var(--ds-ease-in-out);background:0 0;border:none;outline:none;flex:1;font-size:13px;line-height:18px}.q9WRdW_searchExpanded .q9WRdW_searchInput{opacity:1;pointer-events:auto;margin-left:-2px}.q9WRdW_searchInput::placeholder{color:var(--dsw-alias-label-tertiary)}.q9WRdW_clearButton{border-radius:var(--dsw-radius-sm);cursor:pointer;width:24px;height:24px;color:var(--dsw-alias-label-secondary);background:0 0;border:none;flex:none;justify-content:center;align-items:center;padding:0;display:inline-flex}.q9WRdW_clearButton:hover{background:var(--dsw-alias-interactive-bg-hover)}.q9WRdW_rail .q9WRdW_sectionHeader{justify-content:flex-start;gap:0;margin-bottom:12px;padding-left:0}.q9WRdW_rail .q9WRdW_headerActions{max-width:none}.q9WRdW_rail .q9WRdW_iconButton{border-radius:var(--dsw-radius-md);width:36px;height:36px;color:var(--dsw-alias-label-primary)}.q9WRdW_rail .q9WRdW_search{border-radius:var(--dsw-radius-md);background:0 0;border-color:#0000;gap:0;width:36px;height:36px;margin:0 0 12px;padding:0}.q9WRdW_rail .q9WRdW_searchButton{border-radius:var(--dsw-radius-md);width:36px;height:36px;color:var(--dsw-alias-label-primary)}.q9WRdW_rail .q9WRdW_searchButton:hover{background:var(--dsw-alias-interactive-bg-hover)}.q9WRdW_listArea{min-height:0;margin-left:-4px;margin-right:calc(-1 * var(--dsh-session-list-edge-inset));flex-direction:column;flex:1;padding-left:4px;display:flex;overflow:visible}.q9WRdW_rail .q9WRdW_listArea{margin-left:0;margin-right:0;padding-left:0}.q9WRdW_treeBody{flex-direction:column;flex:1;min-height:0;display:flex;position:relative}.q9WRdW_fade{left:0;right:var(--dsh-session-list-edge-inset);background:linear-gradient(to bottom, transparent, var(--dsw-specific-sidebar-fill));pointer-events:none;height:24px;position:absolute;bottom:0}[data-platform=darwin] .q9WRdW_fade{display:none}.q9WRdW_wide{animation:q9WRdW_wide-in .2s var(--ds-ease-in-out)}@keyframes q9WRdW_wide-in{0%{opacity:0}}.q9WRdW_list{min-height:0;margin-left:-4px;margin-right:var(--dsh-session-list-scrollbar-offset);padding-left:4px;padding-right:calc(var(--dsh-session-list-edge-inset) - var(--dsh-session-list-scrollbar-width) - var(--dsh-session-list-scrollbar-offset));scrollbar-gutter:stable;flex:1;padding-bottom:16px;overflow-y:auto}.q9WRdW_flatList>*+*,.q9WRdW_searchTree>[role=treeitem]+[role=treeitem],.q9WRdW_groupSection>*+*{margin-top:2px}.q9WRdW_searchStatus{color:var(--dsw-alias-label-tertiary);padding:10px 12px;font-size:12px;line-height:18px}.q9WRdW_skeletonRow{box-sizing:border-box;align-items:flex-start;gap:8px;min-height:48px;padding:6px 8px 7px;display:flex}.q9WRdW_skeletonDot{corner-shape:round;border-radius:50%;flex:none;width:16px;height:16px}.q9WRdW_skeletonBars{flex-direction:column;flex:1;gap:6px;min-width:0;display:flex}.q9WRdW_skeletonDot,.q9WRdW_skeletonBar{background:var(--dsw-alias-bg-skeleton);animation:2s cubic-bezier(.36,0,.64,1) infinite q9WRdW_search-skeleton}.q9WRdW_skeletonBar{border-radius:var(--dsw-radius-xs);width:65%;height:16px}.q9WRdW_skeletonBarWide{width:90%;height:13px}@keyframes q9WRdW_search-skeleton{0%{opacity:1}40%{opacity:.6}80%,to{opacity:1}}.q9WRdW_groupSection{position:relative}.q9WRdW_groupSection+.q9WRdW_groupSection{margin-top:4px}.q9WRdW_listTopDropIndicator,.q9WRdW_workspaceDropBefore:before,.q9WRdW_workspaceDropAfter:after{content:\"\";z-index:1;background:linear-gradient(55deg, transparent calc(50% - 1px), var(--dsw-alias-state-business-primary) calc(50% - 1px) calc(50% + 1px), transparent calc(50% + 1px)) 0 0 / 5px 7px no-repeat, linear-gradient(125deg, transparent calc(50% - 1px), var(--dsw-alias-state-business-primary) calc(50% - 1px) calc(50% + 1px), transparent calc(50% + 1px)) 0 5px / 5px 7px no-repeat, linear-gradient(var(--dsw-alias-state-business-primary) 0 0) 4px 5px / calc(100% - 4px) 2px no-repeat;pointer-events:none;height:12px;position:absolute;left:0;right:0}.q9WRdW_listTopDropIndicator{top:-8px;left:0;right:var(--dsh-session-list-edge-inset)}.q9WRdW_listTopDropActive>.q9WRdW_workspaceDropBefore:first-child:before{display:none}.q9WRdW_workspaceDropBefore:before{top:-8px}.q9WRdW_workspaceDropAfter:after{bottom:-8px}.q9WRdW_sessionOverflowButton{border-radius:var(--dsw-radius-sm);width:100%;height:28px;padding:0 12px 0 calc(28px + var(--dsh-workspace-indent,0px));cursor:pointer;text-align:left;color:var(--dsw-alias-label-tertiary);background:0 0;border:none;font-size:12px}.q9WRdW_groupSection>.q9WRdW_sessionOverflowButton{margin-top:0}.q9WRdW_sessionOverflowButton:hover{color:var(--dsw-alias-label-secondary);background:0 0}.q9WRdW_empty{color:var(--dsw-alias-label-tertiary);padding:16px 12px;font-size:13px}.q9WRdW_emptyState{color:var(--dsw-alias-label-tertiary);flex-direction:column;align-items:center;gap:8px;margin-top:80px;padding:0 12px;font-size:13px;line-height:20px;display:flex}.q9WRdW_emptyState>svg{color:var(--dsw-alias-label-caption);margin-bottom:4px}.q9WRdW_emptyAction{cursor:pointer;color:var(--dsw-alias-link);background:0 0;border:none;padding:0;font-size:13px;line-height:20px}.q9WRdW_renameInput{box-sizing:border-box;border:.5px solid var(--dsw-alias-border-l4);border-radius:var(--dsw-radius-lg);width:100%;height:44px;color:var(--dsw-alias-label-primary);background:0 0;outline:none;padding:7px 14px;font-size:14px;font-weight:400;line-height:22px}.q9WRdW_renameInput:disabled{color:var(--dsw-alias-label-dimmed)}.q9WRdW_renameError{color:var(--dsw-alias-state-error-primary);margin-top:8px;font-size:12px;line-height:18px}.q9WRdW_deleteAction:not(:disabled){color:var(--dsw-alias-state-error-primary)}.q9WRdW_deleteStatus{color:var(--dsw-alias-label-secondary);font-size:12px;line-height:18px}.q9WRdW_archiveActivity{color:var(--dsw-alias-label-primary);margin:0 0 8px;padding-left:18px;font-size:13px;line-height:20px}.q9WRdW_archiveActivity li{overflow-wrap:anywhere}@media (prefers-reduced-motion:reduce){.q9WRdW_wide,.q9WRdW_skeletonDot,.q9WRdW_skeletonBar{animation:none}.q9WRdW_search,.q9WRdW_sectionLabel,.q9WRdW_searchSlot,.q9WRdW_searchInput,.q9WRdW_headerActions{transition:none}}";
3171
+ //#region \0dsh-css:src/client/rows/WorkspaceBrowser.module.css.mjs
3172
+ const css = ".QAYkWG_root{--dsh-session-list-edge-inset:var(--dsh-sidebar-inline-padding);--dsh-session-list-scrollbar-width:5px;--dsh-session-list-scrollbar-offset:2px;box-sizing:border-box;min-height:0;padding-right:var(--dsh-session-list-edge-inset);flex-direction:column;flex:1;display:flex}.QAYkWG_root.QAYkWG_rail{padding-right:0}.QAYkWG_iconButton{border-radius:var(--dsw-radius-sm);cursor:pointer;width:28px;height:28px;color:var(--dsw-alias-label-secondary);background:0 0;border:none;flex:none;justify-content:center;align-items:center;padding:0;display:inline-flex}.QAYkWG_iconButton:focus-visible,.QAYkWG_searchButton:focus-visible,.QAYkWG_clearButton:focus-visible{outline:var(--dsw-focus-ring-width) solid var(--dsw-focus-ring-color,var(--dsw-alias-state-business-primary));outline-offset:-2px}.QAYkWG_iconButton:hover{background:var(--dsw-alias-interactive-bg-hover)}.QAYkWG_viewOptionsMenu{min-width:200px}.QAYkWG_sectionHeader{box-sizing:border-box;border-radius:var(--dsw-radius-md);height:36px;color:var(--dsw-alias-label-tertiary);flex:none;justify-content:flex-end;align-items:center;gap:4px;margin-bottom:4px;padding-left:4px;display:flex;overflow:hidden}.QAYkWG_root:not(.QAYkWG_rail) .QAYkWG_sectionHeader{margin-top:2px;margin-right:-4px}.QAYkWG_sectionLabel{white-space:nowrap;opacity:1;visibility:visible;min-width:0;max-width:45%;transition:max-width .18s var(--ds-ease-in-out), margin-right .18s var(--ds-ease-in-out), opacity .12s var(--ds-ease-in-out), transform .18s var(--ds-ease-in-out), visibility 0s linear;flex:none;line-height:20px;overflow:hidden}.QAYkWG_sectionLabelHidden{opacity:0;visibility:hidden;max-width:0;margin-right:-4px;transition-delay:0s,0s,0s,0s,.18s;transform:translate(-4px)}.QAYkWG_searchSlot{box-sizing:border-box;min-width:0;max-width:28px;transition:max-width .18s var(--ds-ease-in-out), padding-left .18s var(--ds-ease-in-out);flex:1;align-items:center;margin-left:auto;padding-left:0;display:flex}.QAYkWG_searchSlotExpanded{max-width:100%;padding-left:0}.QAYkWG_headerActions{opacity:1;visibility:visible;max-width:60px;transition:max-width .18s var(--ds-ease-in-out), opacity .12s var(--ds-ease-in-out), transform .18s var(--ds-ease-in-out), visibility 0s linear;flex:none;align-items:center;gap:4px;display:flex;overflow:hidden}.QAYkWG_headerActionsHidden{opacity:0;visibility:hidden;pointer-events:none;max-width:0;transition-delay:0s,0s,0s,.18s;transform:translate(4px)}.QAYkWG_search{box-sizing:border-box;border-radius:var(--dsw-radius-sm);cursor:text;width:100%;height:28px;color:var(--dsw-alias-label-secondary);transition:width .18s var(--ds-ease-in-out), padding .18s var(--ds-ease-in-out), border-color .18s var(--ds-ease-in-out), background-color .18s var(--ds-ease-in-out);background:0 0;border:none;flex:none;align-items:center;gap:0;margin:0;padding:0;display:flex;overflow:hidden}.QAYkWG_searchExpanded{border:.5px solid var(--dsw-alias-border-l4);border-radius:var(--dsw-radius-md);width:calc(100% + 4px);height:30px;color:var(--dsw-alias-label-caption);background:0 0;margin-inline:-2px;padding:0 4px 0 0}.QAYkWG_searchButton{border-radius:var(--dsw-radius-sm);cursor:pointer;width:28px;height:28px;color:inherit;background:0 0;border:none;flex:none;justify-content:center;align-items:center;padding:0;display:inline-flex}.QAYkWG_searchExpanded .QAYkWG_searchButton{width:28px;height:30px}.QAYkWG_searchButton:hover{background:var(--dsw-alias-interactive-bg-hover)}.QAYkWG_searchExpanded .QAYkWG_searchButton:hover{background:0 0}.QAYkWG_searchInput{opacity:0;pointer-events:none;width:0;min-width:0;color:var(--dsw-alias-label-primary);transition:opacity .12s var(--ds-ease-in-out);background:0 0;border:none;outline:none;flex:1;font-size:13px;line-height:18px}.QAYkWG_searchExpanded .QAYkWG_searchInput{opacity:1;pointer-events:auto;margin-left:-2px}.QAYkWG_searchInput::placeholder{color:var(--dsw-alias-label-tertiary)}.QAYkWG_clearButton{border-radius:var(--dsw-radius-sm);cursor:pointer;width:24px;height:24px;color:var(--dsw-alias-label-secondary);background:0 0;border:none;flex:none;justify-content:center;align-items:center;padding:0;display:inline-flex}.QAYkWG_clearButton:hover{background:var(--dsw-alias-interactive-bg-hover)}.QAYkWG_rail .QAYkWG_sectionHeader{justify-content:flex-start;gap:0;margin-bottom:12px;padding-left:0}.QAYkWG_rail .QAYkWG_headerActions{max-width:none}.QAYkWG_rail .QAYkWG_iconButton{border-radius:var(--dsw-radius-md);width:36px;height:36px;color:var(--dsw-alias-label-primary)}.QAYkWG_rail .QAYkWG_search{border-radius:var(--dsw-radius-md);background:0 0;border-color:#0000;gap:0;width:36px;height:36px;margin:0 0 12px;padding:0}.QAYkWG_rail .QAYkWG_searchButton{border-radius:var(--dsw-radius-md);width:36px;height:36px;color:var(--dsw-alias-label-primary)}.QAYkWG_rail .QAYkWG_searchButton:hover{background:var(--dsw-alias-interactive-bg-hover)}.QAYkWG_listArea{min-height:0;margin-left:-4px;margin-right:calc(-1 * var(--dsh-session-list-edge-inset));flex-direction:column;flex:1;padding-left:4px;display:flex;overflow:visible}.QAYkWG_rail .QAYkWG_listArea{margin-left:0;margin-right:0;padding-left:0}.QAYkWG_treeBody{flex-direction:column;flex:1;min-height:0;display:flex;position:relative}.QAYkWG_fade{left:0;right:var(--dsh-session-list-edge-inset);background:linear-gradient(to bottom, transparent, var(--dsw-specific-sidebar-fill));pointer-events:none;height:24px;position:absolute;bottom:0}[data-platform=darwin] .QAYkWG_fade{display:none}.QAYkWG_wide{animation:QAYkWG_wide-in .2s var(--ds-ease-in-out)}@keyframes QAYkWG_wide-in{0%{opacity:0}}.QAYkWG_list{min-height:0;margin-left:-4px;margin-right:var(--dsh-session-list-scrollbar-offset);padding-left:4px;padding-right:calc(var(--dsh-session-list-edge-inset) - var(--dsh-session-list-scrollbar-width) - var(--dsh-session-list-scrollbar-offset));scrollbar-gutter:stable;flex:1;padding-bottom:16px;overflow-y:auto}.QAYkWG_flatList>*+*,.QAYkWG_searchTree>[role=treeitem]+[role=treeitem],.QAYkWG_groupSection>*+*{margin-top:2px}.QAYkWG_searchStatus{color:var(--dsw-alias-label-tertiary);padding:10px 12px;font-size:12px;line-height:18px}.QAYkWG_skeletonRow{box-sizing:border-box;align-items:flex-start;gap:8px;min-height:48px;padding:6px 8px 7px;display:flex}.QAYkWG_skeletonDot{corner-shape:round;border-radius:50%;flex:none;width:16px;height:16px}.QAYkWG_skeletonBars{flex-direction:column;flex:1;gap:6px;min-width:0;display:flex}.QAYkWG_skeletonDot,.QAYkWG_skeletonBar{background:var(--dsw-alias-bg-skeleton);animation:2s cubic-bezier(.36,0,.64,1) infinite QAYkWG_search-skeleton}.QAYkWG_skeletonBar{border-radius:var(--dsw-radius-xs);width:65%;height:16px}.QAYkWG_skeletonBarWide{width:90%;height:13px}@keyframes QAYkWG_search-skeleton{0%{opacity:1}40%{opacity:.6}80%,to{opacity:1}}.QAYkWG_groupSection{position:relative}.QAYkWG_groupSection+.QAYkWG_groupSection{margin-top:4px}.QAYkWG_listTopDropIndicator,.QAYkWG_workspaceDropBefore:before,.QAYkWG_workspaceDropAfter:after{content:\"\";z-index:1;background:linear-gradient(55deg, transparent calc(50% - 1px), var(--dsw-alias-state-business-primary) calc(50% - 1px) calc(50% + 1px), transparent calc(50% + 1px)) 0 0 / 5px 7px no-repeat, linear-gradient(125deg, transparent calc(50% - 1px), var(--dsw-alias-state-business-primary) calc(50% - 1px) calc(50% + 1px), transparent calc(50% + 1px)) 0 5px / 5px 7px no-repeat, linear-gradient(var(--dsw-alias-state-business-primary) 0 0) 4px 5px / calc(100% - 4px) 2px no-repeat;pointer-events:none;height:12px;position:absolute;left:0;right:0}.QAYkWG_listTopDropIndicator{top:-8px;left:0;right:var(--dsh-session-list-edge-inset)}.QAYkWG_listTopDropActive>.QAYkWG_workspaceDropBefore:first-child:before{display:none}.QAYkWG_workspaceDropBefore:before{top:-8px}.QAYkWG_workspaceDropAfter:after{bottom:-8px}.QAYkWG_sessionOverflowButton{border-radius:var(--dsw-radius-sm);width:100%;height:28px;padding:0 12px 0 calc(28px + var(--dsh-workspace-indent,0px));cursor:pointer;text-align:left;color:var(--dsw-alias-label-tertiary);background:0 0;border:none;font-size:12px}.QAYkWG_groupSection>.QAYkWG_sessionOverflowButton{margin-top:0}.QAYkWG_sessionOverflowButton:hover{color:var(--dsw-alias-label-secondary);background:0 0}.QAYkWG_empty{color:var(--dsw-alias-label-tertiary);padding:16px 12px;font-size:13px}.QAYkWG_emptyState{color:var(--dsw-alias-label-tertiary);flex-direction:column;align-items:center;gap:8px;margin-top:80px;padding:0 12px;font-size:13px;line-height:20px;display:flex}.QAYkWG_emptyState>svg{color:var(--dsw-alias-label-caption);margin-bottom:4px}.QAYkWG_emptyAction{cursor:pointer;color:var(--dsw-alias-link);background:0 0;border:none;padding:0;font-size:13px;line-height:20px}.QAYkWG_renameInput{box-sizing:border-box;border:.5px solid var(--dsw-alias-border-l4);border-radius:var(--dsw-radius-lg);width:100%;height:44px;color:var(--dsw-alias-label-primary);background:0 0;outline:none;padding:7px 14px;font-size:14px;font-weight:400;line-height:22px}.QAYkWG_renameInput:disabled{color:var(--dsw-alias-label-dimmed)}.QAYkWG_renameError{color:var(--dsw-alias-state-error-primary);margin-top:8px;font-size:12px;line-height:18px}.QAYkWG_deleteAction:not(:disabled){color:var(--dsw-alias-state-error-primary)}.QAYkWG_deleteStatus{color:var(--dsw-alias-label-secondary);font-size:12px;line-height:18px}.QAYkWG_archiveActivity{color:var(--dsw-alias-label-primary);margin:0 0 8px;padding-left:18px;font-size:13px;line-height:20px}.QAYkWG_archiveActivity li{overflow-wrap:anywhere}@media (prefers-reduced-motion:reduce){.QAYkWG_wide,.QAYkWG_skeletonDot,.QAYkWG_skeletonBar{animation:none}.QAYkWG_search,.QAYkWG_sectionLabel,.QAYkWG_searchSlot,.QAYkWG_searchInput,.QAYkWG_headerActions{transition:none}}";
2799
3173
  const tagId = "dsh-group-status/WorkspaceBrowser.module.css";
2800
3174
  if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId) + "]") === null) {
2801
3175
  const tag = document.createElement("style");
@@ -2805,51 +3179,51 @@ window.__ModuleLoader__.load({
2805
3179
  document.head.appendChild(tag);
2806
3180
  }
2807
3181
  var WorkspaceBrowser_module_css_default = {
2808
- "archiveActivity": "q9WRdW_archiveActivity",
2809
- "clearButton": "q9WRdW_clearButton",
2810
- "deleteAction": "q9WRdW_deleteAction",
2811
- "deleteStatus": "q9WRdW_deleteStatus",
2812
- "empty": "q9WRdW_empty",
2813
- "emptyAction": "q9WRdW_emptyAction",
2814
- "emptyState": "q9WRdW_emptyState",
2815
- "fade": "q9WRdW_fade",
2816
- "flatList": "q9WRdW_flatList",
2817
- "groupSection": "q9WRdW_groupSection",
2818
- "headerActions": "q9WRdW_headerActions",
2819
- "headerActionsHidden": "q9WRdW_headerActionsHidden",
2820
- "iconButton": "q9WRdW_iconButton",
2821
- "list": "q9WRdW_list",
2822
- "listArea": "q9WRdW_listArea",
2823
- "listTopDropActive": "q9WRdW_listTopDropActive",
2824
- "listTopDropIndicator": "q9WRdW_listTopDropIndicator",
2825
- "rail": "q9WRdW_rail",
2826
- "renameError": "q9WRdW_renameError",
2827
- "renameInput": "q9WRdW_renameInput",
2828
- "root": "q9WRdW_root",
2829
- "search": "q9WRdW_search",
2830
- "search-skeleton": "q9WRdW_search-skeleton",
2831
- "searchButton": "q9WRdW_searchButton",
2832
- "searchExpanded": "q9WRdW_searchExpanded",
2833
- "searchInput": "q9WRdW_searchInput",
2834
- "searchSlot": "q9WRdW_searchSlot",
2835
- "searchSlotExpanded": "q9WRdW_searchSlotExpanded",
2836
- "searchStatus": "q9WRdW_searchStatus",
2837
- "searchTree": "q9WRdW_searchTree",
2838
- "sectionHeader": "q9WRdW_sectionHeader",
2839
- "sectionLabel": "q9WRdW_sectionLabel",
2840
- "sectionLabelHidden": "q9WRdW_sectionLabelHidden",
2841
- "sessionOverflowButton": "q9WRdW_sessionOverflowButton",
2842
- "skeletonBar": "q9WRdW_skeletonBar",
2843
- "skeletonBarWide": "q9WRdW_skeletonBarWide",
2844
- "skeletonBars": "q9WRdW_skeletonBars",
2845
- "skeletonDot": "q9WRdW_skeletonDot",
2846
- "skeletonRow": "q9WRdW_skeletonRow",
2847
- "treeBody": "q9WRdW_treeBody",
2848
- "viewOptionsMenu": "q9WRdW_viewOptionsMenu",
2849
- "wide": "q9WRdW_wide",
2850
- "wide-in": "q9WRdW_wide-in",
2851
- "workspaceDropAfter": "q9WRdW_workspaceDropAfter",
2852
- "workspaceDropBefore": "q9WRdW_workspaceDropBefore"
3182
+ "archiveActivity": "QAYkWG_archiveActivity",
3183
+ "clearButton": "QAYkWG_clearButton",
3184
+ "deleteAction": "QAYkWG_deleteAction",
3185
+ "deleteStatus": "QAYkWG_deleteStatus",
3186
+ "empty": "QAYkWG_empty",
3187
+ "emptyAction": "QAYkWG_emptyAction",
3188
+ "emptyState": "QAYkWG_emptyState",
3189
+ "fade": "QAYkWG_fade",
3190
+ "flatList": "QAYkWG_flatList",
3191
+ "groupSection": "QAYkWG_groupSection",
3192
+ "headerActions": "QAYkWG_headerActions",
3193
+ "headerActionsHidden": "QAYkWG_headerActionsHidden",
3194
+ "iconButton": "QAYkWG_iconButton",
3195
+ "list": "QAYkWG_list",
3196
+ "listArea": "QAYkWG_listArea",
3197
+ "listTopDropActive": "QAYkWG_listTopDropActive",
3198
+ "listTopDropIndicator": "QAYkWG_listTopDropIndicator",
3199
+ "rail": "QAYkWG_rail",
3200
+ "renameError": "QAYkWG_renameError",
3201
+ "renameInput": "QAYkWG_renameInput",
3202
+ "root": "QAYkWG_root",
3203
+ "search": "QAYkWG_search",
3204
+ "search-skeleton": "QAYkWG_search-skeleton",
3205
+ "searchButton": "QAYkWG_searchButton",
3206
+ "searchExpanded": "QAYkWG_searchExpanded",
3207
+ "searchInput": "QAYkWG_searchInput",
3208
+ "searchSlot": "QAYkWG_searchSlot",
3209
+ "searchSlotExpanded": "QAYkWG_searchSlotExpanded",
3210
+ "searchStatus": "QAYkWG_searchStatus",
3211
+ "searchTree": "QAYkWG_searchTree",
3212
+ "sectionHeader": "QAYkWG_sectionHeader",
3213
+ "sectionLabel": "QAYkWG_sectionLabel",
3214
+ "sectionLabelHidden": "QAYkWG_sectionLabelHidden",
3215
+ "sessionOverflowButton": "QAYkWG_sessionOverflowButton",
3216
+ "skeletonBar": "QAYkWG_skeletonBar",
3217
+ "skeletonBarWide": "QAYkWG_skeletonBarWide",
3218
+ "skeletonBars": "QAYkWG_skeletonBars",
3219
+ "skeletonDot": "QAYkWG_skeletonDot",
3220
+ "skeletonRow": "QAYkWG_skeletonRow",
3221
+ "treeBody": "QAYkWG_treeBody",
3222
+ "viewOptionsMenu": "QAYkWG_viewOptionsMenu",
3223
+ "wide": "QAYkWG_wide",
3224
+ "wide-in": "QAYkWG_wide-in",
3225
+ "workspaceDropAfter": "QAYkWG_workspaceDropAfter",
3226
+ "workspaceDropBefore": "QAYkWG_workspaceDropBefore"
2853
3227
  };
2854
3228
  //#endregion
2855
3229
  //#region src/client/rows/WorkspaceBrowser.tsx
@@ -2869,14 +3243,7 @@ window.__ModuleLoader__.load({
2869
3243
  * are slot entries with their own behavior, so this component threads no
2870
3244
  * action callbacks and hosts no action surface.
2871
3245
  */
2872
- let useScheduleSummaryHook;
2873
- try {
2874
- useScheduleSummaryHook = require("dsh-later/client")?.useScheduleSummary;
2875
- } catch {}
2876
- let useWaitingSummaryHook;
2877
- try {
2878
- useWaitingSummaryHook = require("dsh-gitea-dispatch/client")?.useWaitingSummary;
2879
- } catch {}
3246
+ const facetsStore = facetStore();
2880
3247
  /**
2881
3248
  * Column slide length (--ds-transition-duration-slow): rail-search focus waits it out —
2882
3249
  * focus() forces a synchronous layout and would jank the slide.
@@ -3087,8 +3454,8 @@ window.__ModuleLoader__.load({
3087
3454
  const panelActive = usePanelInfo((info) => info.activePanelId !== null);
3088
3455
  const badgeSettings = useBadgeSettings((settings) => settings);
3089
3456
  const statuses = useSessionStatus((s) => s);
3090
- const scheduleSummary = useScheduleSummaryHook?.();
3091
- const waitingReviewSummary = useWaitingSummaryHook?.();
3457
+ (0, react.useSyncExternalStore)(facetsStore.subscribe, facetsStore.getVersion, facetsStore.getServerVersion);
3458
+ const facets = (0, react.useMemo)(() => visibleFacets(facetsStore.list(), badgeSettings), [badgeSettings]);
3092
3459
  const current = panelActive ? void 0 : Object.values(list.byId).find((session) => (session.retainedBy.mainView ?? 0) > 0)?.id;
3093
3460
  const revealGroup = revealSessionId === void 0 || !workspaceReady ? void 0 : owningGroupKey(workspaces, revealSessionId);
3094
3461
  const [sessionLimits, setSessionLimits] = (0, react.useState)({});
@@ -3206,11 +3573,7 @@ window.__ModuleLoader__.load({
3206
3573
  }
3207
3574
  return children;
3208
3575
  }, [groups, parents]);
3209
- const directStatusByGroup = (0, react.useMemo)(() => directStatusesForGroups(groups, waitingReviewSummary, scheduleSummary), [
3210
- groups,
3211
- waitingReviewSummary,
3212
- scheduleSummary
3213
- ]);
3576
+ const directStatusByGroup = (0, react.useMemo)(() => directStatusesForGroups(groups, facets, now), [groups, facets]);
3214
3577
  const groupsWithAggregatedStatus = (0, react.useMemo)(() => withAggregatedStatus(groups, directStatusByGroup, childrenByParentForAggregation), [
3215
3578
  groups,
3216
3579
  directStatusByGroup,
@@ -3307,6 +3670,7 @@ window.__ModuleLoader__.load({
3307
3670
  containsCurrentDescendant: currentAncestors.has(group.key),
3308
3671
  home,
3309
3672
  settings: badgeSettings,
3673
+ now,
3310
3674
  t,
3311
3675
  onToggle: () => {
3312
3676
  if (group.expanded) setSessionLimits((limits) => ({
@@ -4765,8 +5129,7 @@ window.__ModuleLoader__.load({
4765
5129
  "status.compact.approval": "待审批",
4766
5130
  "status.compact.planReview": "计划待审",
4767
5131
  "status.compact.answer": "待回答",
4768
- "status.waitingReview": "等待评审",
4769
- "status.scheduled": "定时任务",
5132
+ "status.needsAttention": "需要关注",
4770
5133
  "status.completed": "已完成",
4771
5134
  "hover.created": "创建于 {time}",
4772
5135
  "hover.copied": "已复制",
@@ -4787,8 +5150,8 @@ window.__ModuleLoader__.load({
4787
5150
  "settings.badge.showCompletedHint": "关闭后,「已完成」的会话不再画绿点,也不再计入折叠徽标的数字与悬停明细。会话行自身的绿点不受影响。",
4788
5151
  "settings.badge.showScheduled": "显示「定时任务」时钟",
4789
5152
  "settings.badge.showScheduledHint": "关闭后,带定时提醒的会话(来自 dsh-later)不再画时钟图标,也不再计入折叠徽标的数字与悬停明细。",
4790
- "settings.badge.showWaitingReview": "显示「等待评审」列表笔",
4791
- "settings.badge.showWaitingReviewHint": "关闭后,等待代码评审的会话(来自 dsh-gitea-dispatch)不再画列表笔图标,也不再计入折叠徽标的数字与悬停明细。",
5153
+ "settings.badge.showWaitingReview": "显示「等待评审」沙漏",
5154
+ "settings.badge.showWaitingReviewHint": "关闭后,等待代码评审的会话(来自 dsh-gitea-dispatch)不再画沙漏图标,也不再计入折叠徽标的数字与悬停明细。",
4792
5155
  "settings.hover.section": "悬停明细",
4793
5156
  "settings.hover.showDetails": "显示状态明细行",
4794
5157
  "settings.hover.showDetailsHint": "关闭后,悬停卡只显示工作区标题、路径和创建时间——不再显示聚合状态明细行。",
@@ -4904,8 +5267,7 @@ window.__ModuleLoader__.load({
4904
5267
  "status.compact.approval": "Approval",
4905
5268
  "status.compact.planReview": "Plan review",
4906
5269
  "status.compact.answer": "Answer",
4907
- "status.waitingReview": "Waiting for review",
4908
- "status.scheduled": "Scheduled",
5270
+ "status.needsAttention": "Needs attention",
4909
5271
  "status.completed": "Completed",
4910
5272
  "hover.created": "Created {time}",
4911
5273
  "hover.copied": "Copied",
@@ -4927,7 +5289,7 @@ window.__ModuleLoader__.load({
4927
5289
  "settings.badge.showScheduled": "Show the scheduled icon",
4928
5290
  "settings.badge.showScheduledHint": "When off, Sessions with scheduled reminders (from dsh-later) stop drawing the clock icon and drop out of the collapsed badge count and its hover details.",
4929
5291
  "settings.badge.showWaitingReview": "Show the waiting-for-review icon",
4930
- "settings.badge.showWaitingReviewHint": "When off, Sessions waiting for code review (from dsh-gitea-dispatch) stop drawing the list-pen icon and drop out of the collapsed badge count and its hover details.",
5292
+ "settings.badge.showWaitingReviewHint": "When off, Sessions waiting for code review (from dsh-gitea-dispatch) stop drawing the hourglass icon and drop out of the collapsed badge count and its hover details.",
4931
5293
  "settings.hover.section": "Hover details",
4932
5294
  "settings.hover.showDetails": "Show status detail lines",
4933
5295
  "settings.hover.showDetailsHint": "When off, the hover card shows only the workspace title, path, and creation time — no aggregated status lines.",
@@ -4951,14 +5313,6 @@ window.__ModuleLoader__.load({
4951
5313
  * 本包的补丁把行 id 也写成同名,但两者是不同概念,别改成 ENTRY_ID 的引用。
4952
5314
  */
4953
5315
  const CONFIG_CARD_KEY = "dsh-group-status";
4954
- /**
4955
- * Required services (cordis fiber inject). The target slots are declared by
4956
- * the ui-sidebar / ui-conversation applies, whose activation order relative
4957
- * to this one is NOT constrained: dsh.client.inject edges are informational
4958
- * (loading/prefetch metadata, never apply sequencing) and neither owner
4959
- * provides a waitable service. apply therefore depends on each slot
4960
- * declaration through `slots.inject()` instead of assuming order.
4961
- */
4962
5316
  const inject = [
4963
5317
  "slots",
4964
5318
  "sessions",
@@ -4998,6 +5352,7 @@ window.__ModuleLoader__.load({
4998
5352
  zh,
4999
5353
  en
5000
5354
  }), "ui-workspace: dictionaries");
5355
+ ctx.effect(() => () => disposeFacetStore(), "ui-workspace: status facets");
5001
5356
  const shortcutControls = createWorkspaceShortcutControls();
5002
5357
  const badgeSettings = createBadgeSettingsSource();
5003
5358
  ctx.slots.provideRoot({ hooks: { badgeSettings } });
@@ -5290,8 +5645,12 @@ window.__ModuleLoader__.load({
5290
5645
  return rpcError.code === "workspace/session-active" ? rpcError.details.activity : void 0;
5291
5646
  }
5292
5647
  //#endregion
5648
+ exports.FACET_TONE_RANK = FACET_TONE_RANK;
5649
+ exports.GROUP_STATUS_FACET_PROTOCOL_VERSION = GROUP_STATUS_FACET_PROTOCOL_VERSION;
5293
5650
  exports.apply = apply;
5651
+ exports.groupStatusFacetRegistry = registry;
5294
5652
  exports.inject = inject;
5653
+ exports.registerGroupStatusFacet = registerGroupStatusFacet;
5295
5654
  return module.exports;
5296
5655
  }
5297
5656
  });