dsh-worktodo 0.0.0-stage → 0.1.0

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/CHANGELOG.md ADDED
@@ -0,0 +1,76 @@
1
+ # 更新日志
2
+
3
+ ## 0.1.0
4
+
5
+ 首个可用版本。
6
+
7
+ ### 核心
8
+
9
+ - **人机共享的工作待办**:一份逻辑(`src/service.mjs`)同时供侧栏面板与 agent 工具调用,
10
+ 所以「面板点完成」和「agent 调 `worktodo_done`」在数据上的行为完全一致。
11
+ - **富字段**:优先级(高/普通/低)、截止日期、项目、备注——补上官方 `todo_write`
12
+ 只有「内容 + 三态」的空缺。
13
+ - **逐项编辑**:按 id、完整标题或唯一标题前缀定位单条修改,不像 `todo_write` 那样整表替换。
14
+ - **跨会话持久**:数据不属于任何会话,重启、换会话、换项目都还在。
15
+ - **分组视图**:逾期 / 今天 / 本周 / 以后 / 已完成,含统计(未完成 / 逾期 / 今天 / 进行中 / 今日完成)。
16
+
17
+ ### agent 工具
18
+
19
+ - `worktodo_add` / `worktodo_list` / `worktodo_update` / `worktodo_done` / `worktodo_remove`
20
+ - `worktodo_adopt`:把本会话 `todo_write` 的条目提升为工作待办(按标题去重),
21
+ 桥接官方会话计划与人的长期列表。
22
+
23
+ ### 存储
24
+
25
+ - 优先官方 `ctx.storageDomain`(`$DSH_HOME/storages`):跨重启保留但不进会话事件日志。
26
+ - 回退 JSON 文件(`$DSH_HOME/worktodo/todos.json`):**原子写**(临时文件 + rename)、
27
+ 写入串行化、坏文件不静默清空、单条坏记录跳过;文件是人能直接打开手改、能进 git 的形态。
28
+ - 支持 `storePath` 配置固定位置;`~` 会展开。
29
+
30
+ ### 兼容与降级
31
+
32
+ - 目标 **DSH 0.2.x**(0.2.0-rc.2 实测),Node ≥ 22.19.0,`dependencies` 为空。
33
+ - 平台包全部声明为**可选 peer**:`tools` 或 `webServer` 缺失时,插件以「少一半能力」的形态继续工作;
34
+ `@deepseek-ai/dsh-tools` 解析不到时只失去工具能力,面板照常可用(日志写明原因)。
35
+
36
+ ### 修复(发布前真机回归发现)
37
+
38
+ - **工具返回值必须是 lossless JSON**:`worktodo_add` 在不传截止日期时返回了
39
+ `due: undefined`,被 DSH 的返回值校验判为无效,**整次调用被拒**——而「加一条没有截止日期的
40
+ 待办」正是最常见的用法(`worktodo_update` 清空截止日期后同理)。已改为可选字段缺席时**删键**
41
+ 而不是留 `undefined`。真机重试后暴露的第二个现象见 `docs/verification.md`:
42
+ 以 `link:` 安装的插件,源码改动需要**重启**才会换模块 generation。
43
+ - 新增可观测性:`/worktodo/health` 现在报告运行中的实际 `version` 与
44
+ `tools: { status, registered, names, error }`——工具静默没注册这类问题不用再翻日志猜。
45
+
46
+ ### 测试(116 例,均不需要启动 DSH)
47
+
48
+ - `tools/verify-records.mjs`(22):日期归一化与相对日期、状态/优先级同义词、完成时间维护、
49
+ 引用解析(id / 标题 / 前缀 / 歧义)、分组边界(本周日 vs 下周一)、统计口径、筛选、坏记录修复。
50
+ - `tools/verify-store.mjs`(11):官方 `defineDomain` 契约、领域名合法、记录 schema 与官方读取路径一致、
51
+ domain 后端增删改查、文件后端原子写与并发写、坏文件与坏记录容忍、`~` 展开。
52
+ - `tools/verify-service.mjs`(18):显式状态(`empty-title` / `not-found` / `ambiguous-ref` /
53
+ `empty-patch` / `store-unreadable` / `store-write-failed`)、完成与取消完成的 `completedAt` 语义、
54
+ 统计不随筛选变化、提升去重、变更通知。
55
+ - `tools/smoke-host.mjs`(26):假 ctx 里跑通 `apply` → 7 条路由 → 服务 → 存储的完整增删改查,
56
+ 方法围栏与 `connection` 准入,失败路径(空标题、坏 JSON、未知 filter),health 自检字段,
57
+ 6 个工具经**官方 `defineTool`** 校验注册并真实执行,以及 5 条「工具返回值必须无损」的断言——
58
+ 判定用的是 **DSH 自己的校验器** `snapshotJsonValue`(来自 `@deepseek-ai/dsh-util-values`,
59
+ 就是 `@deepseek-ai/dsh-tools` 报 "value is not lossless JSON" 时用的那一个函数),
60
+ 并有一条专门断言「旧写法 `due: undefined` 必须被它拒绝」,用来证明这条守卫确实有牙齿。
61
+ - `tools/smoke-contract.mjs`(12):**客户端 ↔ 宿主契约**。从 `src/client/index.js` 静态提取它实际请求的
62
+ 路径与字段名,与宿主真实注册的路由、真正消费的字段比对——防「字段名打错 → 那个功能永远静默失效」
63
+ (例如截止日期永远存不上,而没有任何红灯);再用客户端字面上的报文形状打真实路由,验证两处清空约定
64
+ (create 用 `due: null`,行内编辑用 `due: ''` / `project: ''`)以及 toggle / adopt / remove / list 的字段完整性。
65
+ 已做变异验证:路径改成 `/statuss` 会红 3 条,字段 `due` 改成 `duedate` 会红 1 条。
66
+ - `tools/smoke-client.mjs`(14):客户端 bundle 的模块加载器格式(id 必须等于包名)、
67
+ bundle 只允许 require react(官方规范禁止依赖 Harness 客户端包)、侧栏与主面板注册到正确的 slot
68
+ 且 id/key 同名、locale 字典 zh/en 键一一对应、**首屏渲染不抛异常**(白屏是最致命的失败模式)、
69
+ 挂载后确实发起 `/worktodo/list` 请求、`useProjection` 缺失或返回空值时安全。
70
+ - `tools/verify-submission.mjs`(13):**收录 PR 的本地门禁**。上游(deepseek1024.com 的目录仓)
71
+ 会对提交的 `catalog/plugins/*.json` 跑静态审查并**读仓库核对**,所以先在本地把这些规则跑一遍,
72
+ 免得提一个必被拒的 PR:字段集合与 schema 一致、id 与文件名派生规则、`repository`/`name` 与
73
+ `package.json` 一致、`category` 在 11 个法定分类内、`added` 是合法日期、描述里没有最上级措辞。
74
+ 另外两条只在 **GitHub 分发路径**下才致命、npm 发布看不出来的检查:`main` / `cordis.patch.yml` /
75
+ `lib/client.js` 等**必须都在 `git ls-files` 里**(git 安装只拿跟踪文件、且不跑构建),
76
+ 以及 patch 里那一行的 `name` **必须等于包名**(否则新用户装上去 Loader 解析不到插件)。
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 oli20
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.en.md ADDED
@@ -0,0 +1,101 @@
1
+ # Work Todo · dsh-worktodo
2
+
3
+ **A work todo list that belongs to you, not to a session.** A dedicated DSH sidebar panel, plus `worktodo_*` tools so the agent reads and writes **the same** list you do.
4
+
5
+ > Works with **DSH 0.2.x** (tested on 0.2.0-rc.2) · **zero npm runtime dependencies** (`dependencies: {}`) · community plugin, unofficial
6
+
7
+ ## The gap it fills
8
+
9
+ DSH ships `todo_write`: the plan of **one agent session**. It replaces the whole list every call, has only `pending / in_progress / completed`, and — quoting the official docs — "the list belongs to the one agent session that created it… there is no way to share a list across agents". No priority, no due date, no project, no per-item edit. Close the session and the plan is gone.
10
+
11
+ So the things that actually need to survive — waiting on a reply, waiting on review, shipping next week — have nowhere to live.
12
+
13
+ **This list is for the human.** Priorities, due dates, projects, notes. It persists across sessions and projects. The agent can read and write it, but the source of truth is yours.
14
+
15
+ | | official `todo_write` | **dsh-worktodo** |
16
+ |---|---|---|
17
+ | Owned by | one agent session | **you** (cross-session, cross-project) |
18
+ | Update | whole-list replacement | **per-item create / edit / delete** |
19
+ | Fields | content + 3 states | content + **priority + due date + project + notes** |
20
+ | Shared across agents | ❌ not supported | ✅ one list, human and agent both read/write |
21
+ | Views | in-session checklist | **overdue / today / this week / later / done** + stats |
22
+ | Storage | session event log | `$DSH_HOME/storages` (official storage) or `~/.dsh/worktodo/todos.json` |
23
+
24
+ The two are complementary: `worktodo_adopt` promotes the entries worth keeping from this session's `todo_write` into your work list.
25
+
26
+ ## Install
27
+
28
+ **Option 1: straight from GitHub** (no npm package needed; restart DSH afterwards)
29
+
30
+ ```bash
31
+ dsh plugin --profile desktop add github:olimc2016/dsh-worktodo
32
+ ```
33
+
34
+ This path is verified (see `docs/verification.md`): the browser half's build output `lib/client.js` is committed,
35
+ and the package declares no `prepare`/`install` build script, so a git install needs **no build authorization**
36
+ and pulls none of the development dependencies — measured: **1 package** resolved, **316 KB** installed,
37
+ no nested `node_modules`, and the installed tree matches the npm tarball file for file.
38
+
39
+ **Option 2: from npm** (once the package is published)
40
+
41
+ ```bash
42
+ dsh plugin --profile desktop add dsh-worktodo
43
+ ```
44
+
45
+ **Option 3**: use the DSH sidebar — **Plugins → Add plugin** — and enter `dsh-worktodo` or the repository URL.
46
+
47
+ ## Usage
48
+
49
+ **Panel**: stats on top (open / overdue / today / in progress / completed today), a quick-add box (title + due date + priority, Enter to save), then the grouped list. Each row can be checked off, edited inline by clicking the title, or deleted (with a second-click confirmation).
50
+
51
+ **Agent tools**: the model can file, query, advance and archive your work todos without you relaying anything.
52
+
53
+ | Tool | Purpose |
54
+ |---|---|
55
+ | `worktodo_add` | File a task (optional due date, priority, project, notes) |
56
+ | `worktodo_list` | List with `open` / `today` / `overdue` / `week` / `doing` / `completed` / `all`, filterable by project or keyword |
57
+ | `worktodo_update` | Edit one item without touching the rest |
58
+ | `worktodo_done` | Mark done, or back to in-progress / pending |
59
+ | `worktodo_remove` | Delete (use `worktodo_done` to keep the record instead) |
60
+ | `worktodo_adopt` | Promote this session's `todo_write` entries, de-duplicated by title |
61
+
62
+ One sentence is enough:
63
+
64
+ > "File the three things I need to follow up today into my work todos, and mark tomorrow morning's as high priority."
65
+
66
+ ## Where the data lives
67
+
68
+ Chosen automatically; the actual location is reported by `/worktodo/health` and shown at the bottom of the panel:
69
+
70
+ 1. **Official storage** (`ctx.storageDomain`, mounted by DSH by default) — data lands in `$DSH_HOME/storages`. This is the documented place for plugin-owned state that must survive restarts without becoming session events; a few dozen todos do not belong in the session history.
71
+ 2. **JSON file fallback** — `$DSH_HOME/worktodo/todos.json`, used automatically in minimal compositions without the storage service. That file is meant to be opened, hand-edited and committed to git; a corrupted file is never silently wiped, and a single bad record is skipped rather than losing the list.
72
+
73
+ Pin your own path in `cordis.patch.yml`:
74
+
75
+ ```yaml
76
+ - id: worktodo
77
+ name: 'dsh-worktodo'
78
+ config:
79
+ storePath: /Users/me/notes/worktodo.json
80
+ ```
81
+
82
+ ## Compatibility
83
+
84
+ - **DSH 0.2.x** (tested against 0.2.0-rc.2 APIs; DSH's version gate rejects plugins pinned to older lines)
85
+ - **Node ≥ 22.19.0**
86
+ - No runtime npm dependencies. Platform packages (`@deepseek-ai/dsh-tools`, `dsh-storage-domain`, `dsh-home-paths`) are **optional peers** resolved from the DSH installation; if one genuinely cannot be resolved the plugin degrades to panel-only instead of failing to load, and logs the reason.
87
+
88
+ ## Development
89
+
90
+ ```bash
91
+ pnpm install # esbuild ships a prebuilt binary; no approve-builds needed
92
+ pnpm run build # build lib/client.js (browser half)
93
+ pnpm test # 116 cases: data model / storage / service / host wiring and tools / client contract / client bundle / catalog gate
94
+ pnpm run watch # incremental client build
95
+ ```
96
+
97
+ No DSH instance is needed to run the suite: `verify-records` (pure data), `verify-store` (real file IO plus a genuine **official `defineDomain` contract check**), `verify-service` (explicit statuses and boundaries), `smoke-host` (apply → routes → service → storage in a fake context, plus tool registration and execution with schemas validated by the **official `defineTool`**), `smoke-contract` (extracts the paths and field names the client actually sends and checks them against the host's real routes — catching silent failures where a mistyped field means, for instance, that due dates never save), `smoke-client` (bundle format, slot registration, and a first render that must not throw), and `verify-submission` (a local gate for the catalog PR: fields, filename, category and date, plus checks that only matter on the GitHub distribution path, such as whether every file a git install needs is actually tracked).
98
+
99
+ ## License
100
+
101
+ MIT © 2026 oli20
package/README.md CHANGED
@@ -1,3 +1,115 @@
1
- # Temporary Holding Version
1
+ # 工作待办 · dsh-worktodo
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ **归你所有、跨会话持久的工作待办。** DSH 侧栏一个独立面板,agent 通过 `worktodo_*` 工具读写**同一份**列表。
4
+
5
+ > 兼容 **DSH 0.2.x**(0.2.0-rc.2 实测)· **零 npm 依赖**(`dependencies: {}`)· 社区插件,非官方
6
+
7
+ ---
8
+
9
+ ## 它解决什么问题
10
+
11
+ DSH 自带的 `todo_write` 是**当前这个 agent 会话**的计划:整表替换、只有 `pending / in_progress / completed` 三态、**只属于那一个会话**。官方文档写得很直白——「任务列表属于创建它的那一个 agent 会话……不存在跨 agent 共享列表的方式」,而且**没有优先级、没有截止日期、没有项目、没有逐项编辑**。会话一关,那份计划就翻篇了。
12
+
13
+ 于是真实工作里那些「明天还得接着办」的事没有地方放:等对方回复、等评审、下周要交的东西、这个月要跟的活。
14
+
15
+ **这份列表是给人的**:有优先级、截止日期、项目和备注,跨会话、跨项目一直在;agent 能读能写,但真源在你手里。
16
+
17
+ | | 官方 `todo_write` | **dsh-worktodo** |
18
+ |---|---|---|
19
+ | 归属 | 单个 agent 会话 | **你**(跨会话、跨项目) |
20
+ | 更新方式 | 整表替换 | **逐项增删改** |
21
+ | 字段 | 内容 + 三态 | 内容 + **优先级 + 截止日期 + 项目 + 备注** |
22
+ | 跨 agent 共享 | ❌ 官方明确不支持 | ✅ 同一份列表,人与 agent 都读写 |
23
+ | 视图 | 会话内清单 | **逾期 / 今天 / 本周 / 以后 / 已完成** 分组 + 统计 |
24
+ | 数据位置 | 会话事件日志 | `$DSH_HOME/storages`(官方 storage)或 `~/.dsh/worktodo/todos.json` |
25
+
26
+ 两者不冲突:`worktodo_adopt` 可以把你这个会话的 `todo_write` 里值得留下的条目**一键提升**成工作待办。
27
+
28
+ ## 安装
29
+
30
+ **方式一:直接从 GitHub 装**(不依赖 npm 上的包;装完重启 DSH)
31
+
32
+ ```bash
33
+ dsh plugin --profile desktop add github:olimc2016/dsh-worktodo
34
+ ```
35
+
36
+ 这条路是实测过的(见 `docs/verification.md`):仓库里提交了浏览器半侧的构建产物 `lib/client.js`,
37
+ 包内没有 `prepare`/`install` 这类构建脚本,所以 **git 安装不需要任何构建授权**,也不会把开发依赖装进来
38
+ ——实测只解析 **1 个包**、整个安装 **316 KB**、包内无嵌套 `node_modules`,且安装结果与 npm 发布包逐一相同。
39
+
40
+ **方式二:从 npm 装**(包发布后可用)
41
+
42
+ ```bash
43
+ dsh plugin --profile desktop add dsh-worktodo
44
+ ```
45
+
46
+ **方式三**:在 DSH 侧栏「插件 → 添加插件」里填 `dsh-worktodo` 或仓库地址。
47
+
48
+ 装好后侧栏会多一个**「待办」**入口,设置 → 插件里也能看到它。
49
+
50
+ ## 用起来
51
+
52
+ **面板**:顶部是统计(未完成 / 逾期 / 今天 / 进行中 / 今日完成),中间是快速添加框(标题 + 截止日期 + 优先级,回车即存),下面是分组列表。每行可以直接勾完成、点标题行内改、删除(二次确认)。
53
+
54
+ **agent 工具**:模型可以自己登记、查询、推进和归档工作待办,不需要你转述。
55
+
56
+ | 工具 | 用途 |
57
+ |---|---|
58
+ | `worktodo_add` | 登记一件事(可带截止日期、优先级、项目、备注) |
59
+ | `worktodo_list` | 查列表:`open` / `today` / `overdue` / `week` / `doing` / `completed` / `all`,可按项目或关键词过滤 |
60
+ | `worktodo_update` | 逐项修改(不会像整表替换那样动到别的条目) |
61
+ | `worktodo_done` | 标完成,或标回进行中 / 待办 |
62
+ | `worktodo_remove` | 删除(做完的事请用 `worktodo_done` 留痕) |
63
+ | `worktodo_adopt` | 把本会话 `todo_write` 的条目提升为工作待办,按标题去重 |
64
+
65
+ 给 agent 的一句话就能用起来:
66
+
67
+ > 「把今天要跟进的三件事记到工作待办里,明天上午的标高优先级。」
68
+
69
+ ## 数据存在哪
70
+
71
+ 按优先级自动选择,`/worktodo/health` 与面板底部都会显示当前实际位置:
72
+
73
+ 1. **官方 storage**(`ctx.storageDomain`,DSH 默认挂载)——数据落在 `$DSH_HOME/storages`。
74
+ 这是官方给「插件自有、跨重启保留、不进会话事件」的数据指定位置:几十条待办不必变成会话历史。
75
+ 2. **JSON 文件回退**——`$DSH_HOME/worktodo/todos.json`。storage 服务缺失的精简组合里自动走这条路;
76
+ 这个文件**是人能直接打开、手改、进 git** 的形态,写坏了也不会被静默清空(单条坏记录跳过,其余照常读出)。
77
+
78
+ 想在 `cordis.patch.yml` 里固定路径(例如放进自己的笔记仓库):
79
+
80
+ ```yaml
81
+ - id: worktodo
82
+ name: 'dsh-worktodo'
83
+ config:
84
+ storePath: /Users/me/notes/worktodo.json
85
+ ```
86
+
87
+ ## 兼容
88
+
89
+ - **DSH 0.2.x**(官方版本闸门会把锁在旧版本的插件拒掉,本插件按 0.2.0-rc.2 的 API 编写并实测)
90
+ - **Node ≥ 22.19.0**
91
+ - `dependencies` 为空:不装任何运行期 npm 包。平台包(`@deepseek-ai/dsh-tools`、
92
+ `dsh-storage-domain`、`dsh-home-paths`)都声明为**可选 peer**——DSH 自带这些包,从安装目录解析;
93
+ 真解析不到时插件会降级成「只有面板」而不是加载失败(日志里会写一行原因)。
94
+
95
+ ## 开发
96
+
97
+ ```bash
98
+ pnpm install # esbuild 用预编译二进制,无需 approve-builds
99
+ pnpm run build # 打 lib/client.js(浏览器半侧)
100
+ pnpm test # 116 个用例:数据模型 / 存储 / 服务 / 宿主接线与工具 / 客户端↔宿主契约 / 客户端 bundle / 收录门禁
101
+ pnpm run watch # 改客户端时增量构建
102
+ ```
103
+
104
+ 测试策略:不用启动 DSH 就能跑完——
105
+ `verify-records`(纯数据)、`verify-store`(真实文件 IO + **官方 `defineDomain` 契约校验**)、
106
+ `verify-service`(显式状态与边界)、`smoke-host`(假 ctx 里跑通 apply → 路由 → 服务 → 存储,
107
+ 以及工具注册与执行,工具 schema 由**官方 `defineTool`** 校验)、
108
+ `smoke-contract`(从客户端源码提取它实际发的路径与字段,与宿主真实路由比对——防字段名打错导致的
109
+ 静默失效,例如截止日期永远存不上)、`smoke-client`(bundle 格式、slot 注册、首屏渲染不抛异常)、`verify-submission`
110
+ (收录 PR 的本地门禁:字段/文件名/分类/日期 + 「git 安装必需的文件是否都已入库」这类只在 GitHub
111
+ 分发路径下才致命的检查)。
112
+
113
+ ## License
114
+
115
+ MIT © 2026 oli20
@@ -0,0 +1,16 @@
1
+ # worktodo bundle patch:把自己插进 DSH 的 profile 层栈。
2
+ # 宿主半侧(Node)由 Loader 按 `name` 加载 lib/index.js;
3
+ # 浏览器半侧由 package.json 的 dsh.client 声明并组合成 bundle。
4
+ #
5
+ # row id 必须唯一:这里用 `worktodo`(与包名短名一致,也用于面板 slot id 与 /worktodo 路由前缀)。
6
+ # 官方 @deepseek-ai/dsh-tool-todo 占的是 `tool-todo`,不冲突——两者互补:
7
+ # 官方那份是单个 agent 会话的计划(整表替换、三态),本插件是人的跨会话工作待办。
8
+ - insert:
9
+ - id: worktodo
10
+ name: 'dsh-worktodo'
11
+ config:
12
+ # 留空即用默认值:优先官方 storage($DSH_HOME/storages),
13
+ # storage 不可用时回退 $DSH_HOME/worktodo/todos.json。
14
+ # 想固定存到自己的路径(例如放进 git 仓库)就写 storePath:
15
+ # storePath: /Users/me/notes/worktodo.json
16
+ {}
package/icon.svg ADDED
@@ -0,0 +1,13 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48" width="48" height="48" role="img" aria-label="工作待办">
2
+ <!-- 底色用实心圆角块:任何主题(深/浅)下都能看清,不依赖宿主背景色 -->
3
+ <rect x="2" y="2" width="44" height="44" rx="12" fill="#2563EB"/>
4
+ <!-- 第一行:已勾选的事(勾 + 删除线感的短线) -->
5
+ <path d="M12.5 16.5l3.2 3.2 6.3-6.8" fill="none" stroke="#FFFFFF" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/>
6
+ <rect x="26" y="14.5" width="10" height="3" rx="1.5" fill="#FFFFFF" opacity="0.75"/>
7
+ <!-- 第二行:进行中的事(空心圈) -->
8
+ <circle cx="14.2" cy="27.5" r="3.4" fill="none" stroke="#FFFFFF" stroke-width="2.4"/>
9
+ <rect x="26" y="26" width="10" height="3" rx="1.5" fill="#FFFFFF" opacity="0.75"/>
10
+ <!-- 第三行:还没开始的事(空心圈,更淡) -->
11
+ <circle cx="14.2" cy="37.5" r="3.4" fill="none" stroke="#FFFFFF" stroke-width="2.4" opacity="0.6"/>
12
+ <rect x="26" y="36" width="7" height="3" rx="1.5" fill="#FFFFFF" opacity="0.55"/>
13
+ </svg>