dsh-tiddlywiki 0.26.0 → 0.26.1
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 +1 -0
- package/lib/client.js +8 -8
- package/package.json +1 -1
- package/src/client/settings-page.ts +51 -10
package/README.md
CHANGED
|
@@ -416,6 +416,7 @@ lib/ # 预构建产物(发布含 lib/**,提交入库;
|
|
|
416
416
|
|
|
417
417
|
> 最近几个主要版本的一句话记录(完整变更见 [Releases](https://github.com/bbqisbbq/dsh-tiddlywiki/releases) / git log)。
|
|
418
418
|
|
|
419
|
+
- **v0.26.1**(2026-09-22):**修 P0:设置页插件/主题/语言的勾选框全部显示为「未勾选」,且误点「应用」会把启动清单整个清空**。回归由 v0.25.0 的 `CatalogPending` 引入(commit 883196f):契约是「`undefined` = 用户还没碰过、完全跟随服务器」,而挂载时把它初始化成了 `{ plugins: new Set(), themes: new Set(), languages: new Set() }`——`desiredPlugins()` 用 `??` 回退,而**空 Set 不是 `undefined`**,于是「没碰过」被读成「用户期望集合为空」:① 所有勾选框全空(用户看到「TW 里明明装着的插件,这里一个都没勾」——本次由作者实测报障);② 更严重的是,什么都没改就点「应用插件」会 POST 一个**空数组**,把 `tiddlywiki.info` 的 `plugins` 清单**整个清空**(主题清单、语言同理:一次误点等于卸载 codemirror/menubar/全部主题/中文语言,TW 重启后只剩内核 + 自动补装的 markdown)。**修复**:初始化改为空对象 `{}`;并给三个「应用」按钮加闸门——`pending.* === undefined`(未碰过)时按钮禁用、点击直接返回,只有真正改过勾选才允许提交(「用户刻意取消全部勾选」仍是合法提交,host 的 no-op 守卫照旧兜底)。顺带把「wiki 内已装」徽标的判据从 `desiredPlugins()` 改为服务器集合 `serverPlugins`——徽标描述当前事实,不该随未应用的勾选抖动。守门:`scripts/verify-plugin-runtime.mjs` 新增 4 条(空对象初始化 + 三个闸门 + 三个 sync 函数必须在 change 回调里重算按钮 + 徽标判据),**已反向验证**(改回空 Set 即红)。
|
|
419
420
|
- **v0.26.0**(2026-09-22):**修「设置页插件管理与 TW 实际状态对不上」——两套真相源不再互相说谎**。现象(作者本机实测):TW 控制面板里明明启用着的插件(TiddlyFlex、mermaid、pinboard 等经 TW 原生插件库装的),设置页勾选清单里**根本不出现**;反过来 menubar / comments / help 在 tiddlywiki.info 里(勾选着),但用户早在 TW 面板里禁用了它们。根因:**设置页只读 `tiddlywiki.info` 的 `plugins` 数组(启动安装清单),而 TW 运行时集合 = tiddlywiki.info ∪ 以 tiddler 形式装进 wiki 的插件 − `$:/config/Plugins/Disabled/<title>`(正文为 `yes`,TW 核心自己的判定)**——两个面板读的不是同一份真相,谁也没错但谁也不全。**修复(方向「修真」)**:勾选语义保持「是否写入 tiddlywiki.info」不变(它只管引擎自带插件的离线启停,这是 TW 原生界面做不到的唯一能力),但把缺失的另一半真相显示出来——① 每个插件行加状态徽标:「TW 内已禁用」(勾选着但被 TW 禁用)/「wiki 内已装」(不在启动清单但 wiki 里有同名 tiddler 版本,TW 运行时已在用);② 新增只读小节「**wiki 内插件(经 TW 原生安装)**」,列出第三方插件 tiddler(名称/版本/描述/禁用状态),启停/卸载引导去 TW 控制面板;③ 区块说明文字写明两边分工。**实现**:`GET /admin/state` 新增 `runtimePlugins` 字段,由 `scanWikiRuntimePlugins()`(`src/host/admin.ts`)扫 tiddlers 目录得出——插件库安装的插件是 `$__plugins_*.json` + `.json.meta`(只读几十字节的 meta,不碰几 MB 的插件 payload;无 meta 的裸 `.json` 还可能是拖拽导入的**多 tiddler 数组**,一并解析);禁用标记 `$__config_Plugins_Disabled_*.tid` 按文件自身 `title:` 头还原插件标题(不做有损的文件名反解码)。扫描失败返回 `null`(读失败 ≠ 不存在),客户端隐藏徽标与小节而不是显示空的假象;`dsh/*`、`dsh-tiddlywiki/*` 命名空间(插件自己的种子 bundle)排除在外。产品哲学不变:插件对 wiki 的强制定制依旧只有 `--init server` 基线 + 自动补装 markdown + 3 个核心种子,官方自带插件之外的安装/管理全部留给 TW 原生界面。
|
|
420
421
|
- **v0.25.1**(2026-09-21):**修「回复流里的 wiki 链接点了打不开」**——根因在 **DSH web 的 markdown 渲染器**把 `[标题](/dsh-tiddlywiki/tw/#标题)` 这类相对链接**解析成绝对 http(s) URL**,再按 protocol 判为「外链」(`target="_blank"` + 自己的 `openExternalLink` onClick)。旧 `installWikiLinkInterceptor` 只匹配相对路径正则 `^/dsh-tiddlywiki/tw/#(.+)$`,绝对 href 匹配不上 → 拦截器不接管 → 点击落入 DSH 外链逻辑,在新标签页打开 TW 编辑器而不是中央面板(用户感知「打不开」)。**修复**:拦截器改用 `new URL(href, window.location.origin)` 归一化后判**同源 + `pathname === TW_PROXY_BASE` + hash 非空**(相对/绝对两种形态都接),在 capture 阶段 `preventDefault + stopPropagation` 抢在 DSH 外链 onClick 之前打开中央 TW 面板;`scripts/verify-tool-views.mjs` 新增源码级断言(必须用 `new URL` + 同源校验 + 代理 pathname,且旧相对正则已移除)。headless Chrome 实测:TW 面板 hash 导航(含 TiddlyFlex 的 `$:/StoryList-1`)本身正常,问题确在拦截器漏匹配绝对 href。
|
|
421
422
|
- **v0.25.0**(2026-09-20):**第五轮代码审计的修复版**(审计那天被用户判定为「本地单机可接受、不修」的只有一条:`POST /render` 的 **title 分支**可以经被渲染条目正文里的 `{{…}}` 转写把插件配置读出来——本机实测复现,`text` 分支早已 403;不修的理由是单机自用、且真正的边界本就不在这条路由(任何笔记正文的 `{{$:/plugins/dsh-tiddlywiki/config}}` 在嵌入式 TW 面板里同样会被转写渲染)。其余全部修掉,按性质分四组。**① 工具契约(数据安全)**:(a) **`tags: []` 现在真的清空标签**——此前 `normalizeTagArg()` 把空数组归一成 `undefined`(=「未传 = 保留原标签」),而 `tags` 又是 `RESERVED_TIDDLER_FIELDS` 成员(`fields.tags` 会被拒),于是**没有任何途径**把一篇笔记的标签改成空:模型照 `tiddlywiki_lint` 的 junk-tags 建议去清标签、传 `[]`,什么都不会发生,回执还把旧标签原样列出来(本机实测)。(b) `tiddlywiki_append` / `tiddlywiki_rename` / `tiddlywiki_batch_put`(逐条目 `expectedModified`)补上**乐观并发令牌**:这三条与 put/delete/attach 一样是「读全文 → 整篇回写」(append 读旧基底再加一段),而可传令牌的只有后三条 —— 注入提示词让模型回传 `expectedModified`,append 却**没有这个参数**,人类在 TW 编辑器里的并发修改会被旧基底静默回滚。(c) `tiddlywiki_rename` 更新引用时**逐个重读引用者**(列表只是快照),遍历期间被改动过的跳过并在回执里报出数量——跳过一条链接可以手工补,被覆盖掉的一段正文不可恢复。(d) `tiddlywiki_attach` 的 `noteTitle` 指向二进制附件时明确拒绝(会把 base64 写坏而回执看着正常)。(e) `tiddlywiki_lint` 的 `checks` **拼错不再谎报干净**:本机实测 `checks:["broken-link"]`(少了 s)在 2933 条笔记的知识库上**一个检查都没跑**,回执却是「扫描 2933 条文本笔记,发现 0 个问题 / 没有发现问题」;现在回执列出**实际运行**的检查、显式报出无法识别的名字,且「一个检查都没跑」不再等于「没问题」。(f) `valid-until` / `review-after` 的纯日期(`YYYY-MM-DD`)改按**当地日终**判过期,此前按 UTC 0 点算,东八区当天 08:00 就报「已过期」。**② 设置项(都是「保存了却不变」)**:`git.autoCommit`/`git.debounceMs`/`git.remote` 保存后**对运行中的插件立即生效**(`AutoCommitter` 在构造时把 enabled/debounceMs 快照进不可变 options,remote 只在启动 bootstrap 时应用——此前关掉自动提交仍会 commit、换远端仍推老地址,直到重启 dsh web);「重启 TW」按钮的 15s 超时改为 120s(`/admin/restart` 要等 TW 就绪,软窗口默认 60s、大知识库自述 44s+,此前会弹「重启失败」而宿主其实成功);数值项(`bridge.port` / `startup.readyTimeoutMs` / `ui.allArticles.pageSize` / `git.debounceMs`)加 `min`/`max` 与字段旁红字,非法值**拒绝保存**(宿主对越界值是静默夹取/回落,提交只会继续制造「回显 ≠ 生效」);**空补丁不再发请求**、宿主在序列化文本逐字节相同时**跳过 PUT**(每次空点「保存配置」都会重写配置 tiddler 并刷新 `.meta` 的 `modified` → 白造 git diff、两台机器各点一次就冲突在 `.meta` 上,与 v0.24.2 刚修的 `$__language.txt.meta` 同类);保存被拒(409,如配置 tiddler 残留冲突标记)时错误**持久显示**在配置区而不是一闪而过的 toast;微信发布的 `command`/`adapter`/`token`/`dsn` 补齐 UI(token 密码型 + 👁 显隐切换,掩码回填语义照旧);插件/主题/语言的未应用勾选提升为页面级状态,能活过「同步 / 重启」触发的重刷;表单有未保存内容时若配置在**别处**被改过,保留你的输入并显式提示,而不是重建表单把输入清掉;首屏加「加载中…」、加载失败不再清空已填表单。宿主侧 `/admin/config` 的补丁现在必须过新的 `normalizeConfigPatch()`:**已知数值夹取到真实边界、已知布尔/字符串类型错误返回 400 并点名字段、`__proto__`/`constructor`/`prototype` 一律拒绝、未知键透传**(此前任意 JSON 都会落进配置 tiddler——数组体变成 `{"0":1,"1":2}` 这种永久垃圾键,`git.debounceMs: "abc"` 会经 `??` 进 `setTimeout` 被当 0 → 每次写入都 commit)。**③ 会话「知识库」Tab**:`NOTE_TOOL_NAMES` 补 `append`(「增量写入」)/`attach`(「附件」)/`delete`/`trash`(`restore` 计产生),新增「🗑 删除 / 回收站」一节——此前**用 append 写的笔记在这个 Tab 里完全不可见**,而注入提示词恰好推荐「纯增量内容优先用 append」,于是一整轮工作会被显示成「本会话暂时没有产生、读取或检索过任何知识库笔记」;事件日志读不出来的会话**在页面上明说**(不再静默吞掉、把「读不出来」显示成「没碰过笔记」),`counts.sessions` 只统计成功读取的会话;读日志从串行改 **4 路有界并发**、探测整段加 **12s 墙钟预算**(最坏 ~375s → 有界,客户端 25s abort 之后不再继续压着 TW 打),生成加 **3s 单飞复用**;客户端自愈改为**三态探测**(在 / 被清 / 状态未知——未知既不计失败也不清零,此前 `catch { return true }` 把「探测失败」当「一切正常」),自愈 interval 与 `phase` 解耦(进 error 态后不再永久失去重试),并新增**按 3 分钟节拍的静默刷新**(tab 长期开着也能跟上会话,不闪 loading 不砸内容);`escapeInline()` 中和 `{}`(会话日志里出现的 `{{…}}` 会被 `/render` 转写展开)并改为**先截断再转义**(否则 `&` 会被截成 `&am`);`scanRefs` 不再把标题里的 `)`/`]` 一律截断;`TitleState.snippet` 从死字段改为真的渲染。**④ 其余**:检索卡片的范围与工具结果对齐(卡片把模型可见回执里读到的 `workspace` 回传 `/search`,v0.24.0 只改了工具侧——卡片会列出**更多**的结果且不显示范围);`/status` 的 git 冲突内容扫描加上限(200 个文件 / 2MB,批量导入期间不再每 30s 同步读几千个文件阻塞事件循环);`tiddlywiki_delete` 的描述写明 `$:/` 系统条目实为永久删除;`TiddlywikiConfig.note` 补上漏掉的 `workspaceMark`(三处形状 lockstep);`tiddlywiki_attach` 有专属卡片。**守门**:新增 `scripts/verify-config-patch.mjs`(6 组纯函数断言:非对象拒绝 / 越界夹取 / 类型错误点名字段 / 危险键拒绝 / 未知键透传)与 `scripts/verify-tool-views.mjs`(5 条源码级断言:`TOOL_VIEW_KEYS` 与 host 工具名完全一致 / `TOOL_LABELS` 全覆盖 / 工作区前缀两处一致 / 搜索卡片仍回传 `workspace`),两者进 `verify:unit`;`verify-tools.mjs` +4 条真实 TW 断言(`tags:[]` 清空且「不传」仍保留 / append 令牌不匹配拒绝、匹配放行 / batch 逐条失败 / lint 未知检查名不得谎报);`verify-write-policy.mjs` +1 条(空数组 vs 不传的语义边界)。**已反向验证的路径**:把 `normalizeTagArg` 还原成「空数组 → undefined」、把 lint 的 `checks` 还原成自由集合、把 append 的令牌检查删掉,对应断言分别变红。
|