重新开始博客: 使用 StandardNotes

重新开始博客: 使用 StandardNotes

05 February 2019

距离上一篇博文已经过去 10 个月了,而本博客连一篇文章都没有更新,可能已经开始给人一种几乎要荒废的感觉了。而事实是,确实是这样,这个博客已经在荒废的边缘。

这几乎一年间,因为自身情绪上的一些问题,我很少有想要写一些东西的心情,与以前过个几天就想提键盘的状况完全不同。而在那非常少的有“想写一些东西的心情”的时候,我每次都会因为害怕麻烦而打消念头。“麻烦”的缘由则很大程度上是我自己埋下的 —— 我在多年以前抛弃了 WordPressGhost 这样的博客平台,自制了一个简易的博客程序 Typeblog,而这个程序是没有实现编辑器 UI 的。它直接依托于配置文件、插件和文章源文件,而这些文件则需要存储在 Git Repository 中。因此,我以前的做法是使用单独的 Markdown 编辑器,比如 Typora,编辑后提交到 GitHub 并通过 WebHook 来实现自动同步到服务器。这一个过程确实没有几个步骤,在以前我也就很自然地做完了。然而,也许是不良情绪放大了很多事情的复杂性,这确实就是过去这段时间里我不想动博客的主要原因。

关于那些“情绪问题”,我近期可能写一些文章描述,而这篇文章的正题并不是那些问题。总之最近,所谓情绪问题似乎有所缓解,也稍微有些能写的心情,只是回想起来之前的写作体验确实差强人意。当时也没有一个可以接受的手机写作解决方案,因为 Android 端我并没有找到非常好用的、支持同步的 Markdown 编辑器,即使找到也存在如何 push 到 GitHub 的问题;而我却经常在路上、在睡前玩手机无聊的时候想要写一些东西。当然,我的朋友 drakeet 有一款在手机上写作的 纯纯写作 应用,写作体验已经非常棒,只是我需要的并不只是写作,我还需要

  • 多平台统一 (PC Linux + Mobile Android)
  • 支持自托管 (self-hosted) 的完整同步解决方案
  • 完整加密支持 (明文数据不离开客户端)
  • 插件支持 (这样我至少可以自行实现导出到自己的博客)

于是,这就到了该开始安利的时间了。

Standard Notes

我第一次接触到 Standard Notes 与博客完全无关。正如其名,它是我在寻找笔记软件的时候发现的。

从上大学开始,我就习惯于用笔记软件来写电子版笔记,顺便用于记录一些临时起意的想法之类。原因很显然是我的凄惨手写字体和手写速度,但无论如何,经过了一两年以后,使用笔记软件这也就成为了一个习惯,经常没事就打开看一看写一写。

那时候我使用的是 Evernote 的付费订阅 —— 认识的很多人都在用,所以我也自然地使用了它。但是,使用了这个产品以后才发现,它不要说端到端加密和 Markdown 支持了,就连基本的富文本编辑都存在这样那样的很多 BUG,其中不少完全是由于自作聪明。它的扩展性也几乎是零 —— 没有任何办法做出能用的编辑器插件。大一的时候数学课的笔记需要使用 LaTeX 记录公式,唯一可行的办法就是用某些使用 Evernote API 做的 Wrapper 来输入,然后让它们代为同步到 Evernote。这既不优雅也不安全,还要多带上一个专门用来记数学笔记的软件,已经失去了统一笔记平台的意义。

后来 Evernote 出现了隐私问题,尽管后来隐私条款的修改被撤回了,我还是觉得切换自己使用的笔记平台的时间到了。Evernote 官方也迟迟不肯实现插件支持和加密等特性,整个平台也已经很长一段时间没有变动,除了隐私条款之外。如此决定之后,我就开始四处寻找新的替代,希望能够用支持加密、自托管且有足够插件支持的软件来替代。当然,使用代码编辑器 + Markdown + Git 也是完全可行的方案,我可能希望的只是一个更适合做笔记的 UI;同时也不希望自己每次打开代码编辑器,默认记录的上次打开文件都是一堆与代码无关的东西。

就是在这个时候,我从某位朋友那里听说了这个 Standard Notes。那时候,它似乎还在非常早期的阶段,功能也非常简单 —— 默认只有一个纯文本编辑功能,它背后的协议 StandardFile 也就是一个普通的文件同步协议加上客户端加密。除了加密部分[1]以外,这个东西的一切都似乎是从简单和方便长期维护的角度考虑的,这其中就包括了它的插件化的结构。开发者宣称,为了“专注安全和持久性”,

We say 'no' to feature requests.

可是说着这句话的它却有很棒的可扩展性[2],大概是因为傲娇也是一种正面属性吧。利用插件 API 几乎可以自己做任何想要的功能,事实上,官方的收费计划中就有很多他们自己利用这个 API 制作的插件,包括了 Markdown 编辑器 (LaTeX 支持),TODO 编辑器,以及各种加密备份方案等等。

这个收费订阅,也是在不影响开源的前提下的收费 —— 有能力的人完全可以很方便地自行从源码构建所有插件并导入,也可以在不交钱的前提下自行开发插件。我最初就是通过这个方式在没有付费订阅的情况下使用了插件,之后加入了付费订阅。

在当时,它的移动端仍然缺少除了同步以外的很多基本功能,包括 Markdown 编辑器;也因为我那时还有当时自己认为方便的写作方案,所以完全没有考虑使用它作为主要的写作工具,只是作为一个方便的 self-hosted 笔记工具来使用。

Listed

接下来的某个时间,我发现了同样由 Standard Notes 团队提供的工具 Listed。它是一个模仿 Medium 的平台,只是比 Medium 要简单得多,本质上就是一个导出并展示部分 Standard Notes 笔记的地方。

虽然 Listed 本身并没有开源[3],但是其实现方式和大部分关键代码都已经在开发者文档中作为“示例代码”公开了。或者倒不如说,Standard Notes 的整个 Actions API 几乎都是为了这个用例而设计的,Listed 服务则只是最简单的用例而已。

这个服务让我眼前一亮,既然我的笔记软件已经有非常好的 Markdown 编辑体验,为什么不直接把它当作写作工具?况且本来平时的写作灵感最先进入的就是笔记,若是一篇文章没有彻底完成,则行文思路也是在笔记中,使用笔记软件直接写作的话反倒可能免去了不少来回切换的麻烦。

因此,我决定先用 Listed 尝试一下这么做的可行性与实际体验,这也就有了我的英文博客。这个英文博客是直接绑定在 Listed 服务上的,想更多的记录一些想要写成正式文章又想早些公开的想法,当然目前也只有比较完整的英文文章,主要还是之前作为体验写作的几篇。

而体验就是,Standard Notes 的客户端确实是一个不错的写作工具。

重新开始写博客

虽然有了英文博客,发布也方便得多,它仍然也只比这个中文的主博客多更新了一篇文章而已,原因也就是开头提到的那些问题。

最近也许会恢复更新博客这件事情也是最先在英文版上发布的。其实,这其中的第二重要的动机就是,最近我更新了一下 Standard Notes 的客户端们,发现现在的体验比上次有心情好好使用笔记的时候又好了许多。PC 端的不少 BUG 都修复了,终于有了可以管理附件的插件,而之前插件实现还在讨论阶段的移动端也已经支持了主题和编辑器插件。尽管移动端插件实现仍然是通过使用非原生代码(JavaScript),但是考虑到为了能完全和桌面端通用插件,减少复杂度,我觉得也没有很大问题;Standard Notes 的移动端 App 也可能是我见过的体验最好的非原生代码实现的 App 之一了,在现在的状态几乎与原生体验无异。而 Listed 支持绑定自定义域名,也是我这次更新以后发现的事情。

开头已经说过,我并不再想通过老的方式来写作博客了,而且也不想使用某些写作体验更不舒服的博客方案。之前已经发现 Standard Notes 作为写作工具的潜力,但是我也不希望直接把我的整个独立博客都替换成 Listed -- 那样就失去我专门造个博客的价值了。

我需要想办法将 Standard Notes 与我自己的博客程序,Typeblog,整合。这时候,我的计划是使用 Standard Notes 的 Actions API 模仿 Listed 展示一个用于发布博客的菜单。但我很快发现了这么做的问题: Actions 是通过请求远程接口实现的,无法在 Standard Notes 客户端中加密保存任何数据,这也就无法实现从客户端自定义配置 (e.g. 指定 Typeblog 的博客所在的上游 git repository 地址)。虽然即使做出来,这个东西大概也就我一个人使用,但是这样实在令我感觉非常不优雅 —— 毕竟,就连 Typeblog 这个只有我一个人使用的东西本身,也被我加上了很多配置选项甚至扩展。

于是,我尝试给 Standard Notes 写一个 editor-stack 扩展,从自定义远端的 Git repository 里面读取配置,允许直接将当前编辑的 Note 推送到这个 Git repository 并更新 Typeblog 的 config.json 以发布文章。但是很快,我发现要这么做的话至少需要一个将 Git 的功能暴露在 HTTP 上的程序[4],而我的博客的存储部分本身就是托管在具有 HTTP API 的 GitHub 上[5];而 Standard Notes 的本来就有一个名为 GitHub Push 的插件可以直接将笔记原文推送到 GitHub Repository。尝试以后,发现它可以选择推送的目标 repository、目录前缀和文件名后缀,而文件名则是与笔记标题相同。因此,以下 workflow 似乎就可以完全满足需要:

  1. 创建标题为新文章的URL链接域名后部分的笔记,比如这篇的 blogging-with-standardnotes
  2. 写作新文章内容
  3. 使用 GitHub Push 推送到博客 repo 的 posts/ 目录下,后缀名 md
  4. 创建名为 config.json 的笔记并保证其内容与原来 repo 中的同名配置文件相同
  5. 将已经 push 的新文章的相对路径加入这个配置文件
  6. 将其推送到远端 config.json,后缀当然是 json

如果自己编写插件的话,那么手动在笔记软件中同步配置文件的不优雅的几步完全可以自动执行,但是我再次决定这点麻烦并不算麻烦。也许等下一次我因为这种问题懒得更新博客的时候,我就会想起来去解决这个问题了吧。

仍然存在的问题

  • StandardNotes 手机 App 仍未支持 Actions API 和 editor-stack 插件,其中就包括了我使用的 GitHub Push, 也就是说我无法在手机上直接更新博客
  • GitHub Push 仅限于 GitHub API,将来我的博客存储也迁移到自建方案以后需要自己重写这个插件
  • StandardNotes 的部分编辑器插件在处理非英文输入法的时候存在奇怪的 BUG
  • 我还没有实现比较方便的直接给博客贴图的插件,目前仍然是手动上传到自己的 minio
  • 这套自制博客方案实在太垃圾,而且已经多年没有维护,大概是时候重新做一个了……

总结

本来这篇文章的计划是比较各种方案的优势和缺点,以及最后推荐 Standard Notes 并对它作出评测。写完之后回头看才发现,这已经变成了完全在记述“心路历程”的文章。也罢,我确实也一直不太擅长评测这样的事情。

总之,这就是我的新的博客 workflow,也希望我在这篇文章之后能告别咕咕咕……

脚注

  1. Standard Notes 背后的 Standard Files 协议本身经过了一次 Security Audit,但是其具体客户端实现并没有;这个协议也确实还有一些可以发现的问题;不过 Standard Files 至少本身是 self-hosted,协议也基本上都按照正常的 security practice 在做,不像某 T 开头的聊天工具
  2. 使用扩展会带来更高的安全风险,这是可以预料的,尤其是编辑器类的扩展,好在扩展有 iframe 隔离并且必须显式声明权限
  3. 或者我没有发现 Listed 开源在哪里
  4. 浏览器端的 JavaScript 难以操作原生 Git,而 Standard Notes 的插件并没有浏览器端和桌面端的硬性区分
  5. Typeblog 的程序使用 Git 管理文章源文件;博客还暂时没有迁移到自建的 Gitea,但是即使迁移之后本质上也是一样的,只是需要用 Gitea 的 API 重新做一个插件罢了