dsh-tiddlywiki 0.31.1 → 0.32.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/docs/CHANGELOG.md +2 -0
- package/docs/development.md +47 -0
- package/docs/features.md +1 -0
- package/lib/client.js +18 -14
- package/lib/index.js +55 -1
- package/lib/index.js.map +1 -1
- package/package.json +2 -2
- package/src/client/i18n-frame.ts +8 -0
- package/src/client/index.ts +4 -0
- package/src/client/tid-parse.ts +61 -0
- package/src/client/tid-preview.ts +182 -0
- package/src/index.ts +3 -0
package/docs/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,8 @@
|
|
|
4
4
|
|
|
5
5
|
> 最近几个主要版本的一句话记录(完整变更见 [Releases](https://github.com/bbqisbbq/dsh-tiddlywiki/releases) / git log)。
|
|
6
6
|
|
|
7
|
+
- **v0.32.0**(2026-10-26):**新增 `.tid` / `.tiddler` 文件的侧栏预览**(⑦ 的第三项)。wiki 文件夹本身就是一堆 `.tid`,而 DSH 的文件预览把未知扩展名当纯文本 —— 打开一个 `.tid` 看到的是 `title: …` 字段行加原始 wikitext,`[[链接]]`/`<<宏>>` 几乎不可读。现在注册一个预览实现:元数据用 `ctx.documentPreviews.register({ id, extensions: ['tid','tiddler'], priority, title, loading: 'text-pages' })`,正文以**同一个 id** 注入 keyed、Session 作用域的 `sidebar.right.tab.document`;正文把 wikitext POST 到本插件自己的 `/dsh-tiddlywiki/render`(铁律 #7 的净化管道)换成真 TW 渲染结果,字段行单独排一行摘要。**绝不白屏**是这个功能的硬约束:拿不到内容 → "等待文件内容…";只有字段没有正文 → 明确说明;渲染失败 → `<pre>` 原文 + 一句"渲染失败,下面是原文"。宿主没有 `documentPreviews` 服务时**什么都不做**(老版本 DSH 不受影响),且元数据与正文必须**成对**(拿不到 slots 就把已注册的元数据撤回,单边注册会让面板空白)。契约是从宿主 `app.asar` 的 `dsh-client-ui-sidebar-documentpreview/lib/client.js` 逐行核实后写的(含 `TextPreview` 的完整 props 签名与 `content` 三态),全部记在 `docs/development.md`。**守门**:新增 `scripts/verify-tid-preview.mjs`(进 `verify:unit`,10 条)——纯解析用例(字段/CRLF/没有字段必须整份当正文/只有字段/空文本)+ **成对注册**(两处必须用同一个 `TID_PREVIEW_ID` 常量、正文必须先 `slots.inject` 再 `register`)+ **绝不白屏**(`<pre>` 兜底、三种空态各自的 `t()` 文案)+ 渲染必须走 `RENDER_ENDPOINT` 且失败回 `null` 不抛 + 可选依赖的静默跳过与回滚。纯逻辑拆在 `client/tid-parse.ts`(宿主 barrel 也要导它,而宿主包里不能出现 `react`),`client/tid-preview.ts` 只放 UI。客户端 i18n 473 → 477 键。
|
|
8
|
+
|
|
7
9
|
- **v0.31.1**(2026-10-26):**新增 `tiddlywiki_export`(笔记落盘,配合 `present` 交付)**。「把这条笔记给我」此前只有两条路:正文贴进回复(长笔记会撞回执预算,`get` 还得分页),或者让人类自己去 TW 里复制;DSH 的 `present` 能交付**文件**,但一直缺「把笔记变成文件」这一步。新工具 `export(title, format?, dir?)`:读一篇笔记 → 按格式落盘 → 回 `{path, bytes, format}`,助手紧接着 `present([{path, description}])` 即可。三种格式:`md`(YAML frontmatter + 正文,默认)、`tid`(TiddlyWiki 原生字段行 + 空行 + 正文)、`json`(完整 tiddler)。三条纪律都不是可选项:① **绝不覆盖同名文件**(导出是旁路产物,静默覆盖等于毁掉上次的交付物);② **`$:/plugins/dsh-tiddlywiki/` 系统条目拒绝导出**(可能是密钥,走铁律 #6 的共享原语);③ 读失败 ≠ 不存在(铁律 #3)。自定义字段两处都收(顶层键 + `fields`)—— 只收一处会让「导出丢字段」。守门 `scripts/verify-tools.mjs` 新增一条 7 组断言的 E2E(三格式内容逐项核对、同名拒绝、不存在回 `notFound` 且不写空文件、系统条目拒绝、相对 `dir` 拒绝),并与既有的「真实返回值必须通过 `output.schema`」自动联动。工具数 16 → 17(AGENTS §2、`verify-tool-views` / `verify-prompt` 的计数断言、`TOOL_VIEW_KEYS` + `TOOL_LABELS` + `i18n-card` 的 `card.toolExport` 同步)。
|
|
8
10
|
|
|
9
11
|
- **v0.31.0**(2026-10-26):**对齐 DSH 0.2.1 的一轮改造 + 两条真实回归修复**。① **选库入口搬到官方座位**:`conversation.input.selector.context` 在 0.2.1 里**不存在**(v0.28.8 起的 `scope-seat.ts` 整个是死代码),选库改挂官方内联座位 `conversation.input.left`(`id: 'wiki-scope'`),`scope-seat.ts` 删除、dock 里那套 `scope` 内联渲染与自测量一并清掉;右侧栏 guide 条目补 `id` + `keepMounted: true`(0.2.1 的分页约定)。② **新增 `scripts/verify-dsh-contract.mjs`**:解析宿主 `app.asar` 里的 `dsh-cordis-client-runner`,把 103 条官方座位目录与我们源码里注册的 6 个座位逐一比对(存在性 / kind / `tool.call.toolview` 的 54 个已占 key 是否撞车),并断言 `engines.dsh` 已声明 —— 此前"座位名写错"只会静默不渲染。③ **`tiddlywiki_get` 支持分页**(`offset`/`limit`,默认 12000 字符):超长笔记一次读全会撞宿主的 12500 token 回执上限;回执明确写"正文 a–b / 共 N 字符"并给出下一步 `offset`。`search`/`lint` 的回执也加了同量级的体量预算(命中条数与计数结论**永不截断**,只截示例并说明省略了多少)。④ **16 个工具补 `output.schema`**:DSH 0.2.1 在每次调用后用 `validateJsonSchemaValue` 校验返回值,不过即抛 `ToolOutputError` —— 而 `sdk.ts` 的 `output.schema` 自 v0.20.0 起写死成 `{}`(当时注释断言"没人读它",**实测为假**)。现在按"只声明必定存在且类型稳定"的规则为 16 个工具各写一份,并加两条守门:每个工具必须声明非空 object schema;**用 E2E 里每一次真实返回值反查声明**(抓"schema 比现实更严"这种线上致命错误)。⑤ **skill 改用 `ctx.skills.register`**:拆库 skill 过去只能往 `$DSH_HOME/skills` 写文件,现在优先注册到宿主 skill 注册表(内存、随插件卸载消失、不碰用户目录),只有宿主没有该服务时才回落写文件;新增 `scripts/verify-embedded-skill.mjs`(解析/坏输入/disposer/注册表抛错/**启动顺序必须"先注册后回落"**)。⑥ **新增 `/note` 斜杠命令**:一句话记笔记,直接写进当前会话的知识库、不产生模型消息;同名笔记**已存在就拒绝**并指路("同一件事写多份互相矛盾"是最难收拾的失败形态),且写成 `inbox` + 工作站标记、**不打 `agent-written`**(那是人写的);守门 `scripts/verify-note-command.mjs`。⑦ **提示词两处修订**:新增「### 先查再答」(模型可见工具从 57 涨到 243 之后,新会话不再主动检索 —— 注入本身没坏,`system/message` 里逐字可查,坏在提示词只讲"怎么写"、从不讲"什么时候该来查")与「### 时效与去重」(作者原话:agent 爱写短命内容、同一件事写多份有差异的、正文里堆变更流水)—— 钉子:一件事只留一处权威表述(先搜、命中就**原地改那一节**)、每条笔记必须看得出"还有效到什么时候"(`valid-until`/`review-after`/"长期有效",说不清时效的别写)、**不写流水账**、**读到就先判时效**。slim 正文 1079 → **1390 字符**(预算 1000 → 1470,与逐条断言绑定),并把提示词正文的拼装从"硬编码数组 + 导出的块清单"两份实现收成 `governanceBlocks()` 单一来源 —— 加规则时 `verify-prompt` 当场抓到这个双份实现。
|
package/docs/development.md
CHANGED
|
@@ -147,6 +147,53 @@ npm publish # 版本号在 package.json;文件白名单见 files 字段
|
|
|
147
147
|
|
|
148
148
|
`tiddlywiki` 依赖体较大(含全部语言包/插件)。
|
|
149
149
|
|
|
150
|
+
### v0.31.x 的决策记录(2026-10-26,v0.32.0 已补做 .tid 预览)
|
|
151
|
+
|
|
152
|
+
原计划里的这两项**没有实现**,理由写在这里,免得下一个人从头再猜一遍:
|
|
153
|
+
|
|
154
|
+
**① `ctx.locale.register` 替换自研 i18n(470 键 / 7 分片)**
|
|
155
|
+
|
|
156
|
+
- 官方契约要求「用已合并进 `LocaleNamespaceMap` 的命名空间」调用 `ctx.locale.register(ns,{zh,en})`,
|
|
157
|
+
编译器会按该命名空间的**类型化键并集**逐个校验。本插件的客户端是**单文件打包、无 .d.ts**
|
|
158
|
+
(`lib/client.js` 由 esbuild 产出),无法参与那个类型合并 —— 迁移等于**放弃键名拼错的保护**,
|
|
159
|
+
而现有 `scripts/verify-client-i18n.mjs` 恰好是拿这层保护在守(zh/en 键集合一致、每个
|
|
160
|
+
`t('…')` 的键都存在、无未使用键、无残留中文)。
|
|
161
|
+
- 收益只有"改语言即时生效";代价是动到每一个客户端文件 + 重写那条守门。**风险与收益不成比例**,
|
|
162
|
+
故不做。若将来要做,前置条件是先给客户端加类型检查,或确认 `ctx.locale.bind(ns)` 的返回值
|
|
163
|
+
能让纯 React 组件在 `locale/change` 时重渲染(本会话无法取得该证据)。
|
|
164
|
+
|
|
165
|
+
**② `.tid` 的 `documentPreviews` 预览 —— v0.32.0 已实现**(`src/client/tid-parse.ts` 纯逻辑 + `src/client/tid-preview.ts` UI + `scripts/verify-tid-preview.mjs` 10 条守门)。下面这段当初写下的顾虑仍然成立,所以留下的实现约定照旧执行:
|
|
166
|
+
|
|
167
|
+
- 契约是**成对**的:`ctx.documentPreviews.register({ id, extensions, …, loading })` 注册元数据,
|
|
168
|
+
再以**同一个 id** 往 keyed、Session 作用域的 `sidebar.right.tab.document` 注册正文;正文接收
|
|
169
|
+
`resourceAddress`/`content`/`wrap`/`scrollportRef`/`addResource`/`setResources` + `useTabInfo`/`useResource`。
|
|
170
|
+
- 这套 props 没有随包 .d.ts(`app.asar` 里 0 个 d.ts),本会话也**没有可视验证手段**
|
|
171
|
+
(CDP 9222 不通、web 端 401)。按本仓库的规矩「守门就是证据」,没有可视证据就发出去,
|
|
172
|
+
是在赌一个面板不会白屏 —— 而白屏是**沉默失败**,正是这一轮专门在消灭的那一类。
|
|
173
|
+
- 未做的部分不代表没结论:真要做时,正文应当把文件文本 POST 到本插件自己的
|
|
174
|
+
`/dsh-tiddlywiki/render`(铁律 #7 要求所有 TW 注入都走净化管道),拿回净化后的 HTML 再渲染;
|
|
175
|
+
这条路径与现有工具卡、TW 代理面板**共用同一个安全边界**,不要另开一条。
|
|
176
|
+
- **已核实的契约(2026-10-26,直接读 `app.asar` 里 `dsh-client-ui-sidebar-documentpreview/lib/client.js`)**:
|
|
177
|
+
`ctx.documentPreviews.register({ id, extensions, binaryExtensions?, priority, title, loading, wrap? })`
|
|
178
|
+
确实存在,且**按 id 去重**——同 id 重复注册抛「duplicate implementation」;`binaryExtensions`
|
|
179
|
+
必须在 `extensions` 里,否则也抛。
|
|
180
|
+
- **正文组件的 props 形状也核实完了(同一轮,同一个 bundle 的 `TextPreview` 函数签名)**:
|
|
181
|
+
|
|
182
|
+
```js
|
|
183
|
+
TextPreview({ useTabInfo, useResource, useStore, actions, loadPage, reloadPages,
|
|
184
|
+
loadAll, reloadAll, prepareRenderer, useDocumentPreviews, renderSlot,
|
|
185
|
+
t, addResource, setResources })
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
三种 `content` 形态:`{ kind: 'text', pages, text }`(文本页,`text` 是累计前缀)、
|
|
189
|
+
`{ kind: 'bytes', data }`(完整字节)、`{ kind: 'renderer', revision, loaded, failed, reload }`
|
|
190
|
+
(`loading: 'renderer'` 时才用)。`useTabInfo()` 给 `{ tab }`,`tab.contentId` 是资源地址
|
|
191
|
+
(`useResource(tab.contentId)` 取元数据,`meta.value.absolutePath` 才是本地真实路径)。
|
|
192
|
+
|
|
193
|
+
⇒ 因此 `.tid` 预览的正确写法是:`loading: 'text-pages'`,正文从 `content.text` 拿**已累计的**文本,
|
|
194
|
+
解析出 frontmatter 后再把正文 POST 到 `/dsh-tiddlywiki/render` 换成净化 HTML;**拿不到 `content` 时
|
|
195
|
+
必须退回 `<pre>原文</pre>`**(白屏是沉默失败)。这套已无未知量,缺的只是**可视验证**。
|
|
196
|
+
|
|
150
197
|
### 发布时要动的地方(完整清单见 AGENTS.md §4)
|
|
151
198
|
|
|
152
199
|
1. `package.json` 的 `version`;
|
package/docs/features.md
CHANGED
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
| 🧩 **拆库引导(v0.28.0)** | 想把现有库按用途拆开(语料/归档 vs 工作集)时有一份现成流程:[docs/wiki-split.md](wiki-split.md) 顶部是**一整段可直接复制给 Agent 的话**;Agent 会先只读体检、再问五个问题(每个都给默认建议)、出方案表格等你确认,然后备份 → `git mv` 保历史 → 登记进插件 → 校验,**旧库一个都不删**。随包 skill `dsh-tiddlywiki-wiki-split` 在启动时**注册进宿主 skill 注册表**(v0.31.0 起:内存注册、随插件卸载消失、不写你的 `$DSH_HOME`;宿主没有该服务时才回落到 `$DSH_HOME/skills/`,且**只覆盖带标记的文件**),也可以直接说「用拆库 skill 帮我拆」。 |
|
|
16
16
|
| ⚡ **`/note` 一句话记笔记(v0.31.0)** | 输入框里打 `/note 标题` 换行写正文,直接写进当前会话的知识库:**不产生模型消息**(不占上下文、不用等回复),自动打 `inbox` 标签 + 工作区标记,**不打 `agent-written`**(那是人写的)。同名笔记**已存在就拒绝**并指路 —— 静默覆盖正是「同一件事被写成多份互相矛盾」的来源。 |
|
|
17
17
|
| 🧭 **提示词里的读写纪律(v0.31.0)** | 注入提示词新增两块:**「先查再答」**(问题可能已沉淀成笔记时先 `tiddlywiki_search`,别凭记忆答)与**「时效与去重」**(一件事只留一处权威表述、命中就**原地改那一节**;每条笔记要能看出还有效到什么时候 —— `valid-until` / `review-after` / 「长期有效」,说不清时效的别写进 wiki;**不写流水账**,正文不追加「某日改了什么」,git 历史里有;**读到就先判时效**,过期/被取代就更新或明说已过期)。目的是治「agent 爱写短命内容、同一件事写多份、正文越堆越长」。 |
|
|
18
|
+
| 📄 **`.tid` 文件预览(v0.32.0)** | 在右侧栏打开 wiki 里的 `.tid` / `.tiddler` 文件时,看到的是**真 TiddlyWiki 渲染**(链接、宏、样式都生效),而不是一堆 `title:` 字段行加原始 wikitext。字段行会单独排一行摘要。**绝不白屏**:还没读到内容会说「等待文件内容…」,只有字段没有正文会说清楚,渲染失败则直接显示原文。 |
|
|
18
19
|
| 📤 **导出笔记(v0.31.1)** | 「把这条笔记给我」不用再让助手把正文贴进回复:`tiddlywiki_export` 把一篇笔记落成文件(`md` 带 YAML frontmatter / `tid` 是 TW 原生格式 / `json` 是完整 tiddler),然后助手用 DSH 的 `present` 作为交付物给你。**同名文件已存在就拒绝**(导出是旁路产物,静默覆盖会毁掉你上一次的交付物);插件自身命名空间(`$:/plugins/dsh-tiddlywiki/`,可能含密钥)拒绝导出。 |
|
|
19
20
|
| 📍 **知识库位置可切换** | 设置页「知识库位置」可把插件切换到**任意本地文件夹**(不用改 cordis 配置、不用重装):就地停/起 TW 子进程,目标目录没有 `tiddlywiki.info` 时自动 `--init server` 建一个全新知识库;选择记在 `$DSH_HOME/dsh-tiddlywiki/location.json`——一个**在 wiki 之外**的指针文件(所以切到新 wiki 后仍记得「我用的是哪个」),「恢复为配置默认」一键清除;**切换失败自动回滚**到原知识库并如实告知(v0.22.0)。⚠️ 它一次只有一个库在线;要**多库同时在线**见上面的「多知识库」(v0.28.0) |
|
|
20
21
|
| 🔄 **seed 更新检测** | seed 标记记录**内容哈希**:内置内容在本 wiki 预置之后更新过 → 设置页显示「⬆ 有更新」;被你改过 → 显示「✏️ 本地已修改」,且「重新初始化」前**二次确认**;两者都基于哈希判定,绝不猜(旧格式标记会明确显示「尚未启用更新检测」,不会误报为「你改过」,v0.22.0) |
|