玩转 Obsidian 07 :自动化「间歇式日记」
Matrix 是少数派的写作社区,我们主张分享真实的产品体验,有实用价值的经验与思考。我们会不定期挑选 Matrix 最优质的文章,展示来自用户的最真实的体验和观点。
文章代表作者个人观点,少数派仅对标题和排版略作修改。
在系列的第三期文章《玩转 Obsidian 03:如何记录「间歇式日记」》中介绍过如何通过「间歇式日记」管理自己的日常工作和生活:
「间歇式日记」是著名的生产力教练 Coach Tony 在2017 年发表的《Replace Your To-Do List With Interstitial Journaling To Increase Productivity》文章中提出的一个概念。他认为「间歇式日记」可以更好的帮助自己集中精力更聪明的工作。「间歇式日记」既不同于常规的「日记」,也不同于常规的「任务管理软件」,它更强调围绕着「工作流」打造一个「正反馈描述」从而提高「生产力」。
「间歇式日记」的运作方式其实很简单,一句话总结:
在每个「工作间歇」,以「时间戳」为单位,采用类似「日记」的形式做的笔记。
在实际使用中「间歇式日记」也不会有太多负担,因为适当总结和梳理总是会帮助我们「理清现状」并开始下一个行动。但是,在使用「间歇式日记」一段时间后我发现了几个问题。
在《玩转 Obsidian 04:为什么推荐使用 Obsidian 做知识管理》文章中专门提到 Obsidian 遵循「笔记优先法则」,这是它的优势之一:
「笔记优先法则」要求我们我们完全不需要考虑文件「存到哪里」这件事,只需要关注文件的「双向链接」以及 Tag 即可。并且借助 Obsidian 强大的搜索和关联能力,我们可以很方便的找到各种笔记。
在实际使用场景中,每天的「间歇式日记」经常要关联 N 个会议纪要或 N 个项目笔记,虽然在 Obsidian 中无需关注「存到哪里」这件事,但是当我们需要和其他人共享笔记等场景时,就会发现,如果适当的用「文件夹」进行组织,会特别方便。
适当的「文件夹」归档管理笔记,有助于我们分享和整理。
另外,虽然现在计算机运算速度很快,但是操作系统对于「一个文件夹下有大量文件存在」这一情况依然会出现可能的「性能问题」。特别是 Obsidian 还有一些「插件」支持对「文件库」进行扫描、过滤和筛选。
所以我对 Obsidian 中使用「文件夹」做了如下思考:
基于此并结合个人实践,目前我只归档了「会议纪要」和「项目文档」,并按照「年月日」进行归档,方便分享和查阅,例如下图中「会议纪要」归档:
但是在 Obsidian 中使用「归档」并不方便,毕竟我们「创建」笔记是如此简单(只需要添加「双向链接」即可),所以如何快捷「归档」是一个问题。
「间歇式日记」是我每天都在使用的笔记,并且是管理个人工作和生活非常重要的一环,在长期使用下来,我总会遇到「重复性」问题:
作为一个「效率控」自然想到是否有「插件」方便我们创建和管理「间歇式日记」并解决上边的「三个问题」,于是就有了今天的文章,首先给大家推荐一款效率插件「Templater」,它会帮助我们更加便捷的管理「间歇式日记」,减少没必要的操作,让我们将精力「聚焦到内容本身」从而提高效率。
Templater 是一款 Obsidian 插件,可以根据 Templater 的「模板脚本」创建笔记。由于其「模板脚本」基于 Eta 进行渲染,所以具备及其强大的扩展性。
看到这里大家一定想到了 Obsidian 自带的「模板」插件,我们在《玩转 Obsidian 05:如何进行阅读及摘要》中介绍过「模板」的使用方法,感兴趣可以点击连接查看。
Templater 插件相比较官方「模板插件」最大的区别就是它支持「Eta 模板」渲染,即具备了「执行 JavaScript 脚本的能力」,这使得我们可以完成一些自动化的事情。
PS :如果你有能力可以 点击链接 查看 Templater 所有语法和函数。
第一步:在「第三方插件」中浏览并搜索 Templater:
第二步: Templater 基础设置:
PS:以下内容需要懂一些「JavaScript」脚本,如果有些困难,可以直接跳转到「使用脚本创建间歇式日记」部分。
接下来我们将介绍如何使用 Templater 解决刚才提出的问题。
我希望创建「间歇式日记」的内容包含如下内容:
/Daily
目录下。一个符合上边需求的「间歇式日记」样本如下图:
接下来就进入到最关键的「脚本」环节,为了使用 Templater 创建「间歇式日记」,我们需要在上一步设置中的「Templater 目录」\Templater\script\
下创建名为 zzz-daily
的「脚本模板」,见下图:
也许你看到一片代码会比较懵,大家不要担心,我们将这段内容进行拆解后,只需要理解几部分即可。
首先可以看到整个脚本分为了三部分:
首先看下顶部脚本区主要内容:
这部分内容涉及核心代码较难理解,我们可以重点理解图中「① - ⑥」部分,当然如果不理解也没关系,可以直接修改部分可以看懂的内容直接用。
<%* -%>
通常将「主要脚本」集中在一个区域编写,并且放到一对 <%* -%>
之间。当我们执行脚本的时候,Templater 就会执行<%* -%>
之间的所有脚本内容。
today
let
:关键字,表示在其后要声明一个变量。today
:变量名,其值为「当前日期」。tp.date.now("YYYY-MM-DD")
:函数名,此函数将获取「当前日期」的时间值并赋值给「变量名」为 today
的变量。YYYY-MM-DD
,格式化语句,将函数 tp.date.now
返回的「当前日期」格式化成类似 2021-11-13
样式。「当前日期」指的是使用这个脚本那一刻所在的日期。例如我们在「2021年11月13日」使用这个脚本,「当前日期」就是「2021-11-13」,
today
的值就是2021-11-13
第二步,得到了变量 today
,其值为 YYYY-MM-DD
格式的「当前日期」。
inputDate
inputDate
:变量名,它将接受 tp.system.prompt("输入示例:"+today,today)
弹出对话框中「用户输入的值」。
await
:关键字,代表 Templater 执行到这一行的时候,会等待关键字 await
其后的「脚本语句」执行完成。
await
通常是「非必须」的,但是为了保险起见(例如这一行代码可能是一大段「待执行」脚本的一部分时),建议在可能「执行时间较长」的「脚本语句」前都加上await
关键字。
tp.system.prompt("输入示例:"+ today, today)
:函数名,通过此函数会弹出一个「对话框」,函数接受两个参数「对话框标题」和「输入框默认值」(即「括号」中用「逗号」隔开的两部分)。"输入示例:"+ today
:「对话框标题」,假设通过第一步我们得到的 today
变量值为 2021-11-14
,所以当我们执行脚本的时候 "输入示例:"+ today
代表第一个参数的值为 输入示例:2021-11-14
。today
:「输入框默认值」,代表在弹出的「对话框」中显示「默认输入值」,同样假设第一步我们的到的 today
变量值为 2021-11-14
,那么输入框中会显示 2021-11-14
作为「默认值」,显然我们可以将这个值改为真正想创建「间歇式日记」那一天的具体值。这里一定要注意,在弹窗中输入的值一定要符合
YYYY-MM-DD
格式(即2021-11-14
),否则会失败。
第三步,得到了变量 inputDate
,其值为用户输入的「具体日期」内容。
titleName
这一步较为繁琐,它先将我们在「第三步」输入的得到的「字符串类型」变量 inputDate
转换成「时间类型」变量,再将这个「时间类型」变量按照 YYYY-MM-DD_ddd
格式化成「字符串类型」变量。
titleName
:变量名,它接受 window.moment(inputDate, "YYYY-MM-DD", true).format("YYYY-MM-DD_ddd")
最终返回的字符串。window.moment(inputDate, "YYYY-MM-DD", true).format("YYYY-MM-DD_ddd")
:函数名,通过此函数可以将「字符串类型」的变量转换成「时间类型」的变量。inputDate
:变量,第三步我们输入的「日期字符串」。"YYYY-MM-DD"
:字符串,表示将「字符串类型」变量按照这个格式转换成「日期类型」变量。true
:布尔型变量,代表启动转换。window.moment(inputDate, "YYYY-MM-DD", true)
将第三步得到的变量 inputDate
(我们手动输入的「日期字符串」)转换成「日期类型」变量。变量是有类型的,这属于编程范畴的要求,有些函数只接收「符合类型」的参数。所以在很多场合我们要对「变量」进行「类型转换」。
.format("YYYY-MM-DD_ddd")
:函数名,通过此函数可以将「时间类型」变量再次格式化成「字符串类型」变量。YYYY-MM-DD_ddd
: 意思是将「时间类型」变量转换成「YYYY-MM-DD_ddd」样式例如 2021-11-13_Sat
。第四步,得到了变量 titleName
,其值为「inputDate
(第三步输入的「日期字符串」) 按照 YYYY-MM-DD_ddd
格式化后的值」。
这部分语法使用的是 momentjs。
before_date
和 after_date
和第四步很像,这一步我们只说一下 add
函数:
.add()
:函数名,通过此函数可以对「时间类型」变量进行计算。add(-1,"days")
,得到「时间类型」变量「前一天」的值。add(1,"days")
,得到「时间类型」变量「后一天」的值。第五步,得到了两个变量 before_date
和 after_date
,其值分别为用户输入「具体日期」的前一天和后一天。
before_date
和 after_date
<< [[<% before_date %>]] | [[<% after_date %>]] >>
被一对
<% %>
包围的语句,其实就是将被包裹的「变量」对应的值,输出到模板中。举例来说以上语句就会在创建出来的模板中输出:<< [[2021-11-13_Sat]] | [[2021-11-15_Mon]] >>
。
模板区域目前没有用到任何「脚本变量」或「脚本语法」,基本上就是将文本内容输出到将来要创建的笔记中。
需要注意的是我们并不需要在「模板区域」中输出「所有未来可能」要出现的内容,因为未来是不确定的,否则的话我们就得经常修改「模板区域」内容。
我的做法是创建一个名为「workBench」笔记,并通过「双向链接」关联到「模板区域」,这样我就需要将那些会随着时间发生变化的内容在「workBench」中进行统一调整,而不用修改模板,我的「workBench」架构如下:
我会定期调整需要关注的的事项,将其移到对应的区域。例如从「高优关注」-->「有时间关注」或「冷藏」;将「所有完成的任务」-->「Done」。
目前在「底部脚本区」我只编写那些在模板生效之后要做的事情,例如:
await tp.file.move("/Daily/" + titleName)
tp.file.cursor()
第一行是将当前基于「脚本模板」创建的笔记移动到 /Daily
目录中,也就是将日记「归档」。
第二行是让当前笔记获得焦点(鼠标闪烁)。
至此,我们创建了位于 \Templater\script\
的「脚本模板」,文件名为 zzz-daily
,接下来就是应用了。
点击查看 Github 完整代码
<%*
let today = tp.date.now("YYYY-MM-DD")
let inputDate = await tp.system.prompt("输入示例:"+today,today)
titleName = window.moment(inputDate, "YYYY-MM-DD", true).format("YYYY-MM-DD_ddd")
before_date = window.moment(inputDate, "YYYY-MM-DD", true).add(-1,"days").format("YYYY-MM-DD_ddd")
after_date = window.moment(inputDate, "YYYY-MM-DD", true).add(1,"days").format("YYYY-MM-DD_ddd")
let createTime = tp.file.creation_date()
let modificationDate = tp.file.last_modified_date("dddd Do MMMM YYYY HH:mm:ss")
-%>
---
create time : <% createTime %>
modification date: <% modificationDate %>
---
<< [[<% before_date %>]] | [[<% after_date %>]] >>
<% tp.web.daily_quote() %>
<% tp.web.random_picture("200x200", "landscape,water") %>
#### 重点关注
- ==早上 7 件事==
- [ ] 花点时间回顾和反思
- [ ] 查看「反向链接」和「工作待办」
- [ ] 扫一眼邮件
- [ ] 确定最困难的工作,拆分成多个小任务
- [ ] 写下需要思考的东西
- [ ] 忽略人际关系冲突
- [ ] 不开会/少开会
- 工作效率
- [[会议检查清单]]
- [[Workbench]]
#### 阅读笔记 & 会议纪要
通常记录一些需要技术阅读的内容
#### 间歇日记
- 今日重点任务
- xxxxx
<%*
await tp.file.move("/Daily/" + titleName)
tp.file.cursor()
-%>
首先回顾一下我们的使用场景:
/Daily
目录下。使用的时候只需几步即可完成:
1. 使用快捷键「Cmd+N」新建一篇笔记。
2. 点击左侧「快捷入口」,输入「zzz」快速筛选出要执行的「脚本模板」(即刚才创建的 zzz-daily
),选择对应的文件即可完成。
3. 在弹出对话框中,输入「指定日期」,完成「间歇式日记」的创建,并且光标停在当前笔记。
最终生成的「间歇式日记」如下图:
你可能已经从上图中关注到出现了一个新的区域,它包含一段话和一张图片:
这是因为在刚才创建的「脚本模板」 zzz-daily
中有两行代码,他们是 Templater 内置的两个函数,可以随机获得一些文字和图片,我觉得挺好的就保留了。
<% tp.web.daily_quote() %>
<% tp.web.random_picture("200x200", "landscape,water") %>
如果你在模板中也用到了这两行代码,需要注意,当我们创建「间歇式日记」之后,新笔记会有一段时间是「空白」的,需要「稍等一会」才出现全文,这是因为这两行代码产生了「网络请求」,而网络请求受「网络延时」影响可能会比较慢。此时,只需要耐心等待内容加载出来即可,不要做其他操作。
PS:如果遇到类似下图中 「Templater Error:」错误,可以尝试删除这两行代码,原因是网络问题会引起 Timeout 超时错误。
「任务回顾」是我在「间歇式日记」中常用的方式,每当遇到无法当天完成的「任务」,我都会评估一个完成日期,手动创建这一天的「间歇式日记」,并手动将「任务」拷贝到这一天的笔记当中。
这个过程总是比较繁琐,接下来将介绍一个插件「Review」帮我们解决此问题。。
安装步骤就不详述了,说一下使用场景。
PS:Review 插件依赖「Calendar」插件
Review 插件依赖另一个我们之前介绍过的插件Calendar,所以要确保同时安装过此插件。关于「Calendar」插件的使用可以查看《玩转 Obsidian 03:如何记录「间歇式日记」》。
对于未完成的任务,我们只需要在「编辑模式」下,让光标停留在需要「回顾的任务」那一行,按下「Cmd+P」启动命令行,输入「Review」关键字选择 review:Add this block to a daily note for review
这个命令执行。
接下来发生两件事:
1.Review 插件会弹出一个对话框要求输入「指定日期」。
PS:「指定日期」可以是具体的「2021-11-14」样式,也可以是 today
、Tomorrow
等单词。
2.Review 利用「Calendar」创建「指定日期」的「间歇式日记」(如果笔记不存在的话),并将当前光标所在这一行内容拷贝到刚创建的「间歇式日记」笔记中。
仔细看 2021-11-13_Sat
和 2021-11-15_Mon
两篇「间歇式日记」格式不同,2021-11-15_Mon
缺少顶部的「日期导航」。这是因为 「Calendar」创建「间歇式日记」会依赖 Obsidian 官方的「日记」插件的「模板」,存放位置如图:
由于其不支持「JavaScript」脚本,所以也就无法展示「日期导航」。
如果你需要补齐「日期导航」的,可按如下操作:
\Templater\script\
目录下创建名为 zzz-insert-metadata
的「脚本模板」文件。zzz-daily
脚本的部分内容)。zzz-insert-metadata
模板文件。完整脚本内容:
点击查看 Github 完整代码
<%*
let today = tp.date.now("YYYY-MM-DD")
let inputDate = await tp.system.prompt("输入示例:"+ today,today)
titleName = window.moment(inputDate, "YYYY-MM-DD", true).format("YYYY-MM-DD_ddd")
before_date = window.moment(inputDate, "YYYY-MM-DD", true).add(-1,"days").format("YYYY-MM-DD_ddd")
after_date = window.moment(inputDate, "YYYY-MM-DD", true).add(1,"days").format("YYYY-MM-DD_ddd")
let createTime = tp.file.creation_date()
let modificationDate = tp.file.last_modified_date("dddd Do MMMM YYYY HH:mm:ss")
-%>
---
create time : <% createTime %>
modification date: <% modificationDate %>
---
<< [[<% before_date %>]] | [[<% after_date %>]] >>
<% tp.web.daily_quote() %>
<% tp.web.random_picture("200x200", "landscape,water") %>
完成了「间歇式日记」的自动化后,我们看一下如何自动化「会议纪要」,首先回顾一下我们的诉求:
同样我们利用 Templater 的脚本完成,这次我们需要两个文件:
「会议纪要模板」主要存放会议的模板信息,首先在 \Templater\note\
目录下创建名为 Metting-work
的「会议纪要模板」:
模板文件比较简单,只用到了两个脚本语句:
<% tp.date.now("YYYY-MM-DD_ddd") %>]
:上文提到过在一对<% %>
中包含的脚本,会在此位置直接输出对应的值。
tp.date.now("YYYY-MM-DD_ddd")
:函数名,函数获取「当前日期」的时间值,并赋值给名为 today
的变量。YYYY-MM-DD_dd
:格式化语句,它将 tp.date.now
返回的「当前日期」变量值格式化成 2021-11-13_Sat
样式。<% await tp.file.move("/MeetNote/" + tp.date.now("YYYY") + "/" + tp.file.title) %>
:将「当前笔记」移动到 /MeetNote/2021/
文件夹下。
await
:关键字,代表 Templater 执行到这一行的时候,会等待关键字 await
其后的「脚本语句」执行完成。tp.file.move()
:函数名,将当前文件移动到指定目录。"/MeetNote/" + tp.date.now("YYYY") + "/" + tp.file.title)
:将字符串拼接在一起。tp.date.now("YYYY")
:函数名,获取「当前日期」。YYYY
参数,代表返回「当前日期」的格式,只返回「年」,例如 2021
。tp.file.title
:成员变量,其值为「当前笔记」的标题。/MeetNote/2021/aaaaa
值/MeetNote/2021/aaaaa
进行归档。点击查看 Github 完整代码
**Tags:** [[Notes&Drafts]]
----
## 参会人
- 待补充
## 会议信息
- **Topic:** 工作会议
- **Location:** xxxx
- **Date:** [[<% tp.date.now("YYYY-MM-DD_ddd") %>]]
- **Time/Duration:** xx:xx - xx:xx
## 议题
1. 待补充
## Todo
无
<% await tp.file.move("/MeetNote/" + tp.date.now("YYYY") + "/" + tp.file.title) %>
「会议纪要脚本」的作用是:在光标所在位置插入一则笔记,而笔记的内容是根据上一步的「会议纪要模板」而创建的,同时将笔记归档到指定目录。
在 \Templater\script\
目录下创建名为 zzz-meeting
的「会议纪要模板」:
input
input
:变量名,其值为弹窗中输入的内容。await
:关键字,代表 Templater 执行到这一行的时候,会等待关键字 await
其后的「脚本语句」执行完成。tp.system.prompt("输入会议标题:")
:函数,弹窗要求用户输入「会议标题」。第一步,得到变量 input
,其值为用户输入的内容。
templateName
templateName
:变量名,其值为获得的「模板」「句柄」templateName
操作其指定的「模板」。tp.file.find_tfile("Metting-work")
:函数,查找并获取名称为 Metting-work
的「句柄」。第二步,得到变量 input
,其值为用户输入的内容。
today
let
:关键字,代表要声明一个变量;today
:变量,接收 tp.date.now("YYYY-MM-DD")
的返回值;tp.date.now("YYYY-MM-DD")
:函数,通过此函数获取「当前日期」的时间值。YYYY-MM-DD
,格式化语句,代表我们通过 tp.date.now
返回的变量值符合「2021-11-13」这种格式。第三步,得到变量 today
,其值为 YYYY-MM-DD
格式的「当前日期」
titleName
titleName
:变量,其值为「当前日期 - 输入值」。即将第一步和第三步的两个变量值合并,得到类似 2021-11-15 - xxx会议纪要
。第四步,得到变量 titleName
,其值类似 2021-11-15 - xxx会议纪要
。
await
:关键字,代表 Templater 执行到这一行的时候,会等待关键字 await
其后的「脚本语句」执行完成。tp.file.create_new(templateName, titleName, false)
:函数,根据「模板句柄」创建新的笔记。templateName
:变量,其值为第二步获得的「模板句柄」。titleName
:变量,其值为第四步获得的「笔记标题」。.basename
:成员变量,其值为 tp.file.create_new()
创建的笔记的「文件名」。第五步,脚本语句会创建「会议纪要」笔记,并输出笔记的「双向链接」到光标所在处。
点击查看 Github 完整代码
<%*
let input = await tp.system.prompt("输入会议标题:")
let templateName = tp.file.find_tfile("Metting-work")
let today = tp.date.now("YYYY-MM-DD")
let titleName = today+" - " + input
-%>
[[<% (await tp.file.create_new(templateName, titleName , false)).basename %>]]
还记得我们上一步「会议纪要模板」的最后一行代码:
<% await tp.file.move("/MeetNote/" + tp.date.now("YYYY") + "/" + tp.file.title) %>
所以 Templater 插件通过「会议纪要模板」创建「会议纪要」时,笔记「自动」被归档到类似 /MeetNote/2021/
目录下。
创建「会议纪要」步骤很简单:
1.将光标停留在需要添加「会议纪要」的区域,点击「快捷入口」
2. 在弹出对框中输入「会议标题」,如图:
完成后如图:
「间歇式日记」可以让 Obsidian 更好的管理我们的日常工作和生活,在使用中也提出了几个问题:
针对以上问题,本篇花了大量篇幅介绍了 Templater 和 Review 两个插件,解决自动化问题:
相信许多人都有一个疑惑:如果不懂 JavaScript 脚本是否可以用好 Templater?
其实完全没问题,如果不懂得脚本,直接拷贝「每节」的「xxx 完整脚本内容」部分,直接使用即可(也可以做小范围修改),本文包含的脚本内容如下:
希望本文可以起到「抛砖引玉」作用,大家可以利用 Templater 制作出更多「自动化」脚本提高效率。
「玩转 Obsidian」系列会持续更新「如何使用 Obsidian 进行知识管理」,对此系列感兴趣可以在以下渠道找到相关文章:
玩转 Obsidian 系列目前包括文章:
可以在 Twitter、Telegram 、instagram 等渠道关注我,获取更多有意思的讯息。
> 下载 少数派 2.0 客户端、关注 少数派公众号,了解更多 Obsidian 技巧 📰
> 实用、好用的 正版软件,少数派为你呈现 🚀
© 本文著作权归作者所有,并授权少数派独家使用,未经少数派许可,不得转载使用。
本专栏和大家探讨「知识管理」的各种层级和方法,其目的为启发思考与共同探索并找到适合自己的「知识管理之术」。