dsh-task-ledger 0.0.115 → 0.0.117

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 CHANGED
@@ -53574,6 +53574,11 @@ body{
53574
53574
  --dtl-business-soft:color-mix(in srgb,var(--dtl-business) 14%,transparent);
53575
53575
  --dtl-danger-soft:color-mix(in srgb,var(--dtl-danger) 8%,transparent);
53576
53576
 
53577
+ /* ── 趋势图三色(2026-10-29 接回图表时恢复)─────────────────────
53578
+ 完成 = 蓝(--dtl-business)、未结 = 琥珀、新增 = 灰(fg-dim,明暗自带) */
53579
+ --dtl-chart-open:var(--dtl-warning);
53580
+ --dtl-chart-added:var(--dtl-fg-dim);
53581
+
53577
53582
  /* ── 交互底 ──────────────────────────────────────────────────── */
53578
53583
  /* 卡 / 行 hover 高亮:淡蓝(灰高亮在卡底上几乎看不出来) */
53579
53584
  --dtl-card-hover:rgba(37,99,235,.07);
@@ -53665,6 +53670,61 @@ body[data-ds-dark-theme]{
53665
53670
  @media (max-width:960px){.dtl-stats{grid-template-columns:minmax(0,1fr);}}
53666
53671
  @media (max-width:640px){.dtl-stats__cards{grid-template-columns:repeat(2,minmax(0,1fr));grid-template-rows:repeat(4,minmax(0,1fr));}}
53667
53672
 
53673
+ /* ── 趋势图(右上卡:8 周 × 3 柱)────────────────────────────────
53674
+ 柱高是百分比 ⇒ 祖先链必须有**确定高度**(卡 height:100% → body flex:1 → plot 绝对铺满);
53675
+ 2026-10-17 真机教训:百分比高度没有确定父高时整列柱消失,只剩数字。 */
53676
+ .dtl-trend{height:100%;box-sizing:border-box;display:flex;flex-direction:column;gap:var(--dtl-space-2);
53677
+ padding:var(--dtl-space-4);}
53678
+ .dtl-trend__legend{display:flex;align-items:center;flex-wrap:wrap;gap:var(--dtl-space-3);
53679
+ font-size:var(--dtl-font-xs);color:var(--dtl-fg-3);}
53680
+ .dtl-trend__dot{display:inline-block;width:8px;height:8px;margin-right:4px;border-radius:var(--dtl-radius-xs);
53681
+ vertical-align:middle;}
53682
+ .dtl-trend__dot--done{background:var(--dtl-business);}
53683
+ .dtl-trend__dot--open{background:var(--dtl-chart-open);}
53684
+ .dtl-trend__dot--added{background:var(--dtl-chart-added);}
53685
+ /* 「只看我的」:贴图例右侧的小复选框(原生控件 + accent-color,不自绘皮肤) */
53686
+ .dtl-trend__mine{display:inline-flex;align-items:center;gap:4px;margin-left:auto;cursor:pointer;
53687
+ font-size:var(--dtl-font-xs);color:var(--dtl-fg-3);}
53688
+ .dtl-trend__mine input{margin:0;accent-color:var(--dtl-business);}
53689
+ .dtl-trend__body{flex:1 1 auto;min-height:0;display:flex;gap:var(--dtl-space-2);}
53690
+ .dtl-trend__axis{display:flex;flex-direction:column;justify-content:space-between;flex:none;
53691
+ padding-bottom:14px;font-size:10px;line-height:1;text-align:right;font-variant-numeric:tabular-nums;
53692
+ color:var(--dtl-fg-3);}
53693
+ .dtl-trend__plot{position:relative;flex:1 1 auto;min-width:0;}
53694
+ /* 网格:只在绘图区内(底部 14px 留给 x 轴日期),三档刻度与三条线一一对应 */
53695
+ .dtl-trend__grid{position:absolute;left:0;right:0;top:0;bottom:14px;display:flex;flex-direction:column;
53696
+ justify-content:space-between;}
53697
+ .dtl-trend__gridline{border-top:1px solid var(--dtl-border);}
53698
+ .dtl-trend__gridline--base{border-top-color:var(--dtl-border-strong);}
53699
+ .dtl-trend__cols{position:absolute;left:0;right:0;top:0;bottom:0;display:flex;align-items:stretch;
53700
+ gap:var(--dtl-space-2);}
53701
+ .dtl-trend__col{position:relative;flex:1 1 0;min-width:0;display:flex;flex-direction:column;
53702
+ justify-content:flex-end;padding-bottom:14px;}
53703
+ .dtl-trend__bars{display:flex;align-items:flex-end;justify-content:center;gap:1px;flex:1 1 auto;min-height:0;}
53704
+ /* 柱宽 5px、无圆角(用户 2026-10-17:「太粗、不要圆弧」);非零柱给 2px 下限 */
53705
+ .dtl-trend__bar{position:relative;width:5px;flex:none;min-height:2px;
53706
+ transition:filter var(--dtl-dur-fast) var(--dtl-ease);}
53707
+ .dtl-trend__bar--done{background:var(--dtl-business);}
53708
+ .dtl-trend__bar--open{background:var(--dtl-chart-open);}
53709
+ .dtl-trend__bar--added{background:var(--dtl-chart-added);}
53710
+ /* hover 整列三根一起提亮(用户 2026-10-17:不是只亮蓝的一根) */
53711
+ .dtl-trend__col:hover .dtl-trend__bar{filter:brightness(1.18);}
53712
+ .dtl-trend__value{position:absolute;top:-12px;left:50%;transform:translateX(-50%);
53713
+ font-size:10px;line-height:1;color:var(--dtl-fg-2);font-variant-numeric:tabular-nums;white-space:nowrap;}
53714
+ .dtl-trend__x{position:absolute;left:0;right:0;bottom:0;text-align:center;font-size:10px;line-height:1;
53715
+ color:var(--dtl-fg-3);font-variant-numeric:tabular-nums;}
53716
+ /* hover 浮卡:自绘(原生 title 太慢且长相不一) */
53717
+ .dtl-trend__tip{position:absolute;left:50%;bottom:calc(100% - 10px);z-index:2;transform:translateX(-50%);
53718
+ padding:var(--dtl-space-2);border:1px solid var(--dtl-border-strong);border-radius:var(--dtl-radius-sm);
53719
+ background:var(--dtl-surface-1);white-space:nowrap;
53720
+ font-size:var(--dtl-font-xs);line-height:1.5;color:var(--dtl-fg-2);
53721
+ opacity:0;pointer-events:none;transition:opacity var(--dtl-dur-fast) var(--dtl-ease);}
53722
+ .dtl-trend__col:hover .dtl-trend__tip{opacity:1;}
53723
+ .dtl-trend__tip-row{font-variant-numeric:tabular-nums;}
53724
+ /* 未结的拆分行:缩进 + 淡一档,读起来是「未结」的注脚 */
53725
+ .dtl-trend__tip-row--sub{padding-left:10px;color:var(--dtl-fg-3);}
53726
+ .dtl-trend__foot{font-size:var(--dtl-font-xs);color:var(--dtl-fg-3);}
53727
+
53668
53728
  /* 任务行 = 一张卡(样式照抄 dispatch-table TaskCard:surface-1 底 + 1px 边框 + 左缘状态条;
53669
53729
  hover 只亮主行;右侧 时间⇄按钮排 互换) */
53670
53730
  .dtl-taskrow{background:var(--dtl-surface-1);border:1px solid var(--dtl-border);
@@ -54110,6 +54170,85 @@ body[data-ds-dark-theme]{
54110
54170
  }), (0, react$1.createElement)("span", { className: "dtl-taskrow__meta" }, metaText), (0, react$1.createElement)("span", { className: "dtl-taskrow__actions" }, actions.claim ? claimButton : null, actions.resume ? resumeButton : null, actions.finish ? finishButton : null, actions.release ? releaseButton : null, copyButton, actions.view ? viewButton : null)));
54111
54171
  }
54112
54172
  //#endregion
54173
+ //#region src/client/trend-chart.ts
54174
+ /**
54175
+ * 首页趋势图(右上卡):近 8 周 × 三根细柱 = 完成 / 未结 / 新增。
54176
+ *
54177
+ * 数据口径**全由 skill 算好**(`stats --trend week --last 8w --by-owner`,host 已跨工作区求和):
54178
+ * 完成按结案时间、新增按**创建人**、未结是「该周结束时仍未结案」(待认领 + 进行中合成一个数)。
54179
+ *
54180
+ * 「只看我的」(2026-10-29 用户定稿):勾上 = 我的,不勾 = 所有;两套数随快照一起回来 ⇒ 勾选零请求。
54181
+ * 按人口径 = skill 的 per-owner:`added` 按 creator(我提的)、`done` / `open` 按 owner(我负责的)。
54182
+ *
54183
+ * 画法(2026-10-17 参考图口径):
54184
+ * - 柱宽 5px、组内 1px、**无圆角**;柱高百分比(祖先链有确定高度,见 ui.ts 的注释);
54185
+ * - y 轴三档刻度(0 / 半峰 / 峰)+ 淡网格线 + 底部基线;
54186
+ * - **只标「完成」数值**(三系列 × 8 周会糊成一片),未结 / 新增走 hover;
54187
+ * - hover 整列三根一起提亮 + 自绘浮卡(原生 title 太慢且长相不一)——纯 CSS,无额外状态。
54188
+ */
54189
+ const BAR_KINDS = [
54190
+ "done",
54191
+ "open",
54192
+ "added"
54193
+ ];
54194
+ const ZERO = {
54195
+ added: 0,
54196
+ done: 0,
54197
+ open: 0
54198
+ };
54199
+ const labelKey = (kind) => kind === "done" ? "chartDone" : kind === "open" ? "chartOpen" : "chartAdded";
54200
+ function TrendChart(props) {
54201
+ const { trend, me, t } = props;
54202
+ const [mineOnly, setMineOnly] = (0, react$1.useState)(false);
54203
+ const buckets = trend.buckets;
54204
+ const mine = mineOnly && me !== null;
54205
+ if (buckets.length === 0) return (0, react$1.createElement)("div", { className: "dtl-card dtl-trend" }, (0, react$1.createElement)("div", { className: "dtl-trend__foot" }, t("chartEmpty")));
54206
+ /** 当前口径下这一桶的三个数:全量,或「我的」那条(没数据就是 0)。 */
54207
+ const counts = (bucket) => mine ? bucket.owners[me] ?? ZERO : bucket;
54208
+ /** 未结拆两段:进行中 = 各人未结之和;待认领 = 未结 − 进行中。
54209
+ * 「只看我的」时只有进行中(我的任务必然有人接),不显示拆分行。 */
54210
+ const breakdown = (bucket) => {
54211
+ const doing = Object.values(bucket.owners).reduce((sum, cell) => sum + cell.open, 0);
54212
+ return {
54213
+ doing,
54214
+ waiting: Math.max(0, bucket.open - doing)
54215
+ };
54216
+ };
54217
+ const peak = buckets.reduce((max, bucket) => {
54218
+ const cell = counts(bucket);
54219
+ return Math.max(max, cell.added, cell.done, cell.open);
54220
+ }, 0);
54221
+ const top = peak === 0 ? 1 : peak;
54222
+ const ticks = [
54223
+ formatCount(top),
54224
+ formatCount(Math.ceil(top / 2)),
54225
+ "0"
54226
+ ];
54227
+ const bar = (bucket, kind) => {
54228
+ const value = counts(bucket)[kind];
54229
+ return (0, react$1.createElement)("div", {
54230
+ className: `dtl-trend__bar dtl-trend__bar--${kind}`,
54231
+ style: {
54232
+ height: `${String(value / top * 100)}%`,
54233
+ minHeight: value > 0 ? "2px" : "0"
54234
+ }
54235
+ }, kind === "done" && value > 0 ? (0, react$1.createElement)("span", { className: "dtl-trend__value" }, formatCount(value)) : null);
54236
+ };
54237
+ const column = (bucket) => (0, react$1.createElement)("div", { className: "dtl-trend__col" }, (0, react$1.createElement)("div", { className: "dtl-trend__tip" }, (0, react$1.createElement)("div", { className: "dtl-trend__tip-row" }, t("periodRange", {
54238
+ from: bucket.from,
54239
+ to: bucket.to
54240
+ })), ...BAR_KINDS.map((kind) => (0, react$1.createElement)("div", { className: "dtl-trend__tip-row" }, (0, react$1.createElement)("span", { className: `dtl-trend__dot dtl-trend__dot--${kind}` }), `${t(labelKey(kind))} ${formatCount(counts(bucket)[kind])}`)), mine ? null : (0, react$1.createElement)("div", { className: "dtl-trend__tip-row dtl-trend__tip-row--sub" }, `${t("chartDoing")} ${formatCount(breakdown(bucket).doing)} · ${t("chartWaiting")} ${formatCount(breakdown(bucket).waiting)}`)), (0, react$1.createElement)("div", { className: "dtl-trend__bars" }, ...BAR_KINDS.map((kind) => bar(bucket, kind))), (0, react$1.createElement)("div", { className: "dtl-trend__x" }, bucket.to.slice(5)));
54241
+ const first = buckets[0];
54242
+ const partial = trend.coverageFrom !== "" && trend.coverageFrom > first.from ? (0, react$1.createElement)("div", { className: "dtl-trend__foot" }, t("chartPartial", { from: trend.coverageFrom })) : null;
54243
+ return (0, react$1.createElement)("div", { className: "dtl-card dtl-trend" }, (0, react$1.createElement)("div", { className: "dtl-trend__legend" }, ...BAR_KINDS.map((kind) => (0, react$1.createElement)("span", null, (0, react$1.createElement)("span", { className: `dtl-trend__dot dtl-trend__dot--${kind}` }), t(labelKey(kind)))), me === null ? null : (0, react$1.createElement)("label", { className: "dtl-trend__mine" }, (0, react$1.createElement)("input", {
54244
+ type: "checkbox",
54245
+ checked: mineOnly,
54246
+ onChange: (event) => {
54247
+ setMineOnly(event.target.checked);
54248
+ }
54249
+ }), t("chartMine"))), (0, react$1.createElement)("div", { className: "dtl-trend__body" }, (0, react$1.createElement)("div", { className: "dtl-trend__axis" }, ...ticks.map((tick) => (0, react$1.createElement)("span", null, tick))), (0, react$1.createElement)("div", { className: "dtl-trend__plot" }, (0, react$1.createElement)("div", { className: "dtl-trend__grid" }, (0, react$1.createElement)("div", { className: "dtl-trend__gridline" }), (0, react$1.createElement)("div", { className: "dtl-trend__gridline" }), (0, react$1.createElement)("div", { className: "dtl-trend__gridline dtl-trend__gridline--base" })), (0, react$1.createElement)("div", { className: "dtl-trend__cols" }, ...buckets.map(column)))), partial);
54250
+ }
54251
+ //#endregion
54113
54252
  //#region src/client/board-home.ts
54114
54253
  /**
54115
54254
  * 看板首页(`main` 槽整页内容):上 = 8 统计卡 + 趋势图位(**留空**);下 = 单区任务列表。
@@ -54166,16 +54305,6 @@ body[data-ds-dark-theme]{
54166
54305
  * 列数写死 4 → 2(媒体查询在 ui.ts 的 .dtl-stats / .dtl-stats__cards 里),
54167
54306
  * 绝不给 3 列——3 列会排出 3+1 的非对称(用户 2026-10-17:必须上下对称)。 */
54168
54307
  const statsRowStyle = { marginBottom: "var(--dtl-space-4)" };
54169
- const chartCardStyle = {
54170
- display: "flex",
54171
- alignItems: "center",
54172
- justifyContent: "center",
54173
- padding: "var(--dtl-space-4)"
54174
- };
54175
- const hintStyle = {
54176
- fontSize: "var(--dtl-font-sm)",
54177
- color: "var(--dtl-fg-2)"
54178
- };
54179
54308
  /** 列表段头:滑块居左、过滤三件居右(用户 2026-10-28 去掉左侧标题)。 */
54180
54309
  const regionHeadRowStyle = {
54181
54310
  display: "flex",
@@ -54260,7 +54389,6 @@ body[data-ds-dark-theme]{
54260
54389
  };
54261
54390
  }, [state.board, state.status]);
54262
54391
  const board = state.board;
54263
- const stats = board?.stats;
54264
54392
  const view = (0, react$1.useMemo)(() => selectView(state), [state]);
54265
54393
  const degraded = board?.degraded ?? [];
54266
54394
  /** 工作区下拉候选:宿主里所有含台账的工作区(不是从任务里反推,空工作区也要能选)。 */
@@ -54316,14 +54444,18 @@ body[data-ds-dark-theme]{
54316
54444
  to
54317
54445
  })
54318
54446
  });
54319
- /** 左 8 块(2×4)+ 右趋势图位。 */
54320
- const statsGrid = (view) => (0, react$1.createElement)("div", {
54321
- className: "dtl-stats",
54322
- style: statsRowStyle
54323
- }, (0, react$1.createElement)("div", { className: "dtl-stats__cards" }, card("statMyActive", view.myActive, [view.myOverdue > 0 ? t("statMyOverdueHint", { n: view.myOverdue }) : null, view.myStale > 0 ? t("statMyStaleHint", { n: view.myStale }) : null].filter((part) => part !== null).join(" | "), void 0, "mine"), card("statDoing", view.doing, view.doing - view.myActive > 0 ? t("statDoingHint", { n: view.doing - view.myActive }) : t("statDoingAllMine"), void 0, "doing"), card("statOpen", view.open, view.oldestOpenDays === null ? "" : t("statOpenHint", { n: view.oldestOpenDays }), void 0, "open"), card("statBacklog", view.backlog, share(view.backlog, view.open, "statBacklogHint"), t("statBacklogTitle", { n: view.backlogDays }), "backlog"), card("statOverdue", view.overdue, share(view.overdue, view.doing, "statOverdueHint"), t("statOverdueTitle", { n: view.overdueDays }), "overdue"), card("statStale", view.stale, share(view.stale, view.doing, "statStaleHint"), t("statStaleTitle", { n: view.staleDays }), "stale"), weekCard("statWeekDone", view.weekDone, view.weekFrom, view.weekTo), weekCard("statLastWeekDone", view.lastWeekDone, view.lastWeekFrom, view.lastWeekTo)), (0, react$1.createElement)("div", {
54324
- className: "dtl-card",
54325
- style: chartCardStyle
54326
- }, (0, react$1.createElement)("div", { style: hintStyle }, t("chartNotReady"))));
54447
+ /** 左 8 块(2×4)+ 右趋势图。 */
54448
+ const statsGrid = (payload) => {
54449
+ const view = payload.stats;
54450
+ return (0, react$1.createElement)("div", {
54451
+ className: "dtl-stats",
54452
+ style: statsRowStyle
54453
+ }, (0, react$1.createElement)("div", { className: "dtl-stats__cards" }, card("statMyActive", view.myActive, [view.myOverdue > 0 ? t("statMyOverdueHint", { n: view.myOverdue }) : null, view.myStale > 0 ? t("statMyStaleHint", { n: view.myStale }) : null].filter((part) => part !== null).join(" | "), void 0, "mine"), card("statDoing", view.doing, view.doing - view.myActive > 0 ? t("statDoingHint", { n: view.doing - view.myActive }) : t("statDoingAllMine"), void 0, "doing"), card("statOpen", view.open, view.oldestOpenDays === null ? "" : t("statOpenHint", { n: view.oldestOpenDays }), void 0, "open"), card("statBacklog", view.backlog, share(view.backlog, view.open, "statBacklogHint"), t("statBacklogTitle", { n: view.backlogDays }), "backlog"), card("statOverdue", view.overdue, share(view.overdue, view.doing, "statOverdueHint"), t("statOverdueTitle", { n: view.overdueDays }), "overdue"), card("statStale", view.stale, share(view.stale, view.doing, "statStaleHint"), t("statStaleTitle", { n: view.staleDays }), "stale"), weekCard("statWeekDone", view.weekDone, view.weekFrom, view.weekTo), weekCard("statLastWeekDone", view.lastWeekDone, view.lastWeekFrom, view.lastWeekTo)), (0, react$1.createElement)(TrendChart, {
54454
+ trend: payload.trend,
54455
+ me: Object.values(payload.identities).find((uuid) => uuid !== null) ?? null,
54456
+ t
54457
+ }));
54458
+ };
54327
54459
  /** 段头:六档菜单居左,工作区 / 执行人 / 搜索居右(用户 2026-10-28:不要左侧标题)。 */
54328
54460
  const regionHead = (0, react$1.createElement)("div", { style: regionHeadRowStyle }, (0, react$1.createElement)(Segmented, {
54329
54461
  id: "dtl-list-tab",
@@ -54401,8 +54533,8 @@ body[data-ds-dark-theme]{
54401
54533
  onClick: onBack
54402
54534
  }, `← ${t("backToConversation")}`), (0, react$1.createElement)("div", { style: titleStyle }, t("panelTitle"), (0, react$1.createElement)("span", {
54403
54535
  style: buildTagStyle,
54404
- title: `v0.0.115 · 261029-121428-122`
54405
- }, `v0.0.115 261029-121428-122`))), (0, react$1.createElement)(Button, {
54536
+ title: `v0.0.117 · 261029-144427-989`
54537
+ }, `v0.0.117 261029-144427-989`))), (0, react$1.createElement)(Button, {
54406
54538
  variant: "outline",
54407
54539
  size: "lg",
54408
54540
  icon: (0, react$1.createElement)(_deepseek_ai_dsh_client_ui_primitives.IconRefreshOutlineRegular, { size: 16 }),
@@ -54424,7 +54556,7 @@ body[data-ds-dark-theme]{
54424
54556
  onClick: () => {
54425
54557
  refresh();
54426
54558
  }
54427
- }, t("retry"))) : null, stats === void 0 ? null : statsGrid(stats), degraded.length === 0 ? null : (0, react$1.createElement)("div", { style: {
54559
+ }, t("retry"))) : null, board === null ? null : statsGrid(board), degraded.length === 0 ? null : (0, react$1.createElement)("div", { style: {
54428
54560
  display: "flex",
54429
54561
  alignItems: "flex-start",
54430
54562
  gap: "var(--dtl-space-2)",
@@ -54487,7 +54619,14 @@ body[data-ds-dark-theme]{
54487
54619
  statLastWeekDone: "上周完成",
54488
54620
  daysNone: "—",
54489
54621
  daysValue: "{n} 天",
54490
- chartNotReady: "趋势图待接:skill 还没有按周汇总的方法",
54622
+ chartDone: "完成",
54623
+ chartOpen: "未结",
54624
+ chartAdded: "新增",
54625
+ chartEmpty: "还没有可统计的记录",
54626
+ chartPartial: "{from} 起才有台账数据",
54627
+ chartMine: "只看我的",
54628
+ chartDoing: "进行中",
54629
+ chartWaiting: "待认领",
54491
54630
  periodRange: "{from} ~ {to}",
54492
54631
  statusLabel: "状态",
54493
54632
  statusActive: "进行中",
@@ -54568,7 +54707,14 @@ body[data-ds-dark-theme]{
54568
54707
  statLastWeekDone: "Done last week",
54569
54708
  daysNone: "—",
54570
54709
  daysValue: "{n}d",
54571
- chartNotReady: "Trend chart pending: the skill has no weekly roll-up method yet",
54710
+ chartDone: "Done",
54711
+ chartOpen: "Open",
54712
+ chartAdded: "Added",
54713
+ chartEmpty: "No records to chart yet",
54714
+ chartPartial: "Ledger data starts {from}",
54715
+ chartMine: "Only mine",
54716
+ chartDoing: "In progress",
54717
+ chartWaiting: "Unclaimed",
54572
54718
  periodRange: "{from} ~ {to}",
54573
54719
  statusLabel: "Status",
54574
54720
  statusActive: "Active",