dsh-tiddlywiki 0.25.0 → 0.25.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -0
- package/lib/client.js +1 -1
- package/package.json +1 -1
- package/src/client/tool-views.ts +19 -5
package/README.md
CHANGED
|
@@ -414,6 +414,7 @@ lib/ # 预构建产物(发布含 lib/**,提交入库;
|
|
|
414
414
|
|
|
415
415
|
> 最近几个主要版本的一句话记录(完整变更见 [Releases](https://github.com/bbqisbbq/dsh-tiddlywiki/releases) / git log)。
|
|
416
416
|
|
|
417
|
+
- **v0.25.1**(2026-09-21):**修「回复流里的 wiki 链接点了打不开」**——根因在 **DSH web 的 markdown 渲染器**把 `[标题](/dsh-tiddlywiki/tw/#标题)` 这类相对链接**解析成绝对 http(s) URL**,再按 protocol 判为「外链」(`target="_blank"` + 自己的 `openExternalLink` onClick)。旧 `installWikiLinkInterceptor` 只匹配相对路径正则 `^/dsh-tiddlywiki/tw/#(.+)$`,绝对 href 匹配不上 → 拦截器不接管 → 点击落入 DSH 外链逻辑,在新标签页打开 TW 编辑器而不是中央面板(用户感知「打不开」)。**修复**:拦截器改用 `new URL(href, window.location.origin)` 归一化后判**同源 + `pathname === TW_PROXY_BASE` + hash 非空**(相对/绝对两种形态都接),在 capture 阶段 `preventDefault + stopPropagation` 抢在 DSH 外链 onClick 之前打开中央 TW 面板;`scripts/verify-tool-views.mjs` 新增源码级断言(必须用 `new URL` + 同源校验 + 代理 pathname,且旧相对正则已移除)。headless Chrome 实测:TW 面板 hash 导航(含 TiddlyFlex 的 `$:/StoryList-1`)本身正常,问题确在拦截器漏匹配绝对 href。
|
|
417
418
|
- **v0.25.0**(2026-09-20):**第五轮代码审计的修复版**(审计那天被用户判定为「本地单机可接受、不修」的只有一条:`POST /render` 的 **title 分支**可以经被渲染条目正文里的 `{{…}}` 转写把插件配置读出来——本机实测复现,`text` 分支早已 403;不修的理由是单机自用、且真正的边界本就不在这条路由(任何笔记正文的 `{{$:/plugins/dsh-tiddlywiki/config}}` 在嵌入式 TW 面板里同样会被转写渲染)。其余全部修掉,按性质分四组。**① 工具契约(数据安全)**:(a) **`tags: []` 现在真的清空标签**——此前 `normalizeTagArg()` 把空数组归一成 `undefined`(=「未传 = 保留原标签」),而 `tags` 又是 `RESERVED_TIDDLER_FIELDS` 成员(`fields.tags` 会被拒),于是**没有任何途径**把一篇笔记的标签改成空:模型照 `tiddlywiki_lint` 的 junk-tags 建议去清标签、传 `[]`,什么都不会发生,回执还把旧标签原样列出来(本机实测)。(b) `tiddlywiki_append` / `tiddlywiki_rename` / `tiddlywiki_batch_put`(逐条目 `expectedModified`)补上**乐观并发令牌**:这三条与 put/delete/attach 一样是「读全文 → 整篇回写」(append 读旧基底再加一段),而可传令牌的只有后三条 —— 注入提示词让模型回传 `expectedModified`,append 却**没有这个参数**,人类在 TW 编辑器里的并发修改会被旧基底静默回滚。(c) `tiddlywiki_rename` 更新引用时**逐个重读引用者**(列表只是快照),遍历期间被改动过的跳过并在回执里报出数量——跳过一条链接可以手工补,被覆盖掉的一段正文不可恢复。(d) `tiddlywiki_attach` 的 `noteTitle` 指向二进制附件时明确拒绝(会把 base64 写坏而回执看着正常)。(e) `tiddlywiki_lint` 的 `checks` **拼错不再谎报干净**:本机实测 `checks:["broken-link"]`(少了 s)在 2933 条笔记的知识库上**一个检查都没跑**,回执却是「扫描 2933 条文本笔记,发现 0 个问题 / 没有发现问题」;现在回执列出**实际运行**的检查、显式报出无法识别的名字,且「一个检查都没跑」不再等于「没问题」。(f) `valid-until` / `review-after` 的纯日期(`YYYY-MM-DD`)改按**当地日终**判过期,此前按 UTC 0 点算,东八区当天 08:00 就报「已过期」。**② 设置项(都是「保存了却不变」)**:`git.autoCommit`/`git.debounceMs`/`git.remote` 保存后**对运行中的插件立即生效**(`AutoCommitter` 在构造时把 enabled/debounceMs 快照进不可变 options,remote 只在启动 bootstrap 时应用——此前关掉自动提交仍会 commit、换远端仍推老地址,直到重启 dsh web);「重启 TW」按钮的 15s 超时改为 120s(`/admin/restart` 要等 TW 就绪,软窗口默认 60s、大知识库自述 44s+,此前会弹「重启失败」而宿主其实成功);数值项(`bridge.port` / `startup.readyTimeoutMs` / `ui.allArticles.pageSize` / `git.debounceMs`)加 `min`/`max` 与字段旁红字,非法值**拒绝保存**(宿主对越界值是静默夹取/回落,提交只会继续制造「回显 ≠ 生效」);**空补丁不再发请求**、宿主在序列化文本逐字节相同时**跳过 PUT**(每次空点「保存配置」都会重写配置 tiddler 并刷新 `.meta` 的 `modified` → 白造 git diff、两台机器各点一次就冲突在 `.meta` 上,与 v0.24.2 刚修的 `$__language.txt.meta` 同类);保存被拒(409,如配置 tiddler 残留冲突标记)时错误**持久显示**在配置区而不是一闪而过的 toast;微信发布的 `command`/`adapter`/`token`/`dsn` 补齐 UI(token 密码型 + 👁 显隐切换,掩码回填语义照旧);插件/主题/语言的未应用勾选提升为页面级状态,能活过「同步 / 重启」触发的重刷;表单有未保存内容时若配置在**别处**被改过,保留你的输入并显式提示,而不是重建表单把输入清掉;首屏加「加载中…」、加载失败不再清空已填表单。宿主侧 `/admin/config` 的补丁现在必须过新的 `normalizeConfigPatch()`:**已知数值夹取到真实边界、已知布尔/字符串类型错误返回 400 并点名字段、`__proto__`/`constructor`/`prototype` 一律拒绝、未知键透传**(此前任意 JSON 都会落进配置 tiddler——数组体变成 `{"0":1,"1":2}` 这种永久垃圾键,`git.debounceMs: "abc"` 会经 `??` 进 `setTimeout` 被当 0 → 每次写入都 commit)。**③ 会话「知识库」Tab**:`NOTE_TOOL_NAMES` 补 `append`(「增量写入」)/`attach`(「附件」)/`delete`/`trash`(`restore` 计产生),新增「🗑 删除 / 回收站」一节——此前**用 append 写的笔记在这个 Tab 里完全不可见**,而注入提示词恰好推荐「纯增量内容优先用 append」,于是一整轮工作会被显示成「本会话暂时没有产生、读取或检索过任何知识库笔记」;事件日志读不出来的会话**在页面上明说**(不再静默吞掉、把「读不出来」显示成「没碰过笔记」),`counts.sessions` 只统计成功读取的会话;读日志从串行改 **4 路有界并发**、探测整段加 **12s 墙钟预算**(最坏 ~375s → 有界,客户端 25s abort 之后不再继续压着 TW 打),生成加 **3s 单飞复用**;客户端自愈改为**三态探测**(在 / 被清 / 状态未知——未知既不计失败也不清零,此前 `catch { return true }` 把「探测失败」当「一切正常」),自愈 interval 与 `phase` 解耦(进 error 态后不再永久失去重试),并新增**按 3 分钟节拍的静默刷新**(tab 长期开着也能跟上会话,不闪 loading 不砸内容);`escapeInline()` 中和 `{}`(会话日志里出现的 `{{…}}` 会被 `/render` 转写展开)并改为**先截断再转义**(否则 `&` 会被截成 `&am`);`scanRefs` 不再把标题里的 `)`/`]` 一律截断;`TitleState.snippet` 从死字段改为真的渲染。**④ 其余**:检索卡片的范围与工具结果对齐(卡片把模型可见回执里读到的 `workspace` 回传 `/search`,v0.24.0 只改了工具侧——卡片会列出**更多**的结果且不显示范围);`/status` 的 git 冲突内容扫描加上限(200 个文件 / 2MB,批量导入期间不再每 30s 同步读几千个文件阻塞事件循环);`tiddlywiki_delete` 的描述写明 `$:/` 系统条目实为永久删除;`TiddlywikiConfig.note` 补上漏掉的 `workspaceMark`(三处形状 lockstep);`tiddlywiki_attach` 有专属卡片。**守门**:新增 `scripts/verify-config-patch.mjs`(6 组纯函数断言:非对象拒绝 / 越界夹取 / 类型错误点名字段 / 危险键拒绝 / 未知键透传)与 `scripts/verify-tool-views.mjs`(5 条源码级断言:`TOOL_VIEW_KEYS` 与 host 工具名完全一致 / `TOOL_LABELS` 全覆盖 / 工作区前缀两处一致 / 搜索卡片仍回传 `workspace`),两者进 `verify:unit`;`verify-tools.mjs` +4 条真实 TW 断言(`tags:[]` 清空且「不传」仍保留 / append 令牌不匹配拒绝、匹配放行 / batch 逐条失败 / lint 未知检查名不得谎报);`verify-write-policy.mjs` +1 条(空数组 vs 不传的语义边界)。**已反向验证的路径**:把 `normalizeTagArg` 还原成「空数组 → undefined」、把 lint 的 `checks` 还原成自由集合、把 append 的令牌检查删掉,对应断言分别变红。
|
|
418
419
|
- **v0.24.2**(2026-09-20):**修「`$:/language` 每次 dsh web 启动都被无条件重写 → 两台机器每次 pull 都冲突」**。现象:`git pull` 稳定卡在 `tiddlers/$__language.txt.meta` 上,而**正文文件根本不冲突、diff 出来只有 `created`/`modified` 两行**;本仓库的 git 历史里甚至已有一条专门清理该冲突残留的提交,而 2026-09-20 一次会话内又撞了两次。根因定位到行:启动自举应用 `uiLanguage` 时、以及设置页改动语言时,两处都**无条件 PUT** `$:/language`(正文每次都一样,但 TW 会给 `.meta` 盖新时间戳)——两台机器各自启动过就都改了同几行。现在抽出 `pinLanguageTiddler()`(`src/host/admin.ts`):**先读,内容一致就不写**,内容不同/条目不存在才写。⚠️ 关键边界:**读失败绝不等于「内容相同」**(铁律第三条)——`client.get()` 只有 404 返回 undefined、其余抛错,抛错时**照写并记一条日志**,否则用户的语言会永久卡在错的值上且重启也修不回来。**守门**:新增 `scripts/verify-language-pin.mjs`(进 `verify:unit`)——4 条桩 client 行为断言(一致不写 / 不存在要写 / 不同要写 / 读失败照写并报警)+ 源码级断言(`index.ts` 与 `admin.ts` 除原语自身外**一处裸 PUT 都不许有**,且两处都必须经 `pinLanguageTiddler`)。**已反向验证**:还原启动路径为无条件 PUT → 红 1 条;把读失败分支改成「直接 return false」→ 红 1 条。(附带一条方法论教训:第一次做这条反向验证时**构建因我的改写而失败**,而我把构建输出丢进了 `$null`,于是守门测的是旧 `lib/`、假绿了一轮;重做时把构建输出放回台面才抓到真红。反向验证必须确认「改动真的被编译进去了」。)
|
|
419
420
|
- **v0.24.1**(2026-09-20):**修「重启/停止 TW 的四个路径里有三个没排干 syncer 队列」——一类静默丢写入的缺陷**。本仓库铁律第一条要求「重启 TW 前必须把 syncer 队列排干」,但那条规则**只写在文档里**:`POST /restart`、`POST /admin/restart`、`POST /admin/info`(改插件/主题后重启)与「知识库切换」的 `stopServer` 全都是**直接杀 TW 子进程**,而 TW 的 REST PUT 返回 204 时条目还在内存里、文件系统 syncer 要 ~250ms 后才落盘 —— 在那一秒内点「重启 TW」,刚写的笔记就随子进程一起消失(与 v0.19.0 丢 `tw-web-host`、v0.22.0 才发现 throttle 竞态同一类)。只有启动自举 / seed / `/sync` 三处走了 `flushPendingWrites`。**根因不是谁写错了某一行,而是「规则」和「代码」之间没有强制关系** —— 所以这一版不是补四个调用点,而是把不变量变成**原语**:新增 `drainThenStop()`(`src/host/seeds.ts`,**排干发生在它内部**,签名是 `{client, tiddlersDir, stop}`),四个路径全部改为经它停/重启,调用方**没法**绕过;`stop` 的返回类型放宽为 `Promise<unknown>`(`restart()` 回状态视图、`stop()` 回 void,排干都不关心)。`/restart` 与 `/admin/restart` 的回包新增 `drained`,排干不完整时如实上报而不是静默。**守门**:新增 `scripts/verify-restart-drain.mjs`(进 `verify:unit`)——① 顺序断言:`drainThenStop` 内部「排干的 await」必须早于「stop 的 await」,且排干失败必须走显式分支后**仍然 stop**(best-effort:不能因为排不干就把用户锁死在起不来的 TW 上);② 源码级断言:`routes.ts`/`admin.ts` 里**每个** `deps.server.restart()` 都必须作为 `drainThenStop({stop: …})` 的参数出现(裸 restart 即红),`index.ts` 的 `stopServer` 必须包在它里面;③ 行为断言:用桩 client 真跑一遍,确认「写入真的落盘之后才 stop」「client 缺失(TW 没在跑)是安全 no-op」「排干失败也继续 stop 并把警告交出去」。**已反向验证**:分别把 `/sync` 路径、知识库切换还原成裸调用、并删掉 `drainThenStop` 里的 `stop`,三次都稳定变红。顺带把 `/sync` 与 seed 两条**本来就在 flush** 的路径也收敛到同一原语,从此「重启前排干」只有一个实现。
|
package/lib/client.js
CHANGED
|
@@ -770,7 +770,7 @@ Ctrl+Enter 保存`,onSave:()=>{w?.()},onChange:h}),E=document.createElement(`but
|
|
|
770
770
|
`)},_=()=>{let e=n.getState();o!==void 0&&(o.dataset.state=e.state),a!==void 0&&(a.title=`知识库 · ${e.tooltip}`),g()},v=async()=>{p=await tb(),!r&&(c!==void 0&&(c.textContent=p.text),l!==void 0&&(l.dataset.state=p.state),g())},y=p=>{if(r)return;if(i=document.createElement(`div`),i.className=`dsh-tw-fab-wrap`,s=document.createElement(`div`),s.className=`dsh-tw-fab-menu`,s.hidden=!0,p.showPanelStatus){let e=document.createElement(`div`);e.className=`dsh-tw-fab-status dsh-tw-fab-status-tiprow`,l=document.createElement(`span`),l.className=`dsh-tw-fab-status-dot`,l.dataset.state=`unknown`,c=document.createElement(`span`),c.className=`dsh-tw-fab-status-text`,c.textContent=`TiddlyWiki 服务…`,e.append(l,c),u=document.createElement(`div`),u.className=`dsh-tw-fab-tip`,u.hidden=!0,e.append(u),e.addEventListener(`mouseenter`,()=>{g(),u!==void 0&&(u.hidden=!1)}),e.addEventListener(`mouseleave`,()=>{u!==void 0&&(u.hidden=!0)}),s.append(e)}if(p.showQuickNote){let e=document.createElement(`button`);e.type=`button`,e.className=`dsh-tw-fab-item`,e.textContent=`📝 快速笔记`,e.title=`ui.quickNoteMode=native 时直接打开 TW 原生编辑器;card 时打开 Markdown 卡片`,e.addEventListener(`click`,()=>{m(),ye().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(Ke)),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 eb();r||!e.showQuickNote&&!e.showPanelStatus&&!e.showSyncButton||y(e)})(),()=>{r=!0,document.removeEventListener(`click`,h,!0),b(),x(),i?.remove()}}function rb(e){return function(){let[t,n]=c.useState(!1),[r,i]=c.useState(null),a=c.useRef(null),o=c.useRef(null);c.useEffect(()=>{let e=!0,t=e=>{let t=e.detail;n(t?.open===!0)};return window.addEventListener(jy,t),ye().then(t=>{e&&i(t.quickNoteMode)}),()=>{e=!1,window.removeEventListener(jy,t)}},[]),c.useLayoutEffect(()=>{let e=a.current;if(e===null)return;let t=()=>{let t=null,n=null,r=e;for(;r!==null&&r!==document.documentElement&&(r=r.parentElement,r!==null);){for(let e of r.querySelectorAll(`textarea, [contenteditable]:not([contenteditable="false"]), [role="textbox"]`))if(e.getBoundingClientRect().width>300){t=e,n=r;break}if(n!==null)break}if(t===null||n===null)return null;let i=n.getBoundingClientRect().width,a=t,o=null,s=0;for(;a!==null&&a!==n;){let e=a.getBoundingClientRect().width;e>0&&e<i-8&&e>=s&&(s=e,o=a),a=a.parentElement}return o},n=()=>{let n=e.getBoundingClientRect(),r=t(),i=r===null?n.right:r.getBoundingClientRect().right,a=Math.max(0,n.right-i);return e.style.paddingRight!==`${a}px`&&(e.style.paddingRight=`${a}px`),r!==null};n();let r=new ResizeObserver(n),i=e;for(;i!==null&&i!==document.documentElement&&(r.observe(i),!(i instanceof HTMLElement&&i.hasAttribute(`data-phase`)));)i=i.parentElement;window.addEventListener(`resize`,n),document.addEventListener(`visibilitychange`,n);let o=0,s=0,c=window.setInterval(()=>{if(s++,!n()){s>=20&&window.clearInterval(c);return}o++,o>=3&&window.clearInterval(c)},1500),l=window.setTimeout(n,120),u=window.setTimeout(n,600);return()=>{r.disconnect(),window.removeEventListener(`resize`,n),document.removeEventListener(`visibilitychange`,n),window.clearInterval(c),window.clearTimeout(l),window.clearTimeout(u)}},[]);let s=r===`native`,l=s?`快速笔记`:t?`快速笔记(已打开)`:`快速笔记`,u=s?`快速笔记:直接打开 TiddlyWiki 原生编辑器(再次点击可收起弹窗)`:t?`快速笔记已打开(点此按钮或卡片右上角 ✕ 收起)`:`打开快速笔记(在按钮上方弹出,可拖动标题栏移动)`;return c.createElement(`div`,{ref:a,className:`dsh-tw-dock-note`},c.createElement(`button`,{ref:o,type:`button`,className:t?`dsh-tw-dock-note-btn dsh-tw-dock-note-btn-active`:`dsh-tw-dock-note-btn`,title:u,onClick:()=>{ye().then(t=>{t.quickNoteMode===`native`?wt()&&!Tt()?Dt():e.openNative():e.isOpen()?e.close():e.open(o.current??void 0)})}},c.createElement(`span`,{className:`dsh-tw-dock-note-icon`,"aria-hidden":`true`},`📝`),c.createElement(`span`,{className:`dsh-tw-dock-note-label`},l)))}}async function ib(e,t=15e3){try{let n=await fetch(_,{method:`POST`,headers:{"content-type":`application/json`,"x-requested-with":`TiddlyWiki`},body:JSON.stringify({title:e}),signal:AbortSignal.timeout(t)});if(!n.ok)return null;let r=await n.text();return r.trim().length>0?r:null}catch{return null}}function ab(){return Ye()}async function ob(e){try{let t=await fetch(`${g}?title=${encodeURIComponent(e)}`,{signal:AbortSignal.timeout(8e3)});if(t.status===404)return`no`;if(!t.ok)return`unknown`;let n=await t.json().catch(()=>null);return n===null?`unknown`:n.notFound===!0?`no`:`yes`}catch{return`unknown`}}function sb(e){let{sessionId:t,viewRequest:n,completeViewRequest:r}=e,[i,a]=c.useState(`loading`),[o,s]=c.useState(null),[l,u]=c.useState(null),[d,f]=c.useState(``),[p,m]=c.useState(!1),h=c.useRef(0),g=c.useRef(!0),_=c.useRef(0),v=c.useRef(0),y=c.useCallback(async e=>{let n=++h.current;if(!g.current)return;let r=e?.silent===!0;r||a(`loading`);let i=e=>{if(_.current++,r){m(!0);return}a(`error`),f(e)};if(typeof t!=`string`||t.length===0){a(`error`),f(`缺少会话 ID`);return}if(_.current>=3){if(r){m(!0);return}a(`error`),f(`连续 3 次生成失败,已停止自动重试,请点「🔄 刷新」`);return}try{let e=await fetch(w,{method:`POST`,headers:{"content-type":`application/json`},body:JSON.stringify({session:t}),signal:AbortSignal.timeout(25e3)}),o=await e.json().catch(()=>null);if(!g.current||n!==h.current)return;if(!e.ok||o?.ok!==!0){i(o?.error??`HTTP ${e.status}`);return}if(typeof o.title!=`string`||o.title.length===0){i(`汇总生成失败:缺少标题`);return}u(o.title);let c=await ib(o.title);if(!g.current||n!==h.current)return;if(c===null){let e=await ob(o.title);if(!g.current||n!==h.current)return;if(e===`no`){if(_.current++,_.current<3){y();return}a(`error`),f(`汇总条目不存在,自动重建失败,请重试`);return}if(e===`unknown`){if(r){m(!0);return}a(`error`),f(`渲染失败:wiki 渲染服务不可用(host /render),条目状态未知`);return}i(`渲染失败:wiki 渲染服务不可用(host /render)`);return}_.current=0,v.current=Date.now(),m(!1),s(c),a(`ready`)}catch(e){if(!g.current||n!==h.current)return;i(e instanceof Error?e.message:String(e))}},[t]);c.useEffect(()=>(g.current=!0,_.current=0,v.current=Date.now(),m(!1),y(),()=>{g.current=!1}),[y]),c.useEffect(()=>{if(l===null)return;let e=!0,t=window.setInterval(()=>{(async()=>{if(!e||typeof document<`u`&&document.hidden)return;let t=await ob(l);if(e){if(t===`unknown`){m(!0);return}if(t===`no`){if(_.current++,_.current<3){y();return}a(`error`),f(`汇总条目已失效且自动重建多次失败,请点「🔄 刷新」重试`);return}if(Date.now()-v.current>18e4){y({silent:!0});return}_.current=0,m(!1)}})()},3e4);return()=>{e=!1,window.clearInterval(t)}},[l,y]),c.useEffect(()=>{n!=null&&r?.()},[n,r]);let b=()=>{_.current=0,y()},x;return x=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:b},`重试`)):c.createElement(`div`,{className:`dsh-tw-summary-native dsh-tw-toolcard-native`},p?c.createElement(`div`,{className:`dsh-tw-summary-state-detail`},`⚠️ 未能确认最新状态(wiki 查询或刷新失败),以下是上次生成的内容`):null,c.createElement(`div`,{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:b,disabled:i===`loading`,title:`重新生成并刷新汇总`},`🔄 刷新`)),x)}function cb(e,t){if(!(e===void 0||t.showSessionTab===!1))return Je(t.tabLabel),e.inject(`conversation.view`,()=>e.register({name:`conversation.view`,id:`dsh-tiddlywiki-summary`,order:20,label:ab},sb))}function $(e,t,n){let r=document.createElement(e);return t!==void 0&&(r.className=t),n!==void 0&&(r.textContent=n),r}function lb(e){let t=$(`div`,`dsh-tw-settings-banner`);return t.dataset.tone=`error`,t.setAttribute(`style`,`border:1px solid #c0392b;background:#fdecea;color:#8c1c13;border-radius:6px;padding:10px 12px;margin:0 0 12px;font-size:12px;line-height:1.6;white-space:pre-wrap;`),t.textContent=e,t}async function ub(e,t){let n=await fetch(e,{...t,signal:t?.signal??AbortSignal.timeout(15e3)}),r=await n.json().catch(()=>({}));if(!n.ok){let e=r.error??`HTTP ${n.status}`;throw Error(e)}return r}function db(e){let t=!1;e.classList.add(`dsh-tw-settings`);let n=$(`div`,`dsh-tw-settings-error`);n.hidden=!0;let r=$(`div`,`dsh-tw-settings-row dsh-tw-settings-status`),i=$(`div`,`dsh-tw-settings-body`);e.append(n,r,i);let a=[],o={},s={plugins:new Set,themes:new Set,languages:new Set},c=!1,l=async()=>{c||i.replaceChildren($(`div`,`dsh-tw-settings-muted`,`加载中…`));try{let e=await ub(T);if(t)return;c=!0,n.hidden=!0,n.replaceChildren(),fb(r,e,l),_b(i,e,l,()=>t,o,s)}catch(e){if(t)return;c||i.replaceChildren($(`div`,`dsh-tw-settings-muted`,`配置尚未加载。`));let r=$(`button`,`dsh-tw-settings-btn`,`重试`);r.type=`button`,r.addEventListener(`click`,()=>{l()}),n.replaceChildren($(`span`,void 0,`加载配置失败:${e instanceof Error?e.message:String(e)} `),r),n.hidden=!1}};return l(),a.push(()=>{t=!0,e.replaceChildren(),e.classList.remove(`dsh-tw-settings`)}),()=>{for(let e of a.splice(0))e()}}function fb(e,t,n){e.replaceChildren();let r=t.server??{},i=r.status??`unknown`,a=$(`span`,`dsh-tw-settings-chip`,i);a.dataset.state=i;let o=$(`span`,`dsh-tw-settings-muted`,[r.url===void 0?``:`TW ${r.url}`,t.git?.branch===void 0?``:`git ${t.git.branch}`,t.git?.lastCommit===void 0?``:t.git.lastCommit,t.git?.conflict===void 0?t.git?.dirty===!0?`有未提交改动`:``:`⚠️ 冲突未解决(${t.git.conflict.files.length} 个文件,已阻止提交)`].filter(Boolean).join(` · `)),s=$(`button`,`dsh-tw-settings-btn`,`同步`);s.type=`button`,s.title=`git 同步(pull → commit → push)`,s.addEventListener(`click`,()=>{s.disabled=!0,s.textContent=`同步中…`,(async()=>{try{let e=await fetch(ee,{method:`POST`,signal:AbortSignal.timeout(12e4)}),t=ue(await e.json().catch(()=>null),e.status);O(t.ok?`同步完成:${t.message}`:`同步失败:${t.message}`)}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 ub(re,{method:`POST`,signal:AbortSignal.timeout(12e4)}),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 pb(e,t,n,r){let i=$(`section`,`dsh-tw-settings-section`);i.append($(`h3`,`dsh-tw-settings-h`,`常规配置`));let a=t.note??{},o=t.git??{},s=t.ui??{},c=[],l=(e,t,n)=>{let r=$(`input`,`dsh-tw-settings-input`);r.value=n;let a=$(`label`,`dsh-tw-settings-field`);a.append($(`span`,`dsh-tw-settings-label`,t),r),c.push({key:e,initial:n,read:()=>r.value.trim(),changed:()=>r.value.trim()!==n}),i.append(a)},u=(e,t,n)=>{let r=$(`input`,`dsh-tw-settings-input`);r.type=`password`,r.value=n,r.autocomplete=`off`;let a=$(`button`,`dsh-tw-settings-btn dsh-tw-settings-chipbtn`,`👁`);a.type=`button`,a.title=`显示 / 隐藏`,a.setAttribute(`aria-label`,`显示 / 隐藏`),a.addEventListener(`click`,e=>{e.preventDefault();let t=r.type===`password`;r.type=t?`text`:`password`,a.textContent=t?`🙈`:`👁`});let o=$(`label`,`dsh-tw-settings-field`);o.append($(`span`,`dsh-tw-settings-label`,t),r,a),c.push({key:e,initial:n,read:()=>r.value.trim(),changed:()=>r.value.trim()!==n}),i.append(o)},d=(e,t,n)=>{let r=$(`input`,`dsh-tw-settings-check`);r.type=`checkbox`,r.checked=n;let a=$(`label`,`dsh-tw-settings-field dsh-tw-settings-field-check`);return a.append(r,$(`span`,`dsh-tw-settings-label`,t)),c.push({key:e,initial:n,read:()=>r.checked,changed:()=>r.checked!==n}),i.append(a),r},f=(e,t,n,r)=>{let a=$(`input`,`dsh-tw-settings-input`);a.type=`number`,a.value=String(n),r?.min!==void 0&&(a.min=String(r.min)),r?.max!==void 0&&(a.max=String(r.max)),r?.step!==void 0&&(a.step=String(r.step));let o=$(`div`,`dsh-tw-settings-error`);o.hidden=!0;let s=()=>{let e=a.value.trim();if(e.length===0)return`不能为空`;let t=Number(e);if(!Number.isFinite(t))return`请输入数字`;if(r?.integer===!0&&!Number.isInteger(t))return`必须是整数`;if(r?.min!==void 0&&t<r.min)return`不能小于 ${r.min}`;if(r?.max!==void 0&&t>r.max)return`不能大于 ${r.max}`},l=()=>{let e=s();o.textContent=e===void 0?``:`⚠️ ${e}(保存会被拒绝)`,o.hidden=e===void 0,e===void 0?a.removeAttribute(`aria-invalid`):a.setAttribute(`aria-invalid`,`true`)};a.addEventListener(`input`,l);let u=()=>{let e=a.value.trim(),t=Number(e);return e.length===0||!Number.isFinite(t)?e:t},d=$(`label`,`dsh-tw-settings-field`);d.append($(`span`,`dsh-tw-settings-label`,t),a,o),c.push({key:e,initial:n,read:u,changed:()=>u()!==n,invalid:s}),l(),i.append(d)},p=(e,t,n,r)=>{let a=$(`select`,`dsh-tw-settings-input`);for(let e of r){let t=document.createElement(`option`);t.value=e.value,t.textContent=e.label,e.value===n&&(t.selected=!0),a.append(t)}let o=$(`label`,`dsh-tw-settings-field`);return o.append($(`span`,`dsh-tw-settings-label`,t),a),c.push({key:e,initial:n,read:()=>a.value,changed:()=>a.value!==n}),i.append(o),a},m=(e,t,n,r)=>{let a=$(`textarea`,`dsh-tw-settings-input dsh-tw-settings-area`);a.rows=r,a.value=n;let o=$(`label`,`dsh-tw-settings-field dsh-tw-settings-field-area`);return o.append($(`span`,`dsh-tw-settings-label`,t),a),c.push({key:e,initial:n,read:()=>a.value.trim(),changed:()=>a.value.trim()!==n}),i.append(o),a};l(`note.tag`,`快速笔记默认 tag`,typeof a.tag==`string`?a.tag:`inbox`),d(`note.workspaceMark`,`新建笔记自动标工作区(ws/<项目名> 标签 + workspace 字段,取自当前会话工作目录)`,a.workspaceMark!==!1),d(`git.autoCommit`,`自动 commit(防抖)`,o.autoCommit!==!1),f(`git.debounceMs`,`自动 commit 防抖(ms;范围 0–3600000)`,typeof o.debounceMs==`number`?o.debounceMs:6e4,{min:0,max:36e5,step:1e3,integer:!0}),l(`git.remote`,`git 远端(空=仅本地;保存后即时生效,但**清空不会删除已配置的 origin**,需要时请用 git 命令处理)`,typeof o.remote==`string`?o.remote:``),l(`git.branch`,`git 分支(**只在仓库首次初始化时生效**;已有仓库请用 git 命令改分支——上面的状态行显示的是真实分支)`,typeof o.branch==`string`?o.branch:`main`);let h=t.startup??{};f(`startup.readyTimeoutMs`,`TW 启动就绪等待(ms;默认 60000,范围 5000–600000,超时上限为其 3 倍,对下次启动/重启生效)`,typeof h.readyTimeoutMs==`number`&&h.readyTimeoutMs>0?h.readyTimeoutMs:6e4,{min:5e3,max:6e5,step:1e3}),d(`ui.showQuickNote`,`显示「知识库」按钮里的「快速笔记」入口`,s.showQuickNote!==!1),d(`ui.showQuickNoteDock`,`显示聊天输入框上方的「快速笔记」快捷按钮`,s.showQuickNoteDock!==!1),p(`ui.quickNoteMode`,`点击「快速笔记」的打开方式`,typeof s.quickNoteMode==`string`&&s.quickNoteMode===`card`?`card`:`native`,[{value:`native`,label:`原生编辑器:直接弹出 TW 原生编辑页(新建/恢复草稿)`},{value:`card`,label:`Markdown 卡片:弹出现有快速笔记卡片(CodeMirror 编辑器)`}]),l(`ui.sidebarLabel`,`侧边栏 TW 入口显示名称`,typeof s.sidebarLabel==`string`&&s.sidebarLabel.trim().length>0?s.sidebarLabel.trim():`TiddlyWiki`),d(`ui.showPanelStatus`,`显示「知识库」按钮里的 TW 面板/重载入口与状态行`,s.showPanelStatus!==!1),d(`ui.showSyncButton`,`显示「知识库」按钮里的「同步」入口与 git 状态点`,s.showSyncButton!==!1),d(`ui.followDshTheme`,`嵌入式 TW 跟随 DSH 深浅主题(暗色时自动切深色 palette,不写回 wiki)`,s.followDshTheme!==!1),l(`ui.darkPalette`,`暗色时 TW palette(tiddler 标题)`,typeof s.darkPalette==`string`&&s.darkPalette.trim().length>0?s.darkPalette.trim():`$:/palettes/CupertinoDark`),l(`ui.tabLabel`,`会话顶部「知识库」Tab 名称`,typeof s.tabLabel==`string`&&s.tabLabel.trim().length>0?s.tabLabel.trim():`知识库`),d(`ui.showSessionTab`,`显示会话顶部「知识库」Tab(本会话相关 wiki 笔记汇总)`,s.showSessionTab!==!1),d(`ui.showRightbarTab`,`在 DSH 右侧边栏提供 TiddlyWiki 入口/Tab(与聊天并排)`,s.showRightbarTab!==!1);let g=s.sendToAgent??{};d(`ui.sendToAgent.enabled`,`启用「发送给 Agent」(TW 笔记 → DSH 会话注入)`,g.enabled!==!1),l(`ui.sendToAgent.endpoint`,`TW 端请求基址(空=自动取当前 DSH origin)`,typeof g.endpoint==`string`?g.endpoint:``),u(`ui.sendToAgent.token`,`共享 token(非空时路由校验 x-send-to-agent-token 头;已设置时显示为 ********,原样保存=不改,清空=删除)`,typeof g.token==`string`?g.token:``);let _=s.allArticles??{};f(`ui.allArticles.pageSize`,`「所有文章」每页条数(1–200)`,typeof _.pageSize==`number`?_.pageSize:10,{min:1,max:200,step:1,integer:!0}),i.append($(`h3`,`dsh-tw-settings-h`,`可选功能:微信公众号发布`)),i.append($(`div`,`dsh-tw-settings-muted`,`把 wiki 笔记一键发到公众号草稿箱(可选点发表)。**需要额外安装**:opencli 与 Browser Bridge 浏览器扩展,并让浏览器登录 mp.weixin.qq.com;adapter 在本仓库 tools/wechat/,安装与排错见 docs/wechat-publish-setup.md。不安装/不开启它,插件其他功能完全不受影响。开启后:注入提示词会多一条「发布前先读发布元数据规范」的约定,并在启动时把「发布元数据规范」与「微信公众号发布指南」(安装/换机还原步骤,等于仓库 docs/wechat-publish-setup.md)两篇文档写进 wiki(同名不覆盖)。`));let v=t.wechat??{},y=d(`wechat.enabled`,`启用微信公众号发布(默认关;需先按 docs/wechat-publish-setup.md 安装 opencli + 浏览器扩展)`,v.enabled===!0);l(`wechat.command`,`opencli 命令(默认 opencli;用别名或绝对路径时填这里。仅在启用微信发布时生效)`,typeof v.command==`string`&&v.command.trim().length>0?v.command.trim():`opencli`),p(`wechat.adapter`,`发布适配器(仅在启用微信发布时生效)`,v.adapter===`publish-note-imgs`?`publish-note-imgs`:`publish-note`,[{value:`publish-note`,label:`publish-note(默认):图文排版,封面单图`},{value:`publish-note-imgs`,label:`publish-note-imgs:正文内嵌图全部上传 CDN(需先重跑 install-wechat-adapters.mjs)`}]),u(`wechat.token`,`发布 token(非空时 /wechat/* 校验 x-wechat-publish-token 头,强烈建议设置,否则任何能打开本页的人都能替你发文;已设置时显示为 ********,原样保存=不改,清空=删除。仅在启用微信发布时生效)`,typeof v.token==`string`?v.token:``),l(`wechat.dsn`,`渲染基址 dsn(adapter 回连 DSH 用,形如 http://127.0.0.1:<端口>/dsh-tiddlywiki;留空=按请求自动推导。仅在启用微信发布时生效)`,typeof v.dsn==`string`?v.dsn:``);let b=t.prompt??{};i.append($(`h3`,`dsh-tw-settings-h`,`系统提示词(注入每个会话)`)),i.append($(`div`,`dsh-tw-settings-muted`,`插件把自己的约定(同步纪律 / 标签约定 / 链接格式等)注入每个会话的系统提示词。保存后**无需重启 dsh web**:section 会即时重新注册,当前会话从下一步起就使用新文本。`));let x=d(`prompt.enabled`,`注入 TiddlyWiki 提示词(关闭后本插件不再注入任何文本)`,b.enabled!==!1),S=p(`prompt.mode`,`内置文本形态`,b.mode===`full`?`full`:`slim`,[{value:`slim`,label:`精简(默认):只保留工具 schema 表达不了的约定`},{value:`full`,label:`完整:额外附一份由工具注册表实时生成的参数索引`}]),C=m(`prompt.extra`,`附加说明(永远追加在末尾,可放团队/个人规范)`,typeof b.extra==`string`?b.extra:``,5),ee=m(`prompt.override`,`整段替换(非空时取代上面的内置文本,附加说明仍会追加)`,typeof b.override==`string`?b.override:``,8),te=()=>c.some(e=>(e.key.startsWith(`prompt.`)||e.key===`wechat.enabled`)&&e.changed()),w=$(`button`,`dsh-tw-settings-btn`,`预览注入文本(按表单当前值)`);w.type=`button`,w.title=`按表单当前值渲染注入文本(不用先保存);点「保存配置」后这就是实际注入的内容`;let T=$(`pre`,`dsh-tw-settings-prompt-preview`);T.hidden=!0,w.addEventListener(`click`,()=>{if(!T.hidden){T.hidden=!0,T.textContent=``;return}w.disabled=!0,(async()=>{try{let e=await ub(E,{method:`POST`,headers:{"content-type":`application/json`},body:JSON.stringify({enabled:x.checked,mode:S.value,extra:C.value,override:ee.value,wechat:y.checked})});if(e.ok!==!0)throw Error(e.error??`获取失败`);T.textContent=e.enabled===!1?`(已关闭:不会注入任何提示词)`:`# 形态 ${e.mode??``} · ${e.length??0} 字符${te()?`(含未保存的修改,保存后才真正注入)`:``}\n\n${e.text??``}`,T.hidden=!1}catch(e){O(`读取提示词失败:${e instanceof Error?e.message:String(e)}`)}finally{w.disabled=!1}})()}),i.append(w,T);let D=t.bridge??{};d(`bridge.enabled`,`启用「本地剪藏桥」(书签小工具后端监听 127.0.0.1 端口,保存后立即生效)`,D.enabled===!0),f(`bridge.port`,`剪藏桥端口(整数 1–65535;改端口需重启 dsh web 生效)`,typeof D.port==`number`&&D.port>0?D.port:8618,{min:1,max:65535,step:1,integer:!0}),u(`bridge.token`,`剪藏桥 token(非空时校验书签的 x-clip-token 头;强烈建议设置。已设置时显示为 ********,原样保存=不改,清空=删除)`,typeof D.token==`string`?D.token:``),l(`bridge.tag`,`剪藏笔记默认 tag`,typeof D.tag==`string`&&D.tag.trim().length>0?D.tag.trim():`clip`);let re=lb(r.saveError??``);re.hidden=r.saveError===void 0;let ie=$(`button`,`dsh-tw-settings-btn dsh-tw-settings-primary`,`保存配置`);ie.type=`button`,ie.addEventListener(`click`,()=>{ie.disabled=!0,(async()=>{let e=c.filter(e=>e.invalid?.()!==void 0);if(e.length>0){O(`有 ${e.length} 个字段填得不对(见字段旁的红色提示),未保存`),ie.disabled=!1;return}let t={};for(let e of c){if(!e.changed())continue;let n=e.key.split(`.`),r=t;for(let t=0;t<n.length;t++){let i=n[t];if(i===void 0)break;if(t===n.length-1)r[i]=e.read();else{let e=r[i]??{};r[i]=e,r=e}}}if(Object.keys(t).length===0){O(`没有改动`),ie.disabled=!1;return}try{await ub(ne,{method:`POST`,headers:{"content-type":`application/json`},body:JSON.stringify(t)}),ve(),r.saveError=void 0,re.hidden=!0,re.textContent=``,O(`配置已保存`),n()}catch(e){r.saveError=`保存失败:${e instanceof Error?e.message:String(e)}`,re.textContent=r.saveError,re.hidden=!1,n()}finally{ie.disabled=!1}})()}),r.isDirty=()=>c.some(e=>e.changed()),i.append(re,ie),e.append(i)}function mb(e,t,n,r,i){let a=n?.plugins??[],o=n?.themes??[],s=n?.languages??[],c=t?.plugins??[],l=new Set(t?.themes??[]),u=t?.languages??[],d=()=>i.plugins??new Set(c),f=()=>i.themes??new Set(l),p=()=>i.languages??new Set(u),m=$(`section`,`dsh-tw-settings-section`);m.append($(`h3`,`dsh-tw-settings-h`,`插件管理(自带官方插件)`));let h=$(`input`,`dsh-tw-settings-input dsh-tw-settings-search`);h.placeholder=`搜索插件…`;let g=$(`div`,`dsh-tw-settings-list`),_=$(`button`,`dsh-tw-settings-btn dsh-tw-settings-primary`,`应用插件(重启 TW)`);_.type=`button`;let v=e=>{g.replaceChildren();let t=e.toLowerCase();for(let e of a){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=d().has(e.name),n.addEventListener(`change`,()=>{let t=d();n.checked?t.add(e.name):t.delete(e.name),i.plugins=t});let r=$(`label`,`dsh-tw-settings-row dsh-tw-settings-plugin`),a=$(`span`,`dsh-tw-settings-name`,e.label);a.title=e.name;let o=$(`span`,`dsh-tw-settings-muted`,e.description||e.name);r.append(n,a,o),g.append(r)}};h.addEventListener(`input`,()=>v(h.value)),_.addEventListener(`click`,()=>{_.disabled=!0,(async()=>{try{await ub(D,{method:`POST`,headers:{"content-type":`application/json`},body:JSON.stringify({plugins:[...d()]})}),i.plugins=void 0,O(`插件已应用,TW 已重启`),r()}catch(e){O(`应用失败:${e instanceof Error?e.message:String(e)}`),_.disabled=!1}})()}),v(``),m.append(h,g,_),e.append(m);let y=$(`section`,`dsh-tw-settings-section`);y.append($(`h3`,`dsh-tw-settings-h`,`主题管理(自带主题)`));let b=$(`div`,`dsh-tw-settings-muted`,`「加载」= TW 里可用的主题(可多选,依赖链自动带上,如 heavier 会带 snowwhite+vanilla);「活动」= 当前视觉主题(单选,自动加入加载集)。应用后重启 TW。`),x=$(`div`,`dsh-tw-settings-row dsh-tw-settings-head`);x.append($(`span`,`dsh-tw-settings-col`,`加载`),$(`span`,`dsh-tw-settings-col`,`活动`),$(`span`,`dsh-tw-settings-name`,`主题`));let S=t?.themes??[],C=t?.themeActive,ee=S[S.length-1],te=typeof C==`string`&&o.some(e=>e.name===C)?C:ee??`tiddlywiki/vanilla`,w=()=>i.themeActive??te,T=$(`div`,`dsh-tw-settings-list`);for(let e of o){let t=$(`input`,`dsh-tw-settings-check`);t.type=`checkbox`,t.checked=f().has(e.name),t.title=`加载该主题`,t.addEventListener(`change`,()=>{let n=f();t.checked?n.add(e.name):n.delete(e.name),i.themes=n});let n=$(`input`,`dsh-tw-settings-check`);n.type=`radio`,n.name=`dsh-tw-active-theme`,n.checked=e.name===w(),n.title=`设为活动主题`,n.addEventListener(`change`,()=>{n.checked&&(i.themeActive=e.name)});let r=$(`span`,`dsh-tw-settings-name`,e.label);r.title=e.name;let a=$(`span`,`dsh-tw-settings-muted`,e.description||e.name),o=$(`div`,`dsh-tw-settings-row dsh-tw-settings-plugin`);o.append(t,n,r,a),T.append(o)}let E=$(`button`,`dsh-tw-settings-btn dsh-tw-settings-primary`,`应用主题(重启 TW)`);E.type=`button`,E.addEventListener(`click`,()=>{E.disabled=!0,(async()=>{try{await ub(D,{method:`POST`,headers:{"content-type":`application/json`},body:JSON.stringify({themes:[...f()],themeActive:w()})}),i.themes=void 0,i.themeActive=void 0,O(`主题已应用,TW 已重启`),r()}catch(e){O(`应用失败:${e instanceof Error?e.message:String(e)}`),E.disabled=!1}})()}),y.append(b,x,T,E),e.append(y);let ne=$(`section`,`dsh-tw-settings-section`);ne.append($(`h3`,`dsh-tw-settings-h`,`语言管理(自带官方语言包)`));let re=$(`div`,`dsh-tw-settings-muted`,`勾选启用语言插件并重启 TW;如中文请选 zh-Hans(简体)或 zh-CN。`),ie=$(`div`,`dsh-tw-settings-list`);for(let e of s){let t=$(`input`,`dsh-tw-settings-check`);t.type=`checkbox`,t.checked=p().has(e.name),t.addEventListener(`change`,()=>{let n=p();t.checked?n.add(e.name):n.delete(e.name),i.languages=n});let n=$(`label`,`dsh-tw-settings-row dsh-tw-settings-plugin`),r=$(`span`,`dsh-tw-settings-name`,e.label);r.title=e.name;let a=$(`span`,`dsh-tw-settings-muted`,e.description||e.name);n.append(t,r,a),ie.append(n)}let ae=$(`button`,`dsh-tw-settings-btn dsh-tw-settings-primary`,`应用语言(重启 TW)`);ae.type=`button`,ae.addEventListener(`click`,()=>{ae.disabled=!0,(async()=>{try{await ub(D,{method:`POST`,headers:{"content-type":`application/json`},body:JSON.stringify({languages:[...p()]})}),i.languages=void 0,O(`语言已应用,TW 已重启`),r()}catch(e){O(`应用失败:${e instanceof Error?e.message:String(e)}`),ae.disabled=!1}})()}),ne.append(re,ie,ae),e.append(ne)}function hb(e,t,n){let r=$(`section`,`dsh-tw-settings-section`);r.append($(`h3`,`dsh-tw-settings-h`,`知识库位置(可切换)`));let i=$(`div`,`dsh-tw-settings-muted`,`读取中…`),a=$(`div`,`dsh-tw-settings-muted`),o=$(`div`,`dsh-tw-settings-muted`),s=$(`div`,`dsh-tw-settings-muted`),c=$(`input`,`dsh-tw-settings-input`),l=$(`input`,`dsh-tw-settings-input`);c.placeholder=`绝对路径,如 D:\\notes 或 $DSH_HOME/tiddlywiki`,l.placeholder=`文件夹名(默认 main;填 . 表示直接用上面这个目录)`;let u=$(`label`,`dsh-tw-settings-field`);u.append($(`span`,`dsh-tw-settings-label`,`根目录(wikiRoot)`),c);let d=$(`label`,`dsh-tw-settings-field`);d.append($(`span`,`dsh-tw-settings-label`,`文件夹名(wiki)`),l);let f=$(`button`,`dsh-tw-settings-btn dsh-tw-settings-primary`,`切换到这个位置`);f.type=`button`;let p=$(`button`,`dsh-tw-settings-btn`,`恢复为配置默认`);p.type=`button`;let m=$(`div`,`dsh-tw-settings-row`);m.append(f,p);let h=$(`div`,`dsh-tw-settings-row dsh-tw-settings-candidates`),g=$(`div`,`dsh-tw-settings-muted`,"切换会停掉并就地重启 TW 子进程(几秒);新目录若还没有 tiddlywiki.info,插件会自动 `--init server` 初始化一个全新知识库。选中的位置会记在 $DSH_HOME 下的指针文件里(不进 wiki),可用「恢复为配置默认」清除。"),_=(e,t)=>{f.disabled=e,p.disabled=e,f.textContent=e?t:`切换到这个位置`},v=async()=>{if(!t())try{let e=await ub(se);if(t())return;if(e.ok!==!0)throw Error(e.error??`获取失败`);let n=e.current??{};i.textContent=`当前:${n.path??`(未知)`}`;let r=n.source===`state`?`来源:指针文件(在设置页切换过)`:n.source===`config`?`来源:cordis 配置(config.wikiRoot / config.wiki)`:`来源:默认值($DSH_HOME/tiddlywiki + main)`;a.textContent=`${r} · 配置默认:${e.default?.path??`(未知)`}`,o.textContent=e.stateFile===void 0?``:`指针文件:${e.stateFile}(不存在=使用配置默认)`,s.textContent=e.error===void 0?``:`⚠️ ${e.error}`,c.value.length===0&&typeof n.root==`string`&&(c.value=n.root),l.value.length===0&&typeof n.name==`string`&&(l.value=n.name),h.replaceChildren();let u=e.candidates??[];if(u.length>0){h.append($(`span`,`dsh-tw-settings-label`,`同目录下可选的 wiki:`));for(let e of u){let t=$(`button`,`dsh-tw-settings-btn dsh-tw-settings-chipbtn`,e===`.`?`(根目录本身)`:e);t.type=`button`,t.title=`填入文件夹名「${e}」`,t.addEventListener(`click`,()=>{l.value=e}),h.append(t)}}}catch(e){if(t())return;i.textContent=`读取位置失败:${e instanceof Error?e.message:String(e)}`}};f.addEventListener(`click`,()=>{let e=c.value.trim(),t=l.value.trim().length>0?l.value.trim():`main`;if(e.length===0){O(`请先填写根目录(绝对路径)`);return}window.confirm(`切换知识库到:\n${e}\\${t}\n\n会停止并重启 TW 子进程;当前对话/工具随后读写的是新知识库。确定继续?`)&&(_(!0,`切换中…(重启 TW)`),(async()=>{try{let r=await ub(ce,{method:`POST`,headers:{"content-type":`application/json`},body:JSON.stringify({root:e,name:t}),signal:AbortSignal.timeout(12e4)});r.warning===void 0?O(`已切换到 ${r.path??``}`):O(`已切换到 ${r.path??``}(注意:${r.warning})`),ve(),await v(),await n()}catch(e){O(`切换失败:${e instanceof Error?e.message:String(e)}`),await v()}finally{_(!1,``)}})())}),p.addEventListener(`click`,()=>{window.confirm(`恢复为配置默认位置?会删除位置指针文件,并(必要时)切回配置里的知识库。`)&&(_(!0,`切换中…(重启 TW)`),(async()=>{try{let e=await ub(le,{method:`POST`,headers:{"content-type":`application/json`},body:`{}`,signal:AbortSignal.timeout(12e4)});O(e.warning===void 0?`已恢复为配置默认:${e.path??``}`:`已恢复默认(注意:${e.warning})`),ve(),await v(),await n()}catch(e){O(`恢复失败:${e instanceof Error?e.message:String(e)}`),await v()}finally{_(!1,``)}})())}),r.append(i,a,o,u,d,m,h,s,g),e.append(r),v()}function gb(e,t){let n=$(`section`,`dsh-tw-settings-section`);n.append($(`h3`,`dsh-tw-settings-h`,`初始化(一次性预置)`));let r=$(`div`,`dsh-tw-settings-muted`,`这些是插件与 dsh 联动、需要在 wiki 里预置的 tiddler/配置。「发送给 Agent 按钮」和「TW 前端 API 基址」是功能必需项,启动时自动写入(只写缺失,不覆盖你的改动);其余为可选项,默认不自动写入、也不会强绑定——需要时点「重新初始化」写入,不想要了可随时「反初始化」移除。`),i=$(`div`,`dsh-tw-settings-list`),a=$(`div`,`dsh-tw-settings-muted`),o=async()=>{if(!t())try{let e=await ub(ie);if(t())return;if(e.ok!==!0||!Array.isArray(e.items))throw Error(e.error??`获取失败`);i.replaceChildren();let n=0,r=0;for(let t of e.items){t.present&&(n+=1),t.removable&&(r+=1);let e=$(`span`,`dsh-tw-settings-chip`,t.present?`✓`:`✗`);e.dataset.state=t.present?`ok`:`missing`,e.title=t.present?`已存在`:`缺失`;let a=$(`span`,`dsh-tw-settings-name`,t.title);a.title=t.id;let s=$(`span`,`dsh-tw-settings-muted`,t.detail??t.description),c=t.updateAvailable===!0?$(`span`,`dsh-tw-settings-chip`,`⬆ 有更新`):void 0;c!==void 0&&(c.dataset.state=`update`,c.title=`内置内容在本 wiki 预置之后更新过,可点「重新初始化」取用`);let l=t.userModified===!0?$(`span`,`dsh-tw-settings-chip`,`✏️ 本地已修改`):void 0;l!==void 0&&(l.dataset.state=`missing`,l.title=`这篇是你的内容:重新初始化会覆盖它`);let u=$(`button`,`dsh-tw-settings-btn`,t.updateAvailable===!0?`更新到内置版本`:`重新初始化`);u.type=`button`,u.title=`强制重写「${t.title}」的内置内容(会覆盖当前 tiddler)`,u.addEventListener(`click`,()=>{if(t.updateAvailable===!0){let e=t.userModified===!0?`「${t.title}」有你的本地修改,更新会覆盖它。`:t.userModified===void 0?`无法确认「${t.title}」是否被你编辑过(旧格式标记),更新可能覆盖你的改动。`:``;if(!window.confirm(`${e}${e.length>0?`
|
|
771
771
|
|
|
772
772
|
`:``}用内置版本覆盖?`))return}u.disabled=!0,u.textContent=`执行中…`,(async()=>{try{let e=await fetch(ae,{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}」`),o()}catch(e){O(`重新初始化失败:${e instanceof Error?e.message:String(e)}`)}finally{u.disabled=!1,u.textContent=t.updateAvailable===!0?`更新到内置版本`:`重新初始化`}})()});let d=$(`div`,`dsh-tw-settings-row dsh-tw-settings-plugin`);if(d.append(e,a),c!==void 0&&d.append(c),l!==void 0&&d.append(l),d.append(s,u),t.removable){let e=$(`button`,`dsh-tw-settings-btn dsh-tw-settings-danger`,`反初始化`);e.type=`button`,e.title=`移除「${t.title}」写入的 tiddler 与 marker(恢复未初始化状态)`,e.addEventListener(`click`,()=>{window.confirm(`确定反初始化「${t.title}」?将删除它写入的 tiddler(含其一次性 marker),需要时可用「重新初始化」恢复。`)&&(e.disabled=!0,e.textContent=`移除中…`,(async()=>{try{let e=await fetch(oe,{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}」`),o()}catch(e){O(`反初始化失败:${e instanceof Error?e.message:String(e)}`)}finally{e.disabled=!1,e.textContent=`反初始化`}})())}),d.append(e)}i.append(d)}a.textContent=`共 ${e.items.length} 项,${n} 项已就绪;可移除 ${r} 项`}catch(e){if(t())return;a.textContent=`加载初始化状态失败:${e instanceof Error?e.message:String(e)}`}},s=$(`button`,`dsh-tw-settings-btn dsh-tw-settings-primary`,`全部重新初始化`);s.type=`button`,s.title=`强制重写所有一次性预置内容(含可选 seed,会覆盖当前 tiddler/配置)`,s.addEventListener(`click`,()=>{s.disabled=!0,s.textContent=`执行中…`,(async()=>{try{let e=await fetch(ae,{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(`全部已重新初始化`),o()}catch(e){O(`重新初始化失败:${e instanceof Error?e.message:String(e)}`)}finally{s.disabled=!1,s.textContent=`全部重新初始化`}})()});let c=$(`button`,`dsh-tw-settings-btn dsh-tw-settings-danger`,`全部反初始化`);c.type=`button`,c.title=`移除所有可选 seed 写入的 tiddler 与 marker(功能必需项保留)`,c.addEventListener(`click`,()=>{window.confirm(`确定全部反初始化?将删除所有可选 seed 写入的 tiddler(含一次性 marker)。功能必需项(发送给 Agent 按钮 / TW 前端 API 基址)会保留。`)&&(c.disabled=!0,c.textContent=`移除中…`,(async()=>{try{let e=await fetch(oe,{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 已反初始化`),o()}catch(e){O(`反初始化失败:${e instanceof Error?e.message:String(e)}`)}finally{c.disabled=!1,c.textContent=`全部反初始化`}})())}),n.append(r,a,i,s,c),e.append(n),o()}function _b(e,t,n,r,i,a){e.replaceChildren(),typeof t.configError==`string`&&t.configError.length>0&&e.append(lb(`⚠️ 配置未生效:${t.configError}`));let o=JSON.stringify(t.config??{}),s=i.host!==void 0&&i.signature!==o,c=i.isDirty?.()===!0;if(s&&c&&e.append(lb(`⚠️ 服务器上的配置在别处被改动过(另一个标签页保存 / 语言管理等),当前表单仍是旧值:直接点「保存配置」会以本页内容覆盖那些改动。`)),i.host===void 0||s&&!c){let e=$(`div`,`dsh-tw-settings-confighost`);pb(e,t.config??{},n,i),i.host=e,i.signature=o}e.append(i.host),hb(e,r,n),mb(e,t.info,t.catalog,n,a),gb(e,r)}function vb(){let e=c.useRef(null);return c.useEffect(()=>{let t=e.current;return t===null?void 0:db(t)},[]),c.createElement(`div`,{ref:e})}const yb={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 bb(e){return typeof e==`object`&&!!e&&e.kind===`tool-result`}function xb(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 Sb(e){try{let t=JSON.parse(e);return typeof t==`object`&&t&&!Array.isArray(t)?t:{}}catch{return{}}}function Cb(e){return Array.isArray(e)?e.filter(e=>typeof e?.text==`string`).map(e=>e.text).join(`
|
|
773
|
-
`):``}function wb(e){return typeof e==`string`?e:``}async function Tb(e){try{let t=await fetch(e,{signal:AbortSignal.timeout(1e4)});if(t.status===404){let e=await t.json().catch(()=>null);return typeof e==`object`&&e?e:{notFound:!0}}if(!t.ok)return null;let n=await t.json();return typeof n==`object`&&n?n:null}catch{return null}}async function Eb(e){return ib(e,1e4)}function Db(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 Ob(e){return t=>{t.defaultPrevented||t.button!==0||t.metaKey||t.ctrlKey||t.shiftKey||t.altKey||(t.preventDefault(),t.stopPropagation(),pt(e))}}function kb(e){let t=yb[e.toolName]??e.toolName,n=c.createElement(`div`,{className:`dsh-tw-toolcard-head`},c.createElement(`span`,{className:`dsh-tw-toolcard-badge`},t),e.title!==void 0&&e.title.length>0?c.createElement(`span`,{className:`dsh-tw-toolcard-title`,title:e.title},e.title):null,e.onOpen===void 0?null:c.createElement(`button`,{type:`button`,className:`dsh-tw-toolcard-open`,onClick:e.onOpen},`在 TW 打开`)),r=e.subtitle!==void 0||e.tags!==void 0&&e.tags.length>0?c.createElement(`div`,{className:`dsh-tw-toolcard-meta`},e.subtitle===void 0?null:c.createElement(`span`,{className:`dsh-tw-toolcard-sub`},e.subtitle),e.tags!==void 0&&e.tags.length>0?c.createElement(`span`,{className:`dsh-tw-toolcard-tags`},...e.tags.map((e,t)=>c.createElement(`span`,{className:`dsh-tw-toolcard-tag`,key:`${t}-${e}`},e))):null):null;return c.createElement(`div`,{className:`dsh-tw-toolcard`},n,r,c.createElement(`div`,{className:`dsh-tw-toolcard-body`},e.children),e.foot!==void 0&&e.foot!==null?c.createElement(`div`,{className:`dsh-tw-toolcard-foot`},e.foot):null)}const Ab=new Map;function jb(e){let t=Ab.get(e);if(t!==void 0){if(Date.now()-t.at>3e5){Ab.delete(e);return}return Ab.delete(e),Ab.set(e,t),t}}function Mb(e,t){for(Ab.delete(e),Ab.set(e,{at:Date.now(),...t});Ab.size>50;){let e=Ab.keys().next().value;if(e===void 0)break;Ab.delete(e)}}function Nb(e){e.length>0&&Ab.delete(e)}function Pb(e){let t=e.join(`\0`);c.useEffect(()=>{for(let e of t.split(`\0`))Nb(e)},[t])}function Fb(e){let{title:t,fresh:n}=e,{loading:r,data:i}=Db(async()=>{n===!0&&Nb(t);let e=jb(t);if(e!==void 0)return{get:e.get,html:e.html};let[r,i]=await Promise.all([Tb(`${g}?title=${encodeURIComponent(t)}`),Eb(t)]);return typeof i==`string`&&i.length>0&&Mb(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(kb,{toolName:e.toolName,title:t,subtitle:e.subtitle,tags:l,onOpen:Ob(t),foot:u===void 0?void 0:`修改于 ${u}`},s)}function Ib(e){let t=c.createElement(`span`,{className:`dsh-tw-toolcard-row-head`},c.createElement(`span`,{className:`dsh-tw-toolcard-row-title`},e.title),e.tags!==void 0&&e.tags.length>0?c.createElement(`span`,{className:`dsh-tw-toolcard-row-tags`},e.tags.slice(0,4).join(` · `)):null,typeof e.modified==`string`&&e.modified.length>0?c.createElement(`span`,{className:`dsh-tw-toolcard-row-meta`},e.modified):null),n=typeof e.snippet==`string`&&e.snippet.trim().length>0?e.snippet:null;return c.createElement(`a`,{className:`dsh-tw-toolcard-row`,href:`${m}#${encodeURIComponent(e.title)}`,onClick:Ob(e.title),title:n===null?e.title:`${e.title}\n${n}`},t,n===null?null:c.createElement(`span`,{className:`dsh-tw-toolcard-row-snippet`},n))}function 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(Ib,{key:`${e.title}-${t}`,...e}))),c.createElement(kb,{toolName:e.toolName,title:e.title,subtitle:e.subtitle},n)}function Rb(e){let t=/工作区\s+(ws\/[^\s((]+)/.exec(e);if(t===null||t[1]===void 0)return null;let n=t[1].slice(3);return n.length>0?n:null}function zb(e){let t=wb(e.args.query),n=new URLSearchParams;if(t.length>0&&n.set(`query`,t),Array.isArray(e.args.tags))for(let t of e.args.tags)typeof t==`string`&&t.length>0&&n.append(`tags`,t);typeof e.args.tag==`string`&&e.args.tag.length>0&&n.set(`tag`,e.args.tag),typeof e.args.since==`string`&&e.args.since.length>0&&n.set(`since`,e.args.since),typeof e.args.type==`string`&&e.args.type.length>0&&n.set(`type`,e.args.type),typeof e.args.field==`string`&&e.args.field.length>0&&n.set(`field`,e.args.field),typeof e.args.value==`string`&&e.args.value.length>0&&n.set(`value`,e.args.value),typeof e.args.limit==`number`&&n.set(`limit`,String(e.args.limit));let r=Rb(e.text);r!==null&&n.set(`workspace`,r);let i=n.toString(),a=Db(()=>Tb(`${S}?${i}`),[i]).data,o=(Array.isArray(a?.items)?a.items:[]).map(e=>({title:wb(e.title),tags:Array.isArray(e.tags)?e.tags.filter(e=>typeof e==`string`):[],modified:typeof e.modified==`string`?e.modified:null,snippet:typeof e.snippet==`string`?e.snippet:``})),s=typeof a?.total==`number`?a.total:void 0,l=typeof a?.workspace==`string`&&a.workspace.length>0?a.workspace:null,u=``;l!==null&&a?.scope===`workspace`?u=` · 已在工作区 ws/${l} 内缩小范围`:l!==null&&a?.fellBack===!0&&(u=` · 工作区 ws/${l} 内 0 条,已扩大到全库`);let d=`关键词「${t||`(全部)`}」${s===void 0?``:` · 共 ${s} 条`}${u}`;return c.createElement(Lb,{toolName:e.toolName,title:t.length>0?t:`检索`,subtitle:d,rows:o,fallbackText:`没有匹配的笔记`})}function Bb(e){let t=e.title.length>0?c.createElement(`div`,{className:`dsh-tw-toolcard-empty`},`附件已保存进 wiki(二进制条目,卡片不内联渲染)。`):c.createElement(`div`,{className:`dsh-tw-toolcard-empty`},`附件已保存。`);return c.createElement(kb,{toolName:e.toolName,title:e.title.length>0?e.title:void 0,...e.title.length>0?{onOpen:Ob(e.title)}:{}},t)}function Vb(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=Db(()=>Tb(`${x}?${n}`),[n]).data,i=(Array.isArray(r?.items)?r.items:[]).map(e=>({title:wb(e.title),tags:Array.isArray(e.tags)?e.tags.filter(e=>typeof e==`string`):[],modified:typeof e.modified==`string`?e.modified:null,snippet:typeof e.snippet==`string`?e.snippet:``}));return c.createElement(Lb,{toolName:e.toolName,title:`最近修改`,subtitle:i.length>0?`最近 ${i.length} 条`:``,rows:i,fallbackText:`暂无笔记`})}function Hb(e){let t=(Array.isArray(e.args.items)?e.args.items:[]).map(e=>typeof e==`object`&&e&&typeof e.title==`string`?wb(e.title):null).filter(e=>e!==null&&e.length>0).map(e=>({title:e}));return Pb(t.map(e=>e.title)),c.createElement(Lb,{toolName:e.toolName,title:`批量写入`,subtitle:t.length>0?`写入 ${t.length} 篇`:`(参数中没有可解析的 items)`,rows:t,fallbackText:`没有可展示的笔记`})}function Ub(e){let t=Db(()=>Tb(`${b}?limit=60&sort=count`),[]).data,n=(Array.isArray(t?.items)?t.items:[]).map(e=>({tag:wb(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(kb,{toolName:e.toolName,title:`标签`,subtitle:`共 ${i} 个`},a)}function Wb(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(kb,{toolName:e.toolName},t)}function Gb(e){Pb([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(kb,{toolName:e.toolName,title:e.title.length>0?e.title:void 0},t)}function Kb(e,t){switch(e){case`tiddlywiki_get`:case`tiddlywiki_put`:case`tiddlywiki_append`:case`tiddlywiki_delete`:case`tiddlywiki_attach`:case`tiddlywiki_backlinks`:return wb(t.title);case`tiddlywiki_rename`:return wb(t.newTitle);case`tiddlywiki_search`:return wb(t.query);default:return``}}function qb(e){let t=e.block,n=e.toolName,r=bb(t),i=xb(t),a=n!==void 0&&n.length>0?n:i?.name??``,o=Sb(i?.argsRaw??``),s=r?Cb(t.content):``;if(i===null)return c.createElement(kb,{toolName:a},c.createElement(`pre`,{className:`dsh-tw-toolcard-fallback`},s||`(无调用信息)`));if(!r)return c.createElement(kb,{toolName:a,title:Kb(a,o)},c.createElement(`div`,{className:`dsh-tw-toolcard-pending`},`处理中…`));if(t.isError===!0)return c.createElement(kb,{toolName:a,title:Kb(a,o)},c.createElement(`div`,{className:`dsh-tw-toolcard-error`},s||`工具调用失败`));switch(a){case`tiddlywiki_get`:return c.createElement(Fb,{toolName:a,title:wb(o.title),subtitle:`读取`});case`tiddlywiki_put`:return c.createElement(Fb,{toolName:a,title:wb(o.title),subtitle:`已写入`,fresh:!0});case`tiddlywiki_append`:return c.createElement(Fb,{toolName:a,title:wb(o.title),subtitle:`已增量写入`,fresh:!0});case`tiddlywiki_rename`:return c.createElement(Fb,{toolName:a,title:wb(o.newTitle),subtitle:`已重命名「${wb(o.oldTitle)}」→`,fresh:!0});case`tiddlywiki_delete`:return c.createElement(Gb,{toolName:a,title:wb(o.title),text:s});case`tiddlywiki_attach`:return c.createElement(Bb,{toolName:a,title:wb(o.title)});case`tiddlywiki_batch_put`:return c.createElement(Hb,{toolName:a,args:o});case`tiddlywiki_search`:return c.createElement(zb,{toolName:a,args:o,text:s});case`tiddlywiki_recent`:return c.createElement(Vb,{toolName:a,args:o});case`tiddlywiki_list_tags`:return c.createElement(Ub,{toolName:a});case`tiddlywiki_git_sync`:case`tiddlywiki_git_resolve`:return c.createElement(Wb,{toolName:a,text:s});default:return c.createElement(kb,{toolName:a,title:Kb(a,o)},c.createElement(`pre`,{className:`dsh-tw-toolcard-fallback`},s||`(完成)`))}}const Jb=[`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 Jb)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}`},qb));r!==void 0&&t.push(r)}catch(e){console.error(`[dsh-tiddlywiki] register toolview ${n} failed:`,e)}return t}function Xb(){let e=RegExp(`^${m.replace(/[.*+?^${}()|[\]\\]/g,`\\$&`)}#(.+)$`),t=t=>{if(t.defaultPrevented||t.button!==0||t.metaKey||t.ctrlKey||t.shiftKey||t.altKey)return;let n=t.target;if(!(n instanceof Element))return;let r=n.closest(`a`);if(r===null)return;let i=(r.getAttribute(`href`)??``).match(e);if(i===null||i[1]===void 0)return;t.preventDefault(),t.stopPropagation();let a=i[1];try{a=decodeURIComponent(a)}catch{}pt(a)};return document.addEventListener(`click`,t,!0),()=>document.removeEventListener(`click`,t,!0)}const Zb=`dsh-tiddlywiki`,Qb=`dsh-tiddlywiki/rightbar`;function $b(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=at(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(Ge,{detail:Qb}));let e=e=>{e.detail!==Qb&&o()};return document.addEventListener(Ge,e),()=>document.removeEventListener(Ge,e)},[a,o]),c.createElement(`div`,{ref:r,className:`dsh-tw-rightbar-tab`})}function ex(){return{id:Zb,kind:`dsh-tiddlywiki`,title:()=>Ye(),guide:[{order:10,title:()=>`TiddlyWiki 知识库`,description:()=>`在右侧边栏打开 TiddlyWiki 编辑器(与聊天并排)`,icon:Ze}]}}function tx(e,t){let n=[];try{n.push(e.register(ex()));let r=t.inject(`sidebar.right.pane.tab`,()=>t.register({name:`sidebar.right.pane.tab`,key:Zb},$b));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 rt(),()=>{for(let e of n.splice(0))e()}}const nx=[`slots`];function rx(e){try{let t=[],n=!1,r=()=>{n=!0;for(let e of t.splice(0))e()},i=(e,t)=>{try{t()}catch(t){console.error(`[dsh-tiddlywiki] ${e} failed:`,t)}};i(`styles`,()=>t.push(d()));let a=new f,o,s;if(i(`quick-note widget`,()=>{o=Yy(),t.push(()=>o?.dispose())}),i(`sync controller`,()=>{s=$y(),t.push(()=>s?.dispose())}),i(`sidebar entry`,()=>t.push(we(a))),i(`center panel`,()=>t.push(gt(a))),o!==void 0&&s!==void 0){let e=o,n=s;i(`knowledge FAB`,()=>t.push(nb(a,e,n)))}i(`editor popup`,()=>t.push(Ot)),e.slots!==void 0&&ye().then(r=>{if(n)return;let a=o;r.showQuickNoteDock&&a!==void 0&&i(`quick-note dock`,()=>{let n=e.slots?.inject(`conversation.input.dock`,()=>e.slots?.register({name:`conversation.input.dock`,id:`quick-note`,order:8,label:`快速笔记`},rb(a)));n!==void 0&&t.push(n)}),i(`session summary tab`,()=>{let n=cb(e.slots,r);n!==void 0&&t.push(n)})}).catch(e=>{console.error(`[dsh-tiddlywiki] ui config load failed:`,e)}),i(`rightbar mount`,()=>{let r,i=a=>{let o=e.get?.(`sidebarRightTabs`);if(o===void 0){a<6&&!n&&(r=window.setTimeout(()=>i(a+1),500*(a+1)));return}ye().then(r=>{if(n||!r.showRightbarTab)return;let i=tx(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(Xb())),i(`settings section`,()=>{let n=e.slots?.inject(`settings.section`,()=>e.slots?.register({name:`settings.section`,id:`dsh-tiddlywiki`,order:50,label:`TiddlyWiki 知识库`},vb));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=rx,exports.inject=nx,exports.name=`dsh-tiddlywiki/client`;
|
|
773
|
+
`):``}function wb(e){return typeof e==`string`?e:``}async function Tb(e){try{let t=await fetch(e,{signal:AbortSignal.timeout(1e4)});if(t.status===404){let e=await t.json().catch(()=>null);return typeof e==`object`&&e?e:{notFound:!0}}if(!t.ok)return null;let n=await t.json();return typeof n==`object`&&n?n:null}catch{return null}}async function Eb(e){return ib(e,1e4)}function Db(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 Ob(e){return t=>{t.defaultPrevented||t.button!==0||t.metaKey||t.ctrlKey||t.shiftKey||t.altKey||(t.preventDefault(),t.stopPropagation(),pt(e))}}function kb(e){let t=yb[e.toolName]??e.toolName,n=c.createElement(`div`,{className:`dsh-tw-toolcard-head`},c.createElement(`span`,{className:`dsh-tw-toolcard-badge`},t),e.title!==void 0&&e.title.length>0?c.createElement(`span`,{className:`dsh-tw-toolcard-title`,title:e.title},e.title):null,e.onOpen===void 0?null:c.createElement(`button`,{type:`button`,className:`dsh-tw-toolcard-open`,onClick:e.onOpen},`在 TW 打开`)),r=e.subtitle!==void 0||e.tags!==void 0&&e.tags.length>0?c.createElement(`div`,{className:`dsh-tw-toolcard-meta`},e.subtitle===void 0?null:c.createElement(`span`,{className:`dsh-tw-toolcard-sub`},e.subtitle),e.tags!==void 0&&e.tags.length>0?c.createElement(`span`,{className:`dsh-tw-toolcard-tags`},...e.tags.map((e,t)=>c.createElement(`span`,{className:`dsh-tw-toolcard-tag`,key:`${t}-${e}`},e))):null):null;return c.createElement(`div`,{className:`dsh-tw-toolcard`},n,r,c.createElement(`div`,{className:`dsh-tw-toolcard-body`},e.children),e.foot!==void 0&&e.foot!==null?c.createElement(`div`,{className:`dsh-tw-toolcard-foot`},e.foot):null)}const Ab=new Map;function jb(e){let t=Ab.get(e);if(t!==void 0){if(Date.now()-t.at>3e5){Ab.delete(e);return}return Ab.delete(e),Ab.set(e,t),t}}function Mb(e,t){for(Ab.delete(e),Ab.set(e,{at:Date.now(),...t});Ab.size>50;){let e=Ab.keys().next().value;if(e===void 0)break;Ab.delete(e)}}function Nb(e){e.length>0&&Ab.delete(e)}function Pb(e){let t=e.join(`\0`);c.useEffect(()=>{for(let e of t.split(`\0`))Nb(e)},[t])}function Fb(e){let{title:t,fresh:n}=e,{loading:r,data:i}=Db(async()=>{n===!0&&Nb(t);let e=jb(t);if(e!==void 0)return{get:e.get,html:e.html};let[r,i]=await Promise.all([Tb(`${g}?title=${encodeURIComponent(t)}`),Eb(t)]);return typeof i==`string`&&i.length>0&&Mb(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(kb,{toolName:e.toolName,title:t,subtitle:e.subtitle,tags:l,onOpen:Ob(t),foot:u===void 0?void 0:`修改于 ${u}`},s)}function Ib(e){let t=c.createElement(`span`,{className:`dsh-tw-toolcard-row-head`},c.createElement(`span`,{className:`dsh-tw-toolcard-row-title`},e.title),e.tags!==void 0&&e.tags.length>0?c.createElement(`span`,{className:`dsh-tw-toolcard-row-tags`},e.tags.slice(0,4).join(` · `)):null,typeof e.modified==`string`&&e.modified.length>0?c.createElement(`span`,{className:`dsh-tw-toolcard-row-meta`},e.modified):null),n=typeof e.snippet==`string`&&e.snippet.trim().length>0?e.snippet:null;return c.createElement(`a`,{className:`dsh-tw-toolcard-row`,href:`${m}#${encodeURIComponent(e.title)}`,onClick:Ob(e.title),title:n===null?e.title:`${e.title}\n${n}`},t,n===null?null:c.createElement(`span`,{className:`dsh-tw-toolcard-row-snippet`},n))}function 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(Ib,{key:`${e.title}-${t}`,...e}))),c.createElement(kb,{toolName:e.toolName,title:e.title,subtitle:e.subtitle},n)}function Rb(e){let t=/工作区\s+(ws\/[^\s((]+)/.exec(e);if(t===null||t[1]===void 0)return null;let n=t[1].slice(3);return n.length>0?n:null}function zb(e){let t=wb(e.args.query),n=new URLSearchParams;if(t.length>0&&n.set(`query`,t),Array.isArray(e.args.tags))for(let t of e.args.tags)typeof t==`string`&&t.length>0&&n.append(`tags`,t);typeof e.args.tag==`string`&&e.args.tag.length>0&&n.set(`tag`,e.args.tag),typeof e.args.since==`string`&&e.args.since.length>0&&n.set(`since`,e.args.since),typeof e.args.type==`string`&&e.args.type.length>0&&n.set(`type`,e.args.type),typeof e.args.field==`string`&&e.args.field.length>0&&n.set(`field`,e.args.field),typeof e.args.value==`string`&&e.args.value.length>0&&n.set(`value`,e.args.value),typeof e.args.limit==`number`&&n.set(`limit`,String(e.args.limit));let r=Rb(e.text);r!==null&&n.set(`workspace`,r);let i=n.toString(),a=Db(()=>Tb(`${S}?${i}`),[i]).data,o=(Array.isArray(a?.items)?a.items:[]).map(e=>({title:wb(e.title),tags:Array.isArray(e.tags)?e.tags.filter(e=>typeof e==`string`):[],modified:typeof e.modified==`string`?e.modified:null,snippet:typeof e.snippet==`string`?e.snippet:``})),s=typeof a?.total==`number`?a.total:void 0,l=typeof a?.workspace==`string`&&a.workspace.length>0?a.workspace:null,u=``;l!==null&&a?.scope===`workspace`?u=` · 已在工作区 ws/${l} 内缩小范围`:l!==null&&a?.fellBack===!0&&(u=` · 工作区 ws/${l} 内 0 条,已扩大到全库`);let d=`关键词「${t||`(全部)`}」${s===void 0?``:` · 共 ${s} 条`}${u}`;return c.createElement(Lb,{toolName:e.toolName,title:t.length>0?t:`检索`,subtitle:d,rows:o,fallbackText:`没有匹配的笔记`})}function Bb(e){let t=e.title.length>0?c.createElement(`div`,{className:`dsh-tw-toolcard-empty`},`附件已保存进 wiki(二进制条目,卡片不内联渲染)。`):c.createElement(`div`,{className:`dsh-tw-toolcard-empty`},`附件已保存。`);return c.createElement(kb,{toolName:e.toolName,title:e.title.length>0?e.title:void 0,...e.title.length>0?{onOpen:Ob(e.title)}:{}},t)}function Vb(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=Db(()=>Tb(`${x}?${n}`),[n]).data,i=(Array.isArray(r?.items)?r.items:[]).map(e=>({title:wb(e.title),tags:Array.isArray(e.tags)?e.tags.filter(e=>typeof e==`string`):[],modified:typeof e.modified==`string`?e.modified:null,snippet:typeof e.snippet==`string`?e.snippet:``}));return c.createElement(Lb,{toolName:e.toolName,title:`最近修改`,subtitle:i.length>0?`最近 ${i.length} 条`:``,rows:i,fallbackText:`暂无笔记`})}function Hb(e){let t=(Array.isArray(e.args.items)?e.args.items:[]).map(e=>typeof e==`object`&&e&&typeof e.title==`string`?wb(e.title):null).filter(e=>e!==null&&e.length>0).map(e=>({title:e}));return Pb(t.map(e=>e.title)),c.createElement(Lb,{toolName:e.toolName,title:`批量写入`,subtitle:t.length>0?`写入 ${t.length} 篇`:`(参数中没有可解析的 items)`,rows:t,fallbackText:`没有可展示的笔记`})}function Ub(e){let t=Db(()=>Tb(`${b}?limit=60&sort=count`),[]).data,n=(Array.isArray(t?.items)?t.items:[]).map(e=>({tag:wb(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(kb,{toolName:e.toolName,title:`标签`,subtitle:`共 ${i} 个`},a)}function Wb(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(kb,{toolName:e.toolName},t)}function Gb(e){Pb([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(kb,{toolName:e.toolName,title:e.title.length>0?e.title:void 0},t)}function Kb(e,t){switch(e){case`tiddlywiki_get`:case`tiddlywiki_put`:case`tiddlywiki_append`:case`tiddlywiki_delete`:case`tiddlywiki_attach`:case`tiddlywiki_backlinks`:return wb(t.title);case`tiddlywiki_rename`:return wb(t.newTitle);case`tiddlywiki_search`:return wb(t.query);default:return``}}function qb(e){let t=e.block,n=e.toolName,r=bb(t),i=xb(t),a=n!==void 0&&n.length>0?n:i?.name??``,o=Sb(i?.argsRaw??``),s=r?Cb(t.content):``;if(i===null)return c.createElement(kb,{toolName:a},c.createElement(`pre`,{className:`dsh-tw-toolcard-fallback`},s||`(无调用信息)`));if(!r)return c.createElement(kb,{toolName:a,title:Kb(a,o)},c.createElement(`div`,{className:`dsh-tw-toolcard-pending`},`处理中…`));if(t.isError===!0)return c.createElement(kb,{toolName:a,title:Kb(a,o)},c.createElement(`div`,{className:`dsh-tw-toolcard-error`},s||`工具调用失败`));switch(a){case`tiddlywiki_get`:return c.createElement(Fb,{toolName:a,title:wb(o.title),subtitle:`读取`});case`tiddlywiki_put`:return c.createElement(Fb,{toolName:a,title:wb(o.title),subtitle:`已写入`,fresh:!0});case`tiddlywiki_append`:return c.createElement(Fb,{toolName:a,title:wb(o.title),subtitle:`已增量写入`,fresh:!0});case`tiddlywiki_rename`:return c.createElement(Fb,{toolName:a,title:wb(o.newTitle),subtitle:`已重命名「${wb(o.oldTitle)}」→`,fresh:!0});case`tiddlywiki_delete`:return c.createElement(Gb,{toolName:a,title:wb(o.title),text:s});case`tiddlywiki_attach`:return c.createElement(Bb,{toolName:a,title:wb(o.title)});case`tiddlywiki_batch_put`:return c.createElement(Hb,{toolName:a,args:o});case`tiddlywiki_search`:return c.createElement(zb,{toolName:a,args:o,text:s});case`tiddlywiki_recent`:return c.createElement(Vb,{toolName:a,args:o});case`tiddlywiki_list_tags`:return c.createElement(Ub,{toolName:a});case`tiddlywiki_git_sync`:case`tiddlywiki_git_resolve`:return c.createElement(Wb,{toolName:a,text:s});default:return c.createElement(kb,{toolName:a,title:Kb(a,o)},c.createElement(`pre`,{className:`dsh-tw-toolcard-fallback`},s||`(完成)`))}}const Jb=[`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 Jb)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}`},qb));r!==void 0&&t.push(r)}catch(e){console.error(`[dsh-tiddlywiki] register toolview ${n} failed:`,e)}return t}function Xb(){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`)??``,i=null;try{let e=new URL(r,window.location.origin);e.origin===window.location.origin&&e.pathname===m&&e.hash.length>1&&(i=e.hash.substring(1))}catch{}if(i!==null){e.preventDefault(),e.stopPropagation();try{i=decodeURIComponent(i)}catch{}pt(i)}};return document.addEventListener(`click`,e,!0),()=>document.removeEventListener(`click`,e,!0)}const Zb=`dsh-tiddlywiki`,Qb=`dsh-tiddlywiki/rightbar`;function $b(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=at(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(Ge,{detail:Qb}));let e=e=>{e.detail!==Qb&&o()};return document.addEventListener(Ge,e),()=>document.removeEventListener(Ge,e)},[a,o]),c.createElement(`div`,{ref:r,className:`dsh-tw-rightbar-tab`})}function ex(){return{id:Zb,kind:`dsh-tiddlywiki`,title:()=>Ye(),guide:[{order:10,title:()=>`TiddlyWiki 知识库`,description:()=>`在右侧边栏打开 TiddlyWiki 编辑器(与聊天并排)`,icon:Ze}]}}function tx(e,t){let n=[];try{n.push(e.register(ex()));let r=t.inject(`sidebar.right.pane.tab`,()=>t.register({name:`sidebar.right.pane.tab`,key:Zb},$b));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 rt(),()=>{for(let e of n.splice(0))e()}}const nx=[`slots`];function rx(e){try{let t=[],n=!1,r=()=>{n=!0;for(let e of t.splice(0))e()},i=(e,t)=>{try{t()}catch(t){console.error(`[dsh-tiddlywiki] ${e} failed:`,t)}};i(`styles`,()=>t.push(d()));let a=new f,o,s;if(i(`quick-note widget`,()=>{o=Yy(),t.push(()=>o?.dispose())}),i(`sync controller`,()=>{s=$y(),t.push(()=>s?.dispose())}),i(`sidebar entry`,()=>t.push(we(a))),i(`center panel`,()=>t.push(gt(a))),o!==void 0&&s!==void 0){let e=o,n=s;i(`knowledge FAB`,()=>t.push(nb(a,e,n)))}i(`editor popup`,()=>t.push(Ot)),e.slots!==void 0&&ye().then(r=>{if(n)return;let a=o;r.showQuickNoteDock&&a!==void 0&&i(`quick-note dock`,()=>{let n=e.slots?.inject(`conversation.input.dock`,()=>e.slots?.register({name:`conversation.input.dock`,id:`quick-note`,order:8,label:`快速笔记`},rb(a)));n!==void 0&&t.push(n)}),i(`session summary tab`,()=>{let n=cb(e.slots,r);n!==void 0&&t.push(n)})}).catch(e=>{console.error(`[dsh-tiddlywiki] ui config load failed:`,e)}),i(`rightbar mount`,()=>{let r,i=a=>{let o=e.get?.(`sidebarRightTabs`);if(o===void 0){a<6&&!n&&(r=window.setTimeout(()=>i(a+1),500*(a+1)));return}ye().then(r=>{if(n||!r.showRightbarTab)return;let i=tx(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(Xb())),i(`settings section`,()=>{let n=e.slots?.inject(`settings.section`,()=>e.slots?.register({name:`settings.section`,id:`dsh-tiddlywiki`,order:50,label:`TiddlyWiki 知识库`},vb));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=rx,exports.inject=nx,exports.name=`dsh-tiddlywiki/client`;
|
|
774
774
|
return module.exports;
|
|
775
775
|
}
|
|
776
776
|
});
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-tiddlywiki",
|
|
3
3
|
"description": "TiddlyWiki 5 as the DSH persistent knowledge base: fifteen tiddlywiki_* agent tools (filtered search/recent/tags/batch/rewrite/trash/lint + git sync & conflict resolve), a full TiddlyWiki editor embedded in the GUI center column (same-origin proxy, works over Tailscale/LAN/domain), a right-side wiki tab, a per-session summary tab, a quick-note card with draft auto-save behind a single knowledge FAB, an optional clip bookmarklet that also saves selected images as attachments, and git-based sync with auto-commit. Mounts via the official dsh plugin system — no DSH source changes.",
|
|
4
|
-
"version": "0.25.
|
|
4
|
+
"version": "0.25.1",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"keywords": [
|
|
7
7
|
"dsh",
|
package/src/client/tool-views.ts
CHANGED
|
@@ -735,10 +735,15 @@ export function registerToolViews(slots: {
|
|
|
735
735
|
* convention `[标题](/dsh-tiddlywiki/tw/#标题)` and the links inside render
|
|
736
736
|
* fragments — opens the center TW panel at that tiddler instead of navigating
|
|
737
737
|
* the DSH page. Returns a disposer.
|
|
738
|
+
*
|
|
739
|
+
* The href may be RELATIVE (`/dsh-tiddlywiki/tw/#标题`, what agents/tools emit)
|
|
740
|
+
* or ABSOLUTE (`https://host/dsh-tiddlywiki/tw/#标题`, what the DSH web app's
|
|
741
|
+
* markdown renderer resolves to before classifying http(s) links as "external"
|
|
742
|
+
* and giving them its own `target="_blank"` + `openExternalLink` onClick). Both
|
|
743
|
+
* must be matched, otherwise the click falls through to DSH's external-link
|
|
744
|
+
* handler and the note opens in a new browser tab instead of the TW panel.
|
|
738
745
|
*/
|
|
739
746
|
export function installWikiLinkInterceptor(): () => void {
|
|
740
|
-
// Derived from the one proxy-base literal so the route can never drift.
|
|
741
|
-
const proxyHash = new RegExp(`^${TW_PROXY_BASE.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}#(.+)$`)
|
|
742
747
|
const onDocumentClick = (event: MouseEvent): void => {
|
|
743
748
|
// 只接管「普通左键点击」:中键 / Ctrl(⌘)·Shift·Alt+点击是浏览器的新标签页、
|
|
744
749
|
// 新窗口、下载等语义,一律放行(否则无法新标签页打开、无法复制链接)。
|
|
@@ -748,11 +753,20 @@ export function installWikiLinkInterceptor(): () => void {
|
|
|
748
753
|
const anchor = target.closest('a')
|
|
749
754
|
if (anchor === null) return
|
|
750
755
|
const href = anchor.getAttribute('href') ?? ''
|
|
751
|
-
|
|
752
|
-
|
|
756
|
+
// Resolve relative AND absolute hrefs against the current origin, then only
|
|
757
|
+
// take same-origin links to the TW proxy index whose hash carries a title.
|
|
758
|
+
let title: string | null = null
|
|
759
|
+
try {
|
|
760
|
+
const url = new URL(href, window.location.origin)
|
|
761
|
+
if (url.origin === window.location.origin && url.pathname === TW_PROXY_BASE && url.hash.length > 1) {
|
|
762
|
+
title = url.hash.substring(1)
|
|
763
|
+
}
|
|
764
|
+
} catch {
|
|
765
|
+
/* not a parseable URL — leave for the browser's default handling */
|
|
766
|
+
}
|
|
767
|
+
if (title === null) return
|
|
753
768
|
event.preventDefault()
|
|
754
769
|
event.stopPropagation()
|
|
755
|
-
let title = match[1]
|
|
756
770
|
try {
|
|
757
771
|
title = decodeURIComponent(title)
|
|
758
772
|
} catch {
|