dsh-tiddlywiki 0.16.19 → 0.16.20
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/LICENSE +21 -21
- package/README.md +4 -1
- package/cordis.patch.yml +13 -13
- package/lib/index.js +133 -24
- package/lib/index.js.map +1 -1
- package/package.json +1 -1
- package/src/client/endpoints.ts +10 -10
- package/src/client/markdown-editor.ts +128 -128
- package/src/client/session-summary.ts +279 -279
- package/src/client/state.ts +39 -39
- package/src/client/theme-sync.ts +312 -312
- package/src/client/toast.ts +22 -22
- package/src/client/tool-views.ts +604 -604
- package/src/client/ui-config.ts +61 -61
- package/src/host/http.ts +57 -57
- package/src/host/seed-home.ts +1 -1
- package/src/host/seed-render.ts +66 -66
- package/src/host/seed-send-to-agent.ts +63 -63
- package/src/host/tools.ts +40 -5
- package/src/host/tw-api.ts +108 -16
- package/src/host/wiki.ts +299 -299
- package/src/index.ts +4 -4
- package/src/sdk.ts +204 -204
package/LICENSE
CHANGED
|
@@ -1,21 +1,21 @@
|
|
|
1
|
-
MIT License
|
|
2
|
-
|
|
3
|
-
Copyright (c) 2026 dsh-tiddlywiki contributors
|
|
4
|
-
|
|
5
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
-
in the Software without restriction, including without limitation the rights
|
|
8
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
-
furnished to do so, subject to the following conditions:
|
|
11
|
-
|
|
12
|
-
The above copyright notice and this permission notice shall be included in all
|
|
13
|
-
copies or substantial portions of the Software.
|
|
14
|
-
|
|
15
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
-
SOFTWARE.
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 dsh-tiddlywiki contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
|
|
13
13
|
| 能力 | 说明 |
|
|
14
14
|
|---|---|
|
|
15
|
-
| 🤖 **Agent 工具** | 10 个 `tiddlywiki_*` 工具:检索、读写、批量、重命名、删除、git
|
|
15
|
+
| 🤖 **Agent 工具** | 10 个 `tiddlywiki_*` 工具:检索、读写、批量、重命名、删除、git 同步与冲突解决(v0.16.20 起检索/最近在**服务端**排除二进制附件,大 wiki 上从 515MB/17s 降到 ~0.4s) |
|
|
16
16
|
| 📊 **回复流卡片** | 工具结果显示原生 TW 卡片;`[标题](/dsh-tiddlywiki/tw/#标题)` 点击直达 TW 面板 |
|
|
17
17
|
| 📤 **发送给 Agent** | TW 笔记工具栏一键把当前笔记注入所选 dsh 会话(可选工作模式/权限/附加说明) |
|
|
18
18
|
| 🧭 **内嵌编辑器** | 中央列内嵌完整 TW 5 编辑器(同源代理,Tailscale/内网/域名/HTTPS 均可) |
|
|
@@ -74,6 +74,8 @@ dsh plugin --profile web add link:/path/to/dsh-tiddlywiki
|
|
|
74
74
|
2. 冲突后用 `tiddlywiki_git_resolve` 二选一解决,再重新 sync(**绝不自动覆盖**)。
|
|
75
75
|
3. 收工 `tiddlywiki_git_sync action=sync`(pull → commit → push)。
|
|
76
76
|
|
|
77
|
+
> 🔍 **检索/最近跳过二进制附件**(v0.16.20):`search`/`recent` 在**服务端**只取文本 tiddler(无 `type` 字段或 `text/*`),图片/音频/视频/PDF/zip 等二进制 tiddler(base64 正文)不参与检索、不出现在结果里——大 wiki(如数千张书籍扫描页图)从此从 515MB/17s 降到 ~0.4s,也不会被同名书页图刷屏。`get` 对二进制 tiddler 只返回元数据(`binary=true` + 类型/大小 + 链接),不返回 base64 正文。
|
|
78
|
+
|
|
77
79
|
> ⚠️ `fields.type` 是 TW 的**内容类型保留字段**(`text/markdown` 等),业务分类请放 `tags`,别写进 `fields.type`。
|
|
78
80
|
|
|
79
81
|
### 🧑💻 界面操作
|
|
@@ -225,6 +227,7 @@ lib/ # 预构建产物(发布含 lib/**,提交入库;
|
|
|
225
227
|
|
|
226
228
|
> 最近几个主要版本的一句话记录(完整变更见 [Releases](https://github.com/bbqisbbq/dsh-tiddlywiki/releases) / git log)。
|
|
227
229
|
|
|
230
|
+
- **v0.16.20**(2026-09-09):**修:`search`/`recent` 在大 wiki 上超时**。根因:列表请求把全部 tiddler(含图片等二进制附件)的 base64 正文拉回来(2418 个 tiddler ≈ 515MB/17s,超过 10s 请求超时)。修复:`search`/`recent`/`get` 在**服务端**只取文本 tiddler(无 `type` 或 `text/*`)——用外部 filter `[all[tiddlers]!is[system]!has[type]] [all[tiddlers]!is[system]regexp:type[(?i)^text/]]`(含 `$:/config/Server/ExternalFilters/<filter>` 白名单自愈,403 时自动 PUT 重试,仍 403 降级瘦身列表);二进制 tiddler 完全排除(含标题命中,防同名书页图刷屏),`get` 对二进制只回元数据(`binary=true`/`binaryType`/`binaryChars` + 链接)。实测线上 wiki(2418 tiddler/958 图):515MB/17s → **6.24MB/0.3s**,图片零泄漏。⚠️ 白名单 tiddler 文件名含整个 filter 串——filter 必须短(此前 180 字符版在 Windows 上把文件名顶到 217 字符,撑爆 MAX_PATH 使 `git add` 报 "Filename too long"、自动 commit 失效;selftest 已把该场景纳入回归)。代价:`application/json` 等类型 tiddler 不进检索(本 wiki 无此类,且多为配置数据)。
|
|
228
231
|
- **v0.16.19**(2026-09-07):**修:会话「知识库」Tab 把汇总当源码显示(整页 wikitext、像包在代码标签里)**。根因(headless $tw 实测):TW 5.4.1 核心的视图模板级联(`$:/config/ViewTemplateBodyFilters/system` 的 system 规则)把所有 `$:/temp/` 前缀 tiddler 一律按**代码块**渲染(`$:/core/ui/ViewTemplate/body/code` → `<pre><code>`)——因此即便 v0.16.14+ 把 volatile 条目注入 iframe store 再原生导航,story view 仍把汇总当源码展示。修复:客户端**不再用 iframe / story view**,改走与回复流工具卡同一条原生渲染管线——`POST /tw/render {title}`(服务端把汇总 wikitext 块解析成 HTML 片段,`[[链接]]` 重写为 `/dsh-tiddlywiki/tw/#标题`)→ 注入滚动容器(样式复用 `.dsh-tw-toolcard-native` 的 tc-* 重主题);片段内链接点击仍打开中央 TW 面板。无 iframe → 无编辑按钮/草稿,v0.16.16 的三层防误编辑随之不再需要;保留 30s 自愈(volatile 条目被清自动重建)与「🔄 刷新」。**刷新页面**即生效。
|
|
229
232
|
- **v0.16.18**(2026-09-07):README 全面精简重写;seed 优化——首页 seed 跟随 wiki 现状(🏠 主页)、修「所有文章」回主页死链、doc-note 文案修正。
|
|
230
233
|
- **v0.16.17**(2026-09-07):点击快速笔记可选直达 TW 原生编辑页(`ui.quickNoteMode`)+ 修「在 TW 中编辑」弹窗 ✕ 关不掉。
|
package/cordis.patch.yml
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
# dsh-tiddlywiki bundle patch: inserts the plugin row into the web profile
|
|
2
|
-
# roster. Applied as a profile bundle layer (the `dsh.bundle.patch` manifest
|
|
3
|
-
# field) over dsh-base; activate with
|
|
4
|
-
# `dsh plugin --profile web add link:<path-to-this-package>` and restart dsh web.
|
|
5
|
-
#
|
|
6
|
-
# The row is a bare plugin by package name: the node half (exports ".") runs
|
|
7
|
-
# in the host process (WikiServer, routes, tiddlywiki_* tools, prompt section,
|
|
8
|
-
# auto-commit), and the `dsh.client` declaration in package.json makes the
|
|
9
|
-
# browser half (exports "./client", served at /plugins/dsh-tiddlywiki/client.js)
|
|
10
|
-
# load in the web GUI.
|
|
11
|
-
- insert:
|
|
12
|
-
- id: dsh-tiddlywiki
|
|
13
|
-
name: dsh-tiddlywiki
|
|
1
|
+
# dsh-tiddlywiki bundle patch: inserts the plugin row into the web profile
|
|
2
|
+
# roster. Applied as a profile bundle layer (the `dsh.bundle.patch` manifest
|
|
3
|
+
# field) over dsh-base; activate with
|
|
4
|
+
# `dsh plugin --profile web add link:<path-to-this-package>` and restart dsh web.
|
|
5
|
+
#
|
|
6
|
+
# The row is a bare plugin by package name: the node half (exports ".") runs
|
|
7
|
+
# in the host process (WikiServer, routes, tiddlywiki_* tools, prompt section,
|
|
8
|
+
# auto-commit), and the `dsh.client` declaration in package.json makes the
|
|
9
|
+
# browser half (exports "./client", served at /plugins/dsh-tiddlywiki/client.js)
|
|
10
|
+
# load in the web GUI.
|
|
11
|
+
- insert:
|
|
12
|
+
- id: dsh-tiddlywiki
|
|
13
|
+
name: dsh-tiddlywiki
|
package/lib/index.js
CHANGED
|
@@ -2704,7 +2704,7 @@ const HOME_INDEX_ITEMS = [
|
|
|
2704
2704
|
"title": "🏠 主页",
|
|
2705
2705
|
"tags": ["索引", "TableOfContents"],
|
|
2706
2706
|
"type": "text/vnd.tiddlywiki",
|
|
2707
|
-
"text": "\\whitespace trim\n\n\\define quadrant-board()\n<div style=\"display:grid; grid-template-columns:1fr 1fr; gap:10px; margin-top:8px;\">\n<div style=\"border:1px solid rgba(128,128,128,0.22); border-radius:10px; padding:8px 12px; background:linear-gradient(135deg,#e74c3c1a,transparent);\">\n<div style=\"font-weight:700; margin-bottom:6px;\">🔴 重要 · 紧急 <span style=\"color:#888; font-weight:400; font-size:0.82em;\">{{{[tag[todo]!tag[done]field:q[q1]count[]]}}} 件</span></div>\n<$list filter=\"[tag[todo]!tag[done]field:q[q1]sort[modified]]\">\n<div style=\"display:flex; gap:6px; align-items:baseline; padding:3px 0; border-bottom:1px dashed rgba(128,128,128,0.15);\">\n<$checkbox tiddler=<<currentTiddler>> tag=\"done\"/>\n<$link to=<<currentTiddler>> style=\"flex:1;\"><$view field=\"title\"/></$link>\n<$list filter=\"[<currentTiddler>get[due]compare:date:lt<today>]\"><span style=\"color:#c0392b; font-size:0.78em;\">逾期</span></$list>\n<$list filter=\"[<currentTiddler>get[due]!is[blank]]\"><span style=\"color:#888; font-size:0.8em;\"><$view field=\"due\" format=\"date\" template=\"MM-DD\"/></span></$list>\n</div>\n</$list>\n<$list filter=\"[tag[todo]!tag[done]field:q[q1]!count[]]\"><div style=\"color:#aaa; font-size:0.85em;\">(暂无)</div></$list>\n</div>\n<div style=\"border:1px solid rgba(128,128,128,0.22); border-radius:10px; padding:8px 12px; background:linear-gradient(135deg,#3498db1a,transparent);\">\n<div style=\"font-weight:700; margin-bottom:6px;\">🔵 重要 · 不紧急 <span style=\"color:#888; font-weight:400; font-size:0.82em;\">{{{[tag[todo]!tag[done]field:q[q2]count[]]}}} 件</span></div>\n<$list filter=\"[tag[todo]!tag[done]field:q[q2]sort[modified]]\">\n<div style=\"display:flex; gap:6px; align-items:baseline; padding:3px 0; border-bottom:1px dashed rgba(128,128,128,0.15);\">\n<$checkbox tiddler=<<currentTiddler>> tag=\"done\"/>\n<$link to=<<currentTiddler>> style=\"flex:1;\"><$view field=\"title\"/></$link>\n<$list filter=\"[<currentTiddler>get[due]compare:date:lt<today>]\"><span style=\"color:#c0392b; font-size:0.78em;\">逾期</span></$list>\n<$list filter=\"[<currentTiddler>get[due]!is[blank]]\"><span style=\"color:#888; font-size:0.8em;\"><$view field=\"due\" format=\"date\" template=\"MM-DD\"/></span></$list>\n</div>\n</$list>\n<$list filter=\"[tag[todo]!tag[done]field:q[q2]!count[]]\"><div style=\"color:#aaa; font-size:0.85em;\">(暂无)</div></$list>\n</div>\n<div style=\"border:1px solid rgba(128,128,128,0.22); border-radius:10px; padding:8px 12px; background:linear-gradient(135deg,#f39c121a,transparent);\">\n<div style=\"font-weight:700; margin-bottom:6px;\">🟠 紧急 · 不重要 <span style=\"color:#888; font-weight:400; font-size:0.82em;\">{{{[tag[todo]!tag[done]field:q[q3]count[]]}}} 件</span></div>\n<$list filter=\"[tag[todo]!tag[done]field:q[q3]sort[modified]]\">\n<div style=\"display:flex; gap:6px; align-items:baseline; padding:3px 0; border-bottom:1px dashed rgba(128,128,128,0.15);\">\n<$checkbox tiddler=<<currentTiddler>> tag=\"done\"/>\n<$link to=<<currentTiddler>> style=\"flex:1;\"><$view field=\"title\"/></$link>\n<$list filter=\"[<currentTiddler>get[due]compare:date:lt<today>]\"><span style=\"color:#c0392b; font-size:0.78em;\">逾期</span></$list>\n<$list filter=\"[<currentTiddler>get[due]!is[blank]]\"><span style=\"color:#888; font-size:0.8em;\"><$view field=\"due\" format=\"date\" template=\"MM-DD\"/></span></$list>\n</div>\n</$list>\n<$list filter=\"[tag[todo]!tag[done]field:q[q3]!count[]]\"><div style=\"color:#aaa; font-size:0.85em;\">(暂无)</div></$list>\n</div>\n<div style=\"border:1px solid rgba(128,128,128,0.22); border-radius:10px; padding:8px 12px; background:linear-gradient(135deg,#95a5a61a,transparent);\">\n<div style=\"font-weight:700; margin-bottom:6px;\">⚪ 不重要 · 不紧急 <span style=\"color:#888; font-weight:400; font-size:0.82em;\">{{{[tag[todo]!tag[done]field:q[q4]count[]]}}} 件</span></div>\n<$list filter=\"[tag[todo]!tag[done]field:q[q4]sort[modified]]\">\n<div style=\"display:flex; gap:6px; align-items:baseline; padding:3px 0; border-bottom:1px dashed rgba(128,128,128,0.15);\">\n<$checkbox tiddler=<<currentTiddler>> tag=\"done\"/>\n<$link to=<<currentTiddler>> style=\"flex:1;\"><$view field=\"title\"/></$link>\n<$list filter=\"[<currentTiddler>get[due]compare:date:lt<today>]\"><span style=\"color:#c0392b; font-size:0.78em;\">逾期</span></$list>\n<$list filter=\"[<currentTiddler>get[due]!is[blank]]\"><span style=\"color:#888; font-size:0.8em;\"><$view field=\"due\" format=\"date\" template=\"MM-DD\"/></span></$list>\n</div>\n</$list>\n<$list filter=\"[tag[todo]!tag[done]field:q[q4]!count[]]\"><div style=\"color:#aaa; font-size:0.85em;\">(暂无)</div></$list>\n</div>\n</div>\n\\end\n\n<$set name=\"today\" value=<<now \"YYYY0MM0DD\">>>\n\n!! 🏠 主页\n\n<div class=\"tc-message-box\">待办看板与知识库入口。</div>\n\n<div style=\"display:flex; gap:8px; flex-wrap:wrap; margin:8px 0;\">\n<$button class=\"tc-btn-invisible tc-tiddlylink\" style=\"padding:6px 16px; border:1px solid rgba(128,128,128,0.35); border-radius:8px; font-weight:600;\">\n<$action-navigate $to=\"所有标签\"/>\n🏷 所有标签\n</$button>\n<$button class=\"tc-btn-invisible tc-tiddlylink\" style=\"padding:6px 16px; border:1px solid rgba(128,128,128,0.35); border-radius:8px; font-weight:600;\">\n<$action-navigate $to=\"所有文章\"/>\n📚 所有文章\n</$button>\n<$button class=\"tc-btn-invisible tc-tiddlylink\" style=\"padding:6px 16px; border:1px solid rgba(128,128,128,0.35); border-radius:8px; font-weight:600;\">\n<$action-navigate $to=\"主题汇总·示例\"/>\n🗂 主题汇总\n</$button>\n</div>\n\n!! ✅ 待办 · 四象限\n\n<div class=\"tc-message-box\">输入一行要事、选好象限,点「➕ 添加」即建任务(默认 Q2 重要·不紧急)。你输入的这句话会直接成为任务标题,首页一目了然。点任务左侧方框即完成(自动加 <code>done</code> 标签并从看板消失)。给任意笔记打上 <code>todo</code> 标签也会被收集到下方「未分类」,补填 <code>q</code> 字段即进入象限。</div>\n\n<div style=\"display:flex; gap:6px; flex-wrap:wrap; align-items:center; margin:10px 0;\">\n<$edit-text tiddler=\"$:/state/todo/title\" tag=\"input\" placeholder=\"快速添加:写一句要做的事,选象限后点 ➕…\" style=\"flex:1; min-width:220px; padding:6px 10px; border-radius:6px; border:1px solid rgba(128,128,128,0.3);\"/>\n<$select tiddler=\"$:/state/todo/quadrant\" default=\"q2\" style=\"padding:6px 10px; border-radius:6px; border:1px solid rgba(128,128,128,0.3);\">\n<option value=\"q1\">Q1 重要·紧急</option>\n<option value=\"q2\">Q2 重要·不紧急</option>\n<option value=\"q3\">Q3 紧急·不重要</option>\n<option value=\"q4\">Q4 不重要·不紧急</option>\n</$select>\n<$button class=\"tc-btn-invisible\" style=\"padding:6px 14px; border-radius:6px; border:1px solid rgba(128,128,128,0.35); font-weight:600;\">\n<$list filter=\"[{$:/state/todo/title}!is[blank]]\">\n<$action-createtiddler $basetitle={{{[{$:/state/todo/title}]}}} tags=\"todo\" text={{{[{$:/state/todo/title}]}}} q={{{[{$:/state/todo/quadrant}!is[blank]else[q2]]}}}/>\n<$action-setfield $tiddler=\"$:/state/todo/title\" text=\"\"/>\n</$list>\n➕ 添加\n</$button>\n</div>\n\n<div style=\"margin:6px 0; color:#555;\">📅 今日到期 <b>{{{[tag[todo]!tag[done]field:due<today>count[]]}}}</b> 件 · ⏰ 已逾期 <b>{{{[tag[todo]!tag[done]get[due]compare:date:lt<today>count[]]}}}</b> 件</div>\n\n<<quadrant-board>>\n\n<$list filter=\"[tag[todo]!tag[done]!has[q]count[]!match[0]]\">\n<div style=\"margin-top:10px; border:1px dashed rgba(128,128,128,0.35); border-radius:10px; padding:8px 12px;\">\n<div style=\"font-weight:700; margin-bottom:6px;\">📥 未分类待办 <span style=\"color:#888; font-weight:400; font-size:0.82em;\">{{{[tag[todo]!tag[done]!has[q]count[]]}}} 件</span></div>\n<$list filter=\"[tag[todo]!tag[done]!has[q]sort[modified]]\">\n<div style=\"display:flex; gap:6px; align-items:baseline; padding:3px 0; border-bottom:1px dashed rgba(128,128,128,0.15);\">\n<$checkbox tiddler=<<currentTiddler>> tag=\"done\"/>\n<$link to=<<currentTiddler>> style=\"flex:1;\"><$view field=\"title\"/></$link>\n<$list filter=\"[<currentTiddler>get[due]compare:date:lt<today>]\"><span style=\"color:#c0392b; font-size:0.78em;\">逾期</span></$list>\n</div>\n</$list>\n</div>\n</$list>\n\n</$set>\n"
|
|
2707
|
+
"text": "\\whitespace trim\n\n\\define quadrant-board()\n<div style=\"display:grid; grid-template-columns:1fr 1fr; gap:10px; margin-top:8px;\">\n<div style=\"border:1px solid rgba(128,128,128,0.22); border-radius:10px; padding:8px 12px; background:linear-gradient(135deg,#e74c3c1a,transparent);\">\n<div style=\"font-weight:700; margin-bottom:6px;\">🔴 重要 · 紧急 <span style=\"color:#888; font-weight:400; font-size:0.82em;\">{{{[tag[todo]!tag[done]field:q[q1]count[]]}}} 件</span></div>\n<$list filter=\"[tag[todo]!tag[done]field:q[q1]sort[modified]]\">\n<div style=\"display:flex; gap:6px; align-items:baseline; padding:3px 0; border-bottom:1px dashed rgba(128,128,128,0.15);\">\n<$checkbox tiddler=<<currentTiddler>> tag=\"done\"/>\n<$link to=<<currentTiddler>> style=\"flex:1;\"><$view field=\"title\"/></$link>\n<$list filter=\"[<currentTiddler>get[due]compare:date:lt<today>]\"><span style=\"color:#c0392b; font-size:0.78em;\">逾期</span></$list>\n<$list filter=\"[<currentTiddler>get[due]!is[blank]]\"><span style=\"color:#888; font-size:0.8em;\"><$view field=\"due\" format=\"date\" template=\"MM-DD\"/></span></$list>\n</div>\n</$list>\n<$list filter=\"[tag[todo]!tag[done]field:q[q1]!count[]]\"><div style=\"color:#aaa; font-size:0.85em;\">(暂无)</div></$list>\n</div>\n<div style=\"border:1px solid rgba(128,128,128,0.22); border-radius:10px; padding:8px 12px; background:linear-gradient(135deg,#3498db1a,transparent);\">\n<div style=\"font-weight:700; margin-bottom:6px;\">🔵 重要 · 不紧急 <span style=\"color:#888; font-weight:400; font-size:0.82em;\">{{{[tag[todo]!tag[done]field:q[q2]count[]]}}} 件</span></div>\n<$list filter=\"[tag[todo]!tag[done]field:q[q2]sort[modified]]\">\n<div style=\"display:flex; gap:6px; align-items:baseline; padding:3px 0; border-bottom:1px dashed rgba(128,128,128,0.15);\">\n<$checkbox tiddler=<<currentTiddler>> tag=\"done\"/>\n<$link to=<<currentTiddler>> style=\"flex:1;\"><$view field=\"title\"/></$link>\n<$list filter=\"[<currentTiddler>get[due]compare:date:lt<today>]\"><span style=\"color:#c0392b; font-size:0.78em;\">逾期</span></$list>\n<$list filter=\"[<currentTiddler>get[due]!is[blank]]\"><span style=\"color:#888; font-size:0.8em;\"><$view field=\"due\" format=\"date\" template=\"MM-DD\"/></span></$list>\n</div>\n</$list>\n<$list filter=\"[tag[todo]!tag[done]field:q[q2]!count[]]\"><div style=\"color:#aaa; font-size:0.85em;\">(暂无)</div></$list>\n</div>\n<div style=\"border:1px solid rgba(128,128,128,0.22); border-radius:10px; padding:8px 12px; background:linear-gradient(135deg,#f39c121a,transparent);\">\n<div style=\"font-weight:700; margin-bottom:6px;\">🟠 紧急 · 不重要 <span style=\"color:#888; font-weight:400; font-size:0.82em;\">{{{[tag[todo]!tag[done]field:q[q3]count[]]}}} 件</span></div>\n<$list filter=\"[tag[todo]!tag[done]field:q[q3]sort[modified]]\">\n<div style=\"display:flex; gap:6px; align-items:baseline; padding:3px 0; border-bottom:1px dashed rgba(128,128,128,0.15);\">\n<$checkbox tiddler=<<currentTiddler>> tag=\"done\"/>\n<$link to=<<currentTiddler>> style=\"flex:1;\"><$view field=\"title\"/></$link>\n<$list filter=\"[<currentTiddler>get[due]compare:date:lt<today>]\"><span style=\"color:#c0392b; font-size:0.78em;\">逾期</span></$list>\n<$list filter=\"[<currentTiddler>get[due]!is[blank]]\"><span style=\"color:#888; font-size:0.8em;\"><$view field=\"due\" format=\"date\" template=\"MM-DD\"/></span></$list>\n</div>\n</$list>\n<$list filter=\"[tag[todo]!tag[done]field:q[q3]!count[]]\"><div style=\"color:#aaa; font-size:0.85em;\">(暂无)</div></$list>\n</div>\n<div style=\"border:1px solid rgba(128,128,128,0.22); border-radius:10px; padding:8px 12px; background:linear-gradient(135deg,#95a5a61a,transparent);\">\n<div style=\"font-weight:700; margin-bottom:6px;\">⚪ 不重要 · 不紧急 <span style=\"color:#888; font-weight:400; font-size:0.82em;\">{{{[tag[todo]!tag[done]field:q[q4]count[]]}}} 件</span></div>\n<$list filter=\"[tag[todo]!tag[done]field:q[q4]sort[modified]]\">\n<div style=\"display:flex; gap:6px; align-items:baseline; padding:3px 0; border-bottom:1px dashed rgba(128,128,128,0.15);\">\n<$checkbox tiddler=<<currentTiddler>> tag=\"done\"/>\n<$link to=<<currentTiddler>> style=\"flex:1;\"><$view field=\"title\"/></$link>\n<$list filter=\"[<currentTiddler>get[due]compare:date:lt<today>]\"><span style=\"color:#c0392b; font-size:0.78em;\">逾期</span></$list>\n<$list filter=\"[<currentTiddler>get[due]!is[blank]]\"><span style=\"color:#888; font-size:0.8em;\"><$view field=\"due\" format=\"date\" template=\"MM-DD\"/></span></$list>\n</div>\n</$list>\n<$list filter=\"[tag[todo]!tag[done]field:q[q4]!count[]]\"><div style=\"color:#aaa; font-size:0.85em;\">(暂无)</div></$list>\n</div>\n</div>\n\\end\n\n<$set name=\"today\" value=<<now \"YYYY0MM0DD\">>>\n\n!! 🏠 主页\n\n<div class=\"tc-message-box\">待办看板与知识库入口。</div>\n\n<div style=\"display:flex; gap:8px; flex-wrap:wrap; margin:8px 0;\">\n<$button class=\"tc-btn-invisible tc-tiddlylink\" style=\"padding:6px 16px; border:1px solid rgba(128,128,128,0.35); border-radius:8px; font-weight:600;\">\n<$action-navigate $to=\"所有标签\"/>\n🏷 所有标签\n</$button>\n<$button class=\"tc-btn-invisible tc-tiddlylink\" style=\"padding:6px 16px; border:1px solid rgba(128,128,128,0.35); border-radius:8px; font-weight:600;\">\n<$action-navigate $to=\"所有文章\"/>\n📚 所有文章\n</$button>\n<$button class=\"tc-btn-invisible tc-tiddlylink\" style=\"padding:6px 16px; border:1px solid rgba(128,128,128,0.35); border-radius:8px; font-weight:600;\">\n<$action-navigate $to=\"主题汇总·示例\"/>\n🗂 主题汇总\n</$button>\n</div>\n\n!! ✍️ 快速记笔记(完整编辑器)\n\n<div class=\"tc-message-box\">直接在首页写笔记:填标题、用工具栏写正文(支持 Markdown:**加粗**、*斜体*、# 标题、- 列表、> 引用 等)、加标签,点「💾 保存」即创建新笔记(默认 Markdown,标题留空用当前时间)。勾选「同时加入待办」并选象限,可一并进入下方四象限看板。</div>\n\n<div style=\"border:1px solid rgba(128,128,128,0.22); border-radius:10px; padding:10px 12px; margin:10px 0;\">\n\n<div style=\"display:flex; gap:6px; align-items:center; margin-bottom:6px;\">\n<$edit-text tiddler=\"$:/state/home-note/title\" tag=\"input\" placeholder=\"标题(留空用当前时间)\" style=\"flex:1; min-width:200px; padding:6px 10px; border-radius:6px; border:1px solid rgba(128,128,128,0.3);\"/>\n</div>\n\n<$edit-text tiddler=\"$:/state/home-note/text\" tag=\"textarea\" class=\"tc-edit-texteditor\" autoHeight=\"yes\" minHeight=\"150px\" placeholder=\"正文:支持 **加粗**、*斜体*、# 标题、- 列表、> 引用 等 Markdown 语法\" style=\"width:100%; box-sizing:border-box; padding:6px 10px; border-radius:6px; border:1px solid rgba(128,128,128,0.3);\">\n<$button class=\"tc-btn-invisible\" tooltip=\"加粗\" style=\"padding:2px 8px; font-weight:700;\"><$action-sendmessage $message=\"tm-edit-text-operation\" $param=\"wrap-selection\" prefix=\"**\" suffix=\"**\" trimSelection=\"yes\"/>B</$button>\n<$button class=\"tc-btn-invisible\" tooltip=\"斜体\" style=\"padding:2px 8px; font-style:italic;\"><$action-sendmessage $message=\"tm-edit-text-operation\" $param=\"wrap-selection\" prefix=\"*\" suffix=\"*\" trimSelection=\"yes\"/>I</$button>\n<$button class=\"tc-btn-invisible\" tooltip=\"删除线\" style=\"padding:2px 8px;\"><$action-sendmessage $message=\"tm-edit-text-operation\" $param=\"wrap-selection\" prefix=\"~~\" suffix=\"~~\" trimSelection=\"yes\"/>S</$button>\n<$button class=\"tc-btn-invisible\" tooltip=\"标题\" style=\"padding:2px 8px;\"><$action-sendmessage $message=\"tm-edit-text-operation\" $param=\"prefix-lines\" character=\"#\" count=\"1\"/>H</$button>\n<$button class=\"tc-btn-invisible\" tooltip=\"无序列表\" style=\"padding:2px 8px;\"><$action-sendmessage $message=\"tm-edit-text-operation\" $param=\"prefix-lines\" character=\"-\" count=\"1\"/>•</$button>\n<$button class=\"tc-btn-invisible\" tooltip=\"有序列表\" style=\"padding:2px 8px;\"><$action-sendmessage $message=\"tm-edit-text-operation\" $param=\"prefix-lines\" character=\"1.\" count=\"1\"/>1.</$button>\n<$button class=\"tc-btn-invisible\" tooltip=\"引用\" style=\"padding:2px 8px;\"><$action-sendmessage $message=\"tm-edit-text-operation\" $param=\"prefix-lines\" character=\">\" count=\"1\"/>❝</$button>\n<$button class=\"tc-btn-invisible\" tooltip=\"行内代码\" style=\"padding:2px 8px;\"><$action-sendmessage $message=\"tm-edit-text-operation\" $param=\"wrap-selection\" prefix=\"`\" suffix=\"`\" trimSelection=\"yes\"/></></$button>\n</$edit-text>\n\n<div style=\"display:flex; gap:6px; flex-wrap:wrap; align-items:center; margin-top:6px;\">\n<$edit-text tiddler=\"$:/state/home-note/tags\" tag=\"input\" placeholder=\"标签(逗号分隔,可选)\" style=\"flex:1; min-width:160px; padding:6px 10px; border-radius:6px; border:1px solid rgba(128,128,128,0.3);\"/>\n<$checkbox tiddler=\"$:/state/home-note/todo\" field=\"text\" checked=\"yes\" unchecked=\"no\"> 同时加入待办</$checkbox>\n<$select tiddler=\"$:/state/home-note/quadrant\" default=\"q2\" style=\"padding:6px 10px; border-radius:6px; border:1px solid rgba(128,128,128,0.3);\">\n<option value=\"q1\">Q1 重要·紧急</option>\n<option value=\"q2\">Q2 重要·不紧急</option>\n<option value=\"q3\">Q3 紧急·不重要</option>\n<option value=\"q4\">Q4 不重要·不紧急</option>\n</$select>\n</div>\n\n<div style=\"display:flex; gap:6px; align-items:center; margin-top:8px; flex-wrap:wrap;\">\n<$button class=\"tc-btn-invisible\" style=\"padding:6px 16px; border-radius:6px; border:1px solid rgba(128,128,128,0.35); font-weight:600; background:rgba(128,128,128,0.06);\">\n<$list filter=\"[{$:/state/home-note/title}!is[blank]] [{$:/state/home-note/text}!is[blank]]\">\n<$action-createtiddler\n$basetitle={{{ [{$:/state/home-note/title}!is[blank]then{$:/state/home-note/title}] ~[<now \"YYYY-MM-DD-HHmm\">] }}}\ntext={{{ [{$:/state/home-note/text}] }}}\ntags={{{ [{$:/state/home-note/tags}split[,]split[,]trim[]] [{$:/state/home-note/todo}match[yes]then[[todo]]] +[join[ ]] }}}\nq={{{ [{$:/state/home-note/todo}match[yes]then{$:/state/home-note/quadrant}!is[blank]] ~[{$:/state/home-note/todo}match[yes]then[q2]] }}}\ntype=\"text/markdown\"\n$savetitle=\"$:/state/home-note/last-created\"/>\n<$action-setfield $tiddler=\"$:/state/home-note/title\" text=\"\"/>\n<$action-setfield $tiddler=\"$:/state/home-note/text\" text=\"\"/>\n<$action-setfield $tiddler=\"$:/state/home-note/tags\" text=\"\"/>\n</$list>\n💾 保存\n</$button>\n<$button class=\"tc-btn-invisible\" tooltip=\"清空草稿\" style=\"padding:6px 12px; border-radius:6px; border:1px solid rgba(128,128,128,0.3);\">\n<$action-setfield $tiddler=\"$:/state/home-note/title\" text=\"\"/>\n<$action-setfield $tiddler=\"$:/state/home-note/text\" text=\"\"/>\n<$action-setfield $tiddler=\"$:/state/home-note/tags\" text=\"\"/>\n🗑 清空\n</$button>\n<$list filter=\"[{$:/state/home-note/last-created}!is[blank]]\">\n<span style=\"color:#2e7d32; font-size:0.9em;\">✅ 已保存:<$link to={{{ [{$:/state/home-note/last-created}] }}}>{{{$:/state/home-note/last-created}}}</$link></span>\n</$list>\n</div>\n\n</div>\n\n!! ✅ 待办 · 四象限\n\n<div class=\"tc-message-box\">在上方「快速记笔记」勾选「同时加入待办」即建任务(默认 Q2 重要·不紧急);也可以给任意笔记打上 <code>todo</code> 标签收集到下方。点任务左侧方框即完成(自动加 <code>done</code> 标签并从看板消失)。给任意笔记打上 <code>todo</code> 标签也会被收集到下方「未分类」,补填 <code>q</code> 字段即进入象限。</div>\n\n<div style=\"margin:6px 0; color:#555;\">📅 今日到期 <b>{{{[tag[todo]!tag[done]field:due<today>count[]]}}}</b> 件 · ⏰ 已逾期 <b>{{{[tag[todo]!tag[done]get[due]compare:date:lt<today>count[]]}}}</b> 件</div>\n\n<<quadrant-board>>\n\n<$list filter=\"[tag[todo]!tag[done]!has[q]count[]!match[0]]\">\n<div style=\"margin-top:10px; border:1px dashed rgba(128,128,128,0.35); border-radius:10px; padding:8px 12px;\">\n<div style=\"font-weight:700; margin-bottom:6px;\">📥 未分类待办 <span style=\"color:#888; font-weight:400; font-size:0.82em;\">{{{[tag[todo]!tag[done]!has[q]count[]]}}} 件</span></div>\n<$list filter=\"[tag[todo]!tag[done]!has[q]sort[modified]]\">\n<div style=\"display:flex; gap:6px; align-items:baseline; padding:3px 0; border-bottom:1px dashed rgba(128,128,128,0.15);\">\n<$checkbox tiddler=<<currentTiddler>> tag=\"done\"/>\n<$link to=<<currentTiddler>> style=\"flex:1;\"><$view field=\"title\"/></$link>\n<$list filter=\"[<currentTiddler>get[due]compare:date:lt<today>]\"><span style=\"color:#c0392b; font-size:0.78em;\">逾期</span></$list>\n</div>\n</$list>\n</div>\n</$list>\n\n</$set>"
|
|
2708
2708
|
},
|
|
2709
2709
|
{
|
|
2710
2710
|
"title": "所有标签",
|
|
@@ -3386,6 +3386,65 @@ const REQUEST_TIMEOUT_MS = 1e4;
|
|
|
3386
3386
|
const CSRF_HEADER = { "x-requested-with": "TiddlyWiki" };
|
|
3387
3387
|
/** Sentinel `exclude` value: excludes nothing, so `text` stays in the list. */
|
|
3388
3388
|
const LIST_WITH_TEXT_EXCLUDE = "__dsh_tw_none__";
|
|
3389
|
+
/**
|
|
3390
|
+
* Recipe-list filter that keeps ONLY text-bearing tiddlers (notes): no `type`
|
|
3391
|
+
* field at all, or a `text/*` type. Binary tiddlers (images/audio/video/fonts/
|
|
3392
|
+
* PDF/zip…) carry their payload as a base64 `text` field — on a big wiki (e.g.
|
|
3393
|
+
* thousands of scanned book pages) the full listing serializes 500+MB of base64
|
|
3394
|
+
* and times every agent read out. This filter drops them SERVER-SIDE so the
|
|
3395
|
+
* listing stays ~6MB (v0.16.20, verified on a 2418-tiddler wiki:
|
|
3396
|
+
* 515MB/17s → 6MB/0.3s).
|
|
3397
|
+
*
|
|
3398
|
+
* TW 5.4.1 filter facts that matter here (all verified empirically):
|
|
3399
|
+
* - `prefix`/`match` operators match the TITLE only — field matching needs
|
|
3400
|
+
* `regexp:<field>` / `field:<field>`.
|
|
3401
|
+
* - `[has[type]]` = "has a non-empty type field"; `[has:type[]]` is a DIFFERENT
|
|
3402
|
+
* call (suffix "type" + empty operand) that matches everything.
|
|
3403
|
+
* - space-separated operations UNION (no prefix = "or"); `+` means
|
|
3404
|
+
* intersection.
|
|
3405
|
+
* - negated `regexp:type`/`field:type` DROP type-less tiddlers (their field
|
|
3406
|
+
* string is null), so "exclude binary" must be written as the positive
|
|
3407
|
+
* "no type OR text-ish" union below.
|
|
3408
|
+
*
|
|
3409
|
+
* FILENAME BUDGET (Windows, verified): the whitelist tiddler below is stored
|
|
3410
|
+
* as a .tid file whose name contains the ENTIRE filter string, so the filter
|
|
3411
|
+
* must stay short — a ~180-char filter produced a ~217-char filename that
|
|
3412
|
+
* broke `git add` on Windows ("Filename too long", MAX_PATH) and killed the
|
|
3413
|
+
* auto-committer. 86 chars keeps the file at ~123 chars even on a deep wiki
|
|
3414
|
+
* path. (Consequence: application/json tiddlers are not searchable — on this
|
|
3415
|
+
* wiki there are none, and they are config/data tiddlers, not notes.)
|
|
3416
|
+
*/
|
|
3417
|
+
const TEXT_LIST_FILTER = ["[all[tiddlers]!is[system]!has[type]]", "[all[tiddlers]!is[system]regexp:type[(?i)^text/]]"].join(" ");
|
|
3418
|
+
/** TiddlyWeb blocks every non-default filter with 403 unless the EXACT filter
|
|
3419
|
+
* string is whitelisted at `$:/config/Server/ExternalFilters/<filter>` = "yes".
|
|
3420
|
+
* This client writes that tiddler once on 403 (self-heal, idempotent) and
|
|
3421
|
+
* retries; the tiddler lives in the wiki and travels with its git history. */
|
|
3422
|
+
function externalFilterWhitelistTitle(filter) {
|
|
3423
|
+
return `$:/config/Server/ExternalFilters/${filter}`;
|
|
3424
|
+
}
|
|
3425
|
+
/** Binary MIME type prefixes — their `text` field is base64 payload, not content. */
|
|
3426
|
+
const BINARY_TYPE_PREFIXES = [
|
|
3427
|
+
"image/",
|
|
3428
|
+
"audio/",
|
|
3429
|
+
"video/",
|
|
3430
|
+
"font/"
|
|
3431
|
+
];
|
|
3432
|
+
const BINARY_TYPE_EXACT = /* @__PURE__ */ new Set([
|
|
3433
|
+
"application/octet-stream",
|
|
3434
|
+
"application/pdf",
|
|
3435
|
+
"application/zip",
|
|
3436
|
+
"application/gzip",
|
|
3437
|
+
"application/x-gzip",
|
|
3438
|
+
"application/x-7z-compressed",
|
|
3439
|
+
"application/x-rar-compressed",
|
|
3440
|
+
"application/epub+zip",
|
|
3441
|
+
"application/vnd.openxmlformats-officedocument.wordprocessingml.document"
|
|
3442
|
+
]);
|
|
3443
|
+
/** True when a tiddler's `type` marks it as binary (base64 in `text`). */
|
|
3444
|
+
function isBinaryType(type) {
|
|
3445
|
+
if (typeof type !== "string" || type.length === 0) return false;
|
|
3446
|
+
return BINARY_TYPE_PREFIXES.some((p) => type.startsWith(p)) || BINARY_TYPE_EXACT.has(type);
|
|
3447
|
+
}
|
|
3389
3448
|
/** Split TW's whitespace-joined tags string into an array. */
|
|
3390
3449
|
function normalizeTags(tags) {
|
|
3391
3450
|
if (tags === void 0) return void 0;
|
|
@@ -3457,29 +3516,58 @@ var TiddlyWebClient = class {
|
|
|
3457
3516
|
* List tiddlers via the default server filter. Arbitrary `filter=` queries
|
|
3458
3517
|
* are blocked by the server (403) unless whitelisted, so callers needing a
|
|
3459
3518
|
* subset should use search(); a supplied filter that is 403-blocked falls
|
|
3460
|
-
* back to the
|
|
3519
|
+
* back to the text-bearing filter.
|
|
3520
|
+
*
|
|
3521
|
+
* `includeText=true` returns only TEXT-BEARING tiddlers WITH their `text`
|
|
3522
|
+
* (never the base64 payloads of binary tiddlers): on 403 the whitelist
|
|
3523
|
+
* tiddler for TEXT_LIST_FILTER is written once and the request retried
|
|
3524
|
+
* (self-heal); if that write fails (read-only wiki) it degrades to the
|
|
3525
|
+
* skinny listing (title/tags matchable, empty snippets).
|
|
3461
3526
|
*/
|
|
3462
3527
|
async list(filter, includeText = false) {
|
|
3528
|
+
if (!includeText) return this.fetchListing(filter);
|
|
3529
|
+
const target = filter !== void 0 && filter.length > 0 ? filter : TEXT_LIST_FILTER;
|
|
3530
|
+
let res = await this.requestListWithText(target);
|
|
3531
|
+
if (res.status === 403) {
|
|
3532
|
+
await this.ensureExternalFilterWhitelist(TEXT_LIST_FILTER).catch(() => void 0);
|
|
3533
|
+
res = await this.requestListWithText(TEXT_LIST_FILTER);
|
|
3534
|
+
}
|
|
3535
|
+
if (res.status === 403) return this.fetchListing(void 0);
|
|
3536
|
+
if (!res.ok) throw new Error(`TiddlyWeb recipe list HTTP ${res.status}`);
|
|
3537
|
+
return this.parseList(res);
|
|
3538
|
+
}
|
|
3539
|
+
/** GET the recipe listing WITHOUT text payloads (server default excludes `text`). */
|
|
3540
|
+
async fetchListing(filter) {
|
|
3463
3541
|
const params = new URLSearchParams();
|
|
3464
|
-
if (includeText) params.set("exclude", LIST_WITH_TEXT_EXCLUDE);
|
|
3465
3542
|
if (filter !== void 0 && filter.length > 0) params.set("filter", filter);
|
|
3466
3543
|
const query = params.toString();
|
|
3467
3544
|
let res = await this.request(`/recipes/default/tiddlers.json${query.length > 0 ? `?${query}` : ""}`);
|
|
3468
|
-
if (!res.ok && res.status === 403 && filter !== void 0 && filter.length > 0)
|
|
3469
|
-
const retry = new URLSearchParams();
|
|
3470
|
-
if (includeText) retry.set("exclude", LIST_WITH_TEXT_EXCLUDE);
|
|
3471
|
-
const retryQuery = retry.toString();
|
|
3472
|
-
res = await this.request(`/recipes/default/tiddlers.json${retryQuery.length > 0 ? `?${retryQuery}` : ""}`);
|
|
3473
|
-
}
|
|
3545
|
+
if (!res.ok && res.status === 403 && filter !== void 0 && filter.length > 0) res = await this.request("/recipes/default/tiddlers.json");
|
|
3474
3546
|
if (!res.ok) throw new Error(`TiddlyWeb recipe list HTTP ${res.status}`);
|
|
3547
|
+
return this.parseList(res);
|
|
3548
|
+
}
|
|
3549
|
+
/** GET the recipe listing WITH text for a specific (whitelisted) filter. */
|
|
3550
|
+
async requestListWithText(filter) {
|
|
3551
|
+
return this.request(`/recipes/default/tiddlers.json?filter=${encodeURIComponent(filter)}&exclude=${LIST_WITH_TEXT_EXCLUDE}`);
|
|
3552
|
+
}
|
|
3553
|
+
/** Write `$:/config/Server/ExternalFilters/<filter>` = "yes" (idempotent). */
|
|
3554
|
+
async ensureExternalFilterWhitelist(filter) {
|
|
3555
|
+
await this.put({
|
|
3556
|
+
title: externalFilterWhitelistTitle(filter),
|
|
3557
|
+
text: "yes"
|
|
3558
|
+
});
|
|
3559
|
+
}
|
|
3560
|
+
async parseList(res) {
|
|
3475
3561
|
const data = await res.json();
|
|
3476
3562
|
return (Array.isArray(data) ? data : data.tiddlers ?? []).map(normalizeTiddler);
|
|
3477
3563
|
}
|
|
3478
3564
|
/**
|
|
3479
|
-
* Search
|
|
3480
|
-
* local case-insensitive substring matching on title + text, optional
|
|
3481
|
-
* tags (AND), a `since` modified-time floor, an exact `type`, capped at
|
|
3482
|
-
* `limit`. Robust against the server's external-filter 403
|
|
3565
|
+
* Search text-bearing tiddlers: one request (text-bearing listing with text)
|
|
3566
|
+
* plus local case-insensitive substring matching on title + text, optional
|
|
3567
|
+
* exact tags (AND), a `since` modified-time floor, an exact `type`, capped at
|
|
3568
|
+
* `limit`. Robust against the server's external-filter 403 (whitelist is
|
|
3569
|
+
* self-healed). Binary tiddlers (images/audio/…) are NOT in the listing, so
|
|
3570
|
+
* they can never match — a deliberate flood guard on big wikis.
|
|
3483
3571
|
*/
|
|
3484
3572
|
async search(query, options = {}) {
|
|
3485
3573
|
const items = await this.list(void 0, true);
|
|
@@ -3507,9 +3595,11 @@ var TiddlyWebClient = class {
|
|
|
3507
3595
|
};
|
|
3508
3596
|
}
|
|
3509
3597
|
/**
|
|
3510
|
-
* List the most recently modified
|
|
3511
|
-
* (missing/modified-less tiddlers sort to the tail).
|
|
3512
|
-
* tiddlers modified at/after that instant.
|
|
3598
|
+
* List the most recently modified TEXT-BEARING (non-system, non-binary)
|
|
3599
|
+
* tiddlers, newest first (missing/modified-less tiddlers sort to the tail).
|
|
3600
|
+
* `since` keeps only tiddlers modified at/after that instant. Binary
|
|
3601
|
+
* attachments never appear — a book import touching hundreds of images must
|
|
3602
|
+
* not crowd the "最近修改" list.
|
|
3513
3603
|
*/
|
|
3514
3604
|
async recent(limit = 15, since) {
|
|
3515
3605
|
const items = await this.list(void 0, true);
|
|
@@ -3815,7 +3905,7 @@ function registerTiddlywikiTools(ctx, deps) {
|
|
|
3815
3905
|
};
|
|
3816
3906
|
register(defineTool({
|
|
3817
3907
|
name: "tiddlywiki_search",
|
|
3818
|
-
description: "检索 TiddlyWiki 持久知识库:按关键词(可选 tags 数组 / since 修改时间 / type / limit)搜索非系统 tiddler
|
|
3908
|
+
description: "检索 TiddlyWiki 持久知识库:按关键词(可选 tags 数组 / since 修改时间 / type / limit)搜索非系统 tiddler,返回标题、标签、修改时间与摘要片段。二进制 tiddler(图片等附件,正文为 base64)不参与检索。",
|
|
3819
3909
|
parameters: {
|
|
3820
3910
|
query: {
|
|
3821
3911
|
type: "string",
|
|
@@ -3891,7 +3981,7 @@ function registerTiddlywikiTools(ctx, deps) {
|
|
|
3891
3981
|
}));
|
|
3892
3982
|
register(defineTool({
|
|
3893
3983
|
name: "tiddlywiki_recent",
|
|
3894
|
-
description: "查看 TiddlyWiki 知识库最近修改的笔记(按修改时间倒序,排除系统 tiddler
|
|
3984
|
+
description: "查看 TiddlyWiki 知识库最近修改的笔记(按修改时间倒序,排除系统 tiddler 与图片等二进制附件),返回标题、标签、修改时间与摘要。适合开工时快速了解近期动态。",
|
|
3895
3985
|
parameters: {
|
|
3896
3986
|
limit: {
|
|
3897
3987
|
type: "integer",
|
|
@@ -3960,7 +4050,7 @@ function registerTiddlywikiTools(ctx, deps) {
|
|
|
3960
4050
|
}));
|
|
3961
4051
|
register(defineTool({
|
|
3962
4052
|
name: "tiddlywiki_get",
|
|
3963
|
-
description: "读取一个 TiddlyWiki tiddler
|
|
4053
|
+
description: "读取一个 TiddlyWiki tiddler 的完整内容(标题、全文、标签、自定义字段)。二进制 tiddler(图片等附件)只返回元数据,不返回 base64 正文。",
|
|
3964
4054
|
parameters: { title: {
|
|
3965
4055
|
type: "string",
|
|
3966
4056
|
description: "tiddler 标题(精确匹配)",
|
|
@@ -3973,6 +4063,18 @@ function registerTiddlywikiTools(ctx, deps) {
|
|
|
3973
4063
|
type: "text",
|
|
3974
4064
|
text: `tiddler「${value.title}」不存在。可用 tiddlywiki_search 检索,或用 tiddlywiki_put 新建。`
|
|
3975
4065
|
}];
|
|
4066
|
+
if (value.binary === true) {
|
|
4067
|
+
const lines = [`tiddler「${value.title}」是二进制附件(type=${value.binaryType ?? "?"},base64 正文约 ${value.binaryChars ?? 0} 字符),不返回正文。`];
|
|
4068
|
+
if (value.tags.length > 0) lines.push(`标签: ${value.tags.join(", ")}`);
|
|
4069
|
+
if (value.modified !== null) lines.push(`修改: ${value.modified}`);
|
|
4070
|
+
const fields = Object.entries(value.fields);
|
|
4071
|
+
if (fields.length > 0) lines.push(`字段: ${fields.map(([k, v]) => `${k}=${String(v)}`).join(", ")}`);
|
|
4072
|
+
lines.push(`如需查看附件本身,可打开 [${value.title}](/dsh-tiddlywiki/tw/#${encodeURIComponent(value.title)})。`);
|
|
4073
|
+
return [{
|
|
4074
|
+
type: "text",
|
|
4075
|
+
text: lines.join("\n")
|
|
4076
|
+
}];
|
|
4077
|
+
}
|
|
3976
4078
|
const lines = [`tiddler「${value.title}」`];
|
|
3977
4079
|
if (value.tags.length > 0) lines.push(`标签: ${value.tags.join(", ")}`);
|
|
3978
4080
|
if (value.modified !== null) lines.push(`修改: ${value.modified}`);
|
|
@@ -3996,14 +4098,21 @@ function registerTiddlywikiTools(ctx, deps) {
|
|
|
3996
4098
|
fields: {},
|
|
3997
4099
|
modified: null
|
|
3998
4100
|
};
|
|
3999
|
-
|
|
4101
|
+
const binary = isBinaryType(typeof t.type === "string" ? t.type : void 0);
|
|
4102
|
+
const result = {
|
|
4000
4103
|
notFound: false,
|
|
4001
4104
|
title: t.title,
|
|
4002
|
-
text: t.text ?? "",
|
|
4105
|
+
text: binary ? "" : t.text ?? "",
|
|
4003
4106
|
tags: t.tags ?? [],
|
|
4004
4107
|
fields: pickFields(t),
|
|
4005
4108
|
modified: typeof t.modified === "string" ? t.modified : null
|
|
4006
4109
|
};
|
|
4110
|
+
if (binary) {
|
|
4111
|
+
result.binary = true;
|
|
4112
|
+
result.binaryType = typeof t.type === "string" ? t.type : void 0;
|
|
4113
|
+
result.binaryChars = (t.text ?? "").length;
|
|
4114
|
+
}
|
|
4115
|
+
return result;
|
|
4007
4116
|
}
|
|
4008
4117
|
}));
|
|
4009
4118
|
register(defineTool({
|
|
@@ -4614,8 +4723,8 @@ const PROMPT_TEXT = `## TiddlyWiki 持久知识库
|
|
|
4614
4723
|
|
|
4615
4724
|
本机有一个 TiddlyWiki 5 持久知识库(wiki 文件夹即 git 仓库)。你可以用工具读写 tiddler:
|
|
4616
4725
|
|
|
4617
|
-
- \`tiddlywiki_search\`(query 必填;可选 tags[]/tag、since 修改时间、type、limit
|
|
4618
|
-
- \`tiddlywiki_recent\`(limit?, since
|
|
4726
|
+
- \`tiddlywiki_search\`(query 必填;可选 tags[]/tag、since 修改时间、type、limit)检索(图片等二进制附件不参与检索);\`tiddlywiki_get\`(title)读全文(二进制附件只返回元数据,不含 base64 正文);\`tiddlywiki_put\`(title, text, tags?, fields?)写/覆盖;\`tiddlywiki_batch_put\`(items[])批量写;\`tiddlywiki_rename\`(oldTitle, newTitle, updateRefs?)重命名并尽量同步引用;\`tiddlywiki_delete\`(title)删除。
|
|
4727
|
+
- \`tiddlywiki_recent\`(limit?, since?)看最近修改的笔记(不含图片等二进制附件);\`tiddlywiki_list_tags\` 看现有 tag 及计数。
|
|
4619
4728
|
- \`tiddlywiki_git_sync\`(pull|push|sync)做 git 同步;\`tiddlywiki_git_resolve\`(files, strategy=keep-local|keep-remote|list)在 pull 冲突后按 tiddler 二选一解决。
|
|
4620
4729
|
|
|
4621
4730
|
知识库同步纪律(三条):
|
|
@@ -4869,6 +4978,6 @@ function apply(ctx, rawConfig = {}) {
|
|
|
4869
4978
|
}, "dsh-tiddlywiki: host teardown");
|
|
4870
4979
|
}
|
|
4871
4980
|
//#endregion
|
|
4872
|
-
export { ALL_ARTICLES_MARKER_TITLE, ALL_ARTICLES_TEXT, ALL_ARTICLES_TITLE, AutoCommitter, ConfigStore, DOC_NOTE_TAG, DOC_NOTE_TEXT, DOC_NOTE_TITLE, GitFace, HOME_DEFAULT_TIDDLERS, HOME_INDEX_ITEMS, HOME_INDEX_MARKER_TITLE, MENUBAR_THEME_MARKER_TITLE, MENUBAR_THEME_TEXT, MENUBAR_THEME_TIDDLER, PATH_PREFIX, RENDER_BUNDLE_TEXT, RENDER_MARKER_TITLE, RENDER_PLUGIN_TITLE, SEED_DEFS, SEND_TO_AGENT_BUNDLE_TEXT, SEND_TO_AGENT_MARKER_TITLE, SEND_TO_AGENT_PLUGIN_TITLE, SESSION_SUMMARY_PREFIX, TW_PROXY_PATH, TW_PROXY_PREFIX, TW_WEB_HOST_DEFAULT, TW_WEB_HOST_TIDDLER, TiddlyWebClient, WikiServer, apply, bundledCatalog, checkAllSeeds, deepMerge, defineTool, dshHomePath, ensureLanguage, ensureTwWebHost, inject, name, normalizeThemes, openInTwEditor, readWikiInfo, registerAdminRoutes, registerRoutes, registerTiddlywikiTools, removeSeedById, resolveTwRoot, runAllSeeds, runSeedById, seedAllArticles, seedDocNote, seedHomeIndex, seedMenubarTheme, seedRenderRoute, seedSendToAgent, writeSessionSummary, writeWikiInfo };
|
|
4981
|
+
export { ALL_ARTICLES_MARKER_TITLE, ALL_ARTICLES_TEXT, ALL_ARTICLES_TITLE, AutoCommitter, ConfigStore, DOC_NOTE_TAG, DOC_NOTE_TEXT, DOC_NOTE_TITLE, GitFace, HOME_DEFAULT_TIDDLERS, HOME_INDEX_ITEMS, HOME_INDEX_MARKER_TITLE, MENUBAR_THEME_MARKER_TITLE, MENUBAR_THEME_TEXT, MENUBAR_THEME_TIDDLER, PATH_PREFIX, RENDER_BUNDLE_TEXT, RENDER_MARKER_TITLE, RENDER_PLUGIN_TITLE, SEED_DEFS, SEND_TO_AGENT_BUNDLE_TEXT, SEND_TO_AGENT_MARKER_TITLE, SEND_TO_AGENT_PLUGIN_TITLE, SESSION_SUMMARY_PREFIX, TEXT_LIST_FILTER, TW_PROXY_PATH, TW_PROXY_PREFIX, TW_WEB_HOST_DEFAULT, TW_WEB_HOST_TIDDLER, TiddlyWebClient, WikiServer, apply, bundledCatalog, checkAllSeeds, deepMerge, defineTool, dshHomePath, ensureLanguage, ensureTwWebHost, inject, isBinaryType, name, normalizeThemes, openInTwEditor, readWikiInfo, registerAdminRoutes, registerRoutes, registerTiddlywikiTools, removeSeedById, resolveTwRoot, runAllSeeds, runSeedById, seedAllArticles, seedDocNote, seedHomeIndex, seedMenubarTheme, seedRenderRoute, seedSendToAgent, writeSessionSummary, writeWikiInfo };
|
|
4873
4982
|
|
|
4874
4983
|
//# sourceMappingURL=index.js.map
|