江边雁19 downloads自定义规则,把源笔记里某个段落自动衍生到指定位置的文件。写了才生成,没写就不生成。Derive files from a note section with custom rules and templates.
一句话:写一条规则说清楚「哪个文件夹里笔记的哪个段落,要变成哪个文件」,插件就替你看着——段落里写了东西就生成 / 更新产物,没写就不生成。
一个源文件对一个产物文件。已生成的内容跟着源笔记走,你在标记外面写的东西永远不会被覆盖。
English summary: Template Derive watches a folder of notes. For each rule you define — source folder + source section + target path + output template — it renders that section into a separate file. Nothing is created unless you actually wrote something in that section. Files it manages are wrapped in
<!-- template-derive:start -->/endmarkers; anything you write outside those markers is never touched.
Obsidian 里有一类很别扭的需求:一份笔记里塞着两种东西,一种是一天一天的流水,另一种是需要连续积累的记录。
比如日记。今天学了什么、心情如何,这是流水;但「我卡在哪儿、下一步改什么」这种反思,攒成一串才有用,混在日记里会被冲掉。手动复制到另一个文件夹?坚持三天就放弃了。
这个插件把这件事变成自动的:你在日记里写的那四行,会同时出现在 Personal/Review/2026-10-07.md 里,按日期排成一条可检索的序列。
但它的内核是通用的:源段落 → 判定 → 渲染 → 落盘,四步全是配置。日记只是出厂自带的一组示例。
main.js 和 manifest.json<你的库>/.obsidian/plugins/template-derive/插件只支持桌面版:写入走的是「临时文件 + rename」的原子写入,fs 在移动端不可用(见下面「为什么必须原子写入」)。
装上就有一组预设规则,对应下面这套日记体系:
| 规则 | 源 | 触发 | 产物 |
|---|---|---|---|
| 计划结转 | Personal/Diary |
「今日计划」段里没做完的复选框 | 搬进今天日记的同一个段落(带 ↺ 标记) |
| 复盘 | Personal/Diary |
「今日复盘」段写了东西 | Personal/Review/YYYY-MM-DD.md |
| 成长轨迹 | Personal/Diary |
「成长轨迹」段打了分或写了字 | Personal/Growth Track/YYYY-MM-DD.md |
| 阅读记录 | Personal/Diary |
「今日阅读」段写了东西 | Personal/Reading/YYYY-MM-DD.md |
预设里的「今日计划」段约定每条占两行 —— 上面一行是目标,下面缩进两格的那行写结果:
- [ ] 背 30 词(早饭后)
完成:对了 9 道
勾上、以及「完成行写了东西」,都算做完的信号;结转只搬「没勾上、并且完成行还空着」的那些,搬过去时会把空的完成行一起带上,方便回头填。
不需要这套?在设置界面里删掉就行,或者改成你自己的。
设置 → Template Derive → 新增规则,填四个东西:
① 源 — 哪个文件夹、文件名长什么样
源文件夹:Subjects/数据结构与算法文件名日期格式:填 * 表示文件名任意,不认日期(这时用 {{name}} 取文件名);填 YYYY-MM-DD 表示文件名就是日期② 触发 — 哪个段落、什么时候算「写了」
触发段落:自测(不含 ##)写了才创建:开着,就是你想要的行为③ 产物 — 放哪、叫什么
产物路径:Reviews/{{name}} 复习卡.md④ 渲染 — 内容长什么样
输出模板文件:库里的一份 Markdown,比如 Templates/衍生/复习卡.md。留空就原样搬段落。文件头 / 附录:只在创建时写一次。适合放标题和「为什么这么设计」的依据。写在 产物路径 / 输出模板文件 / 文件头 / 附录 里,渲染时替换。
| 变量 | 含义 |
|---|---|
{{date}} |
源文件日期(按规则的日期格式)。文件名任意模式下是空的 |
{{date:YYYY-MM-DD}} {{date:dddd}} |
指定格式。只支持 YYYY YY MM M DD D dddd ddd |
{{name}} |
源文件名(不含 .md) |
{{key}} |
源文件名的「身份」:日期模式下是日期,任意模式下就是文件名 |
{{file}} |
源文件完整路径 |
{{link}} |
[[源文件路径]] |
{{rule}} {{ruleid}} |
规则名 / 规则 id |
{{section}} |
触发段落的正文 |
{{section:段落名}} |
指定段落的正文(可跨段) |
{{field:关键词}} |
触发段落里取 - 标签:值 这一行的值 |
{{field:段落名 > 关键词}} |
从指定段落里取 |
{{field:候选1,候选2}} |
关键词可以写多个候选,命中哪个都行 |
{{field:2}} |
纯数字 = 按出现顺序取第 2 行 |
{{lines:关键词}} |
取值同 {{field:}},但把值按行拆成 - 列表 —— 一个标签下写了好几行时用它分点显示 |
{{checks:段落名}} |
取复选框项,保留勾选状态;完成结果(写在目标下面缩进的那行)另起一行输出 |
{{checks:段落名/子小节}} |
只取某个 ### 子小节 里的 |
{{score:精力}} |
取 精力:4 / 5 里的分数,输出 4 / 5 |
{{fm:course}} |
源文件 frontmatter 里的字段 |
每个变量都能带默认值:{{field:去哪|(今天没写)}} —— 取不到时用竖线后面那段。
不认识的变量原样保留,所以你在同一个文件里用核心 Templates 的 {{title}} 之类不会被吃掉。
段落正文里的折叠 callout(> [!note]-)会被自动过滤——那是模板自带的说明,不属于你写的内容。普通的 > 引用不受影响。
这三条都是踩过坑之后定下来的,用的时候可以放心:
① 原子写入。 写入走「写临时文件 → rename 覆盖」,绝不直接覆盖。原因是:直接覆盖时 Obsidian 的文件监听器可能读到「写了一半」的中间态,然后把它当成新内容回写,结果是文件里两份内容交错拼贴。这个真事故发生过一次,修了两小时。
② auto 标记区。 插件只维护这对标记之间的内容:
<!-- template-derive:start -->
(插件写这里)
<!-- template-derive:end -->
标记外面你写的任何东西永不覆盖。如果你自己建了一个同名文件、里面没有这对标记,插件判定是你手建的,直接跳过不动。
(第一版用的是 wb:auto:start 标记,插件认得,不会迁移你的文件,继续原地维护。)
③ 只在内容真的变了才写盘。 不然会「写入 → 触发保存事件 → 再写入」无限循环。
预设的日记格式里,一条计划是两行:
- [ ] 背 30 词(早饭后)
完成:对了 9 道
手打这个结构很烦,麻烦的根源是 Obsidian 的原生行为 —— 不是插件坏了:
- [ ] 是它内置的任务语法,可它不认识「完成:」那一行。在写满的任务行末尾按回车,它只续出 - [ ] ,下面那行「完成:」得你自己敲。所以插件接管了这几个键。只在上面那些规则管着的文件里生效,别的笔记一个键都不碰:
| 光标所在 | 按回车 → |
|---|---|
- [ ] 目标,下面已有「完成:」行 |
光标送到「完成:」行,接着写结果 |
- [ ] 目标,下面没有 |
补出一行缩进的「完成:」 |
完成:结果,下面还有空占位 |
光标送到下一个方框 |
完成:结果,这一节写到最后一格了 |
补出一整组「- [ ] + 完成:」 |
### 小标题(光标在行尾) |
在这一节下面补出一组「- [ ] + 完成:」 |
也就是说,写完目标按一下回车、写完结果再按一下回车,就能一条接一条写下去,中间不用碰鼠标。
三点说明:
一门课记一个 ### 小节。手动补这一整节最容易错的是完成行的缩进 —— 少敲两个空格,插件就认不出「这两行是一对」,结转和输出的时候整条会静默丢掉。所以给它也配了按键:光标放在「今日计划」里任意一行(不要求停在行尾)按 Shift + 回车:
### Precision Design and Dimensional Inspection
- [ ] 对几何公差有直观认识
完成:
### ▏ ← 光标停这,直接打下一门课的名字
- [ ]
完成:
### 阅读 ← 原来的小节没被劈开,整体往后挪
- [ ]
完成:
四条边界:
## 标题,不在名单里就一个字都不动,所以今日复盘、今日阅读按了没反应。名单在设置里可改(逗号分隔)。新增小节,独立于上面那个回车开关)。核心 Daily Notes 只有一个「打开今天的日记」,它不理解「明天」这个概念。想今晚就把明天的计划写好,两条路:命令面板 → 新建明天的日记(也可以给它绑个顺手的键),或者用 打开日历视图 之后直接点日历上那个日期。
它照抄核心 Daily Notes 自己的配置(直接读 .obsidian/daily-notes.json):日记文件夹、文件名格式、用哪份模板 —— 所以生成出来的 Personal/Diary/2026-10-08.md 和核心自己建的那份看不出区别。模板里的 {{date:YYYY-MM-DD}} {{date:dddd}} 会换成那一天的日期和星期。已经存在就只打开,一个字都不改。
★ 提前写要知道的一件事:结转在建立文件的那一刻发生,之后不回头。 也就是说,今天没做完的条目,在你建明天日记的那一刻就已经搬过去了;之后你再回头改今天这篇,明天那篇不会跟着变 —— 结转对同一天只跑一次,不然会把搬过的条目再追加一遍。真要补,用命令「把上一天没做完的条目结转到今天(忽略去重)」再搬一次。
Ctrl/Cmd + P:
Calendar: Open view,是英文的,而且只在日历没打开的时候才出现,开着的时候搜不到它。这条不限状态。(Calendar 没启用时这条会自己藏起来。)Subjects/<学科>/笔记/ 下按模板建今天的文件并跳过去;已经存在就只打开,绝不覆盖。默认没绑键。fs)。YYYY YY MM M DD D,不支持 {{date+1d}} 这种偏移写法。想在前一天先把明天的日记写出来,用命令「新建明天的日记」,或者用 Calendar 插件直接点日期(两条路建出来的文件一样)。*)的规则不参与结转。- [ ] 目标 + 缩进的 完成:」这一种结构;旧的内联写法(- [ ] 目标 完成:结果)不碰。今日计划 一个),而且新小节永远插在当前小节的末尾。预设那三条规则不是随手写的,每一段的形态都对应一条有据可查的结论。完整出处写在产物文件的附录里(可折叠),这里列个索引:
| 设计 | 依据 |
|---|---|
| 复盘分 Feed up / Feed back / Feed forward | Hattie & Timperley (2007)《The Power of Feedback》 |
| 归因只写内部原因 | Oettingen 的 WOOP / MCII:纯积极幻想反而降动机 |
| 计划写「什么时候做」 | Gollwitzer 实现意图,94 项研究元分析 d = 0.65 |
| 勾选必须接复盘 | Michie et al. (2009):自监控单打 0.26,配其他技术 0.42 |
| 「完成:」写回忆出多少 | Roediger & Karpicke (2006):提取 61% vs 重读 40% |
| 目标与完成拆成两行 | Michie et al. (2009):勾选只是自监控,写了结果才是反馈 |
| 阅读记录第二步要「合上书」 | Karpicke & Blunt (2011, Science):自由回忆多记住 145% |
| 三格打分:自主 / 胜任 / 联结 | Deci & Ryan 自我决定理论 |
| 「掌握性证据」独立成节 | Bandura (1977):自我效能四来源里掌握性经验最强 |
| 「今日小胜」独立成节 | Amabile & Kramer《The Progress Principle》:11,872 篇工作日日记 |
| 「我在成为什么样的人」 | Mueller & Dweck (1998):过程表扬 vs 个人表扬 |
node --check main.js # 语法
node test/run.cjs # 单元测试(纯 Node,不需要 Obsidian),当前 260 项
main.js 是单文件,没有构建步骤:纯函数核心和 Obsidian 插件壳住在同一个文件里,靠 require('obsidian') 是否成功来分流。所以同一份代码既能被 Obsidian 加载,也能被 Node 直接 require 跑测试。
「回车续行」的判断也全在纯函数 taskEnterAction(getLine, lineNo, opts) 里 —— 它只回答「光标在这一行按回车该干什么」,不知道 Obsidian 的存在,所以那套边界全都能单测。「Shift + 回车开新小节」同理,在 sectionEnterAction(getLine, lineNo, opts) 里:输入是几行文本和光标行号,输出是「插什么、光标落在哪」。插件壳里只有一个 keydown 监听负责接键和落光标。
这两个函数的返回值可以直接断言,所以改键盘行为时先写用例再改代码,比在 Obsidian 里反复试快得多。
改了 main.js 之后必须让 Obsidian 重新加载插件代码(设置里关掉再打开;不生效就重启 Obsidian)。
MIT