dsh-tiddlywiki 0.16.25 → 0.16.26

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
@@ -278,6 +278,7 @@ lib/ # 预构建产物(发布含 lib/**,提交入库;
278
278
 
279
279
  > 最近几个主要版本的一句话记录(完整变更见 [Releases](https://github.com/bbqisbbq/dsh-tiddlywiki/releases) / git log)。
280
280
 
281
+ - **v0.16.26**(2026-09-10):**修:剪藏书签在真实页面上崩溃**(`Cannot set properties of null (setting 'value')`)。根因:浮层字段在 `p` 尚未挂到 document 前就用 `document.getElementById` 取值(脱离文档树查不到 → null);同时 `cb_*` id 可能与页面自身元素冲突。修复:所有浮层字段改为 `p.querySelector` 作用域内查找 + 字段赋值挪到 `appendChild` 之后;并把书签放进 **jsdom 真实 DOM 端到端跑通**(浮层/填充/选图/提交载荷/结果态 12 项全过)作为验证,`verify-clip-bridge.mjs` 新增静态回归断言(禁未限定 getElementById + 挂载时序)。行为无变化,仅书签代码(seed 文档)修复。
281
282
  - **v0.16.25**(2026-09-10):**剪藏桥支持图片剪藏(浮层选图)**。书签升级为**选择式浮层**:可改标题/编辑选中文字,浮层列出页面图片缩略图(自动跳过 <80px 小图标与 data:/blob: 占位、去重,`og:image` 标「封面」并默认勾选)勾选图片后一键剪藏。图片由桥(DSH 进程侧)**下载字节存为二进制附件 tiddler**(`type: image/*` + base64 正文,即 TW 原生附件形态;`clip-url`/`clip-note` 溯源),笔记改用 wikitext 用 `[img[标题]]` 内嵌展示;某张下载失败自动降级为链接、不阻塞整次剪藏;纯文字剪藏保持原 markdown 路径不变。安全不变(127.0.0.1 + Host 校验 + 可选 token;下载带 referer/UA 应对常见防盗链)。验收新增 image-flow 测试 + 书签语法检查(`verify-clip-bridge.mjs` 24 项全过)。
282
283
 
283
284
  - **v0.16.24**(2026-09-09):**新:本地剪藏桥 + 书签小工具**。DSH 进程内新增只监听 **127.0.0.1** 的 HTTP 桥(配置组 `bridge.*`:`enabled` 默认关、`port` 默认 8618、`token` 可选共享口令、`tag` 默认 `clip`)——浏览器书签(JS 小工具)把当前页标题/URL/选中文字 POST 给它,经唯一写入通道落进 wiki(markdown、重名自动 `(2)` 去重、默认 `clip` 标签、随 wiki 自动 git commit)。安全:Host 头白名单防 DNS rebinding、CORS/PNA 预检放行(https 页面书签可用)、`bridge.token` 强烈建议设置。附**可选 seed** `clip-bridge`:「本地剪藏桥(书签小工具)」说明文档(含书签代码/启用步骤/安全说明/curl 用法,打 `dsh-docs` 标签进「📚 插件文档」栏,可反初始化);设置页新增 4 个配置字段。`enabled`/`token`/`tag` 设置页保存即生效,改端口需重启 dsh web。
package/lib/index.js CHANGED
@@ -3402,7 +3402,7 @@ DSH 进程里运行着一个只监听 **127.0.0.1**(本机回环)的 HTTP
3402
3402
  3. **装书签**:浏览器书签栏新建书签,名称随意(如「剪藏」),**地址(URL)粘贴下面整段代码**。若设置了 token,把代码里 \`'x-clip-token': ''\` 的空字符串换成你的口令;若改了端口,把 \`127.0.0.1:8618\` 一起改掉:
3403
3403
 
3404
3404
  \`\`\`javascript
3405
- javascript:(function(){var E=function(id){return document.getElementById(id)};var t=(document.title||location.hostname).trim();var sel=(window.getSelection()?window.getSelection().toString():'').trim();var imgs=[],seen={};function add(u,a,c){u=String(u||'').trim();if(!u||u.indexOf('data:')===0||u.indexOf('blob:')===0||seen[u])return;seen[u]=1;imgs.push({u:u,a:(a||'').slice(0,60),c:!!c})}var mm=document.querySelector('meta[property="og:image"],meta[name="twitter:image"],link[rel="image_src"]');if(mm){var mu=mm.content||mm.href;if(mu)add(mu,'封面',true)}for(var ii=0;ii<document.images.length;ii++){var im=document.images[ii],iu=im.currentSrc||im.src;if(!iu)continue;if(im.naturalWidth&&im.naturalHeight&&(im.naturalWidth<80||im.naturalHeight<80))continue;add(iu,im.alt||im.title,false)}if(imgs.length>60)imgs=imgs.slice(0,60);var ov=document.createElement('div');ov.style.cssText='position:fixed;top:0;left:0;right:0;bottom:0;background:rgba(10,14,20,.62);z-index:2147483647;display:flex;align-items:flex-start;justify-content:center;padding:4vh 12px';var p=document.createElement('div');p.id='cb_p';p.style.cssText='background:#1c2128;border:1px solid #3a414c;border-radius:12px;max-width:680px;width:100%;max-height:86vh;overflow:auto;padding:16px 18px;color:#e6e6e6;font-family:system-ui,-apple-system,"Segoe UI",sans-serif;box-sizing:border-box';p.innerHTML='<div style="font-size:16px;font-weight:700">剪藏到 TiddlyWiki</div><label style="display:block;font-size:12px;opacity:.7;margin:10px 0 2px">标题</label><input id="cb_t" style="width:100%;box-sizing:border-box;background:#0f131a;border:1px solid #3a414c;border-radius:6px;color:#eee;padding:7px 9px;font-size:13px" value=""><label style="display:block;font-size:12px;opacity:.7;margin:10px 0 2px">正文 / 选中文字(可选)</label><textarea id="cb_s" rows="5" style="width:100%;box-sizing:border-box;background:#0f131a;border:1px solid #3a414c;border-radius:6px;color:#eee;padding:7px 9px;font-size:13px;resize:vertical"></textarea>';E('cb_t').value=t;E('cb_s').value=sel;var grid=document.createElement('div');grid.style.cssText='display:grid;grid-template-columns:repeat(auto-fill,minmax(120px,1fr));gap:8px;margin:10px 0';for(var j=0;j<imgs.length;j++){(function(idx){var it=imgs[idx],lab=document.createElement('label');lab.style.cssText='display:flex;flex-direction:column;gap:4px;cursor:pointer;background:#0f131a;border:1px solid #3a414c;border-radius:8px;padding:6px;overflow:hidden';var top=document.createElement('div');top.style.cssText='position:relative';var imgE=document.createElement('img');imgE.style.cssText='display:block;width:100%;height:76px;object-fit:cover;border-radius:4px;background:#000';imgE.src=it.u;imgE.loading='lazy';imgE.referrerPolicy='no-referrer';top.appendChild(imgE);if(it.c){var bd=document.createElement('span');bd.style.cssText='position:absolute;top:2px;left:2px;background:#f5a623;color:#000;font-size:10px;padding:0 4px;border-radius:3px';bd.textContent='封面';top.appendChild(bd)}var row=document.createElement('div');row.style.cssText='display:flex;align-items:center;gap:6px';var ch=document.createElement('input');ch.type='checkbox';ch.id='cb_i_'+idx;ch.checked=it.c;var nm=document.createElement('span');nm.style.cssText='font-size:11px;opacity:.85;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;flex:1';nm.textContent=it.a||(it.u.split('/').pop()||'');nm.title=it.u;row.appendChild(ch);row.appendChild(nm);lab.appendChild(top);lab.appendChild(row);grid.appendChild(lab)})(j)};var foot=document.createElement('div');foot.style.cssText='display:flex;align-items:center;justify-content:space-between;margin-top:12px';var cnt=document.createElement('span');cnt.id='cb_cnt';cnt.style.cssText='font-size:12px;opacity:.75';foot.appendChild(cnt);var btns=document.createElement('div');btns.style.cssText='display:flex;gap:8px';var cancel=document.createElement('button');cancel.textContent='取消';cancel.style.cssText='cursor:pointer;background:#333a45;border:0;color:#eee;border-radius:6px;padding:7px 14px;font-size:13px';var go=document.createElement('button');go.id='cb_btn';go.textContent='剪藏';go.style.cssText='cursor:pointer;background:#2f6feb;border:0;color:#fff;border-radius:6px;padding:7px 14px;font-size:13px';btns.appendChild(cancel);btns.appendChild(go);foot.appendChild(btns);p.appendChild(grid);p.appendChild(foot);ov.appendChild(p);document.body.appendChild(ov);function upd(){var n=0;for(var q=0;q<imgs.length;q++){var c=E('cb_i_'+q);if(c&&c.checked)n++}cnt.textContent=(imgs.length?('共 '+imgs.length+' 张,已选 '+n+' 张'):'页面没有可选图片(仍可剪藏文字)')}upd();grid.onchange=function(){upd()};function close(){document.removeEventListener('keydown',onKey,true);if(ov.parentNode){document.body.removeChild(ov)}}var onKey=function(ev){if(ev.key==='Escape')close()};cancel.onclick=close;ov.onclick=function(ev){if(ev.target===ov)close()};p.onclick=function(ev){ev.stopPropagation()};document.addEventListener('keydown',onKey,true);function doClip(){var tt=(E('cb_t').value||'').trim()||t;var urls=[];for(var k=0;k<imgs.length;k++){var ck=E('cb_i_'+k);if(ck&&ck.checked)urls.push(imgs[k].u)}var btn=E('cb_btn');btn.disabled=true;btn.textContent='剪藏中…';fetch('http://127.0.0.1:8618/clip',{method:'POST',headers:{'content-type':'application/json','x-clip-token':''},body:JSON.stringify({title:tt,url:location.href,text:(E('cb_s').value||''),images:urls})}).then(function(r){return r.json().then(function(j){return {ok:r.ok&&!!j.ok,error:(j.error||('HTTP '+r.status)),title:j.title,images:j.images||[]}})}).then(function(res){var okN=0;for(var v=0;v<res.images.length;v++)if(res.images[v].ok)okN++;var line=res.ok?('已剪藏:'+(res.title||tt)+(res.images.length?(' 图片 '+okN+'/'+res.images.length+' 成功'):'')):('剪藏失败:'+res.error);p.innerHTML='<div style="font-size:15px;font-weight:700;margin:8px 0">'+line+'</div><div style="font-size:12px;opacity:.8;margin-bottom:12px">图片以附件形式存进知识库(失败项已降级为链接)。</div><button id="cb_d" style="cursor:pointer;background:#2f6feb;border:0;color:#fff;border-radius:6px;padding:7px 14px;font-size:13px">完成</button>';E('cb_d').onclick=close}).catch(function(e){p.innerHTML='<div style="font-size:14px;color:#ff8080;font-weight:700;margin:8px 0">剪藏桥不可达</div><div style="font-size:12px;opacity:.85;margin-bottom:12px">'+(e&&e.message?e.message:'')+' 请确认:① dsh web 在运行;② 设置页已启用「本地剪藏桥」;③ 书签里的端口与设置一致;④ 设置了 token 的话书签已带上。</div><button id="cb_d" style="cursor:pointer;background:#333a45;border:0;color:#fff;border-radius:6px;padding:7px 14px;font-size:13px">关闭</button>';E('cb_d').onclick=close})}go.onclick=doClip})()
3405
+ javascript:(function(){var t=(document.title||location.hostname).trim();var sel=(window.getSelection()?window.getSelection().toString():'').trim();var imgs=[],seen={};function add(u,a,c){u=String(u||'').trim();if(!u||u.indexOf('data:')===0||u.indexOf('blob:')===0||seen[u])return;seen[u]=1;imgs.push({u:u,a:(a||'').slice(0,60),c:!!c})}var mm=document.querySelector('meta[property="og:image"],meta[name="twitter:image"],link[rel="image_src"]');if(mm){var mu=mm.content||mm.href;if(mu)add(mu,'封面',true)}for(var ii=0;ii<document.images.length;ii++){var im=document.images[ii],iu=im.currentSrc||im.src;if(!iu)continue;if(im.naturalWidth&&im.naturalHeight&&(im.naturalWidth<80||im.naturalHeight<80))continue;add(iu,im.alt||im.title,false)}if(imgs.length>60)imgs=imgs.slice(0,60);var ov=document.createElement('div');ov.style.cssText='position:fixed;top:0;left:0;right:0;bottom:0;background:rgba(10,14,20,.62);z-index:2147483647;display:flex;align-items:flex-start;justify-content:center;padding:4vh 12px';var p=document.createElement('div');p.id='cb_p';p.style.cssText='background:#1c2128;border:1px solid #3a414c;border-radius:12px;max-width:680px;width:100%;max-height:86vh;overflow:auto;padding:16px 18px;color:#e6e6e6;font-family:system-ui,-apple-system,"Segoe UI",sans-serif;box-sizing:border-box';p.innerHTML='<div style="font-size:16px;font-weight:700">剪藏到 TiddlyWiki</div><label style="display:block;font-size:12px;opacity:.7;margin:10px 0 2px">标题</label><input id="cb_t" style="width:100%;box-sizing:border-box;background:#0f131a;border:1px solid #3a414c;border-radius:6px;color:#eee;padding:7px 9px;font-size:13px" value=""><label style="display:block;font-size:12px;opacity:.7;margin:10px 0 2px">正文 / 选中文字(可选)</label><textarea id="cb_s" rows="5" style="width:100%;box-sizing:border-box;background:#0f131a;border:1px solid #3a414c;border-radius:6px;color:#eee;padding:7px 9px;font-size:13px;resize:vertical"></textarea>';var Q=function(id){return p.querySelector('#'+id)};var grid=document.createElement('div');grid.style.cssText='display:grid;grid-template-columns:repeat(auto-fill,minmax(120px,1fr));gap:8px;margin:10px 0';for(var j=0;j<imgs.length;j++){(function(idx){var it=imgs[idx],lab=document.createElement('label');lab.style.cssText='display:flex;flex-direction:column;gap:4px;cursor:pointer;background:#0f131a;border:1px solid #3a414c;border-radius:8px;padding:6px;overflow:hidden';var top=document.createElement('div');top.style.cssText='position:relative';var imgE=document.createElement('img');imgE.style.cssText='display:block;width:100%;height:76px;object-fit:cover;border-radius:4px;background:#000';imgE.src=it.u;imgE.loading='lazy';imgE.referrerPolicy='no-referrer';top.appendChild(imgE);if(it.c){var bd=document.createElement('span');bd.style.cssText='position:absolute;top:2px;left:2px;background:#f5a623;color:#000;font-size:10px;padding:0 4px;border-radius:3px';bd.textContent='封面';top.appendChild(bd)}var row=document.createElement('div');row.style.cssText='display:flex;align-items:center;gap:6px';var ch=document.createElement('input');ch.type='checkbox';ch.id='cb_i_'+idx;ch.checked=it.c;var nm=document.createElement('span');nm.style.cssText='font-size:11px;opacity:.85;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;flex:1';nm.textContent=it.a||(it.u.split('/').pop()||'');nm.title=it.u;row.appendChild(ch);row.appendChild(nm);lab.appendChild(top);lab.appendChild(row);grid.appendChild(lab)})(j)};var foot=document.createElement('div');foot.style.cssText='display:flex;align-items:center;justify-content:space-between;margin-top:12px';var cnt=document.createElement('span');cnt.id='cb_cnt';cnt.style.cssText='font-size:12px;opacity:.75';foot.appendChild(cnt);var btns=document.createElement('div');btns.style.cssText='display:flex;gap:8px';var cancel=document.createElement('button');cancel.textContent='取消';cancel.style.cssText='cursor:pointer;background:#333a45;border:0;color:#eee;border-radius:6px;padding:7px 14px;font-size:13px';var go=document.createElement('button');go.id='cb_btn';go.textContent='剪藏';go.style.cssText='cursor:pointer;background:#2f6feb;border:0;color:#fff;border-radius:6px;padding:7px 14px;font-size:13px';btns.appendChild(cancel);btns.appendChild(go);foot.appendChild(btns);p.appendChild(grid);p.appendChild(foot);ov.appendChild(p);document.body.appendChild(ov);Q('cb_t').value=t;Q('cb_s').value=sel;function upd(){var n=0;for(var q=0;q<imgs.length;q++){var c=Q('cb_i_'+q);if(c&&c.checked)n++}cnt.textContent=(imgs.length?('共 '+imgs.length+' 张,已选 '+n+' 张'):'页面没有可选图片(仍可剪藏文字)')}upd();grid.onchange=function(){upd()};function close(){document.removeEventListener('keydown',onKey,true);if(ov.parentNode){document.body.removeChild(ov)}}var onKey=function(ev){if(ev.key==='Escape')close()};cancel.onclick=close;ov.onclick=function(ev){if(ev.target===ov)close()};p.onclick=function(ev){ev.stopPropagation()};document.addEventListener('keydown',onKey,true);function doClip(){var tt=(Q('cb_t').value||'').trim()||t;var urls=[];for(var k=0;k<imgs.length;k++){var ck=Q('cb_i_'+k);if(ck&&ck.checked)urls.push(imgs[k].u)}var btn=Q('cb_btn');btn.disabled=true;btn.textContent='剪藏中…';fetch('http://127.0.0.1:8618/clip',{method:'POST',headers:{'content-type':'application/json','x-clip-token':''},body:JSON.stringify({title:tt,url:location.href,text:(Q('cb_s').value||''),images:urls})}).then(function(r){return r.json().then(function(j){return {ok:r.ok&&!!j.ok,error:(j.error||('HTTP '+r.status)),title:j.title,images:j.images||[]}})}).then(function(res){var okN=0;for(var v=0;v<res.images.length;v++)if(res.images[v].ok)okN++;var line=res.ok?('已剪藏:'+(res.title||tt)+(res.images.length?(' 图片 '+okN+'/'+res.images.length+' 成功'):'')):('剪藏失败:'+res.error);p.innerHTML='<div style="font-size:15px;font-weight:700;margin:8px 0">'+line+'</div><div style="font-size:12px;opacity:.8;margin-bottom:12px">图片以附件形式存进知识库(失败项已降级为链接)。</div><button id="cb_d" style="cursor:pointer;background:#2f6feb;border:0;color:#fff;border-radius:6px;padding:7px 14px;font-size:13px">完成</button>';Q('cb_d').onclick=close}).catch(function(e){p.innerHTML='<div style="font-size:14px;color:#ff8080;font-weight:700;margin:8px 0">剪藏桥不可达</div><div style="font-size:12px;opacity:.85;margin-bottom:12px">'+(e&&e.message?e.message:'')+' 请确认:① dsh web 在运行;② 设置页已启用「本地剪藏桥」;③ 书签里的端口与设置一致;④ 设置了 token 的话书签已带上。</div><button id="cb_d" style="cursor:pointer;background:#333a45;border:0;color:#fff;border-radius:6px;padding:7px 14px;font-size:13px">关闭</button>';Q('cb_d').onclick=close})}go.onclick=doClip})()
3406
3406
  \`\`\`
3407
3407
 
3408
3408
  > 部分浏览器禁止页面「拖链接到书签栏」,复制粘贴到书签的地址栏是最稳的方式。