Anki 支持 Markdown 的模板
历程
呀啊,这周算是圆了高三的一个梦,那就是加强了 Markdown 模板。
初版模板是基于插件(已停止维护)Markdown and KaTeX Support,后面将其中的脚本迁移到了一个继承的模板后就移除了。在那会我记得还是零差评呢。这个继承下来的模板也经过了不小改动,但从几个图标还是能看出来原主,有机会讲讲。
上个学期刚开学时让这个模板支持了白天模式,结果直接终结了黑夜模式长达数年的统治,我现在反而觉得白天模式更顺眼了,黑夜模式有种「老气」。
哦,原来 Markdown 是将加粗弄为 strong 标签而非 b 标签,是我误解了,难怪难怪。我以前只知道斜体是 emph 不是 i。
而这周一的时候,正在制卡,正好制到了需要 Mermaid 图表辅助说明的地方。联想到这学期的笔记 Mermaid 图表不少,而且对理解很有帮助、不可或缺,要是手动截图的话太麻烦了,因此就决定像是之前一样手动去支持一下。
啥叫「像是之前」?模板是基于上面插件的模板不假,但此前我有过脚注的需求,因此自行加了 footnote 插件。此外还手动更新了一些依赖、修复了一些问题等等,反正就是根据自己的需求定制了一番。因此就打算依葫芦画瓢弄了 Mermaid。
不过这个确实不好弄,我找了一番没找到合适的,基本上相关的插件都太老了,Mermaid 7 什么的,现在已经 11 了,API 已经改了不少,用不了了。
于是干脆就抛给 Gemini,顺带决定重写一下,总觉着现在这样就是不断屎糊墙,太丑陋了。
哦突然想到 To Do,目前最早的一个尚未完成的 To Do,居然正好也是 9.3 哎,说起来今年 9.3 估摸着能看阅兵哎。
这个也蛮早了,上面插件的作者不维护后,有人进行了重构,解决了一些问题,即 REWORK 版本。于是我就寻思着整合了一下,最早的想法看了看估计能追溯到高考后那段时间。
既然说到这了,不如就来说一下原始模板有些什么问题吧。不过其实说的是我改版后的原始模板,具体可以追溯到重写前的最后一版 _Markdown-KaTeX.js
。
欲知后事如何,且听下回分解,今晚太摆了,没写多少。下面是之前写的先复制过来,后面也要改改。
简介
警告
未经过大规模测试,在边缘情况可能还存在问题,欢迎试用、反馈或贡献!此外脚本基于本人的需求而定制,可能有额外少见的用例会影响使用,可自行选择剔除,但不提供相关支持。
脚本 GitHub 地址 _Anki-Markdown.js
,下面内容基于版本 v1.0.1。
一个增强版的 Anki Markdown 脚本,支持:
- Markdown 语法
- 基础语法
- KaTeX 数学公式(含 mhchem 支持)
- highlight.js 语法高亮
==xxx==
标记高亮支持- 脚注支持
- Mermaid 图表支持
- 可自定义拓展
- Cloze 支持
- 支持为 Cloze(
.cloze
,.cloze-inactive
)配置样式 - 支持嵌套 Cloze
- 支持在 KaTeX 公式、代码块等中使用 Cloze
- 不支持在 Mermaid 图表中使用 Cloze
- 支持为 Cloze(
- ……
展示
以 Cloze 为例,字段内容如下(公式有笔误但不影响内容):
1 | ### This is a test |
注意,一些字符会给转为 HTML,如
<
变成<
等,因此与实际 Markdown 语法略有不同。
Cloze 的颜色是 #ec6c4f
,未激活的 Cloze 颜色是 #a8c5f2
,同时加上了下划线。可以自行修改配置,在这里区别开来便于展示。
Cloze 1 正面内容如下:
Cloze 1 反面内容如下:
Cloze 2 正面内容如下:
Cloze 2 反面内容如下:
使用说明
首先要将相关依赖放在 collection.media
目录,包括 _Anki-Markdown.js
与脚本内部出现的依赖(下面的依赖是可选的,若本地文件不存在会从 CDN 获取),可能有:
_katex.css
_highlight.min.css
_katex.js
_auto-render.js
_markdown-it.min.js
_highlight.min.js
_mhchem.js
_markdown-it-mark.min.js
_markdown-it-footnote.min.js
_mermaid.min.js
- …
以 Cloze 类型的笔记模板为例,Basic 类型类似。
上面用于演示的笔记模板如下(局部):
1 | <div class="border1"> |
其中 问题
是笔记模板的第一个字段。
使用方法如下:
- 为想要添加 Markdown 支持的字段添加 id,如上面的
front
- 在模板中添加
<script src="_Anki-Markdown.js"></script>
随后打开 _Anki-Markdown.js
,在 --- Configuration ---
栏目中的 config
常量配置中,为 fieldIds
添加对应的字段 id,如果有 Cloze,还要添加到 clozeFieldIds
。默认值如下:
1 | /** @type {string[]} Field IDs to render */ |
两面都是类似的操作。
配置说明
可在 _Anki-Markdown.js
中修改配置,例如上面的进行渲染的 字段 id。
此外可以修改 resources
变量,在里面添加所需的资源文件,内部顺序会影响加载顺序。默认会从本地目录(即 collection.media
)加载,如果没有则从配置的 CDN 加载。可以选择类型与依赖等等:
1 | { |
上面是 KaTeX 的 mhchem 依赖的例子。
katexOptions
是 KaTeX 的配置,里面有大量个人的配置(包括用于兼容的旧配置),如 macros
宏配置等,可以自行修改。
markdownOptions
是 markdown-it 配置、mermaidOptions
是 Mermaid 配置,同上。
此外脚本内预留了一些可自定义的部分,同时还有清晰的注释与调试语句,可以自行修改扩展并调试。
已知限制
见评论部分。
开发指引
此部分仅供开发者参考,普通用户可忽略。
其他部分的功能基本比较稳定,除了部分 HTML 转义可能出现未处理的问题外,最有可能出现问题的就是 Cloze 部分,尤其是出现在 KaTeX 公式或代码块中的 Cloze。
下面以一个例子简要说明该脚本 Cloze 处理的逻辑:
1 | This is {{c1::a test}}. |
会被 Anki 处理为:
1 | <!-- Cloze 1 --> |
或
1 | <!-- Cloze 1 --> |
可见有两种情况:
- 激活的 Cloze:class 为
cloze
,此外还有data-cloze
属性保存 Cloze 内容 - 未激活的 Cloze:class 为
cloze-inactive
两种 Cloze 都有 data-ordinal
属性,表示 Cloze 的序号。
在普通的 Markdown 解析中,这个没有问题,基本不会对正常渲染产生干扰。
但是在 KaTeX 公式块,或者是 highlight.js 语法高亮的代码块中,就可能出现解析异常。
而同时, Cloze 的 span 标签中的内容是要参与接下来的 Markdown 渲染(包括 KaTeX, Mermaid 等),同时在最后还要支持 Cloze 的自定义样式,因此不能简单地将 Cloze 相关标签删除了事。
此脚本于是采用了一种思路——使用基本用不到的 Unicode 字符替换掉 Cloze 的 span 标签,尽最大可能减小对接下来渲染的干扰。使用 ⛶i🄀xxx⛿i🄀
的格式,其中 i 是 Cloze 的唯一序号,xxx 是 Cloze span 标签中的内容。
i 作为索引,标识了脚本保存在 clozePlaceholdersData
中的具体信息,随后辅助会在渲染结束后进行恢复。
例如上面的 Cloze 1 的内容会被替换为:
1 | This is ⛶1🄀[...]⛿1🄀. |
Cloze 2 的内容会被替换为:
1 | This is ⛶1🄀a test⛿1🄀. |
但是非常不幸的是,即使是使用这样的替换,依旧是会对 KaTeX 公式或代码块的渲染产生影响。
KaTeX 部分以下面为例进行演示:
$a{{c1::b}}c$ |
这个在替换 Cloze 并进行 KaTeX 解析后会变成:
1 |
|
可见在 katex-html
class 的 span 中,分界符 ⛶1🄀
与 ⛿1🄀
被包裹在了 <span class="mord">...</span>
中,因此不可以简单地替换回来,必须要将这个外标签剥离去除。
此外除了 mord
class 外,针对不同位置的 Cloze,还可能出现 mtight
等 class,因此我在脚本中这样进行预处理:
1 | // --- Pre-processing step to remove KaTeX spans around markers --- |
目前是一个很脆弱的补丁,可能需要随着新问题的发现而进行改进。
代码块部分以下面为例:
1 | ```python |
这会给渲染成
1 | <pre |
可见分界符甚至给拆分了,用于标识序号的数字,被 <span class="hljs-number">...</span>
包裹了。
此外这个也并不一定会发生,发生时也未必成对,因此在脚本中进行了比较复杂的处理,这里不详述。
也许可以将序号换成不同的 Unicode 字符,或许能避免这个问题。