dsh-tiddlywiki 0.23.5 → 0.24.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 +15 -4
- package/lib/client.js +1 -1
- package/lib/index.js +423 -60
- package/lib/index.js.map +1 -1
- package/package.json +2 -2
- package/src/client/settings-page.ts +3 -0
- package/src/host/admin.ts +26 -6
- package/src/host/config.ts +13 -1
- package/src/host/prompt.ts +8 -6
- package/src/host/routes.ts +21 -9
- package/src/host/seeds.ts +47 -0
- package/src/host/tools.ts +212 -33
- package/src/host/tw-api.ts +27 -12
- package/src/host/workspace.ts +106 -0
- package/src/index.ts +38 -6
package/README.md
CHANGED
|
@@ -27,9 +27,12 @@
|
|
|
27
27
|
| 📍 **知识库位置可切换** | 设置页「知识库位置」可把插件切换到**任意本地文件夹**(不用改 cordis 配置、不用重装):就地停/起 TW 子进程,目标目录没有 `tiddlywiki.info` 时自动 `--init server` 建一个全新知识库;选择记在 `$DSH_HOME/dsh-tiddlywiki/location.json`——一个**在 wiki 之外**的指针文件(所以切到新 wiki 后仍记得「我用的是哪个」),「恢复为配置默认」一键清除;**切换失败自动回滚**到原知识库并如实告知(v0.22.0) |
|
|
28
28
|
| 🔄 **seed 更新检测** | seed 标记记录**内容哈希**:内置内容在本 wiki 预置之后更新过 → 设置页显示「⬆ 有更新」;被你改过 → 显示「✏️ 本地已修改」,且「重新初始化」前**二次确认**;两者都基于哈希判定,绝不猜(旧格式标记会明确显示「尚未启用更新检测」,不会误报为「你改过」,v0.22.0) |
|
|
29
29
|
| 🎨 **自定义样式** | 「自定义样式」seed:编辑器美化 / 窄屏侧栏隐藏 / menubar 加高 / 批注弹窗等 5 张通用样式表,新 wiki 也能一键初始化(可选,v0.16.22) |
|
|
30
|
-
| 📝 **可配置的注入提示词** | 插件注入每个会话的「TiddlyWiki
|
|
30
|
+
| 📝 **可配置的注入提示词** | 插件注入每个会话的「TiddlyWiki 持久知识库」提示词可在设置页配置:**默认精简版**(v0.24.0 起 ~1.9KB,只保留工具 schema 表达不了的约定——同步纪律 / 工作区标记 / 先窄后宽检索 / 时效标注 / 链接格式),可选**完整版**(额外附一份**由工具注册表实时生成**的参数索引,不会再过期);`extra` 追加自定义规范、`override` 整段接管、可整体停用;**保存后无需重启 dsh web**(section 即时重注册,当前会话下一步即生效),设置页可**按表单当前值预览**即将注入的全文(未保存的形态切换也立刻可见,v0.21.0 / v0.22.7) |
|
|
31
31
|
| 🤖 **Agent 工具** | 15 个 `tiddlywiki_*` 工具:检索(**相关度排序 + 命中处片段 + 字段过滤**)、读写、**增量追加**、批量、重命名、**软删除/回收站**、**反向链接**、**附件入库**、**知识库体检**、git 同步与冲突解决(v0.19.0;检索/最近仍在**服务端**排除二进制附件,大 wiki 上从 515MB/17s 降到 ~0.4s) |
|
|
32
32
|
| 🛡️ **不会被覆盖的写入** | 所有写入路径(agent 工具 **与** 快速笔记/编辑器路由)都**先读后写**:不传 tags 就保留原有标签、自定义字段与**内容类型**(`text/css`/wikitext 等不会被重置成 Markdown,v0.20.1);`tiddlywiki_put(..., expectedModified/expectedRevision)` 与 `tiddlywiki_delete(..., expectedModified/expectedRevision)` 乐观并发——读取后若有人(在 TW 编辑器里)改过,写入/删除被拒绝(HTTP 409)而不是静默覆盖或丢进回收站;**给了哪个令牌就必须匹配哪个**(v0.23.5:两个令牌是 AND 不是 OR——`revision` 是 TW 的内存计数器、重启会复位,旧逻辑会因此静默放行跨重启的覆盖);`tiddlywiki_attach` 的同名标题**默认拒绝**(要覆盖必须 `force: true`);`tiddlywiki_delete` 默认**软删除进回收站**,`tiddlywiki_trash` 可恢复(v0.19.0 / v0.19.1 / v0.19.5 / v0.23.5) |
|
|
33
|
+
| 🏷 **工作区感知的读写** | **新建**笔记自动带 `ws/<项目名>` 标签 + `workspace` 字段(项目名取自会话工作目录,`note.workspaceMark` 可关);`tiddlywiki_search` 因此默认**先在本项目里找、区内 0 条才自动扩大到全库**并在回执里写明实际范围,`query` 按空白切词、**全部词命中**才算(AND)。工作区只是**附加**标记,绝不改写笔记原有的标签/字段,也**只在新建时**打(v0.24.0) |
|
|
34
|
+
| 🕰 **时效性内容只提示不删** | 阶段性笔记(版本记录 / 部署步骤 / 排期 / 临时方案)可声明 `valid-until`(硬过期)或 `review-after`(该复查),被取代时写 `superseded-by` + `superseded` 标签并**保留旧笔记**;`tiddlywiki_lint` 的 `stale` 检查**只报告**(含「版本号或 done 标签 + 长期未改动」的**候选**,明确标注非判定)——**淘汰永远由人决定**,插件不会自动删除或归档任何笔记(v0.24.0) |
|
|
35
|
+
| 🚰 **重启不丢写入** | 停/重启 TW 只有一条路:`drainThenStop()`——**排干 syncer 队列之后才停子进程**,`/restart`、`/admin/restart`、插件/主题变更后的重启、知识库切换、`/sync`、seed 全部经它(此前其中三条直接杀进程,在一秒内点「重启 TW」会静默丢掉刚写的笔记);排干不完整会在回包里如实报 `drained:false`,而不是假装没事(v0.24.1) |
|
|
33
36
|
| 🕒 **时间戳不再丢** | 插件写入的每条笔记都会带上 TW 的 `created`/`modified`(17 位紧凑 UTC,与 TW 编辑器逐字节一致):新建 = 两者都取当前时刻,覆盖 = **保留原 `created`、刷新 `modified`**。此前这两个字段被当成「TW 服务端会补」而丢弃,而服务端**从不补**——缺 `modified` 的条目会被 TW 的 `sortTiddlers` 当空串,在 `+[!sort[modified]]` 页面上**直接沉到最后一名**(表现为「新日记没被收录」)。`TiddlyWebClient.put()` 还有一道兜底,任何写路径都不会写出无时间戳的条目(v0.22.5) |
|
|
34
37
|
| 🧼 **渲染片段净化** | 回复流卡片与会话汇总注入的 TW 片段先经 **host 白名单净化**(丢 `iframe`/`script`/`svg`/`on*`/`javascript:`/`data:text/html` 等)——TW 自己的解析器只剥 `on*`,`<iframe src="javascript:…">` 会原样通过并在 DSH 页面里执行(v0.19.1 修复的存储型 XSS) |
|
|
35
38
|
| 🔒 **写路由方法校验** | 每个写路由只接受自己的 HTTP 方法:跨站 `GET /sync`、`GET /restart`、`GET /upload` 一律 405 且无副作用(v0.19.0 修复了「任意网页一张 `<img>` 即可触发 pull/commit/push」的 CSRF 面) |
|
|
@@ -88,7 +91,7 @@ dsh plugin --profile web add link:/path/to/dsh-tiddlywiki
|
|
|
88
91
|
|
|
89
92
|
| 工具 | 说明 |
|
|
90
93
|
|---|---|
|
|
91
|
-
| `tiddlywiki_search` | 检索(`query` + 可选 `tags[]/tag/since/type/field/value/limit`),**按相关度排序**(标题命中 > 标签 >
|
|
94
|
+
| `tiddlywiki_search` | 检索(`query` + 可选 `tags[]/tag/since/type/field/value/limit`),**按相关度排序**(标题命中 > 标签 > 正文命中次数),摘要取自**命中处上下文**而不是正文开头。v0.24.0:`query` 按空白切词、**全部词命中**才算(AND);默认**先在工作区内查、区内 0 条才自动扩大到全库**,回执里写明实际范围 |
|
|
92
95
|
| `tiddlywiki_recent` | 最近修改的笔记(倒序,支持 `limit`/`since`),开工快速了解动态 |
|
|
93
96
|
| `tiddlywiki_list_tags` | 现有非系统 tag 及计数(已排除只挂在二进制附件上的 tag);默认列使用最多的 200 个(`limit` 可调、上限 1000),截断时返回 `total`/`truncated` |
|
|
94
97
|
| `tiddlywiki_get` | 读单个 tiddler 全文(`modified` 以 ISO 返回,可直接用作 `expectedModified`) |
|
|
@@ -100,7 +103,7 @@ dsh plugin --profile web add link:/path/to/dsh-tiddlywiki
|
|
|
100
103
|
| `tiddlywiki_trash` | 回收站:`action=list\|restore\|empty`(索引读不到或损坏时显式报错,绝不把回收站当空的重建) |
|
|
101
104
|
| `tiddlywiki_backlinks` | 反向链接:谁用 `[[标题]]`/`{{标题}}` 引用了它、谁把它当标签 |
|
|
102
105
|
| `tiddlywiki_attach` | 把**本机文件或公网 http(s) 地址**存成二进制附件(图片/PDF/…),可嵌入某篇笔记;URL 走 SSRF 守卫;**同名 tiddler 已存在时默认拒绝**(避免静默覆盖笔记),确认覆盖要传 `force: true`(tags/自定义字段仍保留) |
|
|
103
|
-
| `tiddlywiki_lint` |
|
|
106
|
+
| `tiddlywiki_lint` | 知识库体检(**只读**):垃圾标签 / 死链 / 空笔记 / 缺内容类型的类 Markdown 笔记 / **时效性内容**(`valid-until`·`review-after` 过期,以及「版本号或 done 标签 + 长期未改动」的**候选**,`staleAfterDays` 可调) |
|
|
104
107
|
| `tiddlywiki_git_sync` | `action: pull\|push\|sync` |
|
|
105
108
|
| `tiddlywiki_git_resolve` | pull 冲突后按 tiddler 二选一(`keep-local\|keep-remote`) |
|
|
106
109
|
|
|
@@ -117,6 +120,10 @@ dsh plugin --profile web add link:/path/to/dsh-tiddlywiki
|
|
|
117
120
|
|
|
118
121
|
> ⚠️ `fields.type` 是 TW 的**内容类型保留字段**(`text/markdown` 等),业务分类请放 `tags`,别写进 `fields.type`。
|
|
119
122
|
|
|
123
|
+
> 🕰 **过期内容怎么淘汰**(v0.24.0):很多笔记是**阶段性**的(版本记录、部署步骤、排期、临时方案、一次性口令),过一段时间就不适用了。策略是**声明时效 + 人工决定,插件绝不自动删**:写这类笔记时带 `valid-until: YYYY-MM-DD`(硬过期)或 `review-after: YYYY-MM-DD`(该复查);被取代时写 `superseded-by: [[新笔记]]` 并打 `superseded` 标签,**旧笔记保留**(wiki 是 git 仓库,留着的成本几乎为零,删错的成本很高)。`tiddlywiki_lint` 的 `stale` 检查会**只报告**三类:`stale-expired`(`valid-until` 已过)、`stale-review`(`review-after` 已到)、`stale-candidates`(带版本号或 `done` 标签且长期未改动——**明确标注是候选,不是判定**)。要清理就自己用 `tiddlywiki_delete`(默认进回收站,可恢复)。
|
|
124
|
+
|
|
125
|
+
> 🏷 **新建笔记自动标工作区**(v0.24.0,可用 `note.workspaceMark: false` 关闭):`put`/`batch_put`/`append` **新建**条目时会自动带上 **`ws/<项目名>` 标签 + `workspace` 字段**,项目名取自当前会话的工作目录(`C:\work\alpha` → `ws/alpha`)。于是 `tiddlywiki_search` 默认就能「先在本项目里找」,而 `field: workspace, value: <项目名>` 可以精确按项目过滤。**不需要也不应该手动加**这两个标记(会在 `agent-written` 之外重复);内容明显属于**另一个**项目时,显式传 `fields: {"workspace": "那个项目"}` 即可(显式值优先,不会被自动值覆盖)。为什么标签要带 `ws/` 前缀:裸项目名会和真实业务标签撞车——作者库里 `dsh-tiddlywiki` 这个标签已挂着 2510 篇导入的书籍章节,去掉前缀会让「在工作区内搜索」返回那 2510 篇。
|
|
126
|
+
|
|
120
127
|
### 🧠 注入的系统提示词(可配置)
|
|
121
128
|
|
|
122
129
|
插件会往每个会话的系统提示词里注入一段「TiddlyWiki 持久知识库」约定(**设置页 → 系统提示词**):
|
|
@@ -124,7 +131,7 @@ dsh plugin --profile web add link:/path/to/dsh-tiddlywiki
|
|
|
124
131
|
| 配置 | 作用 |
|
|
125
132
|
|---|---|
|
|
126
133
|
| `prompt.enabled` | 关掉后本插件不注入任何文本 |
|
|
127
|
-
| `prompt.mode` | `slim
|
|
134
|
+
| `prompt.mode` | `slim`(**默认**,v0.24.0 起约 1.9KB):只保留工具 schema 表达不了的约定(写入/并发纪律、同步纪律、工作区标记、先窄后宽检索、时效标注、可点击链接格式);`full`:额外附一份**参数索引**,由工具注册表在运行时生成,因此永远不会与真实工具脱节 |
|
|
128
135
|
| `prompt.extra` | 追加在末尾的自定义规范(团队 / 个人偏好),始终生效 |
|
|
129
136
|
| `prompt.override` | 非空时整段取代内置文本(`extra` 仍会追加)——想完全自写提示词时用 |
|
|
130
137
|
|
|
@@ -240,6 +247,7 @@ seed 是把「wiki 里预置内容」随插件分发的机制:**ONE-SHOT(只
|
|
|
240
247
|
branch: "main"
|
|
241
248
|
note:
|
|
242
249
|
tag: "inbox" # 快速笔记默认 tag
|
|
250
|
+
workspaceMark: true # v0.24.0:新建笔记自动带 ws/<项目名> 标签 + workspace 字段(项目名取自会话工作目录)
|
|
243
251
|
startup:
|
|
244
252
|
readyTimeoutMs: 60000 # TW 启动就绪窗口(v0.22.5,5s–600s;超出只警告并继续等,硬上限 = 3×,仍未就绪才判失败)
|
|
245
253
|
prompt:
|
|
@@ -405,6 +413,9 @@ lib/ # 预构建产物(发布含 lib/**,提交入库;
|
|
|
405
413
|
|
|
406
414
|
> 最近几个主要版本的一句话记录(完整变更见 [Releases](https://github.com/bbqisbbq/dsh-tiddlywiki/releases) / git log)。
|
|
407
415
|
|
|
416
|
+
- **v0.24.1**(2026-09-20):**修「重启/停止 TW 的四个路径里有三个没排干 syncer 队列」——一类静默丢写入的缺陷**。本仓库铁律第一条要求「重启 TW 前必须把 syncer 队列排干」,但那条规则**只写在文档里**:`POST /restart`、`POST /admin/restart`、`POST /admin/info`(改插件/主题后重启)与「知识库切换」的 `stopServer` 全都是**直接杀 TW 子进程**,而 TW 的 REST PUT 返回 204 时条目还在内存里、文件系统 syncer 要 ~250ms 后才落盘 —— 在那一秒内点「重启 TW」,刚写的笔记就随子进程一起消失(与 v0.19.0 丢 `tw-web-host`、v0.22.0 才发现 throttle 竞态同一类)。只有启动自举 / seed / `/sync` 三处走了 `flushPendingWrites`。**根因不是谁写错了某一行,而是「规则」和「代码」之间没有强制关系** —— 所以这一版不是补四个调用点,而是把不变量变成**原语**:新增 `drainThenStop()`(`src/host/seeds.ts`,**排干发生在它内部**,签名是 `{client, tiddlersDir, stop}`),四个路径全部改为经它停/重启,调用方**没法**绕过;`stop` 的返回类型放宽为 `Promise<unknown>`(`restart()` 回状态视图、`stop()` 回 void,排干都不关心)。`/restart` 与 `/admin/restart` 的回包新增 `drained`,排干不完整时如实上报而不是静默。**守门**:新增 `scripts/verify-restart-drain.mjs`(进 `verify:unit`)——① 顺序断言:`drainThenStop` 内部「排干的 await」必须早于「stop 的 await」,且排干失败必须走显式分支后**仍然 stop**(best-effort:不能因为排不干就把用户锁死在起不来的 TW 上);② 源码级断言:`routes.ts`/`admin.ts` 里**每个** `deps.server.restart()` 都必须作为 `drainThenStop({stop: …})` 的参数出现(裸 restart 即红),`index.ts` 的 `stopServer` 必须包在它里面;③ 行为断言:用桩 client 真跑一遍,确认「写入真的落盘之后才 stop」「client 缺失(TW 没在跑)是安全 no-op」「排干失败也继续 stop 并把警告交出去」。**已反向验证**:分别把 `/sync` 路径、知识库切换还原成裸调用、并删掉 `drainThenStop` 里的 `stop`,三次都稳定变红。顺带把 `/sync` 与 seed 两条**本来就在 flush** 的路径也收敛到同一原语,从此「重启前排干」只有一个实现。
|
|
417
|
+
- **v0.24.0**(2026-09-20):**三处「Agent 读写 wiki 的规矩」按用户要求改进 + 一个 lint 误报修复**。① **新建笔记自动标工作区**:`put`/`batch_put`/`append` 新建条目时自动带上 **`ws/<项目名>` 标签 + `workspace` 字段**,项目名取自调用会话的工作目录(新 `src/host/workspace.ts` 纯函数:Windows/Unix 路径、尾斜杠、CJK、非法字符规范化;`home`/`tmp`/`desktop` 这类通用目录与**驱动器根目录**一律拒绝——后者是写这个模块的守门脚本抓出来的真缺陷:`C:\` 会被算成 `C:`,在盘根开会话时全盘笔记都挂上 `ws/C:`)。标记是**附加式**(调用方标签在前)、**仅新建**(覆盖不追打,也不会改写已有 `workspace` 字段)、`$:/` 豁免、`note.workspaceMark: false` 可关、调用方显式 `fields.workspace` 优先;`exec` 缺失 / 会话无 `cwd` / 未知会话一律**安全降级为不标记**(不猜、不抛错)。为什么标签必须带 `ws/` 前缀:裸项目名与真实业务标签撞车——作者库里 `dsh-tiddlywiki` 已挂着 2510 篇导入的书籍章节。② **检索先窄后宽 + 多词 AND**:`tiddlywiki_search` 先在工作区内查、**区内 0 条才自动扩大到全库**,回执明确写出「已在工作区 ws/X 内缩小范围」或「工作区 ws/X 内 0 条,已扩大到全库」(**「工作区内 0 条」不等于库里没有**,这是把「搜不到」错判成「不存在」的主要来源);`query` 从**整串子串匹配**改为按空白切词、**全部词命中**才算(旧行为连 `alphaprobe betaprobe` 这种自然的双词查询都永远匹配不到);调用方显式传 `tag`/`field`/`value` 时**不自动收窄**(显式范围不被二次猜测)。③ **过期内容淘汰策略(只提示,绝不自动删)**:阶段性笔记写 `valid-until`(硬过期)/ `review-after`(该复查),被取代时写 `superseded-by` + `superseded` 标签、**保留旧笔记**;`tiddlywiki_lint` 新增 `stale` 检查(默认开启、`staleAfterDays` 默认 180),报 `stale-expired` / `stale-review` 两类**硬判定**加一类 `stale-candidates`(带版本号或 `done` 标签且长期未改动,**明确标注「候选,非判定」**)。**淘汰只由人决定**——报告全程只读,不删不改不归档。④ **修 lint 死链误报**(v0.23.6 的内容并入本版):死链正则 `\{\{([^}]+)\}\}` 会把 `{{{ [tag[todo]count[]] }}}` 这类**三花括号过滤器表达式**从第 1 个 `{` 开始捕获成「转写目标」并报死链(实测线上 480 条死链报告里 8 条属此类),现在用前后瞻 + 捕获内容守卫排除。**明确不改的**:≈440 条真实死链**保留不修**——多数是用户手工删掉的笔记,属预期状态(只读报告是对的,自动「修复」是错的)。**守门**:新增 `scripts/verify-workspace.mjs`(进 `verify:unit`,9 组纯函数断言,含「任何 cwd 算出的标签都不得是垃圾标签 / 含空白 / 丢前缀」这条与 `isJunkTag()` 的跨模块一致性检查,以及 `ws/` 前缀不得被「简化」掉的回归),`verify-tools.mjs` +12 条真实 TW 断言(工作区标记六种形态 + 多词 AND + 先窄后宽 + `fellBack` + `stale` 三类),`verify-prompt.mjs` +1 条「三条新治理规则必须在场」并把 slim 长度预算从 1800 调至 2100(**预算上调与新增的逐条断言绑定**:删掉任何一条规则即红)。**所有新守门都做过反向验证**:分别还原旧行为(整串子串检索 / 关掉工作区收窄 / 关掉自动标记 / 关掉 stale 检查 / 去掉驱动器根目录判定 / 从规范化字符集里删掉 `|`)后,对应断言稳定变红。slim 正文实测 1918 字符(full 2722)。
|
|
418
|
+
|
|
408
419
|
- **v0.23.5**(2026-09-18):**修 15 处审计缺陷**(第三轮全面审查列出的 18 条,逐条核实后修掉其中 15 条真缺陷;跳过 3 条纯理论性的信息泄露。完整审计报告见知识库笔记「dsh-tiddlywiki 第三轮代码审计」——审计报告是工作文档,不再进 git)。按性质分四组。**① 数据安全(最重)**:`tiddlywiki_delete` 原先**先删原条目、后读回收站索引**,于是「快照已写 → 原条目已删 → 索引读失败」时报"已中止",但笔记其实已经没了、且没进索引——`trash list` 看不见、`restore` 找不到、`empty` 清不掉,成为永久孤儿(只能从 git 捞);现在**读校验索引排在所有破坏性步骤之前**,中止即等于"没删"。同一处还有两个洞:`$:/` 系统条目会被**软删除**(删掉 `trash-index` 自己 → 下一次读 404 → 被当成"回收站为空" → 索引被重写成 1 条,丢掉全部记录),现在系统条目一律走永久删除分支,且**删回收站索引被显式拒绝**(要清空请用 `trash action=empty`)。`tiddlywiki_put` 不再把**二进制附件**写成文本(保留基底 `type: image/png` 会让 base64 解不出、图裂而回执说"类型没变";要转换得显式 `fields.type`)。`git pull` 不再**无条件 `rebase --abort`**——此前只要 pull 失败(含没配 remote、离线等与被 rebase 无关的原因),用户在 wiki 仓库里正在做的 rebase 与冲突解决就被清掉;现在只 abort「我们自己发起」的那个。乐观并发的两个令牌从 **OR 改回 AND**:`revision` 是 TW 的内存计数器(重启/拉取后复位),旧逻辑「`modified` 命中就放行、否则看 `revision`」会让跨重启的写入被判"一致"而**静默覆盖**人类改动,同时传两个令牌也**并不更安全**。配置 tiddler 的 `modified` 不再被钉死成 `created`(`ensureTiddlerTimestamps` 只补缺的那一半,用 now)。**② 崩溃与资源泄漏**:`fs.watch` 补上常驻 `error` 监听(Node 在无监听器时把 `error` 抛成未捕获异常 → 整个 `dsh web` 进程退出;Windows EPERM / Linux ENOSPC 都能触发);`WikiServer.startOnce()` 在 `spawn` 前**复查 `stopping`**(`stop()` 可在 `await findFreePort()` 期间跑完,此后 spawn 的子进程没人杀 = 孤儿 TW);知识库切换**回滚失败也必须重装 extras**(此前从 catch 直接返回、跳过 `setupExtras()`,此后所有 wiki 写入都不再被提交,而"迟到就绪"复探让 `/status` 看着一切正常——静默停摆);`runSwitch` 补 `disposed` 守卫(热重载/关闭期间的在途切换不再重新挂上 committer/watcher,也不在 teardown 之后再 spawn TW)。**③ 行为瑕疵**:`escapePromptBraces` 对**连续花括号**失效(`{{{name}}}` 会残留 `{{`,而 DSH 对未知变量直接抛错、能炸掉整个系统提示词装配正是该函数存在的理由);`?limit=` 空值不再被夹成 1(`Number('' ?? fallback)` 是 0,`/recent?limit=` 曾只返回 1 条而不是默认 15);`tiddlywiki_append` 在 `mode=prepend` 时不再回执谎报「写进了某段落」(`heading` 只对 append 生效);`POST /render` 的超限 body 从 400 改为 **413**。**④ 两处零成本的安全收口**:代理路径判定不再只看字面量 `/tiddlers/`——TW core 的 `get-tiddler-html` 路由是**单段**(`/^\/([^\/]+)$/`),把 `/` 编码成 `%2F` 即可绕过旧守卫(本机实测:修复前 `/tw/...%2Fconfig` 与 `/api/...%2Fconfig` 都返回 **200 + 715 字节配置 JSON**);`/render` 的 **`text` 分支**也不再是旁路(TW 会解析 `{{…}}` 转写,实测修复前 `{"text":"{{$:/plugins/dsh-tiddlywiki/config}}"}` 返回 200 + 3123 字节配置正文,净化器只管标签、管不住正文),`contextTitle` 一并校验。**守门**:`verify-write-policy` +6 条(令牌 AND 语义 +「证据不足判冲突」+ `onlyCreated` 不再钉死 modified)、`verify-prompt` +1 条连续花括号、`verify-frame-guards` +4 条源码级生命周期断言(`fs.watch` error 监听 / spawn 前复查 `stopping` / 回滚 finally / `runSwitch` disposed)、`verify-audit-fixes` +4 条 E2E(删除顺序:索引读失败后原条目必须仍在、`$:/` 与索引不得被软删、二进制附件拒写、`?limit=` 空值)、`verify-conflict-config-guards` +1 条**真 git E2E**(在真 rebase 中途 pull 失败,用户的 rebase 必须原样保留)、selftest +8 条(编码/双编码代理路径 403、`/render` 转写与 `contextTitle` 403、普通转写仍 200)。**全部 15 条修复都做过反向验证**:把修复还原后,新断言稳定变红。
|
|
409
420
|
- **v0.23.4**(2026-09-18):**三处硬化,全部来自 v0.23.3 上线当天暴露的真实事故**。① **提交前拦冲突(数据安全)**:`git pull --rebase --autostash` 的 **autostash 重新应用**冲突后,`rebase --abort` 已无事可 abort,冲突标记留在工作树里,随后 60s 的 AutoCommitter 照常 `git add -A && commit` —— `<<<<<<< Updated upstream` 就这样被**永久提交并推送**进了 wiki 的配置 tiddler(`0b19ca6`,2026-09-17),插件随即解析不了自己的配置(微信发布一直是关的、`prompt.extra` 静默失效)。现在 `GitFace.conflictState()` 三重探测(进行中的 rebase / 未合并路径 / **工作树里残留的冲突块**),`commit()` 直接抛 `GitConflictStateError` 拒绝提交;`/sync` 回 409 + 文件名、agent 工具返回结构化失败、AutoCommitter 去重上报一次(不去刷屏);`pull()` 在 abort 后**复检**(autostash 形态正是 abort 之后才暴露);`/status` 与设置页状态行显示「冲突未解决(N 个文件,已阻止提交)」。⚠️ `MERGE_HEAD`/`CHERRY_PICK_HEAD` 故意**不**硬拒——`git commit` 正是收尾它们的动作(真 git E2E 抓出了这个设计错误)。② **配置解析失败拒绝写(数据丢失)**:`ConfigStore.set()` 在存量 tiddler 解析失败时,原先拿**空缓存**合并 → 一次设置页保存就把配置写成只剩表单里那几个字段的"残版"(18 日 13:38 实测:`prompt.extra`/`git.remote`/`ui.*` 全丢)。现在抛 `ConfigUnreadableError`、不 PUT,并把原因经 `/admin/state` 的 `configError` 渲染成设置页顶部红色横幅;瞬时**读**失败仍走"合并到缓存"(回归断言守住)。③ **adapter 版本校验**:`/wechat/ready` 原先只数文件在不在,旧版 adapter(不认识 `--title-file`、标题仍是必填位置参数)也会报"就绪",点按钮才在 opencli 里报"缺少必填参数"。现在按**定义**判新旧(`weixin-flow.js` 必须 `export function resolveNoteTitle(`、两个入口必须声明 `name: 'titleFile'`——**不是**字符串出现,注释里提到不算),`missing`(不存在)与 `stale`(过旧)语义互斥;按钮预检与路由 503 分别说「版本过旧」和「缺少发布脚本」。bundle 升 `0.2.0`。守门:新增 `scripts/verify-conflict-config-guards.mjs`(23 条:伪 exec 纯逻辑 + **真起临时 git 仓库的冲突 E2E**(真 merge 冲突 / 真残留标记,且解决后必须能提交)+ ConfigStore 拒绝写/回归/接线断言;**已反向验证**:拆掉 `contentScan` 或配置拒绝各红 2 条),`verify-wechat-publish.mjs` 17→24 条(+注释里提及新符号必须判旧、真源码符号对齐),bundle 守门 +3 条。
|
|
410
421
|
- **v0.23.3**(2026-09-18):**TW 笔记工具栏新增「发布到公众号」按钮**(可选功能,与既有 CLI 等价,只是不用敲命令)。链路:TW 按钮 → `POST /dsh-tiddlywiki/wechat/publish` → 宿主 spawn `opencli weixin publish-note` → `GET …/wechat/publish/status` 轮询 → 覆盖层显示进度与日志尾部。**只到草稿箱为止**,发表仍由人工扫码(沿用 v0.23.2 定的策略)。三处硬骨头:① **标题不进 argv**——Windows 上 `opencli` 是 `.cmd` shim,必须经 cmd.exe 启动,于是标题里的 `&`/`|`/`^`/引号成了命令注入面、中文标题还会被 cmd 的代码页解成乱码;改成宿主写 UTF-8 文件 + adapter 新增 **`--title-file`**(位置参数照旧可用,两者都给时位置参数优先)。② **任务化而非同步请求**——命令要跑几十秒到几分钟,`POST` 立即返回 `jobId`,单并发(第二次 409)、15 分钟上限,超时用 `taskkill /T` 杀**整棵进程树**(只杀 cmd.exe 会留下孤儿 node 抓着 job 目录,实测 `EBUSY`)。③ **可选功能默认不打扰**——`wechat.enabled` 关着时三条 `/wechat/*` 路由一律 403、按钮插件不写进 wiki(新增 gated 起步 seed `wechat-publish`,与 `publish-spec`/`wechat-setup` 同进同退)。新增 `src/host/wechat-publish.ts`(命令组装纯函数 + job registry + 就绪探测)、三条路由(方法/同源守卫 + 可选 `x-wechat-publish-token`;`wechat.token` 与其它密钥一样在 `/admin/state` 打码)、TW bundle `$:/plugins/dsh/wechat-publish`(`scripts/bundle/wechat-publish/` + build/gen/verify 流水线,`wechat-publish.bundle.json` 逐字节守门)。守门:`scripts/verify-wechat-publish.mjs`(17 条:纯函数 + **真跑 spawn 的「假 opencli」E2E**(含标题不进 argv 的逐字断言)+ 真实 HTTP 路由守卫;**已反向验证**:拆掉 `guardWechat` 即红)+ `scripts/verify-wechat-publish-bundle.mjs`(进 `verify:static`)+ `verify-wechat-adapters.mjs` 新增「两个 publish adapter 都必须走 `resolveNoteTitle`」。
|
package/lib/client.js
CHANGED
|
@@ -761,7 +761,7 @@ html[data-dsh-tw-active] .dshDesktopConversationSurface > :not([data-dsh-tw-view
|
|
|
761
761
|
}`,{label:`enum`,detail:`definition`,type:`keyword`})]),Ah=new Ll,jh=new Set([`Script`,`Block`,`FunctionExpression`,`FunctionDeclaration`,`ArrowFunction`,`MethodDeclaration`,`ForStatement`]);function Mh(e){return(t,n)=>{let r=t.node.getChild(`VariableDefinition`);return r&&n(r,e),!0}}const Nh=[`FunctionDeclaration`],Ph={FunctionDeclaration:Mh(`function`),ClassDeclaration:Mh(`class`),ClassExpression:()=>!0,EnumDeclaration:Mh(`constant`),TypeAliasDeclaration:Mh(`type`),NamespaceDeclaration:Mh(`namespace`),VariableDefinition(e,t){e.matchContext(Nh)||t(e,`variable`)},TypeDefinition(e,t){t(e,`type`)},__proto__:null};function Fh(e,t){let n=Ah.get(t);if(n)return n;let r=[],i=!0;function a(t,n){let i=e.sliceString(t.from,t.to);r.push({label:i,type:n})}return t.cursor(W.IncludeAnonymous).iterate(t=>{if(i)i=!1;else if(t.name){let e=Ph[t.name];if(e&&e(t,a)||jh.has(t.name))return!1}else if(t.to-t.from>8192){for(let n of Fh(e,t.node))r.push(n);return!1}}),Ah.set(t,r),r}const Ih=/^[\w$\xa1-\uffff][\w$\d\xa1-\uffff]*$/,Lh=[`TemplateString`,`String`,`RegExp`,`LineComment`,`BlockComment`,`VariableDefinition`,`TypeDefinition`,`Label`,`PropertyDefinition`,`PropertyName`,`PrivatePropertyDefinition`,`PrivatePropertyName`,`JSXText`,`JSXAttributeValue`,`JSXOpenTag`,`JSXCloseTag`,`JSXSelfClosingTag`,`.`,`?.`];function Rh(e){let t=J(e.state).resolveInner(e.pos,-1);if(Lh.indexOf(t.name)>-1)return null;let n=t.name==`VariableName`||t.to-t.from<20&&Ih.test(e.state.sliceDoc(t.from,t.to));if(!n&&!e.explicit)return null;let r=[];for(let n=t;n;n=n.parent)jh.has(n.name)&&(r=r.concat(Fh(e.state.doc,n)));return{options:r,from:n?t.from:e.pos,validFor:Ih}}const zh=Pu.define({name:`javascript`,parser:Dh.configure({props:[Qu.add({IfStatement:ud({except:/^\s*({|else\b)/}),TryStatement:ud({except:/^\s*({|catch\b|finally\b)/}),LabeledStatement:ld,SwitchBody:e=>{let t=e.textAfter,n=/^\s*\}/.test(t),r=/^\s*(case|default)\b/.test(t);return e.baseIndent+(n?0:r?1:2)*e.unit},Block:sd({closing:`}`}),ArrowFunction:e=>e.baseIndent+e.unit,"TemplateString BlockComment":()=>null,"Statement Property":ud({except:/^\s*{/}),JSXElement(e){let t=/^\s*<\//.test(e.textAfter);return e.lineIndent(e.node.from)+(t?0:e.unit)},JSXEscape(e){let t=/\s*\}/.test(e.textAfter);return e.lineIndent(e.node.from)+(t?0:e.unit)},"JSXOpenTag JSXSelfClosingTag"(e){return e.column(e.node.from)+e.unit}}),fd.add({"Block ClassBody SwitchBody EnumBody ObjectExpression ArrayExpression ObjectType":pd,BlockComment(e){return{from:e.from+2,to:e.to-2}},JSXElement(e){let t=e.firstChild;if(!t||t.name==`JSXSelfClosingTag`)return null;let n=e.lastChild;return{from:t.to,to:n.type.isError?e.to:n.from}},"JSXSelfClosingTag JSXOpenTag"(e){let t=e.firstChild?.nextSibling,n=e.lastChild;return!t||t.type.isError?null:{from:t.to,to:n.type.isError?e.to:n.from}}})]}),languageData:{closeBrackets:{brackets:[`(`,`[`,`{`,`'`,`"`,"`"]},commentTokens:{line:`//`,block:{open:`/*`,close:`*/`}},indentOnInput:/^\s*(?:case |default:|\{|\}|<\/)$/,wordChars:`$`}}),Bh={test:e=>/^JSX/.test(e.name),facet:Au({commentTokens:{block:{open:`{/*`,close:`*/}`}}})},Vh=zh.configure({dialect:`ts`},`typescript`),Hh=zh.configure({dialect:`jsx`,props:[ju.add(e=>e.isTop?[Bh]:void 0)]}),Uh=zh.configure({dialect:`jsx ts`,props:[ju.add(e=>e.isTop?[Bh]:void 0)]},`typescript`);let Wh=e=>({label:e,type:`keyword`});const Gh=`break case const continue default delete export extends false finally in instanceof let new return static super switch this throw true typeof var yield`.split(` `).map(Wh),Kh=Gh.concat([`declare`,`implements`,`private`,`protected`,`public`].map(Wh));function qh(e={}){let t=e.jsx?e.typescript?Uh:Hh:e.typescript?Vh:zh,n=e.typescript?kh.concat(Kh):Oh.concat(Gh);return new Wu(t,[zh.data.of({autocomplete:Rd(Lh,Ld(n))}),zh.data.of({autocomplete:Rh}),e.jsx?Zh:[]])}function Jh(e){for(;;){if(e.name==`JSXOpenTag`||e.name==`JSXSelfClosingTag`||e.name==`JSXFragmentTag`)return e;if(e.name==`JSXEscape`||!e.parent)return null;e=e.parent}}function Yh(e,t,n=e.length){for(let r=t?.firstChild;r;r=r.nextSibling)if(r.name==`JSXIdentifier`||r.name==`JSXBuiltin`||r.name==`JSXNamespacedName`||r.name==`JSXMemberExpression`)return e.sliceString(r.from,Math.min(r.to,n));return``}const Xh=typeof navigator==`object`&&/Android\b/.test(navigator.userAgent),Zh=V.inputHandler.of((e,t,n,r,i)=>{if((Xh?e.composing:e.compositionStarted)||e.state.readOnly||t!=n||r!=`>`&&r!=`/`||!zh.isActiveAt(e.state,t,-1))return!1;let a=i(),{state:o}=a,s=o.changeByRange(e=>{let{head:t}=e,n=J(o).resolveInner(t-1,-1),i;if(n.name==`JSXStartTag`&&(n=n.parent),!(o.doc.sliceString(t-1,t)!=r||n.name==`JSXAttributeValue`&&n.to>t)){if(r==`>`&&n.name==`JSXFragmentTag`)return{range:e,changes:{from:t,insert:`</>`}};if(r==`/`&&n.name==`JSXStartCloseTag`){let e=n.parent,r=e.parent;if(r&&e.from==t-2&&((i=Yh(o.doc,r.firstChild,t))||r.firstChild?.name==`JSXFragmentTag`)){let e=`${i}>`;return{range:A.cursor(t+e.length,-1),changes:{from:t,insert:e}}}}else if(r==`>`){let r=Jh(n);if(r&&r.name==`JSXOpenTag`&&!/^\/?>|^<\//.test(o.doc.sliceString(t,t+2))&&(i=Yh(o.doc,r,t)))return{range:e,changes:{from:t,insert:`</${i}>`}}}}return{range:e}});return s.changes.empty?!1:(e.dispatch([a,o.update(s,{userEvent:`input.complete`,scrollIntoView:!0})]),!0)}),Qh=[`_blank`,`_self`,`_top`,`_parent`],$h=[`ascii`,`utf-8`,`utf-16`,`latin1`,`latin1`],eg=[`get`,`post`,`put`,`delete`],tg=[`application/x-www-form-urlencoded`,`multipart/form-data`,`text/plain`],ng=[`true`,`false`],Z={},rg={a:{attrs:{href:null,ping:null,type:null,media:null,target:Qh,hreflang:null}},abbr:Z,address:Z,area:{attrs:{alt:null,coords:null,href:null,target:null,ping:null,media:null,hreflang:null,type:null,shape:[`default`,`rect`,`circle`,`poly`]}},article:Z,aside:Z,audio:{attrs:{src:null,mediagroup:null,crossorigin:[`anonymous`,`use-credentials`],preload:[`none`,`metadata`,`auto`],autoplay:[`autoplay`],loop:[`loop`],controls:[`controls`]}},b:Z,base:{attrs:{href:null,target:Qh}},bdi:Z,bdo:Z,blockquote:{attrs:{cite:null}},body:Z,br:Z,button:{attrs:{form:null,formaction:null,name:null,value:null,autofocus:[`autofocus`],disabled:[`autofocus`],formenctype:tg,formmethod:eg,formnovalidate:[`novalidate`],formtarget:Qh,type:[`submit`,`reset`,`button`]}},canvas:{attrs:{width:null,height:null}},caption:Z,center:Z,cite:Z,code:Z,col:{attrs:{span:null}},colgroup:{attrs:{span:null}},command:{attrs:{type:[`command`,`checkbox`,`radio`],label:null,icon:null,radiogroup:null,command:null,title:null,disabled:[`disabled`],checked:[`checked`]}},data:{attrs:{value:null}},datagrid:{attrs:{disabled:[`disabled`],multiple:[`multiple`]}},datalist:{attrs:{data:null}},dd:Z,del:{attrs:{cite:null,datetime:null}},details:{attrs:{open:[`open`]}},dfn:Z,div:Z,dl:Z,dt:Z,em:Z,embed:{attrs:{src:null,type:null,width:null,height:null}},eventsource:{attrs:{src:null}},fieldset:{attrs:{disabled:[`disabled`],form:null,name:null}},figcaption:Z,figure:Z,footer:Z,form:{attrs:{action:null,name:null,"accept-charset":$h,autocomplete:[`on`,`off`],enctype:tg,method:eg,novalidate:[`novalidate`],target:Qh}},h1:Z,h2:Z,h3:Z,h4:Z,h5:Z,h6:Z,head:{children:[`title`,`base`,`link`,`style`,`meta`,`script`,`noscript`,`command`]},header:Z,hgroup:Z,hr:Z,html:{attrs:{manifest:null}},i:Z,iframe:{attrs:{src:null,srcdoc:null,name:null,width:null,height:null,sandbox:[`allow-top-navigation`,`allow-same-origin`,`allow-forms`,`allow-scripts`],seamless:[`seamless`]}},img:{attrs:{alt:null,src:null,ismap:null,usemap:null,width:null,height:null,crossorigin:[`anonymous`,`use-credentials`]}},input:{attrs:{alt:null,dirname:null,form:null,formaction:null,height:null,list:null,max:null,maxlength:null,min:null,name:null,pattern:null,placeholder:null,size:null,src:null,step:null,value:null,width:null,accept:[`audio/*`,`video/*`,`image/*`],autocomplete:[`on`,`off`],autofocus:[`autofocus`],checked:[`checked`],disabled:[`disabled`],formenctype:tg,formmethod:eg,formnovalidate:[`novalidate`],formtarget:Qh,multiple:[`multiple`],readonly:[`readonly`],required:[`required`],type:[`hidden`,`text`,`search`,`tel`,`url`,`email`,`password`,`datetime`,`date`,`month`,`week`,`time`,`datetime-local`,`number`,`range`,`color`,`checkbox`,`radio`,`file`,`submit`,`image`,`reset`,`button`]}},ins:{attrs:{cite:null,datetime:null}},kbd:Z,keygen:{attrs:{challenge:null,form:null,name:null,autofocus:[`autofocus`],disabled:[`disabled`],keytype:[`RSA`]}},label:{attrs:{for:null,form:null}},legend:Z,li:{attrs:{value:null}},link:{attrs:{href:null,type:null,hreflang:null,media:null,sizes:[`all`,`16x16`,`16x16 32x32`,`16x16 32x32 64x64`]}},map:{attrs:{name:null}},mark:Z,menu:{attrs:{label:null,type:[`list`,`context`,`toolbar`]}},meta:{attrs:{content:null,charset:$h,name:[`viewport`,`application-name`,`author`,`description`,`generator`,`keywords`],"http-equiv":[`content-language`,`content-type`,`default-style`,`refresh`]}},meter:{attrs:{value:null,min:null,low:null,high:null,max:null,optimum:null}},nav:Z,noscript:Z,object:{attrs:{data:null,type:null,name:null,usemap:null,form:null,width:null,height:null,typemustmatch:[`typemustmatch`]}},ol:{attrs:{reversed:[`reversed`],start:null,type:[`1`,`a`,`A`,`i`,`I`]},children:[`li`,`script`,`template`,`ul`,`ol`]},optgroup:{attrs:{disabled:[`disabled`],label:null}},option:{attrs:{disabled:[`disabled`],label:null,selected:[`selected`],value:null}},output:{attrs:{for:null,form:null,name:null}},p:Z,param:{attrs:{name:null,value:null}},pre:Z,progress:{attrs:{value:null,max:null}},q:{attrs:{cite:null}},rp:Z,rt:Z,ruby:Z,samp:Z,script:{attrs:{type:[`text/javascript`],src:null,async:[`async`],defer:[`defer`],charset:$h}},section:Z,select:{attrs:{form:null,name:null,size:null,autofocus:[`autofocus`],disabled:[`disabled`],multiple:[`multiple`]}},slot:{attrs:{name:null}},small:Z,source:{attrs:{src:null,type:null,media:null}},span:Z,strong:Z,style:{attrs:{type:[`text/css`],media:null,scoped:null}},sub:Z,summary:Z,sup:Z,table:Z,tbody:Z,td:{attrs:{colspan:null,rowspan:null,headers:null}},template:Z,textarea:{attrs:{dirname:null,form:null,maxlength:null,name:null,placeholder:null,rows:null,cols:null,autofocus:[`autofocus`],disabled:[`disabled`],readonly:[`readonly`],required:[`required`],wrap:[`soft`,`hard`]}},tfoot:Z,th:{attrs:{colspan:null,rowspan:null,headers:null,scope:[`row`,`col`,`rowgroup`,`colgroup`]}},thead:Z,time:{attrs:{datetime:null}},title:Z,tr:Z,track:{attrs:{src:null,label:null,default:null,kind:[`subtitles`,`captions`,`descriptions`,`chapters`,`metadata`],srclang:null}},ul:{children:[`li`,`script`,`template`,`ul`,`ol`]},var:Z,video:{attrs:{src:null,poster:null,width:null,height:null,crossorigin:[`anonymous`,`use-credentials`],preload:[`auto`,`metadata`,`none`],autoplay:[`autoplay`],mediagroup:[`movie`],muted:[`muted`],controls:[`controls`]}},wbr:Z},ig={accesskey:null,class:null,contenteditable:ng,contextmenu:null,dir:[`ltr`,`rtl`,`auto`],draggable:[`true`,`false`,`auto`],dropzone:[`copy`,`move`,`link`,`string:`,`file:`],hidden:[`hidden`],id:null,inert:[`inert`],itemid:null,itemprop:null,itemref:null,itemscope:[`itemscope`],itemtype:null,lang:[`ar`,`bn`,`de`,`en-GB`,`en-US`,`es`,`fr`,`hi`,`id`,`ja`,`pa`,`pt`,`ru`,`tr`,`zh`],spellcheck:ng,autocorrect:ng,autocapitalize:ng,style:null,tabindex:null,title:null,translate:[`yes`,`no`],rel:[`stylesheet`,`alternate`,`author`,`bookmark`,`help`,`license`,`next`,`nofollow`,`noreferrer`,`prefetch`,`prev`,`search`,`tag`],role:`alert application article banner button cell checkbox complementary contentinfo dialog document feed figure form grid gridcell heading img list listbox listitem main navigation region row rowgroup search switch tab table tabpanel textbox timer`.split(` `),"aria-activedescendant":null,"aria-atomic":ng,"aria-autocomplete":[`inline`,`list`,`both`,`none`],"aria-busy":ng,"aria-checked":[`true`,`false`,`mixed`,`undefined`],"aria-controls":null,"aria-describedby":null,"aria-disabled":ng,"aria-dropeffect":null,"aria-expanded":[`true`,`false`,`undefined`],"aria-flowto":null,"aria-grabbed":[`true`,`false`,`undefined`],"aria-haspopup":ng,"aria-hidden":ng,"aria-invalid":[`true`,`false`,`grammar`,`spelling`],"aria-label":null,"aria-labelledby":null,"aria-level":null,"aria-live":[`off`,`polite`,`assertive`],"aria-multiline":ng,"aria-multiselectable":ng,"aria-owns":null,"aria-posinset":null,"aria-pressed":[`true`,`false`,`mixed`,`undefined`],"aria-readonly":ng,"aria-relevant":null,"aria-required":ng,"aria-selected":[`true`,`false`,`undefined`],"aria-setsize":null,"aria-sort":[`ascending`,`descending`,`none`,`other`],"aria-valuemax":null,"aria-valuemin":null,"aria-valuenow":null,"aria-valuetext":null},ag=`beforeunload copy cut dragstart dragover dragleave dragenter dragend drag paste focus blur change click load mousedown mouseenter mouseleave mouseup keydown keyup resize scroll unload`.split(` `).map(e=>`on`+e);for(let e of ag)ig[e]=null;var og=class{constructor(e,t){this.tags={...rg,...e},this.globalAttrs={...ig,...t},this.allTags=Object.keys(this.tags),this.globalAttrNames=Object.keys(this.globalAttrs)}};og.default=new og;function sg(e,t,n=e.length){if(!t)return``;let r=t.firstChild,i=r&&r.getChild(`TagName`);return i?e.sliceString(i.from,Math.min(i.to,n)):``}function cg(e,t=!1){for(;e;e=e.parent)if(e.name==`Element`)if(t)t=!1;else return e;return null}function lg(e,t,n){return n.tags[sg(e,cg(t))]?.children||n.allTags}function ug(e,t){let n=[];for(let r=cg(t);r&&!r.type.isTop;r=cg(r.parent)){let i=sg(e,r);if(i&&r.lastChild.name==`CloseTag`)break;i&&n.indexOf(i)<0&&(t.name==`EndTag`||t.from>=r.firstChild.to)&&n.push(i)}return n}const dg=/^[:\-\.\w\u00b7-\uffff]*$/;function fg(e,t,n,r,i){let a=/\s*>/.test(e.sliceDoc(i,i+5))?``:`>`,o=cg(n,n.name==`StartTag`||n.name==`TagName`);return{from:r,to:i,options:lg(e.doc,o,t).map(e=>({label:e,type:`type`})).concat(ug(e.doc,n).map((e,t)=>({label:`/`+e,apply:`/`+e+a,type:`type`,boost:99-t}))),validFor:/^\/?[:\-\.\w\u00b7-\uffff]*$/}}function pg(e,t,n,r){let i=/\s*>/.test(e.sliceDoc(r,r+5))?``:`>`;return{from:n,to:r,options:ug(e.doc,t).map((e,t)=>({label:e,apply:e+i,type:`type`,boost:99-t})),validFor:dg}}function mg(e,t,n,r){let i=[],a=0;for(let r of lg(e.doc,n,t))i.push({label:`<`+r,type:`type`});for(let t of ug(e.doc,n))i.push({label:`</`+t+`>`,type:`type`,boost:99-a++});return{from:r,to:r,options:i,validFor:/^<\/?[:\-\.\w\u00b7-\uffff]*$/}}function hg(e,t,n,r,i){let a=cg(n),o=a?t.tags[sg(e.doc,a)]:null,s=o&&o.attrs?Object.keys(o.attrs):[];return{from:r,to:i,options:(o&&o.globalAttrs===!1?s:s.length?s.concat(t.globalAttrNames):t.globalAttrNames).map(e=>({label:e,type:`property`})),validFor:dg}}function gg(e,t,n,r,i){let a=n.parent?.getChild(`AttributeName`),o=[],s;if(a){let c=e.sliceDoc(a.from,a.to),l=t.globalAttrs[c];if(!l){let r=cg(n),i=r?t.tags[sg(e.doc,r)]:null;l=i?.attrs&&i.attrs[c]}if(l){let t=e.sliceDoc(r,i).toLowerCase(),n=`"`,a=`"`;/^['"]/.test(t)?(s=t[0]==`"`?/^[^"]*$/:/^[^']*$/,n=``,a=e.sliceDoc(i,i+1)==t[0]?``:t[0],t=t.slice(1),r++):s=/^[^\s<>='"]*$/;for(let e of l)o.push({label:e,apply:n+e+a,type:`constant`})}}return{from:r,to:i,options:o,validFor:s}}function _g(e,t){let{state:n,pos:r}=t,i=J(n).resolveInner(r,-1),a=i.resolve(r);for(let e=r,t;a==i&&(t=i.childBefore(e));){let n=t.lastChild;if(!n||!n.type.isError||n.from<n.to)break;a=i=t,e=n.from}return i.name==`TagName`?i.parent&&/CloseTag$/.test(i.parent.name)?pg(n,i,i.from,r):fg(n,e,i,i.from,r):i.name==`StartTag`||i.name==`IncompleteTag`?fg(n,e,i,r,r):i.name==`StartCloseTag`||i.name==`IncompleteCloseTag`?pg(n,i,r,r):i.name==`OpenTag`||i.name==`SelfClosingTag`||i.name==`AttributeName`?hg(n,e,i,i.name==`AttributeName`?i.from:r,r):i.name==`Is`||i.name==`AttributeValue`||i.name==`UnquotedAttributeValue`?gg(n,e,i,i.name==`Is`?r:i.from,r):t.explicit&&(a.name==`Element`||a.name==`Text`||a.name==`Document`)?mg(n,e,i,r):null}function vg(e){return _g(og.default,e)}function yg(e){let{extraTags:t,extraGlobalAttributes:n}=e,r=n||t?new og(t,n):og.default;return e=>_g(r,e)}const bg=zh.parser.configure({top:`SingleExpression`}),xg=[{tag:`script`,attrs:e=>e.type==`text/typescript`||e.lang==`ts`,parser:Vh.parser},{tag:`script`,attrs:e=>e.type==`text/babel`||e.type==`text/jsx`,parser:Hh.parser},{tag:`script`,attrs:e=>e.type==`text/typescript-jsx`,parser:Uh.parser},{tag:`script`,attrs(e){return/^(importmap|speculationrules|application\/(.+\+)?json)$/i.test(e.type)},parser:bg},{tag:`script`,attrs(e){return!e.type||/^(?:text|application)\/(?:x-)?(?:java|ecma)script$|^module$|^$/i.test(e.type)},parser:zh.parser},{tag:`style`,attrs(e){return(!e.lang||e.lang==`css`)&&(!e.type||/^(text\/)?(x-)?(stylesheet|css)$/i.test(e.type))},parser:ph.parser}],Sg=[{name:`style`,parser:ph.parser.configure({top:`Styles`})}].concat(ag.map(e=>({name:e,parser:zh.parser}))),Cg=Pu.define({name:`html`,parser:Mm.configure({props:[Qu.add({Element(e){let t=/^(\s*)(<\/)?/.exec(e.textAfter);return e.node.to<=e.pos+t[0].length?e.continue():e.lineIndent(e.node.from)+(t[2]?0:e.unit)},"OpenTag CloseTag SelfClosingTag"(e){return e.column(e.node.from)+e.unit},Document(e){if(e.pos+/\s*/.exec(e.textAfter)[0].length<e.node.to)return e.continue();let t=null,n;for(let n=e.node;;){let e=n.lastChild;if(!e||e.name!=`Element`||e.to!=n.to)break;t=n=e}return t&&!((n=t.lastChild)&&(n.name==`CloseTag`||n.name==`SelfClosingTag`))?e.lineIndent(t.from)+e.unit:null}}),fd.add({Element(e){let t=e.firstChild,n=e.lastChild;return!t||t.name!=`OpenTag`?null:{from:t.to,to:n.name==`CloseTag`?n.from:e.to}}}),xd.add({"OpenTag CloseTag":e=>e.getChild(`TagName`)})]}),languageData:{commentTokens:{block:{open:`<!--`,close:`-->`}},indentOnInput:/^\s*<\/\w+\W$/,wordChars:`-_`}}),wg=Cg.configure({wrap:Im(xg,Sg)});function Tg(e={}){let t=``,n;return e.matchClosingTags===!1&&(t=`noMatch`),e.selfClosingTags===!0&&(t=(t?t+` `:``)+`selfClosing`),(e.nestedLanguages&&e.nestedLanguages.length||e.nestedAttributes&&e.nestedAttributes.length)&&(n=Im((e.nestedLanguages||[]).concat(xg),(e.nestedAttributes||[]).concat(Sg))),new Wu(n?Cg.configure({wrap:n,dialect:t}):t?wg.configure({dialect:t}):wg,[wg.data.of({autocomplete:yg(e)}),e.autoCloseTags===!1?[]:Og,qh().support,mh().support])}const Eg=new Set(`area base br col command embed frame hr img input keygen link meta param source track wbr menuitem`.split(` `));function Dg(e,t,n){for(;;){if(t.lastChild?.name!=`CloseTag`)return!1;let r=t.parent;if(!r||sg(e,r)!=n)return!0;t=r}}const Og=V.inputHandler.of((e,t,n,r,i)=>{if(e.composing||e.state.readOnly||t!=n||r!=`>`&&r!=`/`||!wg.isActiveAt(e.state,t,-1))return!1;let a=i(),{state:o}=a,s=o.changeByRange(e=>{let t=o.doc.sliceString(e.from-1,e.to)==r,{head:n}=e,i=J(o).resolveInner(n,-1),a;if(t&&r==`>`&&i.name==`EndTag`){let t=i.parent;if((a=sg(o.doc,t.parent,n))&&!Eg.has(a)&&!Dg(o.doc,t.parent,a))return{range:e,changes:{from:n,to:n+ +(o.doc.sliceString(n,n+1)===`>`),insert:`</${a}>`}}}else if(t&&r==`/`&&i.name==`IncompleteCloseTag`){let e=i.parent;if(i.from==n-2&&e.lastChild?.name!=`CloseTag`&&(a=sg(o.doc,e,n))&&!Eg.has(a)){let e=n+ +(o.doc.sliceString(n,n+1)===`>`),t=`${a}>`;return{range:A.cursor(n+t.length,-1),changes:{from:n,to:e,insert:t}}}}return{range:e}});return s.changes.empty?!1:(e.dispatch([a,o.update(s,{userEvent:`input.complete`,scrollIntoView:!0})]),!0)}),kg=Au({commentTokens:{block:{open:`<!--`,close:`-->`}}}),Ag=new H,jg=dp.configure({props:[fd.add(e=>!e.is(`Block`)||e.is(`Document`)||Mg(e)!=null||Ng(e)?void 0:(e,t)=>({from:t.doc.lineAt(e.from).to,to:e.to})),Ag.add(Mg),Qu.add({Document:()=>null}),ku.add({Document:kg})]});function Mg(e){let t=/^(?:ATX|Setext)Heading(\d)$/.exec(e.name);return t?+t[1]:void 0}function Ng(e){return e.name==`OrderedList`||e.name==`BulletList`}function Pg(e,t){let n=e;for(;;){let e=n.nextSibling,r;if(!e||(r=Mg(e.type))!=null&&r<=t)break;n=e}return n.to}const Fg=dd.of((e,t,n)=>{for(let r=J(e).resolveInner(n,-1);r&&!(r.from<t);r=r.parent){let e=r.type.prop(Ag);if(e==null)continue;let t=Pg(r,e);if(t>n)return{from:n,to:t}}return null});function Ig(e){return new Mu(kg,e,[],`markdown`)}const Lg=Ig(jg),Rg=Ig(jg.configure([jp,Pp,Np,Fp,{props:[fd.add({Table:(e,t)=>({from:t.doc.lineAt(e.from).to,to:e.to})})]}]));function zg(e,t){return n=>{if(n&&e){let t=null;if(n=/\S*/.exec(n)[0],t=typeof e==`function`?e(n):Gu.matchLanguageName(e,n,!0),t instanceof Gu)return t.support?t.support.language.parser:Lu.getSkippingParser(t.load());if(t)return t.parser}return t?t.parser:null}}var Bg=class{constructor(e,t,n,r,i,a,o){this.node=e,this.from=t,this.to=n,this.spaceBefore=r,this.spaceAfter=i,this.type=a,this.item=o}blank(e,t=!0){let n=this.spaceBefore+(this.node.name==`Blockquote`?`>`:``);if(e!=null){for(;n.length<e;)n+=` `;return n}else{for(let e=this.to-this.from-n.length-this.spaceAfter.length;e>0;e--)n+=` `;return n+(t?this.spaceAfter:``)}}marker(e,t){let n=this.node.name==`OrderedList`?String(+Hg(this.item,e)[2]+t):``;return this.spaceBefore+n+this.type+this.spaceAfter}};function Vg(e,t){let n=[],r=[];for(let t=e;t;t=t.parent){if(t.name==`FencedCode`)return r;(t.name==`ListItem`||t.name==`Blockquote`)&&n.push(t)}for(let e=n.length-1;e>=0;e--){let i=n[e],a,o=t.lineAt(i.from),s=i.from-o.from;if(i.name==`Blockquote`&&(a=/^ *>( ?)/.exec(o.text.slice(s))))r.push(new Bg(i,s,s+a[0].length,``,a[1],`>`,null));else if(i.name==`ListItem`&&i.parent.name==`OrderedList`&&(a=/^( *)\d+([.)])( *)/.exec(o.text.slice(s)))){let e=a[3],t=a[0].length;e.length>=4&&(e=e.slice(0,e.length-4),t-=4),r.push(new Bg(i.parent,s,s+t,a[1],e,a[2],i))}else if(i.name==`ListItem`&&i.parent.name==`BulletList`&&(a=/^( *)([-+*])( {1,4}\[[ xX]\])?( +)/.exec(o.text.slice(s)))){let e=a[4],t=a[0].length;e.length>4&&(e=e.slice(0,e.length-4),t-=4);let n=a[2];a[3]&&(n+=a[3].replace(/[xX]/,` `)),r.push(new Bg(i.parent,s,s+t,a[1],e,n,i))}}return r}function Hg(e,t){return/^(\s*)(\d+)(?=[.)])/.exec(t.sliceString(e.from,e.from+10))}function Ug(e,t,n,r=0){for(let i=-1,a=e;;){if(a.name==`ListItem`){let e=Hg(a,t),o=+e[2];if(i>=0){if(o!=i+1)return;n.push({from:a.from+e[1].length,to:a.from+e[0].length,insert:String(i+2+r)})}i=o}let e=a.nextSibling;if(!e)break;a=e}}function Wg(e,t){let n=/^[ \t]*/.exec(e)[0].length;if(!n||t.facet(qu)!=` `)return e;let r=wr(e,4,n),i=``;for(let e=r;e>0;)e>=4?(i+=` `,e-=4):(i+=` `,e--);return i+e.slice(n)}const Gg=((e={})=>({state:t,dispatch:n})=>{let r=J(t),{doc:i}=t,a=null,o=t.changeByRange(n=>{if(!n.empty||!Rg.isActiveAt(t,n.from,-1)&&!Rg.isActiveAt(t,n.from,1))return a={range:n};let o=n.from,s=i.lineAt(o),c=Vg(r.resolveInner(o,-1),i);for(;c.length&&c[c.length-1].from>o-s.from;)c.pop();if(!c.length)return a={range:n};let l=c[c.length-1];if(l.to-l.spaceAfter.length>o-s.from)return a={range:n};let u=o>=l.to-l.spaceAfter.length&&!/\S/.test(s.text.slice(l.to));if(l.item&&u){if(l.item.from<s.from&&!/^[\s>]*$/.test(s.text.slice(0,l.to)))return a={range:n};let r=l.node.firstChild,u=l.node.getChild(`ListItem`,`ListItem`);if(r.to>=o||u&&u.to<o||s.from>0&&!/[^\s>]/.test(i.lineAt(s.from-1).text)||e.nonTightLists===!1){let e=c.length>1?c[c.length-2]:null,t,n=``;e&&e.item?(t=s.from+e.from,n=e.marker(i,1)):t=s.from+(e?e.to:0);let r=[{from:t,to:o,insert:n}];return l.node.name==`OrderedList`&&Ug(l.item,i,r,-2),e&&e.node.name==`OrderedList`&&Ug(e.item,i,r),{range:A.cursor(t+n.length),changes:r}}else{let e=Jg(c,t,s);return{range:A.cursor(o+e.length+1),changes:{from:s.from,insert:e+t.lineBreak}}}}if(l.node.name==`Blockquote`&&u&&s.from){let e=i.lineAt(s.from-1),r=/>\s*$/.exec(e.text);if(r&&r.index==l.from){let i=t.changes([{from:e.from+r.index,to:e.to},{from:s.from+l.from,to:s.to}]);return{range:n.map(i),changes:i}}}let d=[];l.node.name==`OrderedList`&&Ug(l.item,i,d);let f=l.item&&l.item.from<s.from,p=``;if(!f||/^[\s\d.)\-+*>]*/.exec(s.text)[0].length>=l.to)for(let e=0,t=c.length-1;e<=t;e++)p+=e==t&&!f?c[e].marker(i,1):c[e].blank(e<t?wr(s.text,4,c[e+1].from)-p.length:null);let m=o;for(;m>s.from&&/\s/.test(s.text.charAt(m-s.from-1));)m--;return p=Wg(p,t),qg(l.node,t.doc)&&(p=Jg(c,t,s)+t.lineBreak+p),d.push({from:m,to:o,insert:t.lineBreak+p}),{range:A.cursor(m+p.length+1),changes:d}});return a?!1:(n(t.update(o,{scrollIntoView:!0,userEvent:`input`})),!0)})();function Kg(e){return e.name==`QuoteMark`||e.name==`ListMark`}function qg(e,t){if(e.name!=`OrderedList`&&e.name!=`BulletList`)return!1;let n=e.firstChild,r=e.getChild(`ListItem`,`ListItem`);if(!r)return!1;let i=t.lineAt(n.to),a=t.lineAt(r.from),o=/^[\s>]*$/.test(i.text);return i.number+ +!o<a.number}function Jg(e,t,n){let r=``;for(let t=0,i=e.length-2;t<=i;t++)r+=e[t].blank(t<i?wr(n.text,4,e[t+1].from)-r.length:null,t<i);return Wg(r,t)}function Yg(e,t){let n=e.resolveInner(t,-1),r=t;Kg(n)&&(r=n.from,n=n.parent);for(let e;e=n.childBefore(r);)if(Kg(e))r=e.from;else if(e.name==`OrderedList`||e.name==`BulletList`)n=e.lastChild,r=n.to;else break;return n}const Xg=[{key:`Enter`,run:Gg},{key:`Backspace`,run:({state:e,dispatch:t})=>{let n=J(e),r=null,i=e.changeByRange(t=>{let i=t.from,{doc:a}=e;if(t.empty&&Rg.isActiveAt(e,t.from)){let t=a.lineAt(i),r=Vg(Yg(n,i),a);if(r.length){let n=r[r.length-1],a=n.to-n.spaceAfter.length+ +!!n.spaceAfter;if(i-t.from>a&&!/\S/.test(t.text.slice(a,i-t.from)))return{range:A.cursor(t.from+a),changes:{from:t.from+a,to:i}};if(i-t.from==a&&(n.item&&t.from<=n.item.from||/^[\s>]*$/.test(t.text.slice(0,n.to)))){let r=t.from+n.from;if(n.item&&n.node.from<n.item.from&&/\S/.test(t.text.slice(n.from,n.to))){let i=n.blank(wr(t.text,4,n.to)-wr(t.text,4,n.from));return r==t.from&&(i=Wg(i,e)),{range:A.cursor(r+i.length),changes:{from:r,to:t.from+n.to,insert:i}}}if(r<i)return{range:A.cursor(r),changes:{from:r,to:i}}}}}return r={range:t}});return r?!1:(t(e.update(i,{scrollIntoView:!0,userEvent:`delete`})),!0)}}],Zg=Tg({matchClosingTags:!1});function Qg(e={}){let{codeLanguages:t,defaultCodeLanguage:n,addKeymap:r=!0,base:{parser:i}=Lg,completeHTMLTags:a=!0,pasteURLAsLink:o=!0,htmlTagLanguage:s=Zg}=e;if(!(i instanceof zf))throw RangeError("Base parser provided to `markdown` should be a Markdown parser");let c=e.extensions?[e.extensions]:[],l=[s.support,Fg],u;o&&l.push(r_),n instanceof Wu?(l.push(n.support),u=n.language):n&&(u=n);let d=t||u?zg(t,u):void 0;c.push(pp({codeParser:d,htmlParser:s.language.parser})),r&&l.push(Dn.high(Rc.of(Xg)));let f=Ig(i.configure(c));return a&&l.push(f.data.of({autocomplete:$g})),new Wu(f,l)}function $g(e){let{state:t,pos:n}=e,r=/<[:\-\.\w\u00b7-\uffff]*$/.exec(t.sliceDoc(n-25,n));if(!r)return null;let i=J(t).resolveInner(n,-1);for(;i&&!i.type.isTop;){if(i.name==`CodeBlock`||i.name==`FencedCode`||i.name==`ProcessingInstructionBlock`||i.name==`CommentBlock`||i.name==`Link`||i.name==`Image`)return null;i=i.parent}return{from:n-r[0].length,to:n,options:t_(),validFor:/^<[:\-\.\w\u00b7-\uffff]*$/}}let e_=null;function t_(){if(e_)return e_;let e=vg(new Pd(N.create({extensions:Zg}),0,!0));return e_=e?e.options:[]}const n_=/code|horizontalrule|html|link|comment|processing|escape|entity|image|mark|url/i,r_=V.domEventHandlers({paste:(e,t)=>{let{main:n}=t.state.selection;if(n.empty)return!1;let r=e.clipboardData?.getData(`text/plain`);if(!r||!/^(https?:\/\/|mailto:|xmpp:|www\.)/.test(r)||(/^www\./.test(r)&&(r=`https://`+r),!Rg.isActiveAt(t.state,n.from,1)))return!1;let i=J(t.state),a=!1;return i.iterate({from:n.from,to:n.to,enter:e=>{(e.from>n.from||n_.test(e.name))&&(a=!0)},leave:e=>{e.to<n.to&&(a=!0)}}),a?!1:(t.dispatch({changes:[{from:n.from,insert:`[`},{from:n.to,insert:`](${r})`}],userEvent:`input.paste`,scrollIntoView:!0}),!0)}}),i_=e=>{let{state:t}=e,n=t.doc.lineAt(t.selection.main.from),r=l_(e.state,n.from);return r.line?o_(e):r.block?c_(e):!1};function a_(e,t){return({state:n,dispatch:r})=>{if(n.readOnly)return!1;let i=e(t,n);return i?(r(n.update(i)),!0):!1}}const o_=a_(p_,0),s_=a_(f_,0),c_=a_((e,t)=>f_(e,t,d_(t)),0);function l_(e,t){let n=e.languageDataAt(`commentTokens`,t,1);return n.length?n[0]:{}}function u_(e,{open:t,close:n},r,i){let a=e.sliceDoc(r-50,r),o=e.sliceDoc(i,i+50),s=/\s*$/.exec(a)[0].length,c=/^\s*/.exec(o)[0].length,l=a.length-s;if(a.slice(l-t.length,l)==t&&o.slice(c,c+n.length)==n)return{open:{pos:r-s,margin:s&&1},close:{pos:i+c,margin:c&&1}};let u,d;i-r<=100?u=d=e.sliceDoc(r,i):(u=e.sliceDoc(r,r+50),d=e.sliceDoc(i-50,i));let f=/^\s*/.exec(u)[0].length,p=/\s*$/.exec(d)[0].length,m=d.length-p-n.length;return u.slice(f,f+t.length)==t&&d.slice(m,m+n.length)==n?{open:{pos:r+f+t.length,margin:+!!/\s/.test(u.charAt(f+t.length))},close:{pos:i-p-n.length,margin:+!!/\s/.test(d.charAt(m-1))}}:null}function d_(e){let t=[];for(let n of e.selection.ranges){let r=e.doc.lineAt(n.from),i=n.to<=r.to?r:e.doc.lineAt(n.to);i.from>r.from&&i.from==n.to&&(i=n.to==r.to+1?r:e.doc.lineAt(n.to-1));let a=t.length-1;a>=0&&t[a].to>r.from?t[a].to=i.to:t.push({from:r.from+/^\s*/.exec(r.text)[0].length,to:i.to})}return t}function f_(e,t,n=t.selection.ranges){let r=n.map(e=>l_(t,e.from).block);if(!r.every(e=>e))return null;let i=n.map((e,n)=>u_(t,r[n],e.from,e.to));if(e!=2&&!i.every(e=>e))return{changes:t.changes(n.map((e,t)=>i[t]?[]:[{from:e.from,insert:r[t].open+` `},{from:e.to,insert:` `+r[t].close}]))};if(e!=1&&i.some(e=>e)){let e=[];for(let t=0,n;t<i.length;t++)if(n=i[t]){let i=r[t],{open:a,close:o}=n;e.push({from:a.pos-i.open.length,to:a.pos+a.margin},{from:o.pos-o.margin,to:o.pos+i.close.length})}return{changes:e}}return null}function p_(e,t,n=t.selection.ranges){let r=[],i=-1;ranges:for(let{from:e,to:a}of n){let n=r.length,o=1e9,s;for(let n=e;n<=a;){let c=t.doc.lineAt(n);if(s==null&&(s=l_(t,c.from).line,!s))continue ranges;if(c.from>i&&(e==a||a>c.from)){i=c.from;let e=/^\s*/.exec(c.text)[0].length,t=e==c.length,n=c.text.slice(e,e+s.length)==s?e:-1;e<c.text.length&&e<o&&(o=e),r.push({line:c,comment:n,token:s,indent:e,empty:t,single:!1})}n=c.to+1}if(o<1e9)for(let e=n;e<r.length;e++)r[e].indent<r[e].line.text.length&&(r[e].indent=o);r.length==n+1&&(r[n].single=!0)}if(e!=2&&r.some(e=>e.comment<0&&(!e.empty||e.single))){let e=[];for(let{line:t,token:n,indent:i,empty:a,single:o}of r)(o||!a)&&e.push({from:t.from+i,insert:n+` `});let n=t.changes(e);return{changes:n,selection:t.selection.map(n,1)}}else if(e!=1&&r.some(e=>e.comment>=0)){let e=[];for(let{line:t,comment:n,token:i}of r)if(n>=0){let r=t.from+n,a=r+i.length;t.text[a-t.from]==` `&&a++,e.push({from:r,to:a})}return{changes:e}}return null}const m_=Hn.define(),h_=Hn.define(),g_=j.define(),__=j.define({combine(e){return ar(e,{minDepth:100,newGroupDelay:500,joinToEvent:(e,t)=>t},{minDepth:Math.max,newGroupDelay:Math.min,joinToEvent:(e,t)=>(n,r)=>e(n,r)||t(n,r)})}}),v_=wn.define({create(){return I_.empty},update(e,t){let n=t.state.facet(__),r=t.annotation(m_);if(r){let i=T_.fromTransaction(t,r.selection),a=r.side,o=a==0?e.undone:e.done;return o=i?E_(o,o.length,n.minDepth,i):j_(o,t.startState.selection),new I_(a==0?r.rest:o,a==0?o:r.rest)}let i=t.annotation(h_);if((i==`full`||i==`before`)&&(e=e.isolate()),t.annotation(Gn.addToHistory)===!1)return t.changes.empty?e:e.addMapping(t.changes.desc);let a=T_.fromTransaction(t),o=t.annotation(Gn.time),s=t.annotation(Gn.userEvent);return a?e=e.addChanges(a,o,s,n,t):t.selection&&(e=e.addSelection(t.startState.selection,o,s,n.newGroupDelay)),(i==`full`||i==`after`)&&(e=e.isolate()),e},toJSON(e){return{done:e.done.map(e=>e.toJSON()),undone:e.undone.map(e=>e.toJSON())}},fromJSON(e){return new I_(e.done.map(T_.fromJSON),e.undone.map(T_.fromJSON))}});function y_(e={}){return[v_,__.of(e),V.domEventHandlers({beforeinput(e,t){let n=e.inputType==`historyUndo`?x_:e.inputType==`historyRedo`?S_:null;return n?(e.preventDefault(),n(t)):!1}})]}function b_(e,t){return function({state:n,dispatch:r}){if(!t&&n.readOnly)return!1;let i=n.field(v_,!1);if(!i)return!1;let a=i.pop(e,n,t);return a?(r(a),!0):!1}}const x_=b_(0,!1),S_=b_(1,!1),C_=b_(0,!0),w_=b_(1,!0);var T_=class e{constructor(e,t,n,r,i){this.changes=e,this.effects=t,this.mapped=n,this.startSelection=r,this.selectionsAfter=i}setSelAfter(t){return new e(this.changes,this.effects,this.mapped,this.startSelection,t)}toJSON(){return{changes:this.changes?.toJSON(),mapped:this.mapped?.toJSON(),startSelection:this.startSelection?.toJSON(),selectionsAfter:this.selectionsAfter.map(e=>e.toJSON())}}static fromJSON(t){return new e(t.changes&&cn.fromJSON(t.changes),[],t.mapped&&sn.fromJSON(t.mapped),t.startSelection&&A.fromJSON(t.startSelection),t.selectionsAfter.map(A.fromJSON))}static fromTransaction(t,n){let r=A_;for(let e of t.startState.facet(g_)){let n=e(t);n.length&&(r=r.concat(n))}return!r.length&&t.changes.empty?null:new e(t.changes.invert(t.startState.doc),r,void 0,n||t.startState.selection,A_)}static selection(t){return new e(void 0,A_,void 0,void 0,t)}};function E_(e,t,n,r){let i=t+1>n+20?t-n-1:0,a=e.slice(i,t);return a.push(r),a}function D_(e,t){let n=[],r=!1;return e.iterChangedRanges((e,t)=>n.push(e,t)),t.iterChangedRanges((e,t,i,a)=>{for(let e=0;e<n.length;){let t=n[e++],o=n[e++];a>=t&&i<=o&&(r=!0)}}),r}function O_(e,t){return e.ranges.length==t.ranges.length&&e.ranges.filter((e,n)=>e.empty!=t.ranges[n].empty).length===0}function k_(e,t){return e.length?t.length?e.concat(t):e:t}const A_=[];function j_(e,t){if(e.length){let n=e[e.length-1],r=n.selectionsAfter.slice(Math.max(0,n.selectionsAfter.length-200));return r.length&&r[r.length-1].eq(t)?e:(r.push(t),E_(e,e.length-1,1e9,n.setSelAfter(r)))}else return[T_.selection([t])]}function M_(e){let t=e[e.length-1],n=e.slice();return n[e.length-1]=t.setSelAfter(t.selectionsAfter.slice(0,t.selectionsAfter.length-1)),n}function N_(e,t){if(!e.length)return e;let n=e.length,r=A_;for(;n;){let i=P_(e[n-1],t,r);if(i.changes&&!i.changes.empty||i.effects.length){let t=e.slice(0,n);return t[n-1]=i,t}else t=i.mapped,n--,r=i.selectionsAfter}return r.length?[T_.selection(r)]:A_}function P_(e,t,n){let r=k_(e.selectionsAfter.length?e.selectionsAfter.map(e=>e.map(t)):A_,n);if(!e.changes)return T_.selection(r);let i=e.changes.map(t),a=t.mapDesc(e.changes,!0),o=e.mapped?e.mapped.composeDesc(a):a;return new T_(i,M.mapEffects(e.effects,t),o,e.startSelection.map(a),r)}const F_=/^(input\.type|delete)($|\.)/;var I_=class e{constructor(e,t,n=0,r=void 0){this.done=e,this.undone=t,this.prevTime=n,this.prevUserEvent=r}isolate(){return this.prevTime?new e(this.done,this.undone):this}addChanges(t,n,r,i,a){let o=this.done,s=o[o.length-1];return o=s&&s.changes&&!s.changes.empty&&t.changes&&(!r||F_.test(r))&&(!s.selectionsAfter.length&&n-this.prevTime<i.newGroupDelay&&i.joinToEvent(a,D_(s.changes,t.changes))||r==`input.type.compose`)?E_(o,o.length-1,i.minDepth,new T_(t.changes.compose(s.changes),k_(M.mapEffects(t.effects,s.changes),s.effects),s.mapped,s.startSelection,A_)):E_(o,o.length,i.minDepth,t),new e(o,A_,n,r)}addSelection(t,n,r,i){let a=this.done.length?this.done[this.done.length-1].selectionsAfter:A_;return a.length>0&&n-this.prevTime<i&&r==this.prevUserEvent&&r&&/^select($|\.)/.test(r)&&O_(a[a.length-1],t)?this:new e(j_(this.done,t),this.undone,n,r)}addMapping(t){return new e(N_(this.done,t),N_(this.undone,t),this.prevTime,this.prevUserEvent)}pop(e,t,n){let r=e==0?this.done:this.undone;if(r.length==0)return null;let i=r[r.length-1],a=i.selectionsAfter[0]||(i.startSelection?i.startSelection.map(i.changes.invertedDesc,1):t.selection);if(n&&i.selectionsAfter.length)return t.update({selection:i.selectionsAfter[i.selectionsAfter.length-1],annotations:m_.of({side:e,rest:M_(r),selection:a}),userEvent:e==0?`select.undo`:`select.redo`,scrollIntoView:!0});if(i.changes){let n=r.length==1?A_:r.slice(0,r.length-1);return i.mapped&&(n=N_(n,i.mapped)),t.update({changes:i.changes,selection:i.startSelection,effects:i.effects,annotations:m_.of({side:e,rest:n,selection:a}),filter:!1,userEvent:e==0?`undo`:`redo`,scrollIntoView:!0})}else return null}};I_.empty=new I_(A_,A_);const L_=[{key:`Mod-z`,run:x_,preventDefault:!0},{key:`Mod-y`,mac:`Mod-Shift-z`,run:S_,preventDefault:!0},{linux:`Ctrl-Shift-z`,run:S_,preventDefault:!0},{key:`Mod-u`,run:C_,preventDefault:!0},{key:`Alt-u`,mac:`Mod-Shift-u`,run:w_,preventDefault:!0}];function R_(e,t){return A.create(e.ranges.map(t),e.mainIndex)}function z_(e,t){return e.update({selection:t,scrollIntoView:!0,userEvent:`select`})}function B_({state:e,dispatch:t},n){let r=R_(e.selection,n);return r.eq(e.selection,!0)?!1:(t(z_(e,r)),!0)}function V_(e,t){return A.cursor(t?e.to:e.from)}function H_(e,t){return B_(e,n=>n.empty?e.moveByChar(n,t):V_(n,t))}function Q(e){return e.textDirectionAt(e.state.selection.main.head)==L.LTR}const U_=e=>H_(e,!Q(e)),W_=e=>H_(e,Q(e));function G_(e,t){return B_(e,n=>n.empty?e.moveByGroup(n,t):V_(n,t))}const K_=e=>G_(e,!Q(e)),q_=e=>G_(e,Q(e));typeof Intl<`u`&&Intl.Segmenter;function J_(e,t,n){if(t.type.prop(n))return!0;let r=t.to-t.from;return r&&(r>2||/[^\s,.;:]/.test(e.sliceDoc(t.from,t.to)))||t.firstChild}function Y_(e,t,n){let r=J(e).resolveInner(t.head),i=n?H.closedBy:H.openedBy;for(let a=t.head;;){let t=n?r.childAfter(a):r.childBefore(a);if(!t)break;J_(e,t,i)?r=t:a=n?t.to:t.from}let a=r.type.prop(i),o,s;return s=a&&(o=n?wd(e,r.from,1):wd(e,r.to,-1))&&o.matched?n?o.end.to:o.end.from:n?r.to:r.from,A.cursor(s,n?-1:1)}const X_=e=>B_(e,t=>Y_(e.state,t,!Q(e))),Z_=e=>B_(e,t=>Y_(e.state,t,Q(e)));function Q_(e,t){return B_(e,n=>{if(!n.empty)return V_(n,t);let r=e.moveVertically(n,t);return r.head==n.head?e.moveToLineBoundary(n,t):r})}const $_=e=>Q_(e,!1),ev=e=>Q_(e,!0);function tv(e){let t=e.scrollDOM.clientHeight<e.scrollDOM.scrollHeight-2,n=0,r=0,i;if(t){for(let t of e.state.facet(V.scrollMargins)){let i=t(e);i?.top&&(n=Math.max(i?.top,n)),i?.bottom&&(r=Math.max(i?.bottom,r))}i=e.scrollDOM.clientHeight-n-r}else i=(e.dom.ownerDocument.defaultView||window).innerHeight;return{marginTop:n,marginBottom:r,selfScroll:t,height:Math.max(e.defaultLineHeight,i-5)}}function nv(e,t){let n=tv(e),{state:r}=e,i=R_(r.selection,r=>r.empty?e.moveVertically(r,t,n.height):V_(r,t));if(i.eq(r.selection))return!1;let a;if(n.selfScroll){let t=e.coordsAtPos(r.selection.main.head),o=e.scrollDOM.getBoundingClientRect(),s=o.top+n.marginTop,c=o.bottom-n.marginBottom;t&&t.top>s&&t.bottom<c&&(a=V.scrollIntoView(i.main.head,{y:`start`,yMargin:t.top-s}))}return e.dispatch(z_(r,i),{effects:a}),!0}const rv=e=>nv(e,!1),iv=e=>nv(e,!0);function av(e,t,n){let r=e.lineBlockAt(t.head),i=e.moveToLineBoundary(t,n);if(i.head==t.head&&i.head!=(n?r.to:r.from)&&(i=e.moveToLineBoundary(t,n,!1)),!n&&i.head==r.from&&r.length){let n=/^\s*/.exec(e.state.sliceDoc(r.from,Math.min(r.from+100,r.to)))[0].length;n&&t.head!=r.from+n&&(i=A.cursor(r.from+n))}return i}const ov=e=>B_(e,t=>av(e,t,!0)),sv=e=>B_(e,t=>av(e,t,!1)),cv=e=>B_(e,t=>av(e,t,!Q(e))),lv=e=>B_(e,t=>av(e,t,Q(e))),uv=e=>B_(e,t=>A.cursor(e.lineBlockAt(t.head).from,1)),dv=e=>B_(e,t=>A.cursor(e.lineBlockAt(t.head).to,-1));function fv(e,t,n){let r=!1,i=R_(e.selection,t=>{let i=wd(e,t.head,-1)||wd(e,t.head,1)||t.head>0&&wd(e,t.head-1,1)||t.head<e.doc.length&&wd(e,t.head+1,-1);if(!i||!i.end)return t;r=!0;let a=i.start.from==t.head?i.end.to:i.end.from;return n?A.range(t.anchor,a):A.cursor(a)});return r?(t(z_(e,i)),!0):!1}const pv=({state:e,dispatch:t})=>fv(e,t,!1);function mv(e,t,n){let r=R_(e.state.selection,e=>{e.undirectional&&e.head>=e.anchor!=t&&(e=A.range(e.head,e.anchor));let r=n(e);return A.range(e.anchor,r.head,r.goalColumn,r.bidiLevel||void 0,r.assoc)});return r.eq(e.state.selection)?!1:(e.dispatch(z_(e.state,r)),!0)}function hv(e,t){return mv(e,t,n=>e.moveByChar(n,t))}const gv=e=>hv(e,!Q(e)),_v=e=>hv(e,Q(e));function vv(e,t){return mv(e,t,n=>e.moveByGroup(n,t))}const yv=e=>vv(e,!Q(e)),bv=e=>vv(e,Q(e)),xv=e=>{let t=!Q(e);return mv(e,t,n=>Y_(e.state,n,t))},Sv=e=>{let t=Q(e);return mv(e,t,n=>Y_(e.state,n,t))};function Cv(e,t){return mv(e,t,n=>e.moveVertically(n,t))}const wv=e=>Cv(e,!1),Tv=e=>Cv(e,!0);function Ev(e,t){return mv(e,t,n=>e.moveVertically(n,t,tv(e).height))}const Dv=e=>Ev(e,!1),Ov=e=>Ev(e,!0),kv=e=>mv(e,!0,t=>av(e,t,!0)),Av=e=>mv(e,!1,t=>av(e,t,!1)),jv=e=>{let t=!Q(e);return mv(e,t,n=>av(e,n,t))},Mv=e=>{let t=Q(e);return mv(e,t,n=>av(e,n,t))},Nv=e=>mv(e,!1,t=>A.cursor(e.lineBlockAt(t.head).from)),Pv=e=>mv(e,!0,t=>A.cursor(e.lineBlockAt(t.head).to)),Fv=({state:e,dispatch:t})=>(t(z_(e,{anchor:0})),!0),Iv=({state:e,dispatch:t})=>(t(z_(e,{anchor:e.doc.length})),!0),Lv=({state:e,dispatch:t})=>(t(z_(e,{anchor:e.selection.main.anchor,head:0})),!0),Rv=({state:e,dispatch:t})=>(t(z_(e,{anchor:e.selection.main.anchor,head:e.doc.length})),!0),zv=({state:e,dispatch:t})=>(t(e.update({selection:{anchor:0,head:e.doc.length},userEvent:`select`})),!0),Bv=({state:e,dispatch:t})=>{let n=ay(e).map(({from:t,to:n})=>A.undirectionalRange(t,Math.min(n+1,e.doc.length)));return t(e.update({selection:A.create(n),userEvent:`select`})),!0},Vv=({state:e,dispatch:t})=>{let n=R_(e.selection,t=>{let n=J(e),r=n.resolveStack(t.from,1);if(t.empty){let e=n.resolveStack(t.from,-1);e.node.from>=r.node.from&&e.node.to<=r.node.to&&(r=e)}for(let e=r;e;e=e.next){let{node:n}=e;if((n.from<t.from&&n.to>=t.to||n.to>t.to&&n.from<=t.from)&&e.next)return A.undirectionalRange(n.from,n.to)}return t});return n.eq(e.selection)?!1:(t(z_(e,n)),!0)};function Hv(e,t){let{state:n}=e,r=n.selection,i=n.selection.ranges.slice();for(let r of n.selection.ranges){let a=n.doc.lineAt(r.head);if(t?a.to<e.state.doc.length:a.from>0)for(let n=r;;){let r=e.moveVertically(n,t);if(r.head<a.from||r.head>a.to){i.some(e=>e.head==r.head)||i.push(r);break}else if(r.head==n.head)break;else n=r}}return i.length==r.ranges.length?!1:(e.dispatch(z_(n,A.create(i,i.length-1))),!0)}const Uv=e=>Hv(e,!1),Wv=e=>Hv(e,!0),Gv=({state:e,dispatch:t})=>{let n=e.selection,r=null;return n.ranges.length>1?r=A.create([n.main]):n.main.empty||(r=A.create([A.cursor(n.main.head)])),r?(t(z_(e,r)),!0):!1};function Kv(e,t){if(e.state.readOnly)return!1;let n=`delete.selection`,{state:r}=e,i=r.changeByRange(r=>{let{from:i,to:a}=r;if(i==a){let o=t(r);o<i?(n=`delete.backward`,o=qv(e,o,!1)):o>i&&(n=`delete.forward`,o=qv(e,o,!0)),i=Math.min(i,o),a=Math.max(a,o)}else i=qv(e,i,!1),a=qv(e,a,!0);return i==a?{range:r}:{changes:{from:i,to:a},range:A.cursor(i,i<r.head?-1:1)}});return i.changes.empty?!1:(e.dispatch(r.update(i,{scrollIntoView:!0,userEvent:n,effects:n==`delete.selection`?V.announce.of(r.phrase(`Selection deleted`)):void 0})),!0)}function qv(e,t,n){if(e instanceof V)for(let r of e.state.facet(V.atomicRanges).map(t=>t(e)))r.between(t,t,(e,r)=>{e<t&&r>t&&(t=n?r:e)});return t}const Jv=(e,t,n)=>Kv(e,r=>{let i=r.from,{state:a}=e,o=a.doc.lineAt(i),s,c;if(n&&!t&&i>o.from&&i<o.from+200&&!/[^ \t]/.test(s=o.text.slice(0,i-o.from))){if(s[s.length-1]==` `)return i-1;let e=wr(s,a.tabSize)%Ju(a)||Ju(a);for(let t=0;t<e&&s[s.length-1-t]==` `;t++)i--;c=i}else c=$t(o.text,i-o.from,t,t)+o.from,c==i&&o.number!=(t?a.doc.lines:1)?c+=t?1:-1:!t&&/[\ufe00-\ufe0f]/.test(o.text.slice(c-o.from,i-o.from))&&(c=$t(o.text,c-o.from,!1,!1)+o.from);return c}),Yv=e=>Jv(e,!1,!0),Xv=e=>Jv(e,!0,!1),Zv=(e,t)=>Kv(e,n=>{let r=n.head,{state:i}=e,a=i.doc.lineAt(r),o=i.charCategorizer(r);for(let e=null;;){if(r==(t?a.to:a.from)){r==n.head&&a.number!=(t?i.doc.lines:1)&&(r+=t?1:-1);break}let s=$t(a.text,r-a.from,t)+a.from,c=a.text.slice(Math.min(r,s)-a.from,Math.max(r,s)-a.from),l=o(c);if(e!=null&&l!=e)break;(c!=` `||r!=n.head)&&(e=l),r=s}return r}),Qv=e=>Zv(e,!1),$v=e=>Zv(e,!0),ey=e=>Kv(e,t=>{let n=e.lineBlockAt(t.head).to;return t.head<n?n:Math.min(e.state.doc.length,t.head+1)}),ty=e=>Kv(e,t=>{let n=e.moveToLineBoundary(t,!1).head;return t.head>n?n:Math.max(0,t.head-1)}),ny=e=>Kv(e,t=>{let n=e.moveToLineBoundary(t,!0).head;return t.head<n?n:Math.min(e.state.doc.length,t.head+1)}),ry=({state:e,dispatch:t})=>{if(e.readOnly)return!1;let n=e.changeByRange(e=>({changes:{from:e.from,to:e.to,insert:k.of([``,``])},range:A.cursor(e.from)}));return t(e.update(n,{scrollIntoView:!0,userEvent:`input`})),!0},iy=({state:e,dispatch:t})=>{if(e.readOnly)return!1;let n=e.changeByRange(t=>{if(!t.empty||t.from==0||t.from==e.doc.length)return{range:t};let n=t.from,r=e.doc.lineAt(n),i=n==r.from?n-1:$t(r.text,n-r.from,!1)+r.from,a=n==r.to?n+1:$t(r.text,n-r.from,!0)+r.from;return{changes:{from:i,to:a,insert:e.doc.slice(n,a).append(e.doc.slice(i,n))},range:A.cursor(a)}});return n.changes.empty?!1:(t(e.update(n,{scrollIntoView:!0,userEvent:`move.character`})),!0)};function ay(e){let t=[],n=-1;for(let r of e.selection.ranges){let i=e.doc.lineAt(r.from),a=e.doc.lineAt(r.to);if(!r.empty&&r.to==a.from&&(a=e.doc.lineAt(r.to-1)),n>=i.number){let e=t[t.length-1];e.to=a.to,e.ranges.push(r)}else t.push({from:i.from,to:a.to,ranges:[r]});n=a.number+1}return t}function oy(e,t,n){if(e.readOnly)return!1;let r=[],i=[];for(let t of ay(e)){if(n?t.to==e.doc.length:t.from==0)continue;let a=e.doc.lineAt(n?t.to+1:t.from-1),o=a.length+1;if(n){r.push({from:t.to,to:a.to},{from:t.from,insert:a.text+e.lineBreak});for(let n of t.ranges)i.push(A.range(Math.min(e.doc.length,n.anchor+o),Math.min(e.doc.length,n.head+o)))}else{r.push({from:a.from,to:t.from},{from:t.to,insert:e.lineBreak+a.text});for(let e of t.ranges)i.push(A.range(e.anchor-o,e.head-o))}}return r.length?(t(e.update({changes:r,scrollIntoView:!0,selection:A.create(i,e.selection.mainIndex),userEvent:`move.line`})),!0):!1}const sy=({state:e,dispatch:t})=>oy(e,t,!1),cy=({state:e,dispatch:t})=>oy(e,t,!0);function ly(e,t,n){if(e.readOnly)return!1;let r=[];for(let t of ay(e))n?r.push({from:t.from,insert:e.doc.slice(t.from,t.to)+e.lineBreak}):r.push({from:t.to,insert:e.lineBreak+e.doc.slice(t.from,t.to)});let i=e.changes(r);return t(e.update({changes:i,selection:e.selection.map(i,n?1:-1),scrollIntoView:!0,userEvent:`input.copyline`})),!0}const uy=({state:e,dispatch:t})=>ly(e,t,!1),dy=({state:e,dispatch:t})=>ly(e,t,!0),fy=e=>{if(e.state.readOnly)return!1;let{state:t}=e,n=t.changes(ay(t).map(({from:e,to:n})=>(e>0?e--:n<t.doc.length&&n++,{from:e,to:n}))),r=R_(t.selection,t=>{let n;if(e.lineWrapping){let r=e.lineBlockAt(t.head),i=e.coordsAtPos(t.head,t.assoc||1);i&&(n=r.bottom+e.documentTop-i.bottom+e.defaultLineHeight/2)}return e.moveVertically(t,!0,n)}).map(n);return e.dispatch({changes:n,selection:r,scrollIntoView:!0,userEvent:`delete.line`}),!0};function py(e,t){if(/\(\)|\[\]|\{\}/.test(e.sliceDoc(t-1,t+1)))return{from:t,to:t};let n=J(e).resolveInner(t),r=n.childBefore(t),i=n.childAfter(t),a;return r&&i&&r.to<=t&&i.from>=t&&(a=r.type.prop(H.closedBy))&&a.indexOf(i.name)>-1&&e.doc.lineAt(r.to).from==e.doc.lineAt(i.from).from&&!/\S/.test(e.sliceDoc(r.to,i.from))?{from:r.to,to:i.from}:null}const my=gy(!1),hy=gy(!0);function gy(e){return({state:t,dispatch:n})=>{if(t.readOnly)return!1;let r=t.changeByRange(n=>{let{from:r,to:i}=n,a=t.doc.lineAt(r),o=!e&&r==i&&py(t,r);e&&(r=i=(i<=a.to?a:t.doc.lineAt(i)).to);let s=new Zu(t,{simulateBreak:r,simulateDoubleBreak:!!o}),c=Xu(s,r);for(c??=wr(/^\s*/.exec(t.doc.lineAt(r).text)[0],t.tabSize);i<a.to&&/\s/.test(a.text[i-a.from]);)i++;o?{from:r,to:i}=o:r>a.from&&r<a.from+100&&!/\S/.test(a.text.slice(0,r))&&(r=a.from);let l=[``,Yu(t,c)];return o&&l.push(Yu(t,s.lineIndent(a.from,-1))),{changes:{from:r,to:i,insert:k.of(l)},range:A.cursor(r+1+l[1].length)}});return n(t.update(r,{scrollIntoView:!0,userEvent:`input`})),!0}}function _y(e,t){let n=-1;return e.changeByRange(r=>{let i=[];for(let a=r.from;a<=r.to;){let o=e.doc.lineAt(a);o.number>n&&(r.empty||r.to>o.from)&&(t(o,i,r),n=o.number),a=o.to+1}let a=e.changes(i);return{changes:i,range:A.range(a.mapPos(r.anchor,1),a.mapPos(r.head,1))}})}const vy=({state:e,dispatch:t})=>{if(e.readOnly)return!1;let n=Object.create(null),r=new Zu(e,{overrideIndentation:e=>n[e]??-1}),i=_y(e,(t,i,a)=>{let o=Xu(r,t.from);if(o==null)return;/\S/.test(t.text)||(o=0);let s=/^\s*/.exec(t.text)[0],c=Yu(e,o);(s!=c||a.from<t.from+s.length)&&(n[t.from]=o,i.push({from:t.from,to:t.from+s.length,insert:c}))});return i.changes.empty||t(e.update(i,{userEvent:`indent`})),!0},yy=({state:e,dispatch:t})=>e.readOnly?!1:(t(e.update(_y(e,(t,n)=>{n.push({from:t.from,insert:e.facet(qu)})}),{userEvent:`input.indent`})),!0),by=({state:e,dispatch:t})=>e.readOnly?!1:(t(e.update(_y(e,(t,n)=>{let r=/^\s*/.exec(t.text)[0];if(!r)return;let i=wr(r,e.tabSize),a=0,o=Yu(e,Math.max(0,i-Ju(e)));for(;a<r.length&&a<o.length&&r.charCodeAt(a)==o.charCodeAt(a);)a++;n.push({from:t.from+a,to:t.from+r.length,insert:o.slice(a)})}),{userEvent:`delete.dedent`})),!0),xy=e=>(e.setTabFocusMode(),!0),Sy=[{key:`Ctrl-b`,run:U_,shift:gv,preventDefault:!0},{key:`Ctrl-f`,run:W_,shift:_v},{key:`Ctrl-p`,run:$_,shift:wv},{key:`Ctrl-n`,run:ev,shift:Tv},{key:`Ctrl-a`,run:uv,shift:Nv},{key:`Ctrl-e`,run:dv,shift:Pv},{key:`Ctrl-d`,run:Xv},{key:`Ctrl-h`,run:Yv},{key:`Ctrl-k`,run:ey},{key:`Ctrl-Alt-h`,run:Qv},{key:`Ctrl-o`,run:ry},{key:`Ctrl-t`,run:iy},{key:`Ctrl-v`,run:iv}],Cy=[{key:`ArrowLeft`,run:U_,shift:gv,preventDefault:!0},{key:`Mod-ArrowLeft`,mac:`Alt-ArrowLeft`,run:K_,shift:yv,preventDefault:!0},{mac:`Cmd-ArrowLeft`,run:cv,shift:jv,preventDefault:!0},{key:`ArrowRight`,run:W_,shift:_v,preventDefault:!0},{key:`Mod-ArrowRight`,mac:`Alt-ArrowRight`,run:q_,shift:bv,preventDefault:!0},{mac:`Cmd-ArrowRight`,run:lv,shift:Mv,preventDefault:!0},{key:`ArrowUp`,run:$_,shift:wv,preventDefault:!0},{mac:`Cmd-ArrowUp`,run:Fv,shift:Lv},{mac:`Ctrl-ArrowUp`,run:rv,shift:Dv},{key:`ArrowDown`,run:ev,shift:Tv,preventDefault:!0},{mac:`Cmd-ArrowDown`,run:Iv,shift:Rv},{mac:`Ctrl-ArrowDown`,run:iv,shift:Ov},{key:`PageUp`,run:rv,shift:Dv},{key:`PageDown`,run:iv,shift:Ov},{key:`Home`,run:sv,shift:Av,preventDefault:!0},{key:`Mod-Home`,run:Fv,shift:Lv},{key:`End`,run:ov,shift:kv,preventDefault:!0},{key:`Mod-End`,run:Iv,shift:Rv},{key:`Enter`,run:my,shift:my},{key:`Mod-a`,run:zv},{key:`Backspace`,run:Yv,shift:Yv,preventDefault:!0},{key:`Delete`,run:Xv,preventDefault:!0},{key:`Mod-Backspace`,mac:`Alt-Backspace`,run:Qv,preventDefault:!0},{key:`Mod-Delete`,mac:`Alt-Delete`,run:$v,preventDefault:!0},{mac:`Mod-Backspace`,run:ty,preventDefault:!0},{mac:`Mod-Delete`,run:ny,preventDefault:!0}].concat(Sy.map(e=>({mac:e.key,run:e.run,shift:e.shift}))),wy=[{key:`Alt-ArrowLeft`,mac:`Ctrl-ArrowLeft`,run:X_,shift:xv},{key:`Alt-ArrowRight`,mac:`Ctrl-ArrowRight`,run:Z_,shift:Sv},{key:`Alt-ArrowUp`,run:sy},{key:`Shift-Alt-ArrowUp`,run:uy},{key:`Alt-ArrowDown`,run:cy},{key:`Shift-Alt-ArrowDown`,run:dy},{key:`Mod-Alt-ArrowUp`,run:Uv},{key:`Mod-Alt-ArrowDown`,run:Wv},{key:`Escape`,run:Gv},{key:`Mod-Enter`,run:hy},{key:`Alt-l`,mac:`Ctrl-l`,run:Bv},{key:`Mod-i`,run:Vv,preventDefault:!0},{key:`Mod-[`,run:by},{key:`Mod-]`,run:yy},{key:`Mod-Alt-\\`,run:vy},{key:`Shift-Mod-k`,run:fy},{key:`Shift-Mod-\\`,run:pv},{key:`Mod-/`,run:i_},{key:`Alt-A`,mac:`Ctrl-A`,run:s_},{key:`Ctrl-m`,mac:`Shift-Alt-m`,run:xy}].concat(Cy),Ty=`var(--dsw-alias-brand-primary, #3e63dd)`,Ey=`color-mix(in srgb, var(--dsw-alias-brand-primary, #3e63dd) 55%, transparent)`,Dy=`var(--dsw-alias-label-dimmed, #999)`,Oy=`ui-monospace, "Cascadia Mono", Consolas, "SF Mono", Menlo, monospace`,ky=md.define([{tag:[q.heading1,q.heading2,q.heading3,q.heading4,q.heading5,q.heading6],color:Ty,fontWeight:`700`},{tag:q.processingInstruction,color:Ey},{tag:q.monospace,fontFamily:Oy,backgroundColor:`color-mix(in srgb, var(--dsw-alias-label-secondary, #888) 12%, transparent)`,borderRadius:`4px`,padding:`0 3px`},{tag:q.content,fontFamily:Oy,backgroundColor:`color-mix(in srgb, var(--dsw-alias-label-secondary, #888) 8%, transparent)`},{tag:q.strong,fontWeight:`700`},{tag:q.emphasis,fontStyle:`italic`},{tag:q.strikethrough,textDecoration:`line-through`,opacity:`.75`},{tag:q.link,color:Ty,textDecoration:`underline`},{tag:q.url,color:Ey,textDecoration:`underline dotted`},{tag:q.quote,fontStyle:`italic`,color:`var(--dsw-alias-label-secondary, #888)`},{tag:q.contentSeparator,color:Dy,textDecoration:`line-through`},{tag:q.comment,color:Dy}]);function Ay(e={}){let t=document.createElement(`div`);t.className=`dsh-tw-note-editor`;let n=new V({parent:t,state:N.create({doc:``,extensions:[V.lineWrapping,y_(),Qg({base:Rg}),vd(ky),el(),cl(e.placeholder??``),Rc.of([...wy,...L_,{key:`Mod-Enter`,run:()=>(e.onSave?.(),!0)}]),V.updateListener.of(t=>{t.docChanged&&e.onChange?.()})]})});return{el:t,view:n,getValue:()=>n.state.doc.toString(),setValue(e){n.dispatch({changes:{from:0,to:n.state.doc.length,insert:e}})},insertAtCaret(e){let{from:t}=n.state.selection.main,r=`${t===n.state.doc.lineAt(t).from?``:`
|
|
762
762
|
`}${e}\n`;n.dispatch({changes:{from:t,insert:r},selection:{anchor:t+r.length},scrollIntoView:!0}),n.focus()},focus:()=>n.focus()}}const jy=`dsh-tw-note-state`,My=`dsh-tw-note-draft-v1`,Ny=`dsh-tw-note-window-id`,Py=(()=>{try{let e=sessionStorage.getItem(Ny);if(e!==null&&e.length>0)return e;let t=Math.random().toString(36).slice(2,10);return sessionStorage.setItem(Ny,t),t}catch{return Math.random().toString(36).slice(2,10)}})();function Fy(){return`dsh-tw-note-draft-v1:${Py}`}function Iy(e){if(e===null)return null;try{let t=JSON.parse(e);return typeof t.text!=`string`||typeof t.title!=`string`?null:{text:t.text,title:t.title,tags:Array.isArray(t.tags)?t.tags.filter(e=>typeof e==`string`):[],savedAt:typeof t.savedAt==`number`?t.savedAt:0,windowId:typeof t.windowId==`string`?t.windowId:void 0}}catch{return null}}function Ly(e){try{return Iy(localStorage.getItem(e))}catch{return null}}function Ry(){let e=Ly(Fy());if(e!==null)return{draft:e,foreign:e.windowId!==void 0&&e.windowId!==Py};let t=Ly(My);if(t===null)return null;let n={...t,windowId:Py};By(n);try{localStorage.removeItem(My)}catch{}return{draft:n,foreign:!0}}function zy(e){By({...e,windowId:Py})}function By(e){try{localStorage.setItem(Fy(),JSON.stringify({...e,windowId:Py}))}catch{}}function Vy(){try{localStorage.removeItem(Fy())}catch{}}function Hy(e,t,n){return`${e}\u0000${t}\u0000${n.join(``)}`}function Uy(e){return e<10?`0${e}`:String(e)}function Wy(e=new Date){return`${e.getFullYear()}-${Uy(e.getMonth()+1)}-${Uy(e.getDate())} ${Uy(e.getHours())}:${Uy(e.getMinutes())}`}function Gy(e){if(e===null)return``;let t=new Date(e).getTime();if(Number.isNaN(t))return``;let n=Date.now()-t,r=Math.floor(n/6e4);if(r<1)return`刚刚`;if(r<60)return`${r}分钟前`;let i=Math.floor(r/60);if(i<24)return`${i}小时前`;let a=Math.floor(i/24);return a<30?`${a}天前`:new Date(t).toLocaleDateString(`zh-CN`)}async function Ky(){let e=await pe();return typeof e?.note?.tag==`string`&&e.note.tag.length>0?e.note.tag:`inbox`}function qy(e={}){let t=document.createElement(`div`);t.className=`dsh-tw-note-tags`;let n=document.createElement(`div`);n.className=`dsh-tw-note-chips`;let r=document.createElement(`input`);r.className=`dsh-tw-note-taginput`,r.placeholder=`tag(可多选,自动补全)`;let i=document.createElement(`div`);i.className=`dsh-tw-note-tagsuggest`,i.hidden=!0,t.append(n,r,i);let a=[],o=()=>{i.hidden=!0},s=()=>{e.onChange?.()},c=()=>{n.replaceChildren();for(let e of a){let t=document.createElement(`span`);t.className=`dsh-tw-note-tagchip`,t.textContent=e;let r=document.createElement(`span`);r.className=`dsh-tw-note-tagchip-x`,r.textContent=`×`,r.title=`移除 tag「${e}」`,r.addEventListener(`click`,t=>{t.stopPropagation();let n=a.indexOf(e);n>=0&&(a.splice(n,1),c(),s())}),t.append(r),n.append(t)}},l=e=>{let t=e.trim();t.length===0||a.includes(t)||(a.push(t),r.value=``,c(),o(),r.focus(),s())},u=()=>{for(let e of r.value.split(/\s+/))l(e)},d=[],f,p=()=>(f??=fetch(ee,{signal:AbortSignal.timeout(5e3)}).then(e=>e.ok?e.json():Promise.resolve({})).then(e=>[...e.tags??[]].sort((e,t)=>e.localeCompare(t,`zh`))).catch(()=>[]),f.then(e=>{d=e}),f),m=()=>{let e=r.value.trim().toLowerCase(),t=d.filter(t=>!a.includes(t)&&(e.length===0||t.toLowerCase().includes(e))).slice(0,8);i.replaceChildren();for(let e of t){let t=document.createElement(`div`);t.className=`dsh-tw-note-tagsuggest-item`,t.textContent=e,t.setAttribute(`role`,`button`),t.tabIndex=0,t.setAttribute(`aria-label`,`添加标签「${e}」`),t.addEventListener(`mousedown`,t=>{t.preventDefault(),l(e)}),t.addEventListener(`keydown`,t=>{(t.key===`Enter`||t.key===` `)&&(t.preventDefault(),l(e))}),i.append(t)}i.hidden=t.length===0};r.addEventListener(`focus`,()=>{p().then(m)}),r.addEventListener(`input`,()=>{d.length===0?p().then(m):m()}),r.addEventListener(`keydown`,e=>{e.key===`Enter`||e.key===`,`?(e.preventDefault(),u()):e.key===`Backspace`&&r.value.length===0&&a.length>0?(a.pop(),c(),s()):e.key===`Escape`&&o()});let h=e=>{t.contains(e.target)||o()};return document.addEventListener(`click`,h,!0),{el:t,getTags:()=>(u(),[...a]),peekTags:()=>[...a],setDefault:e=>{if(a.length===0){let t=e.trim();t.length>0&&(a.push(t),c())}},setTags:e=>{a.length=0;for(let t of Array.isArray(e)?e:[]){let e=typeof t==`string`?t.trim():``;e.length>0&&!a.includes(e)&&a.push(e)}r.value=``,c(),o()},dispose:()=>document.removeEventListener(`click`,h,!0)}}async function Jy(e,t){if(e.size>67108864){D(`文件过大(≤ 64MB)`);return}try{let n=await fetch(`${x}?name=${encodeURIComponent(e.name)}`,{method:`POST`,headers:{"content-type":e.type||`application/octet-stream`},body:e,signal:AbortSignal.timeout(12e4)}),r=await n.json().catch(()=>null);if(!n.ok||r?.ok!==!0){D(`上传失败:${r?.error??`HTTP ${n.status}`}`);return}let i=r.name??e.name,a=e.type.startsWith(`image/`)?``:`[${i}](${r.url})`;t.insertAtCaret(a),D(`已上传「${i}」并插入链接`)}catch(e){D(`上传失败:${e instanceof Error?e.message:String(e)}`)}}function Yy(){let e,t=!1,n=!1,r=!1,i,a=`inbox`,o,s=!1,c=null,l=null,u=e=>{try{window.dispatchEvent(new CustomEvent(jy,{detail:{open:e}}))}catch{}},d=t=>{if(e===void 0)return;let n=e.root,r=e.card;if(t!==void 0){let e=t.getBoundingClientRect(),i=r.offsetWidth||340,a=r.offsetHeight||260,o=window.innerWidth,s=window.innerHeight,c=Math.min(Math.max(e.left+e.width/2-i/2,8),Math.max(8,o-i-8)),l=s-e.top+10,u=Math.min(Math.max(8,l),Math.max(8,s-a-8));n.style.right=`auto`,n.style.top=`auto`,n.style.left=`${c}px`,n.style.bottom=`${u}px`}else n.style.right=`24px`,n.style.bottom=`88px`,n.style.left=`auto`,n.style.top=`auto`},f=null,p=()=>{e!==void 0&&(f=Wy(),e.titleInput.value=f)},m=()=>{if(e===void 0)return;let t=e.editor.getValue(),n=e.titleInput.value.trim();if(t.trim().length===0&&(n.length===0||n===f)){Vy();return}let r=Hy(n,t,e.tagEditor.peekTags());l!==null&&r===l||By({text:t,title:n,tags:e.tagEditor.peekTags(),savedAt:Date.now()})},h=()=>{t||!n||e===void 0||(o!==void 0&&(clearTimeout(o),o=void 0),o=window.setTimeout(()=>{o=void 0,m()},500))},_=()=>{e!==void 0&&(e.draftBanner.hidden=!0)},ee=async(e,t,n)=>{try{let r=await fetch(y,{method:`POST`,headers:{"content-type":`application/json`},body:JSON.stringify({title:e,tags:n,text:t}),signal:AbortSignal.timeout(1e4)}),i=await r.json().catch(()=>null);return!r.ok||i?.ok!==!0?(D(`打开失败:${i?.error??`HTTP ${r.status}`}`),!1):typeof i.twUrl!=`string`||typeof i.draftTitle!=`string`?(D(`打开失败:服务未返回编辑器地址`),!1):(Vy(),_(),l=Hy(e,t,n),c=null,wt(`${new URL(i.twUrl,location.origin).href}#${encodeURIComponent(i.draftTitle)}`,i.title??e),D(`已在弹出窗口打开「${i.title??e}」编辑器`),!0)}catch(e){return D(`打开失败:${e instanceof Error?e.message:String(e)}`),!1}},b=()=>{s=!1,e!==void 0&&(e.recentWrap.hidden=!0)},x=async t=>{try{let n=await fetch(`${g}?title=${encodeURIComponent(t)}`,{signal:AbortSignal.timeout(1e4)}),r=await n.json().catch(()=>null);if(!n.ok||r?.ok!==!0||typeof r.title!=`string`){D(`读取失败:${r?.notFound===!0?`不存在`:r?.error??`HTTP ${n.status}`}`);return}if(e===void 0)return;e.editor.setValue(r.text??``),e.titleInput.value=r.title,e.tagEditor.setTags(r.tags??[]),c={title:r.title,...typeof r.modified==`string`&&r.modified.length>0?{modified:r.modified}:{},...typeof r.revision==`number`?{revision:r.revision}:{}},l=Hy(r.title,r.text??``,r.tags??[]),_(),b(),D(`已载入「${r.title}」`),e.editor.focus()}catch(e){D(`读取失败:${e instanceof Error?e.message:String(e)}`)}},S=()=>{if(e===void 0)return;if(s){b();return}s=!0,e.recentWrap.hidden=!1,e.recentWrap.textContent=``;let t=document.createElement(`div`);t.className=`dsh-tw-note-recent-muted`,t.textContent=`加载中…`,e.recentWrap.append(t),(async()=>{try{let t=await(await fetch(`${te}?limit=15`,{signal:AbortSignal.timeout(1e4)})).json().catch(()=>null);if(!s||e===void 0)return;e.recentWrap.replaceChildren();let n=[];if(t?.ok===!0)for(let e of t.items??[]){if(typeof e!=`object`||!e)continue;let t=e,r=typeof t.title==`string`?t.title:``;r.length!==0&&n.push({title:r,tags:Array.isArray(t.tags)?t.tags.filter(e=>typeof e==`string`):[],modified:typeof t.modified==`string`?t.modified:null,snippet:typeof t.snippet==`string`?t.snippet:``})}if(n.length===0){let n=document.createElement(`div`);n.className=`dsh-tw-note-recent-muted`,n.textContent=t?.ok===!0?`暂无笔记`:`加载失败:${t?.error??`未知`}`,e.recentWrap.append(n);return}for(let t of n){let n=document.createElement(`div`);n.className=`dsh-tw-note-recent-item`,n.title=t.snippet||t.title,n.setAttribute(`role`,`button`),n.tabIndex=0,n.setAttribute(`aria-label`,`载入笔记「${t.title}」`);let r=document.createElement(`span`);r.className=`dsh-tw-note-recent-name`,r.textContent=t.title;let i=document.createElement(`span`);i.className=`dsh-tw-note-recent-meta`;let a=[Gy(t.modified)];t.tags.length>0&&a.unshift(t.tags.slice(0,3).join(`,`)),i.textContent=a.join(` · `),n.append(r,i),n.addEventListener(`click`,()=>{x(t.title)}),n.addEventListener(`keydown`,e=>{(e.key===`Enter`||e.key===` `)&&(e.preventDefault(),x(t.title))}),e.recentWrap.append(n)}}catch(t){if(!s||e===void 0)return;e.recentWrap.replaceChildren();let n=document.createElement(`div`);n.className=`dsh-tw-note-recent-muted`,n.textContent=`加载失败:${t instanceof Error?t.message:String(t)}`,e.recentWrap.append(n)}})()},C=()=>{if(e!==void 0)return;let t=document.createElement(`div`);t.className=`dsh-tw-note`,t.hidden=!0;let r=document.createElement(`div`);r.className=`dsh-tw-note-card`;let a=document.createElement(`div`);a.className=`dsh-tw-note-head`;let s=document.createElement(`span`);s.className=`dsh-tw-note-label`,s.textContent=`📝 快速笔记`;let d=document.createElement(`button`);d.type=`button`,d.className=`dsh-tw-note-close`,d.title=`收起`,d.textContent=`✕`,a.append(s,d);let p=document.createElement(`div`);p.className=`dsh-tw-note-draft`,p.hidden=!0;let g=document.createElement(`span`);g.className=`dsh-tw-note-draft-text`,g.textContent=`已恢复未保存草稿`;let y=document.createElement(`button`);y.type=`button`,y.className=`dsh-tw-note-draft-discard`,y.textContent=`丢弃`,p.append(g,y);let te=document.createElement(`div`);te.className=`dsh-tw-note-fields`;let x=document.createElement(`input`);x.className=`dsh-tw-note-title`,x.placeholder=`标题(默认时间戳)`,x.setAttribute(`aria-label`,`笔记标题(默认时间戳)`);let C=qy({onChange:h});te.append(x,C.el),x.addEventListener(`input`,h);let w,T=Ay({placeholder:`写点东西… Markdown 高亮,可 📎/拖入文件
|
|
763
763
|
Ctrl+Enter 保存`,onSave:()=>{w?.()},onChange:h}),ne=document.createElement(`button`);ne.type=`button`,ne.className=`dsh-tw-note-upload`,ne.title=`上传文件到 wiki 并插入 Markdown 链接(也可直接拖入编辑器)`,ne.textContent=`📎 上传`;let E=document.createElement(`input`);E.type=`file`,E.multiple=!0,E.hidden=!0,ne.addEventListener(`click`,()=>{E.click()}),E.addEventListener(`change`,()=>{for(let e of Array.from(E.files??[]))Jy(e,T);E.value=``});let re=0;T.el.addEventListener(`dragenter`,e=>{e.preventDefault(),re++,T.el.classList.add(`dsh-tw-note-drop`)}),T.el.addEventListener(`dragover`,e=>{e.preventDefault()}),T.el.addEventListener(`dragleave`,e=>{e.preventDefault(),re=Math.max(0,re-1),re===0&&T.el.classList.remove(`dsh-tw-note-drop`)}),T.el.addEventListener(`drop`,e=>{e.preventDefault(),re=0,T.el.classList.remove(`dsh-tw-note-drop`);let t=e.dataTransfer?.files;if(!(t===void 0||t.length===0))for(let e of Array.from(t))Jy(e,T)});let ie=document.createElement(`div`);ie.className=`dsh-tw-note-foot`;let ae=document.createElement(`div`);ae.className=`dsh-tw-note-foot-left`;let oe=document.createElement(`span`);oe.className=`dsh-tw-note-hint`,oe.textContent=`Ctrl+Enter`;let se=document.createElement(`button`);se.type=`button`,se.className=`dsh-tw-note-recent-btn`,se.title=`最近修改的笔记,点击载入继续编辑`,se.textContent=`🕘 最近`,se.addEventListener(`click`,S),ae.append(ne,se,oe);let ce=document.createElement(`div`);ce.className=`dsh-tw-note-foot-right`;let le=document.createElement(`button`);le.type=`button`,le.className=`dsh-tw-note-edit`,le.title=`保存并在 TiddlyWiki 原生编辑器中打开`,le.textContent=`✏️ 在 TW 中编辑`;let ue=document.createElement(`button`);ue.type=`button`,ue.className=`dsh-tw-note-save`,ue.textContent=`保存`,ce.append(le,ue),ie.append(ae,ce);let de=document.createElement(`div`);de.className=`dsh-tw-note-recent`,de.hidden=!0,r.append(a,p,te,T.el,ie),t.append(r,de),document.body.append(t),e={root:t,card:r,editor:T,titleInput:x,tagEditor:C,saveBtn:ue,editBtn:le,draftBanner:p,bannerText:g,recentWrap:de};let fe=()=>{n=!1,t.hidden=!0,b(),u(!1)},pe=()=>{o!==void 0&&(clearTimeout(o),o=void 0),T.setValue(``),x.value=Wy(),f=x.value,C.setTags([]),l=Hy(x.value.trim(),``,[])},me=()=>{Vy(),_(),l=Hy(x.value.trim(),T.getValue(),C.getTags()),c=null,pe(),fe()},he=!1;w=async()=>{if(he)return;let e=T.getValue().trim();if(e.length===0){D(`内容为空,未保存`);return}he=!0,ue.disabled=!0,ue.textContent=`保存中…`;try{let t=x.value.trim(),n={title:t,tags:C.getTags(),text:e};c!==null&&c.title===t&&(c.modified!==void 0&&(n.expectedModified=c.modified),c.revision!==void 0&&(n.expectedRevision=c.revision));let r=await fetch(v,{method:`POST`,headers:{"content-type":`application/json`},body:JSON.stringify(n),signal:AbortSignal.timeout(1e4)}),i=await r.json().catch(()=>null);if(!r.ok||i?.ok!==!0){if(r.status===409||i?.conflict===!0){D(`保存被拒绝:这篇笔记在你读取之后被改动过。请用「🕘 最近」重新载入后再保存。`);return}D(`保存失败:${i?.error??`HTTP ${r.status}`}`);return}me(),D(`已保存「${i.title??x.value}」`)}catch(e){D(`保存失败:${e instanceof Error?e.message:String(e)}`)}finally{he=!1,ue.disabled=!1,ue.textContent=`保存`}},ue.addEventListener(`click`,()=>{w?.()});let ge=async()=>{let e=x.value.trim().length>0?x.value.trim():Wy(),t=T.getValue(),n=C.getTags();le.disabled=!0,le.textContent=`打开中…`;try{await ee(e,t,n)}finally{le.disabled=!1,le.textContent=`✏️ 在 TW 中编辑`}};le.addEventListener(`click`,()=>{ge()}),d.addEventListener(`click`,fe);let _e=null,ve=(e,n)=>{let i=window.innerWidth,a=window.innerHeight,o=r.offsetWidth||340,s=r.offsetHeight||260,c=Math.min(Math.max(e,8),Math.max(8,i-o-8)),l=Math.min(Math.max(n,8),Math.max(8,a-s-8));t.style.right=`auto`,t.style.bottom=`auto`,t.style.left=`${c}px`,t.style.top=`${l}px`};a.addEventListener(`pointerdown`,e=>{if(d.contains(e.target))return;e.preventDefault();let n=t.getBoundingClientRect();_e={startX:e.clientX,startY:e.clientY,origLeft:n.left,origTop:n.top,moved:!1};try{a.setPointerCapture(e.pointerId)}catch{}a.classList.add(`dsh-tw-note-head-dragging`)}),a.addEventListener(`pointermove`,e=>{if(_e===null)return;let t=e.clientX-_e.startX,n=e.clientY-_e.startY;!_e.moved&&Math.abs(t)<4&&Math.abs(n)<4||(_e.moved=!0,ve(_e.origLeft+t,_e.origTop+n))});let ye=e=>{if(_e!==null){try{a.releasePointerCapture(e.pointerId)}catch{}_e=null,a.classList.remove(`dsh-tw-note-head-dragging`)}};a.addEventListener(`pointerup`,ye),a.addEventListener(`pointercancel`,ye),y.addEventListener(`click`,()=>{Vy(),_(),pe(),D(`已丢弃草稿`)}),i=()=>{m()},window.addEventListener(`pagehide`,i)};return{async open(r){if(t||(C(),e===void 0||n))return;n=!0,e.root.hidden=!1,d(r),u(!0);let i=Ry(),o=i?.draft??null;if(o!==null&&(o.text.trim().length>0||o.title.trim().length>0))e.editor.setValue(o.text),e.titleInput.value=o.title,e.tagEditor.setTags(o.tags),e.bannerText.textContent=i?.foreign===!0?`已恢复未保存草稿(来自其它窗口)`:`已恢复未保存草稿`,e.draftBanner.hidden=!1,i?.foreign===!0&&(D(`已恢复其它窗口的未保存草稿`),zy(o));else{if(p(),a=await Ky(),t||e===void 0)return;e.tagEditor.setDefault(a)}e!==void 0&&e.editor.focus()},close(){e!==void 0&&(n=!1,e.root.hidden=!0,b(),u(!1))},async openNative(){if(!(t||r)&&!(Tt()&&!Et())){r=!0;try{let e=Ry(),n=e!==null&&(e.draft.text.trim().length>0||e.draft.title.trim().length>0),r=n&&e.draft.title.trim().length>0?e.draft.title.trim():Wy(),i=n?e.draft.text:``,o=[];if(n)o=e.draft.tags;else{if(a=await Ky(),t)return;o=[a]}e!==null&&e.foreign&&(e.draft.text.trim().length>0||e.draft.title.trim().length>0)&&(D(`已恢复其它窗口的未保存草稿`),zy(e.draft)),await ee(r,i,o)}finally{r=!1}}},isOpen(){return n},dispose(){t=!0,i!==void 0&&(window.removeEventListener(`pagehide`,i),i=void 0),o!==void 0&&(clearTimeout(o),o=void 0),m(),e?.tagEditor.dispose(),e?.editor.view.destroy(),e?.root.remove(),e=void 0}}}function Xy(e){return e<10?`0${e}`:String(e)}function Zy(e){return`${Xy(e.getHours())}:${Xy(e.getMinutes())}`}function Qy(e,t){let n=e?.git,r=[`同步知识库`],i=`offline`;if(n==null||n.exists!==!0)r.push(`git 仓库不可用`);else{r.push(`分支 ${n.branch??`?`}`),typeof n.lastCommit==`string`&&r.push(n.lastCommit);let e=typeof n.behind==`number`?n.behind:0,t=typeof n.ahead==`number`?n.ahead:0;t>0&&r.push(`领先 ${t}`),e>0&&r.push(`落后 ${e}`),n.dirty===!0?(i=`dirty`,r.push(`有 ${n.dirtyFiles?.length??0} 个未提交改动`)):i=e>0?`behind`:`clean`}return t!==void 0&&r.push(`上次同步 ${Zy(t)}`),{state:i,tooltip:r.join(` · `)}}function $y(){let e={state:`offline`,tooltip:`同步知识库`},t,n,r=!1,i=!1,a=new Set,o=()=>{for(let e of[...a])e()},s=n=>{if(r)return;let i=Qy(n,t);(i.state!==e.state||i.tooltip!==e.tooltip)&&(e=i,o())},c=async()=>{if(i)return;let e=await pe();i||s(e)};return c(),n=window.setInterval(()=>{c()},3e4),{getState:()=>e,trigger:async()=>{if(i)return e;if(r)return D(`同步进行中…`),e;r=!0,e={...e,state:`syncing`},o();try{let e=await fetch(S,{method:`POST`,signal:AbortSignal.timeout(12e4)}),n=de(await e.json().catch(()=>null),e.status);n.ok?(t=new Date,D(`同步完成:${n.message}`)):D(`同步失败:${n.message}`)}catch(e){D(`同步失败:${e instanceof Error?e.message:String(e)}`)}finally{r=!1,await c()}return e},subscribe(e){return a.add(e),()=>{a.delete(e)}},dispose(){i=!0,n!==void 0&&(clearInterval(n),n=void 0),a.clear()}}}async function eb(){let e=await be();return{showQuickNote:e.showQuickNote,showPanelStatus:e.showPanelStatus,showSyncButton:e.showSyncButton}}async function tb(){let e=await pe();if(e===null)return{state:`failed`,text:`状态不可达`,logs:[]};let t=Array.isArray(e.logs)?e.logs.filter(e=>typeof e==`string`):[];return e.status===`running`?{state:`running`,text:`TW 在线 · ${e.url??``}`,logs:t}:e.status===`starting`?{state:`starting`,text:`TW 启动中…`,logs:t}:{state:`failed`,text:e.error??`TW 状态:${e.status??`?`}`,logs:t}}function nb(e,t,n){let r=!1,i,a,o,s,c,l,u,d,f=!1,p={state:`unknown`,text:`TiddlyWiki 服务…`,logs:[]},m=()=>{f=!1,s!==void 0&&(s.hidden=!0),u!==void 0&&(u.hidden=!0)},h=e=>{if(!f)return;let t=e.target;i!==void 0&&i.contains(t)||m()},g=()=>{if(u===void 0)return;let e=[`TiddlyWiki 服务:${p.text}`];e.push(`知识库同步:${n.getState().tooltip}`),p.logs.length>0&&(e.push(`最近日志:`),e.push(...p.logs.slice(-3))),u.textContent=e.join(`
|
|
764
|
-
`)},_=()=>{let e=n.getState();o!==void 0&&(o.dataset.state=e.state),a!==void 0&&(a.title=`知识库 · ${e.tooltip}`),g()},v=async()=>{p=await tb(),!r&&(c!==void 0&&(c.textContent=p.text),l!==void 0&&(l.dataset.state=p.state),g())},y=p=>{if(r)return;if(i=document.createElement(`div`),i.className=`dsh-tw-fab-wrap`,s=document.createElement(`div`),s.className=`dsh-tw-fab-menu`,s.hidden=!0,p.showPanelStatus){let e=document.createElement(`div`);e.className=`dsh-tw-fab-status dsh-tw-fab-status-tiprow`,l=document.createElement(`span`),l.className=`dsh-tw-fab-status-dot`,l.dataset.state=`unknown`,c=document.createElement(`span`),c.className=`dsh-tw-fab-status-text`,c.textContent=`TiddlyWiki 服务…`,e.append(l,c),u=document.createElement(`div`),u.className=`dsh-tw-fab-tip`,u.hidden=!0,e.append(u),e.addEventListener(`mouseenter`,()=>{g(),u!==void 0&&(u.hidden=!1)}),e.addEventListener(`mouseleave`,()=>{u!==void 0&&(u.hidden=!0)}),s.append(e)}if(p.showQuickNote){let e=document.createElement(`button`);e.type=`button`,e.className=`dsh-tw-fab-item`,e.textContent=`📝 快速笔记`,e.title=`ui.quickNoteMode=native 时直接打开 TW 原生编辑器;card 时打开 Markdown 卡片`,e.addEventListener(`click`,()=>{m(),be().then(e=>{e.quickNoteMode===`native`?t.openNative():t.open()})}),s.append(e)}if(p.showPanelStatus){d=document.createElement(`span`),d.textContent=`打开 TW 面板`;let t=document.createElement(`button`);t.type=`button`,t.className=`dsh-tw-fab-item`,t.append(document.createTextNode(`🖥 `),d),t.addEventListener(`click`,()=>{m(),e.toggle()}),s.append(t);let n=document.createElement(`button`);n.type=`button`,n.className=`dsh-tw-fab-item`,n.textContent=`🔄 重载 TW 面板`,n.addEventListener(`click`,()=>{m(),document.dispatchEvent(new CustomEvent(qe)),v()}),s.append(n)}if(p.showSyncButton){let e=document.createElement(`button`);e.type=`button`,e.className=`dsh-tw-fab-item`,e.textContent=`🔁 同步`,e.addEventListener(`click`,()=>{m(),n.trigger()}),s.append(e)}a=document.createElement(`button`),a.type=`button`,a.className=`dsh-tw-fab`,a.setAttribute(`aria-label`,`TiddlyWiki 知识库`);let y=document.createElement(`span`);y.className=`dsh-tw-fab-icon`,y.innerHTML=`<svg viewBox="0 0 16 16" width="17" height="17" fill="none" stroke="currentColor" stroke-width="1.3" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4 2.5h8a1 1 0 0 1 1 1v9a1 1 0 0 1-1 1H4a1 1 0 0 1-1-1v-9a1 1 0 0 1 1-1z"/><path d="M6 6h4M6 8.5h2.5"/></svg>`,o=document.createElement(`span`),o.className=`dsh-tw-fab-dot`,o.dataset.state=n.getState().state,a.append(y,o),a.addEventListener(`click`,()=>{if(s!==void 0){if(f){m();return}f=!0,s.hidden=!1,_(),v()}}),i.append(s,a),document.body.append(i),_(),document.addEventListener(`click`,h,!0)},ee=e.subscribe(()=>{d!==void 0&&(d.textContent=e.isOpen()?`收起 TW 面板`:`打开 TW 面板`)}),te=n.subscribe(_);return(async()=>{let e=await eb();r||!e.showQuickNote&&!e.showPanelStatus&&!e.showSyncButton||y(e)})(),()=>{r=!0,document.removeEventListener(`click`,h,!0),ee(),te(),i?.remove()}}function rb(e){return function(){let[t,n]=c.useState(!1),[r,i]=c.useState(null),a=c.useRef(null),o=c.useRef(null);c.useEffect(()=>{let e=!0,t=e=>{let t=e.detail;n(t?.open===!0)};return window.addEventListener(jy,t),be().then(t=>{e&&i(t.quickNoteMode)}),()=>{e=!1,window.removeEventListener(jy,t)}},[]),c.useLayoutEffect(()=>{let e=a.current;if(e===null)return;let t=()=>{let t=null,n=null,r=e;for(;r!==null&&r!==document.documentElement&&(r=r.parentElement,r!==null);){for(let e of r.querySelectorAll(`textarea, [contenteditable]:not([contenteditable="false"]), [role="textbox"]`))if(e.getBoundingClientRect().width>300){t=e,n=r;break}if(n!==null)break}if(t===null||n===null)return null;let i=n.getBoundingClientRect().width,a=t,o=null,s=0;for(;a!==null&&a!==n;){let e=a.getBoundingClientRect().width;e>0&&e<i-8&&e>=s&&(s=e,o=a),a=a.parentElement}return o},n=()=>{let n=e.getBoundingClientRect(),r=t(),i=r===null?n.right:r.getBoundingClientRect().right,a=Math.max(0,n.right-i);return e.style.paddingRight!==`${a}px`&&(e.style.paddingRight=`${a}px`),r!==null};n();let r=new ResizeObserver(n),i=e;for(;i!==null&&i!==document.documentElement&&(r.observe(i),!(i instanceof HTMLElement&&i.hasAttribute(`data-phase`)));)i=i.parentElement;window.addEventListener(`resize`,n),document.addEventListener(`visibilitychange`,n);let o=0,s=0,c=window.setInterval(()=>{if(s++,!n()){s>=20&&window.clearInterval(c);return}o++,o>=3&&window.clearInterval(c)},1500),l=window.setTimeout(n,120),u=window.setTimeout(n,600);return()=>{r.disconnect(),window.removeEventListener(`resize`,n),document.removeEventListener(`visibilitychange`,n),window.clearInterval(c),window.clearTimeout(l),window.clearTimeout(u)}},[]);let s=r===`native`,l=s?`快速笔记`:t?`快速笔记(已打开)`:`快速笔记`,u=s?`快速笔记:直接打开 TiddlyWiki 原生编辑器(再次点击可收起弹窗)`:t?`快速笔记已打开(点此按钮或卡片右上角 ✕ 收起)`:`打开快速笔记(在按钮上方弹出,可拖动标题栏移动)`;return c.createElement(`div`,{ref:a,className:`dsh-tw-dock-note`},c.createElement(`button`,{ref:o,type:`button`,className:t?`dsh-tw-dock-note-btn dsh-tw-dock-note-btn-active`:`dsh-tw-dock-note-btn`,title:u,onClick:()=>{be().then(t=>{t.quickNoteMode===`native`?Tt()&&!Et()?Ot():e.openNative():e.isOpen()?e.close():e.open(o.current??void 0)})}},c.createElement(`span`,{className:`dsh-tw-dock-note-icon`,"aria-hidden":`true`},`📝`),c.createElement(`span`,{className:`dsh-tw-dock-note-label`},l)))}}async function ib(e,t=15e3){try{let n=await fetch(_,{method:`POST`,headers:{"content-type":`application/json`,"x-requested-with":`TiddlyWiki`},body:JSON.stringify({title:e}),signal:AbortSignal.timeout(t)});if(!n.ok)return null;let r=await n.text();return r.trim().length>0?r:null}catch{return null}}function ab(){return Xe()}async function ob(e){try{let t=await fetch(`${g}?title=${encodeURIComponent(e)}`,{signal:AbortSignal.timeout(8e3)});return t.status!==404&&(await t.json().catch(()=>null))?.notFound!==!0}catch{return!0}}function sb(e){let{sessionId:t,viewRequest:n,completeViewRequest:r}=e,[i,a]=c.useState(`loading`),[o,s]=c.useState(null),[l,u]=c.useState(null),[d,f]=c.useState(``),p=c.useRef(0),m=c.useRef(!0),h=c.useRef(0),g=c.useCallback(async()=>{let e=++p.current;if(m.current){if(a(`loading`),typeof t!=`string`||t.length===0){a(`error`),f(`缺少会话 ID`);return}if(h.current>=3){a(`error`),f(`连续 3 次生成失败,已停止自动重试,请点「🔄 刷新」`);return}try{let n=await fetch(w,{method:`POST`,headers:{"content-type":`application/json`},body:JSON.stringify({session:t}),signal:AbortSignal.timeout(25e3)}),r=await n.json().catch(()=>null);if(!m.current||e!==p.current)return;if(!n.ok||r?.ok!==!0){h.current++,a(`error`),f(r?.error??`HTTP ${n.status}`);return}if(typeof r.title!=`string`||r.title.length===0){h.current++,a(`error`),f(`汇总生成失败:缺少标题`);return}u(r.title);let i=await ib(r.title);if(!m.current||e!==p.current)return;if(i===null){let t=await ob(r.title);if(!m.current||e!==p.current)return;if(!t){if(h.current++,h.current<3){g();return}a(`error`),f(`汇总条目不存在,自动重建失败,请重试`);return}h.current++,a(`error`),f(`渲染失败:wiki 渲染服务不可用(host /render)`);return}h.current=0,s(i),a(`ready`)}catch(t){if(!m.current||e!==p.current)return;h.current++,a(`error`),f(t instanceof Error?t.message:String(t))}}},[t]);c.useEffect(()=>(m.current=!0,g(),()=>{m.current=!1}),[g]),c.useEffect(()=>{if(i!==`ready`||l===null)return;let e=!0,t=window.setInterval(()=>{(async()=>{if(!e)return;let t=await ob(l);if(e)if(t)h.current=0;else{if(h.current++,h.current<3){g();return}a(`error`),f(`汇总条目已失效且自动重建多次失败,请点「🔄 刷新」重试`)}})()},3e4);return()=>{e=!1,window.clearInterval(t)}},[i,l,g]),c.useEffect(()=>{n!=null&&r?.()},[n,r]);let _=()=>{h.current=0,g()},v;return v=i===`loading`?c.createElement(`div`,{className:`dsh-tw-summary-state`},c.createElement(`div`,{className:`dsh-tw-summary-state-spin`},`⏳`),c.createElement(`div`,null,`正在生成会话 wiki 汇总…`)):i===`error`?c.createElement(`div`,{className:`dsh-tw-summary-state`},c.createElement(`div`,{className:`dsh-tw-summary-state-title`},`知识库汇总暂不可用`),c.createElement(`div`,{className:`dsh-tw-summary-state-detail`},d),c.createElement(`button`,{type:`button`,className:`dsh-tw-summary-btn`,onClick:_},`重试`)):c.createElement(`div`,{className:`dsh-tw-summary-native dsh-tw-toolcard-native`,dangerouslySetInnerHTML:{__html:o??``}}),c.createElement(`div`,{className:`dsh-tw-summary`,"data-dsh-tw-summary":`true`},c.createElement(`div`,{className:`dsh-tw-summary-bar`},c.createElement(`span`,{className:`dsh-tw-summary-bar-title`},`📚 本会话知识库`),c.createElement(`span`,{className:`dsh-tw-summary-bar-hint`},`产生 / 读取 / 检索过的笔记(含子代理)`),c.createElement(`span`,{className:`dsh-tw-summary-bar-spacer`}),c.createElement(`button`,{type:`button`,className:`dsh-tw-summary-btn`,onClick:_,disabled:i===`loading`,title:`重新生成并刷新汇总`},`🔄 刷新`)),v)}function cb(e,t){if(!(e===void 0||t.showSessionTab===!1))return Ye(t.tabLabel),e.inject(`conversation.view`,()=>e.register({name:`conversation.view`,id:`dsh-tiddlywiki-summary`,order:20,label:ab},sb))}function $(e,t,n){let r=document.createElement(e);return t!==void 0&&(r.className=t),n!==void 0&&(r.textContent=n),r}async function lb(e,t){let n=await fetch(e,{...t,signal:t?.signal??AbortSignal.timeout(15e3)}),r=await n.json().catch(()=>({}));if(!n.ok){let e=r.error??`HTTP ${n.status}`;throw Error(e)}return r}function ub(e){let t=!1;e.classList.add(`dsh-tw-settings`);let n=$(`div`,`dsh-tw-settings-row dsh-tw-settings-status`),r=$(`div`,`dsh-tw-settings-body`);e.append(n,r);let i=[],a={},o=async()=>{try{let e=await lb(T);if(t)return;db(n,e,o),gb(r,e,o,()=>t,a)}catch(e){if(t)return;r.replaceChildren(),n.replaceChildren();let i=$(`div`,`dsh-tw-settings-error`,`加载配置失败:${e instanceof Error?e.message:String(e)}`),a=$(`button`,`dsh-tw-settings-btn`,`重试`);a.type=`button`,a.addEventListener(`click`,()=>{o()}),r.append(i,a)}};return o(),i.push(()=>{t=!0,e.replaceChildren(),e.classList.remove(`dsh-tw-settings`)}),()=>{for(let e of i.splice(0))e()}}function db(e,t,n){e.replaceChildren();let r=t.server??{},i=r.status??`unknown`,a=$(`span`,`dsh-tw-settings-chip`,i);a.dataset.state=i;let o=$(`span`,`dsh-tw-settings-muted`,[r.url===void 0?``:`TW ${r.url}`,t.git?.branch===void 0?``:`git ${t.git.branch}`,t.git?.lastCommit===void 0?``:t.git.lastCommit,t.git?.conflict===void 0?t.git?.dirty===!0?`有未提交改动`:``:`⚠️ 冲突未解决(${t.git.conflict.files.length} 个文件,已阻止提交)`].filter(Boolean).join(` · `)),s=$(`button`,`dsh-tw-settings-btn`,`同步`);s.type=`button`,s.title=`git 同步(pull → commit → push)`,s.addEventListener(`click`,()=>{s.disabled=!0,s.textContent=`同步中…`,(async()=>{try{let e=await fetch(S,{method:`POST`,signal:AbortSignal.timeout(12e4)}),t=de(await e.json().catch(()=>null),e.status);D(t.ok?`同步完成:${t.message}`:`同步失败:${t.message}`)}catch(e){D(`同步失败:${e instanceof Error?e.message:String(e)}`)}finally{s.disabled=!1,s.textContent=`同步`,n()}})()});let c=$(`button`,`dsh-tw-settings-btn`,`重启 TW`);c.type=`button`,c.addEventListener(`click`,()=>{c.disabled=!0,c.textContent=`重启中…`,(async()=>{try{await lb(ie,{method:`POST`}),D(`TW 已重启`)}catch(e){D(`重启失败:${e instanceof Error?e.message:String(e)}`)}finally{c.disabled=!1,c.textContent=`重启 TW`,n()}})()}),e.append(a,o,s,c)}function fb(e,t,n){let r=$(`section`,`dsh-tw-settings-section`);r.append($(`h3`,`dsh-tw-settings-h`,`常规配置`));let i=t.note??{},a=t.git??{},o=t.ui??{},s=[],c=(e,t,n)=>{let i=$(`input`,`dsh-tw-settings-input`);i.value=n;let a=$(`label`,`dsh-tw-settings-field`);a.append($(`span`,`dsh-tw-settings-label`,t),i),s.push({key:e,initial:n,read:()=>i.value.trim(),changed:()=>i.value.trim()!==n}),r.append(a)},l=(e,t,n)=>{let i=$(`input`,`dsh-tw-settings-check`);i.type=`checkbox`,i.checked=n;let a=$(`label`,`dsh-tw-settings-field dsh-tw-settings-field-check`);return a.append(i,$(`span`,`dsh-tw-settings-label`,t)),s.push({key:e,initial:n,read:()=>i.checked,changed:()=>i.checked!==n}),r.append(a),i},u=(e,t,n)=>{let i=$(`input`,`dsh-tw-settings-input`);i.type=`number`,i.value=String(n);let a=()=>{let e=i.value.trim();if(e.length===0)return n;let t=Number(e);return Number.isFinite(t)?t:n},o=$(`label`,`dsh-tw-settings-field`);o.append($(`span`,`dsh-tw-settings-label`,t),i),s.push({key:e,initial:n,read:a,changed:()=>a()!==n}),r.append(o)},d=(e,t,n,i)=>{let a=$(`select`,`dsh-tw-settings-input`);for(let e of i){let t=document.createElement(`option`);t.value=e.value,t.textContent=e.label,e.value===n&&(t.selected=!0),a.append(t)}let o=$(`label`,`dsh-tw-settings-field`);return o.append($(`span`,`dsh-tw-settings-label`,t),a),s.push({key:e,initial:n,read:()=>a.value,changed:()=>a.value!==n}),r.append(o),a},f=(e,t,n,i)=>{let a=$(`textarea`,`dsh-tw-settings-input dsh-tw-settings-area`);a.rows=i,a.value=n;let o=$(`label`,`dsh-tw-settings-field dsh-tw-settings-field-area`);return o.append($(`span`,`dsh-tw-settings-label`,t),a),s.push({key:e,initial:n,read:()=>a.value.trim(),changed:()=>a.value.trim()!==n}),r.append(o),a};c(`note.tag`,`快速笔记默认 tag`,typeof i.tag==`string`?i.tag:`inbox`),l(`git.autoCommit`,`自动 commit(防抖)`,a.autoCommit!==!1),u(`git.debounceMs`,`自动 commit 防抖(ms)`,typeof a.debounceMs==`number`?a.debounceMs:6e4),c(`git.remote`,`git 远端(空=仅本地)`,typeof a.remote==`string`?a.remote:``),c(`git.branch`,`git 分支`,typeof a.branch==`string`?a.branch:`main`);let p=t.startup??{};u(`startup.readyTimeoutMs`,`TW 启动就绪等待(ms;默认 60000,范围 5000–600000,超时上限为其 3 倍,对下次启动/重启生效)`,typeof p.readyTimeoutMs==`number`&&p.readyTimeoutMs>0?p.readyTimeoutMs:6e4),l(`ui.showQuickNote`,`显示「知识库」按钮里的「快速笔记」入口`,o.showQuickNote!==!1),l(`ui.showQuickNoteDock`,`显示聊天输入框上方的「快速笔记」快捷按钮`,o.showQuickNoteDock!==!1),d(`ui.quickNoteMode`,`点击「快速笔记」的打开方式`,typeof o.quickNoteMode==`string`&&o.quickNoteMode===`card`?`card`:`native`,[{value:`native`,label:`原生编辑器:直接弹出 TW 原生编辑页(新建/恢复草稿)`},{value:`card`,label:`Markdown 卡片:弹出现有快速笔记卡片(CodeMirror 编辑器)`}]),c(`ui.sidebarLabel`,`侧边栏 TW 入口显示名称`,typeof o.sidebarLabel==`string`&&o.sidebarLabel.trim().length>0?o.sidebarLabel.trim():`TiddlyWiki`),l(`ui.showPanelStatus`,`显示「知识库」按钮里的 TW 面板/重载入口与状态行`,o.showPanelStatus!==!1),l(`ui.showSyncButton`,`显示「知识库」按钮里的「同步」入口与 git 状态点`,o.showSyncButton!==!1),l(`ui.followDshTheme`,`嵌入式 TW 跟随 DSH 深浅主题(暗色时自动切深色 palette,不写回 wiki)`,o.followDshTheme!==!1),c(`ui.darkPalette`,`暗色时 TW palette(tiddler 标题)`,typeof o.darkPalette==`string`&&o.darkPalette.trim().length>0?o.darkPalette.trim():`$:/palettes/CupertinoDark`),c(`ui.tabLabel`,`会话顶部「知识库」Tab 名称`,typeof o.tabLabel==`string`&&o.tabLabel.trim().length>0?o.tabLabel.trim():`知识库`),l(`ui.showSessionTab`,`显示会话顶部「知识库」Tab(本会话相关 wiki 笔记汇总)`,o.showSessionTab!==!1),l(`ui.showRightbarTab`,`在 DSH 右侧边栏提供 TiddlyWiki 入口/Tab(与聊天并排)`,o.showRightbarTab!==!1);let m=o.sendToAgent??{};l(`ui.sendToAgent.enabled`,`启用「发送给 Agent」(TW 笔记 → DSH 会话注入)`,m.enabled!==!1),c(`ui.sendToAgent.endpoint`,`TW 端请求基址(空=自动取当前 DSH origin)`,typeof m.endpoint==`string`?m.endpoint:``),c(`ui.sendToAgent.token`,`共享 token(非空时路由校验 x-send-to-agent-token 头;已设置时显示为 ********,原样保存=不改,清空=删除)`,typeof m.token==`string`?m.token:``);let h=o.allArticles??{};u(`ui.allArticles.pageSize`,`「所有文章」每页条数`,typeof h.pageSize==`number`?h.pageSize:10),r.append($(`h3`,`dsh-tw-settings-h`,`可选功能:微信公众号发布`)),r.append($(`div`,`dsh-tw-settings-muted`,`把 wiki 笔记一键发到公众号草稿箱(可选点发表)。**需要额外安装**:opencli 与 Browser Bridge 浏览器扩展,并让浏览器登录 mp.weixin.qq.com;adapter 在本仓库 tools/wechat/,安装与排错见 docs/wechat-publish-setup.md。不安装/不开启它,插件其他功能完全不受影响。开启后:注入提示词会多一条「发布前先读发布元数据规范」的约定,并在启动时把「发布元数据规范」与「微信公众号发布指南」(安装/换机还原步骤,等于仓库 docs/wechat-publish-setup.md)两篇文档写进 wiki(同名不覆盖)。`));let g=l(`wechat.enabled`,`启用微信公众号发布(默认关;需先按 docs/wechat-publish-setup.md 安装 opencli + 浏览器扩展)`,(t.wechat??{}).enabled===!0),_=t.prompt??{};r.append($(`h3`,`dsh-tw-settings-h`,`系统提示词(注入每个会话)`)),r.append($(`div`,`dsh-tw-settings-muted`,`插件把自己的约定(同步纪律 / 标签约定 / 链接格式等)注入每个会话的系统提示词。保存后**无需重启 dsh web**:section 会即时重新注册,当前会话从下一步起就使用新文本。`));let v=l(`prompt.enabled`,`注入 TiddlyWiki 提示词(关闭后本插件不再注入任何文本)`,_.enabled!==!1),y=d(`prompt.mode`,`内置文本形态`,_.mode===`full`?`full`:`slim`,[{value:`slim`,label:`精简(默认):只保留工具 schema 表达不了的约定`},{value:`full`,label:`完整:额外附一份由工具注册表实时生成的参数索引`}]),ee=f(`prompt.extra`,`附加说明(永远追加在末尾,可放团队/个人规范)`,typeof _.extra==`string`?_.extra:``,5),te=f(`prompt.override`,`整段替换(非空时取代上面的内置文本,附加说明仍会追加)`,typeof _.override==`string`?_.override:``,8),b=()=>s.some(e=>(e.key.startsWith(`prompt.`)||e.key===`wechat.enabled`)&&e.changed()),x=$(`button`,`dsh-tw-settings-btn`,`预览注入文本(按表单当前值)`);x.type=`button`,x.title=`按表单当前值渲染注入文本(不用先保存);点「保存配置」后这就是实际注入的内容`;let S=$(`pre`,`dsh-tw-settings-prompt-preview`);S.hidden=!0,x.addEventListener(`click`,()=>{if(!S.hidden){S.hidden=!0,S.textContent=``;return}x.disabled=!0,(async()=>{try{let e=await lb(ne,{method:`POST`,headers:{"content-type":`application/json`},body:JSON.stringify({enabled:v.checked,mode:y.value,extra:ee.value,override:te.value,wechat:g.checked})});if(e.ok!==!0)throw Error(e.error??`获取失败`);S.textContent=e.enabled===!1?`(已关闭:不会注入任何提示词)`:`# 形态 ${e.mode??``} · ${e.length??0} 字符${b()?`(含未保存的修改,保存后才真正注入)`:``}\n\n${e.text??``}`,S.hidden=!1}catch(e){D(`读取提示词失败:${e instanceof Error?e.message:String(e)}`)}finally{x.disabled=!1}})()}),r.append(x,S);let C=t.bridge??{};l(`bridge.enabled`,`启用「本地剪藏桥」(书签小工具后端监听 127.0.0.1 端口,保存后立即生效)`,C.enabled===!0),u(`bridge.port`,`剪藏桥端口(改端口需重启 dsh web 生效)`,typeof C.port==`number`&&C.port>0?C.port:8618),c(`bridge.token`,`剪藏桥 token(非空时校验书签的 x-clip-token 头;强烈建议设置。已设置时显示为 ********,原样保存=不改,清空=删除)`,typeof C.token==`string`?C.token:``),c(`bridge.tag`,`剪藏笔记默认 tag`,typeof C.tag==`string`&&C.tag.trim().length>0?C.tag.trim():`clip`);let w=$(`button`,`dsh-tw-settings-btn dsh-tw-settings-primary`,`保存配置`);w.type=`button`,w.addEventListener(`click`,()=>{w.disabled=!0,(async()=>{let e={};for(let t of s){if(!t.changed())continue;let n=t.key.split(`.`),r=e;for(let e=0;e<n.length;e++){let i=n[e];if(i===void 0)break;if(e===n.length-1)r[i]=t.read();else{let e=r[i]??{};r[i]=e,r=e}}}try{await lb(re,{method:`POST`,headers:{"content-type":`application/json`},body:JSON.stringify(e)}),ye(),D(`配置已保存`),n()}catch(e){D(`保存失败:${e instanceof Error?e.message:String(e)}`)}finally{w.disabled=!1}})()}),r.append(w),e.append(r)}function pb(e,t,n,r){let i=n?.plugins??[],a=n?.themes??[],o=n?.languages??[],s=new Set(t?.plugins??[]),c=new Set(t?.themes??[]),l=new Set(t?.languages??[]),u=$(`section`,`dsh-tw-settings-section`);u.append($(`h3`,`dsh-tw-settings-h`,`插件管理(自带官方插件)`));let d=$(`input`,`dsh-tw-settings-input dsh-tw-settings-search`);d.placeholder=`搜索插件…`;let f=$(`div`,`dsh-tw-settings-list`),p=$(`button`,`dsh-tw-settings-btn dsh-tw-settings-primary`,`应用插件(重启 TW)`);p.type=`button`;let m=e=>{f.replaceChildren();let t=e.toLowerCase();for(let e of i){if(t.length>0&&!`${e.label} ${e.name} ${e.description}`.toLowerCase().includes(t))continue;let n=$(`input`,`dsh-tw-settings-check`);n.type=`checkbox`,n.checked=s.has(e.name),n.addEventListener(`change`,()=>{n.checked?s.add(e.name):s.delete(e.name)});let r=$(`label`,`dsh-tw-settings-row dsh-tw-settings-plugin`),i=$(`span`,`dsh-tw-settings-name`,e.label);i.title=e.name;let a=$(`span`,`dsh-tw-settings-muted`,e.description||e.name);r.append(n,i,a),f.append(r)}};d.addEventListener(`input`,()=>m(d.value)),p.addEventListener(`click`,()=>{p.disabled=!0,(async()=>{try{await lb(E,{method:`POST`,headers:{"content-type":`application/json`},body:JSON.stringify({plugins:[...s]})}),D(`插件已应用,TW 已重启`),r()}catch(e){D(`应用失败:${e instanceof Error?e.message:String(e)}`),p.disabled=!1}})()}),m(``),u.append(d,f,p),e.append(u);let h=$(`section`,`dsh-tw-settings-section`);h.append($(`h3`,`dsh-tw-settings-h`,`主题管理(自带主题)`));let g=$(`div`,`dsh-tw-settings-muted`,`「加载」= TW 里可用的主题(可多选,依赖链自动带上,如 heavier 会带 snowwhite+vanilla);「活动」= 当前视觉主题(单选,自动加入加载集)。应用后重启 TW。`),_=$(`div`,`dsh-tw-settings-row dsh-tw-settings-head`);_.append($(`span`,`dsh-tw-settings-col`,`加载`),$(`span`,`dsh-tw-settings-col`,`活动`),$(`span`,`dsh-tw-settings-name`,`主题`));let v=t?.themes??[],y=t?.themeActive,ee=typeof y==`string`&&a.some(e=>e.name===y)?y:v.length>0?v[v.length-1]:`tiddlywiki/vanilla`,te=$(`div`,`dsh-tw-settings-list`);for(let e of a){let t=$(`input`,`dsh-tw-settings-check`);t.type=`checkbox`,t.checked=c.has(e.name),t.title=`加载该主题`,t.addEventListener(`change`,()=>{t.checked?c.add(e.name):c.delete(e.name)});let n=$(`input`,`dsh-tw-settings-check`);n.type=`radio`,n.name=`dsh-tw-active-theme`,n.checked=e.name===ee,n.title=`设为活动主题`,n.addEventListener(`change`,()=>{n.checked&&(ee=e.name)});let r=$(`span`,`dsh-tw-settings-name`,e.label);r.title=e.name;let i=$(`span`,`dsh-tw-settings-muted`,e.description||e.name),a=$(`div`,`dsh-tw-settings-row dsh-tw-settings-plugin`);a.append(t,n,r,i),te.append(a)}let b=$(`button`,`dsh-tw-settings-btn dsh-tw-settings-primary`,`应用主题(重启 TW)`);b.type=`button`,b.addEventListener(`click`,()=>{b.disabled=!0,(async()=>{try{await lb(E,{method:`POST`,headers:{"content-type":`application/json`},body:JSON.stringify({themes:[...c],themeActive:ee})}),D(`主题已应用,TW 已重启`),r()}catch(e){D(`应用失败:${e instanceof Error?e.message:String(e)}`),b.disabled=!1}})()}),h.append(g,_,te,b),e.append(h);let x=$(`section`,`dsh-tw-settings-section`);x.append($(`h3`,`dsh-tw-settings-h`,`语言管理(自带官方语言包)`));let S=$(`div`,`dsh-tw-settings-muted`,`勾选启用语言插件并重启 TW;如中文请选 zh-Hans(简体)或 zh-CN。`),C=$(`div`,`dsh-tw-settings-list`);for(let e of o){let t=$(`input`,`dsh-tw-settings-check`);t.type=`checkbox`,t.checked=l.has(e.name),t.addEventListener(`change`,()=>{t.checked?l.add(e.name):l.delete(e.name)});let n=$(`label`,`dsh-tw-settings-row dsh-tw-settings-plugin`),r=$(`span`,`dsh-tw-settings-name`,e.label);r.title=e.name;let i=$(`span`,`dsh-tw-settings-muted`,e.description||e.name);n.append(t,r,i),C.append(n)}let w=$(`button`,`dsh-tw-settings-btn dsh-tw-settings-primary`,`应用语言(重启 TW)`);w.type=`button`,w.addEventListener(`click`,()=>{w.disabled=!0,(async()=>{try{await lb(E,{method:`POST`,headers:{"content-type":`application/json`},body:JSON.stringify({languages:[...l]})}),D(`语言已应用,TW 已重启`),r()}catch(e){D(`应用失败:${e instanceof Error?e.message:String(e)}`),w.disabled=!1}})()}),x.append(S,C,w),e.append(x)}function mb(e,t,n){let r=$(`section`,`dsh-tw-settings-section`);r.append($(`h3`,`dsh-tw-settings-h`,`知识库位置(可切换)`));let i=$(`div`,`dsh-tw-settings-muted`,`读取中…`),a=$(`div`,`dsh-tw-settings-muted`),o=$(`div`,`dsh-tw-settings-muted`),s=$(`div`,`dsh-tw-settings-muted`),c=$(`input`,`dsh-tw-settings-input`),l=$(`input`,`dsh-tw-settings-input`);c.placeholder=`绝对路径,如 D:\\notes 或 $DSH_HOME/tiddlywiki`,l.placeholder=`文件夹名(默认 main;填 . 表示直接用上面这个目录)`;let u=$(`label`,`dsh-tw-settings-field`);u.append($(`span`,`dsh-tw-settings-label`,`根目录(wikiRoot)`),c);let d=$(`label`,`dsh-tw-settings-field`);d.append($(`span`,`dsh-tw-settings-label`,`文件夹名(wiki)`),l);let f=$(`button`,`dsh-tw-settings-btn dsh-tw-settings-primary`,`切换到这个位置`);f.type=`button`;let p=$(`button`,`dsh-tw-settings-btn`,`恢复为配置默认`);p.type=`button`;let m=$(`div`,`dsh-tw-settings-row`);m.append(f,p);let h=$(`div`,`dsh-tw-settings-row dsh-tw-settings-candidates`),g=$(`div`,`dsh-tw-settings-muted`,"切换会停掉并就地重启 TW 子进程(几秒);新目录若还没有 tiddlywiki.info,插件会自动 `--init server` 初始化一个全新知识库。选中的位置会记在 $DSH_HOME 下的指针文件里(不进 wiki),可用「恢复为配置默认」清除。"),_=(e,t)=>{f.disabled=e,p.disabled=e,f.textContent=e?t:`切换到这个位置`},v=async()=>{if(!t())try{let e=await lb(ce);if(t())return;if(e.ok!==!0)throw Error(e.error??`获取失败`);let n=e.current??{};i.textContent=`当前:${n.path??`(未知)`}`;let r=n.source===`state`?`来源:指针文件(在设置页切换过)`:n.source===`config`?`来源:cordis 配置(config.wikiRoot / config.wiki)`:`来源:默认值($DSH_HOME/tiddlywiki + main)`;a.textContent=`${r} · 配置默认:${e.default?.path??`(未知)`}`,o.textContent=e.stateFile===void 0?``:`指针文件:${e.stateFile}(不存在=使用配置默认)`,s.textContent=e.error===void 0?``:`⚠️ ${e.error}`,c.value.length===0&&typeof n.root==`string`&&(c.value=n.root),l.value.length===0&&typeof n.name==`string`&&(l.value=n.name),h.replaceChildren();let u=e.candidates??[];if(u.length>0){h.append($(`span`,`dsh-tw-settings-label`,`同目录下可选的 wiki:`));for(let e of u){let t=$(`button`,`dsh-tw-settings-btn dsh-tw-settings-chipbtn`,e===`.`?`(根目录本身)`:e);t.type=`button`,t.title=`填入文件夹名「${e}」`,t.addEventListener(`click`,()=>{l.value=e}),h.append(t)}}}catch(e){if(t())return;i.textContent=`读取位置失败:${e instanceof Error?e.message:String(e)}`}};f.addEventListener(`click`,()=>{let e=c.value.trim(),t=l.value.trim().length>0?l.value.trim():`main`;if(e.length===0){D(`请先填写根目录(绝对路径)`);return}window.confirm(`切换知识库到:\n${e}\\${t}\n\n会停止并重启 TW 子进程;当前对话/工具随后读写的是新知识库。确定继续?`)&&(_(!0,`切换中…(重启 TW)`),(async()=>{try{let r=await lb(le,{method:`POST`,headers:{"content-type":`application/json`},body:JSON.stringify({root:e,name:t}),signal:AbortSignal.timeout(12e4)});r.warning===void 0?D(`已切换到 ${r.path??``}`):D(`已切换到 ${r.path??``}(注意:${r.warning})`),ye(),await v(),await n()}catch(e){D(`切换失败:${e instanceof Error?e.message:String(e)}`),await v()}finally{_(!1,``)}})())}),p.addEventListener(`click`,()=>{window.confirm(`恢复为配置默认位置?会删除位置指针文件,并(必要时)切回配置里的知识库。`)&&(_(!0,`切换中…(重启 TW)`),(async()=>{try{let e=await lb(ue,{method:`POST`,headers:{"content-type":`application/json`},body:`{}`,signal:AbortSignal.timeout(12e4)});D(e.warning===void 0?`已恢复为配置默认:${e.path??``}`:`已恢复默认(注意:${e.warning})`),ye(),await v(),await n()}catch(e){D(`恢复失败:${e instanceof Error?e.message:String(e)}`),await v()}finally{_(!1,``)}})())}),r.append(i,a,o,u,d,m,h,s,g),e.append(r),v()}function hb(e,t){let n=$(`section`,`dsh-tw-settings-section`);n.append($(`h3`,`dsh-tw-settings-h`,`初始化(一次性预置)`));let r=$(`div`,`dsh-tw-settings-muted`,`这些是插件与 dsh 联动、需要在 wiki 里预置的 tiddler/配置。「发送给 Agent 按钮」和「TW 前端 API 基址」是功能必需项,启动时自动写入(只写缺失,不覆盖你的改动);其余为可选项,默认不自动写入、也不会强绑定——需要时点「重新初始化」写入,不想要了可随时「反初始化」移除。`),i=$(`div`,`dsh-tw-settings-list`),a=$(`div`,`dsh-tw-settings-muted`),o=async()=>{if(!t())try{let e=await lb(ae);if(t())return;if(e.ok!==!0||!Array.isArray(e.items))throw Error(e.error??`获取失败`);i.replaceChildren();let n=0,r=0;for(let t of e.items){t.present&&(n+=1),t.removable&&(r+=1);let e=$(`span`,`dsh-tw-settings-chip`,t.present?`✓`:`✗`);e.dataset.state=t.present?`ok`:`missing`,e.title=t.present?`已存在`:`缺失`;let a=$(`span`,`dsh-tw-settings-name`,t.title);a.title=t.id;let s=$(`span`,`dsh-tw-settings-muted`,t.detail??t.description),c=t.updateAvailable===!0?$(`span`,`dsh-tw-settings-chip`,`⬆ 有更新`):void 0;c!==void 0&&(c.dataset.state=`update`,c.title=`内置内容在本 wiki 预置之后更新过,可点「重新初始化」取用`);let l=t.userModified===!0?$(`span`,`dsh-tw-settings-chip`,`✏️ 本地已修改`):void 0;l!==void 0&&(l.dataset.state=`missing`,l.title=`这篇是你的内容:重新初始化会覆盖它`);let u=$(`button`,`dsh-tw-settings-btn`,t.updateAvailable===!0?`更新到内置版本`:`重新初始化`);u.type=`button`,u.title=`强制重写「${t.title}」的内置内容(会覆盖当前 tiddler)`,u.addEventListener(`click`,()=>{if(t.updateAvailable===!0){let e=t.userModified===!0?`「${t.title}」有你的本地修改,更新会覆盖它。`:t.userModified===void 0?`无法确认「${t.title}」是否被你编辑过(旧格式标记),更新可能覆盖你的改动。`:``;if(!window.confirm(`${e}${e.length>0?`
|
|
764
|
+
`)},_=()=>{let e=n.getState();o!==void 0&&(o.dataset.state=e.state),a!==void 0&&(a.title=`知识库 · ${e.tooltip}`),g()},v=async()=>{p=await tb(),!r&&(c!==void 0&&(c.textContent=p.text),l!==void 0&&(l.dataset.state=p.state),g())},y=p=>{if(r)return;if(i=document.createElement(`div`),i.className=`dsh-tw-fab-wrap`,s=document.createElement(`div`),s.className=`dsh-tw-fab-menu`,s.hidden=!0,p.showPanelStatus){let e=document.createElement(`div`);e.className=`dsh-tw-fab-status dsh-tw-fab-status-tiprow`,l=document.createElement(`span`),l.className=`dsh-tw-fab-status-dot`,l.dataset.state=`unknown`,c=document.createElement(`span`),c.className=`dsh-tw-fab-status-text`,c.textContent=`TiddlyWiki 服务…`,e.append(l,c),u=document.createElement(`div`),u.className=`dsh-tw-fab-tip`,u.hidden=!0,e.append(u),e.addEventListener(`mouseenter`,()=>{g(),u!==void 0&&(u.hidden=!1)}),e.addEventListener(`mouseleave`,()=>{u!==void 0&&(u.hidden=!0)}),s.append(e)}if(p.showQuickNote){let e=document.createElement(`button`);e.type=`button`,e.className=`dsh-tw-fab-item`,e.textContent=`📝 快速笔记`,e.title=`ui.quickNoteMode=native 时直接打开 TW 原生编辑器;card 时打开 Markdown 卡片`,e.addEventListener(`click`,()=>{m(),be().then(e=>{e.quickNoteMode===`native`?t.openNative():t.open()})}),s.append(e)}if(p.showPanelStatus){d=document.createElement(`span`),d.textContent=`打开 TW 面板`;let t=document.createElement(`button`);t.type=`button`,t.className=`dsh-tw-fab-item`,t.append(document.createTextNode(`🖥 `),d),t.addEventListener(`click`,()=>{m(),e.toggle()}),s.append(t);let n=document.createElement(`button`);n.type=`button`,n.className=`dsh-tw-fab-item`,n.textContent=`🔄 重载 TW 面板`,n.addEventListener(`click`,()=>{m(),document.dispatchEvent(new CustomEvent(qe)),v()}),s.append(n)}if(p.showSyncButton){let e=document.createElement(`button`);e.type=`button`,e.className=`dsh-tw-fab-item`,e.textContent=`🔁 同步`,e.addEventListener(`click`,()=>{m(),n.trigger()}),s.append(e)}a=document.createElement(`button`),a.type=`button`,a.className=`dsh-tw-fab`,a.setAttribute(`aria-label`,`TiddlyWiki 知识库`);let y=document.createElement(`span`);y.className=`dsh-tw-fab-icon`,y.innerHTML=`<svg viewBox="0 0 16 16" width="17" height="17" fill="none" stroke="currentColor" stroke-width="1.3" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4 2.5h8a1 1 0 0 1 1 1v9a1 1 0 0 1-1 1H4a1 1 0 0 1-1-1v-9a1 1 0 0 1 1-1z"/><path d="M6 6h4M6 8.5h2.5"/></svg>`,o=document.createElement(`span`),o.className=`dsh-tw-fab-dot`,o.dataset.state=n.getState().state,a.append(y,o),a.addEventListener(`click`,()=>{if(s!==void 0){if(f){m();return}f=!0,s.hidden=!1,_(),v()}}),i.append(s,a),document.body.append(i),_(),document.addEventListener(`click`,h,!0)},ee=e.subscribe(()=>{d!==void 0&&(d.textContent=e.isOpen()?`收起 TW 面板`:`打开 TW 面板`)}),te=n.subscribe(_);return(async()=>{let e=await eb();r||!e.showQuickNote&&!e.showPanelStatus&&!e.showSyncButton||y(e)})(),()=>{r=!0,document.removeEventListener(`click`,h,!0),ee(),te(),i?.remove()}}function rb(e){return function(){let[t,n]=c.useState(!1),[r,i]=c.useState(null),a=c.useRef(null),o=c.useRef(null);c.useEffect(()=>{let e=!0,t=e=>{let t=e.detail;n(t?.open===!0)};return window.addEventListener(jy,t),be().then(t=>{e&&i(t.quickNoteMode)}),()=>{e=!1,window.removeEventListener(jy,t)}},[]),c.useLayoutEffect(()=>{let e=a.current;if(e===null)return;let t=()=>{let t=null,n=null,r=e;for(;r!==null&&r!==document.documentElement&&(r=r.parentElement,r!==null);){for(let e of r.querySelectorAll(`textarea, [contenteditable]:not([contenteditable="false"]), [role="textbox"]`))if(e.getBoundingClientRect().width>300){t=e,n=r;break}if(n!==null)break}if(t===null||n===null)return null;let i=n.getBoundingClientRect().width,a=t,o=null,s=0;for(;a!==null&&a!==n;){let e=a.getBoundingClientRect().width;e>0&&e<i-8&&e>=s&&(s=e,o=a),a=a.parentElement}return o},n=()=>{let n=e.getBoundingClientRect(),r=t(),i=r===null?n.right:r.getBoundingClientRect().right,a=Math.max(0,n.right-i);return e.style.paddingRight!==`${a}px`&&(e.style.paddingRight=`${a}px`),r!==null};n();let r=new ResizeObserver(n),i=e;for(;i!==null&&i!==document.documentElement&&(r.observe(i),!(i instanceof HTMLElement&&i.hasAttribute(`data-phase`)));)i=i.parentElement;window.addEventListener(`resize`,n),document.addEventListener(`visibilitychange`,n);let o=0,s=0,c=window.setInterval(()=>{if(s++,!n()){s>=20&&window.clearInterval(c);return}o++,o>=3&&window.clearInterval(c)},1500),l=window.setTimeout(n,120),u=window.setTimeout(n,600);return()=>{r.disconnect(),window.removeEventListener(`resize`,n),document.removeEventListener(`visibilitychange`,n),window.clearInterval(c),window.clearTimeout(l),window.clearTimeout(u)}},[]);let s=r===`native`,l=s?`快速笔记`:t?`快速笔记(已打开)`:`快速笔记`,u=s?`快速笔记:直接打开 TiddlyWiki 原生编辑器(再次点击可收起弹窗)`:t?`快速笔记已打开(点此按钮或卡片右上角 ✕ 收起)`:`打开快速笔记(在按钮上方弹出,可拖动标题栏移动)`;return c.createElement(`div`,{ref:a,className:`dsh-tw-dock-note`},c.createElement(`button`,{ref:o,type:`button`,className:t?`dsh-tw-dock-note-btn dsh-tw-dock-note-btn-active`:`dsh-tw-dock-note-btn`,title:u,onClick:()=>{be().then(t=>{t.quickNoteMode===`native`?Tt()&&!Et()?Ot():e.openNative():e.isOpen()?e.close():e.open(o.current??void 0)})}},c.createElement(`span`,{className:`dsh-tw-dock-note-icon`,"aria-hidden":`true`},`📝`),c.createElement(`span`,{className:`dsh-tw-dock-note-label`},l)))}}async function ib(e,t=15e3){try{let n=await fetch(_,{method:`POST`,headers:{"content-type":`application/json`,"x-requested-with":`TiddlyWiki`},body:JSON.stringify({title:e}),signal:AbortSignal.timeout(t)});if(!n.ok)return null;let r=await n.text();return r.trim().length>0?r:null}catch{return null}}function ab(){return Xe()}async function ob(e){try{let t=await fetch(`${g}?title=${encodeURIComponent(e)}`,{signal:AbortSignal.timeout(8e3)});return t.status!==404&&(await t.json().catch(()=>null))?.notFound!==!0}catch{return!0}}function sb(e){let{sessionId:t,viewRequest:n,completeViewRequest:r}=e,[i,a]=c.useState(`loading`),[o,s]=c.useState(null),[l,u]=c.useState(null),[d,f]=c.useState(``),p=c.useRef(0),m=c.useRef(!0),h=c.useRef(0),g=c.useCallback(async()=>{let e=++p.current;if(m.current){if(a(`loading`),typeof t!=`string`||t.length===0){a(`error`),f(`缺少会话 ID`);return}if(h.current>=3){a(`error`),f(`连续 3 次生成失败,已停止自动重试,请点「🔄 刷新」`);return}try{let n=await fetch(w,{method:`POST`,headers:{"content-type":`application/json`},body:JSON.stringify({session:t}),signal:AbortSignal.timeout(25e3)}),r=await n.json().catch(()=>null);if(!m.current||e!==p.current)return;if(!n.ok||r?.ok!==!0){h.current++,a(`error`),f(r?.error??`HTTP ${n.status}`);return}if(typeof r.title!=`string`||r.title.length===0){h.current++,a(`error`),f(`汇总生成失败:缺少标题`);return}u(r.title);let i=await ib(r.title);if(!m.current||e!==p.current)return;if(i===null){let t=await ob(r.title);if(!m.current||e!==p.current)return;if(!t){if(h.current++,h.current<3){g();return}a(`error`),f(`汇总条目不存在,自动重建失败,请重试`);return}h.current++,a(`error`),f(`渲染失败:wiki 渲染服务不可用(host /render)`);return}h.current=0,s(i),a(`ready`)}catch(t){if(!m.current||e!==p.current)return;h.current++,a(`error`),f(t instanceof Error?t.message:String(t))}}},[t]);c.useEffect(()=>(m.current=!0,g(),()=>{m.current=!1}),[g]),c.useEffect(()=>{if(i!==`ready`||l===null)return;let e=!0,t=window.setInterval(()=>{(async()=>{if(!e)return;let t=await ob(l);if(e)if(t)h.current=0;else{if(h.current++,h.current<3){g();return}a(`error`),f(`汇总条目已失效且自动重建多次失败,请点「🔄 刷新」重试`)}})()},3e4);return()=>{e=!1,window.clearInterval(t)}},[i,l,g]),c.useEffect(()=>{n!=null&&r?.()},[n,r]);let _=()=>{h.current=0,g()},v;return v=i===`loading`?c.createElement(`div`,{className:`dsh-tw-summary-state`},c.createElement(`div`,{className:`dsh-tw-summary-state-spin`},`⏳`),c.createElement(`div`,null,`正在生成会话 wiki 汇总…`)):i===`error`?c.createElement(`div`,{className:`dsh-tw-summary-state`},c.createElement(`div`,{className:`dsh-tw-summary-state-title`},`知识库汇总暂不可用`),c.createElement(`div`,{className:`dsh-tw-summary-state-detail`},d),c.createElement(`button`,{type:`button`,className:`dsh-tw-summary-btn`,onClick:_},`重试`)):c.createElement(`div`,{className:`dsh-tw-summary-native dsh-tw-toolcard-native`,dangerouslySetInnerHTML:{__html:o??``}}),c.createElement(`div`,{className:`dsh-tw-summary`,"data-dsh-tw-summary":`true`},c.createElement(`div`,{className:`dsh-tw-summary-bar`},c.createElement(`span`,{className:`dsh-tw-summary-bar-title`},`📚 本会话知识库`),c.createElement(`span`,{className:`dsh-tw-summary-bar-hint`},`产生 / 读取 / 检索过的笔记(含子代理)`),c.createElement(`span`,{className:`dsh-tw-summary-bar-spacer`}),c.createElement(`button`,{type:`button`,className:`dsh-tw-summary-btn`,onClick:_,disabled:i===`loading`,title:`重新生成并刷新汇总`},`🔄 刷新`)),v)}function cb(e,t){if(!(e===void 0||t.showSessionTab===!1))return Ye(t.tabLabel),e.inject(`conversation.view`,()=>e.register({name:`conversation.view`,id:`dsh-tiddlywiki-summary`,order:20,label:ab},sb))}function $(e,t,n){let r=document.createElement(e);return t!==void 0&&(r.className=t),n!==void 0&&(r.textContent=n),r}async function lb(e,t){let n=await fetch(e,{...t,signal:t?.signal??AbortSignal.timeout(15e3)}),r=await n.json().catch(()=>({}));if(!n.ok){let e=r.error??`HTTP ${n.status}`;throw Error(e)}return r}function ub(e){let t=!1;e.classList.add(`dsh-tw-settings`);let n=$(`div`,`dsh-tw-settings-row dsh-tw-settings-status`),r=$(`div`,`dsh-tw-settings-body`);e.append(n,r);let i=[],a={},o=async()=>{try{let e=await lb(T);if(t)return;db(n,e,o),gb(r,e,o,()=>t,a)}catch(e){if(t)return;r.replaceChildren(),n.replaceChildren();let i=$(`div`,`dsh-tw-settings-error`,`加载配置失败:${e instanceof Error?e.message:String(e)}`),a=$(`button`,`dsh-tw-settings-btn`,`重试`);a.type=`button`,a.addEventListener(`click`,()=>{o()}),r.append(i,a)}};return o(),i.push(()=>{t=!0,e.replaceChildren(),e.classList.remove(`dsh-tw-settings`)}),()=>{for(let e of i.splice(0))e()}}function db(e,t,n){e.replaceChildren();let r=t.server??{},i=r.status??`unknown`,a=$(`span`,`dsh-tw-settings-chip`,i);a.dataset.state=i;let o=$(`span`,`dsh-tw-settings-muted`,[r.url===void 0?``:`TW ${r.url}`,t.git?.branch===void 0?``:`git ${t.git.branch}`,t.git?.lastCommit===void 0?``:t.git.lastCommit,t.git?.conflict===void 0?t.git?.dirty===!0?`有未提交改动`:``:`⚠️ 冲突未解决(${t.git.conflict.files.length} 个文件,已阻止提交)`].filter(Boolean).join(` · `)),s=$(`button`,`dsh-tw-settings-btn`,`同步`);s.type=`button`,s.title=`git 同步(pull → commit → push)`,s.addEventListener(`click`,()=>{s.disabled=!0,s.textContent=`同步中…`,(async()=>{try{let e=await fetch(S,{method:`POST`,signal:AbortSignal.timeout(12e4)}),t=de(await e.json().catch(()=>null),e.status);D(t.ok?`同步完成:${t.message}`:`同步失败:${t.message}`)}catch(e){D(`同步失败:${e instanceof Error?e.message:String(e)}`)}finally{s.disabled=!1,s.textContent=`同步`,n()}})()});let c=$(`button`,`dsh-tw-settings-btn`,`重启 TW`);c.type=`button`,c.addEventListener(`click`,()=>{c.disabled=!0,c.textContent=`重启中…`,(async()=>{try{await lb(ie,{method:`POST`}),D(`TW 已重启`)}catch(e){D(`重启失败:${e instanceof Error?e.message:String(e)}`)}finally{c.disabled=!1,c.textContent=`重启 TW`,n()}})()}),e.append(a,o,s,c)}function fb(e,t,n){let r=$(`section`,`dsh-tw-settings-section`);r.append($(`h3`,`dsh-tw-settings-h`,`常规配置`));let i=t.note??{},a=t.git??{},o=t.ui??{},s=[],c=(e,t,n)=>{let i=$(`input`,`dsh-tw-settings-input`);i.value=n;let a=$(`label`,`dsh-tw-settings-field`);a.append($(`span`,`dsh-tw-settings-label`,t),i),s.push({key:e,initial:n,read:()=>i.value.trim(),changed:()=>i.value.trim()!==n}),r.append(a)},l=(e,t,n)=>{let i=$(`input`,`dsh-tw-settings-check`);i.type=`checkbox`,i.checked=n;let a=$(`label`,`dsh-tw-settings-field dsh-tw-settings-field-check`);return a.append(i,$(`span`,`dsh-tw-settings-label`,t)),s.push({key:e,initial:n,read:()=>i.checked,changed:()=>i.checked!==n}),r.append(a),i},u=(e,t,n)=>{let i=$(`input`,`dsh-tw-settings-input`);i.type=`number`,i.value=String(n);let a=()=>{let e=i.value.trim();if(e.length===0)return n;let t=Number(e);return Number.isFinite(t)?t:n},o=$(`label`,`dsh-tw-settings-field`);o.append($(`span`,`dsh-tw-settings-label`,t),i),s.push({key:e,initial:n,read:a,changed:()=>a()!==n}),r.append(o)},d=(e,t,n,i)=>{let a=$(`select`,`dsh-tw-settings-input`);for(let e of i){let t=document.createElement(`option`);t.value=e.value,t.textContent=e.label,e.value===n&&(t.selected=!0),a.append(t)}let o=$(`label`,`dsh-tw-settings-field`);return o.append($(`span`,`dsh-tw-settings-label`,t),a),s.push({key:e,initial:n,read:()=>a.value,changed:()=>a.value!==n}),r.append(o),a},f=(e,t,n,i)=>{let a=$(`textarea`,`dsh-tw-settings-input dsh-tw-settings-area`);a.rows=i,a.value=n;let o=$(`label`,`dsh-tw-settings-field dsh-tw-settings-field-area`);return o.append($(`span`,`dsh-tw-settings-label`,t),a),s.push({key:e,initial:n,read:()=>a.value.trim(),changed:()=>a.value.trim()!==n}),r.append(o),a};c(`note.tag`,`快速笔记默认 tag`,typeof i.tag==`string`?i.tag:`inbox`),l(`note.workspaceMark`,`新建笔记自动标工作区(ws/<项目名> 标签 + workspace 字段,取自当前会话工作目录)`,i.workspaceMark!==!1),l(`git.autoCommit`,`自动 commit(防抖)`,a.autoCommit!==!1),u(`git.debounceMs`,`自动 commit 防抖(ms)`,typeof a.debounceMs==`number`?a.debounceMs:6e4),c(`git.remote`,`git 远端(空=仅本地)`,typeof a.remote==`string`?a.remote:``),c(`git.branch`,`git 分支`,typeof a.branch==`string`?a.branch:`main`);let p=t.startup??{};u(`startup.readyTimeoutMs`,`TW 启动就绪等待(ms;默认 60000,范围 5000–600000,超时上限为其 3 倍,对下次启动/重启生效)`,typeof p.readyTimeoutMs==`number`&&p.readyTimeoutMs>0?p.readyTimeoutMs:6e4),l(`ui.showQuickNote`,`显示「知识库」按钮里的「快速笔记」入口`,o.showQuickNote!==!1),l(`ui.showQuickNoteDock`,`显示聊天输入框上方的「快速笔记」快捷按钮`,o.showQuickNoteDock!==!1),d(`ui.quickNoteMode`,`点击「快速笔记」的打开方式`,typeof o.quickNoteMode==`string`&&o.quickNoteMode===`card`?`card`:`native`,[{value:`native`,label:`原生编辑器:直接弹出 TW 原生编辑页(新建/恢复草稿)`},{value:`card`,label:`Markdown 卡片:弹出现有快速笔记卡片(CodeMirror 编辑器)`}]),c(`ui.sidebarLabel`,`侧边栏 TW 入口显示名称`,typeof o.sidebarLabel==`string`&&o.sidebarLabel.trim().length>0?o.sidebarLabel.trim():`TiddlyWiki`),l(`ui.showPanelStatus`,`显示「知识库」按钮里的 TW 面板/重载入口与状态行`,o.showPanelStatus!==!1),l(`ui.showSyncButton`,`显示「知识库」按钮里的「同步」入口与 git 状态点`,o.showSyncButton!==!1),l(`ui.followDshTheme`,`嵌入式 TW 跟随 DSH 深浅主题(暗色时自动切深色 palette,不写回 wiki)`,o.followDshTheme!==!1),c(`ui.darkPalette`,`暗色时 TW palette(tiddler 标题)`,typeof o.darkPalette==`string`&&o.darkPalette.trim().length>0?o.darkPalette.trim():`$:/palettes/CupertinoDark`),c(`ui.tabLabel`,`会话顶部「知识库」Tab 名称`,typeof o.tabLabel==`string`&&o.tabLabel.trim().length>0?o.tabLabel.trim():`知识库`),l(`ui.showSessionTab`,`显示会话顶部「知识库」Tab(本会话相关 wiki 笔记汇总)`,o.showSessionTab!==!1),l(`ui.showRightbarTab`,`在 DSH 右侧边栏提供 TiddlyWiki 入口/Tab(与聊天并排)`,o.showRightbarTab!==!1);let m=o.sendToAgent??{};l(`ui.sendToAgent.enabled`,`启用「发送给 Agent」(TW 笔记 → DSH 会话注入)`,m.enabled!==!1),c(`ui.sendToAgent.endpoint`,`TW 端请求基址(空=自动取当前 DSH origin)`,typeof m.endpoint==`string`?m.endpoint:``),c(`ui.sendToAgent.token`,`共享 token(非空时路由校验 x-send-to-agent-token 头;已设置时显示为 ********,原样保存=不改,清空=删除)`,typeof m.token==`string`?m.token:``);let h=o.allArticles??{};u(`ui.allArticles.pageSize`,`「所有文章」每页条数`,typeof h.pageSize==`number`?h.pageSize:10),r.append($(`h3`,`dsh-tw-settings-h`,`可选功能:微信公众号发布`)),r.append($(`div`,`dsh-tw-settings-muted`,`把 wiki 笔记一键发到公众号草稿箱(可选点发表)。**需要额外安装**:opencli 与 Browser Bridge 浏览器扩展,并让浏览器登录 mp.weixin.qq.com;adapter 在本仓库 tools/wechat/,安装与排错见 docs/wechat-publish-setup.md。不安装/不开启它,插件其他功能完全不受影响。开启后:注入提示词会多一条「发布前先读发布元数据规范」的约定,并在启动时把「发布元数据规范」与「微信公众号发布指南」(安装/换机还原步骤,等于仓库 docs/wechat-publish-setup.md)两篇文档写进 wiki(同名不覆盖)。`));let g=l(`wechat.enabled`,`启用微信公众号发布(默认关;需先按 docs/wechat-publish-setup.md 安装 opencli + 浏览器扩展)`,(t.wechat??{}).enabled===!0),_=t.prompt??{};r.append($(`h3`,`dsh-tw-settings-h`,`系统提示词(注入每个会话)`)),r.append($(`div`,`dsh-tw-settings-muted`,`插件把自己的约定(同步纪律 / 标签约定 / 链接格式等)注入每个会话的系统提示词。保存后**无需重启 dsh web**:section 会即时重新注册,当前会话从下一步起就使用新文本。`));let v=l(`prompt.enabled`,`注入 TiddlyWiki 提示词(关闭后本插件不再注入任何文本)`,_.enabled!==!1),y=d(`prompt.mode`,`内置文本形态`,_.mode===`full`?`full`:`slim`,[{value:`slim`,label:`精简(默认):只保留工具 schema 表达不了的约定`},{value:`full`,label:`完整:额外附一份由工具注册表实时生成的参数索引`}]),ee=f(`prompt.extra`,`附加说明(永远追加在末尾,可放团队/个人规范)`,typeof _.extra==`string`?_.extra:``,5),te=f(`prompt.override`,`整段替换(非空时取代上面的内置文本,附加说明仍会追加)`,typeof _.override==`string`?_.override:``,8),b=()=>s.some(e=>(e.key.startsWith(`prompt.`)||e.key===`wechat.enabled`)&&e.changed()),x=$(`button`,`dsh-tw-settings-btn`,`预览注入文本(按表单当前值)`);x.type=`button`,x.title=`按表单当前值渲染注入文本(不用先保存);点「保存配置」后这就是实际注入的内容`;let S=$(`pre`,`dsh-tw-settings-prompt-preview`);S.hidden=!0,x.addEventListener(`click`,()=>{if(!S.hidden){S.hidden=!0,S.textContent=``;return}x.disabled=!0,(async()=>{try{let e=await lb(ne,{method:`POST`,headers:{"content-type":`application/json`},body:JSON.stringify({enabled:v.checked,mode:y.value,extra:ee.value,override:te.value,wechat:g.checked})});if(e.ok!==!0)throw Error(e.error??`获取失败`);S.textContent=e.enabled===!1?`(已关闭:不会注入任何提示词)`:`# 形态 ${e.mode??``} · ${e.length??0} 字符${b()?`(含未保存的修改,保存后才真正注入)`:``}\n\n${e.text??``}`,S.hidden=!1}catch(e){D(`读取提示词失败:${e instanceof Error?e.message:String(e)}`)}finally{x.disabled=!1}})()}),r.append(x,S);let C=t.bridge??{};l(`bridge.enabled`,`启用「本地剪藏桥」(书签小工具后端监听 127.0.0.1 端口,保存后立即生效)`,C.enabled===!0),u(`bridge.port`,`剪藏桥端口(改端口需重启 dsh web 生效)`,typeof C.port==`number`&&C.port>0?C.port:8618),c(`bridge.token`,`剪藏桥 token(非空时校验书签的 x-clip-token 头;强烈建议设置。已设置时显示为 ********,原样保存=不改,清空=删除)`,typeof C.token==`string`?C.token:``),c(`bridge.tag`,`剪藏笔记默认 tag`,typeof C.tag==`string`&&C.tag.trim().length>0?C.tag.trim():`clip`);let w=$(`button`,`dsh-tw-settings-btn dsh-tw-settings-primary`,`保存配置`);w.type=`button`,w.addEventListener(`click`,()=>{w.disabled=!0,(async()=>{let e={};for(let t of s){if(!t.changed())continue;let n=t.key.split(`.`),r=e;for(let e=0;e<n.length;e++){let i=n[e];if(i===void 0)break;if(e===n.length-1)r[i]=t.read();else{let e=r[i]??{};r[i]=e,r=e}}}try{await lb(re,{method:`POST`,headers:{"content-type":`application/json`},body:JSON.stringify(e)}),ye(),D(`配置已保存`),n()}catch(e){D(`保存失败:${e instanceof Error?e.message:String(e)}`)}finally{w.disabled=!1}})()}),r.append(w),e.append(r)}function pb(e,t,n,r){let i=n?.plugins??[],a=n?.themes??[],o=n?.languages??[],s=new Set(t?.plugins??[]),c=new Set(t?.themes??[]),l=new Set(t?.languages??[]),u=$(`section`,`dsh-tw-settings-section`);u.append($(`h3`,`dsh-tw-settings-h`,`插件管理(自带官方插件)`));let d=$(`input`,`dsh-tw-settings-input dsh-tw-settings-search`);d.placeholder=`搜索插件…`;let f=$(`div`,`dsh-tw-settings-list`),p=$(`button`,`dsh-tw-settings-btn dsh-tw-settings-primary`,`应用插件(重启 TW)`);p.type=`button`;let m=e=>{f.replaceChildren();let t=e.toLowerCase();for(let e of i){if(t.length>0&&!`${e.label} ${e.name} ${e.description}`.toLowerCase().includes(t))continue;let n=$(`input`,`dsh-tw-settings-check`);n.type=`checkbox`,n.checked=s.has(e.name),n.addEventListener(`change`,()=>{n.checked?s.add(e.name):s.delete(e.name)});let r=$(`label`,`dsh-tw-settings-row dsh-tw-settings-plugin`),i=$(`span`,`dsh-tw-settings-name`,e.label);i.title=e.name;let a=$(`span`,`dsh-tw-settings-muted`,e.description||e.name);r.append(n,i,a),f.append(r)}};d.addEventListener(`input`,()=>m(d.value)),p.addEventListener(`click`,()=>{p.disabled=!0,(async()=>{try{await lb(E,{method:`POST`,headers:{"content-type":`application/json`},body:JSON.stringify({plugins:[...s]})}),D(`插件已应用,TW 已重启`),r()}catch(e){D(`应用失败:${e instanceof Error?e.message:String(e)}`),p.disabled=!1}})()}),m(``),u.append(d,f,p),e.append(u);let h=$(`section`,`dsh-tw-settings-section`);h.append($(`h3`,`dsh-tw-settings-h`,`主题管理(自带主题)`));let g=$(`div`,`dsh-tw-settings-muted`,`「加载」= TW 里可用的主题(可多选,依赖链自动带上,如 heavier 会带 snowwhite+vanilla);「活动」= 当前视觉主题(单选,自动加入加载集)。应用后重启 TW。`),_=$(`div`,`dsh-tw-settings-row dsh-tw-settings-head`);_.append($(`span`,`dsh-tw-settings-col`,`加载`),$(`span`,`dsh-tw-settings-col`,`活动`),$(`span`,`dsh-tw-settings-name`,`主题`));let v=t?.themes??[],y=t?.themeActive,ee=typeof y==`string`&&a.some(e=>e.name===y)?y:v.length>0?v[v.length-1]:`tiddlywiki/vanilla`,te=$(`div`,`dsh-tw-settings-list`);for(let e of a){let t=$(`input`,`dsh-tw-settings-check`);t.type=`checkbox`,t.checked=c.has(e.name),t.title=`加载该主题`,t.addEventListener(`change`,()=>{t.checked?c.add(e.name):c.delete(e.name)});let n=$(`input`,`dsh-tw-settings-check`);n.type=`radio`,n.name=`dsh-tw-active-theme`,n.checked=e.name===ee,n.title=`设为活动主题`,n.addEventListener(`change`,()=>{n.checked&&(ee=e.name)});let r=$(`span`,`dsh-tw-settings-name`,e.label);r.title=e.name;let i=$(`span`,`dsh-tw-settings-muted`,e.description||e.name),a=$(`div`,`dsh-tw-settings-row dsh-tw-settings-plugin`);a.append(t,n,r,i),te.append(a)}let b=$(`button`,`dsh-tw-settings-btn dsh-tw-settings-primary`,`应用主题(重启 TW)`);b.type=`button`,b.addEventListener(`click`,()=>{b.disabled=!0,(async()=>{try{await lb(E,{method:`POST`,headers:{"content-type":`application/json`},body:JSON.stringify({themes:[...c],themeActive:ee})}),D(`主题已应用,TW 已重启`),r()}catch(e){D(`应用失败:${e instanceof Error?e.message:String(e)}`),b.disabled=!1}})()}),h.append(g,_,te,b),e.append(h);let x=$(`section`,`dsh-tw-settings-section`);x.append($(`h3`,`dsh-tw-settings-h`,`语言管理(自带官方语言包)`));let S=$(`div`,`dsh-tw-settings-muted`,`勾选启用语言插件并重启 TW;如中文请选 zh-Hans(简体)或 zh-CN。`),C=$(`div`,`dsh-tw-settings-list`);for(let e of o){let t=$(`input`,`dsh-tw-settings-check`);t.type=`checkbox`,t.checked=l.has(e.name),t.addEventListener(`change`,()=>{t.checked?l.add(e.name):l.delete(e.name)});let n=$(`label`,`dsh-tw-settings-row dsh-tw-settings-plugin`),r=$(`span`,`dsh-tw-settings-name`,e.label);r.title=e.name;let i=$(`span`,`dsh-tw-settings-muted`,e.description||e.name);n.append(t,r,i),C.append(n)}let w=$(`button`,`dsh-tw-settings-btn dsh-tw-settings-primary`,`应用语言(重启 TW)`);w.type=`button`,w.addEventListener(`click`,()=>{w.disabled=!0,(async()=>{try{await lb(E,{method:`POST`,headers:{"content-type":`application/json`},body:JSON.stringify({languages:[...l]})}),D(`语言已应用,TW 已重启`),r()}catch(e){D(`应用失败:${e instanceof Error?e.message:String(e)}`),w.disabled=!1}})()}),x.append(S,C,w),e.append(x)}function mb(e,t,n){let r=$(`section`,`dsh-tw-settings-section`);r.append($(`h3`,`dsh-tw-settings-h`,`知识库位置(可切换)`));let i=$(`div`,`dsh-tw-settings-muted`,`读取中…`),a=$(`div`,`dsh-tw-settings-muted`),o=$(`div`,`dsh-tw-settings-muted`),s=$(`div`,`dsh-tw-settings-muted`),c=$(`input`,`dsh-tw-settings-input`),l=$(`input`,`dsh-tw-settings-input`);c.placeholder=`绝对路径,如 D:\\notes 或 $DSH_HOME/tiddlywiki`,l.placeholder=`文件夹名(默认 main;填 . 表示直接用上面这个目录)`;let u=$(`label`,`dsh-tw-settings-field`);u.append($(`span`,`dsh-tw-settings-label`,`根目录(wikiRoot)`),c);let d=$(`label`,`dsh-tw-settings-field`);d.append($(`span`,`dsh-tw-settings-label`,`文件夹名(wiki)`),l);let f=$(`button`,`dsh-tw-settings-btn dsh-tw-settings-primary`,`切换到这个位置`);f.type=`button`;let p=$(`button`,`dsh-tw-settings-btn`,`恢复为配置默认`);p.type=`button`;let m=$(`div`,`dsh-tw-settings-row`);m.append(f,p);let h=$(`div`,`dsh-tw-settings-row dsh-tw-settings-candidates`),g=$(`div`,`dsh-tw-settings-muted`,"切换会停掉并就地重启 TW 子进程(几秒);新目录若还没有 tiddlywiki.info,插件会自动 `--init server` 初始化一个全新知识库。选中的位置会记在 $DSH_HOME 下的指针文件里(不进 wiki),可用「恢复为配置默认」清除。"),_=(e,t)=>{f.disabled=e,p.disabled=e,f.textContent=e?t:`切换到这个位置`},v=async()=>{if(!t())try{let e=await lb(ce);if(t())return;if(e.ok!==!0)throw Error(e.error??`获取失败`);let n=e.current??{};i.textContent=`当前:${n.path??`(未知)`}`;let r=n.source===`state`?`来源:指针文件(在设置页切换过)`:n.source===`config`?`来源:cordis 配置(config.wikiRoot / config.wiki)`:`来源:默认值($DSH_HOME/tiddlywiki + main)`;a.textContent=`${r} · 配置默认:${e.default?.path??`(未知)`}`,o.textContent=e.stateFile===void 0?``:`指针文件:${e.stateFile}(不存在=使用配置默认)`,s.textContent=e.error===void 0?``:`⚠️ ${e.error}`,c.value.length===0&&typeof n.root==`string`&&(c.value=n.root),l.value.length===0&&typeof n.name==`string`&&(l.value=n.name),h.replaceChildren();let u=e.candidates??[];if(u.length>0){h.append($(`span`,`dsh-tw-settings-label`,`同目录下可选的 wiki:`));for(let e of u){let t=$(`button`,`dsh-tw-settings-btn dsh-tw-settings-chipbtn`,e===`.`?`(根目录本身)`:e);t.type=`button`,t.title=`填入文件夹名「${e}」`,t.addEventListener(`click`,()=>{l.value=e}),h.append(t)}}}catch(e){if(t())return;i.textContent=`读取位置失败:${e instanceof Error?e.message:String(e)}`}};f.addEventListener(`click`,()=>{let e=c.value.trim(),t=l.value.trim().length>0?l.value.trim():`main`;if(e.length===0){D(`请先填写根目录(绝对路径)`);return}window.confirm(`切换知识库到:\n${e}\\${t}\n\n会停止并重启 TW 子进程;当前对话/工具随后读写的是新知识库。确定继续?`)&&(_(!0,`切换中…(重启 TW)`),(async()=>{try{let r=await lb(le,{method:`POST`,headers:{"content-type":`application/json`},body:JSON.stringify({root:e,name:t}),signal:AbortSignal.timeout(12e4)});r.warning===void 0?D(`已切换到 ${r.path??``}`):D(`已切换到 ${r.path??``}(注意:${r.warning})`),ye(),await v(),await n()}catch(e){D(`切换失败:${e instanceof Error?e.message:String(e)}`),await v()}finally{_(!1,``)}})())}),p.addEventListener(`click`,()=>{window.confirm(`恢复为配置默认位置?会删除位置指针文件,并(必要时)切回配置里的知识库。`)&&(_(!0,`切换中…(重启 TW)`),(async()=>{try{let e=await lb(ue,{method:`POST`,headers:{"content-type":`application/json`},body:`{}`,signal:AbortSignal.timeout(12e4)});D(e.warning===void 0?`已恢复为配置默认:${e.path??``}`:`已恢复默认(注意:${e.warning})`),ye(),await v(),await n()}catch(e){D(`恢复失败:${e instanceof Error?e.message:String(e)}`),await v()}finally{_(!1,``)}})())}),r.append(i,a,o,u,d,m,h,s,g),e.append(r),v()}function hb(e,t){let n=$(`section`,`dsh-tw-settings-section`);n.append($(`h3`,`dsh-tw-settings-h`,`初始化(一次性预置)`));let r=$(`div`,`dsh-tw-settings-muted`,`这些是插件与 dsh 联动、需要在 wiki 里预置的 tiddler/配置。「发送给 Agent 按钮」和「TW 前端 API 基址」是功能必需项,启动时自动写入(只写缺失,不覆盖你的改动);其余为可选项,默认不自动写入、也不会强绑定——需要时点「重新初始化」写入,不想要了可随时「反初始化」移除。`),i=$(`div`,`dsh-tw-settings-list`),a=$(`div`,`dsh-tw-settings-muted`),o=async()=>{if(!t())try{let e=await lb(ae);if(t())return;if(e.ok!==!0||!Array.isArray(e.items))throw Error(e.error??`获取失败`);i.replaceChildren();let n=0,r=0;for(let t of e.items){t.present&&(n+=1),t.removable&&(r+=1);let e=$(`span`,`dsh-tw-settings-chip`,t.present?`✓`:`✗`);e.dataset.state=t.present?`ok`:`missing`,e.title=t.present?`已存在`:`缺失`;let a=$(`span`,`dsh-tw-settings-name`,t.title);a.title=t.id;let s=$(`span`,`dsh-tw-settings-muted`,t.detail??t.description),c=t.updateAvailable===!0?$(`span`,`dsh-tw-settings-chip`,`⬆ 有更新`):void 0;c!==void 0&&(c.dataset.state=`update`,c.title=`内置内容在本 wiki 预置之后更新过,可点「重新初始化」取用`);let l=t.userModified===!0?$(`span`,`dsh-tw-settings-chip`,`✏️ 本地已修改`):void 0;l!==void 0&&(l.dataset.state=`missing`,l.title=`这篇是你的内容:重新初始化会覆盖它`);let u=$(`button`,`dsh-tw-settings-btn`,t.updateAvailable===!0?`更新到内置版本`:`重新初始化`);u.type=`button`,u.title=`强制重写「${t.title}」的内置内容(会覆盖当前 tiddler)`,u.addEventListener(`click`,()=>{if(t.updateAvailable===!0){let e=t.userModified===!0?`「${t.title}」有你的本地修改,更新会覆盖它。`:t.userModified===void 0?`无法确认「${t.title}」是否被你编辑过(旧格式标记),更新可能覆盖你的改动。`:``;if(!window.confirm(`${e}${e.length>0?`
|
|
765
765
|
|
|
766
766
|
`:``}用内置版本覆盖?`))return}u.disabled=!0,u.textContent=`执行中…`,(async()=>{try{let e=await fetch(oe,{method:`POST`,headers:{"content-type":`application/json`},body:JSON.stringify({id:t.id,force:!0}),signal:AbortSignal.timeout(3e4)}),n=await e.json().catch(()=>null);!e.ok||n?.ok!==!0?D(`重新初始化失败:${n?.error??n?.results?.[0]?.error??`HTTP ${e.status}`}`):D(`已重新初始化「${t.title}」`),o()}catch(e){D(`重新初始化失败:${e instanceof Error?e.message:String(e)}`)}finally{u.disabled=!1,u.textContent=t.updateAvailable===!0?`更新到内置版本`:`重新初始化`}})()});let d=$(`div`,`dsh-tw-settings-row dsh-tw-settings-plugin`);if(d.append(e,a),c!==void 0&&d.append(c),l!==void 0&&d.append(l),d.append(s,u),t.removable){let e=$(`button`,`dsh-tw-settings-btn dsh-tw-settings-danger`,`反初始化`);e.type=`button`,e.title=`移除「${t.title}」写入的 tiddler 与 marker(恢复未初始化状态)`,e.addEventListener(`click`,()=>{window.confirm(`确定反初始化「${t.title}」?将删除它写入的 tiddler(含其一次性 marker),需要时可用「重新初始化」恢复。`)&&(e.disabled=!0,e.textContent=`移除中…`,(async()=>{try{let e=await fetch(se,{method:`POST`,headers:{"content-type":`application/json`},body:JSON.stringify({id:t.id}),signal:AbortSignal.timeout(3e4)}),n=await e.json().catch(()=>null);!e.ok||n?.ok!==!0?D(`反初始化失败:${n?.error??n?.results?.[0]?.error??`HTTP ${e.status}`}`):D(`已反初始化「${t.title}」`),o()}catch(e){D(`反初始化失败:${e instanceof Error?e.message:String(e)}`)}finally{e.disabled=!1,e.textContent=`反初始化`}})())}),d.append(e)}i.append(d)}a.textContent=`共 ${e.items.length} 项,${n} 项已就绪;可移除 ${r} 项`}catch(e){if(t())return;a.textContent=`加载初始化状态失败:${e instanceof Error?e.message:String(e)}`}},s=$(`button`,`dsh-tw-settings-btn dsh-tw-settings-primary`,`全部重新初始化`);s.type=`button`,s.title=`强制重写所有一次性预置内容(含可选 seed,会覆盖当前 tiddler/配置)`,s.addEventListener(`click`,()=>{s.disabled=!0,s.textContent=`执行中…`,(async()=>{try{let e=await fetch(oe,{method:`POST`,headers:{"content-type":`application/json`},body:JSON.stringify({force:!0}),signal:AbortSignal.timeout(6e4)}),t=await e.json().catch(()=>null);!e.ok||t?.ok!==!0?D(`重新初始化失败:${t?.error??`HTTP ${e.status}`}`):D(`全部已重新初始化`),o()}catch(e){D(`重新初始化失败:${e instanceof Error?e.message:String(e)}`)}finally{s.disabled=!1,s.textContent=`全部重新初始化`}})()});let c=$(`button`,`dsh-tw-settings-btn dsh-tw-settings-danger`,`全部反初始化`);c.type=`button`,c.title=`移除所有可选 seed 写入的 tiddler 与 marker(功能必需项保留)`,c.addEventListener(`click`,()=>{window.confirm(`确定全部反初始化?将删除所有可选 seed 写入的 tiddler(含一次性 marker)。功能必需项(发送给 Agent 按钮 / TW 前端 API 基址)会保留。`)&&(c.disabled=!0,c.textContent=`移除中…`,(async()=>{try{let e=await fetch(se,{method:`POST`,headers:{"content-type":`application/json`},body:JSON.stringify({}),signal:AbortSignal.timeout(6e4)}),t=await e.json().catch(()=>null);!e.ok||t?.ok!==!0?D(`反初始化失败:${t?.error??`HTTP ${e.status}`}`):D(`全部可选 seed 已反初始化`),o()}catch(e){D(`反初始化失败:${e instanceof Error?e.message:String(e)}`)}finally{c.disabled=!1,c.textContent=`全部反初始化`}})())}),n.append(r,a,i,s,c),e.append(n),o()}function gb(e,t,n,r,i){if(e.replaceChildren(),typeof t.configError==`string`&&t.configError.length>0){let n=$(`div`,`dsh-tw-settings-banner`);n.dataset.tone=`error`,n.setAttribute(`style`,`border:1px solid #c0392b;background:#fdecea;color:#8c1c13;border-radius:6px;padding:10px 12px;margin:0 0 12px;font-size:12px;line-height:1.6;white-space:pre-wrap;`),n.textContent=`⚠️ 配置未生效:${t.configError}`,e.append(n)}let a=JSON.stringify(t.config??{});if(i.host===void 0||i.signature!==a){let e=$(`div`,`dsh-tw-settings-confighost`);fb(e,t.config??{},n),i.host=e,i.signature=a}e.append(i.host),mb(e,r,n),pb(e,t.info,t.catalog,n),hb(e,r)}function _b(){let e=c.useRef(null);return c.useEffect(()=>{let t=e.current;return t===null?void 0:ub(t)},[]),c.createElement(`div`,{ref:e})}const vb={tiddlywiki_get:`读取笔记`,tiddlywiki_search:`检索笔记`,tiddlywiki_recent:`最近修改`,tiddlywiki_list_tags:`标签列表`,tiddlywiki_put:`写入笔记`,tiddlywiki_batch_put:`批量写入`,tiddlywiki_append:`增量写入`,tiddlywiki_rename:`重命名笔记`,tiddlywiki_delete:`删除笔记`,tiddlywiki_trash:`回收站`,tiddlywiki_backlinks:`反向链接`,tiddlywiki_attach:`保存附件`,tiddlywiki_lint:`知识库体检`,tiddlywiki_git_sync:`git 同步`,tiddlywiki_git_resolve:`git 冲突解决`};function yb(e){return typeof e==`object`&&!!e&&e.kind===`tool-result`}function bb(e){if(typeof e!=`object`||!e)return null;let t=e;if(typeof t.call==`object`&&t.call!==null){let e=t.call;if(typeof e.name==`string`&&typeof e.argsRaw==`string`)return e}let n=e;return typeof n.name==`string`&&typeof n.argsRaw==`string`?{name:n.name,argsRaw:n.argsRaw}:null}function xb(e){try{let t=JSON.parse(e);return typeof t==`object`&&t&&!Array.isArray(t)?t:{}}catch{return{}}}function Sb(e){return Array.isArray(e)?e.filter(e=>typeof e?.text==`string`).map(e=>e.text).join(`
|
|
767
767
|
`):``}function Cb(e){return typeof e==`string`?e:``}async function wb(e){try{let t=await fetch(e,{signal:AbortSignal.timeout(1e4)});if(t.status===404){let e=await t.json().catch(()=>null);return typeof e==`object`&&e?e:{notFound:!0}}if(!t.ok)return null;let n=await t.json();return typeof n==`object`&&n?n:null}catch{return null}}async function Tb(e){return ib(e,1e4)}function Eb(e,t){let[n,r]=c.useState({loading:!0,data:null});return c.useEffect(()=>{let t=!0;return r({loading:!0,data:null}),Promise.resolve(e()).then(e=>{t&&r({loading:!1,data:e})},()=>{t&&r({loading:!1,data:null})}),()=>{t=!1}},t),n}function Db(e){return t=>{t.defaultPrevented||t.button!==0||t.metaKey||t.ctrlKey||t.shiftKey||t.altKey||(t.preventDefault(),t.stopPropagation(),mt(e))}}function Ob(e){let t=vb[e.toolName]??e.toolName,n=c.createElement(`div`,{className:`dsh-tw-toolcard-head`},c.createElement(`span`,{className:`dsh-tw-toolcard-badge`},t),e.title!==void 0&&e.title.length>0?c.createElement(`span`,{className:`dsh-tw-toolcard-title`,title:e.title},e.title):null,e.onOpen===void 0?null:c.createElement(`button`,{type:`button`,className:`dsh-tw-toolcard-open`,onClick:e.onOpen},`在 TW 打开`)),r=e.subtitle!==void 0||e.tags!==void 0&&e.tags.length>0?c.createElement(`div`,{className:`dsh-tw-toolcard-meta`},e.subtitle===void 0?null:c.createElement(`span`,{className:`dsh-tw-toolcard-sub`},e.subtitle),e.tags!==void 0&&e.tags.length>0?c.createElement(`span`,{className:`dsh-tw-toolcard-tags`},...e.tags.map((e,t)=>c.createElement(`span`,{className:`dsh-tw-toolcard-tag`,key:`${t}-${e}`},e))):null):null;return c.createElement(`div`,{className:`dsh-tw-toolcard`},n,r,c.createElement(`div`,{className:`dsh-tw-toolcard-body`},e.children),e.foot!==void 0&&e.foot!==null?c.createElement(`div`,{className:`dsh-tw-toolcard-foot`},e.foot):null)}const kb=new Map;function Ab(e){let t=kb.get(e);if(t!==void 0){if(Date.now()-t.at>3e5){kb.delete(e);return}return kb.delete(e),kb.set(e,t),t}}function jb(e,t){for(kb.delete(e),kb.set(e,{at:Date.now(),...t});kb.size>50;){let e=kb.keys().next().value;if(e===void 0)break;kb.delete(e)}}function Mb(e){e.length>0&&kb.delete(e)}function Nb(e){let t=e.join(`\0`);c.useEffect(()=>{for(let e of t.split(`\0`))Mb(e)},[t])}function Pb(e){let{title:t,fresh:n}=e,{loading:r,data:i}=Eb(async()=>{n===!0&&Mb(t);let e=Ab(t);if(e!==void 0)return{get:e.get,html:e.html};let[r,i]=await Promise.all([wb(`${g}?title=${encodeURIComponent(t)}`),Tb(t)]);return typeof i==`string`&&i.length>0&&jb(t,{get:r,html:i}),{get:r,html:i}},[t,n]),a=i?.get??null,o=i?.html??null,s;s=r?c.createElement(`div`,{className:`dsh-tw-toolcard-loading`},`渲染中…`):a!==null&&a.notFound===!0?c.createElement(`div`,{className:`dsh-tw-toolcard-empty`},`tiddler「${t}」不存在`):typeof o==`string`&&o.length>0?c.createElement(`div`,{className:`dsh-tw-toolcard-native`,dangerouslySetInnerHTML:{__html:o}}):a!==null&&typeof a.text==`string`&&a.text.length>0?c.createElement(`pre`,{className:`dsh-tw-toolcard-fallback`},a.text):c.createElement(`div`,{className:`dsh-tw-toolcard-empty`},`wiki 服务不可用`);let l=Array.isArray(a?.tags)?a.tags.filter(e=>typeof e==`string`):[],u=typeof a?.modified==`string`?a.modified:void 0;return c.createElement(Ob,{toolName:e.toolName,title:t,subtitle:e.subtitle,tags:l,onOpen:Db(t),foot:u===void 0?void 0:`修改于 ${u}`},s)}function Fb(e){let t=c.createElement(`span`,{className:`dsh-tw-toolcard-row-head`},c.createElement(`span`,{className:`dsh-tw-toolcard-row-title`},e.title),e.tags!==void 0&&e.tags.length>0?c.createElement(`span`,{className:`dsh-tw-toolcard-row-tags`},e.tags.slice(0,4).join(` · `)):null,typeof e.modified==`string`&&e.modified.length>0?c.createElement(`span`,{className:`dsh-tw-toolcard-row-meta`},e.modified):null),n=typeof e.snippet==`string`&&e.snippet.trim().length>0?e.snippet:null;return c.createElement(`a`,{className:`dsh-tw-toolcard-row`,href:`${m}#${encodeURIComponent(e.title)}`,onClick:Db(e.title),title:n===null?e.title:`${e.title}\n${n}`},t,n===null?null:c.createElement(`span`,{className:`dsh-tw-toolcard-row-snippet`},n))}function Ib(e){let t=e.rows.slice(0,60),n;return n=t.length===0?c.createElement(`div`,{className:`dsh-tw-toolcard-empty`},e.fallbackText):c.createElement(`div`,{className:`dsh-tw-toolcard-list`},...t.map((e,t)=>c.createElement(Fb,{key:`${e.title}-${t}`,...e}))),c.createElement(Ob,{toolName:e.toolName,title:e.title,subtitle:e.subtitle},n)}function Lb(e){let t=Cb(e.args.query),n=new URLSearchParams;if(t.length>0&&n.set(`query`,t),Array.isArray(e.args.tags))for(let t of e.args.tags)typeof t==`string`&&t.length>0&&n.append(`tags`,t);typeof e.args.tag==`string`&&e.args.tag.length>0&&n.set(`tag`,e.args.tag),typeof e.args.since==`string`&&e.args.since.length>0&&n.set(`since`,e.args.since),typeof e.args.type==`string`&&e.args.type.length>0&&n.set(`type`,e.args.type),typeof e.args.field==`string`&&e.args.field.length>0&&n.set(`field`,e.args.field),typeof e.args.value==`string`&&e.args.value.length>0&&n.set(`value`,e.args.value),typeof e.args.limit==`number`&&n.set(`limit`,String(e.args.limit));let r=n.toString(),i=Eb(()=>wb(`${b}?${r}`),[r]).data,a=(Array.isArray(i?.items)?i.items:[]).map(e=>({title:Cb(e.title),tags:Array.isArray(e.tags)?e.tags.filter(e=>typeof e==`string`):[],modified:typeof e.modified==`string`?e.modified:null,snippet:typeof e.snippet==`string`?e.snippet:``})),o=typeof i?.total==`number`?i.total:void 0,s=`关键词「${t||`(全部)`}」${o===void 0?``:` · 共 ${o} 条`}`;return c.createElement(Ib,{toolName:e.toolName,title:t.length>0?t:`检索`,subtitle:s,rows:a,fallbackText:`没有匹配的笔记`})}function Rb(e){let t=new URLSearchParams;typeof e.args.limit==`number`&&t.set(`limit`,String(e.args.limit)),typeof e.args.since==`string`&&e.args.since.length>0&&t.set(`since`,e.args.since);let n=t.toString(),r=Eb(()=>wb(`${te}?${n}`),[n]).data,i=(Array.isArray(r?.items)?r.items:[]).map(e=>({title:Cb(e.title),tags:Array.isArray(e.tags)?e.tags.filter(e=>typeof e==`string`):[],modified:typeof e.modified==`string`?e.modified:null,snippet:typeof e.snippet==`string`?e.snippet:``}));return c.createElement(Ib,{toolName:e.toolName,title:`最近修改`,subtitle:i.length>0?`最近 ${i.length} 条`:``,rows:i,fallbackText:`暂无笔记`})}function zb(e){let t=(Array.isArray(e.args.items)?e.args.items:[]).map(e=>typeof e==`object`&&e&&typeof e.title==`string`?Cb(e.title):null).filter(e=>e!==null&&e.length>0).map(e=>({title:e}));return Nb(t.map(e=>e.title)),c.createElement(Ib,{toolName:e.toolName,title:`批量写入`,subtitle:t.length>0?`写入 ${t.length} 篇`:`(参数中没有可解析的 items)`,rows:t,fallbackText:`没有可展示的笔记`})}function Bb(e){let t=Eb(()=>wb(`${ee}?limit=60&sort=count`),[]).data,n=(Array.isArray(t?.items)?t.items:[]).map(e=>({tag:Cb(e.tag),count:typeof e.count==`number`?e.count:0})),r=n.slice(0,60),i=typeof t?.total==`number`?t.total:n.length,a;return a=n.length===0?c.createElement(`div`,{className:`dsh-tw-toolcard-empty`},`暂无标签`):c.createElement(`div`,{className:`dsh-tw-toolcard-tags-wrap`},...r.map(e=>c.createElement(`span`,{className:`dsh-tw-toolcard-tag`,key:e.tag},`${e.tag} · ${e.count}`)),...i>r.length?[c.createElement(`span`,{className:`dsh-tw-toolcard-tag-more`,key:`__more`},`…另有 ${i-r.length} 个`)]:[]),c.createElement(Ob,{toolName:e.toolName,title:`标签`,subtitle:`共 ${i} 个`},a)}function Vb(e){let t=e.text.trim().length>0?c.createElement(`pre`,{className:`dsh-tw-toolcard-fallback`},e.text):c.createElement(`div`,{className:`dsh-tw-toolcard-empty`},`(无结果)`);return c.createElement(Ob,{toolName:e.toolName},t)}function Hb(e){Nb([e.title]);let t=e.title.length>0?c.createElement(`div`,{className:`dsh-tw-toolcard-empty`},`已删除 tiddler「${e.title}」`):c.createElement(`pre`,{className:`dsh-tw-toolcard-fallback`},e.text||`(已删除)`);return c.createElement(Ob,{toolName:e.toolName,title:e.title.length>0?e.title:void 0},t)}function Ub(e,t){switch(e){case`tiddlywiki_get`:case`tiddlywiki_put`:case`tiddlywiki_append`:case`tiddlywiki_delete`:case`tiddlywiki_attach`:case`tiddlywiki_backlinks`:return Cb(t.title);case`tiddlywiki_rename`:return Cb(t.newTitle);case`tiddlywiki_search`:return Cb(t.query);default:return``}}function Wb(e){let t=e.block,n=e.toolName,r=yb(t),i=bb(t),a=n!==void 0&&n.length>0?n:i?.name??``,o=xb(i?.argsRaw??``),s=r?Sb(t.content):``;if(i===null)return c.createElement(Ob,{toolName:a},c.createElement(`pre`,{className:`dsh-tw-toolcard-fallback`},s||`(无调用信息)`));if(!r)return c.createElement(Ob,{toolName:a,title:Ub(a,o)},c.createElement(`div`,{className:`dsh-tw-toolcard-pending`},`处理中…`));if(t.isError===!0)return c.createElement(Ob,{toolName:a,title:Ub(a,o)},c.createElement(`div`,{className:`dsh-tw-toolcard-error`},s||`工具调用失败`));switch(a){case`tiddlywiki_get`:return c.createElement(Pb,{toolName:a,title:Cb(o.title),subtitle:`读取`});case`tiddlywiki_put`:return c.createElement(Pb,{toolName:a,title:Cb(o.title),subtitle:`已写入`,fresh:!0});case`tiddlywiki_append`:return c.createElement(Pb,{toolName:a,title:Cb(o.title),subtitle:`已增量写入`,fresh:!0});case`tiddlywiki_rename`:return c.createElement(Pb,{toolName:a,title:Cb(o.newTitle),subtitle:`已重命名「${Cb(o.oldTitle)}」→`,fresh:!0});case`tiddlywiki_delete`:return c.createElement(Hb,{toolName:a,title:Cb(o.title),text:s});case`tiddlywiki_batch_put`:return c.createElement(zb,{toolName:a,args:o});case`tiddlywiki_search`:return c.createElement(Lb,{toolName:a,args:o});case`tiddlywiki_recent`:return c.createElement(Rb,{toolName:a,args:o});case`tiddlywiki_list_tags`:return c.createElement(Bb,{toolName:a});case`tiddlywiki_git_sync`:case`tiddlywiki_git_resolve`:return c.createElement(Vb,{toolName:a,text:s});default:return c.createElement(Ob,{toolName:a,title:Ub(a,o)},c.createElement(`pre`,{className:`dsh-tw-toolcard-fallback`},s||`(完成)`))}}const Gb=[`tiddlywiki_get`,`tiddlywiki_search`,`tiddlywiki_recent`,`tiddlywiki_list_tags`,`tiddlywiki_put`,`tiddlywiki_batch_put`,`tiddlywiki_append`,`tiddlywiki_rename`,`tiddlywiki_delete`,`tiddlywiki_trash`,`tiddlywiki_backlinks`,`tiddlywiki_attach`,`tiddlywiki_lint`,`tiddlywiki_git_sync`,`tiddlywiki_git_resolve`];function Kb(e){let t=[];for(let n of Gb)try{let r=e.inject(`tool.call.toolview`,()=>e.register({name:`tool.call.toolview`,id:`dsh-tiddlywiki-toolview-${n}`,key:n,order:20,label:`TiddlyWiki: ${n}`},Wb));r!==void 0&&t.push(r)}catch(e){console.error(`[dsh-tiddlywiki] register toolview ${n} failed:`,e)}return t}function qb(){let e=RegExp(`^${m.replace(/[.*+?^${}()|[\]\\]/g,`\\$&`)}#(.+)$`),t=t=>{if(t.defaultPrevented||t.button!==0||t.metaKey||t.ctrlKey||t.shiftKey||t.altKey)return;let n=t.target;if(!(n instanceof Element))return;let r=n.closest(`a`);if(r===null)return;let i=(r.getAttribute(`href`)??``).match(e);if(i===null||i[1]===void 0)return;t.preventDefault(),t.stopPropagation();let a=i[1];try{a=decodeURIComponent(a)}catch{}mt(a)};return document.addEventListener(`click`,t,!0),()=>document.removeEventListener(`click`,t,!0)}const Jb=`dsh-tiddlywiki`,Yb=`dsh-tiddlywiki/rightbar`;function Xb(e){let{useTabInfo:t}=e,n=t().tab,r=c.useRef(null),i=c.useRef(null);c.useEffect(()=>{let e=r.current;if(e===null)return;let t=ot(e,n.signal);return i.current=t,()=>{t.dispose(),i.current=null}},[n.id,n.signal]),c.useEffect(()=>{i.current?.setVisible(n.visible)},[n.visible]);let a=n.visible,o=n.actions.close;return c.useEffect(()=>{if(!a)return;document.dispatchEvent(new CustomEvent(Ke,{detail:Yb}));let e=e=>{e.detail!==Yb&&o()};return document.addEventListener(Ke,e),()=>document.removeEventListener(Ke,e)},[a,o]),c.createElement(`div`,{ref:r,className:`dsh-tw-rightbar-tab`})}function Zb(){return{id:Jb,kind:`dsh-tiddlywiki`,title:()=>Xe(),guide:[{order:10,title:()=>`TiddlyWiki 知识库`,description:()=>`在右侧边栏打开 TiddlyWiki 编辑器(与聊天并排)`,icon:Qe}]}}function Qb(e,t){let n=[];try{n.push(e.register(Zb()));let r=t.inject(`sidebar.right.pane.tab`,()=>t.register({name:`sidebar.right.pane.tab`,key:Jb},Xb));r!==void 0&&n.push(r)}catch(e){for(let e of n.splice(0))e();console.error(`[dsh-tiddlywiki] rightbar tab mount failed:`,e);return}return it(),()=>{for(let e of n.splice(0))e()}}const $b=[`slots`];function ex(e){try{let t=[],n=!1,r=()=>{n=!0;for(let e of t.splice(0))e()},i=(e,t)=>{try{t()}catch(t){console.error(`[dsh-tiddlywiki] ${e} failed:`,t)}};i(`styles`,()=>t.push(d()));let a=new f,o,s;if(i(`quick-note widget`,()=>{o=Yy(),t.push(()=>o?.dispose())}),i(`sync controller`,()=>{s=$y(),t.push(()=>s?.dispose())}),i(`sidebar entry`,()=>t.push(Te(a))),i(`center panel`,()=>t.push(_t(a))),o!==void 0&&s!==void 0){let e=o,n=s;i(`knowledge FAB`,()=>t.push(nb(a,e,n)))}i(`editor popup`,()=>t.push(kt)),e.slots!==void 0&&be().then(r=>{if(n)return;let a=o;r.showQuickNoteDock&&a!==void 0&&i(`quick-note dock`,()=>{let n=e.slots?.inject(`conversation.input.dock`,()=>e.slots?.register({name:`conversation.input.dock`,id:`quick-note`,order:8,label:`快速笔记`},rb(a)));n!==void 0&&t.push(n)}),i(`session summary tab`,()=>{let n=cb(e.slots,r);n!==void 0&&t.push(n)})}).catch(e=>{console.error(`[dsh-tiddlywiki] ui config load failed:`,e)}),i(`rightbar mount`,()=>{let r,i=a=>{let o=e.get?.(`sidebarRightTabs`);if(o===void 0){a<6&&!n&&(r=window.setTimeout(()=>i(a+1),500*(a+1)));return}be().then(r=>{if(n||!r.showRightbarTab)return;let i=Qb(o,e.slots);i!==void 0&&t.push(i)}).catch(e=>{console.error(`[dsh-tiddlywiki] rightbar config load failed:`,e)})};t.push(()=>{r!==void 0&&window.clearTimeout(r)}),i(0)}),i(`tool views`,()=>{e.slots!==void 0&&t.push(...Kb(e.slots))}),i(`wiki link interceptor`,()=>t.push(qb())),i(`settings section`,()=>{let n=e.slots?.inject(`settings.section`,()=>e.slots?.register({name:`settings.section`,id:`dsh-tiddlywiki`,order:50,label:`TiddlyWiki 知识库`},_b));n!==void 0&&t.push(n)}),e.effect===void 0?window.addEventListener(`pagehide`,r,{once:!0}):e.effect(()=>r,`dsh-tiddlywiki: client mount`)}catch(e){console.error(`[dsh-tiddlywiki] client half failed to start:`,e)}}exports.apply=ex,exports.inject=$b,exports.name=`dsh-tiddlywiki/client`;
|