From f82d5d731f7c1439a95dda6e1a14d40d58881d69 Mon Sep 17 00:00:00 2001
From: Ariver <shanghai3168@gmail.com>
Date: Tue, 25 Aug 2026 16:18:08 +0800
Subject: [PATCH] checkpoint: 日记改格式前备份
---
X0.Clippings/官网做完别只放截图:Codex + Remotion 做了一条 24 秒产品演示.md | 2
X1.notes/2026-08-25-Ollama-命令行与客户端的关系.md | 0
00index/标签聚合/1.3致知/wiki.md | 1
02DS/Ob/插件Notechain/26.0814-2228-Notechain.md | 4
X1.Knomo/_knomo-system/indexes/memo-index-2026-08.json | 10
X1.notes/2026-08-25-chatgpt一个很酷的用法-无限个人导师.md | 0
02DS/01dril-book/以利为利.求索住房与土地财政之路.md | 37
X1.notes/2026-08-25-bug修复-每日自动足迹的产生包含了不该有的tagfeed内容.md | 0
02DS/Ob/00inbox_ob/Obsidian附件.1个skill教你自动整理 | 0
00index/0inbox/成对的概念.md | 1
00index/0inbox/git.md | 4
02DS/Ob/00inbox_ob/ob学习笔记.md | 0
02DS/Ob/X-本机用法备忘/01全局自动化索引与整理逻辑/版本1-html/规则2_合并页覆盖.html | 46 ++
X1.notes/2026-08-25-开源项目openconnector介绍.md | 44 ++
02DS/Ob/X-本机用法备忘/01全局自动化索引与整理逻辑/2026-08-25-watch机制是怎么实现的.md | 83 ++++
02DS/Ob/X-本机用法备忘/01全局自动化索引与整理逻辑/版本1-html/tagfeed_整体架构.html | 57 ++
02DS/Ob/X-本机用法备忘/01全局自动化索引与整理逻辑/01全局自动化索引与整理逻辑.md | 72 +++
X0.Diary/26.0323.md | 7
02DS/Ob/同步与远程/ignis-让原版 Obsidian 随处可用-浏览器访问自动同步.md | 10
X1.notes/2026-08-25-命运-麦家的观点.md | 0
00index/标签聚合/1.1修身/身体与健康/灰指甲.md | 2
00index/标签聚合/1.6玩软件/obsidian.md | 2
X0.Diary/26.0825.md | 42 +
00index/标签聚合/1.6玩软件/工作流.md | 1
X0.Diary/26.0819.md | 23
02DS/Ob/X-本机用法备忘/01全局自动化索引与整理逻辑/2026-08-25-tagfeed核心逻辑备忘.md | 64 +++
X0.Clippings/达利欧最新长文2026像极了1929.md | 10
X0.Diary/26.0804.md | 6
02DS/Ob/X-本机用法备忘/01全局自动化索引与整理逻辑/00index.canvas生成原理说明.md | 44 ++
X1.notes/2026-08-25-法家发明的户籍制度和收容制度.md | 0
00index/0inbox/命令行.md | 5
02DS/工具与技能/git/2026-08-25-本机ob本仓库git启用备忘.md | 37 +
00index/00index.canvas | 159 +++----
/dev/null | 36 -
X0.Clippings/折腾了一圈,我现在这样在 Obsidian 里管理任务.md | 244 ++++++++++++
X0.Diary/26.0324.md | 9
X0.Diary/26.0322.md | 4
X2.Archived/scripts/sync_0inbox.py | 23
X0.Diary/26.0718.md | 4
02DS/Ob/X-本机用法备忘/01全局自动化索引与整理逻辑/版本1-html/标签生命周期_AB两箱.html | 50 ++
00index/0inbox/任意标签.md | 4
00index/标签聚合/X-废弃/1ThreadPoolBackgroundWorker.md | 1
42 files changed, 950 insertions(+), 198 deletions(-)
diff --git a/00index/00index.canvas b/00index/00index.canvas
index 1b231cc..66ec443 100644
--- a/00index/00index.canvas
+++ b/00index/00index.canvas
@@ -1,78 +1,75 @@
{
"nodes":[
- {"id":"63a9f0ea7bb98050","type":"text","text":"🗂 00index","x":0,"y":2115,"width":150,"height":60},
- {"id":"d465eb3356852cc5","type":"text","text":"📁 标签聚合","x":240,"y":2115,"width":150,"height":60,"color":"1"},
- {"id":"7c4c6a66f489e8ed","type":"text","text":"📁 1.1修身","x":480,"y":39,"width":150,"height":60,"color":"1"},
- {"id":"d9c874e1facd98e0","type":"text","text":"📁 身体与健康","x":720,"y":0,"width":150,"height":60,"color":"1"},
- {"id":"c5f9c902fd1b10de","type":"text","text":"[[00index/标签聚合/1.1修身/身体与健康/灰指甲|灰指甲]]","x":1050,"y":3,"width":240,"height":54,"color":"1"},
- {"id":"677b952aff43ac2e","type":"text","text":"[[00index/标签聚合/1.1修身/锻炼|锻炼]]","x":720,"y":84,"width":240,"height":54,"color":"1"},
- {"id":"429a03021bbbc6b7","type":"text","text":"📁 1.2修心","x":480,"y":198,"width":150,"height":60,"color":"1"},
- {"id":"30e7b617ee958a63","type":"text","text":"[[00index/标签聚合/1.2修心/原则|原则]]","x":720,"y":162,"width":240,"height":54,"color":"1"},
- {"id":"c0b127899f7e27f6","type":"text","text":"[[00index/标签聚合/1.2修心/整理自己|整理自己]]","x":720,"y":240,"width":240,"height":54,"color":"1"},
- {"id":"1866f746e29efd48","type":"text","text":"📁 1.3致知","x":480,"y":432,"width":150,"height":60,"color":"1"},
- {"id":"40c9ffe267639532","type":"text","text":"[[00index/标签聚合/1.3致知/wiki|wiki]]","x":720,"y":318,"width":240,"height":54,"color":"1"},
- {"id":"d40b5e4e9d3628f7","type":"text","text":"[[00index/标签聚合/1.3致知/名词解释|名词解释]]","x":720,"y":396,"width":240,"height":54,"color":"1"},
- {"id":"690347c199d186ac","type":"text","text":"[[00index/标签聚合/1.3致知/怎么用|怎么用]]","x":720,"y":474,"width":240,"height":54,"color":"1"},
- {"id":"768039cd333e48eb","type":"text","text":"[[00index/标签聚合/1.3致知/背诵|背诵]]","x":720,"y":552,"width":240,"height":54,"color":"1"},
- {"id":"949b09d044a9ebe0","type":"text","text":"📁 1.5技能","x":480,"y":783,"width":150,"height":60,"color":"1"},
- {"id":"7a1369704d2e2297","type":"text","text":"[[00index/标签聚合/1.5技能/coding|coding]]","x":720,"y":630,"width":240,"height":54,"color":"1"},
- {"id":"f67f584237697deb","type":"text","text":"📁 视觉设计相关","x":720,"y":822,"width":150,"height":60,"color":"1"},
- {"id":"6c29fb988844d116","type":"text","text":"[[00index/标签聚合/1.5技能/视觉设计相关/UI|UI]]","x":1050,"y":708,"width":240,"height":54,"color":"1"},
- {"id":"d8d177fcf54825dd","type":"text","text":"[[00index/标签聚合/1.5技能/视觉设计相关/ui-kit|ui-kit]]","x":1050,"y":786,"width":240,"height":54,"color":"1"},
- {"id":"6bf77c203f8ad926","type":"text","text":"[[00index/标签聚合/1.5技能/视觉设计相关/ui-好设计|ui-好设计]]","x":1050,"y":864,"width":240,"height":54,"color":"1"},
- {"id":"fee2a3634b154085","type":"text","text":"[[00index/标签聚合/1.5技能/视觉设计相关/动画|动画]]","x":1050,"y":942,"width":240,"height":54,"color":"1"},
- {"id":"ba42c19e08bbcc3c","type":"text","text":"📁 1.6玩软件","x":480,"y":1527,"width":150,"height":60,"color":"1"},
- {"id":"b5d4ef0b3506377c","type":"text","text":"[[00index/标签聚合/1.6玩软件/TitanVault|TitanVault]]","x":720,"y":1020,"width":240,"height":54,"color":"1"},
- {"id":"15cd37551266d11e","type":"text","text":"[[00index/标签聚合/1.6玩软件/excalidraw|excalidraw]]","x":720,"y":1098,"width":240,"height":54,"color":"1"},
- {"id":"65bed45a3159e342","type":"text","text":"[[00index/标签聚合/1.6玩软件/obsidian|obsidian]]","x":720,"y":1176,"width":240,"height":54,"color":"1"},
- {"id":"d7617f156b6c397c","type":"text","text":"[[00index/标签聚合/1.6玩软件/工作流|工作流]]","x":720,"y":1254,"width":240,"height":54,"color":"1"},
- {"id":"a1c50b58f3bb13e2","type":"text","text":"📁 操作系统","x":720,"y":1332,"width":150,"height":60,"color":"1"},
- {"id":"b5dd77af8fdef874","type":"text","text":"[[00index/标签聚合/1.6玩软件/操作系统/ios|ios]]","x":1050,"y":1335,"width":240,"height":54,"color":"1"},
- {"id":"819320edcddb4bee","type":"text","text":"📁 玩转AI","x":720,"y":1647,"width":150,"height":60,"color":"1"},
- {"id":"a52d3c173bccc718","type":"text","text":"[[00index/标签聚合/1.6玩软件/玩转AI/MLX|MLX]]","x":1050,"y":1416,"width":240,"height":54,"color":"1"},
- {"id":"e5fa8e66ac4d9830","type":"text","text":"[[00index/标签聚合/1.6玩软件/玩转AI/ai工具|ai工具]]","x":1050,"y":1494,"width":240,"height":54,"color":"1"},
- {"id":"20c6d6ce289665d6","type":"text","text":"[[00index/标签聚合/1.6玩软件/玩转AI/chatbox|chatbox]]","x":1050,"y":1572,"width":240,"height":54,"color":"1"},
- {"id":"60ae06f8fecd4450","type":"text","text":"[[00index/标签聚合/1.6玩软件/玩转AI/codex|codex]]","x":1050,"y":1650,"width":240,"height":54,"color":"1"},
- {"id":"cb65885377212bb3","type":"text","text":"[[00index/标签聚合/1.6玩软件/玩转AI/hermes|hermes]]","x":1050,"y":1728,"width":240,"height":54,"color":"1"},
- {"id":"23857661c3b1c937","type":"text","text":"[[00index/标签聚合/1.6玩软件/玩转AI/skill|skill]]","x":1050,"y":1806,"width":240,"height":54,"color":"1"},
- {"id":"4b88df50c260b132","type":"text","text":"[[00index/标签聚合/1.6玩软件/玩转AI/配图|配图]]","x":1050,"y":1884,"width":240,"height":54,"color":"1"},
- {"id":"653725a767bc9343","type":"text","text":"[[00index/标签聚合/1.6玩软件/软件研究|软件研究]]","x":720,"y":1962,"width":240,"height":54,"color":"1"},
- {"id":"c17087bb6fc0db72","type":"text","text":"[[00index/标签聚合/1.6玩软件/软件研究_怎么用|软件研究_怎么用]]","x":720,"y":2040,"width":240,"height":54,"color":"1"},
- {"id":"651e92b57a9c845a","type":"text","text":"📁 2.1工作","x":480,"y":2349,"width":150,"height":60,"color":"1"},
- {"id":"e6c7da29521d7fbc","type":"text","text":"[[00index/标签聚合/2.1工作/26锁精环项目|26锁精环项目]]","x":720,"y":2118,"width":240,"height":54,"color":"1"},
- {"id":"ab7a8dc5c0577724","type":"text","text":"[[00index/标签聚合/2.1工作/bobo-轻量版|bobo-轻量版]]","x":720,"y":2196,"width":240,"height":54,"color":"1"},
- {"id":"352a3843afa0366c","type":"text","text":"[[00index/标签聚合/2.1工作/bobo|bobo]]","x":720,"y":2274,"width":240,"height":54,"color":"1"},
- {"id":"e5603eaf5bfdb863","type":"text","text":"[[00index/标签聚合/2.1工作/ideatodo|ideatodo]]","x":720,"y":2352,"width":240,"height":54,"color":"1"},
- {"id":"4629b30545162e2f","type":"text","text":"[[00index/标签聚合/2.1工作/senki|senki]]","x":720,"y":2430,"width":240,"height":54,"color":"1"},
- {"id":"99c89632ba0ba53c","type":"text","text":"[[00index/标签聚合/2.1工作/做事|做事]]","x":720,"y":2508,"width":240,"height":54,"color":"1"},
- {"id":"5531889f64629a4f","type":"text","text":"[[00index/标签聚合/2.1工作/运营|运营]]","x":720,"y":2586,"width":240,"height":54,"color":"1"},
- {"id":"69cc5fe8efadf5c3","type":"text","text":"📁 2.2作品-软件","x":480,"y":2700,"width":150,"height":60,"color":"1"},
- {"id":"099d00387ab3e6d3","type":"text","text":"[[00index/标签聚合/2.2作品-软件/P-Aligner|P-Aligner]]","x":720,"y":2664,"width":240,"height":54,"color":"1"},
- {"id":"940da2bf9d96e73d","type":"text","text":"[[00index/标签聚合/2.2作品-软件/taglauncher|taglauncher]]","x":720,"y":2742,"width":240,"height":54,"color":"1"},
- {"id":"472ad4bce2919901","type":"text","text":"📁 3.3兴趣","x":480,"y":2895,"width":150,"height":60,"color":"1"},
- {"id":"6eb768cc0d60ce26","type":"text","text":"[[00index/标签聚合/3.3兴趣/lyric|lyric]]","x":720,"y":2820,"width":240,"height":54,"color":"1"},
- {"id":"1028244783ef9e60","type":"text","text":"[[00index/标签聚合/3.3兴趣/做饭|做饭]]","x":720,"y":2898,"width":240,"height":54,"color":"1"},
- {"id":"9d3eec8d2d8c0a12","type":"text","text":"[[00index/标签聚合/3.3兴趣/电影|电影]]","x":720,"y":2976,"width":240,"height":54,"color":"1"},
- {"id":"b31b22aeaf04690b","type":"text","text":"📁 5.人","x":480,"y":3090,"width":150,"height":60,"color":"1"},
- {"id":"1b0fdd5cc0911010","type":"text","text":"[[00index/标签聚合/5.人/谢大宝|谢大宝]]","x":720,"y":3054,"width":240,"height":54,"color":"1"},
- {"id":"abc9774d5d5da96b","type":"text","text":"[[00index/标签聚合/5.人/陈跃明|陈跃明]]","x":720,"y":3132,"width":240,"height":54,"color":"1"},
- {"id":"b80dcedb0d854a65","type":"text","text":"📁 6-物","x":480,"y":3210,"width":150,"height":60,"color":"1"},
- {"id":"b23baa101b5995f5","type":"text","text":"[[00index/标签聚合/6-物/15PM|15PM]]","x":720,"y":3213,"width":240,"height":54,"color":"1"},
- {"id":"35aea8074f2a7517","type":"text","text":"📁 9.0其他","x":480,"y":3294,"width":150,"height":60,"color":"1"},
- {"id":"c8319360b4b18b00","type":"text","text":"[[00index/标签聚合/9.0其他/地址|地址]]","x":720,"y":3297,"width":240,"height":54,"color":"1"},
- {"id":"e496692004e32d29","type":"text","text":"📁 X-废弃","x":480,"y":3804,"width":150,"height":60,"color":"1"},
- {"id":"6edfab73566ad9b8","type":"text","text":"[[00index/标签聚合/X-废弃/1ThreadPoolBackgroundWorker|1ThreadPoolBackgroundWorker]]","x":720,"y":3378,"width":240,"height":54,"color":"1"},
- {"id":"dc8e79a40cadaf89","type":"text","text":"[[00index/标签聚合/X-废弃/5-8|5-8]]","x":720,"y":3456,"width":240,"height":54,"color":"1"},
- {"id":"a6ad5a450fc53a8c","type":"text","text":"[[00index/标签聚合/X-废弃/FIXME-a|FIXME-a]]","x":720,"y":3534,"width":240,"height":54,"color":"1"},
- {"id":"570e280f75e4c647","type":"text","text":"[[00index/标签聚合/X-废弃/ch01|ch01]]","x":720,"y":3612,"width":240,"height":54,"color":"1"},
- {"id":"0ef8f1baaacc28b0","type":"text","text":"[[00index/标签聚合/X-废弃/ch02|ch02]]","x":720,"y":3690,"width":240,"height":54,"color":"1"},
- {"id":"709a94b7c8f84c03","type":"text","text":"[[00index/标签聚合/X-废弃/text00000|text00000]]","x":720,"y":3768,"width":240,"height":54,"color":"1"},
- {"id":"fe70e41ef53a73c7","type":"text","text":"[[00index/标签聚合/X-废弃/书籍观点缩进大纲|书籍观点缩进大纲]]","x":720,"y":3846,"width":240,"height":54,"color":"1"},
- {"id":"2a8afd3055c7e8ab","type":"text","text":"[[00index/标签聚合/X-废弃/刘瑜讲义的尺度效应案例|刘瑜讲义的尺度效应案例]]","x":720,"y":3924,"width":240,"height":54,"color":"1"},
- {"id":"1e0329600eb5d18d","type":"text","text":"[[00index/标签聚合/X-废弃/刘瑜谈韩国经验|刘瑜谈韩国经验]]","x":720,"y":4002,"width":240,"height":54,"color":"1"},
- {"id":"268e8afb59183ed9","type":"text","text":"[[00index/标签聚合/X-废弃/古代中国土地所有制演变|古代中国土地所有制演变]]","x":720,"y":4080,"width":240,"height":54,"color":"1"},
- {"id":"844941e806ef725f","type":"text","text":"[[00index/标签聚合/X-废弃/秦晖尺蠖效应|秦晖尺蠖效应]]","x":720,"y":4158,"width":240,"height":54,"color":"1"},
- {"id":"663824f137a02df2","type":"text","text":"[[00index/标签聚合/X-废弃/韩国书中主要观点|韩国书中主要观点]]","x":720,"y":4236,"width":240,"height":54,"color":"1"}
+ {"id":"63a9f0ea7bb98050","type":"text","text":"🗂 00index","x":0,"y":1998,"width":300,"height":60,"color":"#ffffff"},
+ {"id":"d465eb3356852cc5","type":"text","text":"📁 标签聚合","x":240,"y":1998,"width":300,"height":60,"color":"1"},
+ {"id":"7c4c6a66f489e8ed","type":"text","text":"📁 1.1修身","x":620,"y":476,"width":300,"height":60,"color":"1"},
+ {"id":"d9c874e1facd98e0","type":"text","text":"📁 身体与健康","x":1000,"y":439,"width":300,"height":60,"color":"1"},
+ {"id":"c5f9c902fd1b10de","type":"text","text":"[[00index/标签聚合/1.1修身/身体与健康/灰指甲|灰指甲]]","x":1380,"y":442,"width":300,"height":54,"color":"1"},
+ {"id":"677b952aff43ac2e","type":"text","text":"[[00index/标签聚合/1.1修身/锻炼|锻炼]]","x":1000,"y":519,"width":300,"height":54,"color":"1"},
+ {"id":"429a03021bbbc6b7","type":"text","text":"📁 1.2修心","x":620,"y":627,"width":300,"height":60,"color":"1"},
+ {"id":"30e7b617ee958a63","type":"text","text":"[[00index/标签聚合/1.2修心/原则|原则]]","x":1000,"y":593,"width":300,"height":54,"color":"1"},
+ {"id":"c0b127899f7e27f6","type":"text","text":"[[00index/标签聚合/1.2修心/整理自己|整理自己]]","x":1000,"y":667,"width":300,"height":54,"color":"1"},
+ {"id":"1866f746e29efd48","type":"text","text":"📁 1.3致知","x":620,"y":849,"width":300,"height":60,"color":"1"},
+ {"id":"40c9ffe267639532","type":"text","text":"[[00index/标签聚合/1.3致知/wiki|wiki]]","x":1000,"y":741,"width":300,"height":54,"color":"1"},
+ {"id":"d40b5e4e9d3628f7","type":"text","text":"[[00index/标签聚合/1.3致知/名词解释|名词解释]]","x":1000,"y":815,"width":300,"height":54,"color":"1"},
+ {"id":"690347c199d186ac","type":"text","text":"[[00index/标签聚合/1.3致知/怎么用|怎么用]]","x":1000,"y":889,"width":300,"height":54,"color":"1"},
+ {"id":"768039cd333e48eb","type":"text","text":"[[00index/标签聚合/1.3致知/背诵|背诵]]","x":1000,"y":963,"width":300,"height":54,"color":"1"},
+ {"id":"949b09d044a9ebe0","type":"text","text":"📁 1.5技能","x":620,"y":1074,"width":300,"height":60,"color":"1"},
+ {"id":"7a1369704d2e2297","type":"text","text":"[[00index/标签聚合/1.5技能/coding|coding]]","x":1000,"y":1037,"width":300,"height":54,"color":"1"},
+ {"id":"f67f584237697deb","type":"text","text":"📁 视觉设计相关","x":1000,"y":1111,"width":300,"height":60,"color":"1"},
+ {"id":"6c29fb988844d116","type":"text","text":"[[00index/标签聚合/1.5技能/视觉设计相关/UI|UI]]","x":1380,"y":1003,"width":300,"height":54,"color":"1"},
+ {"id":"d8d177fcf54825dd","type":"text","text":"[[00index/标签聚合/1.5技能/视觉设计相关/ui-kit|ui-kit]]","x":1380,"y":1077,"width":300,"height":54,"color":"1"},
+ {"id":"6bf77c203f8ad926","type":"text","text":"[[00index/标签聚合/1.5技能/视觉设计相关/ui-好设计|ui-好设计]]","x":1380,"y":1151,"width":300,"height":54,"color":"1"},
+ {"id":"fee2a3634b154085","type":"text","text":"[[00index/标签聚合/1.5技能/视觉设计相关/动画|动画]]","x":1380,"y":1225,"width":300,"height":54,"color":"1"},
+ {"id":"ba42c19e08bbcc3c","type":"text","text":"📁 1.6玩软件","x":620,"y":1561,"width":300,"height":60,"color":"1"},
+ {"id":"b5d4ef0b3506377c","type":"text","text":"[[00index/标签聚合/1.6玩软件/TitanVault|TitanVault]]","x":1000,"y":1191,"width":300,"height":54,"color":"1"},
+ {"id":"15cd37551266d11e","type":"text","text":"[[00index/标签聚合/1.6玩软件/excalidraw|excalidraw]]","x":1000,"y":1265,"width":300,"height":54,"color":"1"},
+ {"id":"65bed45a3159e342","type":"text","text":"[[00index/标签聚合/1.6玩软件/obsidian|obsidian]]","x":1000,"y":1339,"width":300,"height":54,"color":"1"},
+ {"id":"d7617f156b6c397c","type":"text","text":"[[00index/标签聚合/1.6玩软件/工作流|工作流]]","x":1000,"y":1413,"width":300,"height":54,"color":"1"},
+ {"id":"a1c50b58f3bb13e2","type":"text","text":"📁 操作系统","x":1000,"y":1487,"width":300,"height":60,"color":"1"},
+ {"id":"b5dd77af8fdef874","type":"text","text":"[[00index/标签聚合/1.6玩软件/操作系统/ios|ios]]","x":1380,"y":1490,"width":300,"height":54,"color":"1"},
+ {"id":"819320edcddb4bee","type":"text","text":"📁 玩转AI","x":1000,"y":1783,"width":300,"height":60,"color":"1"},
+ {"id":"a52d3c173bccc718","type":"text","text":"[[00index/标签聚合/1.6玩软件/玩转AI/MLX|MLX]]","x":1380,"y":1564,"width":300,"height":54,"color":"1"},
+ {"id":"e5fa8e66ac4d9830","type":"text","text":"[[00index/标签聚合/1.6玩软件/玩转AI/ai工具|ai工具]]","x":1380,"y":1638,"width":300,"height":54,"color":"1"},
+ {"id":"20c6d6ce289665d6","type":"text","text":"[[00index/标签聚合/1.6玩软件/玩转AI/chatbox|chatbox]]","x":1380,"y":1712,"width":300,"height":54,"color":"1"},
+ {"id":"60ae06f8fecd4450","type":"text","text":"[[00index/标签聚合/1.6玩软件/玩转AI/codex|codex]]","x":1380,"y":1786,"width":300,"height":54,"color":"1"},
+ {"id":"cb65885377212bb3","type":"text","text":"[[00index/标签聚合/1.6玩软件/玩转AI/hermes|hermes]]","x":1380,"y":1860,"width":300,"height":54,"color":"1"},
+ {"id":"23857661c3b1c937","type":"text","text":"[[00index/标签聚合/1.6玩软件/玩转AI/skill|skill]]","x":1380,"y":1934,"width":300,"height":54,"color":"1"},
+ {"id":"4b88df50c260b132","type":"text","text":"[[00index/标签聚合/1.6玩软件/玩转AI/配图|配图]]","x":1380,"y":2008,"width":300,"height":54,"color":"1"},
+ {"id":"653725a767bc9343","type":"text","text":"[[00index/标签聚合/1.6玩软件/软件研究|软件研究]]","x":1000,"y":1863,"width":300,"height":54,"color":"1"},
+ {"id":"c17087bb6fc0db72","type":"text","text":"[[00index/标签聚合/1.6玩软件/软件研究_怎么用|软件研究_怎么用]]","x":1000,"y":1937,"width":300,"height":54,"color":"1"},
+ {"id":"651e92b57a9c845a","type":"text","text":"📁 2.1工作","x":620,"y":2230,"width":300,"height":60,"color":"1"},
+ {"id":"e6c7da29521d7fbc","type":"text","text":"[[00index/标签聚合/2.1工作/26锁精环项目|26锁精环项目]]","x":1000,"y":2011,"width":300,"height":54,"color":"1"},
+ {"id":"ab7a8dc5c0577724","type":"text","text":"[[00index/标签聚合/2.1工作/bobo-轻量版|bobo-轻量版]]","x":1000,"y":2085,"width":300,"height":54,"color":"1"},
+ {"id":"352a3843afa0366c","type":"text","text":"[[00index/标签聚合/2.1工作/bobo|bobo]]","x":1000,"y":2159,"width":300,"height":54,"color":"1"},
+ {"id":"e5603eaf5bfdb863","type":"text","text":"[[00index/标签聚合/2.1工作/ideatodo|ideatodo]]","x":1000,"y":2233,"width":300,"height":54,"color":"1"},
+ {"id":"4629b30545162e2f","type":"text","text":"[[00index/标签聚合/2.1工作/senki|senki]]","x":1000,"y":2307,"width":300,"height":54,"color":"1"},
+ {"id":"99c89632ba0ba53c","type":"text","text":"[[00index/标签聚合/2.1工作/做事|做事]]","x":1000,"y":2381,"width":300,"height":54,"color":"1"},
+ {"id":"5531889f64629a4f","type":"text","text":"[[00index/标签聚合/2.1工作/运营|运营]]","x":1000,"y":2455,"width":300,"height":54,"color":"1"},
+ {"id":"69cc5fe8efadf5c3","type":"text","text":"📁 2.2作品-软件","x":620,"y":2563,"width":300,"height":60,"color":"1"},
+ {"id":"099d00387ab3e6d3","type":"text","text":"[[00index/标签聚合/2.2作品-软件/P-Aligner|P-Aligner]]","x":1000,"y":2529,"width":300,"height":54,"color":"1"},
+ {"id":"940da2bf9d96e73d","type":"text","text":"[[00index/标签聚合/2.2作品-软件/taglauncher|taglauncher]]","x":1000,"y":2603,"width":300,"height":54,"color":"1"},
+ {"id":"472ad4bce2919901","type":"text","text":"📁 3.3兴趣","x":620,"y":2748,"width":300,"height":60,"color":"1"},
+ {"id":"6eb768cc0d60ce26","type":"text","text":"[[00index/标签聚合/3.3兴趣/lyric|lyric]]","x":1000,"y":2677,"width":300,"height":54,"color":"1"},
+ {"id":"1028244783ef9e60","type":"text","text":"[[00index/标签聚合/3.3兴趣/做饭|做饭]]","x":1000,"y":2751,"width":300,"height":54,"color":"1"},
+ {"id":"9d3eec8d2d8c0a12","type":"text","text":"[[00index/标签聚合/3.3兴趣/电影|电影]]","x":1000,"y":2825,"width":300,"height":54,"color":"1"},
+ {"id":"b31b22aeaf04690b","type":"text","text":"📁 5.人","x":620,"y":2933,"width":300,"height":60,"color":"1"},
+ {"id":"1b0fdd5cc0911010","type":"text","text":"[[00index/标签聚合/5.人/谢大宝|谢大宝]]","x":1000,"y":2899,"width":300,"height":54,"color":"1"},
+ {"id":"abc9774d5d5da96b","type":"text","text":"[[00index/标签聚合/5.人/陈跃明|陈跃明]]","x":1000,"y":2973,"width":300,"height":54,"color":"1"},
+ {"id":"b80dcedb0d854a65","type":"text","text":"📁 6-物","x":620,"y":3044,"width":300,"height":60,"color":"1"},
+ {"id":"b23baa101b5995f5","type":"text","text":"[[00index/标签聚合/6-物/15PM|15PM]]","x":1000,"y":3047,"width":300,"height":54,"color":"1"},
+ {"id":"35aea8074f2a7517","type":"text","text":"📁 9.0其他","x":620,"y":3124,"width":300,"height":60,"color":"1"},
+ {"id":"c8319360b4b18b00","type":"text","text":"[[00index/标签聚合/9.0其他/地址|地址]]","x":1000,"y":3127,"width":300,"height":54,"color":"1"},
+ {"id":"e496692004e32d29","type":"text","text":"📁 X-废弃","x":620,"y":3521,"width":300,"height":60,"color":"1"},
+ {"id":"6edfab73566ad9b8","type":"text","text":"[[00index/标签聚合/X-废弃/1ThreadPoolBackgroundWorker|1ThreadPoolBackgroundWorker]]","x":1000,"y":3201,"width":300,"height":60,"color":"1"},
+ {"id":"dc8e79a40cadaf89","type":"text","text":"[[00index/标签聚合/X-废弃/5-8|5-8]]","x":1000,"y":3281,"width":300,"height":60,"color":"1"},
+ {"id":"a6ad5a450fc53a8c","type":"text","text":"[[00index/标签聚合/X-废弃/FIXME-a|FIXME-a]]","x":1000,"y":3361,"width":300,"height":60,"color":"1"},
+ {"id":"570e280f75e4c647","type":"text","text":"[[00index/标签聚合/X-废弃/ch01|ch01]]","x":1000,"y":3441,"width":300,"height":60,"color":"1"},
+ {"id":"0ef8f1baaacc28b0","type":"text","text":"[[00index/标签聚合/X-废弃/ch02|ch02]]","x":1000,"y":3521,"width":300,"height":60,"color":"1"},
+ {"id":"fe70e41ef53a73c7","type":"text","text":"[[00index/标签聚合/X-废弃/书籍观点缩进大纲|书籍观点缩进大纲]]","x":1000,"y":3601,"width":300,"height":60,"color":"1"},
+ {"id":"2a8afd3055c7e8ab","type":"text","text":"[[00index/标签聚合/X-废弃/刘瑜讲义的尺度效应案例|刘瑜讲义的尺度效应案例]]","x":1000,"y":3681,"width":300,"height":60,"color":"1"},
+ {"id":"1e0329600eb5d18d","type":"text","text":"[[00index/标签聚合/X-废弃/刘瑜谈韩国经验|刘瑜谈韩国经验]]","x":1000,"y":3761,"width":300,"height":60,"color":"1"},
+ {"id":"268e8afb59183ed9","type":"text","text":"[[00index/标签聚合/X-废弃/古代中国土地所有制演变|古代中国土地所有制演变]]","x":1000,"y":3841,"width":300,"height":60,"color":"1"}
],
"edges":[
{"id":"56d07a0c64d69107","fromNode":"d9c874e1facd98e0","fromSide":"right","toNode":"c5f9c902fd1b10de","toSide":"left","color":"1"},
@@ -88,9 +85,13 @@
{"id":"d44af03c1b86bcc1","fromNode":"1866f746e29efd48","fromSide":"right","toNode":"768039cd333e48eb","toSide":"left","color":"1"},
{"id":"678aac8e3505de4c","fromNode":"d465eb3356852cc5","fromSide":"right","toNode":"1866f746e29efd48","toSide":"left","color":"1"},
{"id":"4b96a69f66f94b5e","fromNode":"949b09d044a9ebe0","fromSide":"right","toNode":"7a1369704d2e2297","toSide":"left","color":"1"},
+ {"id":"d51068bc283559d6","fromNode":"f67f584237697deb","fromSide":"right","toNode":"6c29fb988844d116","toSide":"left","color":"1"},
+ {"id":"a6de96e25be81276","fromNode":"f67f584237697deb","fromSide":"right","toNode":"d8d177fcf54825dd","toSide":"left","color":"1"},
+ {"id":"be0fd8e650043263","fromNode":"f67f584237697deb","fromSide":"right","toNode":"6bf77c203f8ad926","toSide":"left","color":"1"},
{"id":"6f999641b4ee48ca","fromNode":"f67f584237697deb","fromSide":"right","toNode":"fee2a3634b154085","toSide":"left","color":"1"},
{"id":"6968ebf652ddda94","fromNode":"949b09d044a9ebe0","fromSide":"right","toNode":"f67f584237697deb","toSide":"left","color":"1"},
{"id":"69d563f10b4fec96","fromNode":"d465eb3356852cc5","fromSide":"right","toNode":"949b09d044a9ebe0","toSide":"left","color":"1"},
+ {"id":"de14990091b6e57b","fromNode":"ba42c19e08bbcc3c","fromSide":"right","toNode":"b5d4ef0b3506377c","toSide":"left","color":"1"},
{"id":"37e2e30d5e5f1a34","fromNode":"ba42c19e08bbcc3c","fromSide":"right","toNode":"15cd37551266d11e","toSide":"left","color":"1"},
{"id":"a5fedba64e8c5ceb","fromNode":"ba42c19e08bbcc3c","fromSide":"right","toNode":"65bed45a3159e342","toSide":"left","color":"1"},
{"id":"e790609aea9f1a89","fromNode":"ba42c19e08bbcc3c","fromSide":"right","toNode":"d7617f156b6c397c","toSide":"left","color":"1"},
@@ -134,18 +135,12 @@
{"id":"e4758bff6f08a057","fromNode":"e496692004e32d29","fromSide":"right","toNode":"a6ad5a450fc53a8c","toSide":"left","color":"1"},
{"id":"48630c99a6bee411","fromNode":"e496692004e32d29","fromSide":"right","toNode":"570e280f75e4c647","toSide":"left","color":"1"},
{"id":"5dad9565607e26b8","fromNode":"e496692004e32d29","fromSide":"right","toNode":"0ef8f1baaacc28b0","toSide":"left","color":"1"},
- {"id":"44a4188bfab2a666","fromNode":"e496692004e32d29","fromSide":"right","toNode":"709a94b7c8f84c03","toSide":"left","color":"1"},
{"id":"3c862c4241baf726","fromNode":"e496692004e32d29","fromSide":"right","toNode":"fe70e41ef53a73c7","toSide":"left","color":"1"},
{"id":"303212bc8ef93479","fromNode":"e496692004e32d29","fromSide":"right","toNode":"2a8afd3055c7e8ab","toSide":"left","color":"1"},
{"id":"80760ce4e3439004","fromNode":"e496692004e32d29","fromSide":"right","toNode":"1e0329600eb5d18d","toSide":"left","color":"1"},
{"id":"d1ca5ac2845c8e1c","fromNode":"e496692004e32d29","fromSide":"right","toNode":"268e8afb59183ed9","toSide":"left","color":"1"},
- {"id":"75dffcaf96beaee0","fromNode":"e496692004e32d29","fromSide":"right","toNode":"844941e806ef725f","toSide":"left","color":"1"},
- {"id":"68594118c14f74f8","fromNode":"e496692004e32d29","fromSide":"right","toNode":"663824f137a02df2","toSide":"left","color":"1"},
{"id":"e4f3090db3610668","fromNode":"d465eb3356852cc5","fromSide":"right","toNode":"e496692004e32d29","toSide":"left","color":"1"},
- {"id":"b2eedcdf2d5560bd","fromNode":"63a9f0ea7bb98050","fromSide":"right","toNode":"d465eb3356852cc5","toSide":"left","color":"1"},
- {"id":"d51068bc283559d6","fromNode":"f67f584237697deb","fromSide":"right","toNode":"6c29fb988844d116","toSide":"left","color":"1"},
- {"id":"a6de96e25be81276","fromNode":"f67f584237697deb","fromSide":"right","toNode":"d8d177fcf54825dd","toSide":"left","color":"1"},
- {"id":"be0fd8e650043263","fromNode":"f67f584237697deb","fromSide":"right","toNode":"6bf77c203f8ad926","toSide":"left","color":"1"},
- {"id":"de14990091b6e57b","fromNode":"ba42c19e08bbcc3c","fromSide":"right","toNode":"b5d4ef0b3506377c","toSide":"left","color":"1"}
- ]
+ {"id":"b2eedcdf2d5560bd","fromNode":"63a9f0ea7bb98050","fromSide":"right","toNode":"d465eb3356852cc5","toSide":"left","color":"1"}
+ ],
+ "mindmapCollapsed":["e496692004e32d29"]
}
\ No newline at end of file
diff --git a/00index/0inbox/book.md b/00index/0inbox/git.md
similarity index 98%
copy from 00index/0inbox/book.md
copy to 00index/0inbox/git.md
index c1e72e5..6d77e8b 100644
--- a/00index/0inbox/book.md
+++ b/00index/0inbox/git.md
@@ -7,7 +7,7 @@
2026-08-23: 2728
---
-# 🏷 #book(实时聚合页)
+# 🏷 #git(实时聚合页)
> 本页由 DataviewJS 实时计算,笔记有变动时自动刷新,无需任何后台服务。
@@ -27,7 +27,7 @@
// ============================================================
// ---------------- 配置区(通常只改这里) ----------------
-const targetTag = "book"; // 要聚合的标签(不带 #);多个标签用数组:["lyric", "歌词"]
+const targetTag = "git"; // 要聚合的标签(不带 #);多个标签用数组:["lyric", "歌词"]
const excludedPaths = ["X2.Archived", "00index", "X0.Clippings", "X1.Knomo", "02DS/02copilot", ".agents", ".claude", ".copilot", ".opencode", ".smart-env", ".workbuddy", ".trash"]; // 要排除的文件夹/路径前缀(vault 相对路径)
// 例如:["00index", "模板", "X1.Knomo"]
// 补充说明:
diff --git a/00index/0inbox/nested_tag.md b/00index/0inbox/nested_tag.md
deleted file mode 100644
index 817c6cc..0000000
--- a/00index/0inbox/nested_tag.md
+++ /dev/null
@@ -1,437 +0,0 @@
----
-tags:
- - tagfeed-live
-cssclasses:
- - tagfeed
-words:
- 2026-08-23: 2728
----
-
-# 🏷 #nested/tag(实时聚合页)
-
-> 本页由 DataviewJS 实时计算,笔记有变动时自动刷新,无需任何后台服务。
-
-```dataviewjs
-// ============================================================
-// tagfeed-live:Logseq 风格标签实时聚合(标准 DataviewJS 脚本)
-//
-// 用法:
-// 1. 新建一个笔记(例如 00index/标签聚合/标签名.md)
-// 2. 在笔记里插入一个 dataviewjs 代码块(三个反引号 + dataviewjs),
-// 把本脚本全部粘贴进去
-// 3. 只需要改下面「配置区」的两行:
-// - targetTag 换成你要聚合的标签(不带 #,中英文都可以;多个标签写成数组 ["a", "b"])
-// - excludedPaths 换成你想排除的文件夹
-// 4. 完成。以后任何笔记里出现/删除这个标签,本页自动刷新,
-// 不需要跑任何命令(Dataview 会在文件变动后自动重新渲染)。
-// ============================================================
-
-// ---------------- 配置区(通常只改这里) ----------------
-const targetTag = "nested/tag"; // 要聚合的标签(不带 #);多个标签用数组:["lyric", "歌词"]
-const excludedPaths = ["X2.Archived", "00index", "X0.Clippings", "X1.Knomo", "02DS/02copilot", ".agents", ".claude", ".copilot", ".opencode", ".smart-env", ".workbuddy", ".trash"]; // 要排除的文件夹/路径前缀(vault 相对路径)
- // 例如:["00index", "模板", "X1.Knomo"]
-// 补充说明:
-// - 所有隐藏文件/文件夹(. 开头:.agents/.claude/.copilot/.opencode/.smart-env/.workbuddy/.trash 等)与 X.Attachment 始终跳过
-// - tagfeed 系统页会自动跳过:静态聚合页(文件头含 <!-- tagfeed 标记)
-// 和实时页(frontmatter 带 tagfeed / tagfeed-live 标签),
-// 无需手动排除 00index
-// - 本脚本所在的当前笔记也会自动跳过
-// ---------------------------------------------------------
-
-try {
-const SKIP_DIRS = [".obsidian", ".git", ".trash", "X.Attachment"];
-const MARKER = "<!-- tagfeed";
-const WEEKDAYS = ["周一", "周二", "周三", "周四", "周五", "周六", "周日"];
-const PREV_CHARS = 500; // 预览字数上限:≤上限全文展示;>上限截断并附「查看全文」链接
-
-const esc = s => String(s).replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
-const TAG_LIST = Array.isArray(targetTag) ? targetTag : [targetTag];
-const TAG_RE = new RegExp("(?<![\\w\\u4e00-\\u9fff#])#(?:" + TAG_LIST.map(esc).join("|") + ")(?![\\w\\u4e00-\\u9fff/\\-])");
-const ANY_TAG = /(?<![\w#])#([\w\u4e00-\u9fff][\w\u4e00-\u9fff/\-]*)/g;
-const LIST_RE = /^(\s*)([-*+]|\d+[.)])(?:\s+(.*))?$/;
-const HEAD_RE = /^(#{1,6})\s+(.*)$/;
-const DATEH_RE = /\[\[(\d{4})-(\d{2})-(\d{2})\]\]/;
-const TIME_RE = /(\d{1,2}):(\d{2})(?::(\d{2}))?/;
-const FENCE_RE = /^\s*(`{3,}|~~~)/;
-const indentOf = ln => { // tab 按 2 空格计(与 Python 版一致)
- const expanded = ln.replace(/\t/g, " ");
- return expanded.length - expanded.replace(/^ +/, "").length;
-};
-
-function cleanMatch(ln) {
- let s = ln.replace(/`[^`\n]*`/g, "");
- s = s.replace(/%%[\s\S]*?%%/g, "");
- s = s.replace(/<!--[\s\S]*?-->/g, "");
- return s;
-}
-function vdate(y, m, d) {
- const dt = new Date(y, m - 1, d);
- return (dt.getFullYear() === y && dt.getMonth() === m - 1 && dt.getDate() === d) ? dt : null;
-}
-function dateFromName(name) { // 文件名含 YYYY-MM-DD,或日记命名 26.0805 -> 2026-08-05
- const m1 = name.match(/(\d{4})-(\d{2})-(\d{2})/);
- if (m1) {
- const d1 = vdate(+m1[1], +m1[2], +m1[3]);
- if (d1) return d1;
- }
- const m = name.match(/^(\d{2})\.(\d{2})(\d{2})(?:\D|$)/);
- return m ? vdate(2000 + +m[1], +m[2], +m[3]) : null;
-}
-function parseFrontmatter(text) {
- const lines = text.split("\n");
- if (!lines.length || lines[0].trim() !== "---") return { tags: new Set(), date: null, bodyStart: 0 };
- const n = Math.min(lines.length, 200);
- for (let i = 1; i < n; i++) {
- const t = lines[i].trim();
- if (t === "---" || t === "...") {
- const block = lines.slice(1, i).join("\n");
- const tags = new Set();
- const im = block.match(/^tags:\s*\[(.*?)\]/m);
- if (im) {
- for (const x0 of im[1].split(",")) {
- const x = x0.trim().replace(/^["']|["']$/g, "").replace(/^#/, "").trim();
- if (x) tags.add(x);
- }
- } else {
- const bm = block.match(/^tags:\s*\n((?:[ \t]+-[ \t]+.*\n?)+)/m);
- if (bm) for (const ln of bm[1].split("\n")) {
- const m2 = ln.match(/^\s*-\s*["']?([\w\u4e00-\u9fff/\-]+)["']?\s*$/);
- if (m2) tags.add(m2[1]);
- }
- }
- let fdate = null;
- for (const key of ["date", "created", "published"]) {
- const dm = block.match(new RegExp("^" + key + ":\\s*[\"']?(\\d{4})-(\\d{2})-(\\d{2})", "m"));
- if (dm) { fdate = vdate(+dm[1], +dm[2], +dm[3]); break; }
- }
- return { tags, date: fdate, bodyStart: i + 1 };
- }
- }
- return { tags: new Set(), date: null, bodyStart: 0 };
-}
-const stripTag = s => String(s).replace(TAG_RE, "").replace(/[ \t]{2,}/g, " ").trim();
-
-function scanText(text, note, relpath, fileDate, mtimeDate) {
- const fm = parseFrontmatter(text);
- if (fm.tags.has("tagfeed") || fm.tags.has("tagfeed-live")) return []; // 跳过 tagfeed 系统页(静态聚合页/实时页)
- const lines = text.split("\n");
- const entries = [], seen = new Set();
- let order = 0, inFence = false, inPct = false, inHtml = false;
- let curHead = null, curHeadRaw = null, curDateHead = null;
-
- const entryDate = () => curDateHead || fileDate || fm.date || mtimeDate;
- const anchorOf = () => (curDateHead ? null : curHeadRaw);
- const breadcrumb = () => {
- if (curHead && !DATEH_RE.test(curHead)) {
- const t = curHead.replace(ANY_TAG, "").replace(/\s+/g, " ").trim();
- if (t) return t;
- }
- return null;
- };
- function addBlock(keyIdx, block) {
- const key = relpath + "|" + keyIdx;
- if (seen.has(key) || !block.length) return;
- seen.add(key);
- const d = entryDate();
- if (!d) return;
- let t = null;
- const tm = block[0][1].slice(0, 30).match(TIME_RE);
- if (tm) t = [+tm[1], +tm[2], +(tm[3] || 0)];
- order++;
- entries.push({ date: d, time: t, order, relpath, note,
- breadcrumb: breadcrumb(), lines: block,
- pageLevel: false, notes: [note], anchor: anchorOf() });
- }
- function collectChildren(i, parentIndent) {
- const kids = [], raw = [];
- let j = i + 1;
- while (j < lines.length) {
- const ln = lines[j];
- if (ln.trim() === "") {
- let k = j + 1;
- while (k < lines.length && lines[k].trim() === "") k++;
- if (k < lines.length && LIST_RE.test(lines[k]) && indentOf(lines[k]) > parentIndent) { j = k; continue; }
- break;
- }
- const mi = ln.match(LIST_RE);
- if (mi && indentOf(ln) > parentIndent) {
- raw.push([indentOf(ln), (mi[3] || "").replace(/\s+$/, "")]);
- j++;
- } else break;
- }
- for (const [ind, txt] of raw) kids.push([Math.floor((ind - parentIndent) / 2), txt]);
- return kids;
- }
-
- for (let i = fm.bodyStart; i < lines.length; i++) {
- const ln = lines[i];
- if (FENCE_RE.test(ln)) { inFence = !inFence; continue; }
- if (inFence) continue;
- if (inPct) { if (ln.includes("%%")) inPct = false; continue; }
- if (inHtml) { if (ln.includes("-->")) inHtml = false; continue; }
- const stripped = ln.trim();
- if (stripped.startsWith("%%") && !stripped.slice(2).includes("%%")) { inPct = true; continue; }
- if (stripped.startsWith("<!--") && !stripped.includes("-->")) { inHtml = true; continue; }
-
- const hm = ln.match(HEAD_RE);
- if (hm) {
- const htext = hm[2];
- const dh = htext.match(DATEH_RE);
- if (dh) { curDateHead = vdate(+dh[1], +dh[2], +dh[3]); curHead = null; curHeadRaw = null; }
- else { curHead = htext; curHeadRaw = htext.trim(); }
- if (TAG_RE.test(cleanMatch(htext))) {
- const sec = [[0, "**" + stripTag(htext) + "**"]];
- let j = i + 1;
- const level = hm[1].length;
- let count = 0;
- while (j < lines.length && count < 300) {
- const sl = lines[j];
- const shm = sl.match(HEAD_RE);
- if (shm && shm[1].length <= level) break;
- if (FENCE_RE.test(sl)) break;
- if (sl.trim()) {
- const smi = sl.match(LIST_RE);
- if (smi) sec.push([1 + Math.floor(indentOf(sl) / 2), (smi[3] || "").replace(/\s+$/, "")]);
- else sec.push([1, sl.trim()]);
- count++;
- }
- j++;
- }
- addBlock(i, sec);
- }
- continue;
- }
-
- const clean = cleanMatch(ln);
- if (!TAG_RE.test(clean)) continue;
-
- const mi = ln.match(LIST_RE);
- if (mi) {
- const ind = indentOf(ln);
- const itemText = mi[3] || "";
- const cleaned = stripTag(itemText);
- const kids = collectChildren(i, ind);
- addBlock(i, cleaned ? [[0, cleaned], ...kids] : [[0, itemText.trim()], ...kids]);
- continue;
- }
-
- // 独立标签行:向下吸收 list items,或向上归属到紧邻的块
- if (ln.replace(ANY_TAG, "").trim() === "" || ln.replace(ANY_TAG, "").trim() === "%%") {
- let sec = [];
- let j = i + 1;
- while (j < lines.length) {
- const mj = lines[j].match(LIST_RE);
- if (mj) { sec.push([indentOf(lines[j]), (mj[3] || "").replace(/\s+$/, "")]); j++; }
- else break;
- }
- let keyIdx = i;
- if (!sec.length) {
- const up = [];
- let k = i - 1, bulletIdx = null;
- while (k >= fm.bodyStart) {
- const bk = lines[k];
- if (bk.trim() === "" || HEAD_RE.test(bk) || FENCE_RE.test(bk)) break;
- up.push(k);
- if (LIST_RE.test(bk)) { bulletIdx = k; break; }
- k--;
- }
- if (bulletIdx !== null) {
- const inds = [];
- for (let bl = bulletIdx; bl < i; bl++) {
- const mb = lines[bl].match(LIST_RE);
- if (mb) { inds.push(indentOf(lines[bl])); sec.push([indentOf(lines[bl]), (mb[3] || "").replace(/\s+$/, "")]); }
- else sec.push([(inds.length ? inds[inds.length - 1] + 2 : 0), lines[bl].trim()]);
- }
- keyIdx = bulletIdx;
- } else if (up.length) {
- sec = up.slice().reverse().map(k2 => [0, lines[k2].trim()]);
- } else {
- // 兜底:标签行紧挨在某个标题下方(中间只有空行)→ 捕获该标题下的整个小节
- let h = i - 1;
- while (h >= fm.bodyStart && lines[h].trim() === "") h--;
- const hmk = h >= fm.bodyStart ? lines[h].match(HEAD_RE) : null;
- if (hmk) {
- const level = hmk[1].length;
- const title = hmk[2].replace(ANY_TAG, "").replace(/\s+/g, " ").trim();
- const sec2 = title ? [[0, "**" + title + "**"]] : [];
- let j2 = i + 1, count2 = 0;
- while (j2 < lines.length && count2 < 300) {
- const sl = lines[j2];
- const shm = sl.match(HEAD_RE);
- if (shm && shm[1].length <= level) break;
- if (FENCE_RE.test(sl)) break;
- if (sl.trim()) {
- const smi = sl.match(LIST_RE);
- if (smi) sec2.push([1 + Math.floor(indentOf(sl) / 2), (smi[3] || "").replace(/\s+$/, "")]);
- else sec2.push([1, sl.trim().replace(/^>\s*/, "")]);
- count2++;
- }
- j2++;
- }
- if (sec2.length) sec = sec2;
- }
- }
- }
- if (sec.length) {
- const mi0 = Math.min(...sec.map(x => x[0]));
- addBlock(keyIdx, sec.map(([ind, t]) => [Math.floor((ind - mi0) / 2), t]));
- }
- continue;
- }
-
- // 普通段落带标签:吸收到空行为止
- const para = [stripTag(clean)];
- let j = i + 1;
- while (j < lines.length) {
- const nl = lines[j];
- if (nl.trim() === "" || HEAD_RE.test(nl) || LIST_RE.test(nl) || FENCE_RE.test(nl)) break;
- para.push(nl.trim());
- j++;
- }
- addBlock(i, [[0, para.filter(p => p).join(" ")]]);
- }
-
- // frontmatter 标签 → 页面级条目(附正文开头预览)
- if (TAG_LIST.some(t => fm.tags.has(t))) {
- const d = fm.date || fileDate || mtimeDate;
- if (d) {
- order++;
- // 收集正文文本至 PREV_CHARS:超过上限的长文在渲染时截断并附「查看全文」链接
- const prev = [];
- let prevTotal = 0, prevCount = 0;
- for (let k = fm.bodyStart; k < lines.length && prevCount < 200; k++) {
- const s = lines[k].trim();
- if (!s || s.startsWith("#") || s === "---") continue;
- const clean = s.replace(ANY_TAG, "");
- if (!clean) continue;
- prev.push(clean);
- prevTotal += clean.length;
- prevCount++;
- if (prevTotal > PREV_CHARS) break; // 超过上限:已足够判断,停止收集
- }
- const preview = prev.join(" ");
- entries.push({ date: d, time: null, order, relpath, note, breadcrumb: null,
- lines: preview ? [[0, preview]] : [],
- pageLevel: true, notes: [note], anchor: null });
- }
- }
- return entries;
-}
-
-// ---------------- 扫描整个 vault ----------------
-const currentPath = (dv.current() && dv.current().file) ? dv.current().file.path : null;
-let all = [];
-for (const f of app.vault.getMarkdownFiles()) {
- if (f.path === currentPath || f.path.endsWith(".excalidraw.md")) continue;
- if (SKIP_DIRS.some(d => f.path.startsWith(d + "/"))) continue;
- if (f.path.split("/").some(seg => seg.startsWith("."))) continue; // 排除所有隐藏文件/文件夹(.开头)
- if (excludedPaths.some(p => f.path === p || f.path.startsWith(p.endsWith("/") ? p : p + "/"))) continue;
- let text;
- try { text = await app.vault.cachedRead(f); } catch (e) { continue; }
- if (text.slice(0, 1000).includes(MARKER)) continue; // 跳过 tagfeed 生成的静态页
- all = all.concat(scanText(text, f.basename, f.path, dateFromName(f.basename), new Date(f.stat.mtime)));
-}
-
-// ---------------- 去重:同日期+同内容合并,来源并排 ----------------
-const timeCmp = (a, b) => (a[0] - b[0]) || (a[1] - b[1]) || (a[2] - b[2]);
-const mergedMap = new Map();
-for (const e of all) {
- const fp = e.date.getTime() + "|" + e.lines.map(x => x[0] + ":" + x[1]).join("\n");
- const m = mergedMap.get(fp);
- if (m) {
- if (!m.notes.includes(e.note)) m.notes.push(e.note);
- if (e.time && (!m.time || timeCmp(e.time, m.time) < 0)) m.time = e.time;
- // 若先扫到的来源没有面包屑/锚点,而后面的有,则采用后者(与扫描顺序无关,结果确定)
- if (!m.breadcrumb && e.breadcrumb) {
- m.breadcrumb = e.breadcrumb;
- m.anchor = e.anchor;
- m.relpath = e.relpath;
- m.note = e.note;
- }
- } else mergedMap.set(fp, e);
-}
-const entries = [...mergedMap.values()];
-
-// ---------------- 渲染:按日期倒序 ----------------
-const byDate = new Map();
-for (const e of entries) {
- const k = e.date.getTime();
- if (!byDate.has(k)) byDate.set(k, []);
- byDate.get(k).push(e);
-}
-const dates = [...byDate.keys()].sort((a, b) => b - a);
-const noteSet = new Set();
-for (const e of entries) for (const n of e.notes) noteSet.add(n);
-
-const out = [];
-out.push("> **" + entries.length + "** 个内容块 | **" + noteSet.size + "** 篇笔记 | 按日期倒序 | 随笔记变动自动刷新");
-
-const pad2 = x => String(x).padStart(2, "0");
-const fmtDate = d => d.getFullYear() + "-" + pad2(d.getMonth() + 1) + "-" + pad2(d.getDate());
-const invTime = t => t.map(x => pad2(99 - x)).join("");
-
-for (const dk of dates) {
- const d = new Date(dk);
- out.push("## 📅 " + fmtDate(d) + " · " + WEEKDAYS[d.getDay() === 0 ? 6 : d.getDay() - 1]);
- out.push("");
- const group = byDate.get(dk);
-
- const sub = new Map(); // (笔记+面包屑) -> entries
- for (const e of group) {
- const key = e.relpath + "|" + (e.breadcrumb || "");
- if (!sub.has(key)) sub.set(key, []);
- sub.get(key).push(e);
- }
- const subKey = ([key, es]) => {
- const times = es.filter(e => e.time).map(e => e.time);
- const best = times.length ? times.reduce((a, b) => (timeCmp(a, b) > 0 ? a : b)) : null;
- return (best ? "0" : "1") + (best ? invTime(best) : "000000") + key;
- };
-
- for (const [key, es] of [...sub.entries()].sort((a, b) => (subKey(a) < subKey(b) ? -1 : subKey(a) > subKey(b) ? 1 : 0))) {
- const noteList = [];
- for (const e of es) for (const n of e.notes) if (!noteList.includes(n)) noteList.push(n);
- noteList.sort();
- const crumb = es.map(e => e.breadcrumb).find(b => b) || null;
- out.push("- 🔗 " + noteList.map(n => "[[" + n + "]]").join(" · ") + (crumb ? " · " + crumb : ""));
- const sorted = es.slice().sort((a, b) => {
- const ka = (a.time ? "0" : "1") + (a.time ? invTime(a.time) : "") + String(a.order).padStart(6, "0");
- const kb = (b.time ? "0" : "1") + (b.time ? invTime(b.time) : "") + String(b.order).padStart(6, "0");
- return ka < kb ? -1 : ka > kb ? 1 : 0;
- });
- for (const e of sorted) {
- let bl = e.lines, truncated = false;
- const totalChars = bl.reduce((s, x) => s + x[1].length, 0);
- if (totalChars > PREV_CHARS) { // 只有超过上限才截断;上限内全文展示
- truncated = true;
- const shown = [];
- let chars = 0;
- for (const [lvl, txt] of bl) {
- if (chars + txt.length > PREV_CHARS) {
- if (PREV_CHARS - chars > 0) shown.push([lvl, txt.slice(0, PREV_CHARS - chars) + "…"]);
- break;
- }
- shown.push([lvl, txt]);
- chars += txt.length;
- }
- bl = shown;
- }
- for (const [lvl, txt] of bl) out.push("\t".repeat(lvl + 1) + "- " + txt);
- if (truncated) {
- const target = e.note + (e.anchor ? "#" + e.anchor : "");
- const label = e.pageLevel ? "页面级标签 · 查看全文" : "查看全文";
- out.push("\t- [[" + target + "|↗ " + label + "]]");
- }
- }
- out.push("");
- }
- out.push("");
-}
-dv.paragraph(out.join("\n"));
-
-} catch (err) {
- dv.paragraph("> ❌ tagfeed-live 执行失败:" + err.message);
- console.error("tagfeed-live:", err);
-}
-
-```
diff --git a/00index/0inbox/book.md "b/00index/0inbox/\344\273\273\346\204\217\346\240\207\347\255\276.md"
similarity index 98%
rename from 00index/0inbox/book.md
rename to "00index/0inbox/\344\273\273\346\204\217\346\240\207\347\255\276.md"
index c1e72e5..d5a9f45 100644
--- a/00index/0inbox/book.md
+++ "b/00index/0inbox/\344\273\273\346\204\217\346\240\207\347\255\276.md"
@@ -7,7 +7,7 @@
2026-08-23: 2728
---
-# 🏷 #book(实时聚合页)
+# 🏷 #任意标签(实时聚合页)
> 本页由 DataviewJS 实时计算,笔记有变动时自动刷新,无需任何后台服务。
@@ -27,7 +27,7 @@
// ============================================================
// ---------------- 配置区(通常只改这里) ----------------
-const targetTag = "book"; // 要聚合的标签(不带 #);多个标签用数组:["lyric", "歌词"]
+const targetTag = "任意标签"; // 要聚合的标签(不带 #);多个标签用数组:["lyric", "歌词"]
const excludedPaths = ["X2.Archived", "00index", "X0.Clippings", "X1.Knomo", "02DS/02copilot", ".agents", ".claude", ".copilot", ".opencode", ".smart-env", ".workbuddy", ".trash"]; // 要排除的文件夹/路径前缀(vault 相对路径)
// 例如:["00index", "模板", "X1.Knomo"]
// 补充说明:
diff --git a/00index/0inbox/book.md "b/00index/0inbox/\345\221\275\344\273\244\350\241\214.md"
similarity index 98%
copy from 00index/0inbox/book.md
copy to "00index/0inbox/\345\221\275\344\273\244\350\241\214.md"
index c1e72e5..c6e63cd 100644
--- a/00index/0inbox/book.md
+++ "b/00index/0inbox/\345\221\275\344\273\244\350\241\214.md"
@@ -5,9 +5,10 @@
- tagfeed
words:
2026-08-23: 2728
+ 2026-08-25: 2811
---
-# 🏷 #book(实时聚合页)
+# 🏷 #命令行(实时聚合页)
> 本页由 DataviewJS 实时计算,笔记有变动时自动刷新,无需任何后台服务。
@@ -27,7 +28,7 @@
// ============================================================
// ---------------- 配置区(通常只改这里) ----------------
-const targetTag = "book"; // 要聚合的标签(不带 #);多个标签用数组:["lyric", "歌词"]
+const targetTag = "命令行"; // 要聚合的标签(不带 #);多个标签用数组:["lyric", "歌词"]
const excludedPaths = ["X2.Archived", "00index", "X0.Clippings", "X1.Knomo", "02DS/02copilot", ".agents", ".claude", ".copilot", ".opencode", ".smart-env", ".workbuddy", ".trash"]; // 要排除的文件夹/路径前缀(vault 相对路径)
// 例如:["00index", "模板", "X1.Knomo"]
// 补充说明:
diff --git "a/00index/0inbox/\346\210\220\345\257\271\347\232\204\346\246\202\345\277\265.md" "b/00index/0inbox/\346\210\220\345\257\271\347\232\204\346\246\202\345\277\265.md"
index acca9f3..70e0499 100644
--- "a/00index/0inbox/\346\210\220\345\257\271\347\232\204\346\246\202\345\277\265.md"
+++ "b/00index/0inbox/\346\210\220\345\257\271\347\232\204\346\246\202\345\277\265.md"
@@ -6,6 +6,7 @@
words:
2026-08-23: 2728
2026-08-24: 2763
+ 2026-08-25: 2815
---
# 🏷 #成对的概念(实时聚合页)
diff --git "a/00index/\346\240\207\347\255\276\350\201\232\345\220\210/1.1\344\277\256\350\272\253/\350\272\253\344\275\223\344\270\216\345\201\245\345\272\267/\347\201\260\346\214\207\347\224\262.md" "b/00index/\346\240\207\347\255\276\350\201\232\345\220\210/1.1\344\277\256\350\272\253/\350\272\253\344\275\223\344\270\216\345\201\245\345\272\267/\347\201\260\346\214\207\347\224\262.md"
index 99ffb7d..f10122f 100644
--- "a/00index/\346\240\207\347\255\276\350\201\232\345\220\210/1.1\344\277\256\350\272\253/\350\272\253\344\275\223\344\270\216\345\201\245\345\272\267/\347\201\260\346\214\207\347\224\262.md"
+++ "b/00index/\346\240\207\347\255\276\350\201\232\345\220\210/1.1\344\277\256\350\272\253/\350\272\253\344\275\223\344\270\216\345\201\245\345\272\267/\347\201\260\346\214\207\347\224\262.md"
@@ -3,6 +3,8 @@
- tagfeed-live
cssclasses:
- tagfeed
+words:
+ 2026-08-24: 2633
---
# 🏷 #灰指甲(实时聚合页)
diff --git "a/00index/\346\240\207\347\255\276\350\201\232\345\220\210/1.3\350\207\264\347\237\245/wiki.md" "b/00index/\346\240\207\347\255\276\350\201\232\345\220\210/1.3\350\207\264\347\237\245/wiki.md"
index 3e1e9d4..af4d2ad 100644
--- "a/00index/\346\240\207\347\255\276\350\201\232\345\220\210/1.3\350\207\264\347\237\245/wiki.md"
+++ "b/00index/\346\240\207\347\255\276\350\201\232\345\220\210/1.3\350\207\264\347\237\245/wiki.md"
@@ -6,6 +6,7 @@
words:
2026-08-23: 2728
2026-08-24: 2755
+ 2026-08-25: 2807
---
# 🏷 #wiki(实时聚合页)
diff --git "a/00index/\346\240\207\347\255\276\350\201\232\345\220\210/1.6\347\216\251\350\275\257\344\273\266/obsidian.md" "b/00index/\346\240\207\347\255\276\350\201\232\345\220\210/1.6\347\216\251\350\275\257\344\273\266/obsidian.md"
index 88dfa7f..a736069 100644
--- "a/00index/\346\240\207\347\255\276\350\201\232\345\220\210/1.6\347\216\251\350\275\257\344\273\266/obsidian.md"
+++ "b/00index/\346\240\207\347\255\276\350\201\232\345\220\210/1.6\347\216\251\350\275\257\344\273\266/obsidian.md"
@@ -6,7 +6,7 @@
words:
2026-08-23: 2728
2026-08-24: 2755
- 2026-08-25: 2756
+ 2026-08-25: 2808
---
# 🏷 #obsidian(实时聚合页)
diff --git "a/00index/\346\240\207\347\255\276\350\201\232\345\220\210/1.6\347\216\251\350\275\257\344\273\266/\345\267\245\344\275\234\346\265\201.md" "b/00index/\346\240\207\347\255\276\350\201\232\345\220\210/1.6\347\216\251\350\275\257\344\273\266/\345\267\245\344\275\234\346\265\201.md"
index ae2184d..f06555c 100644
--- "a/00index/\346\240\207\347\255\276\350\201\232\345\220\210/1.6\347\216\251\350\275\257\344\273\266/\345\267\245\344\275\234\346\265\201.md"
+++ "b/00index/\346\240\207\347\255\276\350\201\232\345\220\210/1.6\347\216\251\350\275\257\344\273\266/\345\267\245\344\275\234\346\265\201.md"
@@ -5,6 +5,7 @@
- tagfeed
words:
2026-08-23: 2728
+ 2026-08-25: 2811
---
# 🏷 #工作流(实时聚合页)
diff --git "a/00index/\346\240\207\347\255\276\350\201\232\345\220\210/X-\345\272\237\345\274\203/1ThreadPoolBackgroundWorker.md" "b/00index/\346\240\207\347\255\276\350\201\232\345\220\210/X-\345\272\237\345\274\203/1ThreadPoolBackgroundWorker.md"
index 55b389a..18cc3f0 100644
--- "a/00index/\346\240\207\347\255\276\350\201\232\345\220\210/X-\345\272\237\345\274\203/1ThreadPoolBackgroundWorker.md"
+++ "b/00index/\346\240\207\347\255\276\350\201\232\345\220\210/X-\345\272\237\345\274\203/1ThreadPoolBackgroundWorker.md"
@@ -6,6 +6,7 @@
words:
2026-08-23: 2728
2026-08-24: 2755
+ 2026-08-25: 2807
---
# 🏷 #1ThreadPoolBackgroundWorker(实时聚合页)
diff --git "a/00index/\346\240\207\347\255\276\350\201\232\345\220\210/X-\345\272\237\345\274\203/text00000.md" "b/00index/\346\240\207\347\255\276\350\201\232\345\220\210/X-\345\272\237\345\274\203/text00000.md"
deleted file mode 100644
index 9d14329..0000000
--- "a/00index/\346\240\207\347\255\276\350\201\232\345\220\210/X-\345\272\237\345\274\203/text00000.md"
+++ /dev/null
@@ -1,438 +0,0 @@
----
-tags:
- - tagfeed-live
-cssclasses:
- - tagfeed
-words:
- 2026-08-23: 2728
- 2026-08-24: 2755
----
-
-# 🏷 #text00000(实时聚合页)
-
-> 本页由 DataviewJS 实时计算,笔记有变动时自动刷新,无需任何后台服务。
-
-```dataviewjs
-// ============================================================
-// tagfeed-live:Logseq 风格标签实时聚合(标准 DataviewJS 脚本)
-//
-// 用法:
-// 1. 新建一个笔记(例如 00index/标签聚合/标签名.md)
-// 2. 在笔记里插入一个 dataviewjs 代码块(三个反引号 + dataviewjs),
-// 把本脚本全部粘贴进去
-// 3. 只需要改下面「配置区」的两行:
-// - targetTag 换成你要聚合的标签(不带 #,中英文都可以;多个标签写成数组 ["a", "b"])
-// - excludedPaths 换成你想排除的文件夹
-// 4. 完成。以后任何笔记里出现/删除这个标签,本页自动刷新,
-// 不需要跑任何命令(Dataview 会在文件变动后自动重新渲染)。
-// ============================================================
-
-// ---------------- 配置区(通常只改这里) ----------------
-const targetTag = "text00000"; // 要聚合的标签(不带 #);多个标签用数组:["lyric", "歌词"]
-const excludedPaths = ["X2.Archived", "00index", "X0.Clippings", "X1.Knomo", "02DS/02copilot", ".agents", ".claude", ".copilot", ".opencode", ".smart-env", ".workbuddy", ".trash"]; // 要排除的文件夹/路径前缀(vault 相对路径)
- // 例如:["00index", "模板", "X1.Knomo"]
-// 补充说明:
-// - 所有隐藏文件/文件夹(. 开头:.agents/.claude/.copilot/.opencode/.smart-env/.workbuddy/.trash 等)与 X.Attachment 始终跳过
-// - tagfeed 系统页会自动跳过:静态聚合页(文件头含 <!-- tagfeed 标记)
-// 和实时页(frontmatter 带 tagfeed / tagfeed-live 标签),
-// 无需手动排除 00index
-// - 本脚本所在的当前笔记也会自动跳过
-// ---------------------------------------------------------
-
-try {
-const SKIP_DIRS = [".obsidian", ".git", ".trash", "X.Attachment"];
-const MARKER = "<!-- tagfeed";
-const WEEKDAYS = ["周一", "周二", "周三", "周四", "周五", "周六", "周日"];
-const PREV_CHARS = 500; // 预览字数上限:≤上限全文展示;>上限截断并附「查看全文」链接
-
-const esc = s => String(s).replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
-const TAG_LIST = Array.isArray(targetTag) ? targetTag : [targetTag];
-const TAG_RE = new RegExp("(?<![\\w\\u4e00-\\u9fff#])#(?:" + TAG_LIST.map(esc).join("|") + ")(?![\\w\\u4e00-\\u9fff/\\-])");
-const ANY_TAG = /(?<![\w#])#([\w\u4e00-\u9fff][\w\u4e00-\u9fff/\-]*)/g;
-const LIST_RE = /^(\s*)([-*+]|\d+[.)])(?:\s+(.*))?$/;
-const HEAD_RE = /^(#{1,6})\s+(.*)$/;
-const DATEH_RE = /\[\[(\d{4})-(\d{2})-(\d{2})\]\]/;
-const TIME_RE = /(\d{1,2}):(\d{2})(?::(\d{2}))?/;
-const FENCE_RE = /^\s*(`{3,}|~~~)/;
-const indentOf = ln => { // tab 按 2 空格计(与 Python 版一致)
- const expanded = ln.replace(/\t/g, " ");
- return expanded.length - expanded.replace(/^ +/, "").length;
-};
-
-function cleanMatch(ln) {
- let s = ln.replace(/`[^`\n]*`/g, "");
- s = s.replace(/%%[\s\S]*?%%/g, "");
- s = s.replace(/<!--[\s\S]*?-->/g, "");
- return s;
-}
-function vdate(y, m, d) {
- const dt = new Date(y, m - 1, d);
- return (dt.getFullYear() === y && dt.getMonth() === m - 1 && dt.getDate() === d) ? dt : null;
-}
-function dateFromName(name) { // 文件名含 YYYY-MM-DD,或日记命名 26.0805 -> 2026-08-05
- const m1 = name.match(/(\d{4})-(\d{2})-(\d{2})/);
- if (m1) {
- const d1 = vdate(+m1[1], +m1[2], +m1[3]);
- if (d1) return d1;
- }
- const m = name.match(/^(\d{2})\.(\d{2})(\d{2})(?:\D|$)/);
- return m ? vdate(2000 + +m[1], +m[2], +m[3]) : null;
-}
-function parseFrontmatter(text) {
- const lines = text.split("\n");
- if (!lines.length || lines[0].trim() !== "---") return { tags: new Set(), date: null, bodyStart: 0 };
- const n = Math.min(lines.length, 200);
- for (let i = 1; i < n; i++) {
- const t = lines[i].trim();
- if (t === "---" || t === "...") {
- const block = lines.slice(1, i).join("\n");
- const tags = new Set();
- const im = block.match(/^tags:\s*\[(.*?)\]/m);
- if (im) {
- for (const x0 of im[1].split(",")) {
- const x = x0.trim().replace(/^["']|["']$/g, "").replace(/^#/, "").trim();
- if (x) tags.add(x);
- }
- } else {
- const bm = block.match(/^tags:\s*\n((?:[ \t]+-[ \t]+.*\n?)+)/m);
- if (bm) for (const ln of bm[1].split("\n")) {
- const m2 = ln.match(/^\s*-\s*["']?([\w\u4e00-\u9fff/\-]+)["']?\s*$/);
- if (m2) tags.add(m2[1]);
- }
- }
- let fdate = null;
- for (const key of ["date", "created", "published"]) {
- const dm = block.match(new RegExp("^" + key + ":\\s*[\"']?(\\d{4})-(\\d{2})-(\\d{2})", "m"));
- if (dm) { fdate = vdate(+dm[1], +dm[2], +dm[3]); break; }
- }
- return { tags, date: fdate, bodyStart: i + 1 };
- }
- }
- return { tags: new Set(), date: null, bodyStart: 0 };
-}
-const stripTag = s => String(s).replace(TAG_RE, "").replace(/[ \t]{2,}/g, " ").trim();
-
-function scanText(text, note, relpath, fileDate, mtimeDate) {
- const fm = parseFrontmatter(text);
- if (fm.tags.has("tagfeed") || fm.tags.has("tagfeed-live")) return []; // 跳过 tagfeed 系统页(静态聚合页/实时页)
- const lines = text.split("\n");
- const entries = [], seen = new Set();
- let order = 0, inFence = false, inPct = false, inHtml = false;
- let curHead = null, curHeadRaw = null, curDateHead = null;
-
- const entryDate = () => curDateHead || fileDate || fm.date || mtimeDate;
- const anchorOf = () => (curDateHead ? null : curHeadRaw);
- const breadcrumb = () => {
- if (curHead && !DATEH_RE.test(curHead)) {
- const t = curHead.replace(ANY_TAG, "").replace(/\s+/g, " ").trim();
- if (t) return t;
- }
- return null;
- };
- function addBlock(keyIdx, block) {
- const key = relpath + "|" + keyIdx;
- if (seen.has(key) || !block.length) return;
- seen.add(key);
- const d = entryDate();
- if (!d) return;
- let t = null;
- const tm = block[0][1].slice(0, 30).match(TIME_RE);
- if (tm) t = [+tm[1], +tm[2], +(tm[3] || 0)];
- order++;
- entries.push({ date: d, time: t, order, relpath, note,
- breadcrumb: breadcrumb(), lines: block,
- pageLevel: false, notes: [note], anchor: anchorOf() });
- }
- function collectChildren(i, parentIndent) {
- const kids = [], raw = [];
- let j = i + 1;
- while (j < lines.length) {
- const ln = lines[j];
- if (ln.trim() === "") {
- let k = j + 1;
- while (k < lines.length && lines[k].trim() === "") k++;
- if (k < lines.length && LIST_RE.test(lines[k]) && indentOf(lines[k]) > parentIndent) { j = k; continue; }
- break;
- }
- const mi = ln.match(LIST_RE);
- if (mi && indentOf(ln) > parentIndent) {
- raw.push([indentOf(ln), (mi[3] || "").replace(/\s+$/, "")]);
- j++;
- } else break;
- }
- for (const [ind, txt] of raw) kids.push([Math.floor((ind - parentIndent) / 2), txt]);
- return kids;
- }
-
- for (let i = fm.bodyStart; i < lines.length; i++) {
- const ln = lines[i];
- if (FENCE_RE.test(ln)) { inFence = !inFence; continue; }
- if (inFence) continue;
- if (inPct) { if (ln.includes("%%")) inPct = false; continue; }
- if (inHtml) { if (ln.includes("-->")) inHtml = false; continue; }
- const stripped = ln.trim();
- if (stripped.startsWith("%%") && !stripped.slice(2).includes("%%")) { inPct = true; continue; }
- if (stripped.startsWith("<!--") && !stripped.includes("-->")) { inHtml = true; continue; }
-
- const hm = ln.match(HEAD_RE);
- if (hm) {
- const htext = hm[2];
- const dh = htext.match(DATEH_RE);
- if (dh) { curDateHead = vdate(+dh[1], +dh[2], +dh[3]); curHead = null; curHeadRaw = null; }
- else { curHead = htext; curHeadRaw = htext.trim(); }
- if (TAG_RE.test(cleanMatch(htext))) {
- const sec = [[0, "**" + stripTag(htext) + "**"]];
- let j = i + 1;
- const level = hm[1].length;
- let count = 0;
- while (j < lines.length && count < 300) {
- const sl = lines[j];
- const shm = sl.match(HEAD_RE);
- if (shm && shm[1].length <= level) break;
- if (FENCE_RE.test(sl)) break;
- if (sl.trim()) {
- const smi = sl.match(LIST_RE);
- if (smi) sec.push([1 + Math.floor(indentOf(sl) / 2), (smi[3] || "").replace(/\s+$/, "")]);
- else sec.push([1, sl.trim()]);
- count++;
- }
- j++;
- }
- addBlock(i, sec);
- }
- continue;
- }
-
- const clean = cleanMatch(ln);
- if (!TAG_RE.test(clean)) continue;
-
- const mi = ln.match(LIST_RE);
- if (mi) {
- const ind = indentOf(ln);
- const itemText = mi[3] || "";
- const cleaned = stripTag(itemText);
- const kids = collectChildren(i, ind);
- addBlock(i, cleaned ? [[0, cleaned], ...kids] : [[0, itemText.trim()], ...kids]);
- continue;
- }
-
- // 独立标签行:向下吸收 list items,或向上归属到紧邻的块
- if (ln.replace(ANY_TAG, "").trim() === "" || ln.replace(ANY_TAG, "").trim() === "%%") {
- let sec = [];
- let j = i + 1;
- while (j < lines.length) {
- const mj = lines[j].match(LIST_RE);
- if (mj) { sec.push([indentOf(lines[j]), (mj[3] || "").replace(/\s+$/, "")]); j++; }
- else break;
- }
- let keyIdx = i;
- if (!sec.length) {
- const up = [];
- let k = i - 1, bulletIdx = null;
- while (k >= fm.bodyStart) {
- const bk = lines[k];
- if (bk.trim() === "" || HEAD_RE.test(bk) || FENCE_RE.test(bk)) break;
- up.push(k);
- if (LIST_RE.test(bk)) { bulletIdx = k; break; }
- k--;
- }
- if (bulletIdx !== null) {
- const inds = [];
- for (let bl = bulletIdx; bl < i; bl++) {
- const mb = lines[bl].match(LIST_RE);
- if (mb) { inds.push(indentOf(lines[bl])); sec.push([indentOf(lines[bl]), (mb[3] || "").replace(/\s+$/, "")]); }
- else sec.push([(inds.length ? inds[inds.length - 1] + 2 : 0), lines[bl].trim()]);
- }
- keyIdx = bulletIdx;
- } else if (up.length) {
- sec = up.slice().reverse().map(k2 => [0, lines[k2].trim()]);
- } else {
- // 兜底:标签行紧挨在某个标题下方(中间只有空行)→ 捕获该标题下的整个小节
- let h = i - 1;
- while (h >= fm.bodyStart && lines[h].trim() === "") h--;
- const hmk = h >= fm.bodyStart ? lines[h].match(HEAD_RE) : null;
- if (hmk) {
- const level = hmk[1].length;
- const title = hmk[2].replace(ANY_TAG, "").replace(/\s+/g, " ").trim();
- const sec2 = title ? [[0, "**" + title + "**"]] : [];
- let j2 = i + 1, count2 = 0;
- while (j2 < lines.length && count2 < 300) {
- const sl = lines[j2];
- const shm = sl.match(HEAD_RE);
- if (shm && shm[1].length <= level) break;
- if (FENCE_RE.test(sl)) break;
- if (sl.trim()) {
- const smi = sl.match(LIST_RE);
- if (smi) sec2.push([1 + Math.floor(indentOf(sl) / 2), (smi[3] || "").replace(/\s+$/, "")]);
- else sec2.push([1, sl.trim().replace(/^>\s*/, "")]);
- count2++;
- }
- j2++;
- }
- if (sec2.length) sec = sec2;
- }
- }
- }
- if (sec.length) {
- const mi0 = Math.min(...sec.map(x => x[0]));
- addBlock(keyIdx, sec.map(([ind, t]) => [Math.floor((ind - mi0) / 2), t]));
- }
- continue;
- }
-
- // 普通段落带标签:吸收到空行为止
- const para = [stripTag(clean)];
- let j = i + 1;
- while (j < lines.length) {
- const nl = lines[j];
- if (nl.trim() === "" || HEAD_RE.test(nl) || LIST_RE.test(nl) || FENCE_RE.test(nl)) break;
- para.push(nl.trim());
- j++;
- }
- addBlock(i, [[0, para.filter(p => p).join(" ")]]);
- }
-
- // frontmatter 标签 → 页面级条目(附正文开头预览)
- if (TAG_LIST.some(t => fm.tags.has(t))) {
- const d = fm.date || fileDate || mtimeDate;
- if (d) {
- order++;
- // 收集正文文本至 PREV_CHARS:超过上限的长文在渲染时截断并附「查看全文」链接
- const prev = [];
- let prevTotal = 0, prevCount = 0;
- for (let k = fm.bodyStart; k < lines.length && prevCount < 200; k++) {
- const s = lines[k].trim();
- if (!s || s.startsWith("#") || s === "---") continue;
- const clean = s.replace(ANY_TAG, "");
- if (!clean) continue;
- prev.push(clean);
- prevTotal += clean.length;
- prevCount++;
- if (prevTotal > PREV_CHARS) break; // 超过上限:已足够判断,停止收集
- }
- const preview = prev.join(" ");
- entries.push({ date: d, time: null, order, relpath, note, breadcrumb: null,
- lines: preview ? [[0, preview]] : [],
- pageLevel: true, notes: [note], anchor: null });
- }
- }
- return entries;
-}
-
-// ---------------- 扫描整个 vault ----------------
-const currentPath = (dv.current() && dv.current().file) ? dv.current().file.path : null;
-let all = [];
-for (const f of app.vault.getMarkdownFiles()) {
- if (f.path === currentPath || f.path.endsWith(".excalidraw.md")) continue;
- if (SKIP_DIRS.some(d => f.path.startsWith(d + "/"))) continue;
- if (f.path.split("/").some(seg => seg.startsWith("."))) continue; // 排除所有隐藏文件/文件夹(.开头)
- if (excludedPaths.some(p => f.path === p || f.path.startsWith(p.endsWith("/") ? p : p + "/"))) continue;
- let text;
- try { text = await app.vault.cachedRead(f); } catch (e) { continue; }
- if (text.slice(0, 1000).includes(MARKER)) continue; // 跳过 tagfeed 生成的静态页
- all = all.concat(scanText(text, f.basename, f.path, dateFromName(f.basename), new Date(f.stat.mtime)));
-}
-
-// ---------------- 去重:同日期+同内容合并,来源并排 ----------------
-const timeCmp = (a, b) => (a[0] - b[0]) || (a[1] - b[1]) || (a[2] - b[2]);
-const mergedMap = new Map();
-for (const e of all) {
- const fp = e.date.getTime() + "|" + e.lines.map(x => x[0] + ":" + x[1]).join("\n");
- const m = mergedMap.get(fp);
- if (m) {
- if (!m.notes.includes(e.note)) m.notes.push(e.note);
- if (e.time && (!m.time || timeCmp(e.time, m.time) < 0)) m.time = e.time;
- // 若先扫到的来源没有面包屑/锚点,而后面的有,则采用后者(与扫描顺序无关,结果确定)
- if (!m.breadcrumb && e.breadcrumb) {
- m.breadcrumb = e.breadcrumb;
- m.anchor = e.anchor;
- m.relpath = e.relpath;
- m.note = e.note;
- }
- } else mergedMap.set(fp, e);
-}
-const entries = [...mergedMap.values()];
-
-// ---------------- 渲染:按日期倒序 ----------------
-const byDate = new Map();
-for (const e of entries) {
- const k = e.date.getTime();
- if (!byDate.has(k)) byDate.set(k, []);
- byDate.get(k).push(e);
-}
-const dates = [...byDate.keys()].sort((a, b) => b - a);
-const noteSet = new Set();
-for (const e of entries) for (const n of e.notes) noteSet.add(n);
-
-const out = [];
-out.push("> **" + entries.length + "** 个内容块 | **" + noteSet.size + "** 篇笔记 | 按日期倒序 | 随笔记变动自动刷新");
-
-const pad2 = x => String(x).padStart(2, "0");
-const fmtDate = d => d.getFullYear() + "-" + pad2(d.getMonth() + 1) + "-" + pad2(d.getDate());
-const invTime = t => t.map(x => pad2(99 - x)).join("");
-
-for (const dk of dates) {
- const d = new Date(dk);
- out.push("## 📅 " + fmtDate(d) + " · " + WEEKDAYS[d.getDay() === 0 ? 6 : d.getDay() - 1]);
- out.push("");
- const group = byDate.get(dk);
-
- const sub = new Map(); // (笔记+面包屑) -> entries
- for (const e of group) {
- const key = e.relpath + "|" + (e.breadcrumb || "");
- if (!sub.has(key)) sub.set(key, []);
- sub.get(key).push(e);
- }
- const subKey = ([key, es]) => {
- const times = es.filter(e => e.time).map(e => e.time);
- const best = times.length ? times.reduce((a, b) => (timeCmp(a, b) > 0 ? a : b)) : null;
- return (best ? "0" : "1") + (best ? invTime(best) : "000000") + key;
- };
-
- for (const [key, es] of [...sub.entries()].sort((a, b) => (subKey(a) < subKey(b) ? -1 : subKey(a) > subKey(b) ? 1 : 0))) {
- const noteList = [];
- for (const e of es) for (const n of e.notes) if (!noteList.includes(n)) noteList.push(n);
- noteList.sort();
- const crumb = es.map(e => e.breadcrumb).find(b => b) || null;
- out.push("- 🔗 " + noteList.map(n => "[[" + n + "]]").join(" · ") + (crumb ? " · " + crumb : ""));
- const sorted = es.slice().sort((a, b) => {
- const ka = (a.time ? "0" : "1") + (a.time ? invTime(a.time) : "") + String(a.order).padStart(6, "0");
- const kb = (b.time ? "0" : "1") + (b.time ? invTime(b.time) : "") + String(b.order).padStart(6, "0");
- return ka < kb ? -1 : ka > kb ? 1 : 0;
- });
- for (const e of sorted) {
- let bl = e.lines, truncated = false;
- const totalChars = bl.reduce((s, x) => s + x[1].length, 0);
- if (totalChars > PREV_CHARS) { // 只有超过上限才截断;上限内全文展示
- truncated = true;
- const shown = [];
- let chars = 0;
- for (const [lvl, txt] of bl) {
- if (chars + txt.length > PREV_CHARS) {
- if (PREV_CHARS - chars > 0) shown.push([lvl, txt.slice(0, PREV_CHARS - chars) + "…"]);
- break;
- }
- shown.push([lvl, txt]);
- chars += txt.length;
- }
- bl = shown;
- }
- for (const [lvl, txt] of bl) out.push("\t".repeat(lvl + 1) + "- " + txt);
- if (truncated) {
- const target = e.note + (e.anchor ? "#" + e.anchor : "");
- const label = e.pageLevel ? "页面级标签 · 查看全文" : "查看全文";
- out.push("\t- [[" + target + "|↗ " + label + "]]");
- }
- }
- out.push("");
- }
- out.push("");
-}
-dv.paragraph(out.join("\n"));
-
-} catch (err) {
- dv.paragraph("> ❌ tagfeed-live 执行失败:" + err.message);
- console.error("tagfeed-live:", err);
-}
-
-```
diff --git "a/00index/\346\240\207\347\255\276\350\201\232\345\220\210/X-\345\272\237\345\274\203/\347\247\246\346\231\226\345\260\272\350\240\226\346\225\210\345\272\224.md" "b/00index/\346\240\207\347\255\276\350\201\232\345\220\210/X-\345\272\237\345\274\203/\347\247\246\346\231\226\345\260\272\350\240\226\346\225\210\345\272\224.md"
deleted file mode 100644
index 25fbef8..0000000
--- "a/00index/\346\240\207\347\255\276\350\201\232\345\220\210/X-\345\272\237\345\274\203/\347\247\246\346\231\226\345\260\272\350\240\226\346\225\210\345\272\224.md"
+++ /dev/null
@@ -1,437 +0,0 @@
----
-tags:
- - tagfeed-live
-cssclasses:
- - tagfeed
-words:
- 2026-08-23: 2728
----
-
-# 🏷 #秦晖尺蠖效应(实时聚合页)
-
-> 本页由 DataviewJS 实时计算,笔记有变动时自动刷新,无需任何后台服务。
-
-```dataviewjs
-// ============================================================
-// tagfeed-live:Logseq 风格标签实时聚合(标准 DataviewJS 脚本)
-//
-// 用法:
-// 1. 新建一个笔记(例如 00index/标签聚合/标签名.md)
-// 2. 在笔记里插入一个 dataviewjs 代码块(三个反引号 + dataviewjs),
-// 把本脚本全部粘贴进去
-// 3. 只需要改下面「配置区」的两行:
-// - targetTag 换成你要聚合的标签(不带 #,中英文都可以;多个标签写成数组 ["a", "b"])
-// - excludedPaths 换成你想排除的文件夹
-// 4. 完成。以后任何笔记里出现/删除这个标签,本页自动刷新,
-// 不需要跑任何命令(Dataview 会在文件变动后自动重新渲染)。
-// ============================================================
-
-// ---------------- 配置区(通常只改这里) ----------------
-const targetTag = "秦晖尺蠖效应"; // 要聚合的标签(不带 #);多个标签用数组:["lyric", "歌词"]
-const excludedPaths = ["X2.Archived", "00index", "X0.Clippings", "X1.Knomo", "02DS/02copilot", ".agents", ".claude", ".copilot", ".opencode", ".smart-env", ".workbuddy", ".trash"]; // 要排除的文件夹/路径前缀(vault 相对路径)
- // 例如:["00index", "模板", "X1.Knomo"]
-// 补充说明:
-// - 所有隐藏文件/文件夹(. 开头:.agents/.claude/.copilot/.opencode/.smart-env/.workbuddy/.trash 等)与 X.Attachment 始终跳过
-// - tagfeed 系统页会自动跳过:静态聚合页(文件头含 <!-- tagfeed 标记)
-// 和实时页(frontmatter 带 tagfeed / tagfeed-live 标签),
-// 无需手动排除 00index
-// - 本脚本所在的当前笔记也会自动跳过
-// ---------------------------------------------------------
-
-try {
-const SKIP_DIRS = [".obsidian", ".git", ".trash", "X.Attachment"];
-const MARKER = "<!-- tagfeed";
-const WEEKDAYS = ["周一", "周二", "周三", "周四", "周五", "周六", "周日"];
-const PREV_CHARS = 500; // 预览字数上限:≤上限全文展示;>上限截断并附「查看全文」链接
-
-const esc = s => String(s).replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
-const TAG_LIST = Array.isArray(targetTag) ? targetTag : [targetTag];
-const TAG_RE = new RegExp("(?<![\\w\\u4e00-\\u9fff#])#(?:" + TAG_LIST.map(esc).join("|") + ")(?![\\w\\u4e00-\\u9fff/\\-])");
-const ANY_TAG = /(?<![\w#])#([\w\u4e00-\u9fff][\w\u4e00-\u9fff/\-]*)/g;
-const LIST_RE = /^(\s*)([-*+]|\d+[.)])(?:\s+(.*))?$/;
-const HEAD_RE = /^(#{1,6})\s+(.*)$/;
-const DATEH_RE = /\[\[(\d{4})-(\d{2})-(\d{2})\]\]/;
-const TIME_RE = /(\d{1,2}):(\d{2})(?::(\d{2}))?/;
-const FENCE_RE = /^\s*(`{3,}|~~~)/;
-const indentOf = ln => { // tab 按 2 空格计(与 Python 版一致)
- const expanded = ln.replace(/\t/g, " ");
- return expanded.length - expanded.replace(/^ +/, "").length;
-};
-
-function cleanMatch(ln) {
- let s = ln.replace(/`[^`\n]*`/g, "");
- s = s.replace(/%%[\s\S]*?%%/g, "");
- s = s.replace(/<!--[\s\S]*?-->/g, "");
- return s;
-}
-function vdate(y, m, d) {
- const dt = new Date(y, m - 1, d);
- return (dt.getFullYear() === y && dt.getMonth() === m - 1 && dt.getDate() === d) ? dt : null;
-}
-function dateFromName(name) { // 文件名含 YYYY-MM-DD,或日记命名 26.0805 -> 2026-08-05
- const m1 = name.match(/(\d{4})-(\d{2})-(\d{2})/);
- if (m1) {
- const d1 = vdate(+m1[1], +m1[2], +m1[3]);
- if (d1) return d1;
- }
- const m = name.match(/^(\d{2})\.(\d{2})(\d{2})(?:\D|$)/);
- return m ? vdate(2000 + +m[1], +m[2], +m[3]) : null;
-}
-function parseFrontmatter(text) {
- const lines = text.split("\n");
- if (!lines.length || lines[0].trim() !== "---") return { tags: new Set(), date: null, bodyStart: 0 };
- const n = Math.min(lines.length, 200);
- for (let i = 1; i < n; i++) {
- const t = lines[i].trim();
- if (t === "---" || t === "...") {
- const block = lines.slice(1, i).join("\n");
- const tags = new Set();
- const im = block.match(/^tags:\s*\[(.*?)\]/m);
- if (im) {
- for (const x0 of im[1].split(",")) {
- const x = x0.trim().replace(/^["']|["']$/g, "").replace(/^#/, "").trim();
- if (x) tags.add(x);
- }
- } else {
- const bm = block.match(/^tags:\s*\n((?:[ \t]+-[ \t]+.*\n?)+)/m);
- if (bm) for (const ln of bm[1].split("\n")) {
- const m2 = ln.match(/^\s*-\s*["']?([\w\u4e00-\u9fff/\-]+)["']?\s*$/);
- if (m2) tags.add(m2[1]);
- }
- }
- let fdate = null;
- for (const key of ["date", "created", "published"]) {
- const dm = block.match(new RegExp("^" + key + ":\\s*[\"']?(\\d{4})-(\\d{2})-(\\d{2})", "m"));
- if (dm) { fdate = vdate(+dm[1], +dm[2], +dm[3]); break; }
- }
- return { tags, date: fdate, bodyStart: i + 1 };
- }
- }
- return { tags: new Set(), date: null, bodyStart: 0 };
-}
-const stripTag = s => String(s).replace(TAG_RE, "").replace(/[ \t]{2,}/g, " ").trim();
-
-function scanText(text, note, relpath, fileDate, mtimeDate) {
- const fm = parseFrontmatter(text);
- if (fm.tags.has("tagfeed") || fm.tags.has("tagfeed-live")) return []; // 跳过 tagfeed 系统页(静态聚合页/实时页)
- const lines = text.split("\n");
- const entries = [], seen = new Set();
- let order = 0, inFence = false, inPct = false, inHtml = false;
- let curHead = null, curHeadRaw = null, curDateHead = null;
-
- const entryDate = () => curDateHead || fileDate || fm.date || mtimeDate;
- const anchorOf = () => (curDateHead ? null : curHeadRaw);
- const breadcrumb = () => {
- if (curHead && !DATEH_RE.test(curHead)) {
- const t = curHead.replace(ANY_TAG, "").replace(/\s+/g, " ").trim();
- if (t) return t;
- }
- return null;
- };
- function addBlock(keyIdx, block) {
- const key = relpath + "|" + keyIdx;
- if (seen.has(key) || !block.length) return;
- seen.add(key);
- const d = entryDate();
- if (!d) return;
- let t = null;
- const tm = block[0][1].slice(0, 30).match(TIME_RE);
- if (tm) t = [+tm[1], +tm[2], +(tm[3] || 0)];
- order++;
- entries.push({ date: d, time: t, order, relpath, note,
- breadcrumb: breadcrumb(), lines: block,
- pageLevel: false, notes: [note], anchor: anchorOf() });
- }
- function collectChildren(i, parentIndent) {
- const kids = [], raw = [];
- let j = i + 1;
- while (j < lines.length) {
- const ln = lines[j];
- if (ln.trim() === "") {
- let k = j + 1;
- while (k < lines.length && lines[k].trim() === "") k++;
- if (k < lines.length && LIST_RE.test(lines[k]) && indentOf(lines[k]) > parentIndent) { j = k; continue; }
- break;
- }
- const mi = ln.match(LIST_RE);
- if (mi && indentOf(ln) > parentIndent) {
- raw.push([indentOf(ln), (mi[3] || "").replace(/\s+$/, "")]);
- j++;
- } else break;
- }
- for (const [ind, txt] of raw) kids.push([Math.floor((ind - parentIndent) / 2), txt]);
- return kids;
- }
-
- for (let i = fm.bodyStart; i < lines.length; i++) {
- const ln = lines[i];
- if (FENCE_RE.test(ln)) { inFence = !inFence; continue; }
- if (inFence) continue;
- if (inPct) { if (ln.includes("%%")) inPct = false; continue; }
- if (inHtml) { if (ln.includes("-->")) inHtml = false; continue; }
- const stripped = ln.trim();
- if (stripped.startsWith("%%") && !stripped.slice(2).includes("%%")) { inPct = true; continue; }
- if (stripped.startsWith("<!--") && !stripped.includes("-->")) { inHtml = true; continue; }
-
- const hm = ln.match(HEAD_RE);
- if (hm) {
- const htext = hm[2];
- const dh = htext.match(DATEH_RE);
- if (dh) { curDateHead = vdate(+dh[1], +dh[2], +dh[3]); curHead = null; curHeadRaw = null; }
- else { curHead = htext; curHeadRaw = htext.trim(); }
- if (TAG_RE.test(cleanMatch(htext))) {
- const sec = [[0, "**" + stripTag(htext) + "**"]];
- let j = i + 1;
- const level = hm[1].length;
- let count = 0;
- while (j < lines.length && count < 300) {
- const sl = lines[j];
- const shm = sl.match(HEAD_RE);
- if (shm && shm[1].length <= level) break;
- if (FENCE_RE.test(sl)) break;
- if (sl.trim()) {
- const smi = sl.match(LIST_RE);
- if (smi) sec.push([1 + Math.floor(indentOf(sl) / 2), (smi[3] || "").replace(/\s+$/, "")]);
- else sec.push([1, sl.trim()]);
- count++;
- }
- j++;
- }
- addBlock(i, sec);
- }
- continue;
- }
-
- const clean = cleanMatch(ln);
- if (!TAG_RE.test(clean)) continue;
-
- const mi = ln.match(LIST_RE);
- if (mi) {
- const ind = indentOf(ln);
- const itemText = mi[3] || "";
- const cleaned = stripTag(itemText);
- const kids = collectChildren(i, ind);
- addBlock(i, cleaned ? [[0, cleaned], ...kids] : [[0, itemText.trim()], ...kids]);
- continue;
- }
-
- // 独立标签行:向下吸收 list items,或向上归属到紧邻的块
- if (ln.replace(ANY_TAG, "").trim() === "" || ln.replace(ANY_TAG, "").trim() === "%%") {
- let sec = [];
- let j = i + 1;
- while (j < lines.length) {
- const mj = lines[j].match(LIST_RE);
- if (mj) { sec.push([indentOf(lines[j]), (mj[3] || "").replace(/\s+$/, "")]); j++; }
- else break;
- }
- let keyIdx = i;
- if (!sec.length) {
- const up = [];
- let k = i - 1, bulletIdx = null;
- while (k >= fm.bodyStart) {
- const bk = lines[k];
- if (bk.trim() === "" || HEAD_RE.test(bk) || FENCE_RE.test(bk)) break;
- up.push(k);
- if (LIST_RE.test(bk)) { bulletIdx = k; break; }
- k--;
- }
- if (bulletIdx !== null) {
- const inds = [];
- for (let bl = bulletIdx; bl < i; bl++) {
- const mb = lines[bl].match(LIST_RE);
- if (mb) { inds.push(indentOf(lines[bl])); sec.push([indentOf(lines[bl]), (mb[3] || "").replace(/\s+$/, "")]); }
- else sec.push([(inds.length ? inds[inds.length - 1] + 2 : 0), lines[bl].trim()]);
- }
- keyIdx = bulletIdx;
- } else if (up.length) {
- sec = up.slice().reverse().map(k2 => [0, lines[k2].trim()]);
- } else {
- // 兜底:标签行紧挨在某个标题下方(中间只有空行)→ 捕获该标题下的整个小节
- let h = i - 1;
- while (h >= fm.bodyStart && lines[h].trim() === "") h--;
- const hmk = h >= fm.bodyStart ? lines[h].match(HEAD_RE) : null;
- if (hmk) {
- const level = hmk[1].length;
- const title = hmk[2].replace(ANY_TAG, "").replace(/\s+/g, " ").trim();
- const sec2 = title ? [[0, "**" + title + "**"]] : [];
- let j2 = i + 1, count2 = 0;
- while (j2 < lines.length && count2 < 300) {
- const sl = lines[j2];
- const shm = sl.match(HEAD_RE);
- if (shm && shm[1].length <= level) break;
- if (FENCE_RE.test(sl)) break;
- if (sl.trim()) {
- const smi = sl.match(LIST_RE);
- if (smi) sec2.push([1 + Math.floor(indentOf(sl) / 2), (smi[3] || "").replace(/\s+$/, "")]);
- else sec2.push([1, sl.trim().replace(/^>\s*/, "")]);
- count2++;
- }
- j2++;
- }
- if (sec2.length) sec = sec2;
- }
- }
- }
- if (sec.length) {
- const mi0 = Math.min(...sec.map(x => x[0]));
- addBlock(keyIdx, sec.map(([ind, t]) => [Math.floor((ind - mi0) / 2), t]));
- }
- continue;
- }
-
- // 普通段落带标签:吸收到空行为止
- const para = [stripTag(clean)];
- let j = i + 1;
- while (j < lines.length) {
- const nl = lines[j];
- if (nl.trim() === "" || HEAD_RE.test(nl) || LIST_RE.test(nl) || FENCE_RE.test(nl)) break;
- para.push(nl.trim());
- j++;
- }
- addBlock(i, [[0, para.filter(p => p).join(" ")]]);
- }
-
- // frontmatter 标签 → 页面级条目(附正文开头预览)
- if (TAG_LIST.some(t => fm.tags.has(t))) {
- const d = fm.date || fileDate || mtimeDate;
- if (d) {
- order++;
- // 收集正文文本至 PREV_CHARS:超过上限的长文在渲染时截断并附「查看全文」链接
- const prev = [];
- let prevTotal = 0, prevCount = 0;
- for (let k = fm.bodyStart; k < lines.length && prevCount < 200; k++) {
- const s = lines[k].trim();
- if (!s || s.startsWith("#") || s === "---") continue;
- const clean = s.replace(ANY_TAG, "");
- if (!clean) continue;
- prev.push(clean);
- prevTotal += clean.length;
- prevCount++;
- if (prevTotal > PREV_CHARS) break; // 超过上限:已足够判断,停止收集
- }
- const preview = prev.join(" ");
- entries.push({ date: d, time: null, order, relpath, note, breadcrumb: null,
- lines: preview ? [[0, preview]] : [],
- pageLevel: true, notes: [note], anchor: null });
- }
- }
- return entries;
-}
-
-// ---------------- 扫描整个 vault ----------------
-const currentPath = (dv.current() && dv.current().file) ? dv.current().file.path : null;
-let all = [];
-for (const f of app.vault.getMarkdownFiles()) {
- if (f.path === currentPath || f.path.endsWith(".excalidraw.md")) continue;
- if (SKIP_DIRS.some(d => f.path.startsWith(d + "/"))) continue;
- if (f.path.split("/").some(seg => seg.startsWith("."))) continue; // 排除所有隐藏文件/文件夹(.开头)
- if (excludedPaths.some(p => f.path === p || f.path.startsWith(p.endsWith("/") ? p : p + "/"))) continue;
- let text;
- try { text = await app.vault.cachedRead(f); } catch (e) { continue; }
- if (text.slice(0, 1000).includes(MARKER)) continue; // 跳过 tagfeed 生成的静态页
- all = all.concat(scanText(text, f.basename, f.path, dateFromName(f.basename), new Date(f.stat.mtime)));
-}
-
-// ---------------- 去重:同日期+同内容合并,来源并排 ----------------
-const timeCmp = (a, b) => (a[0] - b[0]) || (a[1] - b[1]) || (a[2] - b[2]);
-const mergedMap = new Map();
-for (const e of all) {
- const fp = e.date.getTime() + "|" + e.lines.map(x => x[0] + ":" + x[1]).join("\n");
- const m = mergedMap.get(fp);
- if (m) {
- if (!m.notes.includes(e.note)) m.notes.push(e.note);
- if (e.time && (!m.time || timeCmp(e.time, m.time) < 0)) m.time = e.time;
- // 若先扫到的来源没有面包屑/锚点,而后面的有,则采用后者(与扫描顺序无关,结果确定)
- if (!m.breadcrumb && e.breadcrumb) {
- m.breadcrumb = e.breadcrumb;
- m.anchor = e.anchor;
- m.relpath = e.relpath;
- m.note = e.note;
- }
- } else mergedMap.set(fp, e);
-}
-const entries = [...mergedMap.values()];
-
-// ---------------- 渲染:按日期倒序 ----------------
-const byDate = new Map();
-for (const e of entries) {
- const k = e.date.getTime();
- if (!byDate.has(k)) byDate.set(k, []);
- byDate.get(k).push(e);
-}
-const dates = [...byDate.keys()].sort((a, b) => b - a);
-const noteSet = new Set();
-for (const e of entries) for (const n of e.notes) noteSet.add(n);
-
-const out = [];
-out.push("> **" + entries.length + "** 个内容块 | **" + noteSet.size + "** 篇笔记 | 按日期倒序 | 随笔记变动自动刷新");
-
-const pad2 = x => String(x).padStart(2, "0");
-const fmtDate = d => d.getFullYear() + "-" + pad2(d.getMonth() + 1) + "-" + pad2(d.getDate());
-const invTime = t => t.map(x => pad2(99 - x)).join("");
-
-for (const dk of dates) {
- const d = new Date(dk);
- out.push("## 📅 " + fmtDate(d) + " · " + WEEKDAYS[d.getDay() === 0 ? 6 : d.getDay() - 1]);
- out.push("");
- const group = byDate.get(dk);
-
- const sub = new Map(); // (笔记+面包屑) -> entries
- for (const e of group) {
- const key = e.relpath + "|" + (e.breadcrumb || "");
- if (!sub.has(key)) sub.set(key, []);
- sub.get(key).push(e);
- }
- const subKey = ([key, es]) => {
- const times = es.filter(e => e.time).map(e => e.time);
- const best = times.length ? times.reduce((a, b) => (timeCmp(a, b) > 0 ? a : b)) : null;
- return (best ? "0" : "1") + (best ? invTime(best) : "000000") + key;
- };
-
- for (const [key, es] of [...sub.entries()].sort((a, b) => (subKey(a) < subKey(b) ? -1 : subKey(a) > subKey(b) ? 1 : 0))) {
- const noteList = [];
- for (const e of es) for (const n of e.notes) if (!noteList.includes(n)) noteList.push(n);
- noteList.sort();
- const crumb = es.map(e => e.breadcrumb).find(b => b) || null;
- out.push("- 🔗 " + noteList.map(n => "[[" + n + "]]").join(" · ") + (crumb ? " · " + crumb : ""));
- const sorted = es.slice().sort((a, b) => {
- const ka = (a.time ? "0" : "1") + (a.time ? invTime(a.time) : "") + String(a.order).padStart(6, "0");
- const kb = (b.time ? "0" : "1") + (b.time ? invTime(b.time) : "") + String(b.order).padStart(6, "0");
- return ka < kb ? -1 : ka > kb ? 1 : 0;
- });
- for (const e of sorted) {
- let bl = e.lines, truncated = false;
- const totalChars = bl.reduce((s, x) => s + x[1].length, 0);
- if (totalChars > PREV_CHARS) { // 只有超过上限才截断;上限内全文展示
- truncated = true;
- const shown = [];
- let chars = 0;
- for (const [lvl, txt] of bl) {
- if (chars + txt.length > PREV_CHARS) {
- if (PREV_CHARS - chars > 0) shown.push([lvl, txt.slice(0, PREV_CHARS - chars) + "…"]);
- break;
- }
- shown.push([lvl, txt]);
- chars += txt.length;
- }
- bl = shown;
- }
- for (const [lvl, txt] of bl) out.push("\t".repeat(lvl + 1) + "- " + txt);
- if (truncated) {
- const target = e.note + (e.anchor ? "#" + e.anchor : "");
- const label = e.pageLevel ? "页面级标签 · 查看全文" : "查看全文";
- out.push("\t- [[" + target + "|↗ " + label + "]]");
- }
- }
- out.push("");
- }
- out.push("");
-}
-dv.paragraph(out.join("\n"));
-
-} catch (err) {
- dv.paragraph("> ❌ tagfeed-live 执行失败:" + err.message);
- console.error("tagfeed-live:", err);
-}
-
-```
diff --git "a/00index/\346\240\207\347\255\276\350\201\232\345\220\210/X-\345\272\237\345\274\203/\351\237\251\345\233\275\344\271\246\344\270\255\344\270\273\350\246\201\350\247\202\347\202\271.md" "b/00index/\346\240\207\347\255\276\350\201\232\345\220\210/X-\345\272\237\345\274\203/\351\237\251\345\233\275\344\271\246\344\270\255\344\270\273\350\246\201\350\247\202\347\202\271.md"
deleted file mode 100644
index dac076a..0000000
--- "a/00index/\346\240\207\347\255\276\350\201\232\345\220\210/X-\345\272\237\345\274\203/\351\237\251\345\233\275\344\271\246\344\270\255\344\270\273\350\246\201\350\247\202\347\202\271.md"
+++ /dev/null
@@ -1,437 +0,0 @@
----
-tags:
- - tagfeed-live
-cssclasses:
- - tagfeed
-words:
- 2026-08-23: 2728
----
-
-# 🏷 #韩国书中主要观点(实时聚合页)
-
-> 本页由 DataviewJS 实时计算,笔记有变动时自动刷新,无需任何后台服务。
-
-```dataviewjs
-// ============================================================
-// tagfeed-live:Logseq 风格标签实时聚合(标准 DataviewJS 脚本)
-//
-// 用法:
-// 1. 新建一个笔记(例如 00index/标签聚合/标签名.md)
-// 2. 在笔记里插入一个 dataviewjs 代码块(三个反引号 + dataviewjs),
-// 把本脚本全部粘贴进去
-// 3. 只需要改下面「配置区」的两行:
-// - targetTag 换成你要聚合的标签(不带 #,中英文都可以;多个标签写成数组 ["a", "b"])
-// - excludedPaths 换成你想排除的文件夹
-// 4. 完成。以后任何笔记里出现/删除这个标签,本页自动刷新,
-// 不需要跑任何命令(Dataview 会在文件变动后自动重新渲染)。
-// ============================================================
-
-// ---------------- 配置区(通常只改这里) ----------------
-const targetTag = "韩国书中主要观点"; // 要聚合的标签(不带 #);多个标签用数组:["lyric", "歌词"]
-const excludedPaths = ["X2.Archived", "00index", "X0.Clippings", "X1.Knomo", "02DS/02copilot", ".agents", ".claude", ".copilot", ".opencode", ".smart-env", ".workbuddy", ".trash"]; // 要排除的文件夹/路径前缀(vault 相对路径)
- // 例如:["00index", "模板", "X1.Knomo"]
-// 补充说明:
-// - 所有隐藏文件/文件夹(. 开头:.agents/.claude/.copilot/.opencode/.smart-env/.workbuddy/.trash 等)与 X.Attachment 始终跳过
-// - tagfeed 系统页会自动跳过:静态聚合页(文件头含 <!-- tagfeed 标记)
-// 和实时页(frontmatter 带 tagfeed / tagfeed-live 标签),
-// 无需手动排除 00index
-// - 本脚本所在的当前笔记也会自动跳过
-// ---------------------------------------------------------
-
-try {
-const SKIP_DIRS = [".obsidian", ".git", ".trash", "X.Attachment"];
-const MARKER = "<!-- tagfeed";
-const WEEKDAYS = ["周一", "周二", "周三", "周四", "周五", "周六", "周日"];
-const PREV_CHARS = 500; // 预览字数上限:≤上限全文展示;>上限截断并附「查看全文」链接
-
-const esc = s => String(s).replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
-const TAG_LIST = Array.isArray(targetTag) ? targetTag : [targetTag];
-const TAG_RE = new RegExp("(?<![\\w\\u4e00-\\u9fff#])#(?:" + TAG_LIST.map(esc).join("|") + ")(?![\\w\\u4e00-\\u9fff/\\-])");
-const ANY_TAG = /(?<![\w#])#([\w\u4e00-\u9fff][\w\u4e00-\u9fff/\-]*)/g;
-const LIST_RE = /^(\s*)([-*+]|\d+[.)])(?:\s+(.*))?$/;
-const HEAD_RE = /^(#{1,6})\s+(.*)$/;
-const DATEH_RE = /\[\[(\d{4})-(\d{2})-(\d{2})\]\]/;
-const TIME_RE = /(\d{1,2}):(\d{2})(?::(\d{2}))?/;
-const FENCE_RE = /^\s*(`{3,}|~~~)/;
-const indentOf = ln => { // tab 按 2 空格计(与 Python 版一致)
- const expanded = ln.replace(/\t/g, " ");
- return expanded.length - expanded.replace(/^ +/, "").length;
-};
-
-function cleanMatch(ln) {
- let s = ln.replace(/`[^`\n]*`/g, "");
- s = s.replace(/%%[\s\S]*?%%/g, "");
- s = s.replace(/<!--[\s\S]*?-->/g, "");
- return s;
-}
-function vdate(y, m, d) {
- const dt = new Date(y, m - 1, d);
- return (dt.getFullYear() === y && dt.getMonth() === m - 1 && dt.getDate() === d) ? dt : null;
-}
-function dateFromName(name) { // 文件名含 YYYY-MM-DD,或日记命名 26.0805 -> 2026-08-05
- const m1 = name.match(/(\d{4})-(\d{2})-(\d{2})/);
- if (m1) {
- const d1 = vdate(+m1[1], +m1[2], +m1[3]);
- if (d1) return d1;
- }
- const m = name.match(/^(\d{2})\.(\d{2})(\d{2})(?:\D|$)/);
- return m ? vdate(2000 + +m[1], +m[2], +m[3]) : null;
-}
-function parseFrontmatter(text) {
- const lines = text.split("\n");
- if (!lines.length || lines[0].trim() !== "---") return { tags: new Set(), date: null, bodyStart: 0 };
- const n = Math.min(lines.length, 200);
- for (let i = 1; i < n; i++) {
- const t = lines[i].trim();
- if (t === "---" || t === "...") {
- const block = lines.slice(1, i).join("\n");
- const tags = new Set();
- const im = block.match(/^tags:\s*\[(.*?)\]/m);
- if (im) {
- for (const x0 of im[1].split(",")) {
- const x = x0.trim().replace(/^["']|["']$/g, "").replace(/^#/, "").trim();
- if (x) tags.add(x);
- }
- } else {
- const bm = block.match(/^tags:\s*\n((?:[ \t]+-[ \t]+.*\n?)+)/m);
- if (bm) for (const ln of bm[1].split("\n")) {
- const m2 = ln.match(/^\s*-\s*["']?([\w\u4e00-\u9fff/\-]+)["']?\s*$/);
- if (m2) tags.add(m2[1]);
- }
- }
- let fdate = null;
- for (const key of ["date", "created", "published"]) {
- const dm = block.match(new RegExp("^" + key + ":\\s*[\"']?(\\d{4})-(\\d{2})-(\\d{2})", "m"));
- if (dm) { fdate = vdate(+dm[1], +dm[2], +dm[3]); break; }
- }
- return { tags, date: fdate, bodyStart: i + 1 };
- }
- }
- return { tags: new Set(), date: null, bodyStart: 0 };
-}
-const stripTag = s => String(s).replace(TAG_RE, "").replace(/[ \t]{2,}/g, " ").trim();
-
-function scanText(text, note, relpath, fileDate, mtimeDate) {
- const fm = parseFrontmatter(text);
- if (fm.tags.has("tagfeed") || fm.tags.has("tagfeed-live")) return []; // 跳过 tagfeed 系统页(静态聚合页/实时页)
- const lines = text.split("\n");
- const entries = [], seen = new Set();
- let order = 0, inFence = false, inPct = false, inHtml = false;
- let curHead = null, curHeadRaw = null, curDateHead = null;
-
- const entryDate = () => curDateHead || fileDate || fm.date || mtimeDate;
- const anchorOf = () => (curDateHead ? null : curHeadRaw);
- const breadcrumb = () => {
- if (curHead && !DATEH_RE.test(curHead)) {
- const t = curHead.replace(ANY_TAG, "").replace(/\s+/g, " ").trim();
- if (t) return t;
- }
- return null;
- };
- function addBlock(keyIdx, block) {
- const key = relpath + "|" + keyIdx;
- if (seen.has(key) || !block.length) return;
- seen.add(key);
- const d = entryDate();
- if (!d) return;
- let t = null;
- const tm = block[0][1].slice(0, 30).match(TIME_RE);
- if (tm) t = [+tm[1], +tm[2], +(tm[3] || 0)];
- order++;
- entries.push({ date: d, time: t, order, relpath, note,
- breadcrumb: breadcrumb(), lines: block,
- pageLevel: false, notes: [note], anchor: anchorOf() });
- }
- function collectChildren(i, parentIndent) {
- const kids = [], raw = [];
- let j = i + 1;
- while (j < lines.length) {
- const ln = lines[j];
- if (ln.trim() === "") {
- let k = j + 1;
- while (k < lines.length && lines[k].trim() === "") k++;
- if (k < lines.length && LIST_RE.test(lines[k]) && indentOf(lines[k]) > parentIndent) { j = k; continue; }
- break;
- }
- const mi = ln.match(LIST_RE);
- if (mi && indentOf(ln) > parentIndent) {
- raw.push([indentOf(ln), (mi[3] || "").replace(/\s+$/, "")]);
- j++;
- } else break;
- }
- for (const [ind, txt] of raw) kids.push([Math.floor((ind - parentIndent) / 2), txt]);
- return kids;
- }
-
- for (let i = fm.bodyStart; i < lines.length; i++) {
- const ln = lines[i];
- if (FENCE_RE.test(ln)) { inFence = !inFence; continue; }
- if (inFence) continue;
- if (inPct) { if (ln.includes("%%")) inPct = false; continue; }
- if (inHtml) { if (ln.includes("-->")) inHtml = false; continue; }
- const stripped = ln.trim();
- if (stripped.startsWith("%%") && !stripped.slice(2).includes("%%")) { inPct = true; continue; }
- if (stripped.startsWith("<!--") && !stripped.includes("-->")) { inHtml = true; continue; }
-
- const hm = ln.match(HEAD_RE);
- if (hm) {
- const htext = hm[2];
- const dh = htext.match(DATEH_RE);
- if (dh) { curDateHead = vdate(+dh[1], +dh[2], +dh[3]); curHead = null; curHeadRaw = null; }
- else { curHead = htext; curHeadRaw = htext.trim(); }
- if (TAG_RE.test(cleanMatch(htext))) {
- const sec = [[0, "**" + stripTag(htext) + "**"]];
- let j = i + 1;
- const level = hm[1].length;
- let count = 0;
- while (j < lines.length && count < 300) {
- const sl = lines[j];
- const shm = sl.match(HEAD_RE);
- if (shm && shm[1].length <= level) break;
- if (FENCE_RE.test(sl)) break;
- if (sl.trim()) {
- const smi = sl.match(LIST_RE);
- if (smi) sec.push([1 + Math.floor(indentOf(sl) / 2), (smi[3] || "").replace(/\s+$/, "")]);
- else sec.push([1, sl.trim()]);
- count++;
- }
- j++;
- }
- addBlock(i, sec);
- }
- continue;
- }
-
- const clean = cleanMatch(ln);
- if (!TAG_RE.test(clean)) continue;
-
- const mi = ln.match(LIST_RE);
- if (mi) {
- const ind = indentOf(ln);
- const itemText = mi[3] || "";
- const cleaned = stripTag(itemText);
- const kids = collectChildren(i, ind);
- addBlock(i, cleaned ? [[0, cleaned], ...kids] : [[0, itemText.trim()], ...kids]);
- continue;
- }
-
- // 独立标签行:向下吸收 list items,或向上归属到紧邻的块
- if (ln.replace(ANY_TAG, "").trim() === "" || ln.replace(ANY_TAG, "").trim() === "%%") {
- let sec = [];
- let j = i + 1;
- while (j < lines.length) {
- const mj = lines[j].match(LIST_RE);
- if (mj) { sec.push([indentOf(lines[j]), (mj[3] || "").replace(/\s+$/, "")]); j++; }
- else break;
- }
- let keyIdx = i;
- if (!sec.length) {
- const up = [];
- let k = i - 1, bulletIdx = null;
- while (k >= fm.bodyStart) {
- const bk = lines[k];
- if (bk.trim() === "" || HEAD_RE.test(bk) || FENCE_RE.test(bk)) break;
- up.push(k);
- if (LIST_RE.test(bk)) { bulletIdx = k; break; }
- k--;
- }
- if (bulletIdx !== null) {
- const inds = [];
- for (let bl = bulletIdx; bl < i; bl++) {
- const mb = lines[bl].match(LIST_RE);
- if (mb) { inds.push(indentOf(lines[bl])); sec.push([indentOf(lines[bl]), (mb[3] || "").replace(/\s+$/, "")]); }
- else sec.push([(inds.length ? inds[inds.length - 1] + 2 : 0), lines[bl].trim()]);
- }
- keyIdx = bulletIdx;
- } else if (up.length) {
- sec = up.slice().reverse().map(k2 => [0, lines[k2].trim()]);
- } else {
- // 兜底:标签行紧挨在某个标题下方(中间只有空行)→ 捕获该标题下的整个小节
- let h = i - 1;
- while (h >= fm.bodyStart && lines[h].trim() === "") h--;
- const hmk = h >= fm.bodyStart ? lines[h].match(HEAD_RE) : null;
- if (hmk) {
- const level = hmk[1].length;
- const title = hmk[2].replace(ANY_TAG, "").replace(/\s+/g, " ").trim();
- const sec2 = title ? [[0, "**" + title + "**"]] : [];
- let j2 = i + 1, count2 = 0;
- while (j2 < lines.length && count2 < 300) {
- const sl = lines[j2];
- const shm = sl.match(HEAD_RE);
- if (shm && shm[1].length <= level) break;
- if (FENCE_RE.test(sl)) break;
- if (sl.trim()) {
- const smi = sl.match(LIST_RE);
- if (smi) sec2.push([1 + Math.floor(indentOf(sl) / 2), (smi[3] || "").replace(/\s+$/, "")]);
- else sec2.push([1, sl.trim().replace(/^>\s*/, "")]);
- count2++;
- }
- j2++;
- }
- if (sec2.length) sec = sec2;
- }
- }
- }
- if (sec.length) {
- const mi0 = Math.min(...sec.map(x => x[0]));
- addBlock(keyIdx, sec.map(([ind, t]) => [Math.floor((ind - mi0) / 2), t]));
- }
- continue;
- }
-
- // 普通段落带标签:吸收到空行为止
- const para = [stripTag(clean)];
- let j = i + 1;
- while (j < lines.length) {
- const nl = lines[j];
- if (nl.trim() === "" || HEAD_RE.test(nl) || LIST_RE.test(nl) || FENCE_RE.test(nl)) break;
- para.push(nl.trim());
- j++;
- }
- addBlock(i, [[0, para.filter(p => p).join(" ")]]);
- }
-
- // frontmatter 标签 → 页面级条目(附正文开头预览)
- if (TAG_LIST.some(t => fm.tags.has(t))) {
- const d = fm.date || fileDate || mtimeDate;
- if (d) {
- order++;
- // 收集正文文本至 PREV_CHARS:超过上限的长文在渲染时截断并附「查看全文」链接
- const prev = [];
- let prevTotal = 0, prevCount = 0;
- for (let k = fm.bodyStart; k < lines.length && prevCount < 200; k++) {
- const s = lines[k].trim();
- if (!s || s.startsWith("#") || s === "---") continue;
- const clean = s.replace(ANY_TAG, "");
- if (!clean) continue;
- prev.push(clean);
- prevTotal += clean.length;
- prevCount++;
- if (prevTotal > PREV_CHARS) break; // 超过上限:已足够判断,停止收集
- }
- const preview = prev.join(" ");
- entries.push({ date: d, time: null, order, relpath, note, breadcrumb: null,
- lines: preview ? [[0, preview]] : [],
- pageLevel: true, notes: [note], anchor: null });
- }
- }
- return entries;
-}
-
-// ---------------- 扫描整个 vault ----------------
-const currentPath = (dv.current() && dv.current().file) ? dv.current().file.path : null;
-let all = [];
-for (const f of app.vault.getMarkdownFiles()) {
- if (f.path === currentPath || f.path.endsWith(".excalidraw.md")) continue;
- if (SKIP_DIRS.some(d => f.path.startsWith(d + "/"))) continue;
- if (f.path.split("/").some(seg => seg.startsWith("."))) continue; // 排除所有隐藏文件/文件夹(.开头)
- if (excludedPaths.some(p => f.path === p || f.path.startsWith(p.endsWith("/") ? p : p + "/"))) continue;
- let text;
- try { text = await app.vault.cachedRead(f); } catch (e) { continue; }
- if (text.slice(0, 1000).includes(MARKER)) continue; // 跳过 tagfeed 生成的静态页
- all = all.concat(scanText(text, f.basename, f.path, dateFromName(f.basename), new Date(f.stat.mtime)));
-}
-
-// ---------------- 去重:同日期+同内容合并,来源并排 ----------------
-const timeCmp = (a, b) => (a[0] - b[0]) || (a[1] - b[1]) || (a[2] - b[2]);
-const mergedMap = new Map();
-for (const e of all) {
- const fp = e.date.getTime() + "|" + e.lines.map(x => x[0] + ":" + x[1]).join("\n");
- const m = mergedMap.get(fp);
- if (m) {
- if (!m.notes.includes(e.note)) m.notes.push(e.note);
- if (e.time && (!m.time || timeCmp(e.time, m.time) < 0)) m.time = e.time;
- // 若先扫到的来源没有面包屑/锚点,而后面的有,则采用后者(与扫描顺序无关,结果确定)
- if (!m.breadcrumb && e.breadcrumb) {
- m.breadcrumb = e.breadcrumb;
- m.anchor = e.anchor;
- m.relpath = e.relpath;
- m.note = e.note;
- }
- } else mergedMap.set(fp, e);
-}
-const entries = [...mergedMap.values()];
-
-// ---------------- 渲染:按日期倒序 ----------------
-const byDate = new Map();
-for (const e of entries) {
- const k = e.date.getTime();
- if (!byDate.has(k)) byDate.set(k, []);
- byDate.get(k).push(e);
-}
-const dates = [...byDate.keys()].sort((a, b) => b - a);
-const noteSet = new Set();
-for (const e of entries) for (const n of e.notes) noteSet.add(n);
-
-const out = [];
-out.push("> **" + entries.length + "** 个内容块 | **" + noteSet.size + "** 篇笔记 | 按日期倒序 | 随笔记变动自动刷新");
-
-const pad2 = x => String(x).padStart(2, "0");
-const fmtDate = d => d.getFullYear() + "-" + pad2(d.getMonth() + 1) + "-" + pad2(d.getDate());
-const invTime = t => t.map(x => pad2(99 - x)).join("");
-
-for (const dk of dates) {
- const d = new Date(dk);
- out.push("## 📅 " + fmtDate(d) + " · " + WEEKDAYS[d.getDay() === 0 ? 6 : d.getDay() - 1]);
- out.push("");
- const group = byDate.get(dk);
-
- const sub = new Map(); // (笔记+面包屑) -> entries
- for (const e of group) {
- const key = e.relpath + "|" + (e.breadcrumb || "");
- if (!sub.has(key)) sub.set(key, []);
- sub.get(key).push(e);
- }
- const subKey = ([key, es]) => {
- const times = es.filter(e => e.time).map(e => e.time);
- const best = times.length ? times.reduce((a, b) => (timeCmp(a, b) > 0 ? a : b)) : null;
- return (best ? "0" : "1") + (best ? invTime(best) : "000000") + key;
- };
-
- for (const [key, es] of [...sub.entries()].sort((a, b) => (subKey(a) < subKey(b) ? -1 : subKey(a) > subKey(b) ? 1 : 0))) {
- const noteList = [];
- for (const e of es) for (const n of e.notes) if (!noteList.includes(n)) noteList.push(n);
- noteList.sort();
- const crumb = es.map(e => e.breadcrumb).find(b => b) || null;
- out.push("- 🔗 " + noteList.map(n => "[[" + n + "]]").join(" · ") + (crumb ? " · " + crumb : ""));
- const sorted = es.slice().sort((a, b) => {
- const ka = (a.time ? "0" : "1") + (a.time ? invTime(a.time) : "") + String(a.order).padStart(6, "0");
- const kb = (b.time ? "0" : "1") + (b.time ? invTime(b.time) : "") + String(b.order).padStart(6, "0");
- return ka < kb ? -1 : ka > kb ? 1 : 0;
- });
- for (const e of sorted) {
- let bl = e.lines, truncated = false;
- const totalChars = bl.reduce((s, x) => s + x[1].length, 0);
- if (totalChars > PREV_CHARS) { // 只有超过上限才截断;上限内全文展示
- truncated = true;
- const shown = [];
- let chars = 0;
- for (const [lvl, txt] of bl) {
- if (chars + txt.length > PREV_CHARS) {
- if (PREV_CHARS - chars > 0) shown.push([lvl, txt.slice(0, PREV_CHARS - chars) + "…"]);
- break;
- }
- shown.push([lvl, txt]);
- chars += txt.length;
- }
- bl = shown;
- }
- for (const [lvl, txt] of bl) out.push("\t".repeat(lvl + 1) + "- " + txt);
- if (truncated) {
- const target = e.note + (e.anchor ? "#" + e.anchor : "");
- const label = e.pageLevel ? "页面级标签 · 查看全文" : "查看全文";
- out.push("\t- [[" + target + "|↗ " + label + "]]");
- }
- }
- out.push("");
- }
- out.push("");
-}
-dv.paragraph(out.join("\n"));
-
-} catch (err) {
- dv.paragraph("> ❌ tagfeed-live 执行失败:" + err.message);
- console.error("tagfeed-live:", err);
-}
-
-```
diff --git "a/02DS/01dril-book/\344\273\245\345\210\251\344\270\272\345\210\251.\346\261\202\347\264\242\344\275\217\346\210\277\344\270\216\345\234\237\345\234\260\350\264\242\346\224\277\344\271\213\350\267\257.md" "b/02DS/01dril-book/\344\273\245\345\210\251\344\270\272\345\210\251.\346\261\202\347\264\242\344\275\217\346\210\277\344\270\216\345\234\237\345\234\260\350\264\242\346\224\277\344\271\213\350\267\257.md"
index 4d52e6c..7c30d15 100644
--- "a/02DS/01dril-book/\344\273\245\345\210\251\344\270\272\345\210\251.\346\261\202\347\264\242\344\275\217\346\210\277\344\270\216\345\234\237\345\234\260\350\264\242\346\224\277\344\271\213\350\267\257.md"
+++ "b/02DS/01dril-book/\344\273\245\345\210\251\344\270\272\345\210\251.\346\261\202\347\264\242\344\275\217\346\210\277\344\270\216\345\234\237\345\234\260\350\264\242\346\224\277\344\271\213\350\267\257.md"
@@ -5,19 +5,20 @@
words:
2026-08-22: 36541
2026-08-24: 36483
+ 2026-08-25: 36465
---
**目录**
-[廉租房之弊:西方的与中国的](#text00000.html_filepos0000002353)
-[户籍制度改革的误区与突破口](#text00000.html_filepos0000018890)
-[土地流转与农业现代化](#text00000.html_filepos0000032957)
-[土地流转的过去、现在和未来](#text00000.html_filepos0000062186)
-[政府的表外业务](#text00000.html_filepos0000073306)
-[治理地方债务激增](#text00000.html_filepos0000077858)
-[如何摆脱土地财政](#text00000.html_filepos0000086498)
-[新一轮财政改革须提上议事日程](#text00000.html_filepos0000090978)
-[土地制度改革与修法建言](#text00000.html_filepos0000095268)
+[廉租房之弊:西方的与中国的](.html_filepos0000002353)
+[户籍制度改革的误区与突破口](.html_filepos0000018890)
+[土地流转与农业现代化](.html_filepos0000032957)
+[土地流转的过去、现在和未来](.html_filepos0000062186)
+[政府的表外业务](.html_filepos0000073306)
+[治理地方债务激增](.html_filepos0000077858)
+[如何摆脱土地财政](.html_filepos0000086498)
+[新一轮财政改革须提上议事日程](.html_filepos0000090978)
+[土地制度改革与修法建言](.html_filepos0000095268)
```
版权页:
@@ -585,20 +586,20 @@
**Table of Contents**
-[廉租房之弊:西方的与中国的](#text00000.html_filepos0000002353)
+[廉租房之弊:西方的与中国的](.html_filepos0000002353)
-[户籍制度改革的误区与突破口](#text00000.html_filepos0000018890)
+[户籍制度改革的误区与突破口](.html_filepos0000018890)
-[土地流转与农业现代化](#text00000.html_filepos0000032957)
+[土地流转与农业现代化](.html_filepos0000032957)
-[土地流转的过去、现在和未来](#text00000.html_filepos0000062186)
+[土地流转的过去、现在和未来](.html_filepos0000062186)
-[政府的表外业务](#text00000.html_filepos0000073306)
+[政府的表外业务](.html_filepos0000073306)
-[治理地方债务激增](#text00000.html_filepos0000077858)
+[治理地方债务激增](.html_filepos0000077858)
-[如何摆脱土地财政](#text00000.html_filepos0000086498)
+[如何摆脱土地财政](.html_filepos0000086498)
-[新一轮财政改革须提上议事日程](#text00000.html_filepos0000090978)
+[新一轮财政改革须提上议事日程](.html_filepos0000090978)
-[土地制度改革与修法建言](#text00000.html_filepos0000095268)
+[土地制度改革与修法建言](.html_filepos0000095268)
diff --git "a/02DS/Ob/Obsidian\351\231\204\344\273\266.1\344\270\252Skill\346\225\231\344\275\240\350\207\252\345\212\250\346\225\264\347\220\206" "b/02DS/Ob/00inbox_ob/Obsidian\351\231\204\344\273\266.1\344\270\252skill\346\225\231\344\275\240\350\207\252\345\212\250\346\225\264\347\220\206"
similarity index 100%
rename from "02DS/Ob/Obsidian\351\231\204\344\273\266.1\344\270\252Skill\346\225\231\344\275\240\350\207\252\345\212\250\346\225\264\347\220\206"
rename to "02DS/Ob/00inbox_ob/Obsidian\351\231\204\344\273\266.1\344\270\252skill\346\225\231\344\275\240\350\207\252\345\212\250\346\225\264\347\220\206"
Binary files differ
diff --git "a/02DS/Ob/ob\345\255\246\344\271\240\347\254\224\350\256\260.md" "b/02DS/Ob/00inbox_ob/ob\345\255\246\344\271\240\347\254\224\350\256\260.md"
similarity index 100%
rename from "02DS/Ob/ob\345\255\246\344\271\240\347\254\224\350\256\260.md"
rename to "02DS/Ob/00inbox_ob/ob\345\255\246\344\271\240\347\254\224\350\256\260.md"
diff --git "a/02DS/Ob/X-\346\234\254\346\234\272\347\224\250\346\263\225\345\244\207\345\277\230/01\345\205\250\345\261\200\350\207\252\345\212\250\345\214\226\347\264\242\345\274\225\344\270\216\346\225\264\347\220\206\351\200\273\350\276\221/00index.canvas\347\224\237\346\210\220\345\216\237\347\220\206\350\257\264\346\230\216.md" "b/02DS/Ob/X-\346\234\254\346\234\272\347\224\250\346\263\225\345\244\207\345\277\230/01\345\205\250\345\261\200\350\207\252\345\212\250\345\214\226\347\264\242\345\274\225\344\270\216\346\225\264\347\220\206\351\200\273\350\276\221/00index.canvas\347\224\237\346\210\220\345\216\237\347\220\206\350\257\264\346\230\216.md"
new file mode 100644
index 0000000..fb2e6db
--- /dev/null
+++ "b/02DS/Ob/X-\346\234\254\346\234\272\347\224\250\346\263\225\345\244\207\345\277\230/01\345\205\250\345\261\200\350\207\252\345\212\250\345\214\226\347\264\242\345\274\225\344\270\216\346\225\264\347\220\206\351\200\273\350\276\221/00index.canvas\347\224\237\346\210\220\345\216\237\347\220\206\350\257\264\346\230\216.md"
@@ -0,0 +1,44 @@
+---
+words:
+ 2026-08-25: 919
+---
+# 00index.canvas 自动生成原理说明
+
+## 一句话概括
+`00index.canvas` **不是 Obsidian 插件生成的**,而是由你自建的守护脚本 **`/Users/ar/bin/tagfeed.py`** 持续重写的一个 Obsidian Canvas(JSON)文件;Obsidian 里的 **Cammvas 社区插件** 读取其中的 `mindmap: true`,把它渲染成「向右展开的目录脑图」。
+
+## 启动链路(谁把它跑起来)
+1. **macOS 登录 / 开机自启**:`~/Library/LaunchAgents/com.ar.tagfeedwatch-login.plist`(Label `com.ar.tagfeedwatch-login`,`RunAtLoad=true`)执行 `/usr/bin/open -g /Users/ar/Applications/TagfeedWatch.app`。
+2. **TagfeedWatch.app**(`/Users/ar/Applications/`):只是一个 ~60KB 的 Mach-O 外壳,内部用 `/usr/bin/python3` 调用真正的脚本。
+3. **真正的生成器**:`/Users/ar/bin/tagfeed.py`,以 `tagfeed.py --watch` 常驻运行,默认 **每 3 秒**轮询一次 vault(检测到变化后还会等 1.5 秒防抖再处理)。
+
+## 生成逻辑(每次触发都重画)
+核心函数:`generate_canvas_index(vault, rel_dir="00index", canvas_name="00index.canvas")`(tagfeed.py 约 1124–1291 行)。
+
+1. **触发条件**:每轮用 `canvas_signature()` 计算 `00index` 目录的「结构指纹」——所有 `.md` 文件与子目录的相对路径集合。文件**改名**不改 mtime(普通内容指纹看不见),所以这个独立指纹专门捕捉「增 / 删 / 改名」。指纹变了才重画,否则跳过本轮。
+2. **建树 `build()`**:递归遍历 `00index`,同层文件夹在前、`.md` 文件在后;跳过 `SKIP_DIRS`(如 `X.Attachment`、`.obsidian`)和 **`CANVAS_SKIP_DIRS={"0inbox"}`**(实时聚合草稿目录,不进脑图)。
+3. **布局**:自底向上算子树高度、每层取最大宽度决定列间距,整体向右展开。
+4. **放置 `place()`**:根节点 → 文件夹节点(📁)→ 文件节点。文件节点用**文字节点 + wikilink**(`[[路径|文件名]]`)而非 `file` 节点,避免渲染满屏「笔记属性」预览;点击可直接打开对应笔记。节点 id 由路径经 md5 确定性生成。
+5. **颜色策略**:生成器**不再自动上任何颜色**(旧逻辑用 `CANVAS_COLORS` 给每个分支染红/绿/蓝,会覆盖你手动设的色,已停用)。改为「读取旧画布里已有的 `color`,按节点 id 匹配后保留」——所以你手动上的色在每次重画后都还在;新出现的节点默认无色,由你上色。
+6. **写盘门控**:整张画布序列化为 JSON 后与磁盘现有内容比对,完全相同就**不写盘**,避免无谓改动触发 Obsidian 重载。
+
+## 渲染(怎么变成脑图)
+- 文件本身是标准 Obsidian Canvas JSON(`{nodes, edges}`)。
+- 你装的 **Cammvas 插件**(社区插件)读取画布里的 `mindmap: true`,渲染成「向右展开的脑图」样式。
+- 注意:Obsidian 直接打开 `.canvas` 默认是自由画布视图,要看到脑图需启用 Cammvas 的 mindmap 模式。
+
+## 涉及的文件 / 插件清单
+| 角色 | 名称 | 位置 |
+|---|---|---|
+| 启动项 | LaunchAgent plist | `~/Library/LaunchAgents/com.ar.tagfeedwatch-login.plist` |
+| 外壳 App | TagfeedWatch.app | `/Users/ar/Applications/TagfeedWatch.app` |
+| 生成脚本 | **tagfeed.py** | `/Users/ar/bin/tagfeed.py` |
+| 生成函数 | `generate_canvas_index()` / `canvas_signature()` | tagfeed.py 内(约 1124–1291 行) |
+| 渲染插件 | Cammvas(社区插件,mindmap 模式) | Obsidian 设置 → 社区插件 |
+| 产物 | 00index.canvas | `/Users/ar/Downloads/Syn/Ob/Ob.temp/00index/00index.canvas` |
+
+## 关于你刚挪动的 0inbox
+你把 `00index/标签聚合/0inbox` 移到了 `00index/0inbox`:
+- **画布**:`CANVAS_SKIP_DIRS={"0inbox"}` 按目录名全局跳过,位置变化不影响排除 → 画布依旧不含 0inbox。
+- **sync_0inbox.py**:其 `INBOX_DIR="0inbox"` 本就拼成 `00index/0inbox`,与你的新位置一致 → 无需改动脚本。
+- 当前画布实测:**73 节点 / 72 连线 / 0 个 0inbox / 72 个带颜色**(即你手动上的色都被成功保留)✅
diff --git "a/02DS/Ob/X-\346\234\254\346\234\272\347\224\250\346\263\225\345\244\207\345\277\230/01\345\205\250\345\261\200\350\207\252\345\212\250\345\214\226\347\264\242\345\274\225\344\270\216\346\225\264\347\220\206\351\200\273\350\276\221/01\345\205\250\345\261\200\350\207\252\345\212\250\345\214\226\347\264\242\345\274\225\344\270\216\346\225\264\347\220\206\351\200\273\350\276\221.md" "b/02DS/Ob/X-\346\234\254\346\234\272\347\224\250\346\263\225\345\244\207\345\277\230/01\345\205\250\345\261\200\350\207\252\345\212\250\345\214\226\347\264\242\345\274\225\344\270\216\346\225\264\347\220\206\351\200\273\350\276\221/01\345\205\250\345\261\200\350\207\252\345\212\250\345\214\226\347\264\242\345\274\225\344\270\216\346\225\264\347\220\206\351\200\273\350\276\221.md"
new file mode 100644
index 0000000..d5b89b1
--- /dev/null
+++ "b/02DS/Ob/X-\346\234\254\346\234\272\347\224\250\346\263\225\345\244\207\345\277\230/01\345\205\250\345\261\200\350\207\252\345\212\250\345\214\226\347\264\242\345\274\225\344\270\216\346\225\264\347\220\206\351\200\273\350\276\221/01\345\205\250\345\261\200\350\207\252\345\212\250\345\214\226\347\264\242\345\274\225\344\270\216\346\225\264\347\220\206\351\200\273\350\276\221.md"
@@ -0,0 +1,72 @@
+---
+excalidraw-plugin: parsed
+tags:
+ - excalidraw
+words:
+ 2026-08-25: 172
+---
+==⚠ Switch to EXCALIDRAW VIEW in the MORE OPTIONS menu of this document. ⚠== You can decompress Drawing data with the command palette: 'Decompress current Excalidraw file'. For more info check in plugin settings under 'Saving'
+
+
+# Excalidraw Data
+
+## Text Elements
+00index.canvas 自动生成原理 ^qFjWSa4G
+
+已删除 ^Mm78PU2K
+
+## Embedded Files
+ce26b034c7f6d154e5866d2a8a8da549bc05e931: [[Pasted Image 20260825152522_494.png]]
+
+3a71c33ea1160140d5535577fac81b7c3102c4e0: [[Pasted Image 20260825152650_226.png]]
+
+5e3b3bc1058f613398410788c5cffd46fb8cae62: [[Pasted Image 20260825152710_603.png]]
+
+ea69d5ce6f12378f1035020b3af0787c08562f64: [[Pasted Image 20260825152817_143.png]]
+
+%%
+## Drawing
+```compressed-json
+N4KAkARALgngDgUwgLgAQQQDwMYEMA2AlgCYBOuA7hADTgQBuCpAzoQPYB2KqATLZMzYBXUtiRoIACyhQ4zZAHoFAc0JRJQgEYA6bGwC2CgF7N6hbEcK4OCtptbErHALRY8RMpWdx8Q1TdIEfARcZgRmBShcZQUebR4AVm0AZho6IIR9BA4oZm4AbXAwUDBSiBJuCA4ANWqAZUIAEQAxKDTSyFhESsJ9aKR+MsxuHgB2AEZtcYAWBPGeAAZx8dGF
+
+6eTRwcgYbmdp0YA2FIAOUYT15OSeY4WEw62IChJ1blHjgE5tUfWeRcTD0ajd4PSQIQjKaTcBJjbTvA7HcbvaY3cYHZIHaYPazKYLcBYPZhQUhsADWCAAwmx8GxSJUidZmHBcIEcu0yppcNgScpiUIOMRKdTaRJ6RxGczsm0HgAzQj4fB1WC4iSCDxsgRE0kIADqz0kIwJmrJipgyvQqoqD15EI44TyaHGDzYTOwah2DoW+KKkB5wjgAEliPbUPkA
+
+Loy8hZQOVcYARW1o2a2uaAHF1eV+VhKrhxunefzbcxgxwhPKCQgEMQRgtkktkcloQ9GCx2Fw0NNPU2mKxOAA5ThibiI5Ko5Iog4PQjMRoZKCV7jSghhB6aYT8gCiwSyOWDYYeQjgxFwc6rDreh2mYyu3wn3ogRA4JO4JbLd+pXPnaEX+GXd8Jx6EYMIEQflCA4ZR01lYJowkMQeAOTQa2mbBRmlA5iHGc4EASY4DnQnhcGOQij3Od5NGwW4EHeEd
+
+02YdxxBDb0wEdJjxm9cM72wYk4GfUt8BBUIABUsCgAAZMCny/JcECKABfQYSjKCoJBgRoAH11wAaXXaoAE1iEacY2AAIV06Z9H0IwRE0dMugY+8wIGO9hjQb5pm0fY8Oog5rjec4HndVBnAbY5tASA4EluPCDhmCY8IeJ5iBeNBfkmBJ3gWRJZjeOFwsxO9QXBSEHW0Y51nGKKrned5MJuBIsXAs0vQ6CBCWJMlBRpSoAGIEBrcYaJXTluXzAUqS
+
+6kVyDFJkWSlO8oIVJV7ItKtDXanU9QNP8jQQE0zVaqlLTva1JELYMWJa51OTdIdOzvX0D0DXcOJa6VIwQGD0DjBMk1TdMwOILMJFwHg8zXYgzu4RSyjskZvXkv8K1PVBER4WsawxY4uxbTgh32bGew4fsOEHB1xmOMZ4Jqvg7ynGdghPBdpJXcHN0ySViz4/dD2PT8UfPDERwp6isbfCTeNfFr3zJZHv1/Fq4DYMDcgKJjCg6Upmo1hYmJejp1Y1
+
+1KwoyrK7g+CKMS2UpJjKkdKp4aratuXX2MNZkoGMgGwIgtAX34u9smIT3QPAiX/Za/9SCgABBUhiQoUFcGRv2HkD2P48T5OubvRX8BgZRcakn8ZNKBHSmh8pkYgABZfQ3gABQAVR4TTbPgey50wOaWpc1A4VGDyxgOPyxxCi6ykC4K0VhMqeFRz1kmmd5vgSza0AbFJUTeDYbmH25F5BMEITaB13ImBIrgis4xmomqGpxBitY1dbOuFdBev6wa7w
+
+5LkHv5V+6RTXFLNSCcpFqmmWodVa211q6iSvqFKa0tR7UgWqK0wgbR2iHE6F0N0PRPwgA9AMQYCh60gG9XAUYq7fUTMmNMk5My9wgLgVI6C+QQywb7bOEckbcGOOcGs7wHa3has2QmQ53ii1Ed2VsxNSaoCBHhaENwCF01nHzOWCAWbsLZtuFWXDJZlAPEeRmZ5TiC0ROFYeDxpYaOZneTuJ90CACfdQABEqABM0vMlBhJd0qG4zxMpOBQAaEYBi
+
+ywvijF+DFeCmU0aRU2PNIJzRKFykCvVBxIlo5EGUG2dAwRpTdzKM2KA5gCBZPBLk6Azp0x6ByLgRypBPopzvDScEYECA+KcRAfx6ZcBCCgGwAASuEQgoTuBEiEFosWtoAASR9ioo3iOklqkghIiXEo+Jmxc5IKVptQ+MtC/oPFhhIChWR0y9y3vEBEnpopXAvmOAKux1gLG0AsNEVxpjD2RJ6BYCSWqJWSijJYcQ/Lm2RKOC+h8ipOOmJebQEUMo
+
+LEkTlX4aN75NSQR1cab8IDdXGAgAlBL0w/xGuDABk0GQzUlKA+UKDKgrVojtOBQKaYRx2vSlUUCwZ+FOpwlGODrqwFugQohT1SERkoR9LOhjIAAyBugXACQeUFn5c0nhfMKplWRAcamBNWwjGWPqvsA4wkxU9Dle4tNpzqNlvYlqq4dFbg5hKu8xjebIxWOYy8iI/mIhseLAx4cyi2LtcXB4HApVhwEswTpGzJKoE0Q8bAgEBn6EaMeXAUMmIQDO
+
+RSHF2aNbPy1D1Ro0wy1lvVGUBaPVo6NDrXWqtkBI1ZApe/UY0cO0dogExWS8MdlFArspL6cB3i9kbhQQZAANNu3QJC9H6Bcw1RxwrHHRHMZEaNHl3jSQPCmI4GwJHSki94yyyiAoQagOFoVwoDSWKcaEc94LQuPtwWskx0SROWLWNGtx/llGxJimBWo23QCAdS1kQ1f6jVA6KYBNKZRgM5eablWKNrwK2uy9ayGDpoOOhgvlRZsEtNwcK/BDwxUk
+
+LQHuea71PoQFGIM3S0dq7jHEv9Rh2YDgqo4URoN5Y+YvN1cvK10ica5PmGeyAYjZGmurB8GsyL5iThtQzOx4bv6s2dTuV1LV3WmP5t664mFPkBs2fxt8bAPxhvlmUf8UBAKVBAo4UOiHoJV2SLgCY2BLghGWAcOsCxiBHsPYCRc2AESaFGD5iqPBsDTD6rReiqsNbj2tq7Ti3Fo0FTWV3eNWywgDvLnsyoJIjBQF0tqQS/paTHPbj0PoygnI924F
+
+fN5lxyZLGqjhYEO7XivNGMkGqpx1g+TOJbO8F6oQOzeW8AaCIKp4RmC+hZ8FJhwsiteo9/CRwYsfmh2D4GJSQe/sNP+Y0hSAKpcdwp5CkNLQZah4DZIWWXrZXZjlD2uV4ZaidSGDpBWujI8C0VvJiHPUlVQyojHmOsfYwwwGTCvM8f+6gdVdneGuVWNcS8xwpFFJkYXXgaxjVEzkx6fY0xybLHe3K1TCADNJs0069mOnqNkIgPpzVAtcdU47MkMz
+
+Cb0eQFDQVqZ7KAJAWc97Wl0rKgJAQMkTQSvsAVRwmhAaQ2yoVTeMcbACRsDSmlMQL50pNB69wAgHySWCAMQNtbK2bEOgc64tUizKzctiUDYm6SRXiglYkDDljbHCCzvsnmpdDp4SwgGosCKHYL5zCeevT4mEFhrovvCaiiRaePDXsCzCXw1hLEG4cYedwVtOIbK8y8p7F7pTnmcL5e28QHYLRIfFhKu8krOzB9v6A4MQdu7m+7EDHs/Y+7A/Pue2
+
+rIK+yhifkA/v8rSxAK6QPAoVVB36cV7PIdy/d0pTjwNjgo7VdwjHfM/iHBz6iUn3Bm93hkyakmYTwrVWvVcFT9MGfqds5AR1DcbTfREMDnLnT1HnG+eYAaQXbLKWKzGWMXCNKNQ/SAVZWNdZb3JnFqFNQkAwDNKIQtV6d6NtaGSfEtDvCtctaYJtEfeUWtetRg2gltfNS7DvLtTtaOHtDWPtDoXgsAMuf3FqYdYCaubAAALQOFMEbjDwa0XQeF7m
+
+cBwmSA8hHG+XXXWCXmTyClmHcip0iTmCBEvFrAilXgw1ckRCmCESUzXWHkXn/TQPmScTxySHWDOBbzQAIVn2xTYIHyOxASgzJXYUO2uwCPmlH32kZTQ1e0w3IONHn1wyOl+wI1R1X3XzwRBwozB131AP3yaQvzlWP0VXeDPz4zRwKNakx35iuF8jRlz2f1yQmFJzkQYmuDRkvHymEPp0Z3tXZC01ZxAJoz0x5gMy9QvGuGqlOFzwfCFwqNFyLn/1
+
+aiiAcyl0DhlzcwP3QBCF1WCzEAOGlHmB3gOJrEikWGV1wGlD+VOAon4R8jQhoIJGS2o1YkdwyxwKy1QKkE93ywWJLgEN2WEKrgAEdmgAArbUOoXAaYehBxerEUESSPIKALPQ8mU4RbTKCqToieXYQwhFD5CmWwoWLEyAKbVyDsL4McTCGqRRB9SvVrAeL5JeC4KnOqRIDw1ALwnaUDTvIlZrdkXvclfvMDUIhDcIulBIqI57dDVlNDHDSU5I3lVI
+
+wHDIrfLInfKjXI2jKVfI2VDMRHbMLgthVVMo4XSozVdYamEFfHaTQnCTP5Zo8nXgRYC+DsO/a1H/HojTB1fovRCHN1EY7nIzamSJAhGYuAkNBAv/cXGGBEiQT0eVTAXQawegUIVAQAK8DAAKpUAHxXQABCNAB85UADAXLxCgTpSoeMxhJMjgFM5gdM7M/MoswJHIEJBiC+WECYU9L5QbOFS8RsqAZJfQVJKEY5TJbJSpfJYfYpUpfAcpHJOkapZN
+
+IJepW0RpGVYNSAVpfwDpWM9AcsxHSs6s2s3Mws3pfpIZEZMZNACZaMkXRyOZGFIcJZGNONLA33UucAF6ZhOAOARUXmIg6AUEc5CQbJSEQYBgQgBACgYyAU4IoU7qI3eC6UNkCAFNOOSUf0OcfQRUF+WCvqA9XMUClC2adCzIKC6DQU3w4U6aG7JCwitCjC5oCI1BJIyAWinIYizC5lPUTSBAQKCALIRwIQfQGikQIijCrCrUaoZkKwTQOUDI5Czg
+
+f8E7MoViqAdi8S+Isfb7Zi5CkSuizIQZFIlfAi3StijCgAeVI03zumUpMtUvoqSRSTziHKKB0tQtMsyGaCCWbOrGMrcrssyE6RnLHIQAKWEr8rUqiCjnTjYAThCFXN8tEsyHXH5GitiqTmzDjhirCsSv0FSsEjhPQFGmyr0v0GaHegMrNHR1ald3lBnTQGcG+BSE3SRBWGgO+DPWquJHlF0la2Hhnh8gC1uGFiRFAqMDYAMCIOkwIEmTxFKg7ACx
+
+4D91cpyoMvYVRwgCKtAp5BIG8pSi1kIVIB2rnB4jQA6u2uIGrjYEBmStwE0GCBs2jIOpIFILvGMipCrlIGUA5AAApG9qBeAVh/q/qOSwoABKdMYZZQUsZkSoT6n639IG2sRG/EEGhIcGpalS9ShACykpBS8M3NcqwOJgGXNACubIW6+68ZUgSZZNIgE61AK85AwChm6m68tffpB8BiRmu8PoUgMkUgXsFAlmmmnm5kfmm6u6vmK8pauwUEhAbAXI
+
+OoSNOAS666yNSWh60CzkEpRgQSca/ASa6AAqyo4IBWg1ZNVNAwfKudco3U+Yn3L06tYkTCjIM2onbAkNUIGOM23W/WsOJaxwZgCm/NHIESaubIIQJAlymSkOZQeuQIaUJgbIeRDAdWym0CqcYOFzZQCWym34jO5gauEgRWZWJW3AL872XOqW1m0C1cTAOoV23GjgVW+yWpKIMCbgsuOg8IKGPtWSIAA=
+```
+%%
\ No newline at end of file
diff --git "a/02DS/Ob/X-\346\234\254\346\234\272\347\224\250\346\263\225\345\244\207\345\277\230/01\345\205\250\345\261\200\350\207\252\345\212\250\345\214\226\347\264\242\345\274\225\344\270\216\346\225\264\347\220\206\351\200\273\350\276\221/2026-08-25-tagfeed\346\240\270\345\277\203\351\200\273\350\276\221\345\244\207\345\277\230.md" "b/02DS/Ob/X-\346\234\254\346\234\272\347\224\250\346\263\225\345\244\207\345\277\230/01\345\205\250\345\261\200\350\207\252\345\212\250\345\214\226\347\264\242\345\274\225\344\270\216\346\225\264\347\220\206\351\200\273\350\276\221/2026-08-25-tagfeed\346\240\270\345\277\203\351\200\273\350\276\221\345\244\207\345\277\230.md"
new file mode 100644
index 0000000..158990d
--- /dev/null
+++ "b/02DS/Ob/X-\346\234\254\346\234\272\347\224\250\346\263\225\345\244\207\345\277\230/01\345\205\250\345\261\200\350\207\252\345\212\250\345\214\226\347\264\242\345\274\225\344\270\216\346\225\264\347\220\206\351\200\273\350\276\221/2026-08-25-tagfeed\346\240\270\345\277\203\351\200\273\350\276\221\345\244\207\345\277\230.md"
@@ -0,0 +1,64 @@
+---
+words:
+ 2026-08-25: 1263
+---
+这套 tagfeed 工作流梳理成「架构图 + 生命周期图 + 规则细节图」三张图
+,先把绘图模块加载出来,然后逐张给你讲。
+
+我先加载图示模块:
+
+先上第一张:**整体架构图**——这套工作流由两个部件 + 两条触发链组成。
+- **生成器** `sync_0inbox.py`:负责"新建" tagfeed 页,扫描全 vault 的 `#tag`,按规则写进 A 收件箱。
+- **监视器** `tagfeed.py --watch`:负责"即时响应"——文件一变动(约 15s 内)就触发生成器,同时顺带刷新已有页、日记足迹、目录脑图。
+- 两条触发链:实时链(launchd → TagfeedWatch.app → watcher)和定时兜底(WorkBuddy 自动化每天 04:00)。
+
+第二张:**标签生命周期图**——A 与 B 的双文件夹机制,以及你的整理动作如何驱动秩序形成。
+- **A = `00index/0inbox`**:所有"还没归类"的标签页自动落在这里。
+- **B = `00index/标签聚合`**:你把页面从 A 拖进来,就代表"已整理"。
+- 规则 1 让新标签自动进 A;规则 2 让进了 B 的标签永不回 A。随着你不断拖拽,A 渐归 0,B 成为你最终的知识秩序。
+
+第三张:**规则 2 进阶图**——这正是你刚遇到的"顽固再生"问题的根因与修法。
+问题在于"一个合并页可以覆盖多个标签"。比如 `UI.md` 在页面里写 `targetTag = ["UI","UI相关"]`,它同时索引这两个标签。旧逻辑只按"文件名是否等于标签名"判断,于是 `UI相关` 永远匹配不上 `UI.md`,被当成未整理反复重建。修复后改为**解析 `targetTag` 声明**:只要被任一聚合页声明覆盖,就判定为"已整理",不再进 A。
+
+
+
+三张图讲完了,下面把这套工作流浓缩成一份**速查参考**,方便你以后照着用。
+
+## 一句话定位
+> 新标签自动落进 **A(收件箱)**,你拖进 **B(标签聚合)** 即视为整理完毕,从此永不回 A——A 渐归 0,B 成秩序。
+
+## 两条核心规则
+- **规则 1(自动生成)**:笔记里出现新 `#tag`(且不在排除目录)→ 约 15s 内由 watcher 触发生成器,在 `00index/0inbox` 建页。每日 04:00 的 WorkBuddy 自动化作兜底。
+- **规则 2(已整理不重建)**:某标签一旦被 `标签聚合/` 下任一页覆盖——判定方式二选一:**① 该页文件名就是 `<标签>.md`;② 该页 `targetTag` 显式声明了它(支持合并页 `["UI","UI相关"]`)**——就不再出现在 A。
+
+## 关键文件与位置
+| 角色 | 路径 |
+| ------------------------------------ | -------------------------------------------------------- |
+| 生成器(建页) | `X2.Archived/scripts/sync_0inbox.py` |
+| <font color=#ff0000>监视器(即时响应)</font> | `/Users/ar/bin/tagfeed.py`(`--watch`) |
+| 监视器启动器 | `/Users/ar/Applications/TagfeedWatch.app` |
+| 登录自启 | `~/Library/LaunchAgents/com.ar.tagfeedwatch-login.plist` |
+| 定时兜底自动化 | WorkBuddy `automation-1787588745696`(每日 04:00) |
+| 运行日志 | `~/.tagfeed-watch.log` / `~/.tagfeed-watch.err.log` |
+| A 收件箱 | `00index/0inbox` |
+| B 已整理 | `00index/标签聚合`(含任意子目录) |
+
+**排除目录**(永不扫描、不会生成页):`X2.Archived` · `00index` · `Knomo` · `X.Attachment` · `P3.bobo`。
+
+## 日常操作
+1. 写笔记时随便打 `#任意标签` → 不用管,一会儿自动出现在 A。
+2. 想整理时,在 Obsidian 里把 A 里的页拖进 B 的某个分类子文件夹。
+3. 之后该标签(含合并页里声明的其他标签)绝不再回 A。
+
+## 两个已知特性(沿用原设计,未改)
+- **只建不删**:某标签从所有笔记彻底消失后,A 里会留孤儿页,需你手动删。
+- **合并页要写对 `targetTag`**:想让一个页同时覆盖多个标签,记得在它的 DataviewJS 里用数组写全,例如 `const targetTag = ["UI","UI相关"]`——这样 `UI相关` 才不会被误判为"未整理"而顽固再生(就是你今天踩的坑,已修好)。
+
+下面把两个核心脚本一并附上,方便你随时回看实现:
+已为你把这套 tagfeed 工作流整理成 **3 张图 + 一份速查参考**:
+
+1. **整体架构图**——实时链(launchd→TagfeedWatch.app→tagfeed.py)与定时兜底(WorkBuddy 04:00)共同驱动生成器 `sync_0inbox.py`,扫描排除目录 + 标签聚合目录后输出到 A 收件箱。
+2. **标签生命周期图**——A(0inbox 收件箱)与 B(标签聚合)的双箱机制,规则 1 让新标签自动进 A、规则 2 让进 B 的永不回 A,最终 A 归 0、B 成秩序。
+3. **规则 2 进阶图**——用一个合并页 `UI.md`(`targetTag=["UI","UI相关"]`)说明"一页覆盖多标签"如何被识别,正是你今天踩的"顽固再生"坑的修法可视化。
+
+核心结论:**新 `#tag` → ~15s 自动进 A;拖进 B 即整理完毕、永不回 A**。两个已知特性沿用原设计:同步只建不删(标签删光后 A 留孤儿页需手清)、合并页务必写全 `targetTag` 数组。两个核心脚本已作为附件附上,可随时回看。
\ No newline at end of file
diff --git "a/02DS/Ob/X-\346\234\254\346\234\272\347\224\250\346\263\225\345\244\207\345\277\230/01\345\205\250\345\261\200\350\207\252\345\212\250\345\214\226\347\264\242\345\274\225\344\270\216\346\225\264\347\220\206\351\200\273\350\276\221/2026-08-25-watch\346\234\272\345\210\266\346\230\257\346\200\216\344\271\210\345\256\236\347\216\260\347\232\204.md" "b/02DS/Ob/X-\346\234\254\346\234\272\347\224\250\346\263\225\345\244\207\345\277\230/01\345\205\250\345\261\200\350\207\252\345\212\250\345\214\226\347\264\242\345\274\225\344\270\216\346\225\264\347\220\206\351\200\273\350\276\221/2026-08-25-watch\346\234\272\345\210\266\346\230\257\346\200\216\344\271\210\345\256\236\347\216\260\347\232\204.md"
new file mode 100644
index 0000000..ee52a0c
--- /dev/null
+++ "b/02DS/Ob/X-\346\234\254\346\234\272\347\224\250\346\263\225\345\244\207\345\277\230/01\345\205\250\345\261\200\350\207\252\345\212\250\345\214\226\347\264\242\345\274\225\344\270\216\346\225\264\347\220\206\351\200\273\350\276\221/2026-08-25-watch\346\234\272\345\210\266\346\230\257\346\200\216\344\271\210\345\256\236\347\216\260\347\232\204.md"
@@ -0,0 +1,83 @@
+---
+words:
+ 2026-08-25: 1213
+---
+>[!question]
+>我不理解,这里面为什么还需要workbuddy的定时任务,我需要这套逻辑要能完全脱离workbuddy来起作用
+
+好问题,这其实点出了我之前方案里一个"设计冗余"——你直觉是对的。我先确认一下监视器(watch)现在到底有没有自己调生成器,再跟你讲清楚,然后把 WorkBuddy 的依赖去掉。
+
+让我读一下 `tagfeed.py` 里那段关键代码确认现状:
+确认了——关键代码在 `tagfeed.py` 第 1372–1386 行:**watch 模式在每次检测到笔记变动时,自己就用子进程调一次 `sync_0inbox.py`**(只对有 `00index/标签聚合` 结构的 vault 执行)。也就是说,生成新页面这件事,**监视器自己已经干了**。
+
+## 为什么之前会有 WorkBuddy 定时任务
+
+这是个"历史遗留 + 我当初没删干净"的问题:
+
+- 最早这套系统里,`sync_0inbox.py`(生成器)**唯一的触发源**就是那个 WorkBuddy 每日 04:00 自动化。watcher 当时只负责"刷新已有页",不负责新建。
+- 后来你要求"新标签要即时生成"(不是等 04:00),我在上一轮把"调用生成器"接进了 watcher(就是上面那段代码)。**这一步做完后,WorkBuddy 任务就已经是多余的了**——只是我当时把它当"兜底"留着,没删。
+
+所以它现在确实是个冗余依赖,你的直觉完全正确。
+
+## 现在的真实触发链(已脱离 WorkBuddy)
+
+```
+macOS 登录
+ └─ launchd (com.ar.tagfeedwatch-login.plist, 系统级, 非 WorkBuddy)
+ └─ TagfeedWatch.app
+ └─ /Users/ar/bin/tagfeed.py --watch ← 常驻进程
+ ├─ 笔记一变动(≈15s) → 刷新已有 tagfeed 页 / 日记足迹 / 目录脑图
+ └─ 同时 subprocess 调用 sync_0inbox.py → 新标签即时进 0inbox
+```
+
+整条链是 **launchd(macOS 系统机制)→ app → python**,从头到尾没有 WorkBuddy 参与。我把那个 WorkBuddy 自动化删掉,系统照样跑。
+
+让我把它删掉:
+已删除。现在做个**无 WorkBuddy 的端到端实证**,让你亲眼确认:生成新标签页面这件事,不依赖 WorkBuddy 也能跑。
+
+先确认监视器常驻进程在跑,再放一个临时笔记+全新标签,看它是否自动建页:
+实证完成,结果很干净:
+
+- 监视器进程 **PID 31269** 常驻,父级是 **`com.ar.tagfeedwatch-login`(launchd 托管)**——这是 macOS 系统级机制,**与 WorkBuddy 无关**。
+- 临时笔记 `nwbproof9z`(带 # 前缀的测试标签) 在约 16 秒后**自动建页**到 `00index/0inbox/`,日志明证:`0inbox 新标签建页: 新建 1 个`。全程没有 WorkBuddy 参与。
+- 测试产物已清理。
+
+现在整个工作流**已经 100% 脱离 WorkBuddy** 了。让我把这条变更记进日志:
+
+
+## 一句话结论
+
+**WorkBuddy 定时任务本来就不是必需的**——它是早期"生成器只有这一个触发源"的历史遗留。自从我们在监视器(watch)里接入了"自动调用生成器"的逻辑后,它就成了冗余。我已经把它删了,并实测证明整条链路现在零依赖 WorkBuddy。
+
+## 现在的完整链路(全部 macOS 原生机制)
+
+```
+macOS 登录
+ └─ launchd 系统守护 (com.ar.tagfeedwatch-login) ← 非 WorkBuddy
+ └─ TagfeedWatch.app
+ └─ /Users/ar/bin/tagfeed.py --watch ← 常驻进程
+ ├─ 笔记变动 ≈15s → 刷新已有页 / 日记足迹 / 目录脑图
+ └─ 同时 subprocess 调 sync_0inbox.py → 新标签即时进 0inbox
+```
+
+- **触发**:文件变动(FSEvents + 轮询),进程常驻,mac 唤醒后会重扫。
+- **生成**:`sync_0inbox.py` 由 watcher 直接调用,不需要任何外部调度器。
+- **已删**:原 WorkBuddy 每日 04:00 自动化 `automation-1787588745696`。
+
+## 为什么当初会有它 / 现在为什么能删
+
+| 阶段 | 新标签建页的触发源 | 是否即时 |
+|---|---|---|
+| 最早 | 仅 WorkBuddy 04:00 自动化 | 否(等每天 04:00)|
+| 上一轮改完后 | watcher 每次变动自调生成器 + WorkBuddy 兜底 | 是(≈15s)+ 冗余兜底 |
+| **现在** | **仅 watcher 自调生成器** | **是,且完全无 WorkBuddy** |
+
+## 实测佐证(无 WorkBuddy 参与)
+
+- 监视器 PID 31269 由 `com.ar.tagfeedwatch-login`(launchd)托管;
+- 临时标签 `nwbproof9z`(带 # 前缀的测试标签) 约 16s 内自动建页,日志:`0inbox 新标签建页: 新建 1 个`;
+- 测试产物已清理。
+
+---
+
+**唯一提醒**:launchd 代理在"你登录 mac"后才运行。如果 mac 关机/登出期间,你在别的设备改了笔记再同步回来,watcher 要在你下次唤醒并触发一次变动时才会补扫(它本身每轮也会基于 mtime 重扫,所以唤醒后一般会自动追上)。如果你想要一个"即便长期睡眠也定时兜底"的纯系统方案(比如用 launchd 的 `StartCalendarInterval` 每日跑一次 `sync_0inbox.py`,同样不依赖 WorkBuddy),我可以帮你加——但当前 watcher 已覆盖日常 99% 场景,要不要加那个兜底你定。
\ No newline at end of file
diff --git "a/02DS/Ob/X-\346\234\254\346\234\272\347\224\250\346\263\225\345\244\207\345\277\230/01\345\205\250\345\261\200\350\207\252\345\212\250\345\214\226\347\264\242\345\274\225\344\270\216\346\225\264\347\220\206\351\200\273\350\276\221/\347\211\210\346\234\2541-html/tagfeed_\346\225\264\344\275\223\346\236\266\346\236\204.html" "b/02DS/Ob/X-\346\234\254\346\234\272\347\224\250\346\263\225\345\244\207\345\277\230/01\345\205\250\345\261\200\350\207\252\345\212\250\345\214\226\347\264\242\345\274\225\344\270\216\346\225\264\347\220\206\351\200\273\350\276\221/\347\211\210\346\234\2541-html/tagfeed_\346\225\264\344\275\223\346\236\266\346\236\204.html"
new file mode 100644
index 0000000..6966efa
--- /dev/null
+++ "b/02DS/Ob/X-\346\234\254\346\234\272\347\224\250\346\263\225\345\244\207\345\277\230/01\345\205\250\345\261\200\350\207\252\345\212\250\345\214\226\347\264\242\345\274\225\344\270\216\346\225\264\347\220\206\351\200\273\350\276\221/\347\211\210\346\234\2541-html/tagfeed_\346\225\264\344\275\223\346\236\266\346\236\204.html"
@@ -0,0 +1,57 @@
+<svg viewBox="0 0 680 450" width="100%" role="img">
+ <title>tagfeed 工作流整体架构</title>
+ <desc>实时链与定时链共同驱动生成器 sync_0inbox.py,扫描 vault 排除目录与标签聚合目录,输出到 0inbox 收件箱</desc>
+ <defs>
+ <marker id="arrow" viewBox="0 0 10 10" refX="8" refY="5" markerWidth="6" markerHeight="6" orient="auto-start-reverse">
+ <path d="M2 1L8 5L2 9" fill="none" stroke="context-stroke" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
+ </marker>
+ </defs>
+
+ <text x="340" y="26" text-anchor="middle" font-size="16" font-weight="500" fill="#1f2937">tagfeed 工作流 · 整体架构</text>
+
+ <g class="node c-blue">
+ <rect x="40" y="50" width="280" height="66" rx="10" stroke-width="0.5"/>
+ <text class="th" x="180" y="74" text-anchor="middle" dominant-baseline="central" fill="#0C447C">实时触发链(即时 ~15s)</text>
+ <text class="ts" x="180" y="96" text-anchor="middle" dominant-baseline="central" fill="#185FA5">launchd → TagfeedWatch.app → tagfeed.py --watch</text>
+ </g>
+
+ <g class="node c-purple">
+ <rect x="360" y="50" width="280" height="66" rx="10" stroke-width="0.5"/>
+ <text class="th" x="500" y="74" text-anchor="middle" dominant-baseline="central" fill="#3C3489">定时兜底</text>
+ <text class="ts" x="500" y="96" text-anchor="middle" dominant-baseline="central" fill="#534AB7">WorkBuddy 自动化 · 每日 04:00</text>
+ </g>
+
+ <path d="M180 116 L300 148" stroke="#5F5E5A" stroke-width="1.5" fill="none" marker-end="url(#arrow)"/>
+ <path d="M500 116 L380 148" stroke="#5F5E5A" stroke-width="1.5" fill="none" marker-end="url(#arrow)"/>
+
+ <g class="node c-green">
+ <rect x="195" y="148" width="290" height="58" rx="10" stroke-width="0.5"/>
+ <text class="th" x="340" y="172" text-anchor="middle" dominant-baseline="central" fill="#27500A">sync_0inbox.py(生成器)</text>
+ <text class="ts" x="340" y="192" text-anchor="middle" dominant-baseline="central" fill="#3B6D11">扫描全 vault #tag · 幂等建页</text>
+ </g>
+
+ <g class="node c-gray">
+ <rect x="40" y="250" width="270" height="70" rx="10" stroke-width="0.5"/>
+ <text class="th" x="175" y="272" text-anchor="middle" dominant-baseline="central" fill="#444441">排除目录(不扫描)</text>
+ <text class="ts" x="175" y="294" text-anchor="middle" dominant-baseline="central" fill="#5F5E5A">X2.Archived · 00index · Knomo</text>
+ <text class="ts" x="175" y="310" text-anchor="middle" dominant-baseline="central" fill="#5F5E5A">X.Attachment · P3.bobo</text>
+ </g>
+
+ <g class="node c-amber">
+ <rect x="370" y="250" width="270" height="70" rx="10" stroke-width="0.5"/>
+ <text class="th" x="505" y="272" text-anchor="middle" dominant-baseline="central" fill="#633806">标签聚合/(规则2 输入)</text>
+ <text class="ts" x="505" y="294" text-anchor="middle" dominant-baseline="central" fill="#854F0B">已整理页 文件名 + targetTag</text>
+ <text class="ts" x="505" y="310" text-anchor="middle" dominant-baseline="central" fill="#854F0B">判定哪些 tag 已覆盖</text>
+ </g>
+
+ <path d="M175 250 L290 206" stroke="#888780" stroke-width="1" fill="none" stroke-dasharray="4 3" marker-end="url(#arrow)"/>
+ <path d="M505 250 L390 206" stroke="#BA7517" stroke-width="1" fill="none" stroke-dasharray="4 3" marker-end="url(#arrow)"/>
+
+ <path d="M340 206 L340 338" stroke="#3B6D11" stroke-width="1.8" fill="none" marker-end="url(#arrow)"/>
+
+ <g class="node c-amber">
+ <rect x="200" y="338" width="280" height="66" rx="10" stroke-width="0.5"/>
+ <text class="th" x="340" y="362" text-anchor="middle" dominant-baseline="central" fill="#633806">00index/0inbox(A 收件箱)</text>
+ <text class="ts" x="340" y="384" text-anchor="middle" dominant-baseline="central" fill="#854F0B">未整理标签 = 自动生成的 tagfeed 页</text>
+ </g>
+</svg>
\ No newline at end of file
diff --git "a/02DS/Ob/X-\346\234\254\346\234\272\347\224\250\346\263\225\345\244\207\345\277\230/01\345\205\250\345\261\200\350\207\252\345\212\250\345\214\226\347\264\242\345\274\225\344\270\216\346\225\264\347\220\206\351\200\273\350\276\221/\347\211\210\346\234\2541-html/\346\240\207\347\255\276\347\224\237\345\221\275\345\221\250\346\234\237_AB\344\270\244\347\256\261.html" "b/02DS/Ob/X-\346\234\254\346\234\272\347\224\250\346\263\225\345\244\207\345\277\230/01\345\205\250\345\261\200\350\207\252\345\212\250\345\214\226\347\264\242\345\274\225\344\270\216\346\225\264\347\220\206\351\200\273\350\276\221/\347\211\210\346\234\2541-html/\346\240\207\347\255\276\347\224\237\345\221\275\345\221\250\346\234\237_AB\344\270\244\347\256\261.html"
new file mode 100644
index 0000000..0c6df4e
--- /dev/null
+++ "b/02DS/Ob/X-\346\234\254\346\234\272\347\224\250\346\263\225\345\244\207\345\277\230/01\345\205\250\345\261\200\350\207\252\345\212\250\345\214\226\347\264\242\345\274\225\344\270\216\346\225\264\347\220\206\351\200\273\350\276\221/\347\211\210\346\234\2541-html/\346\240\207\347\255\276\347\224\237\345\221\275\345\221\250\346\234\237_AB\344\270\244\347\256\261.html"
@@ -0,0 +1,50 @@
+<svg viewBox="0 0 680 440" width="100%" role="img">
+ <title>标签生命周期:A 收件箱到 B 已整理</title>
+ <desc>A 收件箱中的未整理标签经用户拖拽进入 B 标签聚合,规则2 使其永不回 A,A 渐归零</desc>
+ <defs>
+ <marker id="arrow2" viewBox="0 0 10 10" refX="8" refY="5" markerWidth="6" markerHeight="6" orient="auto-start-reverse">
+ <path d="M2 1L8 5L2 9" fill="none" stroke="context-stroke" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
+ </marker>
+ </defs>
+
+ <text x="340" y="26" text-anchor="middle" font-size="16" font-weight="500" fill="#1f2937">标签生命周期:A 收件箱 → B 已整理</text>
+
+ <g class="node c-blue">
+ <rect x="60" y="48" width="560" height="40" rx="8" stroke-width="0.5"/>
+ <text class="ts" x="340" y="68" text-anchor="middle" dominant-baseline="central" fill="#0C447C">规则1:笔记出现新 #tag(且不在排除目录)→ 约 15s 内自动在 A 生成 tagfeed 页</text>
+ </g>
+
+ <g class="node c-amber">
+ <rect x="40" y="110" width="255" height="230" rx="12" stroke-width="0.5"/>
+ <text class="th" x="167" y="136" text-anchor="middle" dominant-baseline="central" fill="#633806">A · 00index/0inbox</text>
+ <text class="ts" x="167" y="156" text-anchor="middle" dominant-baseline="central" fill="#854F0B">未整理 · 待你归类</text>
+ <line x1="60" y1="168" x2="275" y2="168" stroke="#BA7517" stroke-width="0.5"/>
+ <text class="ts" x="72" y="192" fill="#712F0B">• UI</text>
+ <text class="ts" x="72" y="214" fill="#712F0B">• 电影</text>
+ <text class="ts" x="72" y="236" fill="#712F0B">• 写作点子</text>
+ <text class="ts" x="72" y="258" fill="#712F0B">• 密码</text>
+ <text class="ts" x="72" y="280" fill="#712F0B">• 需求</text>
+ <text class="ts" x="72" y="320" fill="#854F0B">随整理推进 → 渐归 0</text>
+ </g>
+
+ <path d="M298 225 L388 225" stroke="#3B6D11" stroke-width="2.5" fill="none" marker-end="url(#arrow2)"/>
+ <text class="th" x="343" y="212" text-anchor="middle" dominant-baseline="central" fill="#27500A">你拖拽整理</text>
+
+ <g class="node c-green">
+ <rect x="385" y="110" width="255" height="230" rx="12" stroke-width="0.5"/>
+ <text class="th" x="512" y="136" text-anchor="middle" dominant-baseline="central" fill="#27500A">B · 00index/标签聚合</text>
+ <text class="ts" x="512" y="156" text-anchor="middle" dominant-baseline="central" fill="#3B6D11">已整理 · 你的管理秩序</text>
+ <line x1="405" y1="168" x2="620" y2="168" stroke="#639922" stroke-width="0.5"/>
+ <text class="ts" x="408" y="192" fill="#173404">📁 1.5技能/UI.md</text>
+ <text class="ts" x="408" y="214" fill="#173404">📁 1.6玩软件/obsidian.md</text>
+ <text class="ts" x="408" y="236" fill="#173404">📁 5.人/谢大宝.md</text>
+ <text class="ts" x="408" y="258" fill="#173404">📁 2.2作品-软件/P-Aligner.md</text>
+ <text class="ts" x="408" y="280" fill="#173404">📁 …(57 个整理页)</text>
+ <text class="ts" x="408" y="320" fill="#3B6D11">只认「在不在 B」不认位置</text>
+ </g>
+
+ <g class="node c-green">
+ <rect x="60" y="372" width="560" height="40" rx="8" stroke-width="0.5"/>
+ <text class="ts" x="340" y="392" text-anchor="middle" dominant-baseline="central" fill="#27500A">规则2:进入 B 即「已覆盖」→ 永不回 A;A 越小 B 越全,最终 A 归 0</text>
+ </g>
+</svg>
\ No newline at end of file
diff --git "a/02DS/Ob/X-\346\234\254\346\234\272\347\224\250\346\263\225\345\244\207\345\277\230/01\345\205\250\345\261\200\350\207\252\345\212\250\345\214\226\347\264\242\345\274\225\344\270\216\346\225\264\347\220\206\351\200\273\350\276\221/\347\211\210\346\234\2541-html/\350\247\204\345\210\2312_\345\220\210\345\271\266\351\241\265\350\246\206\347\233\226.html" "b/02DS/Ob/X-\346\234\254\346\234\272\347\224\250\346\263\225\345\244\207\345\277\230/01\345\205\250\345\261\200\350\207\252\345\212\250\345\214\226\347\264\242\345\274\225\344\270\216\346\225\264\347\220\206\351\200\273\350\276\221/\347\211\210\346\234\2541-html/\350\247\204\345\210\2312_\345\220\210\345\271\266\351\241\265\350\246\206\347\233\226.html"
new file mode 100644
index 0000000..642f0b1
--- /dev/null
+++ "b/02DS/Ob/X-\346\234\254\346\234\272\347\224\250\346\263\225\345\244\207\345\277\230/01\345\205\250\345\261\200\350\207\252\345\212\250\345\214\226\347\264\242\345\274\225\344\270\216\346\225\264\347\220\206\351\200\273\350\276\221/\347\211\210\346\234\2541-html/\350\247\204\345\210\2312_\345\220\210\345\271\266\351\241\265\350\246\206\347\233\226.html"
@@ -0,0 +1,46 @@
+<svg viewBox="0 0 680 380" width="100%" role="img">
+ <title>规则2 进阶:一个合并页覆盖多个标签</title>
+ <desc>聚合页 UI.md 通过 targetTag 同时声明 UI 与 UI相关,两者均被覆盖,因此都不在 0inbox 出现</desc>
+ <defs>
+ <marker id="arrow3" viewBox="0 0 10 10" refX="8" refY="5" markerWidth="6" markerHeight="6" orient="auto-start-reverse">
+ <path d="M2 1L8 5L2 9" fill="none" stroke="context-stroke" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
+ </marker>
+ </defs>
+
+ <text x="340" y="26" text-anchor="middle" font-size="16" font-weight="500" fill="#1f2937">规则2 进阶:一个合并页覆盖多个标签</text>
+
+ <g class="node c-purple">
+ <rect x="40" y="60" width="300" height="120" rx="12" stroke-width="0.5"/>
+ <text class="th" x="190" y="86" text-anchor="middle" dominant-baseline="central" fill="#3C3489">1.5技能/UI.md</text>
+ <text class="ts" x="190" y="108" text-anchor="middle" dominant-baseline="central" fill="#534AB7">已整理页(含 DataviewJS)</text>
+ <rect x="60" y="124" width="260" height="36" rx="6" fill="#1e1b2e"/>
+ <text x="190" y="146" text-anchor="middle" dominant-baseline="central" font-size="12" fill="#a7f3d0" font-family="monospace">const targetTag = ["UI","UI相关"]</text>
+ </g>
+
+ <g class="node c-green">
+ <rect x="40" y="228" width="140" height="54" rx="10" stroke-width="0.5"/>
+ <text class="th" x="110" y="250" text-anchor="middle" dominant-baseline="central" fill="#27500A">UI ✓</text>
+ <text class="ts" x="110" y="270" text-anchor="middle" dominant-baseline="central" fill="#3B6D11">已覆盖</text>
+ </g>
+
+ <g class="node c-green">
+ <rect x="200" y="228" width="140" height="54" rx="10" stroke-width="0.5"/>
+ <text class="th" x="270" y="250" text-anchor="middle" dominant-baseline="central" fill="#27500A">UI相关 ✓</text>
+ <text class="ts" x="270" y="270" text-anchor="middle" dominant-baseline="central" fill="#3B6D11">已覆盖</text>
+ </g>
+
+ <path d="M150 180 L110 226" stroke="#534AB7" stroke-width="1.5" fill="none" marker-end="url(#arrow3)"/>
+ <path d="M250 180 L270 226" stroke="#534AB7" stroke-width="1.5" fill="none" marker-end="url(#arrow3)"/>
+
+ <g class="node c-amber">
+ <rect x="400" y="110" width="240" height="170" rx="12" stroke-width="0.5"/>
+ <text class="th" x="520" y="136" text-anchor="middle" dominant-baseline="central" fill="#633806">00index/0inbox</text>
+ <text class="ts" x="520" y="162" text-anchor="middle" dominant-baseline="central" fill="#854F0B">两者都不出现:</text>
+ <text class="ts" x="520" y="188" text-anchor="middle" dominant-baseline="central" fill="#712F0B">✗ 无 UI.md</text>
+ <text class="ts" x="520" y="210" text-anchor="middle" dominant-baseline="central" fill="#712F0B">✗ 无 UI相关.md</text>
+ <text class="ts" x="520" y="244" text-anchor="middle" dominant-baseline="central" fill="#854F0B">修复前:UI相关 反复再生</text>
+ <text class="ts" x="520" y="264" text-anchor="middle" dominant-baseline="central" fill="#854F0B">修复后:targetTag 命中即抑制</text>
+ </g>
+
+ <path d="M340 140 L398 165" stroke="#BA7517" stroke-width="2" fill="none" marker-end="url(#arrow3)"/>
+</svg>
\ No newline at end of file
diff --git "a/02DS/Ob/\345\220\214\346\255\245\344\270\216\350\277\234\347\250\213/ignis-\350\256\251\345\216\237\347\211\210 Obsidian \351\232\217\345\244\204\345\217\257\347\224\250-\346\265\217\350\247\210\345\231\250\350\256\277\351\227\256\350\207\252\345\212\250\345\220\214\346\255\245.md" "b/02DS/Ob/\345\220\214\346\255\245\344\270\216\350\277\234\347\250\213/ignis-\350\256\251\345\216\237\347\211\210 Obsidian \351\232\217\345\244\204\345\217\257\347\224\250-\346\265\217\350\247\210\345\231\250\350\256\277\351\227\256\350\207\252\345\212\250\345\220\214\346\255\245.md"
index a62645d..f44dcc3 100644
--- "a/02DS/Ob/\345\220\214\346\255\245\344\270\216\350\277\234\347\250\213/ignis-\350\256\251\345\216\237\347\211\210 Obsidian \351\232\217\345\244\204\345\217\257\347\224\250-\346\265\217\350\247\210\345\231\250\350\256\277\351\227\256\350\207\252\345\212\250\345\220\214\346\255\245.md"
+++ "b/02DS/Ob/\345\220\214\346\255\245\344\270\216\350\277\234\347\250\213/ignis-\350\256\251\345\216\237\347\211\210 Obsidian \351\232\217\345\244\204\345\217\257\347\224\250-\346\265\217\350\247\210\345\231\250\350\256\277\351\227\256\350\207\252\345\212\250\345\220\214\346\255\245.md"
@@ -1,13 +1,13 @@
---
-title: "ignis:让原版 Obsidian 随处可用——浏览器访问,自动同步"
-source: "https://mp.weixin.qq.com/s/X2awWXlNLKR5qsglOkXYOg"
+title: ignis:让原版 Obsidian 随处可用——浏览器访问,自动同步
+source: https://mp.weixin.qq.com/s/X2awWXlNLKR5qsglOkXYOg
author:
- "[[处之]]"
published:
created: 2026-08-14
-description: "ignis 将原版 Obsidian 带到了浏览器。不是远程桌面方案,而是真正的 Web 应用,保留了原版的编辑体验,支持绝大多数功能和插件,并能自动同步数据,让你在任何浏览器中无缝继续创作。本文将带你自部署 ignis,并配置 Nginx 反代。"
-tags:
- - "clippings","ob","obsidian"
+description: ignis 将原版 Obsidian 带到了浏览器。不是远程桌面方案,而是真正的 Web 应用,保留了原版的编辑体验,支持绝大多数功能和插件,并能自动同步数据,让你在任何浏览器中无缝继续创作。本文将带你自部署 ignis,并配置 Nginx 反代。
+words:
+ 2026-08-24: 1082
---
处之 承飞之咎 *2026年7月12日 18:20*
diff --git "a/02DS/Ob/\346\217\222\344\273\266Notechain/26.0814-2228-Notechain.md" "b/02DS/Ob/\346\217\222\344\273\266Notechain/26.0814-2228-Notechain.md"
index d7c9915..be42fa3 100644
--- "a/02DS/Ob/\346\217\222\344\273\266Notechain/26.0814-2228-Notechain.md"
+++ "b/02DS/Ob/\346\217\222\344\273\266Notechain/26.0814-2228-Notechain.md"
@@ -1,3 +1,7 @@
+---
+words:
+ 2026-08-14: 1237
+---
diff --git "a/02DS/\345\267\245\345\205\267\344\270\216\346\212\200\350\203\275/git/2026-08-25-\346\234\254\346\234\272ob\346\234\254\344\273\223\345\272\223git\345\220\257\347\224\250\345\244\207\345\277\230.md" "b/02DS/\345\267\245\345\205\267\344\270\216\346\212\200\350\203\275/git/2026-08-25-\346\234\254\346\234\272ob\346\234\254\344\273\223\345\272\223git\345\220\257\347\224\250\345\244\207\345\277\230.md"
new file mode 100644
index 0000000..6980f7b
--- /dev/null
+++ "b/02DS/\345\267\245\345\205\267\344\270\216\346\212\200\350\203\275/git/2026-08-25-\346\234\254\346\234\272ob\346\234\254\344\273\223\345\272\223git\345\220\257\347\224\250\345\244\207\345\277\230.md"
@@ -0,0 +1,37 @@
+---
+words:
+ 2026-08-25: 416
+tags:
+ - git
+---
+**Obsidian Git 已启用并接上你的服务器,首次备份已推送成功!**
+
+## 完成的事
+
+**1. 首次提交 + 推送(核心)**
+
+- 远程:`origin →` [`https://git.boborobots.com/r/crazy-macs/obsidianar.git`](https://git.boborobots.com/r/crazy-macs/obsidianar.git)
+- 凭据:本机 **macOS Keychain 里已有** git.boborobots.com 的账号(`juzi`),我配置了 `credential.helper = osxkeychain` 直接复用,全程没要你输密码
+- **503 个文件、约 44MB** 完成首提交,已推到服务器的 `main` 分支(远程原来是个空仓库,已建好 main)
+
+**2. .gitignore 补了隐藏目录**(和你 tagfeed 的排除逻辑一致)
+
+- 新增:`.agents/ .claude/ .copilot/ .opencode/ .smart-env/ .workbuddy/ .trash/`(把 545 个候选文件里的 42 个工具配置/缓存/废纸篓文件剔除了,`.copilot` 有 4.1M 对话数据)
+
+**3. 插件配置调整**(`data.json`)
+
+|项|原值|新值|
+|---|---|---|
+|autoPushInterval|0(永不推送)|**20 分钟**|
+|autoPullOnBoot|false|**true**|
+|autoBackupAfterFileChange|true|true(保持)|
+|pullBeforePush|true|true(保持)|
+
+效果:**笔记一改动就自动 commit,每 20 分钟自动 push 到你的服务器**,打开 Obsidian 时自动 pull 一次(多设备安全)。
+
+## ⚠️ 你需要做的
+
+1. Obsidian 里 **`Cmd+R` 重载**一下(让插件读到新的自动推送配置)
+2. 可选:打开 **设置 → 第三方插件 → Git**,确认状态栏出现分支/同步信息;如果插件提示需要填凭据(一般不用,Keychain 已覆盖),在插件设置里填一次 `juzi` + 你的令牌
+
+目前工作区有 2 个文件(`X0.Diary/26.0825.md`、`X1.notes` 一篇)是刚被 Obsidian 实时改动的,属正常,下个自动周期就会提交。以后你的笔记就是**真·版本库 + 服务器双保险**了。
\ No newline at end of file
diff --git "a/X0.Clippings/\345\256\230\347\275\221\345\201\232\345\256\214\345\210\253\345\217\252\346\224\276\346\210\252\345\233\276\357\274\232Codex + Remotion \345\201\232\344\272\206\344\270\200\346\235\241 24 \347\247\222\344\272\247\345\223\201\346\274\224\347\244\272.md" "b/X0.Clippings/\345\256\230\347\275\221\345\201\232\345\256\214\345\210\253\345\217\252\346\224\276\346\210\252\345\233\276\357\274\232Codex + Remotion \345\201\232\344\272\206\344\270\200\346\235\241 24 \347\247\222\344\272\247\345\223\201\346\274\224\347\244\272.md"
index 3c19a95..8dfe2f9 100644
--- "a/X0.Clippings/\345\256\230\347\275\221\345\201\232\345\256\214\345\210\253\345\217\252\346\224\276\346\210\252\345\233\276\357\274\232Codex + Remotion \345\201\232\344\272\206\344\270\200\346\235\241 24 \347\247\222\344\272\247\345\223\201\346\274\224\347\244\272.md"
+++ "b/X0.Clippings/\345\256\230\347\275\221\345\201\232\345\256\214\345\210\253\345\217\252\346\224\276\346\210\252\345\233\276\357\274\232Codex + Remotion \345\201\232\344\272\206\344\270\200\346\235\241 24 \347\247\222\344\272\247\345\223\201\346\274\224\347\244\272.md"
@@ -11,6 +11,8 @@
- 动画
- codex
- remotion
+words:
+ 2026-07-17: 1848
---
大拓未来 大拓未来科技 *2026年7月15日 22:45*
diff --git "a/X0.Clippings/\346\212\230\350\205\276\344\272\206\344\270\200\345\234\210\357\274\214\346\210\221\347\216\260\345\234\250\350\277\231\346\240\267\345\234\250 Obsidian \351\207\214\347\256\241\347\220\206\344\273\273\345\212\241.md" "b/X0.Clippings/\346\212\230\350\205\276\344\272\206\344\270\200\345\234\210\357\274\214\346\210\221\347\216\260\345\234\250\350\277\231\346\240\267\345\234\250 Obsidian \351\207\214\347\256\241\347\220\206\344\273\273\345\212\241.md"
new file mode 100644
index 0000000..18f3af3
--- /dev/null
+++ "b/X0.Clippings/\346\212\230\350\205\276\344\272\206\344\270\200\345\234\210\357\274\214\346\210\221\347\216\260\345\234\250\350\277\231\346\240\267\345\234\250 Obsidian \351\207\214\347\256\241\347\220\206\344\273\273\345\212\241.md"
@@ -0,0 +1,244 @@
+---
+title: 折腾了一圈,我现在这样在 Obsidian 里管理任务
+source: https://mp.weixin.qq.com/s/6H1NawbIsKPoVZpeSKAznw
+author:
+ - "[[vran]]"
+published:
+created: 2026-08-25
+description: 小白也能学会,超简单的 obsidian 任务管理方法
+tags:
+ - clippings
+words:
+ 2026-08-25: 2963
+---
+vran vran *2026年8月22日 19:42*
+
+## 0\. 超简单的任务管理思路
+
+最近在 Obsidian 里实践了一套新的任务管理方法,方法很简单,但明显感觉工作和生活更有秩序了。
+
+- 一天一个文件,文件名用 `2026-01-01` 这种日期格式
+- 今天要做的事写在今天,未来要做的事就写在计划执行那天
+- 不要求每天把任务清空,但要做的事情一定要记下来
+- 任务里直接用双链关联相关的人、项目或资料
+- 真有分类或统计需求时,再给任务加标签
+
+> 🤔 因为每多一个标签,就多一分维护成本。
+
+比如
+
+```
+1- [ ] 完成 [[2026 年终汇报.ppt]],发给 @[[张三]] 确认 #优先级/高
+```
+
+## 1\. 在 obsidian 里把这套流程搭起来
+
+上面的规则很简单,但想在 Obsidian 里用得顺手,还有两个问题要解决:
+
+1. 能随手记录今天的内容
+2. 能集中查看和管理任务
+
+方案有很多,但我更希望整个过程简单一点,尽量少折腾代码,所以这次选择用 Components 的数据库 `XDB` 来进行实践。
+
+下面是成品图
+
+
+
+结构拆解如下
+
+
+
+## 2\. 开始之前
+
+接下来就用 Components 插件的 `XDB 数据库` 把这套流程搭起来,开始前先准备两件事:
+
+- 安装并启用最新版 Components
+- 创建一个 `.xdb` 文件
+
+> 插件官网: https://cp.cc1234.cc
+
+第一步,通过快捷键 `ctrl+p` (mac 是 command+p) 打开命令面板,搜索并执行 `Components: 创建 XDB 数据库` 命令来快速创建
+
+
+
+第二步,创建好后插件会自动打开文件,点击右上角的菜单将布局从默认的「标签页」切换到「仪表盘」
+
+
+
+现在舞台搭好了,正戏开始。
+
+## 3\. 用日期页记录每天的内容
+
+按照前面的规则,每天都会有一个对应的日期文件,今天的内容写在今天,未来要做的事情就提前写到计划执行那天。
+
+但每次记录时,都要先在 obsidian 的目录树里找当天文件,如果不存在还得手动创建,次数多了就有点麻烦。
+
+所以我更希望 **有一个固定入口:每天打开就是今天的文件,直接就能写;想看其他日期时,也能快速切过去。**
+
+这种用法刚好可以用 `XDB` 的「日期页」来实现。
+
+
+
+「日期页」的创建流程很简单
+
+- 点击 `xdb` 文件右上角的 `+` 按钮
+- 在搜索框中输入「日期」
+- 点击「日期页」卡片即可创建
+
+
+创建完成后,简单调整一下位置和大小。默认的表格视图这里用不到,可以顺手删掉。
+
+
+
+接着设置存放日期文件的文件夹。点击「选择其他文件夹」,输入 `inbox` 并按下回车就配置好了。
+
+这样每次打开日期页时,今天的文件已经存在就直接打开,还没有的话就自动在 `inbox` 中创建一个。
+
+> 小提示: 目录下的文件名必须是 YYYY-MM-DD 的格式才能被日期页识别并和对应日期关联,例如 `2026-01-01` 。
+
+
+
+## 4\. 用任务数据库集中管理任务
+
+日期页解决了快速记录的问题,但很多任务当天并不会完成,时间一长,它们就会散在不同的日期文件里。想知道还有哪些没完成,还是得一个个翻,很麻烦。
+
+Tasks、Dataview 等插件也能做这类汇总,不过需要写查询语句。这里我直接用 `XDB` 的任务数据库,筛选起来更直观,而且任务可以直接在表格里编辑。
+
+
+
+任务库需要单独创建一个 XDB 文件(参考第 2 节)
+
+- 首先新建一个 XDB 文件,文件名可以叫 `任务库`
+- 然后调整筛选设置,将来源从 `文件` 切换到 `任务`
+- 再加一个筛选条件: `文件所在目录` -> `等于` -> `inbox` (这是第三步日期页设置的目录)
+- 添加 `content` 属性,用来显示和编辑任务内容
+
+> 小提示:前面的 XDB 还要继续使用「文件」作为数据来源,所以这里把任务库单独建成一个 XDB,再通过引用视图放回来。
+
+
+
+
+
+20260821100841
+
+接着回到前面的 XDB,创建一个「引用视图」,关联 `任务库.xdb` 。
+
+
+
+这样 `inbox` 下的任务就能集中到一个表格里了。任务还是照常写在每天的日期文件里,需要管理时再统一到这里看。
+
+
+
+## 5\. 用日历查看接下来的安排
+
+任务集中到一起以后,查还有哪些没完成已经方便很多了。但我平时还会经常 **关注未来几天都安排了什么事情。**
+
+前面已经把未来要做的事情写到了计划执行那天,所以这些安排本身已经有了,现在只差一个更直观的查看方式。
+
+这时候 `XDB` 的日历视图就很合适了
+
+
+
+日历视图的创建流程和「日期页」一样,区别在于配置不同
+
+创建后先把「开始日期字段」设置为「文件名」。因为我们的日期文件本身就是 `YYYY-MM-DD` 格式,所以文件名刚好可以直接作为日历日期。
+
+
+
+接下来再补三个设置:
+
+- 筛选: `文件所在目录 → 等于 → inbox`
+- 添加 `任务清单` 属性(英文名:file.tasks)
+- 把默认的「周」视图切换成「月」视图
+
+> **任务清单属性** :可以直接显示文件里的任务和完成进度,点击后还能展开查看所有任务。
+
+
+
+20260821100853
+
+直接展开任务清单面板的话,效果是这样
+
+
+
+## 6\. 看看最近执行得怎么样
+
+虽然不要求每天把任务清空,但还是可以用几个简单的指标看看最近的执行情况,方便后面复盘和调整。
+
+比如我现在比较关注两个指标:
+
+- 总完成率: 目标不低于 80%。如果长期偏低,就回头看看是不是最近加了太多想法,哪些该优先做,哪些可以取消或归档
+- 当月未完成任务: 主要用来看看这个月留下来的任务是不是开始变多了,如果明显增加,我会回头整理一下哪些还值得继续做
+
+
+确定好要看的指标以后,剩下就是把它们加到页面里。
+
+「指标」的创建方式和前面的视图差不多,这里就不重复讲了,直接给出我现在用的配置。
+
+> 指标视图最好都加一个筛选条件: `文件所在目录 → 等于 → inbox`
+
+
+
+下面的代码等你有需要的时候再来复制就行
+
+```
+123456// 总完成率统计代码
+
+(count($items.flatMap(f => f.file.tasks).filter(t => t.status !== " ")) / count($items.flatMap(f => f.file.tasks))).toFixed(2) * 100 + "%"
+
+// 待办数统计代码
+(count($items.flatMap(f => f.file.tasks).filter(t => t.status === " ")))
+```
+
+本月待办的配置截图如下
+
+
+
+## 7\. 彩蛋篇,颜值美化
+
+到这里,记录、任务管理和数据反馈都有了,这套流程已经完全能用了,就是页面看起来还比较朴素。
+
+那有没有 **简单一点、效果又明显的美化方法** 呢?有的,只需要在顶部加一个「 **时间横幅** 」视图,再配一张自己喜欢的图片,整个页面的感觉就出来了。
+
+
+
+我直接把我的配置页截图和用到的图片素材地址放下面了
+
+> 壁纸: https://wallpapercave.com/download/desktop-city-lights-wallpapers-wp16391030
+
+
+
+20260822140849
+
+如果你对颜值还有更高的要求,我会推荐你下载 `style-context` 插件(在插件市场可获取,作者是 Moy),该插件可以将图片设置为整个 Obsidian 页面的背景图,效果如下
+
+
+
+20260822150827
+
+下面是 style context 插件的配置图
+
+
+
+20260822150803
+
+为了让效果更好,我建议在 `xdb` 的视图样式中将背景色的透明度调低(亮色和暗色都需要单独配置哦)
+
+
+
+
+
+## 8\. AI 与未来
+
+AI 现在也是绕不开的话题。
+
+这套系统本身足够简单,所以也很适合让 AI 参与进来。另外 Components 的 `XDB` 本身也提供了专门的 Skills,可以让 AI 直接基于现有的数据和能力来扩展工作流。
+
+- 比如直接让 AI 统计 `inbox` 目录下最近一周做过的事情,然后生成周报
+- 也可以让 AI 根据现有数据添加更直观的图表,帮助观察最近的状态
+
+但刚开始,我还是建议先用起来,形成记录的习惯。遇到问题了,再去和 AI 讨论解决方案,优化过后继续用一段时间,如此循环,而不是一开始就设计一个完美的系统。
+
+**微信扫一扫赞赏作者**
+
+obsidian · 目录
\ No newline at end of file
diff --git "a/X0.Clippings/\350\276\276\345\210\251\346\254\247\346\234\200\346\226\260\351\225\277\346\226\207\357\274\2322026\357\274\214\345\203\217\346\236\201\344\272\206 1929-yeeyi.md" "b/X0.Clippings/\350\276\276\345\210\251\346\254\247\346\234\200\346\226\260\351\225\277\346\226\2072026\345\203\217\346\236\201\344\272\2061929.md"
similarity index 97%
rename from "X0.Clippings/\350\276\276\345\210\251\346\254\247\346\234\200\346\226\260\351\225\277\346\226\207\357\274\2322026\357\274\214\345\203\217\346\236\201\344\272\206 1929-yeeyi.md"
rename to "X0.Clippings/\350\276\276\345\210\251\346\254\247\346\234\200\346\226\260\351\225\277\346\226\2072026\345\203\217\346\236\201\344\272\2061929.md"
index 658c3b3..e0a7687 100644
--- "a/X0.Clippings/\350\276\276\345\210\251\346\254\247\346\234\200\346\226\260\351\225\277\346\226\207\357\274\2322026\357\274\214\345\203\217\346\236\201\344\272\206 1929-yeeyi.md"
+++ "b/X0.Clippings/\350\276\276\345\210\251\346\254\247\346\234\200\346\226\260\351\225\277\346\226\2072026\345\203\217\346\236\201\344\272\2061929.md"
@@ -1,12 +1,14 @@
---
-title: "达利欧最新长文:2026,像极了 1929-yeeyi"
-source: "https://www.yeeyi.com/news/details/3177503/"
+title: 达利欧最新长文:2026,像极了 1929-yeeyi
+source: https://www.yeeyi.com/news/details/3177503/
author:
published:
created: 2026-08-04
-description: "来源:商业访谈播客《The Diary Of A CEO》2026年年中,全球AI基建投资突破万亿美元门槛,英伟达市值站稳3万亿,但企业端的AI落地回报率仍是一"
+description: 来源:商业访谈播客《The Diary Of A CEO》2026年年中,全球AI基建投资突破万亿美元门槛,英伟达市值站稳3万亿,但企业端的AI落地回报率仍是一
tags:
- - "clippings"
+ - clippings
+words:
+ 2026-08-04: 5165
---
## 达利欧最新长文:2026,像极了 1929
diff --git a/X0.Diary/26.0322.md b/X0.Diary/26.0322.md
index 8486387..10919d6 100755
--- a/X0.Diary/26.0322.md
+++ b/X0.Diary/26.0322.md
@@ -1,3 +1,7 @@
+---
+words:
+ 2026-04-05: 159
+---
# GTD
- #bobo
diff --git a/X0.Diary/26.0323.md b/X0.Diary/26.0323.md
index d4ecfa1..798c587 100755
--- a/X0.Diary/26.0323.md
+++ b/X0.Diary/26.0323.md
@@ -1,7 +1,12 @@
+---
+words:
+ 2026-08-14: 108
+ 2026-08-25: 109
+---
# GTD
- #bobo #谋事
- - [ ] 整理项目进度 <font color=#aeb5bd>填写并整理当前的所有项目进度,以以及工作安排。周一晚上开会</font>
+ - [x] 整理项目进度 <font color=#aeb5bd>填写并整理当前的所有项目进度,以以及工作安排。周一晚上开会</font>
- [x] 做融资股份安排计划 <font color=#aeb5bd>本质是股权激励方案</font>
- #bobo #做事
diff --git a/X0.Diary/26.0324.md b/X0.Diary/26.0324.md
index 0656f1f..45d706a 100755
--- a/X0.Diary/26.0324.md
+++ b/X0.Diary/26.0324.md
@@ -1,3 +1,8 @@
+---
+words:
+ 2026-05-09: 282
+ 2026-08-25: 284
+---
# Misc
@@ -5,7 +10,7 @@
# ddup
-- 命令行一键把所有音量调大50%
+- #命令行 一键把所有音量调大50% #ffmpeg
- ```
cd "/Users/ar/Desktop/06-催眠红茶-4分-up50" && for f in *.mp3; do ffmpeg -i "$f" -filter:a "volume=1.5" "loud_$f"; done
@@ -20,4 +25,4 @@
- unMix vocal remover 也有一个选项可以分离不同的人声(但它也还在测试版,所以需要耐心)
- 在线工具MVSEP<font color=#aeb5bd> --有很多模型可以选不收钱,每日免费额度50,但需要排队,交了钱可以更快。</font>
- https://mvsep.com/en/home#
- - ![[Pasted image 20260324145618.png]]
\ No newline at end of file
+ - ![[26.0324_image_1.png]]
\ No newline at end of file
diff --git a/X0.Diary/26.0718.md b/X0.Diary/26.0718.md
index e23598b..69e468d 100755
--- a/X0.Diary/26.0718.md
+++ b/X0.Diary/26.0718.md
@@ -1,3 +1,7 @@
+---
+words:
+ 2026-08-24: 131
+---
# MemoDaily
-
diff --git a/X0.Diary/26.0804.md b/X0.Diary/26.0804.md
index d1cca8b..df07c16 100755
--- a/X0.Diary/26.0804.md
+++ b/X0.Diary/26.0804.md
@@ -1,3 +1,7 @@
+---
+words:
+ 2026-08-24: 812
+---
# GTD
- #整理自己
@@ -74,6 +78,6 @@
- 16:04 ✏️ [[用 Three.js与GPT-5.pdf]]
- 📝 **笔记** · 2 项
- 17:19 ✏️ [[Memos-2026-08]]
- - 13:59 ✏️ [[达利欧最新长文:2026,像极了 1929-yeeyi]]
+ - 13:59 ✏️ [[达利欧最新长文2026像极了1929]]
<!-- daily-activity:end -->
diff --git a/X0.Diary/26.0819.md b/X0.Diary/26.0819.md
index fb392b5..d6d03c0 100644
--- a/X0.Diary/26.0819.md
+++ b/X0.Diary/26.0819.md
@@ -17,7 +17,6 @@
- 使用方法-通过Chatbox直接用
-
-
## 用法-Qwen3.8
```bash
@@ -35,21 +34,9 @@
```
-
##
--- **总结** | 项目 | 内容 | |------|------| | **五常内容** | 仁、义、礼、智、信 | | **最早系统提出** | 战国荀子(概念雏形),西汉董仲舒(五行配属,理论确立) | | **最早命名出处** | 东汉《白虎通义》正式称“五常” | | **最完整表述** | 董仲舒五行配属 + 宋明理学“天理”阐释(仁义礼智信为天理在人间的体现) | | **内涵变化** | **有显著变化**:从先秦的个人道德修养 → 两汉的政治宇宙论工具 → 宋明的天理绝对化 → 清代的制度僵化 → 近现代的批判与现代化重构 | “五常”本身作为道德范畴具有跨时代的生命力,但其**社会功能、哲学基础和实践要求**在不同历史时期经历了从“自由道德”到“政治律令”再到“天理规范”的转变。
-
-
-<!-- daily-activity:begin (tagfeed 自动生成,勿手改此块内) -->
-## 🕐 活动足迹(自动记录)
-
-> ✏️ = 编辑过 · 👀 = 只打开/碰过,内容没变
-
-- 🖼 **白板** · 1 项
- - 10:44 ✏️ [[00index.canvas]]
-
-<!-- daily-activity:end -->
# MemoDaily
- 13:59:31 做公众账号特别好的一个主题是穿越迷雾或飞越迷雾
@@ -88,3 +75,13 @@
- 19:44:23 别看三纲五常,三纲在五常的前面,但实际上先有五常,后有的三纲。五常是孔孟时代就有的,三纲才是董仲舒那时候才开始有的。。而且三纲根本不是儒家的理论,其实它是法家的理论.
- 19:58:55 悖论 你以为语言是帮助人沟通的,但人与人沟通过程中,最高的墙恰恰是语言。
- 20:02:19 这应该是共产主义社会最主流的新闻--
+
+<!-- daily-activity:begin (tagfeed 自动生成,勿手改此块内) -->
+## 🕐 活动足迹(自动记录)
+
+> ✏️ = 编辑过 · 👀 = 只打开/碰过,内容没变
+
+- 🖼 **白板** · 1 项
+ - 10:44 ✏️ [[00index.canvas]]
+
+<!-- daily-activity:end -->
diff --git a/X0.Diary/26.0825.md b/X0.Diary/26.0825.md
index a0b5f2f..d2aa05c 100644
--- a/X0.Diary/26.0825.md
+++ b/X0.Diary/26.0825.md
@@ -1,6 +1,6 @@
---
words:
- 2026-08-25: 867
+ 2026-08-25: 1010
---
# 26.0825
@@ -9,6 +9,8 @@
## GTD
+- 工作流建设备忘 #工作流 #ob
+ - [x] [[01全局自动化索引与整理逻辑]]
- #软件研究 #音频处理
- [x] TitanVault 体验试用
- [x] 发现不能用
@@ -16,15 +18,15 @@
- [x] 记录以后的使用须知(见本页)
- #工作流 继续完善本机工作流
- [ ] 体验OpenConnector %%旨在:减少AI工具的安全隐患,减少token消耗%%
- - [x] 了解是什么 [[2026-09-01-开源项目openconnector介绍]]
+ - [x] 了解是什么 [[2026-08-25-开源项目openconnector介绍]]
- 给codex做安装
- [ ] 让codex自己去装 %%01a0377f-c6a2-7191-af66-1b008ec4a362%%
- [x] 部署任务给他
- [ ] 验收结果
- 跑下具体相关的任务试试
- - [ ] 给obsidian配上git %%防止文件丢失,为优化知识管理铺路%% #ob
- - [ ] 找到小蔡给的帐号
- - [ ] 给ob配置上
+ - [x] 给obsidian配上git %%防止文件丢失,为优化知识管理铺路%% #ob
+ - [x] 找到小蔡给的帐号
+ - [x] 给ob配置上
- [x] 继续完善自动索引00index #ob #工作流 #自动化
- 排除逻辑发现1个缺陷
- 在tagfeed中添加的别名标签
@@ -59,36 +61,42 @@
> ✏️ = 编辑过 · 👀 = 只打开/碰过,内容没变
- 🖼 **白板** · 4 项
- - 13:57 ✏️ [[00index.canvas]]
+ - 16:06 ✏️ [[00index.canvas]]
- 13:48 ✏️ [[Drawing 26.0329叫床相关.excalidraw]]
- 10:51 ✏️ [[Drawing25.03DY打官司.excalidraw]]
- 10:40 ✏️ [[Drawing 26.0822ob和ai拆书.excalidraw]]
-- 📝 **笔记** · 25 项
+- 📝 **笔记** · 35 项
+ - 16:15 ✏️ [[2026-08-25-watch机制是怎么实现的]]
+ - 15:55 ✏️ [[以利为利.求索住房与土地财政之路]]
+ - 15:35 ✏️ [[01全局自动化索引与整理逻辑]]
+ - 15:17 ✏️ [[折腾了一圈,我现在这样在 Obsidian 里管理任务]]
+ - 15:07 ✏️ [[2026-08-25-tagfeed核心逻辑备忘]]
+ - 15:03 ✏️ [[00index.canvas生成原理说明]]
+ - 14:58 ✏️ [[官网做完别只放截图:Codex + Remotion 做了一条 24 秒产品演示]]
+ - 14:58 ✏️ [[达利欧最新长文2026像极了1929]]
+ - 14:53 ✏️ [[26.0814-2228-Notechain]]
+ - 14:52 ✏️ [[ignis-让原版 Obsidian 随处可用-浏览器访问自动同步]]
+ - 14:45 ✏️ [[2026-08-25-本机ob本仓库git启用备忘]]
+ - 14:42 ✏️ [[2026-08-25-开源项目openconnector介绍]]
- 14:36 ✏️ [[Memos-2026-08]]
- - 14:27 ✏️ [[2026-09-02-Git安装备忘]]
- 14:25 ✏️ [[git]]
- 13:47 ✏️ [[02DS/02copilot/skills/json-canvas/SKILL|SKILL]]
- - 13:33 ✏️ [[2026-09-01-开源项目openconnector介绍]]
- - 12:58 ✏️ [[2026-08-31-命运-麦家的观点]]
+ - 12:58 ✏️ [[2026-08-25-命运-麦家的观点]]
- 11:38 ✏️ [[2026-08-23-尺蠖效应]]
- 11:31 ✏️ [[26.0716-1220-新点子-智能提炼UIKIT]]
- 11:11 ✏️ [[安装记录]]
- 11:11 ✏️ [[2.0805安装录音转写Titanvault]]
- 11:01 ✏️ [[Everything You Need to Ace Computer Science and Coding in One Big Fat Notebook]]
- - 11:00 ✏️ [[2026-08-30-法家发明的户籍制度和收容制度]]
+ - 11:00 ✏️ [[2026-08-25-法家发明的户籍制度和收容制度]]
- 10:57 ✏️ [[刘瑜谈韩国经验@20260824_195603]]
- 10:51 ✏️ [[Task_board_note]]
- 10:50 ✏️ [[26.08-深圳公司-备案-实际收益人]]
- - 10:49 ✏️ [[2026-08-29-chatgpt一个很酷的用法-无限个人导师]]
+ - 10:49 ✏️ [[2026-08-25-chatgpt一个很酷的用法-无限个人导师]]
- 10:40 ✏️ [[05-专题-UI-视觉设计能力]]
- 10:40 ✏️ [[02DS/02copilot/skills/obsidian-markdown/SKILL|SKILL]]
- 10:40 ✏️ [[02DS/02copilot/skills/obsidian-bases/SKILL|SKILL]]
- 10:29 ✏️ [[插件Notechain]]
- - 10:09 ✏️ [[2026-08-24-世人行为的最底层逻辑]]
- - 09:46 ✏️ [[2026-08-28-Ollama-命令行与客户端的关系]]
- - 00:45 ✏️ [[2026-08-27-bug修复-每日自动足迹的产生包含了不该有的tagfeed内容]]
- - 00:43 ✏️ [[Memos-2026-07]]
- - 00:18 ✏️ [[X-刘庆]]
+ - …其余 5 项省略
<!-- daily-activity:end -->
diff --git a/X1.Knomo/_knomo-system/indexes/memo-index-2026-08.json b/X1.Knomo/_knomo-system/indexes/memo-index-2026-08.json
index bf76141..dfb3b06 100644
--- a/X1.Knomo/_knomo-system/indexes/memo-index-2026-08.json
+++ b/X1.Knomo/_knomo-system/indexes/memo-index-2026-08.json
@@ -1,7 +1,7 @@
{
"schemaVersion": 2,
"period": "2026-08",
- "updatedAt": "2026-08-25T06:36:16.379Z",
+ "updatedAt": "2026-08-25T06:46:25.137Z",
"memos": {
"2026080414154132": {
"id": "2026080414154132",
@@ -2241,7 +2241,7 @@
"2026082510582128": {
"id": "2026082510582128",
"createdAt": "2026-08-25T10:58:21.726+08:00",
- "updatedAt": "2026-08-25T06:36:16.376Z",
+ "updatedAt": "2026-08-25T06:46:25.136Z",
"contentSnapshot": "#法家 法家思想(韩非子),其核心是强调中央集权、严格的法律约束和对个体行为的精细化管理",
"contentHash": "fnv1a-1918da11",
"status": "active",
@@ -2256,7 +2256,7 @@
"references": [],
"sourceMemoId": null,
"issue": null,
- "lastMarkdownSyncAt": "2026-08-25T06:36:16.376Z",
+ "lastMarkdownSyncAt": "2026-08-25T06:46:25.136Z",
"lastMarkdownSyncSource": "file_watch",
"dailyRef": {
"path": "X0.Diary/26.0825.md",
@@ -2265,7 +2265,7 @@
"lastKnownBlock": "- 10:58:21 #法家 法家思想(韩非子),其核心是强调中央集权、严格的法律约束和对个体行为的精细化管理",
"lastKnownHash": "fnv1a-57605bbd",
"lineNumberHint": 96,
- "lastSyncedAt": "2026-08-25T06:36:16.364Z"
+ "lastSyncedAt": "2026-08-25T06:46:25.135Z"
},
"monthlyRef": {
"path": "X1.Knomo/Memos-2026-08.md",
@@ -2273,7 +2273,7 @@
"lastKnownBlock": "- 10:58:21 #法家 法家思想(韩非子),其核心是强调中央集权、严格的法律约束和对个体行为的精细化管理",
"lastKnownHash": "fnv1a-57605bbd",
"lineNumberHint": 103,
- "lastSyncedAt": "2026-08-25T06:36:16.376Z"
+ "lastSyncedAt": "2026-08-25T06:46:25.135Z"
}
}
}
diff --git "a/X1.notes/2026-08-28-Ollama-\345\221\275\344\273\244\350\241\214\344\270\216\345\256\242\346\210\267\347\253\257\347\232\204\345\205\263\347\263\273.md" "b/X1.notes/2026-08-25-Ollama-\345\221\275\344\273\244\350\241\214\344\270\216\345\256\242\346\210\267\347\253\257\347\232\204\345\205\263\347\263\273.md"
similarity index 100%
rename from "X1.notes/2026-08-28-Ollama-\345\221\275\344\273\244\350\241\214\344\270\216\345\256\242\346\210\267\347\253\257\347\232\204\345\205\263\347\263\273.md"
rename to "X1.notes/2026-08-25-Ollama-\345\221\275\344\273\244\350\241\214\344\270\216\345\256\242\346\210\267\347\253\257\347\232\204\345\205\263\347\263\273.md"
diff --git "a/X1.notes/2026-08-27-bug\344\277\256\345\244\215-\346\257\217\346\227\245\350\207\252\345\212\250\350\266\263\350\277\271\347\232\204\344\272\247\347\224\237\345\214\205\345\220\253\344\272\206\344\270\215\350\257\245\346\234\211\347\232\204tagfeed\345\206\205\345\256\271.md" "b/X1.notes/2026-08-25-bug\344\277\256\345\244\215-\346\257\217\346\227\245\350\207\252\345\212\250\350\266\263\350\277\271\347\232\204\344\272\247\347\224\237\345\214\205\345\220\253\344\272\206\344\270\215\350\257\245\346\234\211\347\232\204tagfeed\345\206\205\345\256\271.md"
similarity index 100%
rename from "X1.notes/2026-08-27-bug\344\277\256\345\244\215-\346\257\217\346\227\245\350\207\252\345\212\250\350\266\263\350\277\271\347\232\204\344\272\247\347\224\237\345\214\205\345\220\253\344\272\206\344\270\215\350\257\245\346\234\211\347\232\204tagfeed\345\206\205\345\256\271.md"
rename to "X1.notes/2026-08-25-bug\344\277\256\345\244\215-\346\257\217\346\227\245\350\207\252\345\212\250\350\266\263\350\277\271\347\232\204\344\272\247\347\224\237\345\214\205\345\220\253\344\272\206\344\270\215\350\257\245\346\234\211\347\232\204tagfeed\345\206\205\345\256\271.md"
diff --git "a/X1.notes/2026-08-29-chatgpt\344\270\200\344\270\252\345\276\210\351\205\267\347\232\204\347\224\250\346\263\225-\346\227\240\351\231\220\344\270\252\344\272\272\345\257\274\345\270\210.md" "b/X1.notes/2026-08-25-chatgpt\344\270\200\344\270\252\345\276\210\351\205\267\347\232\204\347\224\250\346\263\225-\346\227\240\351\231\220\344\270\252\344\272\272\345\257\274\345\270\210.md"
similarity index 100%
rename from "X1.notes/2026-08-29-chatgpt\344\270\200\344\270\252\345\276\210\351\205\267\347\232\204\347\224\250\346\263\225-\346\227\240\351\231\220\344\270\252\344\272\272\345\257\274\345\270\210.md"
rename to "X1.notes/2026-08-25-chatgpt\344\270\200\344\270\252\345\276\210\351\205\267\347\232\204\347\224\250\346\263\225-\346\227\240\351\231\220\344\270\252\344\272\272\345\257\274\345\270\210.md"
diff --git "a/X1.notes/2026-08-31-\345\221\275\350\277\220-\351\272\246\345\256\266\347\232\204\350\247\202\347\202\271.md" "b/X1.notes/2026-08-25-\345\221\275\350\277\220-\351\272\246\345\256\266\347\232\204\350\247\202\347\202\271.md"
similarity index 100%
rename from "X1.notes/2026-08-31-\345\221\275\350\277\220-\351\272\246\345\256\266\347\232\204\350\247\202\347\202\271.md"
rename to "X1.notes/2026-08-25-\345\221\275\350\277\220-\351\272\246\345\256\266\347\232\204\350\247\202\347\202\271.md"
diff --git "a/X1.notes/2026-08-25-\345\274\200\346\272\220\351\241\271\347\233\256openconnector\344\273\213\347\273\215.md" "b/X1.notes/2026-08-25-\345\274\200\346\272\220\351\241\271\347\233\256openconnector\344\273\213\347\273\215.md"
new file mode 100644
index 0000000..e3d8a36
--- /dev/null
+++ "b/X1.notes/2026-08-25-\345\274\200\346\272\220\351\241\271\347\233\256openconnector\344\273\213\347\273\215.md"
@@ -0,0 +1,44 @@
+---
+words:
+ 2026-08-25: 326
+---
+# OpenConnector介绍
+
+- **要点-两大作用**
+ - 1 密码中转--不用上传到AI服务器了
+ - 2 应用集成--常见的应用对接集成好了,不用每次都写代码
+- **github原地址:**https://github.com/oomol-lab/open-connector
+
+
+
+```mermaid
+flowchart LR
+ Agent["AI Agent / App"] -->|"SDK / CLI / MCP / HTTP"| Gateway["OpenConnector Gateway"]
+ Gateway --> Auth["Credential & OAuth Boundary"]
+ Gateway --> Catalog["Provider Catalog"]
+ Gateway --> Actions["Open-source Action Executors"]
+ Gateway --> Policy["Tokens, Scopes, Allow/Block Policy"]
+ Gateway --> Logs["Run Logs"]
+ Actions --> Providers["1,000+ Providers"]
+ Console["Web Console"] --> Gateway
+ Cloudflare["Cloudflare Workers, D1, R2"] -. deploy .-> Gateway
+
+```
+## 2026-08-25-安装备忘
+
+
+
+- `open-connector` 已在本机 `/tmp/open-connector` 克隆并装好依赖
+- 服务已启动
+- 监听地址是 `http://127.0.0.1:3000`
+- 我已经用 `lsof` 确认它在监听 3000 端口
+
+用的是仓库自带的 `Node.js` 自托管方式
+ 因为它官方支持,不依赖 Docker。这个路径更稳,也更适合你说的“以后本机项目都走这个”。
+
+
+补充说明:
+- 你这台机器也适合 Apple 原生 `container`
+- 但我这次为了尽快把服务真正落地,优先把 `open-connector` 直接跑通了
+- 仓库声明支持 `Node.js 22+`;当前系统 Node 是 `23.11.1`,能跑起来,但如果你要长期稳定使用,我建议后面我帮你把 Node 切到 `22.x` 再固定下来
+
diff --git "a/X1.notes/2026-08-30-\346\263\225\345\256\266\345\217\221\346\230\216\347\232\204\346\210\267\347\261\215\345\210\266\345\272\246\345\222\214\346\224\266\345\256\271\345\210\266\345\272\246.md" "b/X1.notes/2026-08-25-\346\263\225\345\256\266\345\217\221\346\230\216\347\232\204\346\210\267\347\261\215\345\210\266\345\272\246\345\222\214\346\224\266\345\256\271\345\210\266\345\272\246.md"
similarity index 100%
rename from "X1.notes/2026-08-30-\346\263\225\345\256\266\345\217\221\346\230\216\347\232\204\346\210\267\347\261\215\345\210\266\345\272\246\345\222\214\346\224\266\345\256\271\345\210\266\345\272\246.md"
rename to "X1.notes/2026-08-25-\346\263\225\345\256\266\345\217\221\346\230\216\347\232\204\346\210\267\347\261\215\345\210\266\345\272\246\345\222\214\346\224\266\345\256\271\345\210\266\345\272\246.md"
diff --git "a/X1.notes/2026-09-01-\345\274\200\346\272\220\351\241\271\347\233\256openconnector\344\273\213\347\273\215.md" "b/X1.notes/2026-09-01-\345\274\200\346\272\220\351\241\271\347\233\256openconnector\344\273\213\347\273\215.md"
deleted file mode 100644
index 6a6d879..0000000
--- "a/X1.notes/2026-09-01-\345\274\200\346\272\220\351\241\271\347\233\256openconnector\344\273\213\347\273\215.md"
+++ /dev/null
@@ -1,26 +0,0 @@
----
-words:
- 2026-08-25: 129
----
-OpenConnector介绍
-
-- **要点-两大作用**
- - 1 密码中转--不用上传到AI服务器了
- - 2 应用集成--常见的应用对接集成好了,不用每次都写代码
-- **github原地址:**https://github.com/oomol-lab/open-connector
-
-
-
-```mermaid
-flowchart LR
- Agent["AI Agent / App"] -->|"SDK / CLI / MCP / HTTP"| Gateway["OpenConnector Gateway"]
- Gateway --> Auth["Credential & OAuth Boundary"]
- Gateway --> Catalog["Provider Catalog"]
- Gateway --> Actions["Open-source Action Executors"]
- Gateway --> Policy["Tokens, Scopes, Allow/Block Policy"]
- Gateway --> Logs["Run Logs"]
- Actions --> Providers["1,000+ Providers"]
- Console["Web Console"] --> Gateway
- Cloudflare["Cloudflare Workers, D1, R2"] -. deploy .-> Gateway
-
-```
diff --git "a/X1.notes/2026-09-02-Git\345\256\211\350\243\205\345\244\207\345\277\230.md" "b/X1.notes/2026-09-02-Git\345\256\211\350\243\205\345\244\207\345\277\230.md"
deleted file mode 100644
index d245e93..0000000
--- "a/X1.notes/2026-09-02-Git\345\256\211\350\243\205\345\244\207\345\277\230.md"
+++ /dev/null
@@ -1,36 +0,0 @@
----
-words:
- 2026-08-25: 281
----
-
-安装备忘
-
-## 1.1-应该不会太大
-
-实测:当前规则下会被推到远程的 260 个文件,共 192 MB
-
-但拆开看,真正"你的笔记"只占一小部分:
-
-|类型|数量|体积|性质|
-|---|---|---|---|
-|`.ttf` 字体|12|**129 MB**|⚠️ 插件/画布用的字体,不是笔记|
-|`.docx` 文档|2|**28 MB**|⚠️ Word 文档,不是笔记|
-|`.md` 笔记|183|**18 MB**|✅ 这才是你的笔记量|
-|`.excalidraw` 画板|1|7 MB|手绘(内嵌图会变大)|
-|其它杂项(json/swift/canvas…)|~60|~10 MB|配置/脚本|
-
-**结论很关键:**
-- 其中真正的笔记(`.md`)只有 **18 MB**,压缩后约 **6 MB**。
-- 但当前 `.gitignore` 没拦住字体和 Office 文档,导致远程会被塞进 **~150 MB 的字体+Word**——这 95% 都不是你的笔记,纯属浪费,还会让仓库 clone 变慢。
-
-
-## 1.2 -通过排除法进一步压缩
-
-在 `.gitignore` 里补两行排除字体和 Office 文档:
-
-```
-*.ttf *.otf *.woff *.woff2 # 字体
-*.docx *.doc *.pptx *.ppt *.xlsx *.xls # Office 文档
-```
-
-这样远程就只剩纯粹的文本笔记 + 少量配置,首提交 ~6 MB。
\ No newline at end of file
diff --git a/X2.Archived/scripts/sync_0inbox.py b/X2.Archived/scripts/sync_0inbox.py
index 5741037..7b6887d 100644
--- a/X2.Archived/scripts/sync_0inbox.py
+++ b/X2.Archived/scripts/sync_0inbox.py
@@ -27,7 +27,8 @@
--------------------
- 顶层黑名单 EXCLUDE_TOP 整棵子树不扫:X2.Archived / 00index / X1.Knomo / X.Attachment / P3.bobo
- 仅扫描后缀 SCAN_EXT:.md .markdown .excalidraw .canvas;其它后缀一律跳过
-- markdown 同时抽 frontmatter(tags + 关键词字段)与正文 #tag
+- markdown 只抽标准 `tags:` frontmatter 与正文 #tag;topic/category/type 等
+ 元数据字段不再当作标签(避免 copilot 对话、读书笔记的元数据被误建页)
幂等 & 安全
----------
@@ -140,15 +141,18 @@
TAG_RE = re.compile(r"(?<![\w/])#([A-Za-z0-9_\u4e00-\u9fff][\w\u4e00-\u9fff/\-]*)")
SCAN_EXT = {".md", ".markdown", ".excalidraw", ".canvas"}
EXCLUDE_TOP = {"X2.Archived", "00index", "X1.Knomo", "X.Attachment", "P3.bobo"}
+# 嵌套路径排除(只排除指定子树,不影响其同级目录)。
+# 例:02DS/02copilot 是 Obsidian Copilot 的对话日志,其 topic: 字段与聊天正文
+# 不应被当作标签扫描(与 tagfeed 模板的 excludedPaths 保持一致)。
+EXCLUDE_PATHS = {"02DS/02copilot"}
INBOX_DIR = "0inbox"
EXCLUDE_DIRS = {".obsidian", ".trash", ".git", ".agents", ".claude",
".copilot", ".opencode", ".smart-env", ".workbuddy"}
-FM_KEYWORD_KEYS = {
- "aliases", "alias", "keywords", "keyword", "topic", "topics",
- "category", "categories", "subject", "area", "areaOfFocus",
- "people", "person", "project", "projects", "type", "tags",
-}
+# 只把标准 Obsidian 标签字段 `tags:` 当作标签;不再把 topic/category/subject/
+# type/keywords/aliases 等元数据字段误读为标签(否则 copilot 对话的 topic:、
+# 读书笔记的 type: 等会被当成标签疯狂建页)。行内 #标签 仍正常扫描。
+FM_KEYWORD_KEYS = {"tags"}
def split_fm_value(v) -> list:
@@ -358,10 +362,15 @@
all_tags = {}
for root, dirs, files in os.walk(vault):
dirs[:] = [d for d in dirs if d not in EXCLUDE_DIRS and not d.startswith(".")]
- top = os.path.relpath(root, vault).split(os.sep)[0]
+ rel = os.path.relpath(root, vault)
+ top = rel.split(os.sep)[0]
if top in EXCLUDE_TOP:
dirs[:] = []
continue
+ # 嵌套路径排除(如 02DS/02copilot)
+ if any(rel == p or rel.startswith(p + os.sep) for p in EXCLUDE_PATHS):
+ dirs[:] = []
+ continue
for fn in files:
if fn.startswith("."):
continue
--
Gitblit v1.9.3