dsh-task-ledger 0.0.52 → 0.0.54
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 +30 -44
- package/dist/client.js.map +1 -1
- package/dist/host/services/home.service.d.ts +4 -2
- package/dist/host/services/home.service.js +17 -8
- package/dist/host/types.d.ts +14 -10
- package/package.json +1 -1
- package/skill/README.md +6 -8
- package/skill/SKILL.md +11 -8
- package/skill/assets/worklog.md +7 -0
- package/skill/package.json +1 -1
- package/skill/scripts/progress.py +132 -33
package/dist/client.js
CHANGED
|
@@ -177,7 +177,7 @@ window.__ModuleLoader__.load({
|
|
|
177
177
|
if (needle === "") return true;
|
|
178
178
|
return parts.some((part) => part?.toLowerCase().includes(needle) === true);
|
|
179
179
|
}
|
|
180
|
-
/** 「我的」判定:owner = 该工作区的当前 git
|
|
180
|
+
/** 「我的」判定:owner = 该工作区的当前 git 身份,且状态是进行中(blocked 是旧数据,照收;与 host 同口径)。 */
|
|
181
181
|
function isMine(task, board) {
|
|
182
182
|
if (board === null) return false;
|
|
183
183
|
const identity = board.identities[task.workspace.id] ?? null;
|
|
@@ -207,10 +207,10 @@ window.__ModuleLoader__.load({
|
|
|
207
207
|
if (status === "all") return true;
|
|
208
208
|
if (status === "active") return task.status === "doing" || task.status === "blocked";
|
|
209
209
|
if (status === "open") return task.status === "open";
|
|
210
|
-
if (status === "
|
|
210
|
+
if (status === "overdue") return task.overdue === true;
|
|
211
211
|
return task.stale === true;
|
|
212
212
|
}
|
|
213
|
-
/**
|
|
213
|
+
/** 组内排序:别人的进行中在前、待认领在后;各自按创建 / 开始时间倒序。 */
|
|
214
214
|
function inGroupOrder(a, b) {
|
|
215
215
|
if (a.kind !== "task" || b.kind !== "task") return 0;
|
|
216
216
|
const rank = (task) => task.status === "open" ? 1 : 0;
|
|
@@ -234,7 +234,7 @@ window.__ModuleLoader__.load({
|
|
|
234
234
|
total: 0,
|
|
235
235
|
doing: 0,
|
|
236
236
|
open: 0,
|
|
237
|
-
|
|
237
|
+
overdue: 0,
|
|
238
238
|
stale: 0
|
|
239
239
|
}
|
|
240
240
|
};
|
|
@@ -243,7 +243,7 @@ window.__ModuleLoader__.load({
|
|
|
243
243
|
total: allTasks.length,
|
|
244
244
|
doing: allTasks.filter((task) => task.status === "doing").length,
|
|
245
245
|
open: allTasks.filter((task) => task.status === "open").length,
|
|
246
|
-
|
|
246
|
+
overdue: allTasks.filter((task) => task.overdue === true).length,
|
|
247
247
|
stale: allTasks.filter((task) => task.stale === true).length
|
|
248
248
|
};
|
|
249
249
|
const mineRows = board.tasks.filter((task) => isMine(task, board)).filter((task) => query.workspace === "" || task.workspace.id === query.workspace).filter((task) => hitKeyword(query.keyword, [
|
|
@@ -278,7 +278,7 @@ window.__ModuleLoader__.load({
|
|
|
278
278
|
workspaceId,
|
|
279
279
|
title: entry.title,
|
|
280
280
|
doingCount: entry.rows.filter((row) => row.kind === "task" && row.task.status === "doing").length,
|
|
281
|
-
|
|
281
|
+
overdueCount: entry.rows.filter((row) => row.kind === "task" && row.task.overdue === true).length,
|
|
282
282
|
staleCount: entry.rows.filter((row) => row.kind === "task" && row.task.stale === true).length,
|
|
283
283
|
openCount: entry.rows.filter((row) => row.kind === "task" && row.task.status === "open").length,
|
|
284
284
|
total: entry.rows.length,
|
|
@@ -643,9 +643,9 @@ body[data-ds-dark-theme]{
|
|
|
643
643
|
/* 列:任务(弹性)/ 执行人 / 右侧槽(创建信息 ⇄ 操作钮) */
|
|
644
644
|
.dtl-tcell{min-width:0;}
|
|
645
645
|
.dtl-tcell--title{flex:1 1 auto;}
|
|
646
|
-
/* 执行人列(原状态列,用户 2026-10-18
|
|
647
|
-
|
|
648
|
-
.dtl-tcell--status{flex:none;width:
|
|
646
|
+
/* 执行人列(原状态列,用户 2026-10-18 互换):稍放宽一档(用户 2026-10-19:原来有点挤),
|
|
647
|
+
20 字的名字能基本摆全;再长由标签自己的省略号兜住,不许溢出压到右边的槽 */
|
|
648
|
+
.dtl-tcell--status{flex:none;width:136px;}
|
|
649
649
|
.dtl-tcell--status .dtl-tag{max-width:100%;}
|
|
650
650
|
/* 悬停:**创建信息整条让位给操作钮**(用户 2026-10-18:不要「掉掉时分秒」那种半吊子,
|
|
651
651
|
要整条换掉)。两个都**常驻布局**(visibility 切换、不用 display),格子宽度 = 两者较宽的
|
|
@@ -712,8 +712,10 @@ body[data-ds-dark-theme]{
|
|
|
712
712
|
.dtl-taskrow__row:hover .dtl-taskrow__when{display:none;}
|
|
713
713
|
.dtl-taskrow__row:hover .dtl-taskrow__actions{display:inline-flex;}
|
|
714
714
|
|
|
715
|
-
/* 标签(用户 2026-10-18):状态 / 执行人 / #标签
|
|
716
|
-
最大宽度 20ch(≈10 个中文或 20
|
|
715
|
+
/* 标签(用户 2026-10-18):状态 / 执行人 / #标签 **共用一枚件**,只换色调。
|
|
716
|
+
最大宽度 20ch(≈10 个中文或 20 个字符)⇒ 名字上限 20 字;超出**省略号(3 个点)**,
|
|
717
|
+
悬停把全名补出来(用户 2026-10-19:不跑马灯了)。**没被截 ⇒ 悬停无反应**
|
|
718
|
+
(提示由 Tag 元件按实际宽度决定挂不挂,这里只管裁切)。 */
|
|
717
719
|
.dtl-tag{display:inline-block;flex:none;max-width:20ch;padding:0 6px;border-radius:999px;
|
|
718
720
|
background:var(--dtl-chip-bg);color:var(--dtl-fg-3);font-size:10px;line-height:16px;
|
|
719
721
|
white-space:nowrap;overflow:hidden;text-overflow:ellipsis;vertical-align:middle;}
|
|
@@ -726,13 +728,6 @@ body[data-ds-dark-theme]{
|
|
|
726
728
|
.dtl-tag--stale{background:var(--dtl-danger);color:var(--dtl-on-signal);font-weight:600;}
|
|
727
729
|
/* 执行人:浅蓝底(与状态色区分:状态是「事怎么样」,人是「谁在做」) */
|
|
728
730
|
.dtl-tag--person{background:var(--dtl-open-bg-soft);color:var(--dtl-fg-2);}
|
|
729
|
-
/* 内层撑满真实宽度(inline-block ⇒ transform 才生效),外层负责裁切与省略号 */
|
|
730
|
-
.dtl-tag__text{display:inline-block;}
|
|
731
|
-
/* 跑马灯只挂在**真溢出**的标签上(短的挂上去会白飘一段) */
|
|
732
|
-
.dtl-tag--long:hover{text-overflow:clip;}
|
|
733
|
-
.dtl-tag--long:hover .dtl-tag__text{animation:dtl-marquee 6s linear infinite;}
|
|
734
|
-
@keyframes dtl-marquee{0%,12%{transform:translateX(0);}
|
|
735
|
-
88%,100%{transform:translateX(calc(-100% + 20ch - 12px));}}
|
|
736
731
|
|
|
737
732
|
/* 分区标题里的计数:与日期徽同款中括号小字(用户 2026-10-17);0 条不渲染 */
|
|
738
733
|
.dtl-count{font-size:var(--dtl-font-xs);color:var(--dtl-fg-3);font-variant-numeric:tabular-nums;}
|
|
@@ -935,16 +930,17 @@ body[data-ds-dark-theme]{
|
|
|
935
930
|
}
|
|
936
931
|
/**
|
|
937
932
|
* 标签(**唯一**一枚件,用户 2026-10-18):状态 / 执行人 / #标签 都走它,只换 tone。
|
|
938
|
-
*
|
|
933
|
+
* 超过 20 个字符宽 ⇒ 省略号,**且只在被截时**挂悬停提示补出全文(用户 2026-10-19:
|
|
934
|
+
* 不跑马灯;没被截 ⇒ 悬停无反应)。
|
|
939
935
|
*/
|
|
940
936
|
function Tag(props) {
|
|
941
937
|
ensureUiBase();
|
|
942
938
|
const { text, tone = "neutral", title } = props;
|
|
943
939
|
const long = displayWidth(text) > TAG_MAX_WIDTH;
|
|
944
940
|
return (0, react.createElement)("span", {
|
|
945
|
-
className: `dtl-tag dtl-tag--${tone}
|
|
946
|
-
title: long ?
|
|
947
|
-
},
|
|
941
|
+
className: `dtl-tag dtl-tag--${tone}`,
|
|
942
|
+
title: long ? title ?? text : void 0
|
|
943
|
+
}, text);
|
|
948
944
|
}
|
|
949
945
|
/** 空态:`guide` = 真空引导卡(虚线),`filtered` = 筛选空小字。 */
|
|
950
946
|
function EmptyState(props) {
|
|
@@ -1100,24 +1096,14 @@ body[data-ds-dark-theme]{
|
|
|
1100
1096
|
const title = row.record.workpackage ?? row.record.summary;
|
|
1101
1097
|
return title !== void 0 && title !== "" ? title : row.record.src ?? "—";
|
|
1102
1098
|
}
|
|
1103
|
-
/** 状态语义(异常优先:stale 盖过 blocked
|
|
1099
|
+
/** 状态语义(异常优先:stale 盖过 blocked;blocked 是 host 按 7 天算出来的,不是状态)。 */
|
|
1104
1100
|
function toneOf(row) {
|
|
1105
1101
|
if (row.kind !== "task") return "closed";
|
|
1106
1102
|
if (row.task.stale === true) return "stale";
|
|
1107
|
-
if (row.task.
|
|
1103
|
+
if (row.task.overdue === true) return "blocked";
|
|
1108
1104
|
if (row.task.status === "open") return "open";
|
|
1109
1105
|
return "doing";
|
|
1110
1106
|
}
|
|
1111
|
-
/**
|
|
1112
|
-
* ⚠️ 调试态(用户 2026-10-19):创建人 / 执行人(GitHub 账号)的名字**补到 20 个字符**,
|
|
1113
|
-
* 好看清「满 20 字」时标签的截断 + 悬停跑马灯到底是什么效果。
|
|
1114
|
-
* **真实数据一个字没动** —— 只在渲染这一刻把名字补长(mock 仅此一处,用 `-` 当填充,一眼看得出是假的)。
|
|
1115
|
-
* 定稿后:删掉本常量与 `debugName()`,两处调用换回原始名字。
|
|
1116
|
-
*/
|
|
1117
|
-
const DEBUG_NAME_CHARS = 20;
|
|
1118
|
-
function debugName(name) {
|
|
1119
|
-
return name.length >= DEBUG_NAME_CHARS ? name : name + "-".repeat(DEBUG_NAME_CHARS - name.length);
|
|
1120
|
-
}
|
|
1121
1107
|
/** 状态文案(已结案没有在办状态 ⇒ 不给标签)。 */
|
|
1122
1108
|
function statusLabel(tone, t) {
|
|
1123
1109
|
if (tone === "stale") return t("statusStale");
|
|
@@ -1184,11 +1170,11 @@ body[data-ds-dark-theme]{
|
|
|
1184
1170
|
const creator = isTask ? row.task.creator : row.record.owner;
|
|
1185
1171
|
const owner = isTask ? row.task.owner : null;
|
|
1186
1172
|
const hasOwner = !(owner === null || owner === void 0 || owner === "");
|
|
1187
|
-
const who = creator === null || creator === void 0 || creator === "" ? "—" : `@${
|
|
1173
|
+
const who = creator === null || creator === void 0 || creator === "" ? "—" : `@${creator}`;
|
|
1188
1174
|
const createdOk = created !== null && created !== void 0 && Date.parse(created) >= 0;
|
|
1189
1175
|
const startedOk = isTask && row.task.started !== null && row.task.started !== void 0 && Date.parse(row.task.started) >= 0;
|
|
1190
1176
|
const ownerTip = t("startedAt", {
|
|
1191
|
-
who:
|
|
1177
|
+
who: String(owner),
|
|
1192
1178
|
when: startedOk ? formatDateTime(String(row.task.started)) : t("timeUnknown")
|
|
1193
1179
|
});
|
|
1194
1180
|
const tagLine = (0, react.createElement)("div", { className: "dtl-orow__tags" }, statusTag, tags.map((tag) => (0, react.createElement)(Tag, {
|
|
@@ -1196,7 +1182,7 @@ body[data-ds-dark-theme]{
|
|
|
1196
1182
|
text: `#${tag}`
|
|
1197
1183
|
})));
|
|
1198
1184
|
return (0, react.createElement)("div", { className: "dtl-orow" }, (0, react.createElement)("div", { className: "dtl-orow__row" }, (0, react.createElement)("span", { className: `dtl-dot dtl-dot--${tone}` }), (0, react.createElement)("div", { className: "dtl-tcell dtl-tcell--title" }, orowTitleLine, tagLine), (0, react.createElement)("span", { className: "dtl-tcell dtl-tcell--status" }, hasOwner ? (0, react.createElement)(Tag, {
|
|
1199
|
-
text: `@${
|
|
1185
|
+
text: `@${String(owner)}`,
|
|
1200
1186
|
tone: "person",
|
|
1201
1187
|
title: ownerTip
|
|
1202
1188
|
}) : null), (0, react.createElement)("span", { className: "dtl-orow__slot" }, (0, react.createElement)("span", { className: "dtl-orow__created" }, t("createdAt", {
|
|
@@ -1388,11 +1374,11 @@ body[data-ds-dark-theme]{
|
|
|
1388
1374
|
}, state.refreshing ? t("refreshing") : t("refresh")))), tab === "stats" ? (0, react.createElement)("div", { style: hintStyle }, t("statsPlaceholder")) : (0, react.createElement)("div", null, stats === void 0 ? null : (0, react.createElement)("div", {
|
|
1389
1375
|
className: "dtl-stats",
|
|
1390
1376
|
style: statsRowStyle
|
|
1391
|
-
}, (0, react.createElement)("div", { className: "dtl-stats__cards" }, card("statMyActive", stats.myActive, t("statMyActiveHint", { n: stats.
|
|
1377
|
+
}, (0, react.createElement)("div", { className: "dtl-stats__cards" }, card("statMyActive", stats.myActive, t("statMyActiveHint", { n: stats.myOverdue })), card("statOpen", stats.open, t("statOpenHint", { n: days(stats.oldestOpenDays) })), card("statDoing", stats.doing, t("statDoingHint", { n: stats.doing - stats.myActive })), (0, react.createElement)(StatCard, {
|
|
1392
1378
|
label: t("statBlockedStale"),
|
|
1393
|
-
value: stats.
|
|
1379
|
+
value: stats.overdue + stats.stale,
|
|
1394
1380
|
hint: t("statBlockedSplit", {
|
|
1395
|
-
blocked: stats.
|
|
1381
|
+
blocked: stats.overdue,
|
|
1396
1382
|
stale: stats.stale
|
|
1397
1383
|
}),
|
|
1398
1384
|
title: t("statStaleTitle", { n: stats.staleDays }),
|
|
@@ -1526,9 +1512,9 @@ body[data-ds-dark-theme]{
|
|
|
1526
1512
|
badge: orZero(view.allCounts.open)
|
|
1527
1513
|
},
|
|
1528
1514
|
{
|
|
1529
|
-
value: "
|
|
1515
|
+
value: "overdue",
|
|
1530
1516
|
label: t("statusBlocked"),
|
|
1531
|
-
badge: orZero(view.allCounts.
|
|
1517
|
+
badge: orZero(view.allCounts.overdue)
|
|
1532
1518
|
},
|
|
1533
1519
|
{
|
|
1534
1520
|
value: "stale",
|
|
@@ -1586,7 +1572,7 @@ body[data-ds-dark-theme]{
|
|
|
1586
1572
|
t("groupTotal", { n: group.total }),
|
|
1587
1573
|
t("groupOpen", { n: group.openCount }),
|
|
1588
1574
|
t("groupDoing", { n: group.doingCount }),
|
|
1589
|
-
...group.
|
|
1575
|
+
...group.overdueCount > 0 ? [t("groupBlocked", { n: group.overdueCount })] : [],
|
|
1590
1576
|
...group.staleCount > 0 ? [t("groupStale", { n: group.staleCount })] : []
|
|
1591
1577
|
].join(" | ");
|
|
1592
1578
|
return (0, react.createElement)("div", { className: "dtl-group" }, (0, react.createElement)("button", {
|
|
@@ -1859,7 +1845,7 @@ body[data-ds-dark-theme]{
|
|
|
1859
1845
|
* 定位用宿主变量 `--dsh-frame-overlay-top`(标题栏净空 + 20px),**不写死 top 值**;
|
|
1860
1846
|
* 浮层默认 `pointer-events:none`、子元素自动恢复 ⇒ 自绘条目不挡下方应用。
|
|
1861
1847
|
*
|
|
1862
|
-
* 角标 =
|
|
1848
|
+
* 角标 = 我名下进行中计数(host 统计真值,SSE 已否 ⇒ 取自同一份快照,>0 才画);
|
|
1863
1849
|
* 0→n 跳变时一次脉冲动画,不自动消失。宿主**无**通知 API ⇒ 「通知」就是这个角标。
|
|
1864
1850
|
*/
|
|
1865
1851
|
function OverlayEntry(props) {
|