dsh-todo-time-log 0.6.0 → 0.6.2
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/README.md +192 -535
- package/lib/client.js +924 -104
- package/lib/index.js +133 -9
- package/lib/store.js +447 -0
- package/lib/transfer.js +573 -0
- package/package.json +18 -6
package/README.md
CHANGED
|
@@ -1,403 +1,160 @@
|
|
|
1
|
-
|
|
1
|
+
<p align="center">
|
|
2
|
+
<a href="https://www.npmjs.com/package/dsh-todo-time-log">
|
|
3
|
+
<img src="https://github.com/FantasyStarry/dsh-todo-time-log/raw/main/docs/images/banner.png" alt="待办时间日志 · dsh-todo-time-log" width="100%">
|
|
4
|
+
</a>
|
|
5
|
+
</p>
|
|
2
6
|
|
|
3
|
-
|
|
7
|
+
<p align="center">
|
|
8
|
+
<a href="https://www.npmjs.com/package/dsh-todo-time-log"><img alt="npm version" src="https://img.shields.io/npm/v/dsh-todo-time-log?color=4f46e5&label=npm"></a>
|
|
9
|
+
<a href="https://www.npmjs.com/package/dsh-todo-time-log"><img alt="npm downloads" src="https://img.shields.io/npm/dm/dsh-todo-time-log?color=4f46e5"></a>
|
|
10
|
+
<a href="https://github.com/FantasyStarry/dsh-todo-time-log/actions/workflows/ci.yml"><img alt="CI" src="https://github.com/FantasyStarry/dsh-todo-time-log/actions/workflows/ci.yml/badge.svg"></a>
|
|
11
|
+
<a href="https://github.com/FantasyStarry/dsh-todo-time-log/blob/main/LICENSE"><img alt="license" src="https://img.shields.io/badge/license-MIT-blue.svg"></a>
|
|
12
|
+
<img alt="DSH" src="https://img.shields.io/badge/DSH-0.2.x-4f46e5.svg">
|
|
13
|
+
<img alt="node" src="https://img.shields.io/badge/node-%E2%89%A5%2020-3c873a.svg">
|
|
14
|
+
</p>
|
|
4
15
|
|
|
5
|
-
|
|
6
|
-
| --- | --- |
|
|
7
|
-
| 插件 ID / 包名 | `dsh-todo-time-log` |
|
|
8
|
-
| 兼容性 | DSH `>=0.2.0-rc.2 <0.2.3-0`(`engines.dsh` 声明;已在 DSH **0.2.2-rc.1** 真机验证,见 §7 九期记录) |
|
|
9
|
-
| 中文显示名 | 待办时间日志 |
|
|
10
|
-
| 短别名 | `todo-time-log`、`ttl`(文档与工具前缀用;dsh 的 Entry / Slot / Tool 标识统一用包名) |
|
|
11
|
-
| 数据文件 | `<DSH_PROFILE_DIR 或 ~/.dsh>/todo-time-log/todos.json`(可用环境变量或行配置覆盖) |
|
|
12
|
-
| HTTP 接口 | `POST /api/dsh-todo-time-log/*`(**经宿主认证**:每条请求先过 `connection.requestRejection()`,无登录 cookie 返回 401、跨站/不可信来源返回 403) |
|
|
13
|
-
| 智能体工具 | `todo_time_log`(action = list / get / create / complete / uncomplete / update / delete / health) |
|
|
14
|
-
| UI 位置 | 中央面板 `main`(key = `dsh-todo-time-log`)+ 侧边栏图标 `sidebar.panellist`(id 同名,点击打开面板) |
|
|
15
|
-
| 视图 | 四视图:**表格 / 看板 / 甘特图 / 日历**(甘特条 = 创建 → 完成,未完成延伸到「现在」;四视图内均可完成/编辑/删除) |
|
|
16
|
-
| 项目分类 | 每条待办可带 `project` 分类;支持分类筛选、分类分组、分类统计;**`@文件` 时自动取该文件所在目录作为分类** |
|
|
17
|
-
| 界面层级 | 页头(标题 + 本地时间)→ **新建待办**(整页唯一主操作)→ **数据卡片**(四位视图分段控件 + 筛选计数 + 列表);统计数并入筛选分段,不再单独占一张卡片 |
|
|
18
|
-
| 完成反馈 | **浮层 toast**:绝对定位、不占文档流,出现/消失都不改变页面高度;成功/失败/中性三种图标,完成后可「撤销」,可手动关闭,3.5–6 秒自动消失 |
|
|
19
|
-
| 文件引用 | 每条待办可带 `files: string[]`(工作区相对路径);对话里输入 `@` 有文件补全下拉 |
|
|
20
|
-
| 录入方式 | 表单新建、对话式录入(面板内「对话录入」:`新任务 xxx #分类 @工作区文件`)、`/todo` 斜杠命令、智能体工具 `todo_time_log`(含 `quick_add`) |
|
|
21
|
-
|
|
22
|
-
> 命名说明:dsh **不会**自动加命名空间前缀——现有插件的 Entry id、Slot id 都等于包名(例如 `dsh-context`、`dsh-cost-meter`)。因此本插件保留 `dsh-todo-time-log` 作为 ID,`todo-time-log` / `ttl` 只作为短别名。
|
|
16
|
+
<h1 align="center">待办时间日志 · dsh-todo-time-log</h1>
|
|
23
17
|
|
|
24
|
-
|
|
18
|
+
<p align="center">
|
|
19
|
+
<b>待办清单,也是一本时间账本。</b><br>
|
|
20
|
+
一个 <a href="https://github.com/deepseek-ai">DeepSeek Harness</a>(dsh)插件:管理待办的同时,<b>自动记下每条待办的创建时间与完成时间</b>,<br>
|
|
21
|
+
用表格 / 看板 / 甘特图 / 日历四种视角,一眼看清「这件事做了多久」。
|
|
22
|
+
</p>
|
|
25
23
|
|
|
26
|
-
##
|
|
24
|
+
## 为什么是它
|
|
27
25
|
|
|
28
|
-
|
|
29
|
-
dsh-todo-time-log/
|
|
30
|
-
├── package.json # 包清单:bundle patch + dsh.client + exports/files
|
|
31
|
-
├── cordis.patch.yml # Loader patch:插入一条 Host 行(id = 包名)
|
|
32
|
-
├── icon.svg # 插件图标(Plugin Manager / 设置页展示)
|
|
33
|
-
├── locale/
|
|
34
|
-
│ ├── zh.json # 显示名与描述(中文)
|
|
35
|
-
│ └── en.json # 显示名与描述(英文)
|
|
36
|
-
├── src/client/ # ★ Client 半源码(ES 模块,npm run build 用 esbuild 打包)
|
|
37
|
-
│ ├── index.js # 入口:window.__ModuleLoader__.load({ id, factory })
|
|
38
|
-
│ ├── app.js # 模块对象:main + sidebar.panellist 槽位注册
|
|
39
|
-
│ ├── panel.js # 面板主体:状态 / 请求 / 交互(不含四个视图实现)
|
|
40
|
-
│ ├── views/ # table / board / gantt / calendar 四视图
|
|
41
|
-
│ ├── widgets.js # 小组件 / 图标 / 对话录入等
|
|
42
|
-
│ ├── detail.js # 详情区
|
|
43
|
-
│ ├── helpers.js # 时间 / 格式化 / 甘特几何等纯函数
|
|
44
|
-
│ ├── i18n.js # zh / en 文案字典
|
|
45
|
-
│ ├── styles.js # CSS(--dsw-alias-* 主题 token)
|
|
46
|
-
│ ├── sources.js # /todo 输入触发来源(@ 菜单)
|
|
47
|
-
│ └── runtime.js # React 注入层(factory 里 setReact)
|
|
48
|
-
├── scripts/
|
|
49
|
-
│ └── build-client.mjs # esbuild 打包脚本:src/client/ → lib/client.js
|
|
50
|
-
├── lib/
|
|
51
|
-
│ ├── index.js # ★ Host 半:apply / inject / 单一业务入口 / HTTP 路由 / 智能体工具
|
|
52
|
-
│ ├── store.js # ★ 数据模型 + 持久化 + 修复兜底 + 跨进程写入保护(纯 Node ESM,无 dsh 依赖)
|
|
53
|
-
│ ├── time.js # ★ 时间解析 / 本地格式化 YYYY-MM-DD HH:mm:ss / 空值
|
|
54
|
-
│ ├── parse.js # ★ 统一自然语言解析(对话面板 / /todo 命令共用)
|
|
55
|
-
│ ├── files.js # ★ 工作区文件索引(ctx.fs 只读遍历 + 缓存,供 @ 补全)
|
|
56
|
-
│ └── client.js # 构建产物(勿手改;由 src/client/ 打包生成,仍是单文件模块表)
|
|
57
|
-
├── test/
|
|
58
|
-
│ ├── parse.test.mjs # 自然语言解析 / 文件路径 / 分类派生(79 断言,node:test)
|
|
59
|
-
│ ├── smoke.test.mjs # 数据层 / 时间 / 修复 / 分类 / 文件 / 迁移 / 持久化(174 断言)
|
|
60
|
-
│ ├── http.test.mjs # 真实 HTTP 路由端到端(66 断言)
|
|
61
|
-
│ ├── store-concurrency.test.mjs # 跨进程写入:mtime 合并 + 文件锁(8 用例)
|
|
62
|
-
│ ├── ui/ # jsdom + 真实 React 渲染 lib/client.js 的验收 harness(4 用例)
|
|
63
|
-
│ ├── live-utf8.py # 对运行中实例的真实 UTF-8 往返检查(需 DSH_WEB_URL)
|
|
64
|
-
│ ├── live-projects.py # 对运行中实例的分类/迁移/筛选真实往返检查
|
|
65
|
-
│ └── client-audit.mjs # client.js 静态审计(i18n 成对/死键、require、token、四视图)
|
|
66
|
-
├── docs/
|
|
67
|
-
│ ├── verification.md # 独立验证报告(另一个 agent 产出,含 PASS/FAIL/盲区)
|
|
68
|
-
│ ├── publishing.md # npm 发布流程:bootstrap / Trusted Publisher / 版本策略 / 排错
|
|
69
|
-
│ ├── research/ # 看板设计调研 + 现状审计(六期改版的依据)
|
|
70
|
-
│ │ ├── kanban-design-research.md # 15 个可借鉴模式 + 差距 + P0/P1/P2 + 反模式(带来源)
|
|
71
|
-
│ │ └── kanban-current-state.md # 代码/数据/门禁现状、风险 R1–R12、快速改进点 Q1–Q8
|
|
72
|
-
│ ├── design/kanban-plan.md # 看板改版方案与后续路线(P0 已实现 / P1 / P2)
|
|
73
|
-
│ └── preview/ # 面板渲染预览图(真实 client.js + 主题 token 的静态渲染,见其 README)
|
|
74
|
-
├── eslint.config.js # ESLint flat config(产物 lib/client.js 不直接 lint)
|
|
75
|
-
├── tsconfig.json # tsc --checkJs:Host 模块的 JSDoc 类型检查
|
|
76
|
-
├── .github/workflows/
|
|
77
|
-
│ ├── ci.yml # push/PR:check / lint / typecheck / build / test / audit(Node 20·22·24 × Linux·Windows)
|
|
78
|
-
│ └── publish.yml # 打 tag 自动发布 npm(OIDC 可信发布 + 校验 + GitHub Release)
|
|
79
|
-
├── LICENSE # MIT
|
|
80
|
-
└── README.md
|
|
81
|
-
```
|
|
26
|
+
大多数待办工具回答「还有什么没做」。这个插件还想回答另一个问题:**「这件事花了多久?」**
|
|
82
27
|
|
|
83
|
-
|
|
28
|
+
- 🕒 **时间是一等公民** —— 创建时刻、完成时刻、两者之间的真实耗时全部自动记录;甘特图里一根条,就是一条待办从创建到完成的完整生命周期。
|
|
29
|
+
- ⚡ **随手就能记** —— 面板表单、一句话对话、`/todo` 命令、让智能体直接调用工具,四个入口共用同一套解析规则,结果完全一致。
|
|
30
|
+
- 🔒 **数据在你自己的机器上** —— 本地 JSON 文件,不联网、不上传;原子写入 + 跨进程写入保护,文件损坏会自动备份并重建。
|
|
84
31
|
|
|
85
32
|
---
|
|
86
33
|
|
|
87
|
-
##
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
"
|
|
99
|
-
"
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
{
|
|
123
|
-
"version": 5,
|
|
124
|
-
"updatedAt": "2026-11-03T00:37:56.011Z",
|
|
125
|
-
"todos": [ /* Todo[] */ ]
|
|
126
|
-
}
|
|
127
|
-
```
|
|
128
|
-
|
|
129
|
-
版本迁移:`version: 1`(无 `project`)、`version: 2`(无 `files`)、`version: 3`(无 `priority`)、`version: 4`(无 `type`)都会自动迁移为内存中的 v5 记录,各自给出一次提示,下一次写入即落盘为 v5;不支持的版本号(例如 99)不会被猜测,而是归档备份后重建空列表。**注意**:v5 文件对旧版插件属于「不支持版本」(旧版会归档重建),升级前确认没有旧 Host 与本插件并存。
|
|
130
|
-
|
|
131
|
-
写入策略:**临时文件 + rename 原子替换**(Windows 下 rename 失败时回退为覆盖写),全部写操作串行化在一条 Promise 链上,保证落盘顺序与内存顺序一致。读取时逐条用 `normalizeTodo()` 校验修复;JSON 损坏备份为 `todos.json.corrupt-json-<ts>`、结构异常备份为 `.corrupt-shape-<ts>`、版本不支持备份为 `.corrupt-version-<n>-<ts>`,然后重建空列表。
|
|
132
|
-
|
|
133
|
-
### 2.3 状态机
|
|
134
|
-
|
|
135
|
-
```text
|
|
136
|
-
(新建) --create--> pending --complete--> completed
|
|
137
|
-
^ |
|
|
138
|
-
+------ uncomplete ------+ (清空 completedAt,并提示)
|
|
139
|
-
```
|
|
140
|
-
|
|
141
|
-
重复操作(对已完成再 complete / 对未完成再 uncomplete)**不写盘**,返回结构化错误码 `ALREADY_COMPLETED` / `ALREADY_PENDING` 与中文说明。
|
|
142
|
-
|
|
143
|
-
### 2.4 时间约定
|
|
144
|
-
|
|
145
|
-
- 存储:ISO-8601 UTC(跨时区、跨 DST 无歧义)。
|
|
146
|
-
- 展示:本地时间 `YYYY-MM-DD HH:mm:ss`;`null` / 空串 / 非法值一律 `-`。
|
|
147
|
-
- 排序:按绝对时间比较;`completedAt` 排序时未完成项(空值)**始终排在最后**,升降序都一样。
|
|
34
|
+
## 界面速览
|
|
35
|
+
|
|
36
|
+
四种视图共用同一张数据卡片,切换视图不会丢掉当前的筛选与排序。
|
|
37
|
+
|
|
38
|
+
<p align="center">
|
|
39
|
+
<img src="https://github.com/FantasyStarry/dsh-todo-time-log/raw/main/docs/images/hero.png" alt="表格视图:创建时间与完成时间一目了然" width="100%">
|
|
40
|
+
<br>
|
|
41
|
+
<sub><b>表格视图</b>:「创建时间 / 完成时间」两列是主角,完成时间未产生时显示 <code>-</code></sub>
|
|
42
|
+
</p>
|
|
43
|
+
|
|
44
|
+
<p align="center">
|
|
45
|
+
<img src="https://github.com/FantasyStarry/dsh-todo-time-log/raw/main/docs/images/board.png" alt="看板视图" width="49%">
|
|
46
|
+
<img src="https://github.com/FantasyStarry/dsh-todo-time-log/raw/main/docs/images/board-swimlanes.png" alt="状态 × 分类泳道" width="49%">
|
|
47
|
+
<br>
|
|
48
|
+
<sub><b>看板</b>:按状态分列,卡片带优先级、类型与用时</sub>
|
|
49
|
+
|
|
50
|
+
<sub><b>泳道</b>:状态 × 分类分组,泳道可折叠、可在内新建</sub>
|
|
51
|
+
</p>
|
|
52
|
+
|
|
53
|
+
<p align="center">
|
|
54
|
+
<img src="https://github.com/FantasyStarry/dsh-todo-time-log/raw/main/docs/images/gantt.png" alt="甘特图视图" width="100%">
|
|
55
|
+
<br>
|
|
56
|
+
<sub><b>甘特图</b>:一根条 = 创建 → 完成,未完成延伸到「现在」并标「进行中」;可按分类分组、切换近 7 / 30 天</sub>
|
|
57
|
+
</p>
|
|
58
|
+
|
|
59
|
+
<p align="center">
|
|
60
|
+
<img src="https://github.com/FantasyStarry/dsh-todo-time-log/raw/main/docs/images/calendar.png" alt="日历视图" width="49%">
|
|
61
|
+
<img src="https://github.com/FantasyStarry/dsh-todo-time-log/raw/main/docs/images/dark.png" alt="暗色主题" width="49%">
|
|
62
|
+
<br>
|
|
63
|
+
<sub><b>日历</b>:按创建或完成时间落格,点某天处理当天全部条目</sub>
|
|
64
|
+
|
|
65
|
+
<sub><b>暗色主题</b>:颜色全部走宿主主题 token,随系统明暗自动切换</sub>
|
|
66
|
+
</p>
|
|
67
|
+
|
|
68
|
+
> 对话录入与完成反馈的交互过程见下方「操作示范」(静态示意图)。配图由仓库里的真实 `client.js` + 宿主真实主题 token 静态渲染,不是真机截图;生成方式见 [`docs/images/README.md`](https://github.com/FantasyStarry/dsh-todo-time-log/blob/main/docs/images/README.md)。
|
|
148
69
|
|
|
149
70
|
---
|
|
150
71
|
|
|
151
|
-
##
|
|
72
|
+
## 操作示范
|
|
152
73
|
|
|
153
|
-
###
|
|
74
|
+
### ① 新建一条待办
|
|
154
75
|
|
|
155
|
-
|
|
76
|
+
<p align="center"><img src="https://github.com/FantasyStarry/dsh-todo-time-log/raw/main/docs/images/demo-create.png" alt="新建待办操作示范" width="100%"></p>
|
|
156
77
|
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
switch (action) {
|
|
161
|
-
case 'list': /* …返回 { ok, todos, stats, notices, timeFormat } */ break;
|
|
162
|
-
case 'create': return store.create({ title: body.title });
|
|
163
|
-
case 'complete': return store.complete(body.id, { at: body.at });
|
|
164
|
-
// get / uncomplete / update / delete / health …
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
```
|
|
78
|
+
1. 在顶部输入框写标题,顺手可以打 `#工作` 指定分类、`@lib/store.js` 引用工作区文件。
|
|
79
|
+
2. 分类、优先级(高 / 中 / 低)、类型(缺陷 / 新功能 / 改进 …)都能直接点选。
|
|
80
|
+
3. 按 Enter 或点「添加」。带标记时由 Host 统一解析,**标题里不会残留 `#分类` / `@文件`**。
|
|
168
81
|
|
|
169
|
-
|
|
170
|
-
- **智能体** 走 `createTodoTool(store)`(注册在 `tools`,工具名 `todo_time_log`)。
|
|
171
|
-
|
|
172
|
-
两条路径都只是包壳:HTTP 把结果映射成 `{ok,value}` / `{ok,error}` 与状态码,工具把结果加上本地时间字段再交给 `output.render()` 生成文本。因此不存在「UI 能做的工具做不到」或两边行为漂移的问题。
|
|
173
|
-
|
|
174
|
-
### 3.2 Host 入口
|
|
175
|
-
|
|
176
|
-
```js
|
|
177
|
-
export const name = 'dsh-todo-time-log';
|
|
178
|
-
export const inject = ['webServer', 'tools']; // 缺任一服务时保持 pending,而不是崩溃
|
|
179
|
-
|
|
180
|
-
export function apply(ctx, config = {}) {
|
|
181
|
-
const dataFile = resolveDataFile(config);
|
|
182
|
-
const store = new TodoStore({ dataFile, logger: createLogger(ctx) });
|
|
183
|
-
|
|
184
|
-
ctx.effect(() => { // 载入数据 + 卸载时收尾
|
|
185
|
-
store.load();
|
|
186
|
-
return () => { store.dispose(); };
|
|
187
|
-
});
|
|
188
|
-
|
|
189
|
-
mountOn(ctx, ['webServer'], (scope) => { // 路由:供 UI 调用
|
|
190
|
-
scope.effect(() => scope.webServer.register({
|
|
191
|
-
kind: 'prefix', path: ROUTE_PREFIX,
|
|
192
|
-
handler: createHttpHandler(store, {
|
|
193
|
-
// 宿主认证:connection 缺失或抛错一律拒绝,不退回到 Host/Origin 自检
|
|
194
|
-
authorize: (req) => {
|
|
195
|
-
const connection = ctx.get('connection');
|
|
196
|
-
if (!connection) return 403;
|
|
197
|
-
return connection.requestRejection(req) ?? undefined;
|
|
198
|
-
},
|
|
199
|
-
}),
|
|
200
|
-
}));
|
|
201
|
-
});
|
|
202
|
-
|
|
203
|
-
mountOn(ctx, ['tools'], (scope) => { // 工具:供智能体调用
|
|
204
|
-
scope.effect(() => scope.tools.register(createTodoTool(store)));
|
|
205
|
-
});
|
|
206
|
-
}
|
|
207
|
-
```
|
|
82
|
+
### ② 完成 → 自动记录 → 可撤销
|
|
208
83
|
|
|
209
|
-
|
|
84
|
+
<p align="center"><img src="https://github.com/FantasyStarry/dsh-todo-time-log/raw/main/docs/images/demo-complete.png" alt="完成与撤销操作示范" width="100%"></p>
|
|
210
85
|
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
| 完成时间早于创建时间 | 修正为创建时间,提示「完成时间早于创建时间,已修正为创建时间」 |
|
|
215
|
-
| 已完成但没有完成时间 | 回退为创建时间,并提示 |
|
|
216
|
-
| 未完成却带完成时间 | 清空完成时间,并提示 |
|
|
217
|
-
| 重复标记完成 / 重复取消完成 | 不落盘,返回 `ALREADY_COMPLETED` / `ALREADY_PENDING` + 中文说明 |
|
|
218
|
-
| 标题为空 / 全空白 / 超 200 字符 | 返回 `TITLE_REQUIRED` / `TITLE_TOO_LONG`(HTTP 400) |
|
|
219
|
-
| 待办不存在 / 已删除 | 返回 `NOT_FOUND`(HTTP 404) |
|
|
220
|
-
| 数据文件 JSON 损坏 / 结构异常 / 版本不支持 | 分别备份为 `.corrupt-json-<ts>` / `.corrupt-shape-<ts>` / `.corrupt-version-<n>-<ts>`,重建空列表并提示 |
|
|
221
|
-
| 分类非字符串 / 超 40 字符 | 返回 `PROJECT_INVALID` / `PROJECT_TOO_LONG`(HTTP 400) |
|
|
222
|
-
| 旧版本 v1 数据 | 自动迁移为 v2(`project: ''`)并提示,下次写入落盘 |
|
|
223
|
-
| 磁盘写入失败 | 内存变更保留、提示「数据已更新但写入磁盘失败,将在下次写入时重试」,并在 `/health` 的 `lastPersistError` 暴露 |
|
|
224
|
-
| 待办数量超上限(5000) | 返回 `TOO_MANY_TODOS`(HTTP 409) |
|
|
225
|
-
|
|
226
|
-
> 修复/异常提示只投递一次:`TodoStore` 用「pending 队列」把载入期修复提示交给下一次 `list()`,诊断历史仍保留在 `describe().loadNotices`,避免每次刷新都刷提示。
|
|
227
|
-
|
|
228
|
-
### 3.4 Client 半(槽位注册)
|
|
229
|
-
|
|
230
|
-
```js
|
|
231
|
-
window.__ModuleLoader__.load({
|
|
232
|
-
id: 'dsh-todo-time-log',
|
|
233
|
-
factory(require) {
|
|
234
|
-
const React = require('react');
|
|
235
|
-
// …组件、i18n、HTTP helper…
|
|
236
|
-
return {
|
|
237
|
-
inject: ['slots'],
|
|
238
|
-
apply(ctx) {
|
|
239
|
-
ctx.slots.inject('main', () => ctx.slots.register(
|
|
240
|
-
{ name: 'main', key: 'dsh-todo-time-log' }, Panel)); // 中央面板
|
|
241
|
-
ctx.slots.inject('sidebar.panellist', () => ctx.slots.register(
|
|
242
|
-
{ name: 'sidebar.panellist', id: 'dsh-todo-time-log', order: 30,
|
|
243
|
-
label: () => '待办时间日志' }, Icon)); // 侧边栏入口
|
|
244
|
-
},
|
|
245
|
-
};
|
|
246
|
-
},
|
|
247
|
-
});
|
|
248
|
-
```
|
|
249
|
-
|
|
250
|
-
侧边栏 list 槽的 id 与 `main` keyed 槽的 key 相同,宿主据此把点击事件路由到本面板(与官方 `dsh-context` 插件一致的模式)。面板样式只用 `--dsw-alias-*` 主题 token、类名统一 `ttl-` 前缀,随宿主明暗主题切换。
|
|
251
|
-
|
|
252
|
-
**源码与产物**:上图的单文件模块表是构建产物 `lib/client.js`,源码在 `src/client/`(入口 / 面板 / 四视图 / 小组件 / i18n / 样式等 ES 模块)。`npm run build` 用 esbuild 打包回同一形态(IIFE、`charset=utf8`、不 minify、不打包 react),发布物与加载方式都不变;`lib/client.js` 不要手改。
|
|
86
|
+
1. 点行内的「完成」(看板里是卡片上的 checkbox),完成时刻自动写入。
|
|
87
|
+
2. 底部浮出 toast,「撤销」可以一键退回未完成状态。
|
|
88
|
+
3. 表格的「完成时间」列立即出现时间;页面高度不变,视线不被打断。
|
|
253
89
|
|
|
254
|
-
###
|
|
90
|
+
### ③ 一句话记多条
|
|
255
91
|
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
| 规则 | 实现 |
|
|
259
|
-
| --- | --- |
|
|
260
|
-
| 条形区间 | `start = createdAt`,`end = completedAt ?? now`;未完成条形延伸到「现在」,右缘虚线 + 「进行中」标记 |
|
|
261
|
-
| 时间域 | 所有条形 `[min(start), max(end)]`,两端各留 5% 余量;零跨度(全部同一时刻)自动左右各扩展 30 分钟,避免除零 |
|
|
262
|
-
| 刻度 | 依据总跨度自动选步长(分钟 → 小时 → 天),目标 6–10 个刻度;标签 `MM-DD HH:mm`,跨度 > 30 天时用 `MM-DD` |
|
|
263
|
-
| 「现在」线 | 时间域包含当前时刻时绘制竖线 + 标签 |
|
|
264
|
-
| 范围筛选 | 全部 / 近 7 天 / 近 30 天:保留 `[start, end]` 与窗口相交的条目 |
|
|
265
|
-
| 分组 | 按项目分组,组标题显示分类名/未分类与条数 |
|
|
266
|
-
| 兜底 | `createdAt` 不可解析 → 跳过该条;`completedAt` 非法或早于 start → 按「现在」兜底 |
|
|
267
|
-
| 无障碍 | 条形 `role="img"` + `aria-label`(分类/标题/创建/完成或进行中)+ `title` 悬浮提示 |
|
|
268
|
-
| 行内操作 | 左侧标签是 `<button>`(`aria-expanded`/`aria-controls`),点击标签或整行展开行内详情:分类/状态/创建/完成/用时/id + 完成/取消完成、编辑(标题+分类)、删除(二次确认);展开状态按 id 记录并与表格视图独立 |
|
|
92
|
+
<p align="center"><img src="https://github.com/FantasyStarry/dsh-todo-time-log/raw/main/docs/images/demo-quickadd.png" alt="对话式快速录入操作示范" width="100%"></p>
|
|
269
93
|
|
|
270
|
-
|
|
94
|
+
1. 打开「对话录入」,先确认工作区 —— `@` 文件补全按工作区隔离。
|
|
95
|
+
2. 一句话可以写多条,支持 `#分类`、`@文件`、`!优先级`、`^类型`。
|
|
96
|
+
3. 发送后逐条回执;不满意可以整批撤销。
|
|
271
97
|
|
|
272
|
-
|
|
273
|
-
- HTTP / 工具契约(与 UI 共用同一实现 `executeTodoAction`):
|
|
98
|
+
### ④ 看板上的日常操作
|
|
274
99
|
|
|
275
|
-
|
|
276
|
-
| --- | --- | --- | --- |
|
|
277
|
-
| 列表 | `{filter?, sortBy?, direction?, project?}` | `action=list` + 同名字段 | `project` 省略 = 全部;`''` = 只看未分类;`sortBy` 支持 `createdAt/completedAt/updatedAt/title/project/priority`(`project` 未分类置尾、`priority` 未设置置尾) |
|
|
278
|
-
| 新建 | `{title, project?, files?, priority?, type?}` | `action=create` | 分类 / 优先级 / 类型可选;`files` 为工作区相对路径数组,只给 files 时自动派生分类 |
|
|
279
|
-
| 快速添加 | `{text, workspace?, priority?, type?}` | `action=quick_add` + `text` | 自然语言解析(与面板、`/todo` 命令同一实现),`!优先级` / `^类型` 标记优先、否则回退 `body.priority` / `body.type`;返回 `created/skipped/errors` |
|
|
280
|
-
| 文件列表 | `{query?, limit?, workspace?}` | — | `@` 补全用;`workspace` 只接受 `/workspaces` 给出的候选路径 |
|
|
281
|
-
| 项目建议 | `{workspace?}` | — | 分类选择器 / `#` 补全用;返回已用分类、工作区名、顶层文件夹(`used`/`workspace`/`folder`) |
|
|
282
|
-
| 编辑 | `{id, title?, project?, files?, priority?, type?}` | `action=update` | **部分更新**:只传 `project` / `priority` / `type` / `files` 也能改;都没变化返回 `ok:true` + `unchanged:true` |
|
|
283
|
-
| 校验失败 | `PROJECT_INVALID` / `PROJECT_TOO_LONG` / `PRIORITY_INVALID` / `TYPE_INVALID` → HTTP 400 | 同 | 分类必须是字符串且 ≤ 40 字符;优先级必须是 `''/high/medium/low`;类型必须是 `''/bug/feature/improvement/question/chore/other` |
|
|
100
|
+
<p align="center"><img src="https://github.com/FantasyStarry/dsh-todo-time-log/raw/main/docs/images/demo-board.png" alt="看板操作示范" width="100%"></p>
|
|
284
101
|
|
|
285
|
-
|
|
102
|
+
1. 顶部四个分段切换视图:表格 / 看板 / 甘特图 / 日历。
|
|
103
|
+
2. 看板支持分组(状态 / 分类 / 状态×分类泳道)、密度、搜索与筛选。
|
|
104
|
+
3. 卡片「⋯」里可以移到分类、改优先级 / 类型、编辑、删除。
|
|
105
|
+
4. 已完成列默认只显示最近 10 条,可展开查看全部。
|
|
286
106
|
|
|
287
|
-
|
|
288
|
-
- 对话里输入 `#` 会出现分类补全下拉(同 `@` 的操作方式),名称含空格时自动用 `[名称]` 形式插入。
|
|
289
|
-
- 工具栏分类筛选(含各分类计数与「未分类」),行内分类徽标,详情显示分类,`sortBy=project` 时列表也按分类分组。
|
|
107
|
+
---
|
|
290
108
|
|
|
291
|
-
|
|
109
|
+
## 特性一览
|
|
292
110
|
|
|
293
|
-
|
|
111
|
+
| 能力 | 说明 |
|
|
112
|
+
| --- | --- |
|
|
113
|
+
| 🕒 创建 / 完成时间 | 全自动记录,本地时间 `YYYY-MM-DD HH:mm:ss`,未完成显示 `-`;重复完成、重复取消都会给出明确提示 |
|
|
114
|
+
| 🗂 四视图 | 表格 / 看板 / 甘特图 / 日历,同一份数据四种视角;切换视图保留筛选与排序 |
|
|
115
|
+
| 📊 甘特图 | 纯前端计算(不新增接口):条形 = 创建 → 完成,未完成延伸到「现在」,支持全部 / 近 7 天 / 近 30 天 |
|
|
116
|
+
| 🏷 分类 · 优先级 · 类型 | 项目分类(可从「已用分类 / 工作区 / 文件夹」建议里选)、高 / 中 / 低优先级、缺陷 / 新功能 / 改进 / 疑问 / 杂项 / 其他 |
|
|
117
|
+
| 📎 `@工作区文件` | 把待办关联到代码文件;只给文件、不给分类时,自动取文件所在目录作为分类 |
|
|
118
|
+
| ✍️ 四种录入 | 面板表单、对话式一句话(`新任务 写周报 #工作`)、`/todo` 斜杠命令、智能体工具 `todo_time_log` |
|
|
119
|
+
| 🤖 智能体工具 | 智能体可 `list / get / create / complete / uncomplete / update / delete / health`,与 UI 共用同一实现 |
|
|
120
|
+
| 📦 导入 / 导出 | 页头「数据」:JSON 无损备份、CSV 给 Excel 统计;导入先预览再写入,支持合并 / 仅新增 / 覆盖全部,自动备份并可直接撤销 |
|
|
121
|
+
| 🔒 本地数据 | `<DSH_PROFILE_DIR 或 ~/.dsh>/todo-time-log/todos.json`;临时文件 + rename 原子替换、写入串行化、跨进程文件锁 |
|
|
122
|
+
| 🌗 主题与无障碍 | 全部颜色走 `--dsw-alias-*` token,明暗主题自适应;`aria-live`、`aria-sort`、键盘操作、减少动效偏好 |
|
|
123
|
+
| ⌨️ 面板快捷键 | `/` 聚焦搜索、`n` 聚焦新建、`f` 循环切换筛选 |
|
|
294
124
|
|
|
295
|
-
|
|
125
|
+
---
|
|
296
126
|
|
|
297
|
-
|
|
298
|
-
| --- | --- | --- | --- |
|
|
299
|
-
| **表格** | 表头「标题 / 分类 / 状态 / 创建时间 / 完成时间 / 操作」,窄屏横向滚动;标题点击展开详情行;`排序=分类` 时按分类分组(方向生效,未分类最后) | 筛选(全部/未完成/已完成)、排序字段、方向;点表头也可排序(与工具栏单一状态源,`th` 带 `aria-sort`) | 完成/取消完成、编辑(标题+分类行内输入)、删除(二次确认)、详情 |
|
|
300
|
-
| **看板** | 列式看板:列头 = 状态点 + 名称 + 计数 + 「+」列内新建;卡片 = 24×24 完成 checkbox + 标题(2 行)+ 一行元信息(**类型徽标 / 优先级徽标** / 分类点或状态徽标 + **已进行 / 用时**)+ 文件 chips(1 个 + N)+ 「⋯」更多操作(编辑 / 优先级 / 类型 / 移到分类 / 删除,删除二次确认);已完成列默认最近 10 条 + 「显示全部/只看最近」;列宽 `clamp()` + 横向 scroll-snap,≤560px 单列堆叠;**状态 × 分类泳道**(两列固定、行 = 分类、可折叠、未完成泳道带「+」);**密度** 舒适/紧凑(紧凑 = 标题 1 行、隐藏文件 chip 与分类名)。**不做指针拖拽**:两列板上拖拽≈点完成;移动改走「⋯ → 移到分类」(单指针替代,WCAG 2.5.7) | 分组方式:**按状态**(默认)/ **按分类** / **状态 × 分类**(泳道);密度(舒适/紧凑);类型筛选;面板内快捷键:`/` 搜索、`n` 新建、`f` 循环筛选 | 完成/取消完成(checkbox)、编辑/删除(更多操作)、设优先级/类型、移到分类(带撤销 toast)、列头/泳道快捷新建、列内滚动、状态筛选 |
|
|
301
|
-
| **甘特图** | 时间轴条形,见 §3.5;按分类分组 | 时间范围(全部/近 7 天/近 30 天) | 展开行内详情后可完成/编辑/删除 |
|
|
302
|
-
| **日历** | 月视图,周一起始 42 格(前后月补格弱化、今天标记);每格最多 3 条 + 「+N」;点某天在下方展开当天全部条目;**按本地时区**落格 | 上一月 / 今天 / 下一月;落格依据:**按创建时间**(默认)或 **按完成时间**(按完成时未完成条目不出现) | 当天面板内完成/编辑/删除 |
|
|
127
|
+
## 安装
|
|
303
128
|
|
|
304
|
-
|
|
129
|
+
**要求**:DeepSeek Harness `>= 0.2.0-rc.2 < 0.2.3-0`(`engines.dsh` 声明,已在 **0.2.2-rc.1** 真机验证),Node.js ≥ 20。
|
|
305
130
|
|
|
306
|
-
###
|
|
131
|
+
### 方式一:插件管理器
|
|
307
132
|
|
|
308
|
-
|
|
133
|
+
在 DSH 的**插件管理(Plugin Manager)**里安装 bundle,包名填:
|
|
309
134
|
|
|
310
135
|
```text
|
|
311
|
-
|
|
312
|
-
修复登录缺陷 #工作 !high ^bug
|
|
313
|
-
买牛奶 @生活;回邮件 [工作]
|
|
314
|
-
项目:学习 整理笔记,读完第三章
|
|
136
|
+
dsh-todo-time-log
|
|
315
137
|
```
|
|
316
138
|
|
|
317
|
-
|
|
318
|
-
| --- | --- |
|
|
319
|
-
| 多条 | 换行、`;`、`;` 拆成多条 |
|
|
320
|
-
| 分类标记 | `#名称` / `@名称` / `[名称]` / `项目:名称` / `分类:名称`;**取最左的非空值**,所有标记从标题中剥离 |
|
|
321
|
-
| 优先级标记 | `!high` / `!medium` / `!low`(别名 `!h` / `!m` / `!l`、中文 `!高` / `!中` / `!低`);取最左非空值、从标题剥离;`!important` 不会被误判 |
|
|
322
|
-
| 类型标记 | `^bug` / `^feature` / `^improvement` / `^question` / `^chore` / `^other`(中文 `^缺陷` / `^新功能` / `^改进` / `^疑问` / `^杂项` / `^其他`),也支持 `类型:bug` / `type:bug`;取最左非空值、从标题剥离;`^foo` 等未知值保留原文 |
|
|
323
|
-
| 文件引用 | `@路径/文件`、`@file.ts` 或命中工作区文件索引的 `@名称` → 记入 `files`;**没有显式分类时,分类自动取该文件所在目录**(`src/auth/login.ts` → `src/auth`;根目录文件用文件名) |
|
|
324
|
-
| 含空格的路径 | 用引号形式 `@"docs/My File.md"`(补全下拉选中含空格/`#` 的路径时会自动加引号) |
|
|
325
|
-
| 填充词 | 「帮我/请/麻烦/新增/新建/添加/记一下/记录/待办:/新任务/任务/提醒我/todo/task/add」循环剥离 |
|
|
326
|
-
| 边界 | 首尾标点与空白去除;只有填充词的行记入 `skipped`(`NO_CONTENT`),不会创建空待办 |
|
|
327
|
-
| 结果 | `created`(已创建待办)/ `skipped`(跳过原因)/ `errors`(后端校验失败,如标题超长) |
|
|
328
|
-
|
|
329
|
-
**`@` 文件补全**:对话输入框里输入 `@` 会显示工作区文件下拉(防抖 150ms,最多 12 条,↑↓/Enter/Tab 选择、Esc 关闭),选中后插入 `@相对路径`。文件列表来自 `POST /files`,由 Host 通过 `ctx.fs` 只读遍历工作区(深度 ≤ 4、最多 2000 条、忽略 `node_modules`/`.git`/`dist` 等、20s 缓存),客户端**不能**指定任意磁盘路径。
|
|
330
|
-
|
|
331
|
-
**工作区如何确定**:`config.workspaceRoot` > 最近活跃 Live Session 的工作目录(按最后事件时间排序,忽略子代理会话)> `workspaceRegistry` > 进程目录。因为「最近活跃会话」可能漂移,**面板标题行有一个工作区选择器**(来自 `POST /workspaces` 的候选,默认选 active,选中后 `/files` 与 `/quick-add` 都带上该 `workspace`,@ 补全缓存也按工作区隔离)。`/files` 与 `/quick-add` 的 `workspace` 只接受候选路径,未知路径返回 400 `WORKSPACE_UNKNOWN`。
|
|
332
|
-
|
|
333
|
-
**三个可输入的地方**都支持同一套标记:
|
|
334
|
-
|
|
335
|
-
1. **顶部新建框**(「输入待办内容,按 Enter 创建」):输入 `测试 #lib` 或 `@lib/store.js 写文档`,按 Enter 后自动拆出分类/文件,**标题里不会残留 `#lib`**;输入时敲 `#`/`@` 会弹出补全下拉。含标记时走 Host 的 `/quick-add`(多条/跳过/错误汇总进浮层提示 toast),不含标记时按当前选择器创建。
|
|
336
|
-
2. **对话录入面板**:同上,另有逐条气泡与撤销。
|
|
337
|
-
3. **Harness 主输入框的 `/todo` 命令**:`/todo 写周报 #工作 lib/store.js`,回执列出新建条目。
|
|
338
|
-
- **分类 / 优先级 / 类型都可以点选**:**仅当草稿以 `/todo` 开头时**,在行里输入 `@` 会在平台的 `@` 菜单里追加三组候选——「待办分类」(已用分类 / 工作区;工作区文件夹如 `docs`、`lib`、`test` 属于目录噪音,不会出现)、「待办优先级」(高/中/低)、「待办类型」(缺陷/新功能/改进/疑问/杂项/其他);选中后自动插入 `#分类 ` / `!high ` / `^bug `,不需要手打标记。普通消息里输入 `@` 不会出现这些分组。平台的触发字符写死为 `/` 和 `@`,**`#` 本身不会弹出菜单**(插件无法新增触发字符),所以点选入口是 `@`。平台菜单会同时展示它自己的文件/会话来源,插件无法移除它们(只能追加自己的分组)。
|
|
339
|
-
- 命令输入框只支持「命令名补全 + 附件」,插件无法注入 `#`/`@` 的行内补全;因此 `/todo` 同时支持手打:`#分类`、**裸写工作区相对路径**(`lib/store.js`,不需要 `@`)、多条用换行或 `;` 分隔。
|
|
340
|
-
- 该命令声明了 `input.attachments`,主输入框里附加/`@` 引用的文件会被接收,并按文件名在工作区索引里解析成相对路径记入 `files`。
|
|
341
|
-
- 命令输入框由 Harness 提供,只支持「命令名补全 + 附件」,**插件无法在那里注入 `#`/`@` 补全**;因此 `/todo` 特意做成「不用补全也好用」:`#分类` 手打即可、**文件直接写工作区相对路径**(`lib/store.js`,不需要 `@`;写法含空格时用 `@"docs/My File.md"`)、多条用换行或 `;` 分隔。
|
|
342
|
-
- 该命令声明了 `input.attachments`,所以在主输入框里**用 `@` 引用/附加的文件**会被接收:插件按文件名在工作区索引里解析成相对路径并记入 `files`。
|
|
343
|
-
- 需要下拉选择/补全时用插件面板(分类选择器 + `#`/`@` 补全)。
|
|
344
|
-
|
|
345
|
-
服务端兜底:即使调用方把带标记的文本直接发给 `POST /create`(例如旧版界面或工具调用),Host 也会自动解析拆分,不会把 `#分类`/`@文件`/`!优先级`/`^类型` 记进标题。分类优先级:标题里的显式标记 > 表单选择器 > (都不给时)由文件目录派生;优先级与类型同理:显式标记 > 表单选择器 > 未设置。
|
|
346
|
-
|
|
347
|
-
### 3.9 界面层级与完成反馈(视觉重构)
|
|
348
|
-
|
|
349
|
-
面板只有三层信息,按「第一眼该看什么」排序:
|
|
139
|
+
也可以直接让当前会话的智能体执行:
|
|
350
140
|
|
|
351
141
|
```text
|
|
352
|
-
|
|
353
|
-
② 新建待办 输入框 + 分类选择器 + 添加 ── 页面级唯一的主操作(实心按钮)
|
|
354
|
-
③ 数据卡片 视图分段 · 筛选分段(带计数) ── 视图控制与列表同属一张卡片
|
|
355
|
-
分类筛选 / 排序 / 方向 · 列表
|
|
142
|
+
plugin_manager action=install_bundle target=dsh-todo-time-log
|
|
356
143
|
```
|
|
357
144
|
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
- **一张卡片**:工具栏与表格/看板/甘特/日历同属 `.ttl-data`,四视图不再各自叠一层边框;`.ttl-data` 只保留 0.5px 微边框。
|
|
361
|
-
- **唯一主操作**:页面级 `.ttl-button-primary` 只有「添加」一个;其余按钮一律 ghost / outline / 文本链接(对话录入展开后,其卡片内的「发送」也是 primary —— 属于该卡片自己的操作区,不是页面级主操作)。
|
|
362
|
-
- **统计并入筛选**:`全部/未完成/已完成` 分段上直接显示计数(`stats.*` 不再单独渲染成一张卡),标签与筛选文案是同一套 key;**分类筛选生效时计数换成该分类的口径**(`/list` 的 `projects[]` 汇总),该分类一条待办都没有时显示 0,不会出现「计数 4、行数 0」。
|
|
363
|
-
- **分段控件**:视图(4 项)与筛选(3 项)用 `.ttl-seg` + `aria-pressed`,选中态是「浅底 + 阴影」而不是和 hover 相同的底色;方向(2 项)收成 28×28 的 ↑/↓ 按钮,不再占一个 select。
|
|
364
|
-
- **状态徽标**:未完成 = 中性浅底深字,已完成 = 成功色浅底深字(`.ttl-badge`,圆角胶囊),分类徽标降级为弱化文本,避免与状态抢注意力。
|
|
365
|
-
- **看板卡片摘要化**:状态/分类徽标只在「列没有表达该字段」时出现(按状态分组 → 分类点 + 名称;按分类分组 → 状态徽标);创建/完成时间不再常驻,改为「已进行 X / 用时 X」(完整时间进 `<time aria-label>` 与详情);文件 chip 最多 1 个 +「+N」;编辑/删除收进「⋯」更多操作,卡片正面只留一个 24×24 完成 checkbox(每卡 tab stop 从 3+ 降到 2)。
|
|
366
|
-
- **看板列头与收纳**:状态点 + 名称 + 计数(`aria-label` 整句「未完成,共 3 条待办」)+「+」列内新建(分类列预填分类);已完成列默认最近 10 条 +「显示全部(N)/只看最近 10 条」;列体是可聚焦滚动区域(`role="group"` + `tabIndex` + `aria-label`);≤560px 单列堆叠,避免横向 + 纵向双轴滚动。
|
|
367
|
-
- **搜索**:工具栏搜索框匹配标题/分类/文件路径(客户端过滤,与状态/分类筛选 AND),实时显示「N 条匹配」,无结果空态带「清除搜索」,`Escape` 清空。
|
|
368
|
-
- **泳道(状态 × 分类)**:两列固定(未完成/已完成),列内按分类分泳道;泳道头 = 折叠按钮(`aria-expanded`)+ 名称 + 计数 + 未完成泳道的「+」;未分类泳道最后;窄屏单列堆叠时泳道自然退化为小节。
|
|
369
|
-
- **移动与优先级**:按分类分组/泳道时,「⋯」里有「移到分类」select(未分类 + 现有分类),写回 `project` 并给带撤销的 toast;卡片与新建/行内编辑都有优先级 select(高/中/低/未设置),卡片元信息显示优先级徽标(颜色只是辅助,始终带文字)。
|
|
370
|
-
- **类型(bug / 新功能 / 改进 / 疑问 / 杂项 / 其他)**:卡片/表格/详情显示类型徽标(缺陷红、新功能蓝、改进绿、疑问橙、杂项/其他中性,均带文字);「⋯」与新建/行内编辑可改;工具栏「类型筛选」客户端过滤(全部/未设置/六类),搜索也能命中类型名(如搜「缺陷」);快捷添加用 `^bug` 等标记。
|
|
371
|
-
- **密度**:看板工具栏「密度(舒适/紧凑)」;紧凑 = 标题 1 行、隐藏文件 chip 与分类名文字(保留状态点)、内边距收紧,状态跨视图保留。
|
|
372
|
-
- **面板内快捷键**:`/` 聚焦搜索、`n` 聚焦新建、`f` 循环切换筛选;只在面板根节点 `onKeyDown` 处理(焦点在输入类元素或带修饰键时忽略),不注册 document 监听。
|
|
373
|
-
- **时间列右对齐** + `font-variant-numeric: tabular-nums`;表格行高 44px、表头 36px。
|
|
374
|
-
- **圆角同心**:新建卡片外层 16 = 内层 8 + 内边距 8;数据卡片 12,控件 8,分段轨道 8 / 分段 6(8 = 6 + 2)。
|
|
375
|
-
- **全部颜色走 token**:`--dsw-alias-*` + `color-mix()` 调透明度,无硬编码 hex/rgb;明暗主题都可用。
|
|
376
|
-
- **五态齐全**:理想 / 空(原因 + 一个出口按钮)/ 骨架屏(行高列宽对齐真实表格)/ 错误(重试)/ 窄屏(工具栏换行、表格内部横向滚动)。
|
|
377
|
-
|
|
378
|
-
**完成反馈改成 toast(不改变布局高度)**:
|
|
379
|
-
|
|
380
|
-
- 旧实现把提示条插在新建框与对话录入之间(正常文档流),每次「完成」都会把整张表向下推 ~49px、3.5s 后再弹回。
|
|
381
|
-
- 现在提示挂在 `.ttl-root` 下的 `.ttl-toast-host`(`position: absolute; bottom: 44px`,与 `.ttl-scroll` 同级;44px = lv3 阴影偏移 12 + 模糊 32,正好不被 `overflow: hidden` 裁掉),因此**出现/消失都不会新增或移除文档流节点**;面板滚动时它固定在面板底部。
|
|
382
|
-
- 视觉沿用宿主系统 toast 的 token(`--dsw-alias-toast-bg` / `--dsw-alias-toast-label` / `--dsw-shadow-lv3`),成功/失败/中性各一个 14px 图标(成功绿、失败红)。
|
|
383
|
-
- 完成后 toast 里带「撤销」:直接调用 `uncomplete` 并刷新,和行内「取消完成」走同一条 `handleToggle`;带撤销时停留 6s,普通提示 3.5s,错误 6s。
|
|
384
|
-
- 无障碍:宿主容器常驻 `aria-live="polite"` + `aria-atomic="false"`,成功/中性节点 `role="status"`,错误节点 `role="alert"`;关闭按钮有 `aria-label`;`prefers-reduced-motion` 下关闭入场动画,且不把焦点移到 toast。
|
|
385
|
-
|
|
386
|
-
渲染预览见 [`docs/preview/`](./docs/preview/README.md)(真实 `client.js` + 宿主主题 token 的静态渲染)。
|
|
145
|
+
安装成功后:左侧导航出现「待办时间日志」入口,中央面板可打开;Plugin Manager 中能看到中文名称、描述与图标。
|
|
387
146
|
|
|
388
|
-
|
|
147
|
+
> 安装会执行包安装(pnpm)、写入 `dsh.bundle.patch` 声明的 Host 行并启用 bundle;返回 `application: "applied"` 才算真正生效。Host 半改动需要关闭再开启插件(或重启桌面端)才会加载新模块,Client 半(`dsh.client.immediately`)随下一次页面刷新加载。
|
|
389
148
|
|
|
390
|
-
|
|
149
|
+
### 方式二:本地开发安装(link 方式)
|
|
391
150
|
|
|
392
151
|
```text
|
|
393
|
-
plugin_manager action=install_bundle target
|
|
152
|
+
plugin_manager action=install_bundle target=<本地仓库绝对路径>
|
|
394
153
|
```
|
|
395
154
|
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
- 验证入口:Plugin Manager 搜索「待办时间日志」应看到中文标题、描述与图标;左侧导航出现同名入口。
|
|
400
|
-
- 卸载:`plugin_manager action=remove_bundle target=dsh-todo-time-log`(数据文件保留,手动删除即可清空)。
|
|
155
|
+
修改 `lib/*.js` 后需在 Plugin Manager 里关闭再开启该插件(或重启桌面端)才会加载新的 Host 模块;UI 改动刷新页面即可。
|
|
156
|
+
|
|
157
|
+
> 卸载:`plugin_manager action=remove_bundle target=dsh-todo-time-log`。数据文件会保留,手动删除 `todos.json` 即可清空。
|
|
401
158
|
|
|
402
159
|
### 配置
|
|
403
160
|
|
|
@@ -407,224 +164,124 @@ plugin_manager action=install_bundle target=D:\code\dsh-todo-time-log
|
|
|
407
164
|
| 环境变量 `DSH_TODO_TIME_LOG_DATA` | 中 | `DSH_TODO_TIME_LOG_DATA=D:\data\todos.json` |
|
|
408
165
|
| 默认 | 最低 | `<DSH_PROFILE_DIR 或 ~/.dsh>/todo-time-log/todos.json` |
|
|
409
166
|
|
|
410
|
-
|
|
167
|
+
相对路径解析到配置目录下。当前构建未导出 `Config` schema,插件详情页没有配置表单,请用上面两种外部方式配置(原因见 [`docs/limitations.md`](https://github.com/FantasyStarry/dsh-todo-time-log/blob/main/docs/limitations.md))。
|
|
411
168
|
|
|
412
169
|
---
|
|
413
170
|
|
|
414
|
-
##
|
|
171
|
+
## 用法
|
|
415
172
|
|
|
416
|
-
###
|
|
173
|
+
### 四种录入方式
|
|
417
174
|
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
npm run test:coverage # 同上 + Node 内置覆盖率(--experimental-test-coverage)
|
|
425
|
-
npm run audit # client.js 静态审计(28 项)
|
|
426
|
-
npm run verify # 以上全套(build + check + lint + typecheck + test + audit;prepublishOnly 同)
|
|
427
|
-
```
|
|
175
|
+
| 入口 | 怎么用 | 适合 |
|
|
176
|
+
| --- | --- | --- |
|
|
177
|
+
| 面板表单 | 顶部输入框 + 分类 / 优先级 / 类型选择器,Enter 创建 | 手动逐条记录 |
|
|
178
|
+
| 对话式录入 | 面板右上「对话录入」,一句话写多条,逐条回执 + 撤销 | 一次记一堆 |
|
|
179
|
+
| `/todo` 命令 | Harness 主输入框:`/todo 写周报 #工作 lib/store.js` | 不离开对话流 |
|
|
180
|
+
| 智能体工具 | 让模型调用 `todo_time_log`(`create` / `complete` / `list` …) | 让 AI 代记、代查 |
|
|
428
181
|
|
|
429
|
-
|
|
182
|
+
### 快捷标记(三处输入通用)
|
|
430
183
|
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
- `test/live-utf8.py` / `test/live-projects.py`:对**运行中实例**的真实往返(中文标题、分类筛选、迁移版本落盘、清理测试数据),需设置 `DSH_WEB_URL`(默认 `http://127.0.0.1:52283`)。
|
|
184
|
+
```text
|
|
185
|
+
新任务 写周报 #工作 @src/auth/login.ts
|
|
186
|
+
修复登录缺陷 #工作 !high ^bug
|
|
187
|
+
买牛奶 @生活;回邮件 [工作]
|
|
188
|
+
项目:学习 整理笔记,读完第三章
|
|
189
|
+
```
|
|
438
190
|
|
|
439
|
-
|
|
191
|
+
| 标记 | 写法 | 说明 |
|
|
192
|
+
| --- | --- | --- |
|
|
193
|
+
| 分类 | `#工作` / `@生活` / `[工作]` / `项目:工作` / `分类:工作` | 取最左的非空值,并从标题里剥离 |
|
|
194
|
+
| 优先级 | `!high` / `!medium` / `!low`(`!h` `!m` `!l`、`!高` `!中` `!低`) | 未写则回退到表单选择器 |
|
|
195
|
+
| 类型 | `^bug` / `^feature` / `^improvement` / `^question` / `^chore` / `^other`(中文 `^缺陷` / `^新功能` …,也支持 `类型:bug` / `type:bug`) | 未知值保留原文 |
|
|
196
|
+
| 文件 | `@路径/文件`、`@"docs/My File.md"` | 记为工作区相对路径;没有显式分类时自动取文件所在目录 |
|
|
197
|
+
| 多条 | 换行、`;`、`;` 分隔 | 只有填充词的行会被跳过,不会创建空待办 |
|
|
440
198
|
|
|
441
|
-
|
|
199
|
+
> 解析实现只有一份(`lib/parse.js` 的 `parseQuickAdd()`),面板、`/todo` 命令与工具 `quick_add` 共用,不存在两边规则漂移。
|
|
442
200
|
|
|
443
|
-
|
|
201
|
+
### 智能体工具 / HTTP
|
|
444
202
|
|
|
445
203
|
```text
|
|
446
|
-
todo_time_log { "action": "create", "title": "写插件文档" }
|
|
447
|
-
todo_time_log { "action": "create", "title": "写周报", "priority": "high" }
|
|
448
204
|
todo_time_log { "action": "create", "title": "修复登录", "priority": "high", "type": "bug" }
|
|
449
|
-
todo_time_log { "action": "
|
|
450
|
-
todo_time_log { "action": "update", "id": "ttl_…", "priority": "low" }
|
|
205
|
+
todo_time_log { "action": "list", "filter": "pending", "sortBy": "priority", "direction": "asc" }
|
|
451
206
|
todo_time_log { "action": "complete", "id": "ttl_…" }
|
|
452
|
-
todo_time_log { "action": "complete", "id": "ttl_…" } # 期望:ALREADY_COMPLETED 提示
|
|
453
|
-
todo_time_log { "action": "uncomplete", "id": "ttl_…" }
|
|
454
|
-
todo_time_log { "action": "list", "filter": "pending", "sortBy": "createdAt", "direction": "asc" }
|
|
455
|
-
todo_time_log { "action": "list", "sortBy": "priority", "direction": "asc" }
|
|
456
|
-
todo_time_log { "action": "delete", "id": "ttl_…" }
|
|
457
|
-
```
|
|
458
|
-
|
|
459
|
-
实际输出示例:
|
|
460
|
-
|
|
461
|
-
```text
|
|
462
|
-
[已完成] 写插件文档:目录结构与数据模型|创建 2026-11-03 08:37:49|完成 2026-11-03 08:37:50|id ttl_mvzyl2w302jtfbqu
|
|
463
|
-
操作失败(ALREADY_COMPLETED):该待办已完成(完成时间 2026-11-03 08:37:50),无需重复操作
|
|
464
207
|
```
|
|
465
208
|
|
|
466
|
-
### 5.3 手工验证(HTTP / UI)
|
|
467
|
-
|
|
468
209
|
```bash
|
|
210
|
+
# 需要携带 DSH 登录态(未登录 401、跨站/不可信来源 403)
|
|
469
211
|
curl -s http://127.0.0.1:<port>/api/dsh-todo-time-log/health
|
|
470
|
-
curl -s -X POST http://127.0.0.1:<port>/api/dsh-todo-time-log/create \
|
|
471
|
-
-H 'content-type: application/json' -d '{"title":"验证"}'
|
|
472
212
|
```
|
|
473
213
|
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
UI 手测清单(阶段一):
|
|
477
|
-
|
|
478
|
-
新建(Enter 与按钮)→ 列表出现且「完成」列为 `-` → 标记完成 → 完成时间出现 → 取消完成 → 完成时间回到 `-` → 行内编辑(Enter 保存 / Esc 取消)→ 删除(行内二次确认)→ 筛选三态 → 排序(创建/完成 × 升降序)→ 刷新页面数据仍在。
|
|
214
|
+
HTTP 接口与智能体工具共用同一个业务实现(`executeTodoAction`),因此不存在「UI 能做的工具做不到」的情况;接口经宿主认证,未登录返回 401、跨站/不可信来源返回 403。
|
|
479
215
|
|
|
480
|
-
|
|
216
|
+
### 导入 / 导出
|
|
481
217
|
|
|
482
|
-
|
|
483
|
-
2. **分类**:新建时填分类(或用 datalist 选已有分类)→ 行上出现分类徽标;工具栏分类筛选选「工作」只剩该分类,选「未分类」只剩 `project=''`;行内编辑同时改标题与分类并保存;`排序=分类` 时列表按分类分组;详情里能看到分类。
|
|
484
|
-
3. **对话录入**:展开「对话录入」→ 输入 `帮我记一下 写周报 #工作;买牛奶 @生活` 发送 → 助手气泡列出两条(带分类与创建时间)→ 点「撤销」两条消失并提示「已撤销 2 条」;输入只有填充词(如「帮我记一下」)→ 提示示例;输入 `[] 空分类测试` → 标题为「空分类测试」且归入未分类。
|
|
218
|
+
面板右上角「数据」打开对话框:
|
|
485
219
|
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
220
|
+
| 能力 | 说明 |
|
|
221
|
+
| --- | --- |
|
|
222
|
+
| 导出 JSON | 无损信封(`version: 5` + 全部字段 + 时间戳),可原样回导;导出前会先合并其他窗口刚写入的数据 |
|
|
223
|
+
| 导出 CSV | UTF-8 BOM + CRLF,Excel 直接打开;时间为本地 `YYYY-MM-DD HH:mm:ss`(秒级,毫秒级无损请用 JSON),`文件` 用 `;` 分隔,额外一列只读的 `耗时(分钟)` |
|
|
224
|
+
| 导入 JSON | 支持本插件信封 v1–v5、裸 `Todo[]` 数组;旧版本字段自动迁移 |
|
|
225
|
+
| 导入 CSV | 中英文表头别名(标题/title、分类/project、状态/status、优先级/priority、类型/type、创建时间/createdAt …),中文值「已完成 / 高 / 缺陷」也能识别 |
|
|
226
|
+
| 两段式导入 | 选文件后先 `dryRun` 预览「新增 / 更新 / 跳过 / 移除 / 非法行」,确认后才写盘 |
|
|
227
|
+
| 冲突策略 | **合并**(默认,同 ID 取 `updatedAt` 较新)/ **仅新增**(同 ID 跳过)/ **覆盖全部**(先自动备份再整体替换) |
|
|
228
|
+
| 自动备份 + 撤销 | 每次真正写入前生成 `<dataFile>.bak-<时间戳>`;导入结果的 toast 带「撤销」,恢复前还会再备份一次当前状态 |
|
|
495
229
|
|
|
496
|
-
|
|
497
|
-
2. **完成反馈(重点)**:点任意一行的「完成」→ 面板底部浮出深色 toast(勾 + 「已标记为完成。」+「撤销」)→ **表格与页头不应上下跳动**(可观察表格第一行的纵坐标);点「撤销」→ 该行回到未完成、toast 变为「已取消完成。」;点 × 或等待 3.5s 自动消失。
|
|
498
|
-
3. **错误反馈**:断开 Host(或让 `/list` 失败)→ 卡片内显示「加载失败 + 重试」,同时底部出现红色图标的错误 toast;两者都不改变布局高度。
|
|
499
|
-
4. **分段控件**:视图与筛选都是分段控件,选中态有浅底+阴影;方向按钮点一次在 ↑/↓ 间切换,`aria-label` 里写明当前方向;窄面板下工具栏换行、表格在卡片内部横向滚动。
|
|
500
|
-
5. **空态**:删光待办 → 卡片内显示「还没有待办」+「新建第一条待办」,点按钮焦点跳到新建输入框;筛选到没有结果时按钮变为「查看全部待办」,一次点击同时清掉状态筛选与分类筛选;四个视图(表格/看板/甘特图/日历)的空态都有这个出口按钮。
|
|
501
|
-
6. **明暗主题**:切换宿主明暗主题,卡片边框、分段控件、状态徽标、toast 都要有足够对比度。
|
|
230
|
+
限制:单次导入最多 5000 条(与列表上限一致),导入请求体上限 8 MiB;备份文件保留在同一目录,可手动删除或恢复。
|
|
502
231
|
|
|
503
|
-
|
|
232
|
+
---
|
|
504
233
|
|
|
505
|
-
|
|
506
|
-
1. **@ 补全**:输入框敲 `@` → 出现工作区文件下拉(显示本仓库文件)→ 继续输入 `lib/s` 过滤 → ↑↓ 移动、Enter 选中 → 文本变成 `@lib/store.js `;Esc 可关闭;无匹配时显示「无匹配文件」;含空格的文件选中后应插入 `@"…"` 引号形式。
|
|
507
|
-
2. **@文件自动分类**:发送「新任务 写周报 #工作 @lib/store.js」→ 助手气泡显示 `[工作] 写周报 · 创建 …` + 文件 chip;再发送「修复甘特图 @lib/client.js」(不带 #分类)→ 分类自动为 `lib`。
|
|
508
|
-
2b. **分类点选**:新建表单的分类下拉应能直接看到「已用 / 工作区 / 文件夹」三组(工作区组里是 `dsh-todo-time-log`、`File_Manager_Legacy` 等),选一个即可创建,不用手打;选「自定义…」才出现文本框;行内编辑用同一个下拉;对话里输入 `#` 会弹出同样的分类补全。
|
|
509
|
-
3. **多条与跳过**:发送「买牛奶 @生活;回邮件 [工作]」→ 一次添加 2 条;只发送「帮我记一下」→ 提示没有可添加内容。
|
|
510
|
-
4. **撤销**:点气泡里的「撤销」→ 刚添加的条目消失并提示「已撤销 N 条」。
|
|
511
|
-
5. **文件 chips**:表格行标题旁有紧凑附件标识、展开详情能看到完整文件列表;看板卡片显示 1 个文件 chip +「+N」;甘特行内详情、日历当天条目详情都能看到完整文件;点击 chip 会复制完整路径(提示「已复制路径」)。
|
|
512
|
-
6. **`/todo` 命令**:在 Harness 主输入框输入 `/todo 新任务 写周报 #工作 @lib/store.js` → 回执列出新建待办(不进入模型对话)。
|
|
513
|
-
7. **兼容**:既有旧待办(无 files)在各视图正常显示,不出现空 chip;分类筛选/排序/四视图切换不受影响。
|
|
234
|
+
## 数据与安全
|
|
514
235
|
|
|
515
|
-
|
|
236
|
+
- **本地优先**:数据只存在本地 JSON 文件,不联网、不上传、不触发模型调用。
|
|
237
|
+
- **写入可靠**:临时文件 + rename 原子替换;全部写操作串行化;跨进程写入用文件锁 + mtime 合并,避免多窗口互相覆盖。
|
|
238
|
+
- **损坏自愈**:JSON 损坏 / 结构异常 / 版本不支持会分别归档备份,然后重建空列表并给出提示;修复与异常提示只投递一次,不刷屏。
|
|
239
|
+
- **导入可回退**:导入 / 覆盖前自动写入 `<dataFile>.bak-<时间戳>` 备份;撤销只允许恢复同目录下本插件生成的 `.bak-*` 文件,路径穿越与任意文件读取都会被拒绝。
|
|
240
|
+
- **版本迁移**:v1 → v5 全部自动迁移,下一次写入即落盘新版本。
|
|
241
|
+
- **接口边界**:`/files`、`/quick-add` 只接受 `/workspaces` 返回的候选工作区;文件引用只接受工作区相对路径,绝对路径、盘符、`..` 一律拒绝。
|
|
516
242
|
|
|
517
|
-
|
|
518
|
-
2. **操作收敛**:点卡片右上「⋯」→ 展开「编辑 / 优先级 / 删除」(按分类分组时还有「移到分类」);点编辑进入标题 + 分类 + 优先级行内编辑(Enter 保存 / Esc 取消);点删除出现「删除后无法恢复。」二次确认;`Escape` 可关闭更多操作与删除确认。
|
|
519
|
-
3. **列头与收纳**:未完成/已完成列头有状态点、名称、计数;「+」在未完成列/分类列点击后聚焦顶部新建框(分类列会预填该分类);已完成列默认最近 10 条,滚到底显示「显示全部(N)」,展开后可「只看最近 10 条」。
|
|
520
|
-
4. **筛选不再静默失效**:先在表格选「已完成」再切看板 → 工具栏仍显示同一筛选且看板只剩已完成列有内容;空列显示「没有符合筛选的待办」;点「全部」恢复。
|
|
521
|
-
5. **搜索**:输入「部署」→ 只剩匹配卡片并显示「1 条匹配」;输入不存在的词 → 「没有匹配「…」的待办」+「清除搜索」;按 `Escape` 清空搜索。
|
|
522
|
-
6. **窄屏**:把面板拖窄到 <560px → 看板从两列变为单列纵向堆叠,列头置顶,不再需要横向滚动。
|
|
243
|
+
## 兼容性
|
|
523
244
|
|
|
524
|
-
|
|
245
|
+
| 项 | 值 |
|
|
246
|
+
| --- | --- |
|
|
247
|
+
| 插件 ID / 包名 | `dsh-todo-time-log`(dsh 不加命名空间前缀,与官方插件一致;短别名 `todo-time-log` / `ttl`) |
|
|
248
|
+
| DSH | `>=0.2.0-rc.2 <0.2.3-0`(已实测 0.2.2-rc.1) |
|
|
249
|
+
| Node.js | ≥ 20 |
|
|
250
|
+
| 数据版本 | `version: 5`(旧版本自动迁移) |
|
|
251
|
+
| HTTP 接口 | `POST /api/dsh-todo-time-log/*`(经宿主认证:无登录态 401、跨站/不可信来源 403) |
|
|
252
|
+
| 安装位置 | 中央面板 `main` + 侧边栏 `sidebar.panellist` |
|
|
525
253
|
|
|
526
|
-
|
|
527
|
-
2. **移到分类**:按分类分组或泳道模式 → 卡片「⋯」→「移到分类…」选另一个分类 → 卡片换列,toast「已移到「x」」带「撤销」;撤销后回到原分类。
|
|
528
|
-
3. **优先级**:新建框的「优先级」下拉选「高/中/低」→ 创建后卡片显示对应徽标(高红 / 中橙 / 低灰,均带文字);「⋯」里的优先级 select 可直接改并 toast 撤销;展开详情能看到优先级;表格状态列显示徽标;表格排序下拉有「优先级」;快捷添加 `写周报 !high` / `!h` / `!高` 生效且标题不残留标记。
|
|
529
|
-
4. **密度**:看板工具栏「密度」选「紧凑」→ 标题 1 行、文件 chip 与分类名隐藏、卡片变矮;切回「舒适」恢复;切到其它视图再回来密度仍保持。
|
|
530
|
-
5. **快捷键**:焦点在面板空白处按 `/` 聚焦搜索、`n` 聚焦新建、`f` 循环切换筛选;焦点在输入框/下拉里时这些键不触发。
|
|
531
|
-
6. **v4 迁移**:旧 `version: 3` 数据文件加载后首次写入变为 `version: 4`,旧待办 `priority` 为空且无报错;`/list?sortBy=priority` 未设置项始终排最后;`update {priority:"urgent"}` 返回 400 `PRIORITY_INVALID`。
|
|
254
|
+
> **兼容性门禁**:DSH 在 profile 启动时按已安装版本的 `engines.dsh` 做 preflight,不满足的插件会被禁用,并在 Plugin Manager 里显示 `incompatible-version`。升级 DSH 后若遇到该提示,安装声明范围覆盖新版本的插件版本即可。
|
|
532
255
|
|
|
533
|
-
|
|
256
|
+
## 开发
|
|
534
257
|
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
258
|
+
```bash
|
|
259
|
+
npm install
|
|
260
|
+
npm run build # esbuild:src/client/ → lib/client.js
|
|
261
|
+
npm test # 342 个用例:解析 / 数据层 / HTTP(含宿主认证)/ UI 渲染 / 并发写入
|
|
262
|
+
npm run verify # check + lint + typecheck + build + test + audit(发布前必跑)
|
|
263
|
+
```
|
|
540
264
|
|
|
541
|
-
|
|
265
|
+
- Client 源码在 `src/client/`(ES 模块),构建产物 `lib/client.js` 是单文件模块表,**不要手改**。
|
|
266
|
+
- CI 跑 Node 20 / 22 / 24 × Ubuntu / Windows,并在 Linux job 校验构建产物与源码同步。
|
|
267
|
+
- 详细说明:[`docs/development.md`](https://github.com/FantasyStarry/dsh-todo-time-log/blob/main/docs/development.md)。
|
|
542
268
|
|
|
543
|
-
##
|
|
544
|
-
|
|
545
|
-
1. **`Config` schema 形态(TODO config-schema)**
|
|
546
|
-
本构建把「导出普通 JSON Schema 对象」视为非法:行状态曾为 `status: "unsupported"`,激活报 `Cannot read properties of undefined (reading 'validate')`。当前**不导出 `Config`**(`lib/index.js` 导出 `DEFAULT_CONFIG` 常量用于文档化默认值),`Config.listConfigs` 对该行报告 `status: "absent"`,`apply()` 仍防御式读取 `config.dataFile`。
|
|
547
|
-
适配层:把 `DEFAULT_CONFIG` 处注释里的 zod 片段复原并加 `zod` 依赖即可恢复配置表单,插件其余代码无需改动。
|
|
548
|
-
2. **`inject` 的对象形式(TODO cordis-inject)**
|
|
549
|
-
`export const inject = { optional: [...] }` 会被当前 Loader 当作依赖名为 `optional` 的服务,导致行永久 `pending`。已改为数组形式 `inject = ['webServer', 'tools']`(缺服务时保持 pending 而非崩溃)。`mountOn()` 仍保留 `ctx.inject([...], cb)` 优先、`ctx.get()` 兜底的适配逻辑。
|
|
550
|
-
3. **时区(TODO time-zone)**
|
|
551
|
-
展示固定使用 Host/浏览器本地时区。若将来需要「指定 IANA 时区展示」,只需替换 `lib/time.js` 的 `formatLocalDateTime()` 内部实现(`TODO(time-zone)` 已标注),存储格式无需变更。
|
|
552
|
-
4. **Client 文案与 locale 服务(TODO client-locale)**
|
|
553
|
-
UI 文案集中在 `src/client/i18n.js` 的 `I18N` 字典(构建后进入 `lib/client.js`;zh 默认 / en 兜底),暂未接入宿主 Client locale 服务;槽位注册已带 `label` thunk,后续接入时只需把 `t()` 数据源换成宿主 locale。
|
|
554
|
-
5. **渲染外观未截图验证**
|
|
555
|
-
已通过 `cordis_inspect_query`(Client Slots)确认 `main` 与 `sidebar.panellist` 的 occupant 均为 `active: true`(说明 `client.js` 已在浏览器里成功加载并注册),但本会话没有浏览器截图能力,**页面视觉外观(间距/主题色/明暗切换)未做像素级验收**,建议首次打开时人工确认。
|
|
556
|
-
6. **对话式录入是启发式解析(TODO chat-parser)**
|
|
557
|
-
面板内的自然语言解析是确定性的 Host 侧规则(`lib/parse.js`:`#分类`、填充词剥离、按行/分号拆条、多标记剥离、`@文件` 引用),**不是模型理解**:复杂句式(「下周三之前把报告发给老板」这类含时间/对象语义的句子)只会被当作标题文本。需要真正的语义解析时,走智能体路径(`todo_time_log` 工具,模型自己理解后再结构化调用);解析器已隔离为纯函数 `parseQuickAdd()`,替换点单一。
|
|
558
|
-
7. **文件引用是「工作区相对路径」,不带工作区身份(TODO file-scope)**
|
|
559
|
-
`files: ["lib/store.js"]` 记录的是相对路径;如果你在多个工作区之间切换,同一相对路径在不同工作区可能指向不同文件。当前实现只用于展示/复制路径与分类派生,不解析真实文件,因此不会出错;若要跨工作区精确定位,需要给 Todo 增加 `workspaceRoot` 字段(v4)。
|
|
560
|
-
8. **数据文件位置**
|
|
561
|
-
Host 进程通常没有 `DSH_PROFILE_DIR`(它是 profile 启动器的环境变量),因此默认落在 `~/.dsh/todo-time-log/todos.json`;如需按 profile 隔离,请显式配置 `dataFile`。
|
|
562
|
-
9. **看板 P1 已落地(七期)**
|
|
563
|
-
「移到分类」(分类分组/泳道下改 `project`,带撤销 toast,作为 WCAG 2.5.7 的单指针替代);泳道(状态 × 分类,可折叠、未完成泳道可快捷新建);`/` 搜索、`n` 新建、`f` 筛选三个面板内快捷键;舒适/紧凑密度。指针拖拽仍暂缓(两列板上拖拽≈点完成,成本/收益不匹配),将来若要加,用 HTML5 DnD 且必须保留菜单与键盘路径。
|
|
564
|
-
10. **看板剩余 P2(需要继续扩模型)**
|
|
565
|
-
已落地 `priority`(v4)与 `type`(v5,见 §2);仍待评估:`dueAt`(与日历/甘特时间语义交叉,需先统一「按创建还是按截止」)、`order`(手动排序,需与排序互斥)、指针拖拽增强、多值 `tags`(若单值 type 不够用再考虑 v6)。注意 v5 对旧版插件是「不支持版本」(会归档重建),发布前确认没有旧 Host 共存。
|
|
269
|
+
## 文档
|
|
566
270
|
|
|
567
|
-
|
|
271
|
+
| 文档 | 内容 |
|
|
272
|
+
| --- | --- |
|
|
273
|
+
| [`docs/architecture.md`](https://github.com/FantasyStarry/dsh-todo-time-log/blob/main/docs/architecture.md) | 目录结构、数据模型、Host / Client 架构、四视图与界面层级、toast 实现 |
|
|
274
|
+
| [`docs/development.md`](https://github.com/FantasyStarry/dsh-todo-time-log/blob/main/docs/development.md) | 测试矩阵、手工验证清单、发布流程 |
|
|
275
|
+
| [`docs/limitations.md`](https://github.com/FantasyStarry/dsh-todo-time-log/blob/main/docs/limitations.md) | 已知限制与 TODO(配置 schema、时区、启发式解析等) |
|
|
276
|
+
| [`docs/verification.md`](https://github.com/FantasyStarry/dsh-todo-time-log/blob/main/docs/verification.md) | 独立第三方 QA 验证报告(PASS / FAIL / 盲区) |
|
|
277
|
+
| [`docs/verification-log.md`](https://github.com/FantasyStarry/dsh-todo-time-log/blob/main/docs/verification-log.md) | 本机分阶段验证记录(含九期 DSH 0.2.2 兼容性证据) |
|
|
278
|
+
| [`docs/publishing.md`](https://github.com/FantasyStarry/dsh-todo-time-log/blob/main/docs/publishing.md) | npm 发布流程、OIDC 可信发布、排错 |
|
|
279
|
+
| [`docs/preview/`](https://github.com/FantasyStarry/dsh-todo-time-log/blob/main/docs/preview/README.md) | 更多渲染预览(空态 / 骨架屏 / 错误态 / 各种看板状态) |
|
|
568
280
|
|
|
569
|
-
##
|
|
281
|
+
## License
|
|
570
282
|
|
|
571
|
-
|
|
572
|
-
| --- | --- | --- |
|
|
573
|
-
| bundle 安装 | `install_bundle` 返回包安装成功,`plugin_manager` 中 `include:dsh-todo-time-log` `enabled: true` | PASS |
|
|
574
|
-
| Host 激活 | 关闭再开启后返回 `application: "applied"`、`fiberPhase: "active"` | PASS |
|
|
575
|
-
| 智能体工具注册 | 工具表出现 `todo_time_log`,参数/枚举与设计一致 | PASS |
|
|
576
|
-
| 工具实际执行 | 依次 create → complete → 重复 complete(失败提示) → uncomplete → update → delete,输出本地时间 `2026-11-03 08:37:49`、未完成显示 `-` | PASS |
|
|
577
|
-
| 双调用方一致 | 用 HTTP curl 创建的行,随后被工具 `list` 读到(同一 store);工具删除后 HTTP `list` 归零 | PASS |
|
|
578
|
-
| HTTP 路由 | 运行中实例上 `/health`、`/create`、`/list`、`/complete`(重复 409)、`/uncomplete`、`/delete` 全部符合契约 | PASS |
|
|
579
|
-
| UTF-8 往返 | `python test/live-utf8.py` 全通过(中文标题原样返回,无乱码) | PASS |
|
|
580
|
-
| 持久化 | `~/.dsh/todo-time-log/todos.json` 内容为 `{version, updatedAt, todos[]}`,重载后数据仍在 | PASS |
|
|
581
|
-
| Client 槽位注册 | Client Slots:`main` occupant `key=dsh-todo-time-log active=true`;`sidebar.panellist` occupant `id=dsh-todo-time-log order=30 active=true` | PASS |
|
|
582
|
-
| 分类(v2) | `python test/live-projects.py` 全通过:分类归一化、按分类筛选(含未分类)、`projects` 统计、只改分类、no-op、超长/非字符串 400、`sortBy=project` 未分类置尾 | PASS |
|
|
583
|
-
| 数据迁移 | 真机数据文件已写为 `version: 2`;v1 信封加载→提示→下次写入落盘 v2(`test/smoke.mjs` 断言) | PASS |
|
|
584
|
-
| 工具带分类 | `todo_time_log create project=演示` → 列表输出 `[未完成] [演示] …` 且含「分类:演示(1)」行 | PASS |
|
|
585
|
-
| 甘特图 / 项目分类 UI / 对话录入 | 独立 QA 用 Node 沙箱真实求值 client.js(53/53:甘特几何、对话解析、撤销路径、i18n 成对、主题 token)+ 真机 HTTP 契约 46/46 与补充 6/6;**页面需刷新后才加载新 client.js** | PASS(静态/契约);外观与真实交互 NOT VERIFIED |
|
|
586
|
-
| 迁移复核(QA) | 临时目录 52/52:v1→v2(加载不改写、下次写入落 v2)、v99 归档字节保留、分类修复、乱序时间不崩、shape 归档、duplicate-id/remove 失败语义 | PASS |
|
|
587
|
-
| 分类筛选类型校验 | 重载后真机 `/list {"project": 42}` → `400 PROJECT_INVALID`;正常 `/list` 仍 200 | PASS |
|
|
588
|
-
| 四视图静态审计 | `npm run audit` 28 项全通过:i18n 163 键/语言成对无死键、只 `require('react')`、无 document/DOM、无硬编码颜色、`ttl-` 前缀、四视图与渲染器齐备、6×7 日历、模块格式/双槽位 | PASS |
|
|
589
|
-
| 四视图代码审查 | 实现方自测 shim 169 条断言 + 独立 QA 在 Node 沙箱真实求值 client.js 77 条断言(四视图切换与状态保持、表头排序单一状态源、看板两种分组与计数、日历本地时区落格/跨月/补格/+N、甘特行内操作与表格展开独立、i18n 163 键成对) | PASS(静态) |
|
|
590
|
-
| 工作区 / 安装副本一致性 | `lib/client.js` 字节级一致(141,254 B,3,948 行) | PASS |
|
|
591
|
-
| 四期 @文件 / quick-add | 真机:`/files` 按 `workspace` 返回对应仓库文件(`query=lib/s` 命中 `lib/store.js`);`/quick-add`「新任务 写周报 #工作 @lib/store.js;修复甘特图 @lib/client.js」→ 显式分类 `工作` 优先、仅 @文件 时派生 `lib`;工具 `quick_add` 同路径实测通过;`@"含 空格 路径"` 解析通过 | PASS |
|
|
592
|
-
| 显式「未分类」持久化(QA FAIL-1) | 已修:仅当 `project` 字段缺失时才由文件派生分类。真机 create `project:''` + `files` → 重载插件 → 仍为 `''`、无提示 | PASS |
|
|
593
|
-
| 四期静态审计 | `npm test` **49 + 99 + 47**;`npm run audit` **28 项**(i18n 191 键/语言、22 tokens、147 类名);`npm run check` 覆盖 6 个 lib 文件 | PASS(以本节为最新;上方阶段三/四行保留为历史记录) |
|
|
594
|
-
| 五期分类点选 | 真机:`/projects {workspace: 本仓库}` → 4 个工作区名 + `docs/lib/locale/test` 文件夹;用建议项 `lib` 创建 → `/list` 的 projects 出现 `lib(1)`;`update` 到 `docs` 生效;未知 workspace → 400;无 `fs` 时仍 200(仅缺 folder 来源) | PASS(选择器 UI 外观待刷新后人工确认) |
|
|
595
|
-
| 五期项目选择器 | 真机 `POST /projects {workspace: 本仓库}` → 200,建议含 4 个工作区名(`dsh-todo-time-log`/`feiyu-archive`/`File_Manager_Legacy`/`default-workspace`)与 4 个顶层文件夹(`docs`/`lib`/`locale`/`test`);已用分类按次数排最前(单测覆盖合并/去重/超长过滤) | PASS(选择器 UI 外观待刷新后人工确认) |
|
|
596
|
-
| 页面视觉外观 | 本会话无浏览器截图能力;四视图 + @ 补全下拉 + 文件 chips 需刷新后人工确认 | NOT VERIFIED(需人工看一眼) |
|
|
597
|
-
| 视觉重构静态审计 | `npm run audit` 28 项全通过(i18n **194 键/语言**成对无死键、24 个 alias token、160 个 `ttl-` className 字面量);`npm test` **49 + 99 + 47** 全通过;`node --check` 覆盖 6 个 lib 文件 | PASS |
|
|
598
|
-
| 视觉重构渲染验收 | jsdom 真实挂载 `main` 槽组件后跑 **26 条**交互断言 + **8 条**空态断言 + **2 条**未分类断言,全部通过:唯一主操作、两组分段控件、数据卡片包裹工具栏、toast 不在文档流且不新增 `.ttl-page` 子节点、撤销/关闭、**分类筛选下分段计数与行数一致(命名分类 3/1/2、未分类 1/1/0、无条目的分类 0/0/0)**、方向 `aria-label` 翻转、四视图渲染、对话开关;四个视图的空态都有出口按钮,一次点击清掉全部筛选,无筛选时点击聚焦新建框 | PASS(渲染/结构) |
|
|
599
|
-
| 视觉外观(本次) | [`docs/preview/`](./docs/preview/README.md):用真实 `client.js` + 宿主真实主题 token 渲染的 **18 张**静态预览(默认/完成 toast/对话/看板/甘特/日历/空/骨架/错误/暗色/窄屏 + 看板更多操作/按分类/窄屏/暗色/泳道/紧凑/移动)+ 改版前对照 | 真机需刷新页面后人工确认 |
|
|
600
|
-
| 六期看板静态审计 | `node --check lib/client.js`、`npm test` **49 + 99 + 47**、`npm run audit` **28 项**(i18n **216 键/语言**、24 tokens、177 类名)全通过 | PASS |
|
|
601
|
-
| 六期看板渲染验收 | 仓库外 jsdom + 真实 `client.js` + 宿主主题 token:**22 条**交互断言全通过(两列/计数、已完成默认 10 条 + 显示全部、每卡 checkbox/更多/`<time>`、更多操作展开、按分类分组 4 列 + 状态徽标、切回状态分组、看板状态筛选生效/重置、搜索 1 条命中/无结果/Escape 清空、勾选完成后跨列移动与计数);同一渲染结果产出 `04` 与 `12`–`15` 预览 | PASS(渲染/结构);真机交互需刷新后人工确认 |
|
|
602
|
-
| 七期 Host 契约(priority v4) | `node --check` 3 个 Host 模块通过;`npm test` **61 + 139 + 58** 全绿;v1/v2/v3→v4 迁移、priority 校验/排序/未设置置尾、工具 schema、quick-add 优先级均有断言覆盖 | PASS |
|
|
603
|
-
| 七期 A/B/C 静态审计 | `node --check lib/client.js`;`npm run audit` **28 项**(i18n **239 键/语言**、24 tokens、188 类名);`npm test` **61 + 139 + 58** | PASS |
|
|
604
|
-
| 七期 A/B/C 渲染验收 | 同一套 jsdom harness 扩到 **42 条**断言:泳道(≥6 个、折叠/展开)、紧凑密度 class、移到分类写回 + toast + 撤销、`/`/`n`/`f` 快捷键、优先级徽标/卡片 select 写回 + 撤销、新建表单携带优先级、表格排序含优先级;产出 `04`、`12`–`18` 共 11 张看板预览 | PASS(渲染/结构);真机交互需刷新后人工确认 |
|
|
605
|
-
| 八期 Host 契约(type v5) | `node --check` 3 个 Host 模块通过;`npm test` **79 + 174 + 66** 全绿;v1–v4→v5 迁移、type 校验/归一化/部分更新、quick-add 标记与表单回退均有断言覆盖 | PASS |
|
|
606
|
-
| 八期类型 UI + @ 菜单渲染验收 | jsdom harness **58 条**断言全绿(类型徽标、类型 select 写回 + 撤销、新建表单携带类型、类型筛选、搜索命中类型名、表格类型徽标;`/todo` 草稿的 `@` 菜单含「待办优先级 / 待办类型」候选,选中分别插入 `!high ` / `^bug `,分类候选行为不变);`npm run audit` **28/28**(i18n **253 键/语言**、24 token、189 类名);预览 `04`、`12`–`18` 已按类型徽标重拍 | PASS(渲染/结构);真机需重启插件 + 刷新页面确认 |
|
|
607
|
-
| 九期 DSH 0.2.2-rc.1 兼容性(0.5.2) | 旧 `engines.dsh` 上界 `<0.2.2-0` 会排除预发布 `0.2.2-rc.1`(DSH preflight 用 `semver.satisfies(..., {includePrerelease:true})`),导致插件被禁用;已改为 `>=0.2.0-rc.2 <0.2.3-0` 并升 0.5.2。独立复核:Host 9 个集成点 **0 BREAK / 1 RISK(低) / 8 PASS**(真实 DSH JSON-Schema/工具管线探针 **47/47**);Client **6/6 PASS**(117 槽目录含 `main`/`sidebar.panellist`、`inputTriggers` 契约 0.2.1→0.2.2 仅 i18n 差异、29/29 主题 token 有定义);隔离 `DSH_HOME` 真实 0.2.2-rc.1 端到端 **27/27**(安装放行、路由/工具/命令注册、HTTP CRUD 25/25、重启持久化 6/6、client 注入);反证旧范围被 install preflight 拒绝 + 启动 skip + 路由 404;运行中 desktop 重装 0.5.2 后 live 复验通过(`todo_time_log` 可调用、`/health` loaded 12 条、`main`/`sidebar.panellist` 槽位 active)。详见 [`docs/verification.md` §24](./docs/verification.md) | PASS(契约 + 真机端到端);浏览器像素级 UI 与真实模型回合调用工具未覆盖 |
|
|
283
|
+
[MIT](https://github.com/FantasyStarry/dsh-todo-time-log/blob/main/LICENSE) © FantasyStarry
|
|
608
284
|
|
|
609
285
|
---
|
|
610
286
|
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
发布由 GitHub Actions 工作流 [`.github/workflows/publish.yml`](./.github/workflows/publish.yml) 自动完成:
|
|
614
|
-
推送形如 `v0.5.2` 的 tag 时,工作流会校验 tag 与 `package.json` 版本一致、先 `npm ci`,再跑 `npm run check`、`npm run build`(并用 `git diff` 校验产物与源码同步)/ `npm test` / `npm run audit`、打包预演,
|
|
615
|
-
再通过 **npm 可信发布(OIDC,无需 NPM_TOKEN)** 发布并自动创建 GitHub Release。
|
|
616
|
-
|
|
617
|
-
```bash
|
|
618
|
-
# 日常发版:改版本 → 打 tag → 推送
|
|
619
|
-
npm version patch -m "chore: release v%s" # 或 minor / major
|
|
620
|
-
git push origin main --follow-tags
|
|
621
|
-
```
|
|
622
|
-
|
|
623
|
-
> npm 2026 起已限制 bypass-2FA token「直接发布」(`EOTP`),自动化发布必须走 **staged publishing + OIDC**。
|
|
624
|
-
|
|
625
|
-
首次建包:npmjs.com 生成 granular token 存为仓库 secret `NPM_TOKEN` → Actions → Publish to npm → Run workflow(mode=`stage`)
|
|
626
|
-
→ 到 npmjs.com 包页 **Staged Packages → Approve**(2FA)→ 配 Trusted Publisher(工作流文件名 `publish.yml`,勾选允许 `npm publish`)→ 删除 token。
|
|
627
|
-
之后 tag 推送即全自动;工作流还支持 mode=`publish`(OIDC 直接发布)与 mode=`dry-run`(只预演)。
|
|
628
|
-
完整步骤、版本策略、回滚与排错见 [`docs/publishing.md`](./docs/publishing.md)。
|
|
629
|
-
|
|
630
|
-
[](https://github.com/FantasyStarry/dsh-todo-time-log/actions/workflows/publish.yml)
|
|
287
|
+
**In English** — A todo plugin for [DeepSeek Harness](https://github.com/deepseek-ai) that records and shows each todo's **created and completed time**. Four views (table / board / Gantt / calendar) where every Gantt bar is the real created→completed lifetime; project categories, priority, type, `@workspace-file` references, a `/todo` slash command with natural-language capture, and a `todo_time_log` agent tool. All data stays in a local JSON file.
|