dsh-global-todo 0.16.1 → 0.16.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/CHANGELOG.md +21 -0
- package/docs/WINDOWS.md +97 -0
- package/lib/index.js +1 -1
- package/lib/model.js +11 -3
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,24 @@
|
|
|
1
|
+
## [0.16.2] — 2026-10-22
|
|
2
|
+
|
|
3
|
+
A date-only deadline is local midnight, so a date bound stops swallowing part of its own day.
|
|
4
|
+
|
|
5
|
+
- **`before:` / `after:` with a bare date were anchored at UTC midnight.** `Date.parse('2026-11-01')`
|
|
6
|
+
is UTC midnight by the specification; on +08 that is 08:00 local, so `before:2026-11-01` included
|
|
7
|
+
everything due 00:00–08:00 on the bound day, and an `after:` bound lost the same window in the
|
|
8
|
+
west. Date-only values are now built with the local constructor, matching how `dueAt` is authored
|
|
9
|
+
(issue #6).
|
|
10
|
+
- **This also changes what a bare `dueAt: "2026-11-01"` stores**: it used to become UTC midnight
|
|
11
|
+
and now becomes local midnight — the day the person means, on the machine they are on. Existing
|
|
12
|
+
records are untouched; only new writes differ.
|
|
13
|
+
- **The timezone case is pinned in `test/tz.test.mjs`**, which runs the parser in a child process
|
|
14
|
+
with `TZ=Asia/Shanghai`: CI is UTC, where both anchors are the same number, so the regression
|
|
15
|
+
would otherwise be invisible there. On a platform that ignores `TZ` the test skips rather than
|
|
16
|
+
claiming to have checked.
|
|
17
|
+
- **`docs/WINDOWS.md` gained two answers from the same round**: the date-bound row now states the
|
|
18
|
+
local-midnight contract, and a standing three-question checklist (two-layer semantics, timezone,
|
|
19
|
+
path four states) sits beside the per-feature one, plus the note that ARIA states must be
|
|
20
|
+
verified by attribute, not by searching for the words a screen reader speaks.
|
|
21
|
+
|
|
1
22
|
## [0.16.1] — 2026-10-22
|
|
2
23
|
|
|
3
24
|
Same code as 0.16.0; the tag now points at a commit whose CI is green.
|
package/docs/WINDOWS.md
CHANGED
|
@@ -39,6 +39,103 @@ review then reload, assert no `.tmp` debris, write and read back 500 todos, impo
|
|
|
39
39
|
file, write into a path with spaces and non-ASCII characters, and rename over an existing file. It
|
|
40
40
|
prints one line per check and exits non-zero on the first failure.
|
|
41
41
|
|
|
42
|
+
## 0.16 新增:这张清单是为了让新功能先在你那台机器上过一遍
|
|
43
|
+
|
|
44
|
+
0.12—0.15 加的东西里有四项**能在 Windows 上表现得和 Linux 不同**,其中两项直接碰到路径与文件存在性。
|
|
45
|
+
每一项都写清了「怎么做」和「什么样才算通过」,照着走一遍即可,不需要读代码。
|
|
46
|
+
|
|
47
|
+
### 1. 图片附件(0.15.0,最需要真机验证)
|
|
48
|
+
|
|
49
|
+
附件现在可以有第三种类型 `kind: "image"`,它和文档一样是**引用**(不复制字节),但面板会渲染它。
|
|
50
|
+
|
|
51
|
+
准备工作(PowerShell,路径里故意带空格和中文):
|
|
52
|
+
|
|
53
|
+
```powershell
|
|
54
|
+
$dir = Join-Path $env:TEMP 'gt 图片 测试'
|
|
55
|
+
New-Item -ItemType Directory -Force $dir | Out-Null
|
|
56
|
+
# 先写一张 1x1 的合法 PNG,保证没有 Pictures 目录的机器也能照着走:
|
|
57
|
+
[IO.File]::WriteAllBytes(
|
|
58
|
+
(Join-Path $dir 'shot.png'),
|
|
59
|
+
[Convert]::FromBase64String('iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8z8BQDwAEhQGAhKmMIQAAAABJRU5ErkJggg==')
|
|
60
|
+
)
|
|
61
|
+
# 想用真实截图(名字里带空格/中文)就再拷一份:
|
|
62
|
+
Copy-Item "$env:USERPROFILE\Pictures\*" $dir -ErrorAction SilentlyContinue
|
|
63
|
+
Get-ChildItem $dir | Select-Object Name, Length
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
| 步骤 | 期望 |
|
|
67
|
+
|---|---|
|
|
68
|
+
| 新建一条待办,附件给 `{ "kind": "image", "value": "<上面的路径>\shot.png" }`(或用工具 `global_todo_add` 的 `links`) | 行上出现 16px 缩略图,详情里出现预览图 |
|
|
69
|
+
| 把那张 png 改名(`Rename-Item`),刷新面板 | 变成红色的「文件不在了」chip,**不是一张破图** |
|
|
70
|
+
| 用详情里的「重新挂载」把它指回改名后的文件 | 缩略图恢复,且类型仍是 image(不会变成文档) |
|
|
71
|
+
| 给一个 `.svg` 作为 image | 被拒绝,错误里有「图片附件需要图片扩展名」 |
|
|
72
|
+
| 给一个 >8MB 的图片 | 面板提示「图片超过 8MB」,缩略图不加载 |
|
|
73
|
+
| 路径用反斜杠、带盘符(`D:\…\shot.png`) | 与正斜杠一致;`C:/…/shot.png` 也可以 |
|
|
74
|
+
|
|
75
|
+
后端可直接验(服务在本机时):
|
|
76
|
+
|
|
77
|
+
```powershell
|
|
78
|
+
# 换成真实 id 与索引;路径只从记录里取,请求里传路径会被忽略
|
|
79
|
+
curl.exe -s "http://127.0.0.1:3080/api/global-todo/asset?id=<id>&index=0" -o $env:TEMP\probe.png
|
|
80
|
+
Get-Item $env:TEMP\probe.png | Select-Object Length
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### 2. 重新挂载(0.16.0)
|
|
84
|
+
|
|
85
|
+
- 详情里**只有失效附件**才出现「重新挂载」按钮;点开是内联输入框,预填旧路径;
|
|
86
|
+
- 保存后**原地替换**(附件顺序、标题、类型都不变),不会变成一条新附件;
|
|
87
|
+
- 换成一个**非**绝对路径(如 `notes.md`)应被拒绝;换成一个不存在的路径:若 `requireExistingFiles` 默认为真,也会被拒绝——这正是设计意图,先建文件再挂。
|
|
88
|
+
|
|
89
|
+
### 3. 搜索的 `before:` / `after:`(0.16.0)
|
|
90
|
+
|
|
91
|
+
| 输入 | 期望 |
|
|
92
|
+
|---|---|
|
|
93
|
+
| `before:2026-11-01` | 只留下**截止时间早于本地 11-01 00:00** 的条目;等于该时刻的不算。纯日期按**本地零点**锚定(0.16.2 起;此前锚在 UTC 零点,+08 上会吞掉边界日 00:00–08:00,见 issue #6) |
|
|
94
|
+
| `after:2026-10-01` | 只留下截止时间晚于该时刻的;**没有截止时间的条目两个方向都不出现** |
|
|
95
|
+
| `after:2026-10-01 before:2026-12-01` | 两个条件同时生效(AND) |
|
|
96
|
+
| `before:2026-1`(没打完) | **不报错**,该 token 退化成普通搜索词;这不是 bug |
|
|
97
|
+
| `before:2026-02-31` | 同上:非法日期不当作边界,也不会被当成 3 月 1 日 |
|
|
98
|
+
|
|
99
|
+
注意时区:无时区的日期按**本机本地时间**解释,与截止时间输入框的行为一致。如果你的机器是 +08:00,`before:2026-11-01` 指的是本地 11 月 1 日 00:00。
|
|
100
|
+
|
|
101
|
+
### 4. 读屏(0.16.0)
|
|
102
|
+
|
|
103
|
+
用 NVDA 或 Windows 讲述人过一遍:
|
|
104
|
+
|
|
105
|
+
| 位置 | 期望 |
|
|
106
|
+
|---|---|
|
|
107
|
+
| 分组头(会话 / 时间 / 级别 三种分组下都有) | 朗读「已展开 / 已收起」;用键盘 Space/Enter 可切换 |
|
|
108
|
+
| 顶部筛选(全部 / 未完成 / …) | 当前筛选项朗读为「已按下」 |
|
|
109
|
+
| 操作提示(例如「已让某对话抓取」) | 作为状态区域被朗读一次,不会抢焦点 |
|
|
110
|
+
| 错误横幅 | 立即朗读(alert) |
|
|
111
|
+
| 纯图标按钮(关闭、更多、保存等) | 有可读名称,不是「按钮」两个字 |
|
|
112
|
+
|
|
113
|
+
> 验证方法:这些状态是**读屏软件从 ARIA 属性原生翻译**出来的,所以查代码里是否挂了属性
|
|
114
|
+
> (`aria-expanded` / `aria-pressed` / live region),**不要**去源码里搜「已展开」「已按下」字面量——
|
|
115
|
+
> 那样会误判成 FAIL(`#6` 里对方第一版就是这么误判的,随后自己更正)。
|
|
116
|
+
|
|
117
|
+
### 回归(每次都跑,10 秒)
|
|
118
|
+
|
|
119
|
+
```powershell
|
|
120
|
+
npm run check:windows # 7 项真实文件系统冒烟
|
|
121
|
+
npm run check:contracts # 58 项跨字段契约,模块级、离线
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
两项都过,再填上面四节的表格。哪一格和期望不符,就按「What to report if something fails」的四条信息回给我。
|
|
125
|
+
|
|
126
|
+
## 常设三问:每版发版前必答
|
|
127
|
+
|
|
128
|
+
三轮 Windows 测试(#2/#4/#5/#6)打的都是同一类 bug:**同一个值在两层或两平台被不同规则解释**,而每层单测都绿。
|
|
129
|
+
所以除了上面具体的功能清单,发版前固定问这三句:
|
|
130
|
+
|
|
131
|
+
1. **两层口径**:新字段/新解析器在 **HTTP 校验层**与**文件容错层**的行为是否都有定义,且文档说的是同一件事?
|
|
132
|
+
(`#5` 的 `null`、`#3` 的 `settings` 形状,都是这一问漏掉的。)
|
|
133
|
+
2. **时区**:新的时间/日期输入,在**非 UTC 机器**上过判别对了吗?要覆盖边界日本身、非整点时区、DST。
|
|
134
|
+
(`#6` 就是这一问:`Date.parse('YYYY-MM-DD')` 是 UTC 零点,而 `dueAt` 按本地录。)
|
|
135
|
+
3. **路径四态**:新的路径处理,**盘符 / 反斜杠 / UNC / 大小写**都过了吗?(`#2`、`#3` 的豁免路径、Windows 测试轮都在这。)
|
|
136
|
+
|
|
137
|
+
这三问的教训来源是 Windows 测试轮 #2–#6;有 `npm run check:contracts` 兜住一半,剩下的一半只能靠发版前问。
|
|
138
|
+
|
|
42
139
|
## What to report if something fails
|
|
43
140
|
|
|
44
141
|
1. the check name from the `FAIL` line and the full message;
|
package/lib/index.js
CHANGED
|
@@ -37,7 +37,7 @@ import { createStore, resolveStorePath } from './store.js'
|
|
|
37
37
|
import { createTools, normalizeToolGroups, selectTools } from './tools.js'
|
|
38
38
|
|
|
39
39
|
/** Must equal `package.json`'s version; `npm run check` enforces that. */
|
|
40
|
-
export const VERSION = '0.16.
|
|
40
|
+
export const VERSION = '0.16.2'
|
|
41
41
|
|
|
42
42
|
/** Plugin id. Also the Loader row id in `cordis.patch.yml`. */
|
|
43
43
|
export const name = 'dsh-global-todo'
|
package/lib/model.js
CHANGED
|
@@ -139,9 +139,17 @@ export function parseDueText(value) {
|
|
|
139
139
|
if (match[4] !== undefined && (Number(match[4]) > 23 || Number(match[5]) > 59 || Number(match[6] ?? 0) > 59)) {
|
|
140
140
|
return { ok: false, reason: '时间不合法' }
|
|
141
141
|
}
|
|
142
|
-
// A
|
|
143
|
-
//
|
|
144
|
-
|
|
142
|
+
// A date without a time means "that day, as the person reading the list experiences it", so
|
|
143
|
+
// it is anchored at **local** midnight. `Date.parse('2026-11-01')` is UTC midnight per the
|
|
144
|
+
// specification — on +08 that is 08:00 local, which made `before:2026-11-01` swallow the
|
|
145
|
+
// first eight hours of the bound day (issue #6). Date-times keep their usual meaning: no
|
|
146
|
+
// offset means local, an offset means that offset.
|
|
147
|
+
let parsed
|
|
148
|
+
if (match[4] === undefined) {
|
|
149
|
+
parsed = new Date(year, month - 1, day, 0, 0, 0, 0).getTime()
|
|
150
|
+
} else {
|
|
151
|
+
parsed = Date.parse(text)
|
|
152
|
+
}
|
|
145
153
|
if (Number.isNaN(parsed)) return { ok: false, reason: '无法解析' }
|
|
146
154
|
return { ok: true, iso: new Date(parsed).toISOString() }
|
|
147
155
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-global-todo",
|
|
3
|
-
"version": "0.16.
|
|
3
|
+
"version": "0.16.2",
|
|
4
4
|
"icon": "./docs/icon.svg",
|
|
5
5
|
"description": "Cross-session todo list for DeepSeek Harness: one JSON file a person and an agent both write to, with step plans that ship as a numbered hand-off, session read-back, and a human confirmation before an agent's \"done\" counts.",
|
|
6
6
|
"type": "module",
|