dsh-task-ledger 0.0.10 → 0.0.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/client.js +141 -118
- package/dist/client.js.map +1 -1
- package/package.json +1 -1
package/dist/client.js
CHANGED
|
@@ -321,6 +321,8 @@ body{
|
|
|
321
321
|
/* chip / 标签小底板:浅色下要看得见 ⇒ 掺 7% 近黑;hover 加倍 */
|
|
322
322
|
--dtl-chip-bg:rgba(15,15,15,.07);
|
|
323
323
|
--dtl-chip-bg-hover:rgba(15,15,15,.14);
|
|
324
|
+
/* 图表「新增」柱:**实底**中性灰。半透明底压在卡上会发糊(用户 2026-10-17:「模模糊糊」) */
|
|
325
|
+
--dtl-chart-created:var(--dsw-static-neutral-300,#d4d4d8);
|
|
324
326
|
|
|
325
327
|
/* ── 圆角(宿主真值 4/8/12/16/20/28)──────────────────────────── */
|
|
326
328
|
--dtl-radius-xs:var(--dsw-radius-xs,4px);
|
|
@@ -353,6 +355,8 @@ body{
|
|
|
353
355
|
--dtl-line-lg:var(--dsw-font-s-14-line-height,22px);
|
|
354
356
|
--dtl-line-xl:var(--dsw-font-base-16-line-height,24px);
|
|
355
357
|
--dtl-font-mono:var(--ds-font-family-code,monospace);
|
|
358
|
+
/* 统计卡主数字:宿主字号族最大 16px,看板主数字要更大 ⇒ 单开一档(别处不许直接写 px) */
|
|
359
|
+
--dtl-font-stat:26px;
|
|
356
360
|
|
|
357
361
|
/* ── 投影 / 动效 / 层级 ──────────────────────────────────────── */
|
|
358
362
|
--dtl-shadow-1:var(--dsw-elevation-soft,0 4px 16px 0 #00000008);
|
|
@@ -380,6 +384,7 @@ body[data-ds-dark-theme]{
|
|
|
380
384
|
/* 暗色 chip 底板反过来掺白 */
|
|
381
385
|
--dtl-chip-bg:rgba(255,255,255,.08);
|
|
382
386
|
--dtl-chip-bg-hover:rgba(255,255,255,.16);
|
|
387
|
+
--dtl-chart-created:var(--dsw-static-neutral-600,#525252);
|
|
383
388
|
/* 语义色浅底在深色下压到 5%(8% 在深底上偏重) */
|
|
384
389
|
--dtl-business-soft:color-mix(in srgb,var(--dtl-business) 5%,transparent);
|
|
385
390
|
--dtl-success-soft:color-mix(in srgb,var(--dtl-success) 5%,transparent);
|
|
@@ -508,7 +513,7 @@ body[data-ds-dark-theme]{
|
|
|
508
513
|
/* 统计卡:可点、hover 淡蓝、选中蓝描边 */
|
|
509
514
|
.dtl-stat{display:flex;flex-direction:column;gap:var(--dtl-space-1);
|
|
510
515
|
padding:var(--dtl-space-3) var(--dtl-space-4);text-align:left;}
|
|
511
|
-
.dtl-stat__value{font-size:var(--dtl-font-
|
|
516
|
+
.dtl-stat__value{font-size:var(--dtl-font-stat);font-weight:600;line-height:1.15;font-variant-numeric:tabular-nums;}
|
|
512
517
|
.dtl-stat__hint{font-size:var(--dtl-font-xs);line-height:var(--dtl-line-xs);color:var(--dtl-fg-3);}
|
|
513
518
|
|
|
514
519
|
/* 列表行:头部可点,hover 淡蓝;展开行走 --dtl-open-bg */
|
|
@@ -533,50 +538,54 @@ body[data-ds-dark-theme]{
|
|
|
533
538
|
.dtl-badge[data-pulse="true"]{animation:dtl-pulse 900ms var(--dtl-ease) 1}
|
|
534
539
|
@keyframes dtl-pulse{0%{transform:scale(.6);opacity:.4}60%{transform:scale(1.15)}100%{transform:scale(1)}}
|
|
535
540
|
|
|
536
|
-
/*
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
/* 统计区整体:左 8 块(2 行 × 4 列)+ 右趋势图(参考图同款分栏)。
|
|
540
|
-
⚠️ 列数写死 4 → 2,绝不给 3:3 列会排出 3+1 的非对称(用户 2026-10-17)。 */
|
|
541
|
+
/* 统计区整体:左 8 块(2 行 × 4 列)+ 右趋势图,参考图同款分栏。
|
|
542
|
+
⚠️ 列数写死 4 → 2,绝不给 3:3 列会排出 3+1 的非对称(用户 2026-10-17)。
|
|
543
|
+
两栏**完全等高**:cards 两行 minmax(0,1fr) 均分 + 卡 height:100% ⇒ 图表卡与左边一样高。 */
|
|
541
544
|
.dtl-stats{display:grid;grid-template-columns:minmax(0,2fr) minmax(0,1fr);gap:var(--dtl-space-3);align-items:stretch;}
|
|
542
|
-
.dtl-stats__cards{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));grid-template-rows:repeat(2,
|
|
543
|
-
gap:var(--dtl-space-3);
|
|
545
|
+
.dtl-stats__cards{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));grid-template-rows:repeat(2,minmax(0,1fr));
|
|
546
|
+
gap:var(--dtl-space-3);}
|
|
547
|
+
.dtl-stats__cards > .dtl-stat{height:100%;box-sizing:border-box;}
|
|
544
548
|
@media (max-width:960px){.dtl-stats{grid-template-columns:minmax(0,1fr);}}
|
|
545
|
-
@media (max-width:640px){.dtl-stats__cards{grid-template-columns:repeat(2,minmax(0,1fr));}}
|
|
549
|
+
@media (max-width:640px){.dtl-stats__cards{grid-template-columns:repeat(2,minmax(0,1fr));grid-template-rows:repeat(4,minmax(0,1fr));}}
|
|
546
550
|
|
|
547
|
-
/*
|
|
548
|
-
.dtl-
|
|
551
|
+
/* 「只看我的」小复选框(替掉右上角的分段切换) */
|
|
552
|
+
.dtl-check{display:inline-flex;align-items:center;gap:5px;height:20px;margin-top:-2px;
|
|
553
|
+
font-size:var(--dtl-font-xs);color:var(--dtl-fg-3);cursor:pointer;user-select:none;white-space:nowrap;}
|
|
554
|
+
.dtl-check input{width:12px;height:12px;margin:0;accent-color:var(--dtl-business);cursor:pointer;}
|
|
555
|
+
.dtl-check:hover{color:var(--dtl-fg-2);}
|
|
556
|
+
|
|
557
|
+
/* 趋势分组柱状图:绘图区高度**随卡拉伸**(flex),柱 / 刻度 / 网格全用百分比 ⇒ 与卡等高 */
|
|
558
|
+
.dtl-gchart-wrap{flex:1 1 auto;min-height:0;display:flex;flex-direction:column;padding-left:30px;padding-bottom:18px;}
|
|
559
|
+
.dtl-gchart{position:relative;flex:1 1 auto;min-height:96px;border-bottom:1px solid var(--dtl-border);}
|
|
549
560
|
.dtl-gchart__grid{position:absolute;left:0;right:0;border-top:1px solid var(--dtl-border-faint);pointer-events:none;}
|
|
550
|
-
.dtl-gchart__ytick{position:absolute;left
|
|
561
|
+
.dtl-gchart__ytick{position:absolute;left:-30px;width:26px;text-align:right;font-size:10px;line-height:12px;
|
|
551
562
|
color:var(--dtl-fg-3);transform:translateY(50%);font-variant-numeric:tabular-nums;}
|
|
552
|
-
.dtl-gchart__avg{position:absolute;left:0;right:0;border-top:1px dashed var(--dtl-border-strong);pointer-events:none;}
|
|
553
|
-
.dtl-gchart__avgtag{position:absolute;right:0;font-size:10px;line-height:12px;color:var(--dtl-fg-3);
|
|
554
|
-
background:var(--dtl-surface-1);padding:0 2px;transform:translateY(-100%);}
|
|
555
563
|
.dtl-gchart__col{position:absolute;top:0;bottom:0;min-width:0;}
|
|
556
|
-
|
|
564
|
+
/* ⚠️ pair 必须 top/bottom 双锚(或 height:100%):绝对定位 + 只给 bottom 时高度是 auto,
|
|
565
|
+
子元素的 height:% 没有可解析的包含块 ⇒ 整根柱子塌成 0(2026-10-17 真机教训:
|
|
566
|
+
改百分比柱高后「一个柱子都看不见,只剩数字」)。 */
|
|
567
|
+
.dtl-gchart__pair{position:absolute;top:0;bottom:0;left:50%;transform:translateX(-50%);
|
|
557
568
|
display:flex;align-items:flex-end;gap:2px;}
|
|
558
|
-
.dtl-gchart__bar{width:
|
|
569
|
+
.dtl-gchart__bar{width:10px;min-height:0;border-radius:var(--dtl-radius-xs) var(--dtl-radius-xs) 0 0;}
|
|
559
570
|
.dtl-gchart__bar--done{background:var(--dtl-business);}
|
|
560
|
-
.dtl-gchart__bar--created{background:var(--dtl-
|
|
571
|
+
.dtl-gchart__bar--created{background:var(--dtl-chart-created);}
|
|
561
572
|
.dtl-gchart__col:hover .dtl-gchart__bar--done{background:var(--dtl-accent);}
|
|
562
573
|
.dtl-gchart__val{position:absolute;left:50%;transform:translateX(-50%);font-size:10px;line-height:12px;
|
|
563
574
|
color:var(--dtl-fg-2);font-variant-numeric:tabular-nums;white-space:nowrap;}
|
|
564
575
|
.dtl-gchart__label{position:absolute;left:0;right:0;bottom:-16px;text-align:center;
|
|
565
576
|
font-size:10px;line-height:14px;color:var(--dtl-fg-3);white-space:nowrap;}
|
|
577
|
+
/* hover 浮卡(参考图同款:日期 + 两个数;原生 title 太慢、长相不一) */
|
|
578
|
+
.dtl-gchart__tip{position:absolute;top:0;z-index:2;min-width:104px;box-sizing:border-box;
|
|
579
|
+
padding:6px 8px;border:1px solid var(--dtl-border);border-radius:var(--dtl-radius-sm);
|
|
580
|
+
background:var(--dtl-surface-1);box-shadow:var(--dtl-shadow-2);
|
|
581
|
+
font-size:var(--dtl-font-xs);line-height:var(--dtl-line-sm);color:var(--dtl-fg-2);pointer-events:none;}
|
|
582
|
+
.dtl-gchart__tip b{display:block;color:var(--dtl-fg);font-weight:600;}
|
|
583
|
+
.dtl-gchart__tip i{display:inline-block;width:8px;height:8px;border-radius:2px;margin-right:4px;}
|
|
566
584
|
.dtl-gchart-legend{display:flex;align-items:center;gap:var(--dtl-space-3);font-size:var(--dtl-font-xs);color:var(--dtl-fg-2);}
|
|
567
585
|
.dtl-gchart-legend i{display:inline-block;width:8px;height:8px;border-radius:2px;margin-right:4px;}
|
|
568
586
|
|
|
569
587
|
/* 过程正文:宽度受限、长行折行 */
|
|
570
588
|
.dtl-worklog{max-width:72ch;font-size:var(--dtl-font-md);line-height:1.6}
|
|
571
|
-
|
|
572
|
-
/* 趋势柱状图:div 柱 + token 色,无新依赖 */
|
|
573
|
-
.dtl-chart{display:flex;align-items:flex-end;gap:var(--dtl-space-2);height:120px;}
|
|
574
|
-
.dtl-chart__col{display:flex;flex-direction:column;justify-content:flex-end;align-items:center;gap:var(--dtl-space-1);flex:1 1 0;height:100%;min-width:0;}
|
|
575
|
-
.dtl-chart__bar{width:100%;max-width:28px;min-height:2px;border-radius:var(--dtl-radius-xs) var(--dtl-radius-xs) 0 0;
|
|
576
|
-
background:var(--dtl-business);transition:background-color var(--dtl-dur-fast) var(--dtl-ease);}
|
|
577
|
-
.dtl-chart__col:hover .dtl-chart__bar{background:var(--dtl-accent);}
|
|
578
|
-
.dtl-chart__val{font-size:var(--dtl-font-xs);line-height:var(--dtl-line-xs);color:var(--dtl-fg-2);font-variant-numeric:tabular-nums;}
|
|
579
|
-
.dtl-chart__label{font-size:10px;line-height:var(--dtl-line-xs);color:var(--dtl-fg-3);white-space:nowrap;}
|
|
580
589
|
`;
|
|
581
590
|
const STYLE_ID = "dtl-ui-base";
|
|
582
591
|
/** 幂等注入 token 层 + 控件皮肤 + 基础层(面板与浮标谁先挂载都只注入一次)。 */
|
|
@@ -696,69 +705,112 @@ body[data-ds-dark-theme]{
|
|
|
696
705
|
color: "var(--dtl-fg-2)"
|
|
697
706
|
} }, props.hint));
|
|
698
707
|
}
|
|
708
|
+
/** 台账图标(黑白线条,描边走 currentColor ⇒ 随主题 / 选中态反色)。
|
|
709
|
+
* 侧栏条目与右上浮标共用这一份(图标只画一次,别处不许再画第二个)。 */
|
|
710
|
+
function LedgerIcon(props) {
|
|
711
|
+
const size = props.size ?? 18;
|
|
712
|
+
const stroke = {
|
|
713
|
+
fill: "none",
|
|
714
|
+
stroke: "currentColor",
|
|
715
|
+
strokeWidth: 7,
|
|
716
|
+
strokeLinecap: "round",
|
|
717
|
+
strokeLinejoin: "round"
|
|
718
|
+
};
|
|
719
|
+
return (0, react.createElement)("svg", {
|
|
720
|
+
width: size,
|
|
721
|
+
height: size,
|
|
722
|
+
viewBox: "0 0 128 128",
|
|
723
|
+
"aria-hidden": true
|
|
724
|
+
}, (0, react.createElement)("path", {
|
|
725
|
+
d: "M96 34 A10 10 0 0 1 106 44 V102 A10 10 0 0 1 96 112 H32 A10 10 0 0 1 22 102 V44 A10 10 0 0 1 32 34 Z",
|
|
726
|
+
...stroke
|
|
727
|
+
}), (0, react.createElement)("path", {
|
|
728
|
+
d: "M50 22 V46 M78 22 V46",
|
|
729
|
+
...stroke
|
|
730
|
+
}), (0, react.createElement)("path", {
|
|
731
|
+
d: "M44 66 H84 M44 86 H70",
|
|
732
|
+
...stroke
|
|
733
|
+
}));
|
|
734
|
+
}
|
|
699
735
|
/** 千位压缩(1.2k / 12k):y 轴刻度与柱顶数值用,避免 4 位数把刻度挤宽。 */
|
|
700
736
|
function compactCount(value) {
|
|
701
737
|
if (value >= 1e4) return `${Math.round(value / 1e3)}k`;
|
|
702
738
|
if (value >= 1e3) return `${(value / 1e3).toFixed(1)}k`;
|
|
703
739
|
return String(value);
|
|
704
740
|
}
|
|
705
|
-
/** 分组柱状图(div
|
|
706
|
-
*
|
|
741
|
+
/** 分组柱状图(div 柱,无新依赖):完成(蓝)/ 新增(灰)各一细柱。
|
|
742
|
+
* - 高度全用百分比 ⇒ 绘图区随卡片拉伸,图表卡与左边 8 块**完全等高**;
|
|
743
|
+
* - y 轴三档刻度 + 淡网格线;柱顶只标「完成」(16 个数值标签会糊成一片);
|
|
744
|
+
* - hover 出浮卡(日期 + 两个数)——参考图同款,原生 title 太慢;
|
|
745
|
+
* - **没有**均值线与长注释(用户 2026-10-17:均值不要、注释太长不要)。 */
|
|
707
746
|
function GroupedBarChart(props) {
|
|
708
747
|
ensureUiBase();
|
|
709
|
-
const
|
|
748
|
+
const [hover, setHover] = (0, react.useState)(null);
|
|
710
749
|
const top = Math.max(1, props.weeks.reduce((peak, week) => Math.max(peak, week.done, week.created), 0));
|
|
711
|
-
const doneTotal = props.weeks.reduce((sum, week) => sum + week.done, 0);
|
|
712
|
-
const avg = props.weeks.length === 0 ? 0 : doneTotal / props.weeks.length;
|
|
713
750
|
const ticks = [
|
|
714
751
|
0,
|
|
715
752
|
top / 2,
|
|
716
753
|
top
|
|
717
754
|
];
|
|
718
|
-
const
|
|
755
|
+
const pct = (value) => `${value / top * 100}%`;
|
|
719
756
|
const columnStyle = (index) => ({
|
|
720
757
|
left: `${index / props.weeks.length * 100}%`,
|
|
721
758
|
right: `${(props.weeks.length - index - 1) / props.weeks.length * 100}%`
|
|
722
759
|
});
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
760
|
+
const tipStyle = (index) => {
|
|
761
|
+
if (index <= 1) return { left: 0 };
|
|
762
|
+
if (index >= props.weeks.length - 2) return { right: 0 };
|
|
763
|
+
return {
|
|
764
|
+
left: `${(index + .5) / props.weeks.length * 100}%`,
|
|
765
|
+
transform: "translateX(-50%)"
|
|
766
|
+
};
|
|
767
|
+
};
|
|
768
|
+
return (0, react.createElement)("div", { className: "dtl-gchart-wrap" }, (0, react.createElement)("div", {
|
|
728
769
|
className: "dtl-gchart",
|
|
729
770
|
role: "img",
|
|
730
|
-
"aria-label": props.ariaLabel
|
|
771
|
+
"aria-label": props.ariaLabel,
|
|
772
|
+
onMouseLeave: () => {
|
|
773
|
+
setHover(null);
|
|
774
|
+
}
|
|
731
775
|
}, ticks.map((tick) => (0, react.createElement)("div", {
|
|
732
776
|
key: `grid-${String(tick)}`,
|
|
733
777
|
className: "dtl-gchart__grid",
|
|
734
|
-
style: { bottom:
|
|
778
|
+
style: { bottom: pct(tick) }
|
|
735
779
|
})), ticks.map((tick) => (0, react.createElement)("span", {
|
|
736
780
|
key: `tick-${String(tick)}`,
|
|
737
781
|
className: "dtl-gchart__ytick",
|
|
738
|
-
style: { bottom:
|
|
739
|
-
}, compactCount(Math.round(tick)))),
|
|
740
|
-
className: "dtl-gchart__avg",
|
|
741
|
-
style: { bottom: `${heightOf(avg)}px` }
|
|
742
|
-
}, (0, react.createElement)("span", { className: "dtl-gchart__avgtag" }, props.avgLabel)) : null, props.weeks.map((week, index) => (0, react.createElement)("div", {
|
|
782
|
+
style: { bottom: pct(tick) }
|
|
783
|
+
}, compactCount(Math.round(tick)))), props.weeks.map((week, index) => (0, react.createElement)("div", {
|
|
743
784
|
key: week.label,
|
|
744
785
|
className: "dtl-gchart__col",
|
|
745
786
|
style: columnStyle(index),
|
|
746
|
-
|
|
787
|
+
onMouseEnter: () => {
|
|
788
|
+
setHover(index);
|
|
789
|
+
}
|
|
747
790
|
}, (0, react.createElement)("div", { className: "dtl-gchart__pair" }, (0, react.createElement)("span", {
|
|
748
791
|
className: "dtl-gchart__bar dtl-gchart__bar--done",
|
|
749
|
-
style: {
|
|
792
|
+
style: {
|
|
793
|
+
height: pct(week.done),
|
|
794
|
+
minHeight: week.done > 0 ? "2px" : void 0
|
|
795
|
+
}
|
|
750
796
|
}), (0, react.createElement)("span", {
|
|
751
797
|
className: "dtl-gchart__bar dtl-gchart__bar--created",
|
|
752
|
-
style: {
|
|
798
|
+
style: {
|
|
799
|
+
height: pct(week.created),
|
|
800
|
+
minHeight: week.created > 0 ? "2px" : void 0
|
|
801
|
+
}
|
|
753
802
|
})), week.done === 0 ? null : (0, react.createElement)("span", {
|
|
754
803
|
className: "dtl-gchart__val",
|
|
755
|
-
style: { bottom:
|
|
756
|
-
}, String(week.done)), (0, react.createElement)("span", { className: "dtl-gchart__label" }, week.label))))
|
|
804
|
+
style: { bottom: `calc(${pct(week.done)} + 3px)` }
|
|
805
|
+
}, String(week.done)), (0, react.createElement)("span", { className: "dtl-gchart__label" }, week.label))), hover === null ? null : (0, react.createElement)("div", {
|
|
806
|
+
className: "dtl-gchart__tip",
|
|
807
|
+
style: tipStyle(hover)
|
|
808
|
+
}, (0, react.createElement)("b", null, props.weeks[hover].title), (0, react.createElement)("div", null, (0, react.createElement)("i", { style: { background: "var(--dtl-business)" } }), `${props.doneLabel} ${props.weeks[hover].done}`), (0, react.createElement)("div", null, (0, react.createElement)("i", { style: { background: "var(--dtl-chart-created)" } }), `${props.createdLabel} ${props.weeks[hover].created}`))));
|
|
757
809
|
}
|
|
758
810
|
/** 图例(完成 / 新增 两个色块)。 */
|
|
759
811
|
function ChartLegend(props) {
|
|
760
812
|
ensureUiBase();
|
|
761
|
-
return (0, react.createElement)("div", { className: "dtl-gchart-legend" }, (0, react.createElement)("span", null, (0, react.createElement)("i", { style: { background: "var(--dtl-business)" } }), props.doneLabel), (0, react.createElement)("span", null, (0, react.createElement)("i", { style: { background: "var(--dtl-
|
|
813
|
+
return (0, react.createElement)("div", { className: "dtl-gchart-legend" }, (0, react.createElement)("span", null, (0, react.createElement)("i", { style: { background: "var(--dtl-business)" } }), props.doneLabel), (0, react.createElement)("span", null, (0, react.createElement)("i", { style: { background: "var(--dtl-chart-created)" } }), props.createdLabel));
|
|
762
814
|
}
|
|
763
815
|
//#endregion
|
|
764
816
|
//#region src/client/task-row.ts
|
|
@@ -965,7 +1017,7 @@ body[data-ds-dark-theme]{
|
|
|
965
1017
|
display: "flex",
|
|
966
1018
|
flexDirection: "column",
|
|
967
1019
|
gap: "var(--dtl-space-2)",
|
|
968
|
-
padding: "var(--dtl-space-
|
|
1020
|
+
padding: "var(--dtl-space-2) var(--dtl-space-3)"
|
|
969
1021
|
};
|
|
970
1022
|
const filterRowStyle = {
|
|
971
1023
|
display: "flex",
|
|
@@ -1049,24 +1101,19 @@ body[data-ds-dark-theme]{
|
|
|
1049
1101
|
label: bucket.week.slice(5),
|
|
1050
1102
|
done: bucket.done,
|
|
1051
1103
|
created: bucket.created,
|
|
1052
|
-
title: t("
|
|
1053
|
-
week: bucket.week,
|
|
1054
|
-
done: bucket.done,
|
|
1055
|
-
created: bucket.created
|
|
1056
|
-
})
|
|
1104
|
+
title: `${bucket.week}|${t("chartLegendDone")} ${bucket.done}|${t("chartLegendCreated")} ${bucket.created}`
|
|
1057
1105
|
})), [
|
|
1058
1106
|
board,
|
|
1059
1107
|
chartScope,
|
|
1060
1108
|
t
|
|
1061
1109
|
]);
|
|
1062
|
-
const chartDoneTotal = chartWeeks.reduce((sum, week) => sum + week.done, 0);
|
|
1063
1110
|
const chartEmpty = chartWeeks.every((week) => week.done === 0 && week.created === 0);
|
|
1064
1111
|
return (0, react.createElement)("div", { style: pageStyle }, (0, react.createElement)("div", { style: {
|
|
1065
1112
|
display: "flex",
|
|
1066
1113
|
justifyContent: "center"
|
|
1067
1114
|
} }, (0, react.createElement)("div", { style: {
|
|
1068
1115
|
width: "100%",
|
|
1069
|
-
maxWidth: "
|
|
1116
|
+
maxWidth: "1280px",
|
|
1070
1117
|
minWidth: 0,
|
|
1071
1118
|
boxSizing: "border-box"
|
|
1072
1119
|
} }, (0, react.createElement)("div", { style: headerStyle }, (0, react.createElement)("div", { style: {
|
|
@@ -1130,7 +1177,7 @@ body[data-ds-dark-theme]{
|
|
|
1130
1177
|
status: "blocked",
|
|
1131
1178
|
workspace: ""
|
|
1132
1179
|
}),
|
|
1133
|
-
title: t("
|
|
1180
|
+
title: t("statStaleTitle", { n: stats.staleDays }),
|
|
1134
1181
|
onClick: () => {
|
|
1135
1182
|
setQuery({
|
|
1136
1183
|
scope: "all",
|
|
@@ -1144,39 +1191,25 @@ body[data-ds-dark-theme]{
|
|
|
1144
1191
|
}, (0, react.createElement)("div", { style: {
|
|
1145
1192
|
display: "flex",
|
|
1146
1193
|
alignItems: "center",
|
|
1147
|
-
justifyContent: "
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
label: t("scopeLabel"),
|
|
1156
|
-
size: "sm",
|
|
1157
|
-
variant: "inset",
|
|
1158
|
-
items: [{
|
|
1159
|
-
value: "all",
|
|
1160
|
-
label: t("chartScopeAll")
|
|
1161
|
-
}, {
|
|
1162
|
-
value: "mine",
|
|
1163
|
-
label: t("chartScopeMine")
|
|
1164
|
-
}],
|
|
1165
|
-
onChange: setChartScope
|
|
1166
|
-
})), chartEmpty ? (0, react.createElement)("div", { style: {
|
|
1194
|
+
justifyContent: "flex-end"
|
|
1195
|
+
} }, (0, react.createElement)("label", { className: "dtl-check" }, (0, react.createElement)("input", {
|
|
1196
|
+
type: "checkbox",
|
|
1197
|
+
checked: chartScope === "mine",
|
|
1198
|
+
onChange: (event) => {
|
|
1199
|
+
setChartScope(event.target.checked ? "mine" : "all");
|
|
1200
|
+
}
|
|
1201
|
+
}), t("chartOnlyMine"))), chartEmpty ? (0, react.createElement)("div", { style: {
|
|
1167
1202
|
...hintStyle,
|
|
1168
1203
|
padding: "var(--dtl-space-4) 0"
|
|
1169
1204
|
} }, t("chartEmpty")) : (0, react.createElement)(GroupedBarChart, {
|
|
1170
1205
|
weeks: chartWeeks,
|
|
1171
1206
|
ariaLabel: t("chartTitle"),
|
|
1172
|
-
|
|
1207
|
+
doneLabel: t("chartLegendDone"),
|
|
1208
|
+
createdLabel: t("chartLegendCreated")
|
|
1173
1209
|
}), (0, react.createElement)(ChartLegend, {
|
|
1174
1210
|
doneLabel: t("chartLegendDone"),
|
|
1175
1211
|
createdLabel: t("chartLegendCreated")
|
|
1176
|
-
}), (0, react.createElement)("div", { style: {
|
|
1177
|
-
fontSize: "var(--dtl-font-xs)",
|
|
1178
|
-
color: "var(--dtl-fg-3)"
|
|
1179
|
-
} }, t("chartCaption")))), (0, react.createElement)("div", { style: filterRowStyle }, (0, react.createElement)(Segmented, {
|
|
1212
|
+
}))), (0, react.createElement)("div", { style: filterRowStyle }, (0, react.createElement)(Segmented, {
|
|
1180
1213
|
id: "dtl-scope",
|
|
1181
1214
|
value: state.query.scope,
|
|
1182
1215
|
label: t("scopeLabel"),
|
|
@@ -1332,14 +1365,15 @@ body[data-ds-dark-theme]{
|
|
|
1332
1365
|
loadFailed: "读取失败",
|
|
1333
1366
|
statsPlaceholder: "统计报表尚未开建(另立任务):这里以后放分类分布、按人完成数、时间线。",
|
|
1334
1367
|
statMyActive: "我的待办",
|
|
1335
|
-
statMyActiveHint: "
|
|
1368
|
+
statMyActiveHint: "阻塞 {n} 条",
|
|
1336
1369
|
statOpen: "待认领",
|
|
1337
|
-
statOpenHint: "
|
|
1370
|
+
statOpenHint: "最久未认领 {n} 天",
|
|
1338
1371
|
statDoing: "进行中",
|
|
1339
|
-
statDoingHint: "
|
|
1372
|
+
statDoingHint: "他人负责 {n} 条",
|
|
1340
1373
|
statBlockedStale: "阻塞 · 停滞",
|
|
1341
1374
|
statStaleWord: "停滞",
|
|
1342
1375
|
statStaleHint: "{n} 条",
|
|
1376
|
+
statStaleTitle: "停滞 = 进行中或阻塞超过 {n} 天未更新",
|
|
1343
1377
|
statWeekDone: "本周完成",
|
|
1344
1378
|
statLastWeekDone: "上周完成",
|
|
1345
1379
|
statMonthDone: "本月完成",
|
|
@@ -1349,10 +1383,11 @@ body[data-ds-dark-theme]{
|
|
|
1349
1383
|
daysValue: "{n} 天",
|
|
1350
1384
|
chartTitle: "近 8 周",
|
|
1351
1385
|
chartCaption: "完成(按结案时间)/ 新增(按创建时间),周一起桶;我的 = owner 是我",
|
|
1352
|
-
chartBucket: "{week}
|
|
1386
|
+
chartBucket: "{week} 当周:完成 {done} 条,新增 {created} 条",
|
|
1353
1387
|
chartEmpty: "近 8 周没有记录",
|
|
1354
1388
|
chartScopeAll: "所有",
|
|
1355
1389
|
chartScopeMine: "我的",
|
|
1390
|
+
chartOnlyMine: "只看我的",
|
|
1356
1391
|
chartLegendDone: "完成",
|
|
1357
1392
|
chartLegendCreated: "新增",
|
|
1358
1393
|
chartAvg: "周均 {n}",
|
|
@@ -1408,14 +1443,15 @@ body[data-ds-dark-theme]{
|
|
|
1408
1443
|
loadFailed: "Load failed",
|
|
1409
1444
|
statsPlaceholder: "Stats reports are not built yet (separate task): category breakdown, completions by person, timeline.",
|
|
1410
1445
|
statMyActive: "My todos",
|
|
1411
|
-
statMyActiveHint: "{n}
|
|
1446
|
+
statMyActiveHint: "blocked {n}",
|
|
1412
1447
|
statOpen: "Unclaimed",
|
|
1413
|
-
statOpenHint: "
|
|
1448
|
+
statOpenHint: "unclaimed {n}d at most",
|
|
1414
1449
|
statDoing: "In progress",
|
|
1415
|
-
statDoingHint: "
|
|
1450
|
+
statDoingHint: "owned by others: {n}",
|
|
1416
1451
|
statBlockedStale: "Blocked · Stale",
|
|
1417
1452
|
statStaleWord: "Stale",
|
|
1418
1453
|
statStaleHint: "{n}",
|
|
1454
|
+
statStaleTitle: "stale = active or blocked with no update for {n}+ days",
|
|
1419
1455
|
statWeekDone: "Done this week",
|
|
1420
1456
|
statLastWeekDone: "Done last week",
|
|
1421
1457
|
statMonthDone: "Done this month",
|
|
@@ -1429,6 +1465,7 @@ body[data-ds-dark-theme]{
|
|
|
1429
1465
|
chartEmpty: "Nothing in the last 8 weeks",
|
|
1430
1466
|
chartScopeAll: "All",
|
|
1431
1467
|
chartScopeMine: "Mine",
|
|
1468
|
+
chartOnlyMine: "Mine only",
|
|
1432
1469
|
chartLegendDone: "Done",
|
|
1433
1470
|
chartLegendCreated: "Created",
|
|
1434
1471
|
chartAvg: "avg {n}/wk",
|
|
@@ -1493,32 +1530,6 @@ body[data-ds-dark-theme]{
|
|
|
1493
1530
|
* 角标 = 我名下 `doing` + `blocked` 计数(host 统计真值,SSE 已否 ⇒ 取自同一份快照,>0 才画);
|
|
1494
1531
|
* 0→n 跳变时一次脉冲动画,不自动消失。宿主**无**通知 API ⇒ 「通知」就是这个角标。
|
|
1495
1532
|
*/
|
|
1496
|
-
/** 台账图标(黑白线条,描边走 currentColor ⇒ 随主题反色)。 */
|
|
1497
|
-
function LedgerIcon(props) {
|
|
1498
|
-
const size = props.size ?? 18;
|
|
1499
|
-
const stroke = {
|
|
1500
|
-
fill: "none",
|
|
1501
|
-
stroke: "currentColor",
|
|
1502
|
-
strokeWidth: 7,
|
|
1503
|
-
strokeLinecap: "round",
|
|
1504
|
-
strokeLinejoin: "round"
|
|
1505
|
-
};
|
|
1506
|
-
return (0, react.createElement)("svg", {
|
|
1507
|
-
width: size,
|
|
1508
|
-
height: size,
|
|
1509
|
-
viewBox: "0 0 128 128",
|
|
1510
|
-
"aria-hidden": true
|
|
1511
|
-
}, (0, react.createElement)("path", {
|
|
1512
|
-
d: "M96 34 A10 10 0 0 1 106 44 V102 A10 10 0 0 1 96 112 H32 A10 10 0 0 1 22 102 V44 A10 10 0 0 1 32 34 Z",
|
|
1513
|
-
...stroke
|
|
1514
|
-
}), (0, react.createElement)("path", {
|
|
1515
|
-
d: "M50 22 V46 M78 22 V46",
|
|
1516
|
-
...stroke
|
|
1517
|
-
}), (0, react.createElement)("path", {
|
|
1518
|
-
d: "M44 66 H84 M44 86 H70",
|
|
1519
|
-
...stroke
|
|
1520
|
-
}));
|
|
1521
|
-
}
|
|
1522
1533
|
function OverlayEntry(props) {
|
|
1523
1534
|
const { t, onOpen } = props;
|
|
1524
1535
|
ensureUiBase();
|
|
@@ -1577,6 +1588,8 @@ body[data-ds-dark-theme]{
|
|
|
1577
1588
|
* `t` 席位由宿主渲染器按注册项的 `locale:` 声明合成进组件 props。
|
|
1578
1589
|
*/
|
|
1579
1590
|
/** 命名空间(settings / locale / 面板 id 同名,便于对应)。 */
|
|
1591
|
+
/** 模块级 t 席位(侧栏 label 渲染期求值用;绑不上就回落键名,绝不上界面)。 */
|
|
1592
|
+
let runtimeT = (key) => key;
|
|
1580
1593
|
const NS = "dsh-task-ledger";
|
|
1581
1594
|
/** `main` 槽的 key(非 `conversation` ⇒ 不绑 Session)。 */
|
|
1582
1595
|
const PANEL_ID = NS;
|
|
@@ -1590,6 +1603,9 @@ body[data-ds-dark-theme]{
|
|
|
1590
1603
|
zh,
|
|
1591
1604
|
en
|
|
1592
1605
|
}));
|
|
1606
|
+
try {
|
|
1607
|
+
runtimeT = localeCtx.locale?.bind(NS) ?? runtimeT;
|
|
1608
|
+
} catch {}
|
|
1593
1609
|
});
|
|
1594
1610
|
let selectPanel = () => {};
|
|
1595
1611
|
ctx.inject(["layout"], (sub) => {
|
|
@@ -1599,6 +1615,13 @@ body[data-ds-dark-theme]{
|
|
|
1599
1615
|
};
|
|
1600
1616
|
});
|
|
1601
1617
|
ctx.inject(["slots"], (sub) => {
|
|
1618
|
+
sub.slots.inject("sidebar.panellist", () => sub.slots.register({
|
|
1619
|
+
name: "sidebar.panellist",
|
|
1620
|
+
id: PANEL_ID,
|
|
1621
|
+
order: 30,
|
|
1622
|
+
label: () => runtimeT("panelTitle"),
|
|
1623
|
+
locale: NS
|
|
1624
|
+
}, (props) => (0, react.createElement)(LedgerIcon, { size: props.size ?? 18 })));
|
|
1602
1625
|
sub.slots.inject("main", () => sub.slots.register({
|
|
1603
1626
|
name: "main",
|
|
1604
1627
|
key: PANEL_ID,
|
package/dist/client.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.js","names":["h","h","useState","useMemo","StateDot","IconChevronDownOutlineRegular","IconChevronRightOutlineRegular","MarkdownText","useSyncExternalStore","useState","useMemo","h","IconRefreshOutlineRegular","h","useSyncExternalStore","useRef","useState","h"],"sources":["../src/client/api.ts","../src/client/store.ts","../src/client/rows.ts","../src/client/format.ts","../src/client/tokens.ts","../src/client/ui.ts","../src/client/task-row.ts","../src/client/board-home.ts","../src/client/locales.ts","../src/client/overlay-entry.ts","../src/client/index.ts"],"sourcesContent":["/**\n * 数据面取数**单源**:所有 client → host 的 HTTP 都在这里,页面不许各写一份 fetch。\n *\n * 纪律:只做传输与形状校验,不造兜底假值(DSH 插件规则「数据真实」)——\n * 失败就抛,由调用方显示错误态;返回什么渲染什么。\n *\n * ⚠️ 必须带超时且 abort 要带 reason:一次挂起的请求会让面板永久停在「读取中」,\n * 而不带 reason 时浏览器把 error.message 填成「signal is aborted without reason」,\n * 看不出是超时(姊妹仓 2026-10-07 真机教训)。\n */\nimport type { BoardPayload, FilterOptions, HistoryRecord, TaskDetail } from './types'\n\n/** host 路由前缀(全仓唯一;与 host/routes.ts 的 API_PREFIX 对应)。 */\nexport const API_PREFIX = 'api/task-ledger'\n\nconst DEFAULT_TIMEOUT_MS = 8_000\n\n/**\n * 错误响应榨人话:host 路由(routes.ts)发的是 `{ error: \"...\" }`,那里的话是**能直接上界面的**\n * (带工作区名、python3 起不来之类的原文)。不带它界面上只剩「HTTP 500」,等于没报错。\n */\nexport function httpErrorMessage(status: number, body: string): string {\n try {\n const parsed = JSON.parse(body) as { error?: unknown }\n if (typeof parsed.error === 'string' && parsed.error !== '') return `HTTP ${status}:${parsed.error}`\n } catch { /* 不是 JSON:走原文 */ }\n const text = body.trim().slice(0, 200)\n return text === '' ? `HTTP ${String(status)}` : `HTTP ${String(status)}:${text}`\n}\n\n/** 带超时的 JSON 取数(相对路径,同源)。 */\nexport async function fetchJson<T>(path: string, init: RequestInit = {}, timeoutMs = DEFAULT_TIMEOUT_MS): Promise<T> {\n const controller = new AbortController()\n const timer = setTimeout(() => {\n controller.abort(new DOMException(`请求超时(${timeoutMs}ms 未响应)`, 'TimeoutError'))\n }, timeoutMs)\n try {\n const res = await fetch(`${API_PREFIX}${path}`, { ...init, signal: controller.signal, cache: 'no-store' })\n if (!res.ok) throw new Error(httpErrorMessage(res.status, await res.text().catch(() => '')))\n return await res.json() as T\n } finally {\n clearTimeout(timer)\n }\n}\n\n/** 首页快照(在办全量 + 两个完成类统计 + 各工作区身份)。 */\nexport function fetchBoard(): Promise<BoardPayload> {\n return fetchJson<BoardPayload>('/board')\n}\n\n/** 已结案列表(服务端按窗口 / 标签 / 分页过滤)。 */\nexport function fetchHistory(params: { since?: string; until?: string; tag?: string; limit?: number }): Promise<HistoryRecord[]> {\n const query = new URLSearchParams()\n if (params.since !== undefined) query.set('since', params.since)\n if (params.until !== undefined) query.set('until', params.until)\n if (params.tag !== undefined && params.tag !== '') query.set('tag', params.tag)\n if (params.limit !== undefined) query.set('limit', String(params.limit))\n const suffix = query.toString()\n return fetchJson<{ records: HistoryRecord[] }>(`/history${suffix === '' ? '' : `?${suffix}`}`)\n .then(body => body.records ?? [])\n}\n\n/** 单条在办 + 过程文档正文(展开行用)。 */\nexport function fetchTaskDetail(workspaceId: string, taskId: string): Promise<TaskDetail> {\n const query = new URLSearchParams({ ws: workspaceId, id: taskId })\n return fetchJson<TaskDetail>(`/task?${query.toString()}`)\n}\n\n/** 筛选项候选(在办全量 + 近 13 个月历史的去重 owner / category / tag)。 */\nexport function fetchFilters(): Promise<FilterOptions> {\n return fetchJson<FilterOptions>('/filters')\n}\n","/**\n * 看板数据仓(client 侧**唯一**取数入口):浮标角标与整页共用同一份快照,\n * 谁先挂载谁触发首读;刷新按钮走同一个 `refresh()`,**不轮询**(2026-10-17 拍板:\n * 后端纯按需,刷新时机归前端 = 打开面板首读 + 手动刷新)。\n *\n * 消费方式:`useSyncExternalStore(boardSubscribe, boardSnapshot)`。\n * ⚠️ `getSnapshot` 必须返回**稳定引用**(每次新建对象会被判定为「一直在变」⇒ 无限重渲染),\n * 故只在真变了时才换 `state` 对象。\n */\nimport { fetchBoard } from './api'\nimport type { BoardPayload } from './types'\n\n/** 范围:我的 / 全部工作区。 */\nexport type Scope = 'mine' | 'all'\n/** 状态档:进行中 = doing+blocked;停滞 = host 已标记的 stale。 */\nexport type StatusFilter = 'all' | 'active' | 'open' | 'blocked' | 'stale'\n\nexport interface Query {\n scope: Scope\n status: StatusFilter\n /** 工作区 id;'' = 全部工作区。 */\n workspace: string\n keyword: string\n}\n\nexport interface BoardState {\n status: 'loading' | 'ready' | 'error'\n board: BoardPayload | null\n error: string | null\n query: Query\n /** 手动刷新在途(按钮转圈 / 禁用)。 */\n refreshing: boolean\n}\n\nconst INITIAL_QUERY: Query = { scope: 'all', status: 'all', workspace: '', keyword: '' }\n\nlet state: BoardState = {\n status: 'loading',\n board: null,\n error: null,\n query: INITIAL_QUERY,\n refreshing: false,\n}\n\nconst listeners = new Set<() => void>()\n/** 首读只触发一次(谁先订阅谁触发),之后一律靠 `refresh()` / `setQuery()`。 */\nlet booted = false\n/** 在途轮次:迟到的响应按 token 丢弃,避免旧结果盖新结果。 */\nlet runToken = 0\n\nfunction emit(): void {\n for (const listener of [...listeners]) listener()\n}\n\nfunction set(patch: Partial<BoardState>): void {\n state = { ...state, ...patch }\n emit()\n}\n\nexport function boardSnapshot(): BoardState {\n return state\n}\n\nexport function boardSubscribe(listener: () => void): () => void {\n listeners.add(listener)\n if (!booted) {\n booted = true\n void refresh()\n }\n return () => { listeners.delete(listener) }\n}\n\n/** 重读:在办快照 + 完成类统计与近 8 周趋势(一次 `/board` 全带回)。 */\nexport async function refresh(): Promise<void> {\n const token = ++runToken\n set({ refreshing: true })\n try {\n const board = await fetchBoard()\n if (token !== runToken) return\n set({ status: 'ready', board, error: null })\n } catch (error) {\n if (token !== runToken) return\n set({ status: 'error', error: error instanceof Error ? error.message : String(error) })\n } finally {\n if (token === runToken) set({ refreshing: false })\n }\n}\n\n/** 改筛选:全部在浏览器里完成(快照已含全部在办),不发请求。 */\nexport function setQuery(patch: Partial<Query>): void {\n set({ query: { ...state.query, ...patch } })\n}\n","/**\n * 列表视图的筛选 / 分区 / 排序(**唯一**实现,纯函数、不碰网络):页面只渲染,不各写一份过滤。\n * 定稿见 docs/design/features/board-home.md §二.3:两区结构(我的待办平铺 / 按工作区分组),\n * 已结案**不在首页**(去统计 tab)。\n *\n * 单独成文件是为了能被冒烟直测(store.ts 引 api.ts,Node 剥类型解析不了无扩展名相对导入)。\n */\nimport type { BoardPayload, BoardTask, HistoryRecord, TaskRow } from './types'\nimport type { Query } from './store'\n\n/** 列表行:在办与已结案两套形状,行件按 kind 分支(不做「统一成一个假结构」的归一化)。 */\nexport type Row =\n | { kind: 'task'; key: string; task: TaskRow; mine: boolean }\n | { kind: 'closed'; key: string; record: HistoryRecord }\n\n/** 一个工作区分组。 */\nexport interface WorkspaceGroup {\n readonly workspaceId: string\n readonly title: string\n readonly doingCount: number\n readonly openCount: number\n readonly rows: readonly Row[]\n}\n\n/** 两区视图:第一区我的待办 + 第二区按工作区分组(两者不重复)。 */\nexport interface BoardView {\n readonly mine: readonly Row[]\n readonly groups: readonly WorkspaceGroup[]\n readonly total: number\n}\n\n/** 关键字命中(标题 / 任务号 / 人 / 分类 / 标签;已结案另含「完成了什么」)。 */\nfunction hitKeyword(keyword: string, parts: readonly (string | null | undefined)[]): boolean {\n const needle = keyword.trim().toLowerCase()\n if (needle === '') return true\n return parts.some(part => part?.toLowerCase().includes(needle) === true)\n}\n\n/** 「我的」判定:owner = 该工作区的当前 git 身份,且状态是 doing / blocked(与 host 统计同口径)。 */\nexport function isMine(task: TaskRow, board: BoardPayload | null): boolean {\n if (board === null) return false\n const identity = board.identities[task.workspace.id] ?? null\n if (identity === null || identity === '') return false\n return task.owner === identity && (task.status === 'doing' || task.status === 'blocked')\n}\n\n/** 状态档命中(停滞由 host 标记,前端不判阈值)。 */\nfunction statusHit(task: BoardTask, status: Query['status']): boolean {\n if (status === 'all') return true\n if (status === 'active') return task.status === 'doing' || task.status === 'blocked'\n if (status === 'open') return task.status === 'open'\n if (status === 'blocked') return task.status === 'blocked'\n return task.stale === true\n}\n\n/** 组内排序:别人的 doing/blocked 在前、待认领在后;各自按创建 / 开始时间倒序。 */\nfunction inGroupOrder(a: Row, b: Row): number {\n if (a.kind !== 'task' || b.kind !== 'task') return 0\n const rank = (task: TaskRow): number => (task.status === 'open' ? 1 : 0)\n if (rank(a.task) !== rank(b.task)) return rank(a.task) - rank(b.task)\n return String(b.task.created ?? b.task.started ?? '').localeCompare(String(a.task.created ?? a.task.started ?? ''))\n}\n\n/**\n * 当前筛选下的两区视图:\n * - 第一区「我的待办」= 我名下 doing/blocked(命中状态档 / 工作区 / 关键字);\n * - 第二区「按工作区」= 其余任务(别人的 doing/blocked + 全部待认领),按工作区分组;\n * - 范围=「我的」时只出第一区。\n */\nexport function selectView(current: { board: BoardPayload | null; query: Query }): BoardView {\n const { board, query } = current\n if (board === null) return { mine: [], groups: [], total: 0 }\n const tasks = board.tasks.filter((task) => {\n if (!statusHit(task, query.status)) return false\n if (query.workspace !== '' && task.workspace.id !== query.workspace) return false\n return hitKeyword(query.keyword, [task.title, task.id, task.owner, task.creator, task.category, ...(task.tags ?? [])])\n })\n const mineRows: Row[] = tasks\n .filter((task) => isMine(task, board))\n .sort((a, b) => String(b.created ?? b.started ?? '').localeCompare(String(a.created ?? a.started ?? '')))\n .map((task): Row => ({ kind: 'task', key: `${task.workspace.id}:${task.id}`, task, mine: true }))\n const rest = query.scope === 'mine'\n ? []\n : tasks.filter((task) => !isMine(task, board))\n const byWorkspace = new Map<string, { title: string; rows: Row[] }>()\n for (const task of rest) {\n const entry = byWorkspace.get(task.workspace.id) ?? { title: task.workspace.title, rows: [] }\n entry.rows.push({ kind: 'task', key: `${task.workspace.id}:${task.id}`, task, mine: false })\n byWorkspace.set(task.workspace.id, entry)\n }\n const groups: WorkspaceGroup[] = [...byWorkspace.entries()]\n .map(([workspaceId, entry]) => ({\n workspaceId,\n title: entry.title,\n doingCount: entry.rows.filter((row) => row.kind === 'task' && row.task.status !== 'open').length,\n openCount: entry.rows.filter((row) => row.kind === 'task' && row.task.status === 'open').length,\n rows: entry.rows.sort(inGroupOrder),\n }))\n .sort((a, b) => a.title.localeCompare(b.title, 'zh-Hans-CN'))\n return { mine: mineRows, groups, total: mineRows.length + groups.reduce((sum, group) => sum + group.rows.length, 0) }\n}\n\n/**\n * 已结案列表(统计 tab 用;首页不渲染已结案 ⇒ 这里只留数据面,供后续统计 tab 复用同一套筛选口径)。\n */\nexport function selectClosed(current: { history: readonly HistoryRecord[]; query: Query }): Row[] {\n return current.history\n .filter((record) => hitKeyword(current.query.keyword, [record.summary, record.workpackage, record.owner, record.category, ...(record.tags ?? [])]))\n .map((record, index) => ({ kind: 'closed', key: `${record.src ?? ''}:${record.time ?? ''}:${index}`, record }))\n}\n","/**\n * 客户端时间口径**单源**:补零 / 时刻串(本机时区)。\n * 解析失败按调用方给的兜底返回(不编造时间)。\n *\n * 2026-10-17 改版后首页不再做时间范围过滤(时间只作用于统计卡与趋势图,由 host 聚合)\n * ⇒ 原来的 `rangeToDates` / `withinRange` / `RangeKey` 一并删除;统计 tab 要做时间筛选时\n * 按当时口径在此重建,别处不许再写一份。\n */\nexport function pad2(value: number | string): string {\n return String(value).padStart(2, '0')\n}\n\n/** ISO → `YYYY-MM-DD HH:mm`(`seconds: true` 时补 `:ss`);解析失败返回 `fallback`(缺省原串)。 */\nexport function formatDateTime(iso: string, opts?: { seconds?: boolean; fallback?: string }): string {\n const ms = Date.parse(iso)\n if (Number.isNaN(ms)) return opts?.fallback ?? iso\n const d = new Date(ms)\n const base = `${d.getFullYear()}-${pad2(d.getMonth() + 1)}-${pad2(d.getDate())} ${pad2(d.getHours())}:${pad2(d.getMinutes())}`\n return opts?.seconds === true ? `${base}:${pad2(d.getSeconds())}` : base\n}\n","/**\n * 语义 token 层:`--dtl-*`(dtl = dsh-task-ledger)。\n *\n * 规矩(docs/design/ui-style.md §三,2026-10-17 对齐 dispatch 表后收严):\n * - **只有本文件定义 `--dtl-*`**;其它文件一律 `var(--dtl-*)` 消费,不许直接写 `var(--dsw-*)`;\n * - 每个 token 都带兜底值 ⇒ 宿主升级换名时不炸(最坏退到兜底色,不会白屏);\n * - 明暗差异只写在本文件末尾的 `body[data-ds-dark-theme]` 段:能靠宿主 alias 表达的\n * 一律不写覆盖(alias 自己随主题变),只有「固定中性面 / 半透明底」这类 alias 表达不了的才进该段。\n * - **离散刻度**:圆角 4/8/12/16/20/28、间距 4/8/12/16、控件高 24/28/32、字号 11/12/13/14/16\n * ——业务文件不许出现 5/7/9px、不许自造高度档(要加档位改本文件)。\n *\n * 明暗判据 = `body[data-ds-dark-theme]`(宿主 0.2.0-rc.2 源码核实:启动脚本 toggleAttribute 写入)。\n * 插件**禁用** `prefers-color-scheme`(它跟系统、不跟用户在宿主里的选择)。\n * 因为判据是纯 CSS 选择器 ⇒ 宿主切主题即生效,**不需要 MutationObserver**。\n *\n * 挂载点 = `body`:插件界面(含将来 portal 到 body 的弹层)全在 body 内,零调用点成本。\n */\nexport const UI_TOKENS_CSS = `\nbody{\n /* ── 文字四档 + 反色面 ───────────────────────────────────────── */\n --dtl-fg:var(--dsw-alias-label-primary,#1f2328);\n --dtl-fg-2:var(--dsw-alias-label-secondary,#5c6370);\n --dtl-fg-3:var(--dsw-alias-label-tertiary,#767d87);\n --dtl-fg-4:var(--dsw-alias-label-caption,#9aa1ab);\n --dtl-fg-dim:var(--dsw-alias-label-dimmed,#b9bfc7);\n /* 反色面上的字(角标 / 实心钮):暗色下是 bluish-1000 ⇒ 用 -foreground 而非 -inverted */\n --dtl-fg-inverse:var(--dsw-alias-label-primary-foreground,#fff);\n\n /* ── 面 ──────────────────────────────────────────────────────── */\n --dtl-surface-1:var(--dsw-alias-bg-layer-1,#fff);\n --dtl-surface-2:var(--dsw-alias-bg-layer-2,#fff);\n --dtl-surface-base:var(--dsw-alias-bg-base,#fff);\n /* 「选中亮片」底盘(配 shadow-raised 用) */\n --dtl-surface-raised:var(--dsw-alias-bg-layer-1,#fff);\n /* 固定中性实面(自绘浮标 / 角标这类必须自己变的东西) */\n --dtl-solid:var(--dsw-static-neutral-00,#fff);\n --dtl-on-solid:var(--dsw-alias-label-primary,#1f2328);\n /* 斑马纹 / 次层面(浅色下近白、深色下深一档) */\n --dtl-plate:var(--dsw-static-neutral-50,#fafafa);\n --dtl-plate-hover:var(--dsw-static-neutral-100,#f5f5f5);\n /* 表头底:浅色比斑马纹再深一档(两者不能撞色) */\n --dtl-head-bg:var(--dsw-static-neutral-100,#f5f5f5);\n\n /* ── 描边四档(宿主真值 l1 4% / l2 10% / l3 12% / l4 16%)────── */\n --dtl-border-faint:var(--dsw-alias-border-l1,#0000000a);\n --dtl-border:var(--dsw-alias-border-l2,#0000001a);\n --dtl-border-strong:var(--dsw-alias-border-l3,#0000001f);\n --dtl-border-heavy:var(--dsw-alias-border-l4,#00000029);\n\n /* ── 语义色 ──────────────────────────────────────────────────── */\n /* 品牌「面」色:明色近黑、暗色近白(实心钮选中态即此),不是蓝 */\n --dtl-accent:var(--dsw-alias-brand-primary,#0f1115);\n /* 蓝色强调 / 选中 / 焦点一律走这条 */\n --dtl-business:var(--dsw-alias-state-business-primary,#3b5bdb);\n --dtl-success:var(--dsw-alias-state-success-primary,#22c55e);\n /* ⚠️ 宿主是 state-warn(不是 state-warning,后者无此定义) */\n --dtl-warning:var(--dsw-alias-state-warn-primary,#f59e0b);\n --dtl-danger:var(--dsw-alias-state-error-primary,#ec1313);\n --dtl-link:var(--dsw-alias-link,#3b5bdb);\n /* 语义色实面上的字(角标 / 实心提示):红绿黄都是中调 ⇒ 白字两个主题都可读 */\n --dtl-on-signal:#fff;\n /* 语义色浅底:color-mix 现算 ⇒ 跟随宿主 alias,主题一换自动成立 */\n --dtl-business-soft:color-mix(in srgb,var(--dtl-business) 8%,transparent);\n --dtl-success-soft:color-mix(in srgb,var(--dtl-success) 8%,transparent);\n --dtl-warning-soft:color-mix(in srgb,var(--dtl-warning) 8%,transparent);\n --dtl-danger-soft:color-mix(in srgb,var(--dtl-danger) 8%,transparent);\n\n /* ── 交互底 ──────────────────────────────────────────────────── */\n --dtl-hover:var(--dsw-alias-interactive-bg-hover,rgba(38,49,72,.06));\n --dtl-active:var(--dsw-alias-interactive-bg-active,#2631481a);\n /* 卡 / 行 hover 高亮:淡蓝(灰高亮在卡底上几乎看不出来) */\n --dtl-card-hover:rgba(37,99,235,.07);\n /* 展开行底:带透明度的蓝,透出卡片底色(灰底跟斑马纹分不出来) */\n --dtl-open-bg:rgba(37,99,235,.10);\n --dtl-open-bg-soft:rgba(37,99,235,.05);\n /* chip / 标签小底板:浅色下要看得见 ⇒ 掺 7% 近黑;hover 加倍 */\n --dtl-chip-bg:rgba(15,15,15,.07);\n --dtl-chip-bg-hover:rgba(15,15,15,.14);\n\n /* ── 圆角(宿主真值 4/8/12/16/20/28)──────────────────────────── */\n --dtl-radius-xs:var(--dsw-radius-xs,4px);\n --dtl-radius-sm:var(--dsw-radius-sm,8px);\n --dtl-radius-md:var(--dsw-radius-md,12px);\n --dtl-radius-lg:var(--dsw-radius-lg,16px);\n --dtl-radius-xl:var(--dsw-radius-xl,20px);\n --dtl-radius-panel:var(--dsw-radius-panel,28px);\n\n /* ── 间距四拍(只许这四档)────────────────────────────────────── */\n --dtl-space-1:4px;\n --dtl-space-2:8px;\n --dtl-space-3:12px;\n --dtl-space-4:16px;\n\n /* ── 控件高度:离散三档(调用点不许自定义高度)────────────────── */\n --dtl-control-h-sm:24px;\n --dtl-control-h-md:28px;\n --dtl-control-h-lg:32px;\n\n /* ── 字号 / 行高(直绑宿主字号族 11/12/13/14/16)──────────────── */\n --dtl-font-xs:var(--dsw-font-xxxs-11-font-size,11px);\n --dtl-font-sm:var(--dsw-font-xxs-12-font-size,12px);\n --dtl-font-md:var(--dsw-font-xs-13-font-size,13px);\n --dtl-font-lg:var(--dsw-font-s-14-font-size,14px);\n --dtl-font-xl:var(--dsw-font-base-16-font-size,16px);\n --dtl-line-xs:var(--dsw-font-xxxs-11-line-height,14px);\n --dtl-line-sm:var(--dsw-font-xxs-12-line-height,18px);\n --dtl-line-md:var(--dsw-font-xs-13-line-height,20px);\n --dtl-line-lg:var(--dsw-font-s-14-line-height,22px);\n --dtl-line-xl:var(--dsw-font-base-16-line-height,24px);\n --dtl-font-mono:var(--ds-font-family-code,monospace);\n\n /* ── 投影 / 动效 / 层级 ──────────────────────────────────────── */\n --dtl-shadow-1:var(--dsw-elevation-soft,0 4px 16px 0 #00000008);\n --dtl-shadow-raised:var(--dsw-elevation-soft,0 4px 16px 0 #00000008);\n --dtl-shadow-2:var(--dsw-shadow-lv3,0 12px 32px 0 #00000014);\n --dtl-dur:var(--ds-transition-duration,.2s);\n --dtl-dur-fast:var(--ds-transition-duration-fast,.12s);\n --dtl-ease:var(--ds-ease-in-out,cubic-bezier(.4,0,.2,1));\n --dtl-z-badge:20;\n}\n\n/* ── 明暗差异的唯一落点 ─────────────────────────────────────────\n 只放 alias 表达不了的固定面 / 半透明底;颜色若能靠 alias 自动跟随,一律不写在这里。 */\nbody[data-ds-dark-theme]{\n --dtl-solid:var(--dsw-static-neutral-900,#0f0f0f);\n --dtl-on-solid:var(--dsw-static-neutral-00,#fff);\n --dtl-plate:var(--dsw-static-neutral-850,#212123);\n --dtl-plate-hover:var(--dsw-static-neutral-800,#292929);\n /* 暗色表头反过来:比卡片面亮、比斑马纹浅一档 */\n --dtl-head-bg:var(--dtl-surface-2);\n /* 暗色下蓝要更亮、透明度更高才压得住深底 */\n --dtl-open-bg:rgba(96,165,250,.18);\n --dtl-open-bg-soft:rgba(96,165,250,.08);\n --dtl-card-hover:rgba(96,165,250,.13);\n /* 暗色 chip 底板反过来掺白 */\n --dtl-chip-bg:rgba(255,255,255,.08);\n --dtl-chip-bg-hover:rgba(255,255,255,.16);\n /* 语义色浅底在深色下压到 5%(8% 在深底上偏重) */\n --dtl-business-soft:color-mix(in srgb,var(--dtl-business) 5%,transparent);\n --dtl-success-soft:color-mix(in srgb,var(--dtl-success) 5%,transparent);\n --dtl-warning-soft:color-mix(in srgb,var(--dtl-warning) 5%,transparent);\n --dtl-danger-soft:color-mix(in srgb,var(--dtl-danger) 5%,transparent);\n}\n`\n\n/**\n * 控件皮肤(L2):**一类控件唯一实现**,抄 dispatch 表 controls-css 的规矩——\n * 只消费 var(--dtl-*),一个颜色 / 尺寸 / 圆角 / 字号字面量都不许出现;\n * 高度只取三档,有边框的变体用 1px 真边框 + 内部 padding 补回 ⇒ 有边 / 无边同高。\n */\nexport const UI_CONTROLS_CSS = `\n/* ── 分段控件(滑动块)────────────────────────────────────────────\n 结构一套 + 两个受控轴:size(sm|md|lg) × variant(default|inset)。\n 变体只覆盖两个颜色变量(--seg-track / --seg-thumb),结构 / 尺寸 / 交互零重复。 */\n.dtl-seg{display:inline-flex;align-items:center;gap:2px;padding:2px;border-radius:var(--dtl-radius-md);background:var(--seg-track);}\n.dtl-seg--default{--seg-track:var(--dtl-surface-2);--seg-thumb:var(--dtl-surface-raised);border:1px solid var(--dtl-border);}\n.dtl-seg--inset{--seg-track:var(--dtl-hover);--seg-thumb:var(--dtl-surface-raised);border:0;padding:3px;}\n.dtl-seg__item{appearance:none;display:inline-flex;align-items:center;gap:4px;box-sizing:border-box;\n height:calc(var(--dtl-control-h-sm) - 6px);padding:0 12px;border:0;border-radius:var(--dtl-radius-sm);\n background:transparent;color:var(--dtl-fg-2);font-family:inherit;font-size:var(--dtl-font-sm);line-height:var(--dtl-line-sm);\n font-weight:400;white-space:nowrap;cursor:pointer;\n transition:color var(--dtl-dur-fast) var(--dtl-ease),background-color var(--dtl-dur-fast) var(--dtl-ease),box-shadow var(--dtl-dur-fast) var(--dtl-ease);}\n.dtl-seg--md .dtl-seg__item{height:calc(var(--dtl-control-h-md) - 6px);}\n.dtl-seg--lg .dtl-seg__item{height:calc(var(--dtl-control-h-lg) - 6px);}\n.dtl-seg__item:hover:not([aria-pressed='true']):not(:disabled){color:var(--dtl-fg);}\n.dtl-seg__item[aria-pressed='true']{background:var(--seg-thumb);box-shadow:var(--dtl-shadow-raised);color:var(--dtl-fg);font-weight:600;}\n.dtl-seg__item:focus-visible{outline:2px solid var(--dtl-business);outline-offset:-2px;}\n.dtl-seg__item:disabled{cursor:default;opacity:.4;}\n/* 段内角标(如「待认领 3」):语义色实面 + 反白字,0 不渲染;必须是正圆 */\n.dtl-seg__badge{display:inline-flex;align-items:center;justify-content:center;flex:none;\n width:16px;height:16px;padding:0;overflow:hidden;border-radius:50%;\n background:var(--dtl-danger);color:var(--dtl-on-signal);font-size:10px;line-height:16px;font-weight:500;font-variant-numeric:tabular-nums;}\n\n/* ── 按钮 / 图标钮 ───────────────────────────────────────────────\n 与 Segmented 同一条高度纪律;variant 只换颜色,结构 / 尺寸 / 交互只此一份。 */\n.dtl-btn{appearance:none;display:inline-flex;align-items:center;justify-content:center;gap:var(--dtl-space-1);\n box-sizing:border-box;border:1px solid transparent;border-radius:var(--dtl-radius-md);\n background:transparent;color:var(--dtl-fg);font-family:inherit;font-weight:500;white-space:nowrap;cursor:pointer;\n transition:background-color var(--dtl-dur-fast) var(--dtl-ease),border-color var(--dtl-dur-fast) var(--dtl-ease),color var(--dtl-dur-fast) var(--dtl-ease),opacity var(--dtl-dur-fast) var(--dtl-ease);}\n.dtl-btn--sm{height:var(--dtl-control-h-sm);padding:0 10px;font-size:var(--dtl-font-sm);line-height:var(--dtl-line-sm);}\n.dtl-btn--md{height:var(--dtl-control-h-md);padding:0 12px;font-size:var(--dtl-font-md);line-height:var(--dtl-line-md);}\n.dtl-btn--lg{height:var(--dtl-control-h-lg);padding:0 14px;font-size:var(--dtl-font-md);line-height:var(--dtl-line-md);}\n.dtl-btn--primary{background:var(--dtl-accent);color:var(--dtl-fg-inverse);}\n.dtl-btn--primary:hover:not(:disabled){opacity:.9;}\n.dtl-btn--outline{background:var(--dtl-surface-1);border-color:var(--dtl-border-strong);color:var(--dtl-fg);}\n.dtl-btn--outline:hover:not(:disabled){background:var(--dtl-hover);}\n.dtl-btn--ghost{color:var(--dtl-fg-2);}\n.dtl-btn--ghost:hover:not(:disabled){background:var(--dtl-hover);color:var(--dtl-fg);}\n.dtl-btn:focus-visible{outline:2px solid var(--dtl-business);outline-offset:2px;}\n.dtl-btn:disabled{cursor:default;opacity:.5;}\n.dtl-iconbtn{appearance:none;display:inline-flex;align-items:center;justify-content:center;box-sizing:border-box;\n border:1px solid transparent;border-radius:var(--dtl-radius-sm);background:transparent;color:var(--dtl-fg-2);\n font-family:inherit;cursor:pointer;flex:none;\n transition:background-color var(--dtl-dur-fast) var(--dtl-ease),border-color var(--dtl-dur-fast) var(--dtl-ease),color var(--dtl-dur-fast) var(--dtl-ease),opacity var(--dtl-dur-fast) var(--dtl-ease);}\n.dtl-iconbtn--sm{width:var(--dtl-control-h-sm);height:var(--dtl-control-h-sm);font-size:var(--dtl-font-sm);}\n.dtl-iconbtn--md{width:var(--dtl-control-h-md);height:var(--dtl-control-h-md);font-size:var(--dtl-font-md);}\n.dtl-iconbtn--lg{width:var(--dtl-control-h-lg);height:var(--dtl-control-h-lg);font-size:var(--dtl-font-md);}\n.dtl-iconbtn--outline{background:var(--dtl-surface-1);border-color:var(--dtl-border);color:var(--dtl-fg-2);}\n.dtl-iconbtn:hover:not(:disabled){background:var(--dtl-hover);color:var(--dtl-fg);}\n.dtl-iconbtn:focus-visible{outline:2px solid var(--dtl-business);outline-offset:1px;}\n.dtl-iconbtn:disabled{cursor:default;opacity:.4;}\n\n/* ── 输入 / 搜索框 / 下拉 ──────────────────────────────────────── */\n.dtl-input{box-sizing:border-box;border:1px solid var(--dtl-border);border-radius:var(--dtl-radius-md);\n background:var(--dtl-surface-1);color:var(--dtl-fg);font-family:inherit;outline:none;\n transition:border-color var(--dtl-dur-fast) var(--dtl-ease),box-shadow var(--dtl-dur-fast) var(--dtl-ease);}\n.dtl-input--sm{height:var(--dtl-control-h-sm);padding:0 8px;font-size:var(--dtl-font-sm);line-height:var(--dtl-line-sm);}\n.dtl-input--md{height:var(--dtl-control-h-md);padding:0 10px;font-size:var(--dtl-font-md);line-height:var(--dtl-line-md);}\n.dtl-input--lg{height:var(--dtl-control-h-lg);padding:0 12px;font-size:var(--dtl-font-md);line-height:var(--dtl-line-md);}\n.dtl-input::placeholder{color:var(--dtl-fg-dim);}\n.dtl-input:focus{border-color:var(--dtl-business);box-shadow:0 0 0 1px var(--dtl-business);}\n.dtl-input:disabled{cursor:default;opacity:.5;}\n/* 原生 select: appearance:none + 自绘箭头 ⇒ 与输入框同宗(宿主没有廉价下拉件) */\n.dtl-select{appearance:none;-webkit-appearance:none;box-sizing:border-box;\n border:1px solid var(--dtl-border);border-radius:var(--dtl-radius-md);background:var(--dtl-surface-1);color:var(--dtl-fg);\n font-family:inherit;outline:none;cursor:pointer;\n background-image:url(\"data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%23767d87' stroke-width='1.5'><path d='M4 6.5 8 10.5 12 6.5'/></svg>\");\n background-repeat:no-repeat;background-position:right 8px center;background-size:14px;\n transition:border-color var(--dtl-dur-fast) var(--dtl-ease),box-shadow var(--dtl-dur-fast) var(--dtl-ease);}\n.dtl-select--sm{height:var(--dtl-control-h-sm);padding:0 26px 0 8px;font-size:var(--dtl-font-sm);line-height:var(--dtl-line-sm);}\n.dtl-select--md{height:var(--dtl-control-h-md);padding:0 26px 0 10px;font-size:var(--dtl-font-md);line-height:var(--dtl-line-md);}\n.dtl-select--lg{height:var(--dtl-control-h-lg);padding:0 26px 0 12px;font-size:var(--dtl-font-md);line-height:var(--dtl-line-md);}\n.dtl-select:focus{border-color:var(--dtl-business);box-shadow:0 0 0 1px var(--dtl-business);}\n.dtl-select:disabled{cursor:default;opacity:.5;}\n\n/* ── 卡 / chip / 空态 / 列表行(业务壳,皮肤仍只消费 token)────── */\n.dtl-card{background:var(--dtl-surface-1);border:1px solid var(--dtl-border);border-radius:var(--dtl-radius-md);\n transition:background-color var(--dtl-dur-fast) var(--dtl-ease);}\n.dtl-card--clickable{cursor:pointer;text-align:left;font:inherit;color:inherit;width:100%;}\n.dtl-card--clickable:hover{background:var(--dtl-card-hover);}\n.dtl-card--clickable[data-active='true']{border-color:var(--dtl-business);}\n.dtl-card--clickable:focus-visible{outline:2px solid var(--dtl-business);outline-offset:1px;}\n.dtl-chip{display:inline-flex;align-items:center;gap:4px;height:20px;padding:0 8px;border-radius:var(--dtl-radius-sm);\n background:var(--dtl-chip-bg);color:var(--dtl-fg-2);font-size:var(--dtl-font-xs);line-height:var(--dtl-line-xs);white-space:nowrap;}\n.dtl-row{display:flex;align-items:center;gap:var(--dtl-space-2);width:100%;box-sizing:border-box;\n padding:var(--dtl-space-2) var(--dtl-space-3);border:0;border-bottom:1px solid var(--dtl-border-faint);\n background:transparent;font:inherit;color:inherit;text-align:left;cursor:pointer;\n transition:background-color var(--dtl-dur-fast) var(--dtl-ease);}\n.dtl-row:hover{background:var(--dtl-card-hover);}\n.dtl-row:focus-visible{outline:2px solid var(--dtl-business);outline-offset:-2px;}\n.dtl-row[data-open='true']{background:var(--dtl-open-bg);}\n.dtl-row-body{padding:var(--dtl-space-2) var(--dtl-space-3) var(--dtl-space-3);border-bottom:1px solid var(--dtl-border-faint);\n background:var(--dtl-open-bg-soft);font-size:var(--dtl-font-md);}\n.dtl-ellipsis{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}\n\n@media (prefers-reduced-motion: reduce){\n .dtl-seg__item,.dtl-btn,.dtl-iconbtn,.dtl-input,.dtl-select,.dtl-card,.dtl-row{transition:none;}\n}\n`\n","/**\n * UI 基础层:样式单源 + 共用元件(**一类控件只有一份实现**)。\n *\n * 纪律(用户 2026-10-17「样式与方法一律抽象,不要反复重写」;形态抄本机\n * `dsh-task-dispatch-table` 的 `ui/tokens.ts` + `ui/controls-css.ts` + `ui/*.tsx`):\n * 1. **样式**:颜色 / 圆角 / 字号 / 高度一律 `var(--dtl-*)`(定义只在 tokens.ts),\n * 业务文件不许再写第二份 `var(--dsw-*)`、不许自造 `--dtl-*`、不许自造高度档;\n * 2. **元件**:分段 / 按钮 / 图标钮 / 输入 / 下拉 / 卡 / chip / 空态 / 图表各一份,\n * 页面与浮标共用。官方 primitives 只留「官方做得更好」的(StateDot / MarkdownText / 图标)。\n */\nimport { createElement as h, type ChangeEvent, type CSSProperties, type ReactNode } from 'react'\nimport { UI_CONTROLS_CSS, UI_TOKENS_CSS } from './tokens'\n\n/** 基础层样式(tokens + 控件皮肤;幂等注入)。 */\nconst UI_BASE_CSS = `\n/* 统计卡:可点、hover 淡蓝、选中蓝描边 */\n.dtl-stat{display:flex;flex-direction:column;gap:var(--dtl-space-1);\n padding:var(--dtl-space-3) var(--dtl-space-4);text-align:left;}\n.dtl-stat__value{font-size:var(--dtl-font-xl);font-weight:600;line-height:var(--dtl-line-xl);font-variant-numeric:tabular-nums;}\n.dtl-stat__hint{font-size:var(--dtl-font-xs);line-height:var(--dtl-line-xs);color:var(--dtl-fg-3);}\n\n/* 列表行:头部可点,hover 淡蓝;展开行走 --dtl-open-bg */\n.dtl-row-head{display:flex;align-items:center;gap:var(--dtl-space-2);\n width:100%;padding:var(--dtl-space-2) var(--dtl-space-3);border:0;background:transparent;\n color:inherit;font:inherit;text-align:left;cursor:pointer;\n transition:background-color var(--dtl-dur-fast) var(--dtl-ease);}\n.dtl-row-head:hover{background:var(--dtl-card-hover);}\n.dtl-row-head:focus-visible{outline:2px solid var(--dtl-business);outline-offset:-2px;}\n.dtl-row[data-open=\"true\"]{background:var(--dtl-open-bg);}\n.dtl-row-body{padding:var(--dtl-space-2) var(--dtl-space-3) var(--dtl-space-3);\n background:var(--dtl-open-bg-soft);font-size:var(--dtl-font-md);}\n\n/* 空态:真空 = 虚线引导卡;筛选空 = 小字 */\n.dtl-empty-guide{padding:var(--dtl-space-4);border:1px dashed var(--dtl-border-strong);border-radius:var(--dtl-radius-md);background:transparent;}\n.dtl-empty-filtered{padding:var(--dtl-space-4) 0;color:var(--dtl-fg-3);font-size:var(--dtl-font-md)}\n\n/* 浮标角标:0→n 跳变时一次脉冲,不自动消失 */\n.dtl-badge{position:absolute;top:-2px;right:-2px;min-width:16px;height:16px;padding:0 4px;\n border-radius:8px;background:var(--dtl-danger);color:var(--dtl-on-signal);\n font-size:10px;line-height:16px;text-align:center;font-weight:600;}\n.dtl-badge[data-pulse=\"true\"]{animation:dtl-pulse 900ms var(--dtl-ease) 1}\n@keyframes dtl-pulse{0%{transform:scale(.6);opacity:.4}60%{transform:scale(1.15)}100%{transform:scale(1)}}\n\n/* 过程正文:宽度受限、长行折行 */\n.dtl-worklog{max-width:72ch;font-size:var(--dtl-font-md);line-height:1.6}\n\n/* 统计区整体:左 8 块(2 行 × 4 列)+ 右趋势图(参考图同款分栏)。\n ⚠️ 列数写死 4 → 2,绝不给 3:3 列会排出 3+1 的非对称(用户 2026-10-17)。 */\n.dtl-stats{display:grid;grid-template-columns:minmax(0,2fr) minmax(0,1fr);gap:var(--dtl-space-3);align-items:stretch;}\n.dtl-stats__cards{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));grid-template-rows:repeat(2,auto);\n gap:var(--dtl-space-3);align-content:start;}\n@media (max-width:960px){.dtl-stats{grid-template-columns:minmax(0,1fr);}}\n@media (max-width:640px){.dtl-stats__cards{grid-template-columns:repeat(2,minmax(0,1fr));}}\n\n/* 趋势分组柱状图:细柱 + y 轴刻度 + 网格线 + 柱顶数值 + 均值虚线(参考图信息密度) */\n.dtl-gchart{position:relative;height:104px;border-bottom:1px solid var(--dtl-border);}\n.dtl-gchart__grid{position:absolute;left:0;right:0;border-top:1px solid var(--dtl-border-faint);pointer-events:none;}\n.dtl-gchart__ytick{position:absolute;left:0;width:26px;text-align:right;font-size:10px;line-height:12px;\n color:var(--dtl-fg-3);transform:translateY(50%);font-variant-numeric:tabular-nums;}\n.dtl-gchart__avg{position:absolute;left:0;right:0;border-top:1px dashed var(--dtl-border-strong);pointer-events:none;}\n.dtl-gchart__avgtag{position:absolute;right:0;font-size:10px;line-height:12px;color:var(--dtl-fg-3);\n background:var(--dtl-surface-1);padding:0 2px;transform:translateY(-100%);}\n.dtl-gchart__col{position:absolute;top:0;bottom:0;min-width:0;}\n.dtl-gchart__pair{position:absolute;bottom:0;left:50%;transform:translateX(-50%);\n display:flex;align-items:flex-end;gap:2px;}\n.dtl-gchart__bar{width:8px;min-height:0;border-radius:var(--dtl-radius-xs) var(--dtl-radius-xs) 0 0;}\n.dtl-gchart__bar--done{background:var(--dtl-business);}\n.dtl-gchart__bar--created{background:var(--dtl-chip-bg);}\n.dtl-gchart__col:hover .dtl-gchart__bar--done{background:var(--dtl-accent);}\n.dtl-gchart__val{position:absolute;left:50%;transform:translateX(-50%);font-size:10px;line-height:12px;\n color:var(--dtl-fg-2);font-variant-numeric:tabular-nums;white-space:nowrap;}\n.dtl-gchart__label{position:absolute;left:0;right:0;bottom:-16px;text-align:center;\n font-size:10px;line-height:14px;color:var(--dtl-fg-3);white-space:nowrap;}\n.dtl-gchart-legend{display:flex;align-items:center;gap:var(--dtl-space-3);font-size:var(--dtl-font-xs);color:var(--dtl-fg-2);}\n.dtl-gchart-legend i{display:inline-block;width:8px;height:8px;border-radius:2px;margin-right:4px;}\n\n/* 过程正文:宽度受限、长行折行 */\n.dtl-worklog{max-width:72ch;font-size:var(--dtl-font-md);line-height:1.6}\n\n/* 趋势柱状图:div 柱 + token 色,无新依赖 */\n.dtl-chart{display:flex;align-items:flex-end;gap:var(--dtl-space-2);height:120px;}\n.dtl-chart__col{display:flex;flex-direction:column;justify-content:flex-end;align-items:center;gap:var(--dtl-space-1);flex:1 1 0;height:100%;min-width:0;}\n.dtl-chart__bar{width:100%;max-width:28px;min-height:2px;border-radius:var(--dtl-radius-xs) var(--dtl-radius-xs) 0 0;\n background:var(--dtl-business);transition:background-color var(--dtl-dur-fast) var(--dtl-ease);}\n.dtl-chart__col:hover .dtl-chart__bar{background:var(--dtl-accent);}\n.dtl-chart__val{font-size:var(--dtl-font-xs);line-height:var(--dtl-line-xs);color:var(--dtl-fg-2);font-variant-numeric:tabular-nums;}\n.dtl-chart__label{font-size:10px;line-height:var(--dtl-line-xs);color:var(--dtl-fg-3);white-space:nowrap;}\n`\n\nconst STYLE_ID = 'dtl-ui-base'\n\n/** 幂等注入 token 层 + 控件皮肤 + 基础层(面板与浮标谁先挂载都只注入一次)。 */\nexport function ensureUiBase(): void {\n if (typeof document === 'undefined') return\n if (document.getElementById(STYLE_ID) !== null) return\n const style = document.createElement('style')\n style.id = STYLE_ID\n style.textContent = `${UI_TOKENS_CSS}\\n${UI_CONTROLS_CSS}\\n${UI_BASE_CSS}`\n document.head.appendChild(style)\n}\n\n/* ── 分段控件 ─────────────────────────────────────────────────────── */\n\n/** 一段(一个可选值)。 */\nexport interface SegmentedItem<Value extends string = string> {\n readonly value: Value\n readonly label: string\n /** 段内角标数字;`undefined` 或 ≤0 不显示。 */\n readonly badge?: number\n}\n\n/** 分段控件(单选):同一份结构 + size 轴;外观差异只由 variant 的两个颜色变量表达。 */\nexport function Segmented<Value extends string>(props: {\n items: readonly SegmentedItem<Value>[]\n value: Value\n onChange: (next: Value) => void\n size?: 'sm' | 'md' | 'lg'\n variant?: 'default' | 'inset'\n label: string\n id?: string\n style?: CSSProperties\n}): ReactNode {\n ensureUiBase()\n const size = props.size ?? 'md'\n const variant = props.variant ?? 'default'\n return h('div', {\n role: 'group',\n 'aria-label': props.label,\n id: props.id,\n className: `dtl-seg dtl-seg--${variant} dtl-seg--${size}`,\n style: props.style,\n }, props.items.map(item => h('button', {\n key: item.value,\n type: 'button',\n className: 'dtl-seg__item',\n 'aria-pressed': item.value === props.value,\n onClick: () => { props.onChange(item.value) },\n }, item.label, item.badge !== undefined && item.badge > 0\n ? h('span', { className: 'dtl-seg__badge' }, String(item.badge))\n : null)))\n}\n\n/* ── 按钮 / 图标钮 ────────────────────────────────────────────────── */\n\nexport function Button(props: {\n children?: ReactNode\n onClick: () => void\n variant?: 'primary' | 'outline' | 'ghost'\n size?: 'sm' | 'md' | 'lg'\n icon?: ReactNode\n disabled?: boolean\n title?: string\n}): ReactNode {\n ensureUiBase()\n return h('button', {\n type: 'button',\n className: `dtl-btn dtl-btn--${props.variant ?? 'outline'} dtl-btn--${props.size ?? 'md'}`,\n disabled: props.disabled === true,\n title: props.title,\n onClick: props.onClick,\n }, props.icon, props.children)\n}\n\nexport function IconButton(props: {\n icon: ReactNode\n label: string\n onClick: () => void\n variant?: 'plain' | 'outline'\n size?: 'sm' | 'md' | 'lg'\n disabled?: boolean\n title?: string\n}): ReactNode {\n ensureUiBase()\n return h('button', {\n type: 'button',\n className: `dtl-iconbtn dtl-iconbtn--${props.variant ?? 'plain'} dtl-iconbtn--${props.size ?? 'md'}`,\n 'aria-label': props.label,\n disabled: props.disabled === true,\n title: props.title ?? props.label,\n onClick: props.onClick,\n }, props.icon)\n}\n\n/* ── 输入 / 下拉 / 搜索 ───────────────────────────────────────────── */\n\n/** 原生下拉(工作区 / 状态共用一份;宿主没有廉价下拉件 ⇒ 原生 + token 皮肤)。 */\nexport function Select<Value extends string>(props: {\n value: Value\n options: readonly { value: Value; label: string }[]\n onChange: (next: Value) => void\n ariaLabel: string\n /** 定宽(px);不给就按内容。 */\n width?: number\n size?: 'sm' | 'md' | 'lg'\n}): ReactNode {\n ensureUiBase()\n const size = props.size ?? 'md'\n return h('select', {\n className: `dtl-select dtl-select--${size}`,\n 'aria-label': props.ariaLabel,\n style: props.width === undefined ? undefined : { width: `${props.width}px` },\n value: props.value,\n onChange: (event: ChangeEvent<HTMLSelectElement>) => { props.onChange(event.target.value as Value) },\n }, props.options.map(option => h('option', { key: option.value, value: option.value }, option.label)))\n}\n\n/** 搜索框(关键字过滤;占满剩余宽度由调用方的 flex 决定)。 */\nexport function SearchInput(props: {\n value: string\n placeholder: string\n onChange: (next: string) => void\n ariaLabel: string\n size?: 'sm' | 'md' | 'lg'\n}): ReactNode {\n ensureUiBase()\n const size = props.size ?? 'md'\n return h('input', {\n className: `dtl-input dtl-input--${size}`,\n type: 'search',\n 'aria-label': props.ariaLabel,\n placeholder: props.placeholder,\n value: props.value,\n spellCheck: false,\n onChange: (event: ChangeEvent<HTMLInputElement>) => { props.onChange(event.target.value) },\n })\n}\n\n/* ── 卡 / chip / 空态 ─────────────────────────────────────────────── */\n\n/** 统计卡:主数字 + 灰色口径小字;点一下联动下方列表筛选。\n * `value` 为 0 时显示「—」(用户 2026-10-17 定稿:0 会被读成「没数据 / 出错了」);\n * `hint` 可以是富文本(如把「停滞」两字标红);\n * `interactive: false` = 纯展示卡(统计台:首页没有已结案可筛,不装可点)。 */\nexport function StatCard(props: {\n label: string\n value: string | number\n hint?: ReactNode\n active?: boolean\n interactive?: boolean\n title?: string\n onClick?: () => void\n}): ReactNode {\n ensureUiBase()\n const interactive = props.interactive !== false && props.onClick !== undefined\n const shown = props.value === 0 || props.value === '0' ? '—' : String(props.value)\n return h(interactive ? 'button' : 'div', {\n type: interactive ? 'button' : undefined,\n className: `dtl-card dtl-stat${interactive ? ' dtl-card--clickable' : ''}`,\n 'data-active': props.active === true,\n title: props.title,\n onClick: interactive ? props.onClick : undefined,\n },\n h('span', { style: { fontSize: 'var(--dtl-font-sm)', color: 'var(--dtl-fg-2)' } }, props.label),\n h('span', { className: 'dtl-stat__value' }, shown),\n props.hint === undefined || props.hint === null || props.hint === ''\n ? null\n : h('span', { className: 'dtl-stat__hint' }, props.hint),\n )\n}\n\n/** chip(分类 / 标签 / 工作区名)。 */\nexport function Chip(props: { text: string; color?: string }): ReactNode {\n ensureUiBase()\n return h('span', {\n className: 'dtl-chip',\n style: props.color === undefined ? undefined : { background: props.color, color: 'var(--dtl-on-signal)' },\n title: props.text,\n }, props.text)\n}\n\n/** 空态:`guide` = 真空引导卡(虚线),`filtered` = 筛选空小字。 */\nexport function EmptyState(props: { tone: 'guide' | 'filtered'; title: string; hint?: string }): ReactNode {\n ensureUiBase()\n if (props.tone === 'filtered') return h('div', { className: 'dtl-empty-filtered' }, props.title)\n return h('div', { className: 'dtl-empty-guide' },\n h('div', { style: { fontSize: 'var(--dtl-font-lg)', fontWeight: 600, marginBottom: 'var(--dtl-space-1)' } }, props.title),\n props.hint === undefined ? null : h('div', { style: { fontSize: 'var(--dtl-font-md)', color: 'var(--dtl-fg-2)' } }, props.hint),\n )\n}\n\n/* ── 趋势分组柱状图 ───────────────────────────────────────────────────── */\n\n/** 一周两根柱(完成 / 新增)。 */\nexport interface GroupedBarWeek {\n /** 轴标签(MM-DD)。 */\n readonly label: string\n readonly done: number\n readonly created: number\n /** 悬停文案。 */\n readonly title: string\n}\n\n/** 千位压缩(1.2k / 12k):y 轴刻度与柱顶数值用,避免 4 位数把刻度挤宽。 */\nfunction compactCount(value: number): string {\n if (value >= 10_000) return `${Math.round(value / 1000)}k`\n if (value >= 1000) return `${(value / 1000).toFixed(1)}k`\n return String(value)\n}\n\n/** 分组柱状图(div 柱,无新依赖):完成 / 新增各一细柱,y 轴三档刻度 + 网格线 + 柱顶数值 + 均值虚线。\n * 列 / 柱 / 标签全部相对 104px 绘图区绝对定位 ⇒ 与刻度同一基准,不会各算各的高度。 */\nexport function GroupedBarChart(props: {\n weeks: readonly GroupedBarWeek[]\n ariaLabel: string\n avgLabel: string\n}): ReactNode {\n ensureUiBase()\n const plotHeight = 104\n const top = Math.max(1, props.weeks.reduce((peak, week) => Math.max(peak, week.done, week.created), 0))\n const doneTotal = props.weeks.reduce((sum, week) => sum + week.done, 0)\n const avg = props.weeks.length === 0 ? 0 : doneTotal / props.weeks.length\n const ticks = [0, top / 2, top]\n const heightOf = (value: number): number => Math.round((value / top) * plotHeight)\n const columnStyle = (index: number): Record<string, string | number> => ({\n left: `${(index / props.weeks.length) * 100}%`,\n right: `${((props.weeks.length - index - 1) / props.weeks.length) * 100}%`,\n })\n return h('div', { style: { position: 'relative', paddingLeft: '30px', paddingBottom: '18px' } },\n h('div', { className: 'dtl-gchart', role: 'img', 'aria-label': props.ariaLabel },\n ticks.map((tick) => h('div', { key: `grid-${String(tick)}`, className: 'dtl-gchart__grid', style: { bottom: `${heightOf(tick)}px` } })),\n ticks.map((tick) => h('span', { key: `tick-${String(tick)}`, className: 'dtl-gchart__ytick', style: { bottom: `${heightOf(tick)}px` } }, compactCount(Math.round(tick)))),\n avg > 0\n ? h('div', { className: 'dtl-gchart__avg', style: { bottom: `${heightOf(avg)}px` } },\n h('span', { className: 'dtl-gchart__avgtag' }, props.avgLabel))\n : null,\n props.weeks.map((week, index) => h('div', { key: week.label, className: 'dtl-gchart__col', style: columnStyle(index), title: week.title },\n h('div', { className: 'dtl-gchart__pair' },\n h('span', { className: 'dtl-gchart__bar dtl-gchart__bar--done', style: { height: `${heightOf(week.done)}px` } }),\n h('span', { className: 'dtl-gchart__bar dtl-gchart__bar--created', style: { height: `${heightOf(week.created)}px` } }),\n ),\n // 柱顶数值只标「完成」(核心指标);新增的数走 hover(16 个数值标签会糊成一片)\n week.done === 0\n ? null\n : h('span', { className: 'dtl-gchart__val', style: { bottom: `${heightOf(week.done) + 3}px` } }, String(week.done)),\n h('span', { className: 'dtl-gchart__label' }, week.label),\n )),\n ),\n )\n}\n\n/** 图例(完成 / 新增 两个色块)。 */\nexport function ChartLegend(props: { doneLabel: string; createdLabel: string }): ReactNode {\n ensureUiBase()\n return h('div', { className: 'dtl-gchart-legend' },\n h('span', null, h('i', { style: { background: 'var(--dtl-business)' } }), props.doneLabel),\n h('span', null, h('i', { style: { background: 'var(--dtl-chip-bg)' } }), props.createdLabel),\n )\n}\n","/**\n * 任务列表行(宿主**无表格件** ⇒ 自绘,几何参照官方 `DisclosureRow`,状态走官方 `StateDot`)。\n * 定稿见 docs/design/features/board-home.md §二 与 docs/design/ui-style.md:\n * 状态语义 open=idle / doing=ongoing / blocked=warning / 已结案=done。\n *\n * 行展开 = 过程文档路径 + 过程正文(在办走 `GET /task` 取正文;已结案只有路径——\n * host 没有按历史行取正文的路由,不编造内容)。\n *\n * `showWorkspace`:第一区「我的待办」是跨工作区平铺 ⇒ 工作区升为一列(定宽,不再是\n * 和标签混在一起的小 chip);第二区分组行由分组标题说明归属,行内不再重复。\n */\nimport { createElement as h, useEffect, useMemo, useState, type ReactNode } from 'react'\nimport {\n IconChevronDownOutlineRegular, IconChevronRightOutlineRegular,\n MarkdownText, StateDot,\n} from '@deepseek-ai/dsh-client-ui-primitives'\nimport { fetchTaskDetail } from './api'\nimport { formatDateTime } from './format'\nimport type { Translate } from './locales'\nimport type { Row } from './rows'\nimport { Chip } from './ui'\nimport type { TaskDetail, Workspace } from './types'\n\n/** 行的状态点语义(在办三态 + 已结案)。 */\nfunction dotOf(row: Row): 'idle' | 'ongoing' | 'warning' | 'done' {\n if (row.kind === 'closed') return 'done'\n if (row.task.status === 'blocked') return 'warning'\n if (row.task.status === 'doing') return 'ongoing'\n return 'idle'\n}\n\n/** 行标题:在办 = 任务名(缺则任务号);已结案 = 任务包名(缺则「完成了什么」首句)。 */\nfunction titleOf(row: Row): string {\n if (row.kind === 'task') return row.task.title ?? row.task.id\n const title = row.record.workpackage ?? row.record.summary\n return title !== undefined && title !== '' ? title : row.record.src ?? '—'\n}\n\n/** 行的「谁」:负责人优先,没有就创建人,都没有 = 未认领。 */\nfunction whoOf(row: Row): string | null {\n if (row.kind === 'task') return row.task.owner ?? row.task.creator ?? null\n return row.record.owner ?? null\n}\n\n/** 工作区徽标(第一区专用列):色块首字母 + 标题,定宽、超出省略(不猜官方图标名)。 */\nfunction WorkspaceTag(props: { workspace: Workspace | undefined }): ReactNode {\n if (props.workspace === undefined) return null\n const initial = props.workspace.title.trim().slice(0, 1)\n return h('span', {\n className: 'dtl-ellipsis',\n style: { display: 'inline-flex', alignItems: 'center', gap: 'var(--dtl-space-1)', flex: '0 0 140px', minWidth: 0, color: 'var(--dtl-fg-2)', fontSize: 'var(--dtl-font-xs)' },\n title: props.workspace.title,\n },\n h('span', {\n style: {\n display: 'inline-flex', alignItems: 'center', justifyContent: 'center', flex: 'none',\n width: '16px', height: '16px', borderRadius: 'var(--dtl-radius-xs)',\n background: 'var(--dtl-business-soft)', color: 'var(--dtl-business)', fontSize: '10px', fontWeight: 600,\n },\n }, initial === '' ? '·' : initial),\n props.workspace.title,\n )\n}\n\nexport function TaskRowView(props: { t: Translate; row: Row; showWorkspace?: boolean }): ReactNode {\n const { t, row } = props\n const [open, setOpen] = useState(false)\n\n // 官方 MarkdownText 要求 labels 引用稳定(新身份会丢弃渲染缓存)⇒ 按 t 记忆一次。\n const mdLabels = useMemo(() => ({ code: { copyLabel: t('mdCopy'), copiedLabel: t('mdCopied') }, footnotes: t('mdFootnotes') }), [t])\n\n /** 行的时间:在办 doing/blocked 看开始、其余看创建;已结案看结案。 */\n const stamp = row.kind === 'closed'\n ? { value: row.record.time ?? row.record.created ?? null, label: t('fieldFinished') }\n : row.task.status === 'open'\n ? { value: row.task.created ?? null, label: t('fieldCreated') }\n : { value: row.task.started ?? row.task.created ?? null, label: t('fieldStarted') }\n const when = stamp.value === null || stamp.value === undefined || stamp.value === ''\n ? t('timeUnknown')\n : formatDateTime(stamp.value, { fallback: t('timeUnknown') })\n const who = whoOf(row)\n const workspace = row.kind === 'task' ? row.task.workspace : row.record.workspace\n const category = row.kind === 'task' ? row.task.category : row.record.category\n const tags = (row.kind === 'task' ? row.task.tags : row.record.tags) ?? []\n const logPath = row.kind === 'task' ? row.task.worklog : row.record.log\n\n return h('div', { className: 'dtl-row', 'data-open': open },\n h('button', {\n type: 'button',\n className: 'dtl-row-head',\n 'aria-expanded': open,\n title: open ? t('collapseAria') : t('expandAria'),\n onClick: () => { setOpen(!open) },\n },\n h(StateDot, { state: dotOf(row) }),\n props.showWorkspace === true ? h(WorkspaceTag, { workspace }) : null,\n h('span', {\n className: 'dtl-ellipsis',\n style: { flex: '1 1 auto', minWidth: 0, fontSize: 'var(--dtl-font-md)' },\n title: titleOf(row),\n }, titleOf(row)),\n category === null || category === undefined || category === ''\n ? null\n : h(Chip, { text: category }),\n tags.slice(0, 2).map(tag => h(Chip, { key: tag, text: `#${tag}` })),\n h('span', {\n style: {\n display: 'flex', alignItems: 'center', gap: 'var(--dtl-space-2)', flex: 'none',\n color: 'var(--dtl-fg-3)', fontSize: 'var(--dtl-font-xs)', whiteSpace: 'nowrap',\n },\n },\n h('span', { title: stamp.value ?? undefined }, `${stamp.label} ${when}`),\n h('span', null, who === null || who === '' ? t('ownerUnclaimed') : `@${who}`),\n ),\n h(open ? IconChevronDownOutlineRegular : IconChevronRightOutlineRegular, { size: 16 }),\n ),\n open\n ? h('div', { className: 'dtl-row-body' },\n // 过程文档路径(真实值;没有工作区文件服务可点开 ⇒ 只展示路径,不假装能跳转)\n h('div', { style: { margin: 'var(--dtl-space-2) 0', fontSize: 'var(--dtl-font-sm)', color: 'var(--dtl-fg-2)' } },\n `${t('worklogTitle')}·${t('worklogPath')}:${\n logPath === null || logPath === undefined || logPath === '' ? t('worklogMissing') : logPath\n }`,\n ),\n row.kind === 'closed'\n ? h('div', { style: { fontSize: 'var(--dtl-font-md)', color: 'var(--dtl-fg-2)' } }, row.record.summary ?? '')\n : h(WorklogBody, { t, workspaceId: row.task.workspace.id, taskId: row.task.id, labels: mdLabels }),\n )\n : null,\n )\n}\n\n/** 在办行的过程正文:**展开才取**(首屏不为正文付代价),且只在挂载时取一次(不随重渲染重取)。 */\nfunction WorklogBody(props: {\n t: Translate\n workspaceId: string\n taskId: string\n labels: { code: { copyLabel: string; copiedLabel: string }; footnotes: string }\n}): ReactNode {\n const { t, workspaceId, taskId, labels } = props\n const [detail, setDetail] = useState<TaskDetail | null>(null)\n const [failed, setFailed] = useState<string | null>(null)\n useEffect(() => {\n let alive = true\n fetchTaskDetail(workspaceId, taskId)\n .then((body) => { if (alive) setDetail(body) })\n .catch((error: unknown) => {\n if (alive) setFailed(error instanceof Error ? error.message : String(error))\n })\n return () => { alive = false }\n }, [workspaceId, taskId])\n\n if (failed !== null) {\n return h('div', { style: { color: 'var(--dtl-danger)', fontSize: 'var(--dtl-font-sm)' } }, `${t('loadFailed')}:${failed}`)\n }\n if (detail === null) return h('div', { style: { color: 'var(--dtl-fg-3)', fontSize: 'var(--dtl-font-sm)' } }, t('loading'))\n if (detail.worklog === null) {\n return h('div', { style: { color: 'var(--dtl-fg-3)', fontSize: 'var(--dtl-font-sm)' } }, t('worklogMissing'))\n }\n return h('div', { className: 'dtl-worklog' }, h(MarkdownText, { text: detail.worklog, labels, variant: 'compact' }))\n}\n","/**\n * 看板首页(`main` 槽整页内容):6 统计卡 + 近 8 周趋势图 + 两区任务列表。\n * 定稿见 docs/design/features/board-home.md §二(2026-10-17 专家组改版);\n * 按钮矩阵归「任务按钮与会话交接」任务(写入路径 A/B 未拍板),本页**不画**任务动作按钮。\n *\n * 刷新纪律(2026-10-17 拍板):挂载首读 + 手动刷新按钮,**不轮询**;空态三分\n * (未就绪不渲染 / 真空引导卡 / 筛选空小字)。\n */\nimport { createElement as h, useEffect, useMemo, useState, useSyncExternalStore, type ReactNode } from 'react'\nimport { IconRefreshOutlineRegular } from '@deepseek-ai/dsh-client-ui-primitives'\nimport type { LocaleKey, Translate } from './locales'\nimport { boardSnapshot, boardSubscribe, refresh, setQuery, type Query, type Scope, type StatusFilter } from './store'\nimport { selectView, type WorkspaceGroup } from './rows'\nimport type { PeriodCounts } from './types'\nimport { TaskRowView } from './task-row'\nimport {\n Button, ChartLegend, EmptyState, ensureUiBase, GroupedBarChart, SearchInput, Segmented, Select, StatCard,\n} from './ui'\n\ntype Tab = 'tasks' | 'stats'\n\n/** 整页容器:占满中栏、自己滚动。 */\nconst pageStyle: Record<string, string | number> = {\n height: '100%', width: '100%', boxSizing: 'border-box', overflow: 'auto',\n padding: '24px 22px', color: 'var(--dtl-fg)',\n}\nconst headerStyle: Record<string, string | number> = {\n display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 'var(--dtl-space-3)', flexWrap: 'wrap',\n marginBottom: 'var(--dtl-space-4)',\n}\nconst titleStyle: Record<string, string | number> = { fontSize: 'var(--dtl-font-xl)', fontWeight: 600 }\n/** 统计区:左 8 块(2 行 × 4 列)+ 右趋势图,参考图同款分栏。\n * 列数写死 4 → 2(媒体查询在 ui.ts 的 .dtl-stats / .dtl-stats__cards 里),\n * 绝不给 3 列——3 列会排出 3+1 的非对称(用户 2026-10-17:必须上下对称)。 */\nconst statsRowStyle: Record<string, string | number> = { marginBottom: 'var(--dtl-space-4)' }\nconst chartCardStyle: Record<string, string | number> = {\n display: 'flex', flexDirection: 'column', gap: 'var(--dtl-space-2)', padding: 'var(--dtl-space-3) var(--dtl-space-4)',\n}\nconst filterRowStyle: Record<string, string | number> = {\n display: 'flex', alignItems: 'center', gap: 'var(--dtl-space-2)', flexWrap: 'wrap',\n marginBottom: 'var(--dtl-space-3)',\n}\nconst searchWrapStyle: Record<string, string | number> = { flex: '1 1 220px', minWidth: 0 }\nconst hintStyle: Record<string, string | number> = { fontSize: 'var(--dtl-font-sm)', color: 'var(--dtl-fg-2)' }\nconst regionHeadStyle: Record<string, string | number> = {\n display: 'flex', alignItems: 'baseline', gap: 'var(--dtl-space-2)',\n margin: 'var(--dtl-space-4) 0 var(--dtl-space-2)', fontSize: 'var(--dtl-font-lg)', fontWeight: 600,\n}\nconst groupHeadStyle: Record<string, string | number> = {\n display: 'flex', alignItems: 'center', gap: 'var(--dtl-space-2)', width: '100%',\n padding: 'var(--dtl-space-2) var(--dtl-space-3)', border: 0, borderRadius: 'var(--dtl-radius-md)',\n background: 'var(--dtl-surface-2)', color: 'inherit', font: 'inherit', textAlign: 'left', cursor: 'pointer',\n}\nconst groupCountStyle: Record<string, string | number> = { fontSize: 'var(--dtl-font-xs)', color: 'var(--dtl-fg-3)', fontWeight: 400 }\n\n/** 分组默认折叠阈值(组内太多条时先收起,用户点开)。 */\nconst GROUP_COLLAPSE_OVER = 30\n\nexport function BoardHome(props: { t: Translate; onBack: () => void }): ReactNode {\n const { t, onBack } = props\n ensureUiBase()\n const state = useSyncExternalStore(boardSubscribe, boardSnapshot)\n const [tab, setTab] = useState<Tab>('tasks')\n\n // 打开面板首读(后端纯按需,无推送无轮询)。\n useEffect(() => { void refresh() }, [])\n\n const board = state.board\n const view = useMemo(() => selectView(state), [state])\n const stats = board?.stats\n const days = (value: number | null | undefined): string =>\n value === null || value === undefined ? t('daysNone') : t('daysValue', { n: value })\n\n /** 统计卡 → 筛选联动(卡即入口:点一下跳到对应视角)。 */\n const card = (label: LocaleKey, value: number, hint: string, patch: Partial<Query>): ReactNode =>\n h(StatCard, {\n label: t(label),\n value,\n hint,\n active: isActive(state.query, patch),\n onClick: () => { setQuery(patch) },\n })\n\n /** 统计台卡:不可点(首页没有已结案可筛),hover 给具体日期区间。 */\n const periodCard = (label: LocaleKey, period: PeriodCounts | undefined): ReactNode =>\n h(StatCard, {\n label: t(label),\n value: period?.done ?? 0,\n hint: t('statCreatedHint', { n: period?.created ?? 0 }),\n interactive: false,\n title: period === undefined ? undefined : t('periodRange', { from: period.from, to: period.to }),\n })\n\n const [chartScope, setChartScope] = useState<'all' | 'mine'>('all')\n const chartWeeks = useMemo(() => (board === null ? [] : board.closed.weekly[chartScope]).map((bucket) => ({\n label: bucket.week.slice(5),\n done: bucket.done,\n created: bucket.created,\n title: t('chartBucket', { week: bucket.week, done: bucket.done, created: bucket.created }),\n })), [board, chartScope, t])\n const chartDoneTotal = chartWeeks.reduce((sum, week) => sum + week.done, 0)\n const chartEmpty = chartWeeks.every((week) => week.done === 0 && week.created === 0)\n\n return h('div', { style: pageStyle },\n h('div', { style: { display: 'flex', justifyContent: 'center' } },\n h('div', { style: { width: '100%', maxWidth: '1120px', minWidth: 0, boxSizing: 'border-box' } },\n h('div', { style: headerStyle },\n h('div', { style: { display: 'flex', alignItems: 'center', gap: 'var(--dtl-space-3)', minWidth: 0 } },\n h(Button, { variant: 'outline', size: 'md', onClick: onBack }, `← ${t('backToConversation')}`),\n h('div', { style: titleStyle }, t('panelTitle')),\n ),\n h('div', { style: { display: 'flex', alignItems: 'center', gap: 'var(--dtl-space-2)' } },\n h(Segmented<Tab>, {\n id: 'dtl-tab',\n value: tab,\n label: t('panelTitle'),\n items: [\n { value: 'tasks', label: t('tabTasks') },\n { value: 'stats', label: t('tabStats') },\n ],\n onChange: setTab,\n }),\n h(Button, {\n variant: 'outline',\n size: 'md',\n icon: h(IconRefreshOutlineRegular, { size: 16 }),\n disabled: state.refreshing,\n title: t('refresh'),\n onClick: () => { void refresh() },\n }, state.refreshing ? t('refreshing') : t('refresh')),\n ),\n ),\n\n tab === 'stats'\n // 统计报表尚未开建(另立任务):占位说明,不画假图表。\n ? h('div', { style: hintStyle }, t('statsPlaceholder'))\n : h('div', null,\n // ── 统计区:左 8 块(操作台 4 + 统计台 4)+ 右趋势图 ──\n stats === undefined\n ? null\n : h('div', { className: 'dtl-stats', style: statsRowStyle },\n h('div', { className: 'dtl-stats__cards' },\n // 第一行 · 操作台(可点联动筛选)\n card('statMyActive', stats.myActive, t('statMyActiveHint', { n: stats.myBlocked }), { scope: 'mine', status: 'all', workspace: '' }),\n card('statOpen', stats.open, t('statOpenHint', { n: days(stats.oldestOpenDays) }), { scope: 'all', status: 'open', workspace: '' }),\n card('statDoing', stats.doing, t('statDoingHint', { n: stats.doing - stats.myActive + stats.myBlocked }), { scope: 'all', status: 'active', workspace: '' }),\n // 阻塞 · 停滞合并卡:主数字 = 阻塞数;有停滞时小字把「停滞」标红\n h(StatCard, {\n label: t('statBlockedStale'),\n value: stats.blocked,\n hint: stats.stale === 0\n ? undefined\n : h('span', null,\n h('span', { style: { color: 'var(--dtl-danger)', fontWeight: 600 } }, t('statStaleWord')),\n ` ${t('statStaleHint', { n: stats.stale })}`,\n ),\n active: isActive(state.query, { scope: 'all', status: 'blocked', workspace: '' }),\n title: t('statStaleHint', { n: stats.staleDays }),\n onClick: () => { setQuery({ scope: 'all', status: 'blocked', workspace: '' }) },\n }),\n // 第二行 · 统计台(自然周期,不可点)\n periodCard('statWeekDone', board?.trend.thisWeek),\n periodCard('statLastWeekDone', board?.trend.lastWeek),\n periodCard('statMonthDone', board?.trend.thisMonth),\n periodCard('statLastMonthDone', board?.trend.lastMonth),\n ),\n h('div', { className: 'dtl-card', style: chartCardStyle },\n h('div', { style: { display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 'var(--dtl-space-2)' } },\n h('span', { style: { fontSize: 'var(--dtl-font-md)', fontWeight: 600 } }, t('chartTitle')),\n h(Segmented<'all' | 'mine'>, {\n id: 'dtl-chart-scope',\n value: chartScope,\n label: t('scopeLabel'),\n size: 'sm',\n variant: 'inset',\n items: [\n { value: 'all', label: t('chartScopeAll') },\n { value: 'mine', label: t('chartScopeMine') },\n ],\n onChange: setChartScope,\n }),\n ),\n chartEmpty\n ? h('div', { style: { ...hintStyle, padding: 'var(--dtl-space-4) 0' } }, t('chartEmpty'))\n : h(GroupedBarChart, {\n weeks: chartWeeks,\n ariaLabel: t('chartTitle'),\n avgLabel: t('chartAvg', { n: Math.round((chartDoneTotal / Math.max(1, chartWeeks.length)) * 10) / 10 }),\n }),\n h(ChartLegend, { doneLabel: t('chartLegendDone'), createdLabel: t('chartLegendCreated') }),\n h('div', { style: { fontSize: 'var(--dtl-font-xs)', color: 'var(--dtl-fg-3)' } }, t('chartCaption')),\n ),\n ),\n\n // ── 筛选行(控件全同高 28px)──\n h('div', { style: filterRowStyle },\n h(Segmented<Scope>, {\n id: 'dtl-scope',\n value: state.query.scope,\n label: t('scopeLabel'),\n items: [\n { value: 'mine', label: t('scopeMine') },\n { value: 'all', label: t('scopeAll') },\n ],\n onChange: (next) => { setQuery({ scope: next }) },\n }),\n h(Segmented<StatusFilter>, {\n id: 'dtl-status',\n value: state.query.status,\n label: t('statusLabel'),\n items: [\n { value: 'all', label: t('statusAll') },\n { value: 'active', label: t('statusActive') },\n { value: 'open', label: t('statusOpen') },\n { value: 'blocked', label: t('statusBlocked') },\n { value: 'stale', label: t('statusStale') },\n ],\n onChange: (next) => { setQuery({ status: next }) },\n }),\n h(Select<string>, {\n value: state.query.workspace,\n ariaLabel: t('workspaceLabel'),\n width: 180,\n options: [\n { value: '', label: t('workspaceAll') },\n ...(board?.workspaces ?? []).map((workspace) => ({ value: workspace.id, label: workspace.title })),\n ],\n onChange: (next) => { setQuery({ workspace: next }) },\n }),\n h('div', { style: searchWrapStyle },\n h(SearchInput, {\n value: state.query.keyword,\n placeholder: t('searchPlaceholder'),\n ariaLabel: t('searchPlaceholder'),\n onChange: (next) => { setQuery({ keyword: next }) },\n }),\n ),\n ),\n\n // ── 列表(两区;空态三分)──\n state.status === 'loading' && board === null\n ? null // 未就绪:不渲染\n : state.status === 'error' && board === null\n ? h('div', { style: { display: 'flex', alignItems: 'center', gap: 'var(--dtl-space-2)' } },\n h('span', { style: { color: 'var(--dtl-danger)', fontSize: 'var(--dtl-font-md)' } }, `${t('loadFailed')}:${state.error ?? ''}`),\n h(Button, { variant: 'outline', size: 'sm', onClick: () => { void refresh() } }, t('retry')),\n )\n : board !== null && board.workspaces.length === 0\n ? h(EmptyState, { tone: 'guide', title: t('emptyNoWorkspace'), hint: t('emptyNoWorkspaceHint') })\n : view.total === 0\n ? h(EmptyState, {\n tone: isPristine(state.query) ? 'guide' : 'filtered',\n title: isPristine(state.query) ? t('emptyNoTask') : t('emptyFiltered'),\n hint: isPristine(state.query) ? t('emptyNoTaskHint') : undefined,\n })\n : h('div', null,\n // 第一区:我的待办(跨工作区平铺,工作区为一列)\n view.mine.length === 0\n ? null\n : h('div', null,\n h('div', { style: regionHeadStyle },\n h('span', null, `${t('regionMine')} ${view.mine.length}`),\n h('span', { style: { fontSize: 'var(--dtl-font-xs)', color: 'var(--dtl-fg-3)', fontWeight: 400 } }, t('regionMineHint')),\n ),\n view.mine.map(row => h(TaskRowView, { key: row.key, t, row, showWorkspace: true })),\n ),\n // 第二区:按工作区分组(组可折叠)\n view.groups.length === 0\n ? null\n : h('div', null,\n h('div', { style: regionHeadStyle },\n h('span', null, t('regionWorkspaces')),\n h('span', { style: { fontSize: 'var(--dtl-font-xs)', color: 'var(--dtl-fg-3)', fontWeight: 400 } }, t('showingCount', { n: view.total - view.mine.length })),\n ),\n view.groups.map(group => h(WorkspaceGroupView, { key: group.workspaceId, t, group })),\n ),\n ),\n ),\n ),\n ),\n )\n}\n\n/** 一个工作区分组:标题行(可点折叠)+ 行列表。 */\nfunction WorkspaceGroupView(props: { t: Translate; group: WorkspaceGroup }): ReactNode {\n const { t, group } = props\n const [open, setOpen] = useState(group.rows.length <= GROUP_COLLAPSE_OVER)\n return h('div', { style: { marginBottom: 'var(--dtl-space-3)' } },\n h('button', {\n type: 'button',\n className: 'dtl-row-head',\n style: groupHeadStyle,\n 'aria-expanded': open,\n title: open ? t('collapseAria') : t('expandAria'),\n onClick: () => { setOpen(!open) },\n },\n h('span', { className: 'dtl-ellipsis', style: { flex: '1 1 auto', fontSize: 'var(--dtl-font-md)', fontWeight: 600 } }, group.title),\n h('span', { style: groupCountStyle }, t('groupHint', { doing: group.doingCount, open: group.openCount })),\n ),\n open\n ? h('div', { style: { marginTop: 'var(--dtl-space-2)' } },\n group.rows.map(row => h(TaskRowView, { key: row.key, t, row })),\n )\n : null,\n )\n}\n\n/** 卡片高亮:当前 query 是否正好落在该卡对应的视角上。 */\nfunction isActive(query: Query, patch: Partial<Query>): boolean {\n return Object.entries(patch).every(([key, value]) => query[key as keyof Query] === value)\n}\n\n/** 是否「什么都没筛」:真空才给引导卡,一有筛选就只能是「筛选空」。 */\nfunction isPristine(query: Query): boolean {\n return query.scope === 'all' && query.status === 'all' && query.workspace === '' && query.keyword.trim() === ''\n}\n","/**\n * 文案唯一真源:命名空间 `dsh-task-ledger`,zh / en 两档。\n *\n * 规矩(docs/design/ui-style.md §五):\n * - 两档**键必须完全对齐**(冒烟有断言),漏一种语言界面就露键名;\n * - 界面上不许出现未登记的硬编码中文;\n * - 占位符一律 `{name}`,由 `interpolateTranslate` 自己替换(不依赖宿主是否支持 params);\n * - 语言切换**跟随宿主**:插件不自备语言开关(注册字典 + 注册项声明 `locale:` ⇒ 宿主渲染器合成 `t` 席位)。\n */\n\n/** 文案键全集(增删键必须同时改 zh / en)。 */\nexport type LocaleKey =\n | 'backToConversation' | 'panelTitle' | 'tabTasks' | 'tabStats'\n | 'refresh' | 'refreshing' | 'loading' | 'retry' | 'loadFailed'\n | 'statsPlaceholder'\n // 统计卡\n | 'statMyActive' | 'statMyActiveHint' | 'statOpen' | 'statOpenHint'\n | 'statDoing' | 'statDoingHint' | 'statBlockedStale' | 'statStaleWord' | 'statStaleHint'\n | 'statWeekDone' | 'statLastWeekDone' | 'statMonthDone' | 'statLastMonthDone' | 'statCreatedHint'\n | 'daysNone' | 'daysValue'\n // 趋势图\n | 'chartTitle' | 'chartCaption' | 'chartBucket' | 'chartEmpty'\n | 'chartScopeAll' | 'chartScopeMine' | 'chartLegendDone' | 'chartLegendCreated' | 'chartAvg'\n | 'periodRange'\n // 筛选行\n | 'scopeLabel' | 'scopeMine' | 'scopeAll'\n | 'statusLabel' | 'statusAll' | 'statusActive' | 'statusOpen' | 'statusBlocked' | 'statusStale'\n | 'workspaceLabel' | 'workspaceAll'\n | 'searchPlaceholder'\n // 列表分区\n | 'regionMine' | 'regionMineHint' | 'regionWorkspaces' | 'groupHint' | 'showingCount'\n | 'expandAria' | 'collapseAria'\n | 'ownerUnclaimed' | 'fieldCreated' | 'fieldStarted' | 'fieldFinished' | 'timeUnknown'\n | 'worklogTitle' | 'worklogPath' | 'worklogMissing'\n | 'mdCopy' | 'mdCopied' | 'mdFootnotes'\n // 空态\n | 'emptyNoWorkspace' | 'emptyNoWorkspaceHint' | 'emptyNoTask' | 'emptyNoTaskHint' | 'emptyFiltered'\n // 浮标入口\n | 'badgeTitle' | 'openBoard'\n\n/** 中文(默认档)。 */\nexport const zh: Record<LocaleKey, string> = {\n backToConversation: '返回会话',\n panelTitle: '任务台账',\n tabTasks: '任务',\n tabStats: '统计',\n refresh: '刷新',\n refreshing: '刷新中',\n loading: '读取中…',\n retry: '重试',\n loadFailed: '读取失败',\n statsPlaceholder: '统计报表尚未开建(另立任务):这里以后放分类分布、按人完成数、时间线。',\n\n statMyActive: '我的待办',\n statMyActiveHint: '其中阻塞 {n}',\n statOpen: '待认领',\n statOpenHint: '最老一条躺了 {n}',\n statDoing: '进行中',\n statDoingHint: '别人在做 {n}',\n statBlockedStale: '阻塞 · 停滞',\n statStaleWord: '停滞',\n statStaleHint: '{n} 条',\n statWeekDone: '本周完成',\n statLastWeekDone: '上周完成',\n statMonthDone: '本月完成',\n statLastMonthDone: '上月完成',\n statCreatedHint: '新增 {n} 条',\n daysNone: '—',\n daysValue: '{n} 天',\n\n chartTitle: '近 8 周',\n chartCaption: '完成(按结案时间)/ 新增(按创建时间),周一起桶;我的 = owner 是我',\n chartBucket: '{week} 那一周:完成 {done} 条,新增 {created} 条',\n chartEmpty: '近 8 周没有记录',\n chartScopeAll: '所有',\n chartScopeMine: '我的',\n chartLegendDone: '完成',\n chartLegendCreated: '新增',\n chartAvg: '周均 {n}',\n periodRange: '{from} ~ {to}',\n\n scopeLabel: '范围',\n scopeMine: '我的',\n scopeAll: '全部工作区',\n statusLabel: '状态',\n statusAll: '全部',\n statusActive: '进行中',\n statusOpen: '待认领',\n statusBlocked: '阻塞',\n statusStale: '停滞',\n workspaceLabel: '工作区',\n workspaceAll: '全部工作区',\n searchPlaceholder: '搜索标题 / 任务号 / 人 / 分类 / 标签',\n\n regionMine: '我的待办',\n regionMineHint: '跨工作区,按创建时间倒序',\n regionWorkspaces: '按工作区',\n groupHint: '进行中 {doing} | 待认领 {open}',\n showingCount: '显示 {n} 条',\n expandAria: '展开过程文档',\n collapseAria: '收起',\n\n ownerUnclaimed: '待认领',\n fieldCreated: '创建',\n fieldStarted: '开始',\n fieldFinished: '结案',\n timeUnknown: '时间未知',\n worklogTitle: '过程文档',\n worklogPath: '路径',\n worklogMissing: '(缺失)',\n mdCopy: '复制',\n mdCopied: '已复制',\n mdFootnotes: '脚注',\n\n emptyNoWorkspace: '还没有台账工作区',\n emptyNoWorkspaceHint: '在某个项目里跑 task-ledger skill 的 init 建档(生成 ledger/progress.json),这里就会出现它。',\n emptyNoTask: '一条任务都没有',\n emptyNoTaskHint: '用 task-ledger skill 的 new 开一个任务,或让别人开在这里等你认领。',\n emptyFiltered: '没有符合当前筛选的任务',\n\n badgeTitle: '我名下待办',\n openBoard: '打开任务台账',\n}\n\n/** English(与 zh 键完全对齐)。 */\nexport const en: Record<LocaleKey, string> = {\n backToConversation: 'Back to chat',\n panelTitle: 'Task ledger',\n tabTasks: 'Tasks',\n tabStats: 'Stats',\n refresh: 'Refresh',\n refreshing: 'Refreshing',\n loading: 'Loading…',\n retry: 'Retry',\n loadFailed: 'Load failed',\n statsPlaceholder: 'Stats reports are not built yet (separate task): category breakdown, completions by person, timeline.',\n\n statMyActive: 'My todos',\n statMyActiveHint: '{n} blocked',\n statOpen: 'Unclaimed',\n statOpenHint: 'oldest waiting {n}',\n statDoing: 'In progress',\n statDoingHint: '{n} by others',\n statBlockedStale: 'Blocked · Stale',\n statStaleWord: 'Stale',\n statStaleHint: '{n}',\n statWeekDone: 'Done this week',\n statLastWeekDone: 'Done last week',\n statMonthDone: 'Done this month',\n statLastMonthDone: 'Done last month',\n statCreatedHint: '{n} created',\n daysNone: '—',\n daysValue: '{n}d',\n\n chartTitle: 'Last 8 weeks',\n chartCaption: 'done (by close time) / created (by create time), weeks start Monday; mine = owner is me',\n chartBucket: 'week of {week}: {done} done, {created} created',\n chartEmpty: 'Nothing in the last 8 weeks',\n chartScopeAll: 'All',\n chartScopeMine: 'Mine',\n chartLegendDone: 'Done',\n chartLegendCreated: 'Created',\n chartAvg: 'avg {n}/wk',\n periodRange: '{from} ~ {to}',\n\n scopeLabel: 'Scope',\n scopeMine: 'Mine',\n scopeAll: 'All workspaces',\n statusLabel: 'Status',\n statusAll: 'All',\n statusActive: 'Active',\n statusOpen: 'Unclaimed',\n statusBlocked: 'Blocked',\n statusStale: 'Stale',\n workspaceLabel: 'Workspace',\n workspaceAll: 'All workspaces',\n searchPlaceholder: 'Search title / id / person / category / tag',\n\n regionMine: 'My todos',\n regionMineHint: 'across workspaces, newest first',\n regionWorkspaces: 'By workspace',\n groupHint: '{doing} active | {open} unclaimed',\n showingCount: '{n} shown',\n expandAria: 'Expand worklog',\n collapseAria: 'Collapse',\n\n ownerUnclaimed: 'unclaimed',\n fieldCreated: 'created',\n fieldStarted: 'started',\n fieldFinished: 'closed',\n timeUnknown: 'unknown time',\n worklogTitle: 'Worklog',\n worklogPath: 'path',\n worklogMissing: '(missing)',\n mdCopy: 'Copy',\n mdCopied: 'Copied',\n mdFootnotes: 'Footnotes',\n\n emptyNoWorkspace: 'No ledger workspace yet',\n emptyNoWorkspaceHint: 'Run the task-ledger skill init in a project (creates ledger/progress.json) and it shows up here.',\n emptyNoTask: 'No tasks yet',\n emptyNoTaskHint: 'Create one with the task-ledger skill new, or leave one here for others to claim.',\n emptyFiltered: 'No tasks match the current filters',\n\n badgeTitle: 'my todos',\n openBoard: 'Open task ledger',\n}\n\n/** 翻译函数类型(宿主渲染器按 `locale:` 声明合成的 `t` 席位同形)。 */\nexport type Translate = (key: LocaleKey, params?: Record<string, string | number>) => string\n\n/** 把宿主给的无参 t 包成带占位符替换的 t(模板一律 `{name}`,不依赖宿主是否支持 params)。 */\nexport function interpolateTranslate(base: Translate): Translate {\n return (key, params) => {\n const raw = base(key)\n if (params === undefined) return raw\n return raw.replace(/\\{(\\w+)\\}/g, (match, name: string) => (name in params ? String(params[name]) : match))\n }\n}\n\n/** 键名兜底:宿主没给 t 席位时用(键名绝不上界面)。 */\nexport function fallbackTranslate(key: LocaleKey, params?: Record<string, string | number>): string {\n return interpolateTranslate((name) => zh[name] ?? name)(key, params)\n}\n","/**\n * 右上浮标入口(`shell.overlay` list 条目,全帧浮层,条目并排添加不替换)。\n * 定位用宿主变量 `--dsh-frame-overlay-top`(标题栏净空 + 20px),**不写死 top 值**;\n * 浮层默认 `pointer-events:none`、子元素自动恢复 ⇒ 自绘条目不挡下方应用。\n *\n * 角标 = 我名下 `doing` + `blocked` 计数(host 统计真值,SSE 已否 ⇒ 取自同一份快照,>0 才画);\n * 0→n 跳变时一次脉冲动画,不自动消失。宿主**无**通知 API ⇒ 「通知」就是这个角标。\n */\nimport { createElement as h, useEffect, useRef, useState, useSyncExternalStore, type ReactNode } from 'react'\nimport type { Translate } from './locales'\nimport { boardSnapshot, boardSubscribe } from './store'\nimport { ensureUiBase } from './ui'\n\n/** 台账图标(黑白线条,描边走 currentColor ⇒ 随主题反色)。 */\nfunction LedgerIcon(props: { size?: number }): ReactNode {\n const size = props.size ?? 18\n const stroke = { fill: 'none', stroke: 'currentColor', strokeWidth: 7, strokeLinecap: 'round', strokeLinejoin: 'round' } as const\n return h('svg', { width: size, height: size, viewBox: '0 0 128 128', 'aria-hidden': true },\n h('path', { d: 'M96 34 A10 10 0 0 1 106 44 V102 A10 10 0 0 1 96 112 H32 A10 10 0 0 1 22 102 V44 A10 10 0 0 1 32 34 Z', ...stroke }),\n h('path', { d: 'M50 22 V46 M78 22 V46', ...stroke }),\n h('path', { d: 'M44 66 H84 M44 86 H70', ...stroke }),\n )\n}\n\nexport function OverlayEntry(props: { t: Translate; onOpen: () => void }): ReactNode {\n const { t, onOpen } = props\n ensureUiBase()\n const state = useSyncExternalStore(boardSubscribe, boardSnapshot)\n const count = state.board?.stats.myActive ?? 0\n const previous = useRef(0)\n const [pulse, setPulse] = useState(0)\n\n // 0→n 跳变播一次脉冲;计数归零不播(角标本就不渲染)。\n useEffect(() => {\n if (count > previous.current) setPulse(value => value + 1)\n previous.current = count\n }, [count])\n\n return h('div', {\n style: {\n position: 'absolute',\n top: 'var(--dsh-frame-overlay-top,20px)',\n right: '12px',\n zIndex: 'var(--dtl-z-badge,20)',\n pointerEvents: 'auto',\n },\n },\n h('button', {\n type: 'button',\n title: t('openBoard'),\n 'aria-label': t('openBoard'),\n onClick: onOpen,\n style: {\n position: 'relative',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n width: '32px',\n height: '32px',\n padding: 0,\n border: '1px solid var(--dtl-border)',\n borderRadius: 'var(--dtl-radius-sm)',\n background: 'var(--dtl-solid)',\n color: 'var(--dtl-on-solid)',\n boxShadow: 'var(--dtl-shadow-1)',\n cursor: 'pointer',\n },\n },\n h(LedgerIcon, { size: 18 }),\n count > 0\n ? h('span', { key: pulse, className: 'dtl-badge', 'data-pulse': pulse > 0, title: t('badgeTitle') }, String(count))\n : null,\n ),\n )\n}\n","/**\n * 浏览器半入口(client bundle,产物 dist/client.js):装配层,不写业务逻辑。\n *\n * 三处注册各自挂进「服务就位才触发」的 slots 注入(参考插件真机教训:顶层 inject 声明\n * 组合满足不了的依赖会让 entry 一直 pending、卡死整个 dsh 启动):\n * · locale 字典(zh / en)——界面语言跟随宿主,插件不自备开关;\n * · `main` keyed 槽整页(看板首页);\n * · `shell.overlay` list 槽浮标入口(点开 = `ctx.layout.selectPanel(<看板 id>)`)。\n *\n * 宿主能力一律**本地结构化声明**,不 import 官方包(rc 接口会无声变);\n * `t` 席位由宿主渲染器按注册项的 `locale:` 声明合成进组件 props。\n */\nimport { createElement as h } from 'react'\nimport { BoardHome } from './board-home'\nimport { en, fallbackTranslate, interpolateTranslate, zh, type LocaleKey, type Translate } from './locales'\nimport { OverlayEntry } from './overlay-entry'\n\n/** 命名空间(settings / locale / 面板 id 同名,便于对应)。 */\nconst NS = 'dsh-task-ledger'\n/** `main` 槽的 key(非 `conversation` ⇒ 不绑 Session)。 */\nconst PANEL_ID = NS\n/** `shell.overlay` list 条目的 id(新 id = 并排添加,不替换宿主条目)。 */\nconst OVERLAY_ID = `${NS}-badge`\n\n/** 浏览器插件上下文:只声明本文件实际用到的服务面。 */\ninterface ClientContext {\n inject(deps: readonly string[], callback: (ctx: ClientContext) => void): void\n effect(setup: () => void): void\n locale?: {\n register(ns: string, dictionaries: Record<string, Record<string, string>>): void\n }\n layout?: {\n selectPanel(panelId: string | null): void\n }\n slots: {\n inject(slot: string, factory: () => () => void): () => void\n register(options: Record<string, unknown>, component: unknown): () => void\n }\n}\n\n/** 宿主没给 t 席位时的兜底(键名绝不上界面)。 */\nconst t: Translate = interpolateTranslate((key: LocaleKey, params?: Record<string, string | number>) =>\n fallbackTranslate(key, params))\n\nexport function apply(ctx: ClientContext): void {\n // ① 字典注册(宿主语言切换 ⇒ 渲染器换 t 引用 ⇒ 整页重渲染)\n ctx.inject(['locale'], (localeCtx) => {\n ctx.effect(() => localeCtx.locale?.register(NS, { zh, en }))\n })\n\n // ② 布局服务:浮标点开整页 / 整页返回会话\n let selectPanel: (id: string | null) => void = () => {}\n ctx.inject(['layout'], (sub) => {\n const layout = sub.layout\n if (layout !== undefined) selectPanel = (id) => { layout.selectPanel(id) }\n })\n\n // ③ 槽位注册(只依赖 slots,自成一块:settings / locale 缺席也不该让入口消失)\n ctx.inject(['slots'], (sub) => {\n sub.slots.inject('main', () =>\n sub.slots.register(\n { name: 'main', key: PANEL_ID, locale: NS },\n (props: { t?: Translate }) => h(BoardHome, {\n t: props.t ?? t,\n onBack: () => { selectPanel(null) },\n }),\n ),\n )\n sub.slots.inject('shell.overlay', () =>\n sub.slots.register(\n { name: 'shell.overlay', id: OVERLAY_ID, locale: NS },\n (props: { t?: Translate }) => h(OverlayEntry, {\n t: props.t ?? t,\n onOpen: () => { selectPanel(PANEL_ID) },\n }),\n ),\n )\n })\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"client.js","names":["h","useState","h","useState","useMemo","StateDot","IconChevronDownOutlineRegular","IconChevronRightOutlineRegular","MarkdownText","useSyncExternalStore","useState","useMemo","h","IconRefreshOutlineRegular","useSyncExternalStore","useRef","useState","h","h"],"sources":["../src/client/api.ts","../src/client/store.ts","../src/client/rows.ts","../src/client/format.ts","../src/client/tokens.ts","../src/client/ui.ts","../src/client/task-row.ts","../src/client/board-home.ts","../src/client/locales.ts","../src/client/overlay-entry.ts","../src/client/index.ts"],"sourcesContent":["/**\n * 数据面取数**单源**:所有 client → host 的 HTTP 都在这里,页面不许各写一份 fetch。\n *\n * 纪律:只做传输与形状校验,不造兜底假值(DSH 插件规则「数据真实」)——\n * 失败就抛,由调用方显示错误态;返回什么渲染什么。\n *\n * ⚠️ 必须带超时且 abort 要带 reason:一次挂起的请求会让面板永久停在「读取中」,\n * 而不带 reason 时浏览器把 error.message 填成「signal is aborted without reason」,\n * 看不出是超时(姊妹仓 2026-10-07 真机教训)。\n */\nimport type { BoardPayload, FilterOptions, HistoryRecord, TaskDetail } from './types'\n\n/** host 路由前缀(全仓唯一;与 host/routes.ts 的 API_PREFIX 对应)。 */\nexport const API_PREFIX = 'api/task-ledger'\n\nconst DEFAULT_TIMEOUT_MS = 8_000\n\n/**\n * 错误响应榨人话:host 路由(routes.ts)发的是 `{ error: \"...\" }`,那里的话是**能直接上界面的**\n * (带工作区名、python3 起不来之类的原文)。不带它界面上只剩「HTTP 500」,等于没报错。\n */\nexport function httpErrorMessage(status: number, body: string): string {\n try {\n const parsed = JSON.parse(body) as { error?: unknown }\n if (typeof parsed.error === 'string' && parsed.error !== '') return `HTTP ${status}:${parsed.error}`\n } catch { /* 不是 JSON:走原文 */ }\n const text = body.trim().slice(0, 200)\n return text === '' ? `HTTP ${String(status)}` : `HTTP ${String(status)}:${text}`\n}\n\n/** 带超时的 JSON 取数(相对路径,同源)。 */\nexport async function fetchJson<T>(path: string, init: RequestInit = {}, timeoutMs = DEFAULT_TIMEOUT_MS): Promise<T> {\n const controller = new AbortController()\n const timer = setTimeout(() => {\n controller.abort(new DOMException(`请求超时(${timeoutMs}ms 未响应)`, 'TimeoutError'))\n }, timeoutMs)\n try {\n const res = await fetch(`${API_PREFIX}${path}`, { ...init, signal: controller.signal, cache: 'no-store' })\n if (!res.ok) throw new Error(httpErrorMessage(res.status, await res.text().catch(() => '')))\n return await res.json() as T\n } finally {\n clearTimeout(timer)\n }\n}\n\n/** 首页快照(在办全量 + 两个完成类统计 + 各工作区身份)。 */\nexport function fetchBoard(): Promise<BoardPayload> {\n return fetchJson<BoardPayload>('/board')\n}\n\n/** 已结案列表(服务端按窗口 / 标签 / 分页过滤)。 */\nexport function fetchHistory(params: { since?: string; until?: string; tag?: string; limit?: number }): Promise<HistoryRecord[]> {\n const query = new URLSearchParams()\n if (params.since !== undefined) query.set('since', params.since)\n if (params.until !== undefined) query.set('until', params.until)\n if (params.tag !== undefined && params.tag !== '') query.set('tag', params.tag)\n if (params.limit !== undefined) query.set('limit', String(params.limit))\n const suffix = query.toString()\n return fetchJson<{ records: HistoryRecord[] }>(`/history${suffix === '' ? '' : `?${suffix}`}`)\n .then(body => body.records ?? [])\n}\n\n/** 单条在办 + 过程文档正文(展开行用)。 */\nexport function fetchTaskDetail(workspaceId: string, taskId: string): Promise<TaskDetail> {\n const query = new URLSearchParams({ ws: workspaceId, id: taskId })\n return fetchJson<TaskDetail>(`/task?${query.toString()}`)\n}\n\n/** 筛选项候选(在办全量 + 近 13 个月历史的去重 owner / category / tag)。 */\nexport function fetchFilters(): Promise<FilterOptions> {\n return fetchJson<FilterOptions>('/filters')\n}\n","/**\n * 看板数据仓(client 侧**唯一**取数入口):浮标角标与整页共用同一份快照,\n * 谁先挂载谁触发首读;刷新按钮走同一个 `refresh()`,**不轮询**(2026-10-17 拍板:\n * 后端纯按需,刷新时机归前端 = 打开面板首读 + 手动刷新)。\n *\n * 消费方式:`useSyncExternalStore(boardSubscribe, boardSnapshot)`。\n * ⚠️ `getSnapshot` 必须返回**稳定引用**(每次新建对象会被判定为「一直在变」⇒ 无限重渲染),\n * 故只在真变了时才换 `state` 对象。\n */\nimport { fetchBoard } from './api'\nimport type { BoardPayload } from './types'\n\n/** 范围:我的 / 全部工作区。 */\nexport type Scope = 'mine' | 'all'\n/** 状态档:进行中 = doing+blocked;停滞 = host 已标记的 stale。 */\nexport type StatusFilter = 'all' | 'active' | 'open' | 'blocked' | 'stale'\n\nexport interface Query {\n scope: Scope\n status: StatusFilter\n /** 工作区 id;'' = 全部工作区。 */\n workspace: string\n keyword: string\n}\n\nexport interface BoardState {\n status: 'loading' | 'ready' | 'error'\n board: BoardPayload | null\n error: string | null\n query: Query\n /** 手动刷新在途(按钮转圈 / 禁用)。 */\n refreshing: boolean\n}\n\nconst INITIAL_QUERY: Query = { scope: 'all', status: 'all', workspace: '', keyword: '' }\n\nlet state: BoardState = {\n status: 'loading',\n board: null,\n error: null,\n query: INITIAL_QUERY,\n refreshing: false,\n}\n\nconst listeners = new Set<() => void>()\n/** 首读只触发一次(谁先订阅谁触发),之后一律靠 `refresh()` / `setQuery()`。 */\nlet booted = false\n/** 在途轮次:迟到的响应按 token 丢弃,避免旧结果盖新结果。 */\nlet runToken = 0\n\nfunction emit(): void {\n for (const listener of [...listeners]) listener()\n}\n\nfunction set(patch: Partial<BoardState>): void {\n state = { ...state, ...patch }\n emit()\n}\n\nexport function boardSnapshot(): BoardState {\n return state\n}\n\nexport function boardSubscribe(listener: () => void): () => void {\n listeners.add(listener)\n if (!booted) {\n booted = true\n void refresh()\n }\n return () => { listeners.delete(listener) }\n}\n\n/** 重读:在办快照 + 完成类统计与近 8 周趋势(一次 `/board` 全带回)。 */\nexport async function refresh(): Promise<void> {\n const token = ++runToken\n set({ refreshing: true })\n try {\n const board = await fetchBoard()\n if (token !== runToken) return\n set({ status: 'ready', board, error: null })\n } catch (error) {\n if (token !== runToken) return\n set({ status: 'error', error: error instanceof Error ? error.message : String(error) })\n } finally {\n if (token === runToken) set({ refreshing: false })\n }\n}\n\n/** 改筛选:全部在浏览器里完成(快照已含全部在办),不发请求。 */\nexport function setQuery(patch: Partial<Query>): void {\n set({ query: { ...state.query, ...patch } })\n}\n","/**\n * 列表视图的筛选 / 分区 / 排序(**唯一**实现,纯函数、不碰网络):页面只渲染,不各写一份过滤。\n * 定稿见 docs/design/features/board-home.md §二.3:两区结构(我的待办平铺 / 按工作区分组),\n * 已结案**不在首页**(去统计 tab)。\n *\n * 单独成文件是为了能被冒烟直测(store.ts 引 api.ts,Node 剥类型解析不了无扩展名相对导入)。\n */\nimport type { BoardPayload, BoardTask, HistoryRecord, TaskRow } from './types'\nimport type { Query } from './store'\n\n/** 列表行:在办与已结案两套形状,行件按 kind 分支(不做「统一成一个假结构」的归一化)。 */\nexport type Row =\n | { kind: 'task'; key: string; task: TaskRow; mine: boolean }\n | { kind: 'closed'; key: string; record: HistoryRecord }\n\n/** 一个工作区分组。 */\nexport interface WorkspaceGroup {\n readonly workspaceId: string\n readonly title: string\n readonly doingCount: number\n readonly openCount: number\n readonly rows: readonly Row[]\n}\n\n/** 两区视图:第一区我的待办 + 第二区按工作区分组(两者不重复)。 */\nexport interface BoardView {\n readonly mine: readonly Row[]\n readonly groups: readonly WorkspaceGroup[]\n readonly total: number\n}\n\n/** 关键字命中(标题 / 任务号 / 人 / 分类 / 标签;已结案另含「完成了什么」)。 */\nfunction hitKeyword(keyword: string, parts: readonly (string | null | undefined)[]): boolean {\n const needle = keyword.trim().toLowerCase()\n if (needle === '') return true\n return parts.some(part => part?.toLowerCase().includes(needle) === true)\n}\n\n/** 「我的」判定:owner = 该工作区的当前 git 身份,且状态是 doing / blocked(与 host 统计同口径)。 */\nexport function isMine(task: TaskRow, board: BoardPayload | null): boolean {\n if (board === null) return false\n const identity = board.identities[task.workspace.id] ?? null\n if (identity === null || identity === '') return false\n return task.owner === identity && (task.status === 'doing' || task.status === 'blocked')\n}\n\n/** 状态档命中(停滞由 host 标记,前端不判阈值)。 */\nfunction statusHit(task: BoardTask, status: Query['status']): boolean {\n if (status === 'all') return true\n if (status === 'active') return task.status === 'doing' || task.status === 'blocked'\n if (status === 'open') return task.status === 'open'\n if (status === 'blocked') return task.status === 'blocked'\n return task.stale === true\n}\n\n/** 组内排序:别人的 doing/blocked 在前、待认领在后;各自按创建 / 开始时间倒序。 */\nfunction inGroupOrder(a: Row, b: Row): number {\n if (a.kind !== 'task' || b.kind !== 'task') return 0\n const rank = (task: TaskRow): number => (task.status === 'open' ? 1 : 0)\n if (rank(a.task) !== rank(b.task)) return rank(a.task) - rank(b.task)\n return String(b.task.created ?? b.task.started ?? '').localeCompare(String(a.task.created ?? a.task.started ?? ''))\n}\n\n/**\n * 当前筛选下的两区视图:\n * - 第一区「我的待办」= 我名下 doing/blocked(命中状态档 / 工作区 / 关键字);\n * - 第二区「按工作区」= 其余任务(别人的 doing/blocked + 全部待认领),按工作区分组;\n * - 范围=「我的」时只出第一区。\n */\nexport function selectView(current: { board: BoardPayload | null; query: Query }): BoardView {\n const { board, query } = current\n if (board === null) return { mine: [], groups: [], total: 0 }\n const tasks = board.tasks.filter((task) => {\n if (!statusHit(task, query.status)) return false\n if (query.workspace !== '' && task.workspace.id !== query.workspace) return false\n return hitKeyword(query.keyword, [task.title, task.id, task.owner, task.creator, task.category, ...(task.tags ?? [])])\n })\n const mineRows: Row[] = tasks\n .filter((task) => isMine(task, board))\n .sort((a, b) => String(b.created ?? b.started ?? '').localeCompare(String(a.created ?? a.started ?? '')))\n .map((task): Row => ({ kind: 'task', key: `${task.workspace.id}:${task.id}`, task, mine: true }))\n const rest = query.scope === 'mine'\n ? []\n : tasks.filter((task) => !isMine(task, board))\n const byWorkspace = new Map<string, { title: string; rows: Row[] }>()\n for (const task of rest) {\n const entry = byWorkspace.get(task.workspace.id) ?? { title: task.workspace.title, rows: [] }\n entry.rows.push({ kind: 'task', key: `${task.workspace.id}:${task.id}`, task, mine: false })\n byWorkspace.set(task.workspace.id, entry)\n }\n const groups: WorkspaceGroup[] = [...byWorkspace.entries()]\n .map(([workspaceId, entry]) => ({\n workspaceId,\n title: entry.title,\n doingCount: entry.rows.filter((row) => row.kind === 'task' && row.task.status !== 'open').length,\n openCount: entry.rows.filter((row) => row.kind === 'task' && row.task.status === 'open').length,\n rows: entry.rows.sort(inGroupOrder),\n }))\n .sort((a, b) => a.title.localeCompare(b.title, 'zh-Hans-CN'))\n return { mine: mineRows, groups, total: mineRows.length + groups.reduce((sum, group) => sum + group.rows.length, 0) }\n}\n\n/**\n * 已结案列表(统计 tab 用;首页不渲染已结案 ⇒ 这里只留数据面,供后续统计 tab 复用同一套筛选口径)。\n */\nexport function selectClosed(current: { history: readonly HistoryRecord[]; query: Query }): Row[] {\n return current.history\n .filter((record) => hitKeyword(current.query.keyword, [record.summary, record.workpackage, record.owner, record.category, ...(record.tags ?? [])]))\n .map((record, index) => ({ kind: 'closed', key: `${record.src ?? ''}:${record.time ?? ''}:${index}`, record }))\n}\n","/**\n * 客户端时间口径**单源**:补零 / 时刻串(本机时区)。\n * 解析失败按调用方给的兜底返回(不编造时间)。\n *\n * 2026-10-17 改版后首页不再做时间范围过滤(时间只作用于统计卡与趋势图,由 host 聚合)\n * ⇒ 原来的 `rangeToDates` / `withinRange` / `RangeKey` 一并删除;统计 tab 要做时间筛选时\n * 按当时口径在此重建,别处不许再写一份。\n */\nexport function pad2(value: number | string): string {\n return String(value).padStart(2, '0')\n}\n\n/** ISO → `YYYY-MM-DD HH:mm`(`seconds: true` 时补 `:ss`);解析失败返回 `fallback`(缺省原串)。 */\nexport function formatDateTime(iso: string, opts?: { seconds?: boolean; fallback?: string }): string {\n const ms = Date.parse(iso)\n if (Number.isNaN(ms)) return opts?.fallback ?? iso\n const d = new Date(ms)\n const base = `${d.getFullYear()}-${pad2(d.getMonth() + 1)}-${pad2(d.getDate())} ${pad2(d.getHours())}:${pad2(d.getMinutes())}`\n return opts?.seconds === true ? `${base}:${pad2(d.getSeconds())}` : base\n}\n","/**\n * 语义 token 层:`--dtl-*`(dtl = dsh-task-ledger)。\n *\n * 规矩(docs/design/ui-style.md §三,2026-10-17 对齐 dispatch 表后收严):\n * - **只有本文件定义 `--dtl-*`**;其它文件一律 `var(--dtl-*)` 消费,不许直接写 `var(--dsw-*)`;\n * - 每个 token 都带兜底值 ⇒ 宿主升级换名时不炸(最坏退到兜底色,不会白屏);\n * - 明暗差异只写在本文件末尾的 `body[data-ds-dark-theme]` 段:能靠宿主 alias 表达的\n * 一律不写覆盖(alias 自己随主题变),只有「固定中性面 / 半透明底」这类 alias 表达不了的才进该段。\n * - **离散刻度**:圆角 4/8/12/16/20/28、间距 4/8/12/16、控件高 24/28/32、字号 11/12/13/14/16\n * ——业务文件不许出现 5/7/9px、不许自造高度档(要加档位改本文件)。\n *\n * 明暗判据 = `body[data-ds-dark-theme]`(宿主 0.2.0-rc.2 源码核实:启动脚本 toggleAttribute 写入)。\n * 插件**禁用** `prefers-color-scheme`(它跟系统、不跟用户在宿主里的选择)。\n * 因为判据是纯 CSS 选择器 ⇒ 宿主切主题即生效,**不需要 MutationObserver**。\n *\n * 挂载点 = `body`:插件界面(含将来 portal 到 body 的弹层)全在 body 内,零调用点成本。\n */\nexport const UI_TOKENS_CSS = `\nbody{\n /* ── 文字四档 + 反色面 ───────────────────────────────────────── */\n --dtl-fg:var(--dsw-alias-label-primary,#1f2328);\n --dtl-fg-2:var(--dsw-alias-label-secondary,#5c6370);\n --dtl-fg-3:var(--dsw-alias-label-tertiary,#767d87);\n --dtl-fg-4:var(--dsw-alias-label-caption,#9aa1ab);\n --dtl-fg-dim:var(--dsw-alias-label-dimmed,#b9bfc7);\n /* 反色面上的字(角标 / 实心钮):暗色下是 bluish-1000 ⇒ 用 -foreground 而非 -inverted */\n --dtl-fg-inverse:var(--dsw-alias-label-primary-foreground,#fff);\n\n /* ── 面 ──────────────────────────────────────────────────────── */\n --dtl-surface-1:var(--dsw-alias-bg-layer-1,#fff);\n --dtl-surface-2:var(--dsw-alias-bg-layer-2,#fff);\n --dtl-surface-base:var(--dsw-alias-bg-base,#fff);\n /* 「选中亮片」底盘(配 shadow-raised 用) */\n --dtl-surface-raised:var(--dsw-alias-bg-layer-1,#fff);\n /* 固定中性实面(自绘浮标 / 角标这类必须自己变的东西) */\n --dtl-solid:var(--dsw-static-neutral-00,#fff);\n --dtl-on-solid:var(--dsw-alias-label-primary,#1f2328);\n /* 斑马纹 / 次层面(浅色下近白、深色下深一档) */\n --dtl-plate:var(--dsw-static-neutral-50,#fafafa);\n --dtl-plate-hover:var(--dsw-static-neutral-100,#f5f5f5);\n /* 表头底:浅色比斑马纹再深一档(两者不能撞色) */\n --dtl-head-bg:var(--dsw-static-neutral-100,#f5f5f5);\n\n /* ── 描边四档(宿主真值 l1 4% / l2 10% / l3 12% / l4 16%)────── */\n --dtl-border-faint:var(--dsw-alias-border-l1,#0000000a);\n --dtl-border:var(--dsw-alias-border-l2,#0000001a);\n --dtl-border-strong:var(--dsw-alias-border-l3,#0000001f);\n --dtl-border-heavy:var(--dsw-alias-border-l4,#00000029);\n\n /* ── 语义色 ──────────────────────────────────────────────────── */\n /* 品牌「面」色:明色近黑、暗色近白(实心钮选中态即此),不是蓝 */\n --dtl-accent:var(--dsw-alias-brand-primary,#0f1115);\n /* 蓝色强调 / 选中 / 焦点一律走这条 */\n --dtl-business:var(--dsw-alias-state-business-primary,#3b5bdb);\n --dtl-success:var(--dsw-alias-state-success-primary,#22c55e);\n /* ⚠️ 宿主是 state-warn(不是 state-warning,后者无此定义) */\n --dtl-warning:var(--dsw-alias-state-warn-primary,#f59e0b);\n --dtl-danger:var(--dsw-alias-state-error-primary,#ec1313);\n --dtl-link:var(--dsw-alias-link,#3b5bdb);\n /* 语义色实面上的字(角标 / 实心提示):红绿黄都是中调 ⇒ 白字两个主题都可读 */\n --dtl-on-signal:#fff;\n /* 语义色浅底:color-mix 现算 ⇒ 跟随宿主 alias,主题一换自动成立 */\n --dtl-business-soft:color-mix(in srgb,var(--dtl-business) 8%,transparent);\n --dtl-success-soft:color-mix(in srgb,var(--dtl-success) 8%,transparent);\n --dtl-warning-soft:color-mix(in srgb,var(--dtl-warning) 8%,transparent);\n --dtl-danger-soft:color-mix(in srgb,var(--dtl-danger) 8%,transparent);\n\n /* ── 交互底 ──────────────────────────────────────────────────── */\n --dtl-hover:var(--dsw-alias-interactive-bg-hover,rgba(38,49,72,.06));\n --dtl-active:var(--dsw-alias-interactive-bg-active,#2631481a);\n /* 卡 / 行 hover 高亮:淡蓝(灰高亮在卡底上几乎看不出来) */\n --dtl-card-hover:rgba(37,99,235,.07);\n /* 展开行底:带透明度的蓝,透出卡片底色(灰底跟斑马纹分不出来) */\n --dtl-open-bg:rgba(37,99,235,.10);\n --dtl-open-bg-soft:rgba(37,99,235,.05);\n /* chip / 标签小底板:浅色下要看得见 ⇒ 掺 7% 近黑;hover 加倍 */\n --dtl-chip-bg:rgba(15,15,15,.07);\n --dtl-chip-bg-hover:rgba(15,15,15,.14);\n /* 图表「新增」柱:**实底**中性灰。半透明底压在卡上会发糊(用户 2026-10-17:「模模糊糊」) */\n --dtl-chart-created:var(--dsw-static-neutral-300,#d4d4d8);\n\n /* ── 圆角(宿主真值 4/8/12/16/20/28)──────────────────────────── */\n --dtl-radius-xs:var(--dsw-radius-xs,4px);\n --dtl-radius-sm:var(--dsw-radius-sm,8px);\n --dtl-radius-md:var(--dsw-radius-md,12px);\n --dtl-radius-lg:var(--dsw-radius-lg,16px);\n --dtl-radius-xl:var(--dsw-radius-xl,20px);\n --dtl-radius-panel:var(--dsw-radius-panel,28px);\n\n /* ── 间距四拍(只许这四档)────────────────────────────────────── */\n --dtl-space-1:4px;\n --dtl-space-2:8px;\n --dtl-space-3:12px;\n --dtl-space-4:16px;\n\n /* ── 控件高度:离散三档(调用点不许自定义高度)────────────────── */\n --dtl-control-h-sm:24px;\n --dtl-control-h-md:28px;\n --dtl-control-h-lg:32px;\n\n /* ── 字号 / 行高(直绑宿主字号族 11/12/13/14/16)──────────────── */\n --dtl-font-xs:var(--dsw-font-xxxs-11-font-size,11px);\n --dtl-font-sm:var(--dsw-font-xxs-12-font-size,12px);\n --dtl-font-md:var(--dsw-font-xs-13-font-size,13px);\n --dtl-font-lg:var(--dsw-font-s-14-font-size,14px);\n --dtl-font-xl:var(--dsw-font-base-16-font-size,16px);\n --dtl-line-xs:var(--dsw-font-xxxs-11-line-height,14px);\n --dtl-line-sm:var(--dsw-font-xxs-12-line-height,18px);\n --dtl-line-md:var(--dsw-font-xs-13-line-height,20px);\n --dtl-line-lg:var(--dsw-font-s-14-line-height,22px);\n --dtl-line-xl:var(--dsw-font-base-16-line-height,24px);\n --dtl-font-mono:var(--ds-font-family-code,monospace);\n /* 统计卡主数字:宿主字号族最大 16px,看板主数字要更大 ⇒ 单开一档(别处不许直接写 px) */\n --dtl-font-stat:26px;\n\n /* ── 投影 / 动效 / 层级 ──────────────────────────────────────── */\n --dtl-shadow-1:var(--dsw-elevation-soft,0 4px 16px 0 #00000008);\n --dtl-shadow-raised:var(--dsw-elevation-soft,0 4px 16px 0 #00000008);\n --dtl-shadow-2:var(--dsw-shadow-lv3,0 12px 32px 0 #00000014);\n --dtl-dur:var(--ds-transition-duration,.2s);\n --dtl-dur-fast:var(--ds-transition-duration-fast,.12s);\n --dtl-ease:var(--ds-ease-in-out,cubic-bezier(.4,0,.2,1));\n --dtl-z-badge:20;\n}\n\n/* ── 明暗差异的唯一落点 ─────────────────────────────────────────\n 只放 alias 表达不了的固定面 / 半透明底;颜色若能靠 alias 自动跟随,一律不写在这里。 */\nbody[data-ds-dark-theme]{\n --dtl-solid:var(--dsw-static-neutral-900,#0f0f0f);\n --dtl-on-solid:var(--dsw-static-neutral-00,#fff);\n --dtl-plate:var(--dsw-static-neutral-850,#212123);\n --dtl-plate-hover:var(--dsw-static-neutral-800,#292929);\n /* 暗色表头反过来:比卡片面亮、比斑马纹浅一档 */\n --dtl-head-bg:var(--dtl-surface-2);\n /* 暗色下蓝要更亮、透明度更高才压得住深底 */\n --dtl-open-bg:rgba(96,165,250,.18);\n --dtl-open-bg-soft:rgba(96,165,250,.08);\n --dtl-card-hover:rgba(96,165,250,.13);\n /* 暗色 chip 底板反过来掺白 */\n --dtl-chip-bg:rgba(255,255,255,.08);\n --dtl-chip-bg-hover:rgba(255,255,255,.16);\n --dtl-chart-created:var(--dsw-static-neutral-600,#525252);\n /* 语义色浅底在深色下压到 5%(8% 在深底上偏重) */\n --dtl-business-soft:color-mix(in srgb,var(--dtl-business) 5%,transparent);\n --dtl-success-soft:color-mix(in srgb,var(--dtl-success) 5%,transparent);\n --dtl-warning-soft:color-mix(in srgb,var(--dtl-warning) 5%,transparent);\n --dtl-danger-soft:color-mix(in srgb,var(--dtl-danger) 5%,transparent);\n}\n`\n\n/**\n * 控件皮肤(L2):**一类控件唯一实现**,抄 dispatch 表 controls-css 的规矩——\n * 只消费 var(--dtl-*),一个颜色 / 尺寸 / 圆角 / 字号字面量都不许出现;\n * 高度只取三档,有边框的变体用 1px 真边框 + 内部 padding 补回 ⇒ 有边 / 无边同高。\n */\nexport const UI_CONTROLS_CSS = `\n/* ── 分段控件(滑动块)────────────────────────────────────────────\n 结构一套 + 两个受控轴:size(sm|md|lg) × variant(default|inset)。\n 变体只覆盖两个颜色变量(--seg-track / --seg-thumb),结构 / 尺寸 / 交互零重复。 */\n.dtl-seg{display:inline-flex;align-items:center;gap:2px;padding:2px;border-radius:var(--dtl-radius-md);background:var(--seg-track);}\n.dtl-seg--default{--seg-track:var(--dtl-surface-2);--seg-thumb:var(--dtl-surface-raised);border:1px solid var(--dtl-border);}\n.dtl-seg--inset{--seg-track:var(--dtl-hover);--seg-thumb:var(--dtl-surface-raised);border:0;padding:3px;}\n.dtl-seg__item{appearance:none;display:inline-flex;align-items:center;gap:4px;box-sizing:border-box;\n height:calc(var(--dtl-control-h-sm) - 6px);padding:0 12px;border:0;border-radius:var(--dtl-radius-sm);\n background:transparent;color:var(--dtl-fg-2);font-family:inherit;font-size:var(--dtl-font-sm);line-height:var(--dtl-line-sm);\n font-weight:400;white-space:nowrap;cursor:pointer;\n transition:color var(--dtl-dur-fast) var(--dtl-ease),background-color var(--dtl-dur-fast) var(--dtl-ease),box-shadow var(--dtl-dur-fast) var(--dtl-ease);}\n.dtl-seg--md .dtl-seg__item{height:calc(var(--dtl-control-h-md) - 6px);}\n.dtl-seg--lg .dtl-seg__item{height:calc(var(--dtl-control-h-lg) - 6px);}\n.dtl-seg__item:hover:not([aria-pressed='true']):not(:disabled){color:var(--dtl-fg);}\n.dtl-seg__item[aria-pressed='true']{background:var(--seg-thumb);box-shadow:var(--dtl-shadow-raised);color:var(--dtl-fg);font-weight:600;}\n.dtl-seg__item:focus-visible{outline:2px solid var(--dtl-business);outline-offset:-2px;}\n.dtl-seg__item:disabled{cursor:default;opacity:.4;}\n/* 段内角标(如「待认领 3」):语义色实面 + 反白字,0 不渲染;必须是正圆 */\n.dtl-seg__badge{display:inline-flex;align-items:center;justify-content:center;flex:none;\n width:16px;height:16px;padding:0;overflow:hidden;border-radius:50%;\n background:var(--dtl-danger);color:var(--dtl-on-signal);font-size:10px;line-height:16px;font-weight:500;font-variant-numeric:tabular-nums;}\n\n/* ── 按钮 / 图标钮 ───────────────────────────────────────────────\n 与 Segmented 同一条高度纪律;variant 只换颜色,结构 / 尺寸 / 交互只此一份。 */\n.dtl-btn{appearance:none;display:inline-flex;align-items:center;justify-content:center;gap:var(--dtl-space-1);\n box-sizing:border-box;border:1px solid transparent;border-radius:var(--dtl-radius-md);\n background:transparent;color:var(--dtl-fg);font-family:inherit;font-weight:500;white-space:nowrap;cursor:pointer;\n transition:background-color var(--dtl-dur-fast) var(--dtl-ease),border-color var(--dtl-dur-fast) var(--dtl-ease),color var(--dtl-dur-fast) var(--dtl-ease),opacity var(--dtl-dur-fast) var(--dtl-ease);}\n.dtl-btn--sm{height:var(--dtl-control-h-sm);padding:0 10px;font-size:var(--dtl-font-sm);line-height:var(--dtl-line-sm);}\n.dtl-btn--md{height:var(--dtl-control-h-md);padding:0 12px;font-size:var(--dtl-font-md);line-height:var(--dtl-line-md);}\n.dtl-btn--lg{height:var(--dtl-control-h-lg);padding:0 14px;font-size:var(--dtl-font-md);line-height:var(--dtl-line-md);}\n.dtl-btn--primary{background:var(--dtl-accent);color:var(--dtl-fg-inverse);}\n.dtl-btn--primary:hover:not(:disabled){opacity:.9;}\n.dtl-btn--outline{background:var(--dtl-surface-1);border-color:var(--dtl-border-strong);color:var(--dtl-fg);}\n.dtl-btn--outline:hover:not(:disabled){background:var(--dtl-hover);}\n.dtl-btn--ghost{color:var(--dtl-fg-2);}\n.dtl-btn--ghost:hover:not(:disabled){background:var(--dtl-hover);color:var(--dtl-fg);}\n.dtl-btn:focus-visible{outline:2px solid var(--dtl-business);outline-offset:2px;}\n.dtl-btn:disabled{cursor:default;opacity:.5;}\n.dtl-iconbtn{appearance:none;display:inline-flex;align-items:center;justify-content:center;box-sizing:border-box;\n border:1px solid transparent;border-radius:var(--dtl-radius-sm);background:transparent;color:var(--dtl-fg-2);\n font-family:inherit;cursor:pointer;flex:none;\n transition:background-color var(--dtl-dur-fast) var(--dtl-ease),border-color var(--dtl-dur-fast) var(--dtl-ease),color var(--dtl-dur-fast) var(--dtl-ease),opacity var(--dtl-dur-fast) var(--dtl-ease);}\n.dtl-iconbtn--sm{width:var(--dtl-control-h-sm);height:var(--dtl-control-h-sm);font-size:var(--dtl-font-sm);}\n.dtl-iconbtn--md{width:var(--dtl-control-h-md);height:var(--dtl-control-h-md);font-size:var(--dtl-font-md);}\n.dtl-iconbtn--lg{width:var(--dtl-control-h-lg);height:var(--dtl-control-h-lg);font-size:var(--dtl-font-md);}\n.dtl-iconbtn--outline{background:var(--dtl-surface-1);border-color:var(--dtl-border);color:var(--dtl-fg-2);}\n.dtl-iconbtn:hover:not(:disabled){background:var(--dtl-hover);color:var(--dtl-fg);}\n.dtl-iconbtn:focus-visible{outline:2px solid var(--dtl-business);outline-offset:1px;}\n.dtl-iconbtn:disabled{cursor:default;opacity:.4;}\n\n/* ── 输入 / 搜索框 / 下拉 ──────────────────────────────────────── */\n.dtl-input{box-sizing:border-box;border:1px solid var(--dtl-border);border-radius:var(--dtl-radius-md);\n background:var(--dtl-surface-1);color:var(--dtl-fg);font-family:inherit;outline:none;\n transition:border-color var(--dtl-dur-fast) var(--dtl-ease),box-shadow var(--dtl-dur-fast) var(--dtl-ease);}\n.dtl-input--sm{height:var(--dtl-control-h-sm);padding:0 8px;font-size:var(--dtl-font-sm);line-height:var(--dtl-line-sm);}\n.dtl-input--md{height:var(--dtl-control-h-md);padding:0 10px;font-size:var(--dtl-font-md);line-height:var(--dtl-line-md);}\n.dtl-input--lg{height:var(--dtl-control-h-lg);padding:0 12px;font-size:var(--dtl-font-md);line-height:var(--dtl-line-md);}\n.dtl-input::placeholder{color:var(--dtl-fg-dim);}\n.dtl-input:focus{border-color:var(--dtl-business);box-shadow:0 0 0 1px var(--dtl-business);}\n.dtl-input:disabled{cursor:default;opacity:.5;}\n/* 原生 select: appearance:none + 自绘箭头 ⇒ 与输入框同宗(宿主没有廉价下拉件) */\n.dtl-select{appearance:none;-webkit-appearance:none;box-sizing:border-box;\n border:1px solid var(--dtl-border);border-radius:var(--dtl-radius-md);background:var(--dtl-surface-1);color:var(--dtl-fg);\n font-family:inherit;outline:none;cursor:pointer;\n background-image:url(\"data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%23767d87' stroke-width='1.5'><path d='M4 6.5 8 10.5 12 6.5'/></svg>\");\n background-repeat:no-repeat;background-position:right 8px center;background-size:14px;\n transition:border-color var(--dtl-dur-fast) var(--dtl-ease),box-shadow var(--dtl-dur-fast) var(--dtl-ease);}\n.dtl-select--sm{height:var(--dtl-control-h-sm);padding:0 26px 0 8px;font-size:var(--dtl-font-sm);line-height:var(--dtl-line-sm);}\n.dtl-select--md{height:var(--dtl-control-h-md);padding:0 26px 0 10px;font-size:var(--dtl-font-md);line-height:var(--dtl-line-md);}\n.dtl-select--lg{height:var(--dtl-control-h-lg);padding:0 26px 0 12px;font-size:var(--dtl-font-md);line-height:var(--dtl-line-md);}\n.dtl-select:focus{border-color:var(--dtl-business);box-shadow:0 0 0 1px var(--dtl-business);}\n.dtl-select:disabled{cursor:default;opacity:.5;}\n\n/* ── 卡 / chip / 空态 / 列表行(业务壳,皮肤仍只消费 token)────── */\n.dtl-card{background:var(--dtl-surface-1);border:1px solid var(--dtl-border);border-radius:var(--dtl-radius-md);\n transition:background-color var(--dtl-dur-fast) var(--dtl-ease);}\n.dtl-card--clickable{cursor:pointer;text-align:left;font:inherit;color:inherit;width:100%;}\n.dtl-card--clickable:hover{background:var(--dtl-card-hover);}\n.dtl-card--clickable[data-active='true']{border-color:var(--dtl-business);}\n.dtl-card--clickable:focus-visible{outline:2px solid var(--dtl-business);outline-offset:1px;}\n.dtl-chip{display:inline-flex;align-items:center;gap:4px;height:20px;padding:0 8px;border-radius:var(--dtl-radius-sm);\n background:var(--dtl-chip-bg);color:var(--dtl-fg-2);font-size:var(--dtl-font-xs);line-height:var(--dtl-line-xs);white-space:nowrap;}\n.dtl-row{display:flex;align-items:center;gap:var(--dtl-space-2);width:100%;box-sizing:border-box;\n padding:var(--dtl-space-2) var(--dtl-space-3);border:0;border-bottom:1px solid var(--dtl-border-faint);\n background:transparent;font:inherit;color:inherit;text-align:left;cursor:pointer;\n transition:background-color var(--dtl-dur-fast) var(--dtl-ease);}\n.dtl-row:hover{background:var(--dtl-card-hover);}\n.dtl-row:focus-visible{outline:2px solid var(--dtl-business);outline-offset:-2px;}\n.dtl-row[data-open='true']{background:var(--dtl-open-bg);}\n.dtl-row-body{padding:var(--dtl-space-2) var(--dtl-space-3) var(--dtl-space-3);border-bottom:1px solid var(--dtl-border-faint);\n background:var(--dtl-open-bg-soft);font-size:var(--dtl-font-md);}\n.dtl-ellipsis{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}\n\n@media (prefers-reduced-motion: reduce){\n .dtl-seg__item,.dtl-btn,.dtl-iconbtn,.dtl-input,.dtl-select,.dtl-card,.dtl-row{transition:none;}\n}\n`\n","/**\n * UI 基础层:样式单源 + 共用元件(**一类控件只有一份实现**)。\n *\n * 纪律(用户 2026-10-17「样式与方法一律抽象,不要反复重写」;形态抄本机\n * `dsh-task-dispatch-table` 的 `ui/tokens.ts` + `ui/controls-css.ts` + `ui/*.tsx`):\n * 1. **样式**:颜色 / 圆角 / 字号 / 高度一律 `var(--dtl-*)`(定义只在 tokens.ts),\n * 业务文件不许再写第二份 `var(--dsw-*)`、不许自造 `--dtl-*`、不许自造高度档;\n * 2. **元件**:分段 / 按钮 / 图标钮 / 输入 / 下拉 / 卡 / chip / 空态 / 图表各一份,\n * 页面与浮标共用。官方 primitives 只留「官方做得更好」的(StateDot / MarkdownText / 图标)。\n */\nimport { createElement as h, useState, type ChangeEvent, type CSSProperties, type ReactNode } from 'react'\nimport { UI_CONTROLS_CSS, UI_TOKENS_CSS } from './tokens'\n\n/** 基础层样式(tokens + 控件皮肤;幂等注入)。 */\nconst UI_BASE_CSS = `\n/* 统计卡:可点、hover 淡蓝、选中蓝描边 */\n.dtl-stat{display:flex;flex-direction:column;gap:var(--dtl-space-1);\n padding:var(--dtl-space-3) var(--dtl-space-4);text-align:left;}\n.dtl-stat__value{font-size:var(--dtl-font-stat);font-weight:600;line-height:1.15;font-variant-numeric:tabular-nums;}\n.dtl-stat__hint{font-size:var(--dtl-font-xs);line-height:var(--dtl-line-xs);color:var(--dtl-fg-3);}\n\n/* 列表行:头部可点,hover 淡蓝;展开行走 --dtl-open-bg */\n.dtl-row-head{display:flex;align-items:center;gap:var(--dtl-space-2);\n width:100%;padding:var(--dtl-space-2) var(--dtl-space-3);border:0;background:transparent;\n color:inherit;font:inherit;text-align:left;cursor:pointer;\n transition:background-color var(--dtl-dur-fast) var(--dtl-ease);}\n.dtl-row-head:hover{background:var(--dtl-card-hover);}\n.dtl-row-head:focus-visible{outline:2px solid var(--dtl-business);outline-offset:-2px;}\n.dtl-row[data-open=\"true\"]{background:var(--dtl-open-bg);}\n.dtl-row-body{padding:var(--dtl-space-2) var(--dtl-space-3) var(--dtl-space-3);\n background:var(--dtl-open-bg-soft);font-size:var(--dtl-font-md);}\n\n/* 空态:真空 = 虚线引导卡;筛选空 = 小字 */\n.dtl-empty-guide{padding:var(--dtl-space-4);border:1px dashed var(--dtl-border-strong);border-radius:var(--dtl-radius-md);background:transparent;}\n.dtl-empty-filtered{padding:var(--dtl-space-4) 0;color:var(--dtl-fg-3);font-size:var(--dtl-font-md)}\n\n/* 浮标角标:0→n 跳变时一次脉冲,不自动消失 */\n.dtl-badge{position:absolute;top:-2px;right:-2px;min-width:16px;height:16px;padding:0 4px;\n border-radius:8px;background:var(--dtl-danger);color:var(--dtl-on-signal);\n font-size:10px;line-height:16px;text-align:center;font-weight:600;}\n.dtl-badge[data-pulse=\"true\"]{animation:dtl-pulse 900ms var(--dtl-ease) 1}\n@keyframes dtl-pulse{0%{transform:scale(.6);opacity:.4}60%{transform:scale(1.15)}100%{transform:scale(1)}}\n\n/* 统计区整体:左 8 块(2 行 × 4 列)+ 右趋势图,参考图同款分栏。\n ⚠️ 列数写死 4 → 2,绝不给 3:3 列会排出 3+1 的非对称(用户 2026-10-17)。\n 两栏**完全等高**:cards 两行 minmax(0,1fr) 均分 + 卡 height:100% ⇒ 图表卡与左边一样高。 */\n.dtl-stats{display:grid;grid-template-columns:minmax(0,2fr) minmax(0,1fr);gap:var(--dtl-space-3);align-items:stretch;}\n.dtl-stats__cards{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));grid-template-rows:repeat(2,minmax(0,1fr));\n gap:var(--dtl-space-3);}\n.dtl-stats__cards > .dtl-stat{height:100%;box-sizing:border-box;}\n@media (max-width:960px){.dtl-stats{grid-template-columns:minmax(0,1fr);}}\n@media (max-width:640px){.dtl-stats__cards{grid-template-columns:repeat(2,minmax(0,1fr));grid-template-rows:repeat(4,minmax(0,1fr));}}\n\n/* 「只看我的」小复选框(替掉右上角的分段切换) */\n.dtl-check{display:inline-flex;align-items:center;gap:5px;height:20px;margin-top:-2px;\n font-size:var(--dtl-font-xs);color:var(--dtl-fg-3);cursor:pointer;user-select:none;white-space:nowrap;}\n.dtl-check input{width:12px;height:12px;margin:0;accent-color:var(--dtl-business);cursor:pointer;}\n.dtl-check:hover{color:var(--dtl-fg-2);}\n\n/* 趋势分组柱状图:绘图区高度**随卡拉伸**(flex),柱 / 刻度 / 网格全用百分比 ⇒ 与卡等高 */\n.dtl-gchart-wrap{flex:1 1 auto;min-height:0;display:flex;flex-direction:column;padding-left:30px;padding-bottom:18px;}\n.dtl-gchart{position:relative;flex:1 1 auto;min-height:96px;border-bottom:1px solid var(--dtl-border);}\n.dtl-gchart__grid{position:absolute;left:0;right:0;border-top:1px solid var(--dtl-border-faint);pointer-events:none;}\n.dtl-gchart__ytick{position:absolute;left:-30px;width:26px;text-align:right;font-size:10px;line-height:12px;\n color:var(--dtl-fg-3);transform:translateY(50%);font-variant-numeric:tabular-nums;}\n.dtl-gchart__col{position:absolute;top:0;bottom:0;min-width:0;}\n/* ⚠️ pair 必须 top/bottom 双锚(或 height:100%):绝对定位 + 只给 bottom 时高度是 auto,\n 子元素的 height:% 没有可解析的包含块 ⇒ 整根柱子塌成 0(2026-10-17 真机教训:\n 改百分比柱高后「一个柱子都看不见,只剩数字」)。 */\n.dtl-gchart__pair{position:absolute;top:0;bottom:0;left:50%;transform:translateX(-50%);\n display:flex;align-items:flex-end;gap:2px;}\n.dtl-gchart__bar{width:10px;min-height:0;border-radius:var(--dtl-radius-xs) var(--dtl-radius-xs) 0 0;}\n.dtl-gchart__bar--done{background:var(--dtl-business);}\n.dtl-gchart__bar--created{background:var(--dtl-chart-created);}\n.dtl-gchart__col:hover .dtl-gchart__bar--done{background:var(--dtl-accent);}\n.dtl-gchart__val{position:absolute;left:50%;transform:translateX(-50%);font-size:10px;line-height:12px;\n color:var(--dtl-fg-2);font-variant-numeric:tabular-nums;white-space:nowrap;}\n.dtl-gchart__label{position:absolute;left:0;right:0;bottom:-16px;text-align:center;\n font-size:10px;line-height:14px;color:var(--dtl-fg-3);white-space:nowrap;}\n/* hover 浮卡(参考图同款:日期 + 两个数;原生 title 太慢、长相不一) */\n.dtl-gchart__tip{position:absolute;top:0;z-index:2;min-width:104px;box-sizing:border-box;\n padding:6px 8px;border:1px solid var(--dtl-border);border-radius:var(--dtl-radius-sm);\n background:var(--dtl-surface-1);box-shadow:var(--dtl-shadow-2);\n font-size:var(--dtl-font-xs);line-height:var(--dtl-line-sm);color:var(--dtl-fg-2);pointer-events:none;}\n.dtl-gchart__tip b{display:block;color:var(--dtl-fg);font-weight:600;}\n.dtl-gchart__tip i{display:inline-block;width:8px;height:8px;border-radius:2px;margin-right:4px;}\n.dtl-gchart-legend{display:flex;align-items:center;gap:var(--dtl-space-3);font-size:var(--dtl-font-xs);color:var(--dtl-fg-2);}\n.dtl-gchart-legend i{display:inline-block;width:8px;height:8px;border-radius:2px;margin-right:4px;}\n\n/* 过程正文:宽度受限、长行折行 */\n.dtl-worklog{max-width:72ch;font-size:var(--dtl-font-md);line-height:1.6}\n`\n\nconst STYLE_ID = 'dtl-ui-base'\n\n/** 幂等注入 token 层 + 控件皮肤 + 基础层(面板与浮标谁先挂载都只注入一次)。 */\nexport function ensureUiBase(): void {\n if (typeof document === 'undefined') return\n if (document.getElementById(STYLE_ID) !== null) return\n const style = document.createElement('style')\n style.id = STYLE_ID\n style.textContent = `${UI_TOKENS_CSS}\\n${UI_CONTROLS_CSS}\\n${UI_BASE_CSS}`\n document.head.appendChild(style)\n}\n\n/* ── 分段控件 ─────────────────────────────────────────────────────── */\n\n/** 一段(一个可选值)。 */\nexport interface SegmentedItem<Value extends string = string> {\n readonly value: Value\n readonly label: string\n /** 段内角标数字;`undefined` 或 ≤0 不显示。 */\n readonly badge?: number\n}\n\n/** 分段控件(单选):同一份结构 + size 轴;外观差异只由 variant 的两个颜色变量表达。 */\nexport function Segmented<Value extends string>(props: {\n items: readonly SegmentedItem<Value>[]\n value: Value\n onChange: (next: Value) => void\n size?: 'sm' | 'md' | 'lg'\n variant?: 'default' | 'inset'\n label: string\n id?: string\n style?: CSSProperties\n}): ReactNode {\n ensureUiBase()\n const size = props.size ?? 'md'\n const variant = props.variant ?? 'default'\n return h('div', {\n role: 'group',\n 'aria-label': props.label,\n id: props.id,\n className: `dtl-seg dtl-seg--${variant} dtl-seg--${size}`,\n style: props.style,\n }, props.items.map(item => h('button', {\n key: item.value,\n type: 'button',\n className: 'dtl-seg__item',\n 'aria-pressed': item.value === props.value,\n onClick: () => { props.onChange(item.value) },\n }, item.label, item.badge !== undefined && item.badge > 0\n ? h('span', { className: 'dtl-seg__badge' }, String(item.badge))\n : null)))\n}\n\n/* ── 按钮 / 图标钮 ────────────────────────────────────────────────── */\n\nexport function Button(props: {\n children?: ReactNode\n onClick: () => void\n variant?: 'primary' | 'outline' | 'ghost'\n size?: 'sm' | 'md' | 'lg'\n icon?: ReactNode\n disabled?: boolean\n title?: string\n}): ReactNode {\n ensureUiBase()\n return h('button', {\n type: 'button',\n className: `dtl-btn dtl-btn--${props.variant ?? 'outline'} dtl-btn--${props.size ?? 'md'}`,\n disabled: props.disabled === true,\n title: props.title,\n onClick: props.onClick,\n }, props.icon, props.children)\n}\n\nexport function IconButton(props: {\n icon: ReactNode\n label: string\n onClick: () => void\n variant?: 'plain' | 'outline'\n size?: 'sm' | 'md' | 'lg'\n disabled?: boolean\n title?: string\n}): ReactNode {\n ensureUiBase()\n return h('button', {\n type: 'button',\n className: `dtl-iconbtn dtl-iconbtn--${props.variant ?? 'plain'} dtl-iconbtn--${props.size ?? 'md'}`,\n 'aria-label': props.label,\n disabled: props.disabled === true,\n title: props.title ?? props.label,\n onClick: props.onClick,\n }, props.icon)\n}\n\n/* ── 输入 / 下拉 / 搜索 ───────────────────────────────────────────── */\n\n/** 原生下拉(工作区 / 状态共用一份;宿主没有廉价下拉件 ⇒ 原生 + token 皮肤)。 */\nexport function Select<Value extends string>(props: {\n value: Value\n options: readonly { value: Value; label: string }[]\n onChange: (next: Value) => void\n ariaLabel: string\n /** 定宽(px);不给就按内容。 */\n width?: number\n size?: 'sm' | 'md' | 'lg'\n}): ReactNode {\n ensureUiBase()\n const size = props.size ?? 'md'\n return h('select', {\n className: `dtl-select dtl-select--${size}`,\n 'aria-label': props.ariaLabel,\n style: props.width === undefined ? undefined : { width: `${props.width}px` },\n value: props.value,\n onChange: (event: ChangeEvent<HTMLSelectElement>) => { props.onChange(event.target.value as Value) },\n }, props.options.map(option => h('option', { key: option.value, value: option.value }, option.label)))\n}\n\n/** 搜索框(关键字过滤;占满剩余宽度由调用方的 flex 决定)。 */\nexport function SearchInput(props: {\n value: string\n placeholder: string\n onChange: (next: string) => void\n ariaLabel: string\n size?: 'sm' | 'md' | 'lg'\n}): ReactNode {\n ensureUiBase()\n const size = props.size ?? 'md'\n return h('input', {\n className: `dtl-input dtl-input--${size}`,\n type: 'search',\n 'aria-label': props.ariaLabel,\n placeholder: props.placeholder,\n value: props.value,\n spellCheck: false,\n onChange: (event: ChangeEvent<HTMLInputElement>) => { props.onChange(event.target.value) },\n })\n}\n\n/* ── 卡 / chip / 空态 ─────────────────────────────────────────────── */\n\n/** 统计卡:主数字 + 灰色口径小字;点一下联动下方列表筛选。\n * `value` 为 0 时显示「—」(用户 2026-10-17 定稿:0 会被读成「没数据 / 出错了」);\n * `hint` 可以是富文本(如把「停滞」两字标红);\n * `interactive: false` = 纯展示卡(统计台:首页没有已结案可筛,不装可点)。 */\nexport function StatCard(props: {\n label: string\n value: string | number\n hint?: ReactNode\n active?: boolean\n interactive?: boolean\n title?: string\n onClick?: () => void\n}): ReactNode {\n ensureUiBase()\n const interactive = props.interactive !== false && props.onClick !== undefined\n const shown = props.value === 0 || props.value === '0' ? '—' : String(props.value)\n return h(interactive ? 'button' : 'div', {\n type: interactive ? 'button' : undefined,\n className: `dtl-card dtl-stat${interactive ? ' dtl-card--clickable' : ''}`,\n 'data-active': props.active === true,\n title: props.title,\n onClick: interactive ? props.onClick : undefined,\n },\n h('span', { style: { fontSize: 'var(--dtl-font-sm)', color: 'var(--dtl-fg-2)' } }, props.label),\n h('span', { className: 'dtl-stat__value' }, shown),\n props.hint === undefined || props.hint === null || props.hint === ''\n ? null\n : h('span', { className: 'dtl-stat__hint' }, props.hint),\n )\n}\n\n/** chip(分类 / 标签 / 工作区名)。 */\nexport function Chip(props: { text: string; color?: string }): ReactNode {\n ensureUiBase()\n return h('span', {\n className: 'dtl-chip',\n style: props.color === undefined ? undefined : { background: props.color, color: 'var(--dtl-on-signal)' },\n title: props.text,\n }, props.text)\n}\n\n/** 空态:`guide` = 真空引导卡(虚线),`filtered` = 筛选空小字。 */\nexport function EmptyState(props: { tone: 'guide' | 'filtered'; title: string; hint?: string }): ReactNode {\n ensureUiBase()\n if (props.tone === 'filtered') return h('div', { className: 'dtl-empty-filtered' }, props.title)\n return h('div', { className: 'dtl-empty-guide' },\n h('div', { style: { fontSize: 'var(--dtl-font-lg)', fontWeight: 600, marginBottom: 'var(--dtl-space-1)' } }, props.title),\n props.hint === undefined ? null : h('div', { style: { fontSize: 'var(--dtl-font-md)', color: 'var(--dtl-fg-2)' } }, props.hint),\n )\n}\n\n/** 台账图标(黑白线条,描边走 currentColor ⇒ 随主题 / 选中态反色)。\n * 侧栏条目与右上浮标共用这一份(图标只画一次,别处不许再画第二个)。 */\nexport function LedgerIcon(props: { size?: number }): ReactNode {\n const size = props.size ?? 18\n const stroke = { fill: 'none', stroke: 'currentColor', strokeWidth: 7, strokeLinecap: 'round', strokeLinejoin: 'round' } as const\n return h('svg', { width: size, height: size, viewBox: '0 0 128 128', 'aria-hidden': true },\n h('path', { d: 'M96 34 A10 10 0 0 1 106 44 V102 A10 10 0 0 1 96 112 H32 A10 10 0 0 1 22 102 V44 A10 10 0 0 1 32 34 Z', ...stroke }),\n h('path', { d: 'M50 22 V46 M78 22 V46', ...stroke }),\n h('path', { d: 'M44 66 H84 M44 86 H70', ...stroke }),\n )\n}\n\n/* ── 趋势分组柱状图 ───────────────────────────────────────────────────── */\n\n/** 一周两根柱(完成 / 新增)。 */\nexport interface GroupedBarWeek {\n /** 轴标签(MM-DD)。 */\n readonly label: string\n readonly done: number\n readonly created: number\n /** 悬停文案。 */\n readonly title: string\n}\n\n/** 千位压缩(1.2k / 12k):y 轴刻度与柱顶数值用,避免 4 位数把刻度挤宽。 */\nfunction compactCount(value: number): string {\n if (value >= 10_000) return `${Math.round(value / 1000)}k`\n if (value >= 1000) return `${(value / 1000).toFixed(1)}k`\n return String(value)\n}\n\n/** 分组柱状图(div 柱,无新依赖):完成(蓝)/ 新增(灰)各一细柱。\n * - 高度全用百分比 ⇒ 绘图区随卡片拉伸,图表卡与左边 8 块**完全等高**;\n * - y 轴三档刻度 + 淡网格线;柱顶只标「完成」(16 个数值标签会糊成一片);\n * - hover 出浮卡(日期 + 两个数)——参考图同款,原生 title 太慢;\n * - **没有**均值线与长注释(用户 2026-10-17:均值不要、注释太长不要)。 */\nexport function GroupedBarChart(props: {\n weeks: readonly GroupedBarWeek[]\n ariaLabel: string\n doneLabel: string\n createdLabel: string\n}): ReactNode {\n ensureUiBase()\n const [hover, setHover] = useState<number | null>(null)\n const top = Math.max(1, props.weeks.reduce((peak, week) => Math.max(peak, week.done, week.created), 0))\n const ticks = [0, top / 2, top]\n const pct = (value: number): string => `${(value / top) * 100}%`\n const columnStyle = (index: number): Record<string, string | number> => ({\n left: `${(index / props.weeks.length) * 100}%`,\n right: `${((props.weeks.length - index - 1) / props.weeks.length) * 100}%`,\n })\n // 浮卡定位:贴边两列向内收,其余居中(否则溢出图表卡)\n const tipStyle = (index: number): Record<string, string | number> => {\n if (index <= 1) return { left: 0 }\n if (index >= props.weeks.length - 2) return { right: 0 }\n return { left: `${((index + 0.5) / props.weeks.length) * 100}%`, transform: 'translateX(-50%)' }\n }\n return h('div', { className: 'dtl-gchart-wrap' },\n h('div', {\n className: 'dtl-gchart',\n role: 'img',\n 'aria-label': props.ariaLabel,\n onMouseLeave: () => { setHover(null) },\n },\n ticks.map((tick) => h('div', { key: `grid-${String(tick)}`, className: 'dtl-gchart__grid', style: { bottom: pct(tick) } })),\n ticks.map((tick) => h('span', { key: `tick-${String(tick)}`, className: 'dtl-gchart__ytick', style: { bottom: pct(tick) } }, compactCount(Math.round(tick)))),\n props.weeks.map((week, index) => h('div', {\n key: week.label,\n className: 'dtl-gchart__col',\n style: columnStyle(index),\n onMouseEnter: () => { setHover(index) },\n },\n h('div', { className: 'dtl-gchart__pair' },\n h('span', { className: 'dtl-gchart__bar dtl-gchart__bar--done', style: { height: pct(week.done), minHeight: week.done > 0 ? '2px' : undefined } }),\n h('span', { className: 'dtl-gchart__bar dtl-gchart__bar--created', style: { height: pct(week.created), minHeight: week.created > 0 ? '2px' : undefined } }),\n ),\n week.done === 0\n ? null\n : h('span', { className: 'dtl-gchart__val', style: { bottom: `calc(${pct(week.done)} + 3px)` } }, String(week.done)),\n h('span', { className: 'dtl-gchart__label' }, week.label),\n )),\n hover === null\n ? null\n : h('div', { className: 'dtl-gchart__tip', style: tipStyle(hover) },\n h('b', null, props.weeks[hover].title),\n h('div', null, h('i', { style: { background: 'var(--dtl-business)' } }), `${props.doneLabel} ${props.weeks[hover].done}`),\n h('div', null, h('i', { style: { background: 'var(--dtl-chart-created)' } }), `${props.createdLabel} ${props.weeks[hover].created}`),\n ),\n ),\n )\n}\n\n/** 图例(完成 / 新增 两个色块)。 */\nexport function ChartLegend(props: { doneLabel: string; createdLabel: string }): ReactNode {\n ensureUiBase()\n return h('div', { className: 'dtl-gchart-legend' },\n h('span', null, h('i', { style: { background: 'var(--dtl-business)' } }), props.doneLabel),\n h('span', null, h('i', { style: { background: 'var(--dtl-chart-created)' } }), props.createdLabel),\n )\n}\n","/**\n * 任务列表行(宿主**无表格件** ⇒ 自绘,几何参照官方 `DisclosureRow`,状态走官方 `StateDot`)。\n * 定稿见 docs/design/features/board-home.md §二 与 docs/design/ui-style.md:\n * 状态语义 open=idle / doing=ongoing / blocked=warning / 已结案=done。\n *\n * 行展开 = 过程文档路径 + 过程正文(在办走 `GET /task` 取正文;已结案只有路径——\n * host 没有按历史行取正文的路由,不编造内容)。\n *\n * `showWorkspace`:第一区「我的待办」是跨工作区平铺 ⇒ 工作区升为一列(定宽,不再是\n * 和标签混在一起的小 chip);第二区分组行由分组标题说明归属,行内不再重复。\n */\nimport { createElement as h, useEffect, useMemo, useState, type ReactNode } from 'react'\nimport {\n IconChevronDownOutlineRegular, IconChevronRightOutlineRegular,\n MarkdownText, StateDot,\n} from '@deepseek-ai/dsh-client-ui-primitives'\nimport { fetchTaskDetail } from './api'\nimport { formatDateTime } from './format'\nimport type { Translate } from './locales'\nimport type { Row } from './rows'\nimport { Chip } from './ui'\nimport type { TaskDetail, Workspace } from './types'\n\n/** 行的状态点语义(在办三态 + 已结案)。 */\nfunction dotOf(row: Row): 'idle' | 'ongoing' | 'warning' | 'done' {\n if (row.kind === 'closed') return 'done'\n if (row.task.status === 'blocked') return 'warning'\n if (row.task.status === 'doing') return 'ongoing'\n return 'idle'\n}\n\n/** 行标题:在办 = 任务名(缺则任务号);已结案 = 任务包名(缺则「完成了什么」首句)。 */\nfunction titleOf(row: Row): string {\n if (row.kind === 'task') return row.task.title ?? row.task.id\n const title = row.record.workpackage ?? row.record.summary\n return title !== undefined && title !== '' ? title : row.record.src ?? '—'\n}\n\n/** 行的「谁」:负责人优先,没有就创建人,都没有 = 未认领。 */\nfunction whoOf(row: Row): string | null {\n if (row.kind === 'task') return row.task.owner ?? row.task.creator ?? null\n return row.record.owner ?? null\n}\n\n/** 工作区徽标(第一区专用列):色块首字母 + 标题,定宽、超出省略(不猜官方图标名)。 */\nfunction WorkspaceTag(props: { workspace: Workspace | undefined }): ReactNode {\n if (props.workspace === undefined) return null\n const initial = props.workspace.title.trim().slice(0, 1)\n return h('span', {\n className: 'dtl-ellipsis',\n style: { display: 'inline-flex', alignItems: 'center', gap: 'var(--dtl-space-1)', flex: '0 0 140px', minWidth: 0, color: 'var(--dtl-fg-2)', fontSize: 'var(--dtl-font-xs)' },\n title: props.workspace.title,\n },\n h('span', {\n style: {\n display: 'inline-flex', alignItems: 'center', justifyContent: 'center', flex: 'none',\n width: '16px', height: '16px', borderRadius: 'var(--dtl-radius-xs)',\n background: 'var(--dtl-business-soft)', color: 'var(--dtl-business)', fontSize: '10px', fontWeight: 600,\n },\n }, initial === '' ? '·' : initial),\n props.workspace.title,\n )\n}\n\nexport function TaskRowView(props: { t: Translate; row: Row; showWorkspace?: boolean }): ReactNode {\n const { t, row } = props\n const [open, setOpen] = useState(false)\n\n // 官方 MarkdownText 要求 labels 引用稳定(新身份会丢弃渲染缓存)⇒ 按 t 记忆一次。\n const mdLabels = useMemo(() => ({ code: { copyLabel: t('mdCopy'), copiedLabel: t('mdCopied') }, footnotes: t('mdFootnotes') }), [t])\n\n /** 行的时间:在办 doing/blocked 看开始、其余看创建;已结案看结案。 */\n const stamp = row.kind === 'closed'\n ? { value: row.record.time ?? row.record.created ?? null, label: t('fieldFinished') }\n : row.task.status === 'open'\n ? { value: row.task.created ?? null, label: t('fieldCreated') }\n : { value: row.task.started ?? row.task.created ?? null, label: t('fieldStarted') }\n const when = stamp.value === null || stamp.value === undefined || stamp.value === ''\n ? t('timeUnknown')\n : formatDateTime(stamp.value, { fallback: t('timeUnknown') })\n const who = whoOf(row)\n const workspace = row.kind === 'task' ? row.task.workspace : row.record.workspace\n const category = row.kind === 'task' ? row.task.category : row.record.category\n const tags = (row.kind === 'task' ? row.task.tags : row.record.tags) ?? []\n const logPath = row.kind === 'task' ? row.task.worklog : row.record.log\n\n return h('div', { className: 'dtl-row', 'data-open': open },\n h('button', {\n type: 'button',\n className: 'dtl-row-head',\n 'aria-expanded': open,\n title: open ? t('collapseAria') : t('expandAria'),\n onClick: () => { setOpen(!open) },\n },\n h(StateDot, { state: dotOf(row) }),\n props.showWorkspace === true ? h(WorkspaceTag, { workspace }) : null,\n h('span', {\n className: 'dtl-ellipsis',\n style: { flex: '1 1 auto', minWidth: 0, fontSize: 'var(--dtl-font-md)' },\n title: titleOf(row),\n }, titleOf(row)),\n category === null || category === undefined || category === ''\n ? null\n : h(Chip, { text: category }),\n tags.slice(0, 2).map(tag => h(Chip, { key: tag, text: `#${tag}` })),\n h('span', {\n style: {\n display: 'flex', alignItems: 'center', gap: 'var(--dtl-space-2)', flex: 'none',\n color: 'var(--dtl-fg-3)', fontSize: 'var(--dtl-font-xs)', whiteSpace: 'nowrap',\n },\n },\n h('span', { title: stamp.value ?? undefined }, `${stamp.label} ${when}`),\n h('span', null, who === null || who === '' ? t('ownerUnclaimed') : `@${who}`),\n ),\n h(open ? IconChevronDownOutlineRegular : IconChevronRightOutlineRegular, { size: 16 }),\n ),\n open\n ? h('div', { className: 'dtl-row-body' },\n // 过程文档路径(真实值;没有工作区文件服务可点开 ⇒ 只展示路径,不假装能跳转)\n h('div', { style: { margin: 'var(--dtl-space-2) 0', fontSize: 'var(--dtl-font-sm)', color: 'var(--dtl-fg-2)' } },\n `${t('worklogTitle')}·${t('worklogPath')}:${\n logPath === null || logPath === undefined || logPath === '' ? t('worklogMissing') : logPath\n }`,\n ),\n row.kind === 'closed'\n ? h('div', { style: { fontSize: 'var(--dtl-font-md)', color: 'var(--dtl-fg-2)' } }, row.record.summary ?? '')\n : h(WorklogBody, { t, workspaceId: row.task.workspace.id, taskId: row.task.id, labels: mdLabels }),\n )\n : null,\n )\n}\n\n/** 在办行的过程正文:**展开才取**(首屏不为正文付代价),且只在挂载时取一次(不随重渲染重取)。 */\nfunction WorklogBody(props: {\n t: Translate\n workspaceId: string\n taskId: string\n labels: { code: { copyLabel: string; copiedLabel: string }; footnotes: string }\n}): ReactNode {\n const { t, workspaceId, taskId, labels } = props\n const [detail, setDetail] = useState<TaskDetail | null>(null)\n const [failed, setFailed] = useState<string | null>(null)\n useEffect(() => {\n let alive = true\n fetchTaskDetail(workspaceId, taskId)\n .then((body) => { if (alive) setDetail(body) })\n .catch((error: unknown) => {\n if (alive) setFailed(error instanceof Error ? error.message : String(error))\n })\n return () => { alive = false }\n }, [workspaceId, taskId])\n\n if (failed !== null) {\n return h('div', { style: { color: 'var(--dtl-danger)', fontSize: 'var(--dtl-font-sm)' } }, `${t('loadFailed')}:${failed}`)\n }\n if (detail === null) return h('div', { style: { color: 'var(--dtl-fg-3)', fontSize: 'var(--dtl-font-sm)' } }, t('loading'))\n if (detail.worklog === null) {\n return h('div', { style: { color: 'var(--dtl-fg-3)', fontSize: 'var(--dtl-font-sm)' } }, t('worklogMissing'))\n }\n return h('div', { className: 'dtl-worklog' }, h(MarkdownText, { text: detail.worklog, labels, variant: 'compact' }))\n}\n","/**\n * 看板首页(`main` 槽整页内容):6 统计卡 + 近 8 周趋势图 + 两区任务列表。\n * 定稿见 docs/design/features/board-home.md §二(2026-10-17 专家组改版);\n * 按钮矩阵归「任务按钮与会话交接」任务(写入路径 A/B 未拍板),本页**不画**任务动作按钮。\n *\n * 刷新纪律(2026-10-17 拍板):挂载首读 + 手动刷新按钮,**不轮询**;空态三分\n * (未就绪不渲染 / 真空引导卡 / 筛选空小字)。\n */\nimport { createElement as h, useEffect, useMemo, useState, useSyncExternalStore, type ChangeEvent, type ReactNode } from 'react'\nimport { IconRefreshOutlineRegular } from '@deepseek-ai/dsh-client-ui-primitives'\nimport type { LocaleKey, Translate } from './locales'\nimport { boardSnapshot, boardSubscribe, refresh, setQuery, type Query, type Scope, type StatusFilter } from './store'\nimport { selectView, type WorkspaceGroup } from './rows'\nimport type { PeriodCounts } from './types'\nimport { TaskRowView } from './task-row'\nimport {\n Button, ChartLegend, EmptyState, ensureUiBase, GroupedBarChart, SearchInput, Segmented, Select, StatCard,\n} from './ui'\n\ntype Tab = 'tasks' | 'stats'\n\n/** 整页容器:占满中栏、自己滚动。 */\nconst pageStyle: Record<string, string | number> = {\n height: '100%', width: '100%', boxSizing: 'border-box', overflow: 'auto',\n padding: '24px 22px', color: 'var(--dtl-fg)',\n}\nconst headerStyle: Record<string, string | number> = {\n display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 'var(--dtl-space-3)', flexWrap: 'wrap',\n marginBottom: 'var(--dtl-space-4)',\n}\nconst titleStyle: Record<string, string | number> = { fontSize: 'var(--dtl-font-xl)', fontWeight: 600 }\n/** 统计区:左 8 块(2 行 × 4 列)+ 右趋势图,参考图同款分栏。\n * 列数写死 4 → 2(媒体查询在 ui.ts 的 .dtl-stats / .dtl-stats__cards 里),\n * 绝不给 3 列——3 列会排出 3+1 的非对称(用户 2026-10-17:必须上下对称)。 */\nconst statsRowStyle: Record<string, string | number> = { marginBottom: 'var(--dtl-space-4)' }\nconst chartCardStyle: Record<string, string | number> = {\n display: 'flex', flexDirection: 'column', gap: 'var(--dtl-space-2)', padding: 'var(--dtl-space-2) var(--dtl-space-3)',\n}\nconst filterRowStyle: Record<string, string | number> = {\n display: 'flex', alignItems: 'center', gap: 'var(--dtl-space-2)', flexWrap: 'wrap',\n marginBottom: 'var(--dtl-space-3)',\n}\nconst searchWrapStyle: Record<string, string | number> = { flex: '1 1 220px', minWidth: 0 }\nconst hintStyle: Record<string, string | number> = { fontSize: 'var(--dtl-font-sm)', color: 'var(--dtl-fg-2)' }\nconst regionHeadStyle: Record<string, string | number> = {\n display: 'flex', alignItems: 'baseline', gap: 'var(--dtl-space-2)',\n margin: 'var(--dtl-space-4) 0 var(--dtl-space-2)', fontSize: 'var(--dtl-font-lg)', fontWeight: 600,\n}\nconst groupHeadStyle: Record<string, string | number> = {\n display: 'flex', alignItems: 'center', gap: 'var(--dtl-space-2)', width: '100%',\n padding: 'var(--dtl-space-2) var(--dtl-space-3)', border: 0, borderRadius: 'var(--dtl-radius-md)',\n background: 'var(--dtl-surface-2)', color: 'inherit', font: 'inherit', textAlign: 'left', cursor: 'pointer',\n}\nconst groupCountStyle: Record<string, string | number> = { fontSize: 'var(--dtl-font-xs)', color: 'var(--dtl-fg-3)', fontWeight: 400 }\n\n/** 分组默认折叠阈值(组内太多条时先收起,用户点开)。 */\nconst GROUP_COLLAPSE_OVER = 30\n\nexport function BoardHome(props: { t: Translate; onBack: () => void }): ReactNode {\n const { t, onBack } = props\n ensureUiBase()\n const state = useSyncExternalStore(boardSubscribe, boardSnapshot)\n const [tab, setTab] = useState<Tab>('tasks')\n\n // 打开面板首读(后端纯按需,无推送无轮询)。\n useEffect(() => { void refresh() }, [])\n\n const board = state.board\n const view = useMemo(() => selectView(state), [state])\n const stats = board?.stats\n const days = (value: number | null | undefined): string =>\n value === null || value === undefined ? t('daysNone') : t('daysValue', { n: value })\n\n /** 统计卡 → 筛选联动(卡即入口:点一下跳到对应视角)。 */\n const card = (label: LocaleKey, value: number, hint: string, patch: Partial<Query>): ReactNode =>\n h(StatCard, {\n label: t(label),\n value,\n hint,\n active: isActive(state.query, patch),\n onClick: () => { setQuery(patch) },\n })\n\n /** 统计台卡:不可点(首页没有已结案可筛),hover 给具体日期区间。 */\n const periodCard = (label: LocaleKey, period: PeriodCounts | undefined): ReactNode =>\n h(StatCard, {\n label: t(label),\n value: period?.done ?? 0,\n hint: t('statCreatedHint', { n: period?.created ?? 0 }),\n interactive: false,\n title: period === undefined ? undefined : t('periodRange', { from: period.from, to: period.to }),\n })\n\n const [chartScope, setChartScope] = useState<'all' | 'mine'>('all')\n const chartWeeks = useMemo(() => (board === null ? [] : board.closed.weekly[chartScope]).map((bucket) => ({\n label: bucket.week.slice(5),\n done: bucket.done,\n created: bucket.created,\n title: `${bucket.week}|${t('chartLegendDone')} ${bucket.done}|${t('chartLegendCreated')} ${bucket.created}`,\n })), [board, chartScope, t])\n const chartEmpty = chartWeeks.every((week) => week.done === 0 && week.created === 0)\n\n return h('div', { style: pageStyle },\n h('div', { style: { display: 'flex', justifyContent: 'center' } },\n h('div', { style: { width: '100%', maxWidth: '1280px', minWidth: 0, boxSizing: 'border-box' } },\n h('div', { style: headerStyle },\n h('div', { style: { display: 'flex', alignItems: 'center', gap: 'var(--dtl-space-3)', minWidth: 0 } },\n h(Button, { variant: 'outline', size: 'md', onClick: onBack }, `← ${t('backToConversation')}`),\n h('div', { style: titleStyle }, t('panelTitle')),\n ),\n h('div', { style: { display: 'flex', alignItems: 'center', gap: 'var(--dtl-space-2)' } },\n h(Segmented<Tab>, {\n id: 'dtl-tab',\n value: tab,\n label: t('panelTitle'),\n items: [\n { value: 'tasks', label: t('tabTasks') },\n { value: 'stats', label: t('tabStats') },\n ],\n onChange: setTab,\n }),\n h(Button, {\n variant: 'outline',\n size: 'md',\n icon: h(IconRefreshOutlineRegular, { size: 16 }),\n disabled: state.refreshing,\n title: t('refresh'),\n onClick: () => { void refresh() },\n }, state.refreshing ? t('refreshing') : t('refresh')),\n ),\n ),\n\n tab === 'stats'\n // 统计报表尚未开建(另立任务):占位说明,不画假图表。\n ? h('div', { style: hintStyle }, t('statsPlaceholder'))\n : h('div', null,\n // ── 统计区:左 8 块(操作台 4 + 统计台 4)+ 右趋势图 ──\n stats === undefined\n ? null\n : h('div', { className: 'dtl-stats', style: statsRowStyle },\n h('div', { className: 'dtl-stats__cards' },\n // 第一行 · 操作台(可点联动筛选)\n card('statMyActive', stats.myActive, t('statMyActiveHint', { n: stats.myBlocked }), { scope: 'mine', status: 'all', workspace: '' }),\n card('statOpen', stats.open, t('statOpenHint', { n: days(stats.oldestOpenDays) }), { scope: 'all', status: 'open', workspace: '' }),\n card('statDoing', stats.doing, t('statDoingHint', { n: stats.doing - stats.myActive + stats.myBlocked }), { scope: 'all', status: 'active', workspace: '' }),\n // 阻塞 · 停滞合并卡:主数字 = 阻塞数;有停滞时小字把「停滞」标红\n h(StatCard, {\n label: t('statBlockedStale'),\n value: stats.blocked,\n hint: stats.stale === 0\n ? undefined\n : h('span', null,\n h('span', { style: { color: 'var(--dtl-danger)', fontWeight: 600 } }, t('statStaleWord')),\n ` ${t('statStaleHint', { n: stats.stale })}`,\n ),\n active: isActive(state.query, { scope: 'all', status: 'blocked', workspace: '' }),\n title: t('statStaleTitle', { n: stats.staleDays }),\n onClick: () => { setQuery({ scope: 'all', status: 'blocked', workspace: '' }) },\n }),\n // 第二行 · 统计台(自然周期,不可点)\n periodCard('statWeekDone', board?.trend.thisWeek),\n periodCard('statLastWeekDone', board?.trend.lastWeek),\n periodCard('statMonthDone', board?.trend.thisMonth),\n periodCard('statLastMonthDone', board?.trend.lastMonth),\n ),\n h('div', { className: 'dtl-card', style: chartCardStyle },\n // 右上角只留一个「只看我的」小复选框:勾上 = 我的,不勾 = 所有(用户 2026-10-17)\n h('div', { style: { display: 'flex', alignItems: 'center', justifyContent: 'flex-end' } },\n h('label', { className: 'dtl-check' },\n h('input', {\n type: 'checkbox',\n checked: chartScope === 'mine',\n onChange: (event: ChangeEvent<HTMLInputElement>) => { setChartScope(event.target.checked ? 'mine' : 'all') },\n }),\n t('chartOnlyMine'),\n ),\n ),\n chartEmpty\n ? h('div', { style: { ...hintStyle, padding: 'var(--dtl-space-4) 0' } }, t('chartEmpty'))\n : h(GroupedBarChart, {\n weeks: chartWeeks,\n ariaLabel: t('chartTitle'),\n doneLabel: t('chartLegendDone'),\n createdLabel: t('chartLegendCreated'),\n }),\n h(ChartLegend, { doneLabel: t('chartLegendDone'), createdLabel: t('chartLegendCreated') }),\n ),\n ),\n\n // ── 筛选行(控件全同高 28px)──\n h('div', { style: filterRowStyle },\n h(Segmented<Scope>, {\n id: 'dtl-scope',\n value: state.query.scope,\n label: t('scopeLabel'),\n items: [\n { value: 'mine', label: t('scopeMine') },\n { value: 'all', label: t('scopeAll') },\n ],\n onChange: (next) => { setQuery({ scope: next }) },\n }),\n h(Segmented<StatusFilter>, {\n id: 'dtl-status',\n value: state.query.status,\n label: t('statusLabel'),\n items: [\n { value: 'all', label: t('statusAll') },\n { value: 'active', label: t('statusActive') },\n { value: 'open', label: t('statusOpen') },\n { value: 'blocked', label: t('statusBlocked') },\n { value: 'stale', label: t('statusStale') },\n ],\n onChange: (next) => { setQuery({ status: next }) },\n }),\n h(Select<string>, {\n value: state.query.workspace,\n ariaLabel: t('workspaceLabel'),\n width: 180,\n options: [\n { value: '', label: t('workspaceAll') },\n ...(board?.workspaces ?? []).map((workspace) => ({ value: workspace.id, label: workspace.title })),\n ],\n onChange: (next) => { setQuery({ workspace: next }) },\n }),\n h('div', { style: searchWrapStyle },\n h(SearchInput, {\n value: state.query.keyword,\n placeholder: t('searchPlaceholder'),\n ariaLabel: t('searchPlaceholder'),\n onChange: (next) => { setQuery({ keyword: next }) },\n }),\n ),\n ),\n\n // ── 列表(两区;空态三分)──\n state.status === 'loading' && board === null\n ? null // 未就绪:不渲染\n : state.status === 'error' && board === null\n ? h('div', { style: { display: 'flex', alignItems: 'center', gap: 'var(--dtl-space-2)' } },\n h('span', { style: { color: 'var(--dtl-danger)', fontSize: 'var(--dtl-font-md)' } }, `${t('loadFailed')}:${state.error ?? ''}`),\n h(Button, { variant: 'outline', size: 'sm', onClick: () => { void refresh() } }, t('retry')),\n )\n : board !== null && board.workspaces.length === 0\n ? h(EmptyState, { tone: 'guide', title: t('emptyNoWorkspace'), hint: t('emptyNoWorkspaceHint') })\n : view.total === 0\n ? h(EmptyState, {\n tone: isPristine(state.query) ? 'guide' : 'filtered',\n title: isPristine(state.query) ? t('emptyNoTask') : t('emptyFiltered'),\n hint: isPristine(state.query) ? t('emptyNoTaskHint') : undefined,\n })\n : h('div', null,\n // 第一区:我的待办(跨工作区平铺,工作区为一列)\n view.mine.length === 0\n ? null\n : h('div', null,\n h('div', { style: regionHeadStyle },\n h('span', null, `${t('regionMine')} ${view.mine.length}`),\n h('span', { style: { fontSize: 'var(--dtl-font-xs)', color: 'var(--dtl-fg-3)', fontWeight: 400 } }, t('regionMineHint')),\n ),\n view.mine.map(row => h(TaskRowView, { key: row.key, t, row, showWorkspace: true })),\n ),\n // 第二区:按工作区分组(组可折叠)\n view.groups.length === 0\n ? null\n : h('div', null,\n h('div', { style: regionHeadStyle },\n h('span', null, t('regionWorkspaces')),\n h('span', { style: { fontSize: 'var(--dtl-font-xs)', color: 'var(--dtl-fg-3)', fontWeight: 400 } }, t('showingCount', { n: view.total - view.mine.length })),\n ),\n view.groups.map(group => h(WorkspaceGroupView, { key: group.workspaceId, t, group })),\n ),\n ),\n ),\n ),\n ),\n )\n}\n\n/** 一个工作区分组:标题行(可点折叠)+ 行列表。 */\nfunction WorkspaceGroupView(props: { t: Translate; group: WorkspaceGroup }): ReactNode {\n const { t, group } = props\n const [open, setOpen] = useState(group.rows.length <= GROUP_COLLAPSE_OVER)\n return h('div', { style: { marginBottom: 'var(--dtl-space-3)' } },\n h('button', {\n type: 'button',\n className: 'dtl-row-head',\n style: groupHeadStyle,\n 'aria-expanded': open,\n title: open ? t('collapseAria') : t('expandAria'),\n onClick: () => { setOpen(!open) },\n },\n h('span', { className: 'dtl-ellipsis', style: { flex: '1 1 auto', fontSize: 'var(--dtl-font-md)', fontWeight: 600 } }, group.title),\n h('span', { style: groupCountStyle }, t('groupHint', { doing: group.doingCount, open: group.openCount })),\n ),\n open\n ? h('div', { style: { marginTop: 'var(--dtl-space-2)' } },\n group.rows.map(row => h(TaskRowView, { key: row.key, t, row })),\n )\n : null,\n )\n}\n\n/** 卡片高亮:当前 query 是否正好落在该卡对应的视角上。 */\nfunction isActive(query: Query, patch: Partial<Query>): boolean {\n return Object.entries(patch).every(([key, value]) => query[key as keyof Query] === value)\n}\n\n/** 是否「什么都没筛」:真空才给引导卡,一有筛选就只能是「筛选空」。 */\nfunction isPristine(query: Query): boolean {\n return query.scope === 'all' && query.status === 'all' && query.workspace === '' && query.keyword.trim() === ''\n}\n","/**\n * 文案唯一真源:命名空间 `dsh-task-ledger`,zh / en 两档。\n *\n * 规矩(docs/design/ui-style.md §五):\n * - 两档**键必须完全对齐**(冒烟有断言),漏一种语言界面就露键名;\n * - 界面上不许出现未登记的硬编码中文;\n * - 占位符一律 `{name}`,由 `interpolateTranslate` 自己替换(不依赖宿主是否支持 params);\n * - 语言切换**跟随宿主**:插件不自备语言开关(注册字典 + 注册项声明 `locale:` ⇒ 宿主渲染器合成 `t` 席位)。\n */\n\n/** 文案键全集(增删键必须同时改 zh / en)。 */\nexport type LocaleKey =\n | 'backToConversation' | 'panelTitle' | 'tabTasks' | 'tabStats'\n | 'refresh' | 'refreshing' | 'loading' | 'retry' | 'loadFailed'\n | 'statsPlaceholder'\n // 统计卡\n | 'statMyActive' | 'statMyActiveHint' | 'statOpen' | 'statOpenHint' | 'statStaleTitle'\n | 'statDoing' | 'statDoingHint' | 'statBlockedStale' | 'statStaleWord' | 'statStaleHint'\n | 'statWeekDone' | 'statLastWeekDone' | 'statMonthDone' | 'statLastMonthDone' | 'statCreatedHint'\n | 'daysNone' | 'daysValue'\n // 趋势图\n | 'chartTitle' | 'chartCaption' | 'chartBucket' | 'chartEmpty'\n | 'chartScopeAll' | 'chartScopeMine' | 'chartOnlyMine' | 'chartLegendDone' | 'chartLegendCreated' | 'chartAvg'\n | 'periodRange'\n // 筛选行\n | 'scopeLabel' | 'scopeMine' | 'scopeAll'\n | 'statusLabel' | 'statusAll' | 'statusActive' | 'statusOpen' | 'statusBlocked' | 'statusStale'\n | 'workspaceLabel' | 'workspaceAll'\n | 'searchPlaceholder'\n // 列表分区\n | 'regionMine' | 'regionMineHint' | 'regionWorkspaces' | 'groupHint' | 'showingCount'\n | 'expandAria' | 'collapseAria'\n | 'ownerUnclaimed' | 'fieldCreated' | 'fieldStarted' | 'fieldFinished' | 'timeUnknown'\n | 'worklogTitle' | 'worklogPath' | 'worklogMissing'\n | 'mdCopy' | 'mdCopied' | 'mdFootnotes'\n // 空态\n | 'emptyNoWorkspace' | 'emptyNoWorkspaceHint' | 'emptyNoTask' | 'emptyNoTaskHint' | 'emptyFiltered'\n // 浮标入口\n | 'badgeTitle' | 'openBoard'\n\n/** 中文(默认档)。 */\nexport const zh: Record<LocaleKey, string> = {\n backToConversation: '返回会话',\n panelTitle: '任务台账',\n tabTasks: '任务',\n tabStats: '统计',\n refresh: '刷新',\n refreshing: '刷新中',\n loading: '读取中…',\n retry: '重试',\n loadFailed: '读取失败',\n statsPlaceholder: '统计报表尚未开建(另立任务):这里以后放分类分布、按人完成数、时间线。',\n\n statMyActive: '我的待办',\n statMyActiveHint: '阻塞 {n} 条',\n statOpen: '待认领',\n statOpenHint: '最久未认领 {n} 天',\n statDoing: '进行中',\n statDoingHint: '他人负责 {n} 条',\n statBlockedStale: '阻塞 · 停滞',\n statStaleWord: '停滞',\n statStaleHint: '{n} 条',\n statStaleTitle: '停滞 = 进行中或阻塞超过 {n} 天未更新',\n statWeekDone: '本周完成',\n statLastWeekDone: '上周完成',\n statMonthDone: '本月完成',\n statLastMonthDone: '上月完成',\n statCreatedHint: '新增 {n} 条',\n daysNone: '—',\n daysValue: '{n} 天',\n\n chartTitle: '近 8 周',\n chartCaption: '完成(按结案时间)/ 新增(按创建时间),周一起桶;我的 = owner 是我',\n chartBucket: '{week} 当周:完成 {done} 条,新增 {created} 条',\n chartEmpty: '近 8 周没有记录',\n chartScopeAll: '所有',\n chartScopeMine: '我的',\n chartOnlyMine: '只看我的',\n chartLegendDone: '完成',\n chartLegendCreated: '新增',\n chartAvg: '周均 {n}',\n periodRange: '{from} ~ {to}',\n\n scopeLabel: '范围',\n scopeMine: '我的',\n scopeAll: '全部工作区',\n statusLabel: '状态',\n statusAll: '全部',\n statusActive: '进行中',\n statusOpen: '待认领',\n statusBlocked: '阻塞',\n statusStale: '停滞',\n workspaceLabel: '工作区',\n workspaceAll: '全部工作区',\n searchPlaceholder: '搜索标题 / 任务号 / 人 / 分类 / 标签',\n\n regionMine: '我的待办',\n regionMineHint: '跨工作区,按创建时间倒序',\n regionWorkspaces: '按工作区',\n groupHint: '进行中 {doing} | 待认领 {open}',\n showingCount: '显示 {n} 条',\n expandAria: '展开过程文档',\n collapseAria: '收起',\n\n ownerUnclaimed: '待认领',\n fieldCreated: '创建',\n fieldStarted: '开始',\n fieldFinished: '结案',\n timeUnknown: '时间未知',\n worklogTitle: '过程文档',\n worklogPath: '路径',\n worklogMissing: '(缺失)',\n mdCopy: '复制',\n mdCopied: '已复制',\n mdFootnotes: '脚注',\n\n emptyNoWorkspace: '还没有台账工作区',\n emptyNoWorkspaceHint: '在某个项目里跑 task-ledger skill 的 init 建档(生成 ledger/progress.json),这里就会出现它。',\n emptyNoTask: '一条任务都没有',\n emptyNoTaskHint: '用 task-ledger skill 的 new 开一个任务,或让别人开在这里等你认领。',\n emptyFiltered: '没有符合当前筛选的任务',\n\n badgeTitle: '我名下待办',\n openBoard: '打开任务台账',\n}\n\n/** English(与 zh 键完全对齐)。 */\nexport const en: Record<LocaleKey, string> = {\n backToConversation: 'Back to chat',\n panelTitle: 'Task ledger',\n tabTasks: 'Tasks',\n tabStats: 'Stats',\n refresh: 'Refresh',\n refreshing: 'Refreshing',\n loading: 'Loading…',\n retry: 'Retry',\n loadFailed: 'Load failed',\n statsPlaceholder: 'Stats reports are not built yet (separate task): category breakdown, completions by person, timeline.',\n\n statMyActive: 'My todos',\n statMyActiveHint: 'blocked {n}',\n statOpen: 'Unclaimed',\n statOpenHint: 'unclaimed {n}d at most',\n statDoing: 'In progress',\n statDoingHint: 'owned by others: {n}',\n statBlockedStale: 'Blocked · Stale',\n statStaleWord: 'Stale',\n statStaleHint: '{n}',\n statStaleTitle: 'stale = active or blocked with no update for {n}+ days',\n statWeekDone: 'Done this week',\n statLastWeekDone: 'Done last week',\n statMonthDone: 'Done this month',\n statLastMonthDone: 'Done last month',\n statCreatedHint: '{n} created',\n daysNone: '—',\n daysValue: '{n}d',\n\n chartTitle: 'Last 8 weeks',\n chartCaption: 'done (by close time) / created (by create time), weeks start Monday; mine = owner is me',\n chartBucket: 'week of {week}: {done} done, {created} created',\n chartEmpty: 'Nothing in the last 8 weeks',\n chartScopeAll: 'All',\n chartScopeMine: 'Mine',\n chartOnlyMine: 'Mine only',\n chartLegendDone: 'Done',\n chartLegendCreated: 'Created',\n chartAvg: 'avg {n}/wk',\n periodRange: '{from} ~ {to}',\n\n scopeLabel: 'Scope',\n scopeMine: 'Mine',\n scopeAll: 'All workspaces',\n statusLabel: 'Status',\n statusAll: 'All',\n statusActive: 'Active',\n statusOpen: 'Unclaimed',\n statusBlocked: 'Blocked',\n statusStale: 'Stale',\n workspaceLabel: 'Workspace',\n workspaceAll: 'All workspaces',\n searchPlaceholder: 'Search title / id / person / category / tag',\n\n regionMine: 'My todos',\n regionMineHint: 'across workspaces, newest first',\n regionWorkspaces: 'By workspace',\n groupHint: '{doing} active | {open} unclaimed',\n showingCount: '{n} shown',\n expandAria: 'Expand worklog',\n collapseAria: 'Collapse',\n\n ownerUnclaimed: 'unclaimed',\n fieldCreated: 'created',\n fieldStarted: 'started',\n fieldFinished: 'closed',\n timeUnknown: 'unknown time',\n worklogTitle: 'Worklog',\n worklogPath: 'path',\n worklogMissing: '(missing)',\n mdCopy: 'Copy',\n mdCopied: 'Copied',\n mdFootnotes: 'Footnotes',\n\n emptyNoWorkspace: 'No ledger workspace yet',\n emptyNoWorkspaceHint: 'Run the task-ledger skill init in a project (creates ledger/progress.json) and it shows up here.',\n emptyNoTask: 'No tasks yet',\n emptyNoTaskHint: 'Create one with the task-ledger skill new, or leave one here for others to claim.',\n emptyFiltered: 'No tasks match the current filters',\n\n badgeTitle: 'my todos',\n openBoard: 'Open task ledger',\n}\n\n/** 翻译函数类型(宿主渲染器按 `locale:` 声明合成的 `t` 席位同形)。 */\nexport type Translate = (key: LocaleKey, params?: Record<string, string | number>) => string\n\n/** 把宿主给的无参 t 包成带占位符替换的 t(模板一律 `{name}`,不依赖宿主是否支持 params)。 */\nexport function interpolateTranslate(base: Translate): Translate {\n return (key, params) => {\n const raw = base(key)\n if (params === undefined) return raw\n return raw.replace(/\\{(\\w+)\\}/g, (match, name: string) => (name in params ? String(params[name]) : match))\n }\n}\n\n/** 键名兜底:宿主没给 t 席位时用(键名绝不上界面)。 */\nexport function fallbackTranslate(key: LocaleKey, params?: Record<string, string | number>): string {\n return interpolateTranslate((name) => zh[name] ?? name)(key, params)\n}\n","/**\n * 右上浮标入口(`shell.overlay` list 条目,全帧浮层,条目并排添加不替换)。\n * 定位用宿主变量 `--dsh-frame-overlay-top`(标题栏净空 + 20px),**不写死 top 值**;\n * 浮层默认 `pointer-events:none`、子元素自动恢复 ⇒ 自绘条目不挡下方应用。\n *\n * 角标 = 我名下 `doing` + `blocked` 计数(host 统计真值,SSE 已否 ⇒ 取自同一份快照,>0 才画);\n * 0→n 跳变时一次脉冲动画,不自动消失。宿主**无**通知 API ⇒ 「通知」就是这个角标。\n */\nimport { createElement as h, useEffect, useRef, useState, useSyncExternalStore, type ReactNode } from 'react'\nimport type { Translate } from './locales'\nimport { boardSnapshot, boardSubscribe } from './store'\nimport { ensureUiBase, LedgerIcon } from './ui'\n\nexport function OverlayEntry(props: { t: Translate; onOpen: () => void }): ReactNode {\n const { t, onOpen } = props\n ensureUiBase()\n const state = useSyncExternalStore(boardSubscribe, boardSnapshot)\n const count = state.board?.stats.myActive ?? 0\n const previous = useRef(0)\n const [pulse, setPulse] = useState(0)\n\n // 0→n 跳变播一次脉冲;计数归零不播(角标本就不渲染)。\n useEffect(() => {\n if (count > previous.current) setPulse(value => value + 1)\n previous.current = count\n }, [count])\n\n return h('div', {\n style: {\n position: 'absolute',\n top: 'var(--dsh-frame-overlay-top,20px)',\n right: '12px',\n zIndex: 'var(--dtl-z-badge,20)',\n pointerEvents: 'auto',\n },\n },\n h('button', {\n type: 'button',\n title: t('openBoard'),\n 'aria-label': t('openBoard'),\n onClick: onOpen,\n style: {\n position: 'relative',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n width: '32px',\n height: '32px',\n padding: 0,\n border: '1px solid var(--dtl-border)',\n borderRadius: 'var(--dtl-radius-sm)',\n background: 'var(--dtl-solid)',\n color: 'var(--dtl-on-solid)',\n boxShadow: 'var(--dtl-shadow-1)',\n cursor: 'pointer',\n },\n },\n h(LedgerIcon, { size: 18 }),\n count > 0\n ? h('span', { key: pulse, className: 'dtl-badge', 'data-pulse': pulse > 0, title: t('badgeTitle') }, String(count))\n : null,\n ),\n )\n}\n","/**\n * 浏览器半入口(client bundle,产物 dist/client.js):装配层,不写业务逻辑。\n *\n * 三处注册各自挂进「服务就位才触发」的 slots 注入(参考插件真机教训:顶层 inject 声明\n * 组合满足不了的依赖会让 entry 一直 pending、卡死整个 dsh 启动):\n * · locale 字典(zh / en)——界面语言跟随宿主,插件不自备开关;\n * · `main` keyed 槽整页(看板首页);\n * · `shell.overlay` list 槽浮标入口(点开 = `ctx.layout.selectPanel(<看板 id>)`)。\n *\n * 宿主能力一律**本地结构化声明**,不 import 官方包(rc 接口会无声变);\n * `t` 席位由宿主渲染器按注册项的 `locale:` 声明合成进组件 props。\n */\nimport { createElement as h } from 'react'\nimport { BoardHome } from './board-home'\nimport { en, fallbackTranslate, interpolateTranslate, zh, type LocaleKey, type Translate } from './locales'\nimport { LedgerIcon } from './ui'\nimport { OverlayEntry } from './overlay-entry'\n\n/** 命名空间(settings / locale / 面板 id 同名,便于对应)。 */\n/** 模块级 t 席位(侧栏 label 渲染期求值用;绑不上就回落键名,绝不上界面)。 */\nlet runtimeT: Translate = (key) => key\n\nconst NS = 'dsh-task-ledger'\n/** `main` 槽的 key(非 `conversation` ⇒ 不绑 Session)。 */\nconst PANEL_ID = NS\n/** `shell.overlay` list 条目的 id(新 id = 并排添加,不替换宿主条目)。 */\nconst OVERLAY_ID = `${NS}-badge`\n\n/** 浏览器插件上下文:只声明本文件实际用到的服务面。 */\ninterface ClientContext {\n inject(deps: readonly string[], callback: (ctx: ClientContext) => void): void\n effect(setup: () => void): void\n locale?: {\n register(ns: string, dictionaries: Record<string, Record<string, string>>): void\n /** 绑一份无参 t(侧栏 label 在渲染期求值,拿不到组件 props 的 t 席位)。 */\n bind(ns: string): Translate\n }\n layout?: {\n selectPanel(panelId: string | null): void\n }\n slots: {\n inject(slot: string, factory: () => () => void): () => void\n register(options: Record<string, unknown>, component: unknown): () => void\n }\n}\n\n/** 宿主没给 t 席位时的兜底(键名绝不上界面)。 */\nconst t: Translate = interpolateTranslate((key: LocaleKey, params?: Record<string, string | number>) =>\n fallbackTranslate(key, params))\n\nexport function apply(ctx: ClientContext): void {\n // ① 字典注册(宿主语言切换 ⇒ 渲染器换 t 引用 ⇒ 整页重渲染)\n ctx.inject(['locale'], (localeCtx) => {\n ctx.effect(() => localeCtx.locale?.register(NS, { zh, en }))\n // 模块级 t 席位:`sidebar.panellist` 的 label 由侧栏在渲染期求值,拿不到组件 props 的 t\n try { runtimeT = localeCtx.locale?.bind(NS) ?? runtimeT } catch { /* 保持键名兜底 */ }\n })\n\n // ② 布局服务:浮标点开整页 / 整页返回会话\n let selectPanel: (id: string | null) => void = () => {}\n ctx.inject(['layout'], (sub) => {\n const layout = sub.layout\n if (layout !== undefined) selectPanel = (id) => { layout.selectPanel(id) }\n })\n\n // ③ 槽位注册(只依赖 slots,自成一块:locale / layout 缺席也不该让入口消失)\n ctx.inject(['slots'], (sub) => {\n // 左侧入口:侧栏面板条目(list 槽)。**行按钮、点击、激活态全由侧栏 / 布局服务托管**,\n // 组件只画图标;id 必须与 main 槽的 key 一致(同一 MainPanelId)。\n // 事实见 external/client-ui-layout.md §一/§二(0.2.0-rc.2 亲验)。\n sub.slots.inject('sidebar.panellist', () =>\n sub.slots.register(\n { name: 'sidebar.panellist', id: PANEL_ID, order: 30, label: () => runtimeT('panelTitle'), locale: NS },\n (props: { size?: number }) => h(LedgerIcon, { size: props.size ?? 18 }),\n ),\n )\n sub.slots.inject('main', () =>\n sub.slots.register(\n { name: 'main', key: PANEL_ID, locale: NS },\n (props: { t?: Translate }) => h(BoardHome, {\n t: props.t ?? t,\n onBack: () => { selectPanel(null) },\n }),\n ),\n )\n sub.slots.inject('shell.overlay', () =>\n sub.slots.register(\n { name: 'shell.overlay', id: OVERLAY_ID, locale: NS },\n (props: { t?: Translate }) => h(OverlayEntry, {\n t: props.t ?? t,\n onOpen: () => { selectPanel(PANEL_ID) },\n }),\n ),\n )\n })\n}\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-task-ledger",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.12",
|
|
4
4
|
"description": "Task ledger board for dsh (DeepSeek Harness): read-only board over task-ledger skill workspaces. The task-ledger skill is bundled — install the plugin, get the skill globally.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"deepseek",
|