dsh-task-ledger 0.0.115 → 0.0.116

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,59 @@ 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
+ .dtl-trend__foot{font-size:var(--dtl-font-xs);color:var(--dtl-fg-3);}
53725
+
53668
53726
  /* 任务行 = 一张卡(样式照抄 dispatch-table TaskCard:surface-1 底 + 1px 边框 + 左缘状态条;
53669
53727
  hover 只亮主行;右侧 时间⇄按钮排 互换) */
53670
53728
  .dtl-taskrow{background:var(--dtl-surface-1);border:1px solid var(--dtl-border);
@@ -54110,6 +54168,76 @@ body[data-ds-dark-theme]{
54110
54168
  }), (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
54169
  }
54112
54170
  //#endregion
54171
+ //#region src/client/trend-chart.ts
54172
+ /**
54173
+ * 首页趋势图(右上卡):近 8 周 × 三根细柱 = 完成 / 未结 / 新增。
54174
+ *
54175
+ * 数据口径**全由 skill 算好**(`stats --trend week --last 8w --by-owner`,host 已跨工作区求和):
54176
+ * 完成按结案时间、新增按**创建人**、未结是「该周结束时仍未结案」(待认领 + 进行中合成一个数)。
54177
+ *
54178
+ * 「只看我的」(2026-10-29 用户定稿):勾上 = 我的,不勾 = 所有;两套数随快照一起回来 ⇒ 勾选零请求。
54179
+ * 按人口径 = skill 的 per-owner:`added` 按 creator(我提的)、`done` / `open` 按 owner(我负责的)。
54180
+ *
54181
+ * 画法(2026-10-17 参考图口径):
54182
+ * - 柱宽 5px、组内 1px、**无圆角**;柱高百分比(祖先链有确定高度,见 ui.ts 的注释);
54183
+ * - y 轴三档刻度(0 / 半峰 / 峰)+ 淡网格线 + 底部基线;
54184
+ * - **只标「完成」数值**(三系列 × 8 周会糊成一片),未结 / 新增走 hover;
54185
+ * - hover 整列三根一起提亮 + 自绘浮卡(原生 title 太慢且长相不一)——纯 CSS,无额外状态。
54186
+ */
54187
+ const BAR_KINDS = [
54188
+ "done",
54189
+ "open",
54190
+ "added"
54191
+ ];
54192
+ const ZERO = {
54193
+ added: 0,
54194
+ done: 0,
54195
+ open: 0
54196
+ };
54197
+ const labelKey = (kind) => kind === "done" ? "chartDone" : kind === "open" ? "chartOpen" : "chartAdded";
54198
+ function TrendChart(props) {
54199
+ const { trend, me, t } = props;
54200
+ const [mineOnly, setMineOnly] = (0, react$1.useState)(false);
54201
+ const buckets = trend.buckets;
54202
+ const mine = mineOnly && me !== null;
54203
+ 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")));
54204
+ /** 当前口径下这一桶的三个数:全量,或「我的」那条(没数据就是 0)。 */
54205
+ const counts = (bucket) => mine ? bucket.owners[me] ?? ZERO : bucket;
54206
+ const peak = buckets.reduce((max, bucket) => {
54207
+ const cell = counts(bucket);
54208
+ return Math.max(max, cell.added, cell.done, cell.open);
54209
+ }, 0);
54210
+ const top = peak === 0 ? 1 : peak;
54211
+ const ticks = [
54212
+ formatCount(top),
54213
+ formatCount(Math.ceil(top / 2)),
54214
+ "0"
54215
+ ];
54216
+ const bar = (bucket, kind) => {
54217
+ const value = counts(bucket)[kind];
54218
+ return (0, react$1.createElement)("div", {
54219
+ className: `dtl-trend__bar dtl-trend__bar--${kind}`,
54220
+ style: {
54221
+ height: `${String(value / top * 100)}%`,
54222
+ minHeight: value > 0 ? "2px" : "0"
54223
+ }
54224
+ }, kind === "done" && value > 0 ? (0, react$1.createElement)("span", { className: "dtl-trend__value" }, formatCount(value)) : null);
54225
+ };
54226
+ 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", {
54227
+ from: bucket.from,
54228
+ to: bucket.to
54229
+ })), ...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])}`))), (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)));
54230
+ const first = buckets[0];
54231
+ const partial = trend.coverageFrom !== "" && trend.coverageFrom > first.from ? (0, react$1.createElement)("div", { className: "dtl-trend__foot" }, t("chartPartial", { from: trend.coverageFrom })) : null;
54232
+ 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", {
54233
+ type: "checkbox",
54234
+ checked: mineOnly,
54235
+ onChange: (event) => {
54236
+ setMineOnly(event.target.checked);
54237
+ }
54238
+ }), 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);
54239
+ }
54240
+ //#endregion
54113
54241
  //#region src/client/board-home.ts
54114
54242
  /**
54115
54243
  * 看板首页(`main` 槽整页内容):上 = 8 统计卡 + 趋势图位(**留空**);下 = 单区任务列表。
@@ -54166,16 +54294,6 @@ body[data-ds-dark-theme]{
54166
54294
  * 列数写死 4 → 2(媒体查询在 ui.ts 的 .dtl-stats / .dtl-stats__cards 里),
54167
54295
  * 绝不给 3 列——3 列会排出 3+1 的非对称(用户 2026-10-17:必须上下对称)。 */
54168
54296
  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
54297
  /** 列表段头:滑块居左、过滤三件居右(用户 2026-10-28 去掉左侧标题)。 */
54180
54298
  const regionHeadRowStyle = {
54181
54299
  display: "flex",
@@ -54260,7 +54378,6 @@ body[data-ds-dark-theme]{
54260
54378
  };
54261
54379
  }, [state.board, state.status]);
54262
54380
  const board = state.board;
54263
- const stats = board?.stats;
54264
54381
  const view = (0, react$1.useMemo)(() => selectView(state), [state]);
54265
54382
  const degraded = board?.degraded ?? [];
54266
54383
  /** 工作区下拉候选:宿主里所有含台账的工作区(不是从任务里反推,空工作区也要能选)。 */
@@ -54316,14 +54433,18 @@ body[data-ds-dark-theme]{
54316
54433
  to
54317
54434
  })
54318
54435
  });
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"))));
54436
+ /** 左 8 块(2×4)+ 右趋势图。 */
54437
+ const statsGrid = (payload) => {
54438
+ const view = payload.stats;
54439
+ return (0, react$1.createElement)("div", {
54440
+ className: "dtl-stats",
54441
+ style: statsRowStyle
54442
+ }, (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, {
54443
+ trend: payload.trend,
54444
+ me: Object.values(payload.identities).find((uuid) => uuid !== null) ?? null,
54445
+ t
54446
+ }));
54447
+ };
54327
54448
  /** 段头:六档菜单居左,工作区 / 执行人 / 搜索居右(用户 2026-10-28:不要左侧标题)。 */
54328
54449
  const regionHead = (0, react$1.createElement)("div", { style: regionHeadRowStyle }, (0, react$1.createElement)(Segmented, {
54329
54450
  id: "dtl-list-tab",
@@ -54401,8 +54522,8 @@ body[data-ds-dark-theme]{
54401
54522
  onClick: onBack
54402
54523
  }, `← ${t("backToConversation")}`), (0, react$1.createElement)("div", { style: titleStyle }, t("panelTitle"), (0, react$1.createElement)("span", {
54403
54524
  style: buildTagStyle,
54404
- title: `v0.0.115 · 261029-121428-122`
54405
- }, `v0.0.115 261029-121428-122`))), (0, react$1.createElement)(Button, {
54525
+ title: `v0.0.116 · 261029-132126-295`
54526
+ }, `v0.0.116 261029-132126-295`))), (0, react$1.createElement)(Button, {
54406
54527
  variant: "outline",
54407
54528
  size: "lg",
54408
54529
  icon: (0, react$1.createElement)(_deepseek_ai_dsh_client_ui_primitives.IconRefreshOutlineRegular, { size: 16 }),
@@ -54424,7 +54545,7 @@ body[data-ds-dark-theme]{
54424
54545
  onClick: () => {
54425
54546
  refresh();
54426
54547
  }
54427
- }, t("retry"))) : null, stats === void 0 ? null : statsGrid(stats), degraded.length === 0 ? null : (0, react$1.createElement)("div", { style: {
54548
+ }, t("retry"))) : null, board === null ? null : statsGrid(board), degraded.length === 0 ? null : (0, react$1.createElement)("div", { style: {
54428
54549
  display: "flex",
54429
54550
  alignItems: "flex-start",
54430
54551
  gap: "var(--dtl-space-2)",
@@ -54487,7 +54608,12 @@ body[data-ds-dark-theme]{
54487
54608
  statLastWeekDone: "上周完成",
54488
54609
  daysNone: "—",
54489
54610
  daysValue: "{n} 天",
54490
- chartNotReady: "趋势图待接:skill 还没有按周汇总的方法",
54611
+ chartDone: "完成",
54612
+ chartOpen: "未结",
54613
+ chartAdded: "新增",
54614
+ chartEmpty: "还没有可统计的记录",
54615
+ chartPartial: "{from} 起才有台账数据",
54616
+ chartMine: "只看我的",
54491
54617
  periodRange: "{from} ~ {to}",
54492
54618
  statusLabel: "状态",
54493
54619
  statusActive: "进行中",
@@ -54568,7 +54694,12 @@ body[data-ds-dark-theme]{
54568
54694
  statLastWeekDone: "Done last week",
54569
54695
  daysNone: "—",
54570
54696
  daysValue: "{n}d",
54571
- chartNotReady: "Trend chart pending: the skill has no weekly roll-up method yet",
54697
+ chartDone: "Done",
54698
+ chartOpen: "Open",
54699
+ chartAdded: "Added",
54700
+ chartEmpty: "No records to chart yet",
54701
+ chartPartial: "Ledger data starts {from}",
54702
+ chartMine: "Only mine",
54572
54703
  periodRange: "{from} ~ {to}",
54573
54704
  statusLabel: "Status",
54574
54705
  statusActive: "Active",