dsh-tiddlywiki 0.19.3 → 0.19.4

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 CHANGED
@@ -84,7 +84,7 @@ dsh plugin --profile web add link:/path/to/dsh-tiddlywiki
84
84
  |---|---|
85
85
  | `tiddlywiki_search` | 检索(`query` + 可选 `tags[]/tag/since/type/field/value/limit`),**按相关度排序**(标题命中 > 标签 > 正文命中次数),摘要取自**命中处上下文**而不是正文开头 |
86
86
  | `tiddlywiki_recent` | 最近修改的笔记(倒序),开工快速了解动态 |
87
- | `tiddlywiki_list_tags` | 现有非系统 tag 及计数(已排除只挂在二进制附件上的 tag) |
87
+ | `tiddlywiki_list_tags` | 现有非系统 tag 及计数(已排除只挂在二进制附件上的 tag);默认列使用最多的 200 个(`limit` 可调、上限 1000),截断时返回 `total`/`truncated` |
88
88
  | `tiddlywiki_get` | 读单个 tiddler 全文(`modified` 以 ISO 返回,可直接用作 `expectedModified`) |
89
89
  | `tiddlywiki_put` | 写/覆盖;未指定类型时自动默认 `text/markdown`(`$:/` 系统条目除外);`expectedModified` + `force` 提供乐观并发保护 |
90
90
  | `tiddlywiki_batch_put` | 批量写入(`overwrite=false` 跳过已存在;单条失败不影响其余,逐条报错) |
@@ -242,7 +242,7 @@ node scripts/gen-seed-ui-styles.mjs '<wiki>/tiddlers/<样式.css>' … src/host/
242
242
  | 路由 | 方法 | 用途 |
243
243
  |---|---|---|
244
244
  | `/dsh-tiddlywiki/status` | GET | 面板健康(service/url/git/tag/ui) |
245
- | `/dsh-tiddlywiki/note` `/edit` `/tags` `/recent` `/get` `/search` | POST/GET | 快速笔记、打开编辑器、tag/最近/单个/搜索 |
245
+ | `/dsh-tiddlywiki/note` `/edit` `/tags` `/recent` `/get` `/search` | POST/GET | 快速笔记、打开编辑器、tag/最近/单个/搜索(列表路由都支持 `limit`;`/tags` 另支持 `sort=alpha\|count` 与 `total`/`truncated`,v0.19.4) |
246
246
  | `/dsh-tiddlywiki/render` | POST | TW 片段渲染(host 净化后返回,回复流卡片/会话汇总用,v0.19.1) |
247
247
  | `/dsh-tiddlywiki/sync` `/upload` `/restart` | POST | 一键同步、文件上传、重启 TW |
248
248
  | `/dsh-tiddlywiki/session/summary` | POST | 会话「知识库」Tab 汇总 |
@@ -293,6 +293,8 @@ lib/ # 预构建产物(发布含 lib/**,提交入库;
293
293
 
294
294
  > 最近几个主要版本的一句话记录(完整变更见 [Releases](https://github.com/bbqisbbq/dsh-tiddlywiki/releases) / git log)。
295
295
 
296
+ - **v0.19.4**(2026-09-11):**标签列表全面「有界」——最后一个没有上限的列表接口**。`GET /dsh-tiddlywiki/tags` 此前会把**每一个**去重标签(大 wiki 上千个)连同计数全量回给浏览器,而回复流工具卡只是展示前 60 个;`tiddlywiki_list_tags` 更严重——同样全量灌进**模型上下文**。现在:① `/tags` 支持 `limit`(1–500,缺省仍是全量,快速笔记的标签自动补全保留完整词表)与 `sort=alpha|count`(默认 `alpha`,`count` 按使用次数降序),回包新增 `total`(去重后的真实总数)与 `truncated`;② 工具 `tiddlywiki_list_tags` 新增可选 `limit`(默认 200、上限 1000),截断时 render 明说「共 N 个,仅列出最多的 M 个」,不再让模型以为那就是全部;③ 客户端 `TagsCard` 改为直接请求 `?limit=60&sort=count`(不再下载上千条再丢掉),并用 `total` 显示「共 N 个 · …另有 M 个」;④ 顺手消掉重复实现:路由与工具现在共用 `TiddlyWebClient.tagStats()` 一份计数逻辑(此前路由自己遍历一遍,且不跳过 `$:/` 系统标题,两侧口径可能不一致),`/recent`、`/search`、`/tags` 的 limit 解析收进 `readLimit()`/`readOptionalLimit()` 两个助手。守门:selftest 增 6 条断言(截断/`total` 不丢分母/`tags` 与 `items` 同集同序/上限 500/`sort=count` 单调递减/无 limit 时 `truncated=false`),`verify-tools` 增 `list_tags` 的 limit 用例。
297
+
296
298
  - **v0.19.3**(2026-09-11):**清掉审计清单里剩下的中优先级项**(安全面 / 健壮性 / 正确性)。**密钥不再外泄**:`bridge.token` / `ui.sendToAgent.token` 会被 `GET /admin/state` 与 `POST /admin/config` 的回包替换成 `********`(并给 `tokenSet` 标记),设置页原样保存不会把真 token 覆盖成掩码、清空即删除;`git.remote` 里的 PAT 同时打码,回填同值也被丢弃。更要紧的是**代理旁路**:`/get` 早先禁止读插件配置 tiddler,但 `/tw` 与 `/api` 会把任意路径转发给 TW 子进程,而它的 TiddlyWeb REST 对 `$:/…` 标题照答不误——`GET /dsh-tiddlywiki/tw/recipes/default/tiddlers/%24%3A%2Fplugins%2Fdsh-tiddlywiki%2Fconfig` 实测能拿到配置正文。现在两个代理都拦截 `$:/plugins/dsh-tiddlywiki/` 命名空间(selftest 有回归断言)。**健壮性**:所有路由统一经 `guardHandler` 包装——此前每个注册都是 `void handleX(req,res)`,而宿主没有 `unhandledRejection` 处理器,任何一次 rejection(含代理里 try 之外的 `new URL()`)都会**直接结束 dsh web 进程**、请求挂死;剪藏桥 listen 之后补上**常驻 `error` 监听**(此前移除唯一监听后,运行期 server error 就是未处理事件 → 崩进程),`stop()` 用 `closeAllConnections()` + 1s 上限,keep-alive 不再是卸载时的死等;`/tw` 代理在客户端断连时**中止上游 fetch 并销毁流**(此前会一直拉到 30s 超时)。**正确性**:会话汇总不再把查询失败(超时/5xx)报成「已删除/不存在」(只有 404 才算不存在),来自请求体/会话日志的 sessionId、rename 旧标题、检索词一律 HTML 转义(TW 会原样透传 HTML,汇总片段又会被注入 DSH 页面),sessionId 还加了字符集白名单;`/upload` 改**原子写**(`wx` + 仅对 EEXIST 走后缀链,修掉 TOCTOU 与「任何 access 错误都当名字可用」)、文件名末尾点/空格先规范化(`evil.html.` 此前能绕过可执行扩展名黑名单);SSRF 白名单补上多播 `224/4`、保留 `240/4`(含 `255.255.255.255`)、`192.0.2.0/24`、`198.51.100.0/24`、`203.0.113.0/24`、`192.88.99.0/24`;`readWikiInfo` 对顶层 `null`/数组给出可读错误而不是 `TypeError`;`/admin/info` 空操作不再重写 `tiddlywiki.info` + 重启 TW,校验失败 400 / 内部失败 500 分开;`/agent/create` 先校验工作模式再建目录(此前未知模式会留下孤儿目录);body 超限统一 413。
297
299
 
298
300
  - **v0.19.2**(2026-09-11):**v0.19.1 的内容 + 让 flush 哨兵真正生效**。v0.19.1 在 npm 上只是被 **staged**(未确认),而那份 tarball 少了下面这个修复,所以补一个版本号(**请以 0.19.2 为准,0.19.1 可以取消掉**)。修复内容:`flushPendingWrites`(「重启 TW 前把 syncer 队列排干」的哨兵)**从来没成功过**——① 它写 `type: 'text/plain'` 却去等 `.tid` 文件,而 TW 的文件系统适配器按内容类型决定扩展名(`text/plain` 实际落 `.txt` + `.txt.meta`);② 它用 `mtime >= startedAt` 判定,而哨兵是紧接在等待之前写的,Windows 上新文件的 mtime 可能落在同一/前一个时钟刻,条件永不成立。两者叠加 → 每次调用都超时返回 false,而**所有调用方只把它当 warning**:`/sync`、工具 `git_sync`、seed 重启与启动路径的「排干 syncer」全部静默失效(启动路径还白等 8 秒)。现在按「文件名含 `dsh-tiddlywiki_flush-probe` + 文件内容含本次随机戳」判定(与扩展名/时钟无关),实测 <1.2s 返回;selftest 加了断言,另修掉一处「轮询结果被忽略就重启」的测试竞态(首轮 GitHub CI selftest 抓到的 404)。**其余同 v0.19.1**:CI 复活(`.gitattributes` LF + sourcemap 可复现)、渲染片段净化(存储型 XSS)、`/note` `/edit` 共享写策略(不再清掉人类笔记的标签/自定义字段)、重启单飞与启动/teardown 握手、`tiddlywiki_get` 字段摊平、`lint` 两个坏检查、客户端泄漏与草稿回写等。
package/lib/client.js CHANGED
@@ -720,7 +720,7 @@ html[data-dsh-tw-active] .dshDesktopConversationSurface > :not([data-dsh-tw-view
720
720
  `}${e}\n`;n.dispatch({changes:{from:t,insert:r},selection:{anchor:t+r.length},scrollIntoView:!0}),n.focus()},focus:()=>n.focus()}}const Zv=`dsh-tw-note-state`,Qv=`dsh-tw-note-draft-v1`,$v=`dsh-tw-note-window-id`,ey=(()=>{try{let e=sessionStorage.getItem($v);if(e!==null&&e.length>0)return e;let t=Math.random().toString(36).slice(2,10);return sessionStorage.setItem($v,t),t}catch{return Math.random().toString(36).slice(2,10)}})();function ty(){return`dsh-tw-note-draft-v1:${ey}`}function ny(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 ry(e){try{return ny(localStorage.getItem(e))}catch{return null}}function iy(){let e=ry(ty());if(e!==null)return{draft:e,foreign:e.windowId!==void 0&&e.windowId!==ey};let t=ry(Qv);if(t===null)return null;let n={...t,windowId:ey};oy(n);try{localStorage.removeItem(Qv)}catch{}return{draft:n,foreign:!0}}function ay(e){oy({...e,windowId:ey})}function oy(e){try{localStorage.setItem(ty(),JSON.stringify({...e,windowId:ey}))}catch{}}function sy(){try{localStorage.removeItem(ty())}catch{}}function cy(e,t,n){return`${e}\u0000${t}\u0000${n.join(``)}`}function ly(e){return e<10?`0${e}`:String(e)}function uy(e=new Date){return`${e.getFullYear()}-${ly(e.getMonth()+1)}-${ly(e.getDate())} ${ly(e.getHours())}:${ly(e.getMinutes())}`}function dy(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 fy(){let e={showQuickNote:!0,defaultTag:`inbox`};try{let t=await fetch(p,{signal:AbortSignal.timeout(5e3)});if(!t.ok)return e;let n=await t.json();return{showQuickNote:n.ui?.showQuickNote!==!1,defaultTag:n.note?.tag??`inbox`}}catch{return e}}function py(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(`/dsh-tiddlywiki/tags`,{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]),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 my(e,t){if(e.size>67108864){O(`文件过大(≤ 64MB)`);return}try{let n=await fetch(`/dsh-tiddlywiki/upload?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){O(`上传失败:${r?.error??`HTTP ${n.status}`}`);return}let i=r.name??e.name,a=e.type.startsWith(`image/`)?`![${i}](${r.url})`:`[${i}](${r.url})`;t.insertAtCaret(a),O(`已上传「${i}」并插入链接`)}catch(e){O(`上传失败:${e instanceof Error?e.message:String(e)}`)}}function hy(){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(Zv,{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=()=>{e!==void 0&&(e.titleInput.value=uy())},p=()=>{if(e===void 0)return;let t=e.editor.getValue(),n=e.titleInput.value.trim();if(t.trim().length===0&&n.length===0){sy();return}let r=cy(n,t,e.tagEditor.getTags());l!==null&&r===l||oy({text:t,title:n,tags:e.tagEditor.getTags(),savedAt:Date.now()})},m=()=>{t||!n||e===void 0||(o!==void 0&&(clearTimeout(o),o=void 0),o=window.setTimeout(()=>{o=void 0,p()},500))},h=()=>{e!==void 0&&(e.draftBanner.hidden=!0)},g=async(e,t,n)=>{try{let r=await fetch(`/dsh-tiddlywiki/edit`,{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?(O(`打开失败:${i?.error??`HTTP ${r.status}`}`),!1):typeof i.twUrl!=`string`||typeof i.draftTitle!=`string`?(O(`打开失败:服务未返回编辑器地址`),!1):(sy(),h(),l=cy(e,t,n),c=null,We(`${new URL(i.twUrl,location.origin).href}#${encodeURIComponent(i.draftTitle)}`,i.title??e),O(`已在弹出窗口打开「${i.title??e}」编辑器`),!0)}catch(e){return O(`打开失败:${e instanceof Error?e.message:String(e)}`),!1}},_=()=>{s=!1,e!==void 0&&(e.recentWrap.hidden=!0)},v=async t=>{try{let n=await fetch(`/dsh-tiddlywiki/get?title=${encodeURIComponent(t)}`,{signal:AbortSignal.timeout(1e4)}),r=await n.json().catch(()=>null);if(!n.ok||r?.ok!==!0||typeof r.title!=`string`){O(`读取失败:${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=cy(r.title,r.text??``,r.tags??[]),h(),_(),O(`已载入「${r.title}」`),e.editor.focus()}catch(e){O(`读取失败:${e instanceof Error?e.message:String(e)}`)}},y=()=>{if(e===void 0)return;if(s){_();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(`/dsh-tiddlywiki/recent?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=[dy(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`,()=>{v(t.title)}),n.addEventListener(`keydown`,e=>{(e.key===`Enter`||e.key===` `)&&(e.preventDefault(),v(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)}})()},b=()=>{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 o=document.createElement(`span`);o.className=`dsh-tw-note-label`,o.textContent=`📝 快速笔记`;let s=document.createElement(`button`);s.type=`button`,s.className=`dsh-tw-note-close`,s.title=`收起`,s.textContent=`✕`,a.append(o,s);let d=document.createElement(`div`);d.className=`dsh-tw-note-draft`,d.hidden=!0;let f=document.createElement(`span`);f.className=`dsh-tw-note-draft-text`,f.textContent=`已恢复未保存草稿`;let v=document.createElement(`button`);v.type=`button`,v.className=`dsh-tw-note-draft-discard`,v.textContent=`丢弃`,d.append(f,v);let b=document.createElement(`div`);b.className=`dsh-tw-note-fields`;let x=document.createElement(`input`);x.className=`dsh-tw-note-title`,x.placeholder=`标题(默认时间戳)`,x.setAttribute(`aria-label`,`笔记标题(默认时间戳)`);let S=py({onChange:m});b.append(x,S.el),x.addEventListener(`input`,m);let C,w=Xv({placeholder:`写点东西… Markdown 高亮,可 📎/拖入文件
721
721
  Ctrl+Enter 保存`,onSave:()=>{C?.()},onChange:m}),T=document.createElement(`button`);T.type=`button`,T.className=`dsh-tw-note-upload`,T.title=`上传文件到 wiki 并插入 Markdown 链接(也可直接拖入编辑器)`,T.textContent=`📎 上传`;let ee=document.createElement(`input`);ee.type=`file`,ee.multiple=!0,ee.hidden=!0,T.addEventListener(`click`,()=>{ee.click()}),ee.addEventListener(`change`,()=>{for(let e of Array.from(ee.files??[]))my(e,w);ee.value=``});let te=0;w.el.addEventListener(`dragenter`,e=>{e.preventDefault(),te++,w.el.classList.add(`dsh-tw-note-drop`)}),w.el.addEventListener(`dragover`,e=>{e.preventDefault()}),w.el.addEventListener(`dragleave`,e=>{e.preventDefault(),te=Math.max(0,te-1),te===0&&w.el.classList.remove(`dsh-tw-note-drop`)}),w.el.addEventListener(`drop`,e=>{e.preventDefault(),te=0,w.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))my(e,w)});let E=document.createElement(`div`);E.className=`dsh-tw-note-foot`;let D=document.createElement(`div`);D.className=`dsh-tw-note-foot-left`;let ne=document.createElement(`span`);ne.className=`dsh-tw-note-hint`,ne.textContent=`Ctrl+Enter`;let re=document.createElement(`button`);re.type=`button`,re.className=`dsh-tw-note-recent-btn`,re.title=`最近修改的笔记,点击载入继续编辑`,re.textContent=`🕘 最近`,re.addEventListener(`click`,y),D.append(T,re,ne);let ie=document.createElement(`div`);ie.className=`dsh-tw-note-foot-right`;let ae=document.createElement(`button`);ae.type=`button`,ae.className=`dsh-tw-note-edit`,ae.title=`保存并在 TiddlyWiki 原生编辑器中打开`,ae.textContent=`✏️ 在 TW 中编辑`;let oe=document.createElement(`button`);oe.type=`button`,oe.className=`dsh-tw-note-save`,oe.textContent=`保存`,ie.append(ae,oe),E.append(D,ie);let se=document.createElement(`div`);se.className=`dsh-tw-note-recent`,se.hidden=!0,r.append(a,d,b,w.el,E),t.append(r,se),document.body.append(t),e={root:t,card:r,editor:w,titleInput:x,tagEditor:S,saveBtn:oe,editBtn:ae,draftBanner:d,bannerText:f,recentBtn:re,recentWrap:se};let ce=()=>{n=!1,t.hidden=!0,_(),u(!1)},le=()=>{sy(),h(),l=cy(x.value.trim(),w.getValue(),S.getTags()),c=null,w.setValue(``),x.value=uy(),S.setTags([]),ce()},ue=!1;C=async()=>{if(ue)return;let e=w.getValue().trim();if(e.length===0){O(`内容为空,未保存`);return}ue=!0,oe.disabled=!0,oe.textContent=`保存中…`;try{let t=x.value.trim(),n={title:t,tags:S.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(`/dsh-tiddlywiki/note`,{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){O(`保存被拒绝:这篇笔记在你读取之后被改动过。请用「🕘 最近」重新载入后再保存。`);return}O(`保存失败:${i?.error??`HTTP ${r.status}`}`);return}le(),O(`已保存「${i.title??x.value}」`)}catch(e){O(`保存失败:${e instanceof Error?e.message:String(e)}`)}finally{ue=!1,oe.disabled=!1,oe.textContent=`保存`}},oe.addEventListener(`click`,()=>{C?.()});let de=async()=>{let e=x.value.trim().length>0?x.value.trim():uy(),t=w.getValue(),n=S.getTags();ae.disabled=!0,ae.textContent=`打开中…`;try{await g(e,t,n)}finally{ae.disabled=!1,ae.textContent=`✏️ 在 TW 中编辑`}};ae.addEventListener(`click`,()=>{de()}),s.addEventListener(`click`,ce);let fe=null,pe=(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(s.contains(e.target))return;e.preventDefault();let n=t.getBoundingClientRect();fe={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(fe===null)return;let t=e.clientX-fe.startX,n=e.clientY-fe.startY;!fe.moved&&Math.abs(t)<4&&Math.abs(n)<4||(fe.moved=!0,pe(fe.origLeft+t,fe.origTop+n))});let me=e=>{if(fe!==null){try{a.releasePointerCapture(e.pointerId)}catch{}fe=null,a.classList.remove(`dsh-tw-note-head-dragging`)}};a.addEventListener(`pointerup`,me),a.addEventListener(`pointercancel`,me),v.addEventListener(`click`,()=>{sy(),h(),w.setValue(``),x.value=uy(),S.setTags([]),O(`已丢弃草稿`)}),i=()=>{p()},window.addEventListener(`pagehide`,i)};return{async open(r){if(t||(b(),e===void 0||n))return;n=!0,e.root.hidden=!1,d(r),u(!0);let i=iy(),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&&(O(`已恢复其它窗口的未保存草稿`),ay(o));else{if(f(),a=(await fy()).defaultTag,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,_(),u(!1))},async openNative(){if(!(t||r||Ge())){r=!0;try{let e=iy(),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():uy(),i=n?e.draft.text:``,o=[];if(n)o=e.draft.tags;else{if(a=(await fy()).defaultTag,t)return;o=[a]}e!==null&&e.foreign&&(e.draft.text.trim().length>0||e.draft.title.trim().length>0)&&(O(`已恢复其它窗口的未保存草稿`),ay(e.draft)),await g(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),p(),e?.tagEditor.dispose(),e?.editor.view.destroy(),e?.root.remove(),document.querySelector(`.dsh-tw-toast`)?.remove(),e=void 0}}}function gy(e){return e<10?`0${e}`:String(e)}function _y(e){return`${gy(e.getHours())}:${gy(e.getMinutes())}`}async function vy(){try{let e=await fetch(p,{signal:AbortSignal.timeout(8e3)});return e.ok?await e.json():null}catch{return null}}function yy(e,t){let n=e?.git,r=[`同步知识库`],i=`offline`,a=`离线`;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`,a=`待提交`,r.push(`有 ${n.dirtyFiles?.length??0} 个未提交改动`)):e>0?(i=`behind`,a=`可更新`):(i=`clean`,a=`已同步`)}return t!==void 0&&r.push(`上次同步 ${_y(t)}`),{state:i,label:a,tooltip:r.join(` · `),...t===void 0?{}:{lastSync:t}}}function by(){let e={state:`offline`,label:`离线`,tooltip:`同步知识库`},t,n,r=!1,i=!1,a=new Set,o=()=>{for(let e of[...a])e()},s=n=>{if(r)return;let i=yy(n,t);(i.state!==e.state||i.tooltip!==e.tooltip)&&(e=i,o())},c=async()=>{if(i)return;let e=await vy();i||s(e)};return c(),n=window.setInterval(()=>{c()},3e4),{getState:()=>e,trigger:async()=>{if(i)return e;if(r)return O(`同步进行中…`),e;r=!0,e={...e,state:`syncing`,label:`同步中…`},o();try{let e=await fetch(`/dsh-tiddlywiki/sync`,{method:`POST`,signal:AbortSignal.timeout(12e4)}),n=await e.json().catch(()=>null);if(n===null||n.ok!==!0)O(`同步失败:${n?.error??n?.message??`HTTP ${e.status}`}`);else{t=new Date;let e=``;n.push&&n.push!==`nothing to commit`&&(e=`(${n.push})`),n.changed===!0&&(e+=n.restarted===!0?`,TW 已重启`:`,TW 未自动重启`,n.restartError&&(e+=`(${n.restartError})`)),O(`同步完成:${n.message??`OK`}${e}`)}}catch(e){O(`同步失败:${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()}}}const xy={showQuickNoteDock:!0,quickNoteMode:`native`,sidebarLabel:`TiddlyWiki`,tabLabel:`知识库`,showSessionTab:!0,showRightbarTab:!0};let Sy;function Cy(){Sy=void 0}async function wy(e={}){let t=Date.now();if(!e.force&&Sy!==void 0&&t-Sy.at<15e3)return Sy.value;try{let e=await fetch(p,{signal:AbortSignal.timeout(5e3)});if(!e.ok)return xy;let t=await e.json(),n={showQuickNoteDock:t.ui?.showQuickNoteDock!==!1,quickNoteMode:t.ui?.quickNoteMode===`card`?`card`:`native`,sidebarLabel:typeof t.ui?.sidebarLabel==`string`&&t.ui.sidebarLabel.trim().length>0?t.ui.sidebarLabel.trim():`TiddlyWiki`,tabLabel:typeof t.ui?.tabLabel==`string`&&t.ui.tabLabel.trim().length>0?t.ui.tabLabel.trim():`知识库`,showSessionTab:t.ui?.showSessionTab!==!1,showRightbarTab:t.ui?.showRightbarTab!==!1};return Sy={at:Date.now(),value:n},n}catch{return xy}}async function Ty(){try{let e=await fetch(p,{signal:AbortSignal.timeout(5e3)});if(!e.ok)return{showQuickNote:!0,showPanelStatus:!0,showSyncButton:!0};let t=await e.json();return{showQuickNote:t.ui?.showQuickNote!==!1,showPanelStatus:t.ui?.showPanelStatus!==!1,showSyncButton:t.ui?.showSyncButton!==!1}}catch{return{showQuickNote:!0,showPanelStatus:!0,showSyncButton:!0}}}async function Ey(){try{let e=await fetch(p,{signal:AbortSignal.timeout(8e3)});if(!e.ok)return{state:`failed`,text:`状态不可达`,logs:[]};let t=await e.json(),n=Array.isArray(t.logs)?t.logs.filter(e=>typeof e==`string`):[];return t.status===`running`?{state:`running`,text:`TW 在线 · ${t.url??``}`,logs:n}:t.status===`starting`?{state:`starting`,text:`TW 启动中…`,logs:n}:{state:`failed`,text:t.error??`TW 状态:${t.status??`?`}`,logs:n}}catch{return{state:`failed`,text:`状态不可达`,logs:[]}}}function Dy(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(`
722
722
  `)},_=()=>{let e=n.getState();o!==void 0&&(o.dataset.state=e.state),a!==void 0&&(a.title=`知识库 · ${e.tooltip}`),g()},v=async()=>{p=await Ey(),!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(),wy().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(me)),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)},b=e.subscribe(()=>{d!==void 0&&(d.textContent=e.isOpen()?`收起 TW 面板`:`打开 TW 面板`)}),x=n.subscribe(_);return(async()=>{let e=await Ty();r||!e.showQuickNote&&!e.showPanelStatus&&!e.showSyncButton||y(e)})(),()=>{r=!0,document.removeEventListener(`click`,h,!0),b(),x(),i?.remove()}}function Oy(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=e=>{let t=e.detail;n(t?.open===!0)};return window.addEventListener(Zv,e),wy().then(e=>i(e.quickNoteMode)),()=>window.removeEventListener(Zv,e)},[]),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:()=>{wy().then(t=>{t.quickNoteMode===`native`?Ge()?qe():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)))}}function ky(e){ge(e)}function Ay(){return _e()}async function jy(e){try{let t=await fetch(h,{method:`POST`,headers:{"content-type":`application/json`,"x-requested-with":`TiddlyWiki`},body:JSON.stringify({title:e}),signal:AbortSignal.timeout(15e3)});if(!t.ok)return null;let n=await t.text();return n.length>0?n:null}catch{return null}}async function My(e){try{let t=await fetch(`${m}?title=${encodeURIComponent(e)}`,{signal:AbortSignal.timeout(8e3)});return t.status!==404&&(await t.json().catch(()=>null))?.notFound!==!0}catch{return!0}}function Ny(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),h=c.useRef(0),g=c.useCallback(async()=>{let e=++p.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(`/dsh-tiddlywiki/session/summary`,{method:`POST`,headers:{"content-type":`application/json`},body:JSON.stringify({session:t}),signal:AbortSignal.timeout(25e3)}),r=await n.json().catch(()=>null);if(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 jy(r.title);if(e!==p.current)return;if(i===null){let t=await My(r.title);if(e!==p.current)return;if(!t){if(h.current++,h.current<3){g();return}a(`error`),f(`汇总条目不存在,自动重建失败,请重试`);return}h.current++,a(`error`),f(`渲染失败:wiki 渲染服务不可用(/tw/render)`);return}h.current=0,s(i),a(`ready`)}catch(t){if(e!==p.current)return;h.current++,a(`error`),f(t instanceof Error?t.message:String(t))}},[t]);c.useEffect(()=>{g()},[g]),c.useEffect(()=>{if(i!==`ready`||l===null)return;let e=!0,t=window.setInterval(()=>{(async()=>{if(!e)return;let t=!1;try{let e=await fetch(`${m}?title=${encodeURIComponent(l)}`,{signal:AbortSignal.timeout(8e3)}),n=await e.json().catch(()=>null);t=e.status!==404&&n?.notFound!==!0}catch{return}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 Py(e,t){if(!(e===void 0||t.showSessionTab===!1))return ky(t.tabLabel),e.inject(`conversation.view`,()=>e.register({name:`conversation.view`,id:`dsh-tiddlywiki-summary`,order:20,label:Ay},Ny))}const Fy=`/dsh-tiddlywiki/admin/info`,Iy=`/dsh-tiddlywiki/admin/seeds/run`;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 Ly(e,t){let n=await fetch(e,{...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 Ry(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=async()=>{try{let e=await Ly(`/dsh-tiddlywiki/admin/state`);if(t)return;zy(n,e,a),Uy(r,e,a,()=>t)}catch(e){if(t)return;r.replaceChildren(),n.replaceChildren();let i=$(`div`,`dsh-tw-settings-error`,`加载配置失败:${e instanceof Error?e.message:String(e)}`),o=$(`button`,`dsh-tw-settings-btn`,`重试`);o.type=`button`,o.addEventListener(`click`,()=>{a()}),r.append(i,o)}};return a(),i.push(()=>{t=!0,e.replaceChildren(),e.classList.remove(`dsh-tw-settings`)}),()=>{for(let e of i.splice(0))e()}}function zy(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?.dirty===!0?`有未提交改动`:``].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(`/dsh-tiddlywiki/sync`,{method:`POST`,signal:AbortSignal.timeout(12e4)}),t=await e.json().catch(()=>null);if(!e.ok||t?.ok!==!0)O(`同步失败:${t?.error??t?.message??`HTTP ${e.status}`}`);else{let e=``;t.changed===!0&&(e+=t.restarted===!0?`,TW 已重启`:`,TW 未自动重启`,t.restartError&&(e+=`(${t.restartError})`)),O(`同步完成:${t.message??`OK`}${e}`)}}catch(e){O(`同步失败:${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 Ly(`/dsh-tiddlywiki/admin/restart`,{method:`POST`}),O(`TW 已重启`)}catch(e){O(`重启失败:${e instanceof Error?e.message:String(e)}`)}finally{c.disabled=!1,c.textContent=`重启 TW`,n()}})()}),e.append(a,o,s,c)}function By(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,input:i,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`);a.append(i,$(`span`,`dsh-tw-settings-label`,t)),s.push({key:e,input:i,initial:n,read:()=>i.checked,changed:()=>i.checked!==n}),r.append(a)},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,input:i,initial:n,read:a,changed:()=>a()!==n}),r.append(o)};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`),l(`ui.showQuickNote`,`显示「知识库」按钮里的「快速笔记」入口`,o.showQuickNote!==!1),l(`ui.showQuickNoteDock`,`显示聊天输入框上方的「快速笔记」快捷按钮`,o.showQuickNoteDock!==!1),((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`);o.append($(`span`,`dsh-tw-settings-label`,t),a),s.push({key:e,input:a,initial:n,read:()=>a.value,changed:()=>a.value!==n}),r.append(o)})(`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 d=o.sendToAgent??{};l(`ui.sendToAgent.enabled`,`启用「发送给 Agent」(TW 笔记 → DSH 会话注入)`,d.enabled!==!1),c(`ui.sendToAgent.endpoint`,`TW 端请求基址(空=自动取当前 DSH origin)`,typeof d.endpoint==`string`?d.endpoint:``),c(`ui.sendToAgent.token`,`共享 token(非空时路由校验 x-send-to-agent-token 头;已设置时显示为 ********,原样保存=不改,清空=删除)`,typeof d.token==`string`?d.token:``);let f=o.allArticles??{};u(`ui.allArticles.pageSize`,`「所有文章」每页条数`,typeof f.pageSize==`number`?f.pageSize:10);let p=t.bridge??{};l(`bridge.enabled`,`启用「本地剪藏桥」(书签小工具后端监听 127.0.0.1 端口,保存后立即生效)`,p.enabled===!0),u(`bridge.port`,`剪藏桥端口(改端口需重启 dsh web 生效)`,typeof p.port==`number`&&p.port>0?p.port:8618),c(`bridge.token`,`剪藏桥 token(非空时校验书签的 x-clip-token 头;强烈建议设置。已设置时显示为 ********,原样保存=不改,清空=删除)`,typeof p.token==`string`?p.token:``),c(`bridge.tag`,`剪藏笔记默认 tag`,typeof p.tag==`string`&&p.tag.trim().length>0?p.tag.trim():`clip`);let m=$(`button`,`dsh-tw-settings-btn dsh-tw-settings-primary`,`保存配置`);m.type=`button`,m.addEventListener(`click`,()=>{m.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 Ly(`/dsh-tiddlywiki/admin/config`,{method:`POST`,headers:{"content-type":`application/json`},body:JSON.stringify(e)}),Cy(),O(`配置已保存`),n()}catch(e){O(`保存失败:${e instanceof Error?e.message:String(e)}`)}finally{m.disabled=!1}})()}),r.append(m),e.append(r)}function Vy(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 Ly(Fy,{method:`POST`,headers:{"content-type":`application/json`},body:JSON.stringify({plugins:[...s]})}),O(`插件已应用,TW 已重启`),r()}catch(e){O(`应用失败:${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=v.length>0?v[v.length-1]:`tiddlywiki/vanilla`,b=$(`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===y,n.title=`设为活动主题`,n.addEventListener(`change`,()=>{n.checked&&(y=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),b.append(a)}let x=$(`button`,`dsh-tw-settings-btn dsh-tw-settings-primary`,`应用主题(重启 TW)`);x.type=`button`,x.addEventListener(`click`,()=>{x.disabled=!0,(async()=>{try{await Ly(Fy,{method:`POST`,headers:{"content-type":`application/json`},body:JSON.stringify({themes:[...c],themeActive:y})}),O(`主题已应用,TW 已重启`),r()}catch(e){O(`应用失败:${e instanceof Error?e.message:String(e)}`),x.disabled=!1}})()}),h.append(g,_,b,x),e.append(h);let S=$(`section`,`dsh-tw-settings-section`);S.append($(`h3`,`dsh-tw-settings-h`,`语言管理(自带官方语言包)`));let C=$(`div`,`dsh-tw-settings-muted`,`勾选启用语言插件并重启 TW;如中文请选 zh-Hans(简体)或 zh-CN。`),w=$(`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),w.append(n)}let T=$(`button`,`dsh-tw-settings-btn dsh-tw-settings-primary`,`应用语言(重启 TW)`);T.type=`button`,T.addEventListener(`click`,()=>{T.disabled=!0,(async()=>{try{await Ly(Fy,{method:`POST`,headers:{"content-type":`application/json`},body:JSON.stringify({languages:[...l]})}),O(`语言已应用,TW 已重启`),r()}catch(e){O(`应用失败:${e instanceof Error?e.message:String(e)}`),T.disabled=!1}})()}),S.append(C,w,T),e.append(S)}function Hy(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=`/dsh-tiddlywiki/admin/seeds/remove`,s=async()=>{if(!t())try{let e=await Ly(`/dsh-tiddlywiki/admin/seeds`);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 c=$(`span`,`dsh-tw-settings-muted`,t.detail??t.description),l=$(`button`,`dsh-tw-settings-btn`,`重新初始化`);l.type=`button`,l.title=`强制重写「${t.title}」的内置内容(会覆盖当前 tiddler)`,l.addEventListener(`click`,()=>{l.disabled=!0,l.textContent=`执行中…`,(async()=>{try{let e=await fetch(Iy,{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?O(`重新初始化失败:${n?.error??n?.results?.[0]?.error??`HTTP ${e.status}`}`):O(`已重新初始化「${t.title}」`),s()}catch(e){O(`重新初始化失败:${e instanceof Error?e.message:String(e)}`)}finally{l.disabled=!1,l.textContent=`重新初始化`}})()});let u=$(`div`,`dsh-tw-settings-row dsh-tw-settings-plugin`);if(u.append(e,a,c,l),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(o,{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?O(`反初始化失败:${n?.error??n?.results?.[0]?.error??`HTTP ${e.status}`}`):O(`已反初始化「${t.title}」`),s()}catch(e){O(`反初始化失败:${e instanceof Error?e.message:String(e)}`)}finally{e.disabled=!1,e.textContent=`反初始化`}})())}),u.append(e)}i.append(u)}a.textContent=`共 ${e.items.length} 项,${n} 项已就绪;可移除 ${r} 项`}catch(e){if(t())return;a.textContent=`加载初始化状态失败:${e instanceof Error?e.message:String(e)}`}},c=$(`button`,`dsh-tw-settings-btn dsh-tw-settings-primary`,`全部重新初始化`);c.type=`button`,c.title=`强制重写所有一次性预置内容(含可选 seed,会覆盖当前 tiddler/配置)`,c.addEventListener(`click`,()=>{c.disabled=!0,c.textContent=`执行中…`,(async()=>{try{let e=await fetch(Iy,{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?O(`重新初始化失败:${t?.error??`HTTP ${e.status}`}`):O(`全部已重新初始化`),s()}catch(e){O(`重新初始化失败:${e instanceof Error?e.message:String(e)}`)}finally{c.disabled=!1,c.textContent=`全部重新初始化`}})()});let l=$(`button`,`dsh-tw-settings-btn dsh-tw-settings-danger`,`全部反初始化`);l.type=`button`,l.title=`移除所有可选 seed 写入的 tiddler 与 marker(功能必需项保留)`,l.addEventListener(`click`,()=>{window.confirm(`确定全部反初始化?将删除所有可选 seed 写入的 tiddler(含一次性 marker)。功能必需项(发送给 Agent 按钮 / TW 前端 API 基址)会保留。`)&&(l.disabled=!0,l.textContent=`移除中…`,(async()=>{try{let e=await fetch(o,{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?O(`反初始化失败:${t?.error??`HTTP ${e.status}`}`):O(`全部可选 seed 已反初始化`),s()}catch(e){O(`反初始化失败:${e instanceof Error?e.message:String(e)}`)}finally{l.disabled=!1,l.textContent=`全部反初始化`}})())}),n.append(r,a,i,c,l),e.append(n),s()}function Uy(e,t,n,r){e.replaceChildren(),By(e,t.config??{},n),Vy(e,t.info,t.catalog,n),Hy(e,r)}function Wy(){let e=c.useRef(null);return c.useEffect(()=>{let t=e.current;return t===null?void 0:Ry(t)},[]),c.createElement(`div`,{ref:e})}const Gy={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 Ky(e){return typeof e==`object`&&!!e&&e.kind===`tool-result`}function qy(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 Jy(e){try{let t=JSON.parse(e);return typeof t==`object`&&t&&!Array.isArray(t)?t:{}}catch{return{}}}function Yy(e){return Array.isArray(e)?e.filter(e=>typeof e?.text==`string`).map(e=>e.text).join(`
723
- `):``}function Xy(e){return typeof e==`string`?e:``}async function Zy(e){try{let t=await fetch(e,{signal:AbortSignal.timeout(1e4)});if(!t.ok)return null;let n=await t.json();return typeof n==`object`&&n?n:null}catch{return null}}async function Qy(e){try{let t=await fetch(h,{method:`POST`,headers:{"content-type":`application/json`,"x-requested-with":`TiddlyWiki`},body:JSON.stringify({title:e}),signal:AbortSignal.timeout(1e4)});return t.ok?await t.text():null}catch{return null}}function $y(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 eb(e){return t=>{t.defaultPrevented||t.button!==0||t.metaKey||t.ctrlKey||t.shiftKey||t.altKey||(t.preventDefault(),t.stopPropagation(),Me(e))}}function tb(e){let t=Gy[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=>c.createElement(`span`,{className:`dsh-tw-toolcard-tag`,key: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 nb=new Map;function rb(e){let t=nb.get(e);if(t!==void 0){if(Date.now()-t.at>3e5){nb.delete(e);return}return nb.delete(e),nb.set(e,t),t}}function ib(e,t){for(nb.delete(e),nb.set(e,{at:Date.now(),...t});nb.size>50;){let e=nb.keys().next().value;if(e===void 0)break;nb.delete(e)}}function ab(e){e.length>0&&nb.delete(e)}function ob(e){let t=e.join(`\0`);c.useEffect(()=>{for(let e of t.split(`\0`))ab(e)},[t])}function sb(e){let{title:t,fresh:n}=e,{loading:r,data:i}=$y(async()=>{n===!0&&ab(t);let e=rb(t);if(e!==void 0)return{get:e.get,html:e.html};let[r,i]=await Promise.all([Zy(`${m}?title=${encodeURIComponent(t)}`),Qy(t)]);return typeof i==`string`&&i.length>0&&ib(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(tb,{toolName:e.toolName,title:t,subtitle:e.subtitle,tags:l,onOpen:eb(t),foot:u===void 0?void 0:`修改于 ${u}`},s)}function cb(e){return c.createElement(`a`,{className:`dsh-tw-toolcard-row`,href:`/dsh-tiddlywiki/tw/#${encodeURIComponent(e.title)}`,onClick:eb(e.title),title:e.title},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)}function lb(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(cb,{key:`${e.title}-${t}`,...e}))),c.createElement(tb,{toolName:e.toolName,title:e.title,subtitle:e.subtitle},n)}function ub(e){let t=Xy(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.limit==`number`&&n.set(`limit`,String(e.args.limit));let r=n.toString(),i=$y(()=>Zy(`/dsh-tiddlywiki/search?${r}`),[r]).data,a=(Array.isArray(i?.items)?i.items:[]).map(e=>({title:Xy(e.title),tags:Array.isArray(e.tags)?e.tags.filter(e=>typeof e==`string`):[],modified:typeof e.modified==`string`?e.modified:null})),o=typeof i?.total==`number`?i.total:void 0,s=`关键词「${t||`(全部)`}」${o===void 0?``:` · 共 ${o} 条`}`;return c.createElement(lb,{toolName:e.toolName,title:t.length>0?t:`检索`,subtitle:s,rows:a,fallbackText:`没有匹配的笔记`})}function db(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=$y(()=>Zy(`/dsh-tiddlywiki/recent?${n}`),[n]).data,i=(Array.isArray(r?.items)?r.items:[]).map(e=>({title:Xy(e.title),tags:Array.isArray(e.tags)?e.tags.filter(e=>typeof e==`string`):[],modified:typeof e.modified==`string`?e.modified:null}));return c.createElement(lb,{toolName:e.toolName,title:`最近修改`,subtitle:i.length>0?`最近 ${i.length} 条`:``,rows:i,fallbackText:`暂无笔记`})}function fb(e){let t=(Array.isArray(e.args.items)?e.args.items:[]).map(e=>typeof e==`object`&&e&&typeof e.title==`string`?Xy(e.title):null).filter(e=>e!==null&&e.length>0).map(e=>({title:e}));return ob(t.map(e=>e.title)),c.createElement(lb,{toolName:e.toolName,title:`批量写入`,subtitle:t.length>0?`写入 ${t.length} 篇`:`(参数中没有可解析的 items)`,rows:t,fallbackText:`没有可展示的笔记`})}function pb(e){let t=$y(()=>Zy(`/dsh-tiddlywiki/tags`),[]).data,n=(Array.isArray(t?.items)?t.items:[]).map(e=>({tag:Xy(e.tag),count:typeof e.count==`number`?e.count:0})),r=n.slice(0,60),i;return i=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}`)),...n.length>r.length?[c.createElement(`span`,{className:`dsh-tw-toolcard-tag-more`,key:`__more`},`…另有 ${n.length-r.length} 个`)]:[]),c.createElement(tb,{toolName:e.toolName,title:`标签`,subtitle:`共 ${n.length} 个`},i)}function mb(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(tb,{toolName:e.toolName},t)}function hb(e){ob([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(tb,{toolName:e.toolName,title:e.title.length>0?e.title:void 0},t)}function gb(e,t){switch(e){case`tiddlywiki_get`:case`tiddlywiki_put`:case`tiddlywiki_append`:case`tiddlywiki_delete`:case`tiddlywiki_attach`:case`tiddlywiki_backlinks`:return Xy(t.title);case`tiddlywiki_rename`:return Xy(t.newTitle);case`tiddlywiki_search`:return Xy(t.query);case`tiddlywiki_recent`:case`tiddlywiki_list_tags`:case`tiddlywiki_batch_put`:case`tiddlywiki_trash`:case`tiddlywiki_lint`:case`tiddlywiki_git_sync`:case`tiddlywiki_git_resolve`:return``;default:return``}}function _b(e){let t=e.block,n=e.toolName,r=Ky(t),i=qy(t),a=n!==void 0&&n.length>0?n:i?.name??``,o=Jy(i?.argsRaw??``),s=r?Yy(t.content):``;if(i===null)return c.createElement(tb,{toolName:a},c.createElement(`pre`,{className:`dsh-tw-toolcard-fallback`},s||`(无调用信息)`));if(!r)return c.createElement(tb,{toolName:a,title:gb(a,o)},c.createElement(`div`,{className:`dsh-tw-toolcard-pending`},`处理中…`));if(t.isError===!0)return c.createElement(tb,{toolName:a,title:gb(a,o)},c.createElement(`div`,{className:`dsh-tw-toolcard-error`},s||`工具调用失败`));switch(a){case`tiddlywiki_get`:return c.createElement(sb,{toolName:a,title:Xy(o.title),subtitle:`读取`});case`tiddlywiki_put`:return c.createElement(sb,{toolName:a,title:Xy(o.title),subtitle:`已写入`,fresh:!0});case`tiddlywiki_append`:return c.createElement(sb,{toolName:a,title:Xy(o.title),subtitle:`已增量写入`,fresh:!0});case`tiddlywiki_rename`:return c.createElement(sb,{toolName:a,title:Xy(o.newTitle),subtitle:`已重命名「${Xy(o.oldTitle)}」→`,fresh:!0});case`tiddlywiki_delete`:return c.createElement(hb,{toolName:a,title:Xy(o.title),text:s});case`tiddlywiki_batch_put`:return c.createElement(fb,{toolName:a,args:o,text:s});case`tiddlywiki_search`:return c.createElement(ub,{toolName:a,args:o,text:s});case`tiddlywiki_recent`:return c.createElement(db,{toolName:a,args:o,text:s});case`tiddlywiki_list_tags`:return c.createElement(pb,{toolName:a,text:s});case`tiddlywiki_git_sync`:case`tiddlywiki_git_resolve`:return c.createElement(mb,{toolName:a,text:s});default:return c.createElement(tb,{toolName:a,title:gb(a,o)},c.createElement(`pre`,{className:`dsh-tw-toolcard-fallback`},s||`(完成)`))}}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`,`tiddlywiki_git_resolve`];function yb(e){let t=[];for(let n of vb)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}`},_b));r!==void 0&&t.push(r)}catch(e){console.error(`[dsh-tiddlywiki] register toolview ${n} failed:`,e)}return t}function bb(){let e=e=>{if(e.defaultPrevented||e.button!==0||e.metaKey||e.ctrlKey||e.shiftKey||e.altKey)return;let t=e.target;if(!(t instanceof Element))return;let n=t.closest(`a`);if(n===null)return;let r=(n.getAttribute(`href`)??``).match(/^\/dsh-tiddlywiki\/tw\/#(.+)$/);if(r===null||r[1]===void 0)return;e.preventDefault(),e.stopPropagation();let i=r[1];try{i=decodeURIComponent(i)}catch{}Me(i)};return document.addEventListener(`click`,e,!0),()=>document.removeEventListener(`click`,e,!0)}const xb=`dsh-tiddlywiki`,Sb=`dsh-tiddlywiki/rightbar`;function Cb(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=Te(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(pe,{detail:Sb}));let e=e=>{e.detail!==Sb&&o()};return document.addEventListener(pe,e),()=>document.removeEventListener(pe,e)},[a,o]),c.createElement(`div`,{ref:r,className:`dsh-tw-rightbar-tab`,"data-dsh-tw-rightbar":``})}function wb(){return{id:xb,kind:`dsh-tiddlywiki`,title:()=>_e(),guide:[{order:10,title:()=>`TiddlyWiki 知识库`,description:()=>`在右侧边栏打开 TiddlyWiki 编辑器(与聊天并排)`,icon:be}]}}function Tb(e,t){let n=[];try{n.push(e.register(wb()));let r=t.inject(`sidebar.right.pane.tab`,()=>t.register({name:`sidebar.right.pane.tab`,key:xb},Cb));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 we(),()=>{for(let e of n.splice(0))e()}}const Eb=[`slots`];function Db(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=hy(),t.push(()=>o?.dispose())}),i(`sync controller`,()=>{s=by(),t.push(()=>s?.dispose())}),i(`sidebar entry`,()=>t.push(b(a))),i(`center panel`,()=>t.push(Le(a))),o!==void 0&&s!==void 0){let e=o,n=s;i(`knowledge FAB`,()=>t.push(Dy(a,e,n)))}i(`editor popup`,()=>t.push(Je)),e.slots!==void 0&&wy().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:`快速笔记`},Oy(a)));n!==void 0&&t.push(n)}),i(`session summary tab`,()=>{let n=Py(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}wy().then(r=>{if(n||!r.showRightbarTab)return;let i=Tb(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(...yb(e.slots))}),i(`wiki link interceptor`,()=>t.push(bb())),i(`settings section`,()=>{let n=e.slots?.inject(`settings.section`,()=>e.slots?.register({name:`settings.section`,id:`dsh-tiddlywiki`,order:50,label:`TiddlyWiki 知识库`},Wy));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=Db,exports.inject=Eb,exports.name=`dsh-tiddlywiki/client`;
723
+ `):``}function Xy(e){return typeof e==`string`?e:``}async function Zy(e){try{let t=await fetch(e,{signal:AbortSignal.timeout(1e4)});if(!t.ok)return null;let n=await t.json();return typeof n==`object`&&n?n:null}catch{return null}}async function Qy(e){try{let t=await fetch(h,{method:`POST`,headers:{"content-type":`application/json`,"x-requested-with":`TiddlyWiki`},body:JSON.stringify({title:e}),signal:AbortSignal.timeout(1e4)});return t.ok?await t.text():null}catch{return null}}function $y(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 eb(e){return t=>{t.defaultPrevented||t.button!==0||t.metaKey||t.ctrlKey||t.shiftKey||t.altKey||(t.preventDefault(),t.stopPropagation(),Me(e))}}function tb(e){let t=Gy[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=>c.createElement(`span`,{className:`dsh-tw-toolcard-tag`,key: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 nb=new Map;function rb(e){let t=nb.get(e);if(t!==void 0){if(Date.now()-t.at>3e5){nb.delete(e);return}return nb.delete(e),nb.set(e,t),t}}function ib(e,t){for(nb.delete(e),nb.set(e,{at:Date.now(),...t});nb.size>50;){let e=nb.keys().next().value;if(e===void 0)break;nb.delete(e)}}function ab(e){e.length>0&&nb.delete(e)}function ob(e){let t=e.join(`\0`);c.useEffect(()=>{for(let e of t.split(`\0`))ab(e)},[t])}function sb(e){let{title:t,fresh:n}=e,{loading:r,data:i}=$y(async()=>{n===!0&&ab(t);let e=rb(t);if(e!==void 0)return{get:e.get,html:e.html};let[r,i]=await Promise.all([Zy(`${m}?title=${encodeURIComponent(t)}`),Qy(t)]);return typeof i==`string`&&i.length>0&&ib(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(tb,{toolName:e.toolName,title:t,subtitle:e.subtitle,tags:l,onOpen:eb(t),foot:u===void 0?void 0:`修改于 ${u}`},s)}function cb(e){return c.createElement(`a`,{className:`dsh-tw-toolcard-row`,href:`/dsh-tiddlywiki/tw/#${encodeURIComponent(e.title)}`,onClick:eb(e.title),title:e.title},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)}function lb(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(cb,{key:`${e.title}-${t}`,...e}))),c.createElement(tb,{toolName:e.toolName,title:e.title,subtitle:e.subtitle},n)}function ub(e){let t=Xy(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.limit==`number`&&n.set(`limit`,String(e.args.limit));let r=n.toString(),i=$y(()=>Zy(`/dsh-tiddlywiki/search?${r}`),[r]).data,a=(Array.isArray(i?.items)?i.items:[]).map(e=>({title:Xy(e.title),tags:Array.isArray(e.tags)?e.tags.filter(e=>typeof e==`string`):[],modified:typeof e.modified==`string`?e.modified:null})),o=typeof i?.total==`number`?i.total:void 0,s=`关键词「${t||`(全部)`}」${o===void 0?``:` · 共 ${o} 条`}`;return c.createElement(lb,{toolName:e.toolName,title:t.length>0?t:`检索`,subtitle:s,rows:a,fallbackText:`没有匹配的笔记`})}function db(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=$y(()=>Zy(`/dsh-tiddlywiki/recent?${n}`),[n]).data,i=(Array.isArray(r?.items)?r.items:[]).map(e=>({title:Xy(e.title),tags:Array.isArray(e.tags)?e.tags.filter(e=>typeof e==`string`):[],modified:typeof e.modified==`string`?e.modified:null}));return c.createElement(lb,{toolName:e.toolName,title:`最近修改`,subtitle:i.length>0?`最近 ${i.length} 条`:``,rows:i,fallbackText:`暂无笔记`})}function fb(e){let t=(Array.isArray(e.args.items)?e.args.items:[]).map(e=>typeof e==`object`&&e&&typeof e.title==`string`?Xy(e.title):null).filter(e=>e!==null&&e.length>0).map(e=>({title:e}));return ob(t.map(e=>e.title)),c.createElement(lb,{toolName:e.toolName,title:`批量写入`,subtitle:t.length>0?`写入 ${t.length} 篇`:`(参数中没有可解析的 items)`,rows:t,fallbackText:`没有可展示的笔记`})}function pb(e){let t=$y(()=>Zy(`/dsh-tiddlywiki/tags?limit=60&sort=count`),[]).data,n=(Array.isArray(t?.items)?t.items:[]).map(e=>({tag:Xy(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(tb,{toolName:e.toolName,title:`标签`,subtitle:`共 ${i} 个`},a)}function mb(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(tb,{toolName:e.toolName},t)}function hb(e){ob([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(tb,{toolName:e.toolName,title:e.title.length>0?e.title:void 0},t)}function gb(e,t){switch(e){case`tiddlywiki_get`:case`tiddlywiki_put`:case`tiddlywiki_append`:case`tiddlywiki_delete`:case`tiddlywiki_attach`:case`tiddlywiki_backlinks`:return Xy(t.title);case`tiddlywiki_rename`:return Xy(t.newTitle);case`tiddlywiki_search`:return Xy(t.query);case`tiddlywiki_recent`:case`tiddlywiki_list_tags`:case`tiddlywiki_batch_put`:case`tiddlywiki_trash`:case`tiddlywiki_lint`:case`tiddlywiki_git_sync`:case`tiddlywiki_git_resolve`:return``;default:return``}}function _b(e){let t=e.block,n=e.toolName,r=Ky(t),i=qy(t),a=n!==void 0&&n.length>0?n:i?.name??``,o=Jy(i?.argsRaw??``),s=r?Yy(t.content):``;if(i===null)return c.createElement(tb,{toolName:a},c.createElement(`pre`,{className:`dsh-tw-toolcard-fallback`},s||`(无调用信息)`));if(!r)return c.createElement(tb,{toolName:a,title:gb(a,o)},c.createElement(`div`,{className:`dsh-tw-toolcard-pending`},`处理中…`));if(t.isError===!0)return c.createElement(tb,{toolName:a,title:gb(a,o)},c.createElement(`div`,{className:`dsh-tw-toolcard-error`},s||`工具调用失败`));switch(a){case`tiddlywiki_get`:return c.createElement(sb,{toolName:a,title:Xy(o.title),subtitle:`读取`});case`tiddlywiki_put`:return c.createElement(sb,{toolName:a,title:Xy(o.title),subtitle:`已写入`,fresh:!0});case`tiddlywiki_append`:return c.createElement(sb,{toolName:a,title:Xy(o.title),subtitle:`已增量写入`,fresh:!0});case`tiddlywiki_rename`:return c.createElement(sb,{toolName:a,title:Xy(o.newTitle),subtitle:`已重命名「${Xy(o.oldTitle)}」→`,fresh:!0});case`tiddlywiki_delete`:return c.createElement(hb,{toolName:a,title:Xy(o.title),text:s});case`tiddlywiki_batch_put`:return c.createElement(fb,{toolName:a,args:o,text:s});case`tiddlywiki_search`:return c.createElement(ub,{toolName:a,args:o,text:s});case`tiddlywiki_recent`:return c.createElement(db,{toolName:a,args:o,text:s});case`tiddlywiki_list_tags`:return c.createElement(pb,{toolName:a,text:s});case`tiddlywiki_git_sync`:case`tiddlywiki_git_resolve`:return c.createElement(mb,{toolName:a,text:s});default:return c.createElement(tb,{toolName:a,title:gb(a,o)},c.createElement(`pre`,{className:`dsh-tw-toolcard-fallback`},s||`(完成)`))}}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`,`tiddlywiki_git_resolve`];function yb(e){let t=[];for(let n of vb)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}`},_b));r!==void 0&&t.push(r)}catch(e){console.error(`[dsh-tiddlywiki] register toolview ${n} failed:`,e)}return t}function bb(){let e=e=>{if(e.defaultPrevented||e.button!==0||e.metaKey||e.ctrlKey||e.shiftKey||e.altKey)return;let t=e.target;if(!(t instanceof Element))return;let n=t.closest(`a`);if(n===null)return;let r=(n.getAttribute(`href`)??``).match(/^\/dsh-tiddlywiki\/tw\/#(.+)$/);if(r===null||r[1]===void 0)return;e.preventDefault(),e.stopPropagation();let i=r[1];try{i=decodeURIComponent(i)}catch{}Me(i)};return document.addEventListener(`click`,e,!0),()=>document.removeEventListener(`click`,e,!0)}const xb=`dsh-tiddlywiki`,Sb=`dsh-tiddlywiki/rightbar`;function Cb(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=Te(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(pe,{detail:Sb}));let e=e=>{e.detail!==Sb&&o()};return document.addEventListener(pe,e),()=>document.removeEventListener(pe,e)},[a,o]),c.createElement(`div`,{ref:r,className:`dsh-tw-rightbar-tab`,"data-dsh-tw-rightbar":``})}function wb(){return{id:xb,kind:`dsh-tiddlywiki`,title:()=>_e(),guide:[{order:10,title:()=>`TiddlyWiki 知识库`,description:()=>`在右侧边栏打开 TiddlyWiki 编辑器(与聊天并排)`,icon:be}]}}function Tb(e,t){let n=[];try{n.push(e.register(wb()));let r=t.inject(`sidebar.right.pane.tab`,()=>t.register({name:`sidebar.right.pane.tab`,key:xb},Cb));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 we(),()=>{for(let e of n.splice(0))e()}}const Eb=[`slots`];function Db(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=hy(),t.push(()=>o?.dispose())}),i(`sync controller`,()=>{s=by(),t.push(()=>s?.dispose())}),i(`sidebar entry`,()=>t.push(b(a))),i(`center panel`,()=>t.push(Le(a))),o!==void 0&&s!==void 0){let e=o,n=s;i(`knowledge FAB`,()=>t.push(Dy(a,e,n)))}i(`editor popup`,()=>t.push(Je)),e.slots!==void 0&&wy().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:`快速笔记`},Oy(a)));n!==void 0&&t.push(n)}),i(`session summary tab`,()=>{let n=Py(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}wy().then(r=>{if(n||!r.showRightbarTab)return;let i=Tb(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(...yb(e.slots))}),i(`wiki link interceptor`,()=>t.push(bb())),i(`settings section`,()=>{let n=e.slots?.inject(`settings.section`,()=>e.slots?.register({name:`settings.section`,id:`dsh-tiddlywiki`,order:50,label:`TiddlyWiki 知识库`},Wy));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=Db,exports.inject=Eb,exports.name=`dsh-tiddlywiki/client`;
724
724
  return module.exports;
725
725
  }
726
726
  });
package/lib/index.js CHANGED
@@ -836,8 +836,18 @@ var TiddlyWebClient = class TiddlyWebClient {
836
836
  /**
837
837
  * Distinct non-system tags with their tiddler counts, most-used first then
838
838
  * zh-locale. One skinny listing request (no text payloads).
839
+ *
840
+ * `limit` (v0.19.4) caps the returned array after sorting; the caller loses
841
+ * the tail count unless it also asks `tagStats()`.
839
842
  */
840
- async listTags() {
843
+ async listTags(limit) {
844
+ const tags = (await this.tagStats()).tags;
845
+ if (limit === void 0) return tags;
846
+ return tags.slice(0, Math.max(1, Math.min(Math.floor(limit), 1e3)));
847
+ }
848
+ /** Full tag vocabulary + its size, so callers that cap can still report the
849
+ * total ("showing 200 of N") — one listing request either way. */
850
+ async tagStats() {
841
851
  const items = await this.list(TEXT_LIST_FILTER, false);
842
852
  const map = /* @__PURE__ */ new Map();
843
853
  for (const t of items) {
@@ -847,10 +857,14 @@ var TiddlyWebClient = class TiddlyWebClient {
847
857
  map.set(tag, (map.get(tag) ?? 0) + 1);
848
858
  }
849
859
  }
850
- return [...map.entries()].sort((a, b) => b[1] - a[1] || a[0].localeCompare(b[0], "zh")).map(([tag, count]) => ({
860
+ const tags = [...map.entries()].sort((a, b) => b[1] - a[1] || a[0].localeCompare(b[0], "zh")).map(([tag, count]) => ({
851
861
  tag,
852
862
  count
853
863
  }));
864
+ return {
865
+ total: tags.length,
866
+ tags
867
+ };
854
868
  }
855
869
  };
856
870
  /** Clamp a caller-supplied limit into 1…200 (default when absent/invalid). */
@@ -3747,6 +3761,24 @@ function snippetOf$1(text, max = 120) {
3747
3761
  const flat = text.replace(/\s+/g, " ").trim();
3748
3762
  return flat.length <= max ? flat : `${flat.slice(0, max)}…`;
3749
3763
  }
3764
+ /** Max `limit` accepted by the list routes (bounded payloads, v0.19.4). */
3765
+ const MAX_LIST_LIMIT = 200;
3766
+ /** Max `limit` accepted by `/tags` — the tag list is a small wrapper around one
3767
+ * full listing, so a larger cap is fine while still bounding the payload. */
3768
+ const MAX_TAGS_LIMIT = 500;
3769
+ /** Clamp a `limit` query param. `fallback` covers absent/unparsable values. */
3770
+ function readLimit(url, fallback, max = MAX_LIST_LIMIT) {
3771
+ const raw = Number(url.searchParams.get("limit") ?? fallback);
3772
+ return Number.isFinite(raw) ? Math.max(1, Math.min(Math.floor(raw), max)) : fallback;
3773
+ }
3774
+ /** Optional `limit` query param: `undefined` when absent/unparsable (= no cap). */
3775
+ function readOptionalLimit(url, max) {
3776
+ const raw = url.searchParams.get("limit");
3777
+ if (raw === null || raw.trim().length === 0) return void 0;
3778
+ const parsed = Number(raw);
3779
+ if (!Number.isFinite(parsed)) return void 0;
3780
+ return Math.max(1, Math.min(Math.floor(parsed), max));
3781
+ }
3750
3782
  /** Default note title: `YYYY-MM-DD HH:mm` (design doc D6). */
3751
3783
  function timestampTitle(date = /* @__PURE__ */ new Date()) {
3752
3784
  return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())} ${pad(date.getHours())}:${pad(date.getMinutes())}`;
@@ -4380,7 +4412,12 @@ function registerRoutes(ctx, deps) {
4380
4412
  /** Distinct non-system tags for the quick-note tag autocomplete. The flat
4381
4413
  * `tags` array feeds note-widget's chip autocomplete; the parallel `items`
4382
4414
  * (tag → tiddler count) feeds the reply-stream `tiddlywiki_list_tags` tool
4383
- * card, so both consumers share one endpoint. */
4415
+ * card, so both consumers share one endpoint.
4416
+ *
4417
+ * Query params (v0.19.4): `limit` caps the payload (absent = every tag, so
4418
+ * the autocomplete keeps its full vocabulary), `sort=count` orders by usage
4419
+ * (default `alpha`). `tags`/`items` are always the same set in the same
4420
+ * order, plus `total`/`truncated` so a capped caller can say "N of M". */
4384
4421
  const handleTags = async (req, res) => {
4385
4422
  if (rejectNonRead(req, res)) return;
4386
4423
  const client = deps.getClient();
@@ -4392,17 +4429,18 @@ function registerRoutes(ctx, deps) {
4392
4429
  return;
4393
4430
  }
4394
4431
  try {
4395
- const items = await client.list(TEXT_LIST_FILTER, false);
4396
- const counts = /* @__PURE__ */ new Map();
4397
- for (const item of items) for (const tag of item.tags ?? []) if (tag.length > 0 && !tag.startsWith("$:/")) counts.set(tag, (counts.get(tag) ?? 0) + 1);
4398
- const tags = [...counts.keys()].sort((a, b) => a.localeCompare(b, "zh"));
4432
+ const url = new URL(req.url ?? "/", "http://127.0.0.1");
4433
+ const limit = readOptionalLimit(url, MAX_TAGS_LIMIT);
4434
+ const byCount = (url.searchParams.get("sort") ?? "alpha") === "count";
4435
+ const { total, tags: all } = await client.tagStats();
4436
+ const ordered = byCount ? all : [...all].sort((a, b) => a.tag.localeCompare(b.tag, "zh"));
4437
+ const picked = limit === void 0 ? ordered : ordered.slice(0, limit);
4399
4438
  json(res, {
4400
4439
  ok: true,
4401
- tags,
4402
- items: tags.map((tag) => ({
4403
- tag,
4404
- count: counts.get(tag) ?? 0
4405
- }))
4440
+ tags: picked.map((t) => t.tag),
4441
+ items: picked,
4442
+ total,
4443
+ truncated: picked.length < total
4406
4444
  });
4407
4445
  } catch (err) {
4408
4446
  json(res, {
@@ -4423,9 +4461,7 @@ function registerRoutes(ctx, deps) {
4423
4461
  return;
4424
4462
  }
4425
4463
  try {
4426
- const url = new URL(req.url ?? "/", "http://127.0.0.1");
4427
- const limitRaw = Number(url.searchParams.get("limit") ?? 15);
4428
- const limit = Number.isFinite(limitRaw) ? Math.max(1, Math.min(Math.floor(limitRaw), 200)) : 15;
4464
+ const limit = readLimit(new URL(req.url ?? "/", "http://127.0.0.1"), 15);
4429
4465
  json(res, {
4430
4466
  ok: true,
4431
4467
  limit,
@@ -4522,8 +4558,7 @@ function registerRoutes(ctx, deps) {
4522
4558
  const tag = url.searchParams.get("tag") ?? void 0;
4523
4559
  const since = url.searchParams.get("since") ?? void 0;
4524
4560
  const type = url.searchParams.get("type") ?? void 0;
4525
- const limitRaw = Number(url.searchParams.get("limit") ?? 30);
4526
- const limit = Number.isFinite(limitRaw) ? Math.max(1, Math.min(Math.floor(limitRaw), 200)) : 30;
4561
+ const limit = readLimit(url, 30);
4527
4562
  const { items, total } = await client.search(query, {
4528
4563
  tags,
4529
4564
  tag,
@@ -6913,8 +6948,11 @@ function registerTiddlywikiTools(ctx, deps) {
6913
6948
  }));
6914
6949
  register(defineTool({
6915
6950
  name: "tiddlywiki_list_tags",
6916
- description: "列出 TiddlyWiki 知识库现有的非系统标签及各自计数(按使用次数降序),方便决定给笔记打什么 tag。",
6917
- parameters: {},
6951
+ description: "列出 TiddlyWiki 知识库现有的非系统标签及各自计数(按使用次数降序),方便决定给笔记打什么 tag。默认最多返回 200 个(`limit` 可调,上限 1000),被截断时结果里会带 total/truncated。",
6952
+ parameters: { limit: {
6953
+ type: "integer",
6954
+ description: "可选:最多返回多少个标签(按使用次数降序),默认 200,上限 1000。"
6955
+ } },
6918
6956
  output: {
6919
6957
  schema: { type: "json" },
6920
6958
  render: (_args, value) => {
@@ -6922,18 +6960,23 @@ function registerTiddlywikiTools(ctx, deps) {
6922
6960
  type: "text",
6923
6961
  text: "知识库暂无标签。"
6924
6962
  }];
6925
- const lines = [`现有标签(${value.tags.length} 个,按使用次数降序):`];
6963
+ const lines = [value.truncated ? `现有标签(共 ${value.total} 个,仅列出使用最多的 ${value.tags.length} 个,按使用次数降序):` : `现有标签(${value.total} 个,按使用次数降序):`];
6926
6964
  for (const t of value.tags) lines.push(`- ${t.tag} × ${t.count}`);
6965
+ if (value.truncated) lines.push(`(其余 ${value.total - value.tags.length} 个较少使用的标签未列出;需要时可提高 limit 重试)`);
6927
6966
  return [{
6928
6967
  type: "text",
6929
6968
  text: lines.join("\n")
6930
6969
  }];
6931
6970
  }
6932
6971
  },
6933
- execute: async () => {
6934
- const tags = await requireWiki().listTags();
6972
+ execute: async (args) => {
6973
+ const stats = await requireWiki().tagStats();
6974
+ const limit = typeof args.limit === "number" && Number.isFinite(args.limit) ? Math.max(1, Math.min(Math.floor(args.limit), 1e3)) : 200;
6975
+ const tags = stats.tags.slice(0, limit);
6935
6976
  return {
6936
6977
  count: tags.length,
6978
+ total: stats.total,
6979
+ truncated: tags.length < stats.total,
6937
6980
  tags
6938
6981
  };
6939
6982
  }