复用 Anki 卡片模板、CSS 与媒体
在多个牌组中复用同一套 Anki 卡片设计:把笔记类型的模板、CSS 和媒体放在同一目录中,用于普通卡片或 Cloze 卡片。示例使用 Rust;Python 和 Node.js 分别通过 NoteType.from_bundle 和 NoteType.fromBundle 加载模板包,参见 Python API 和 Node API。
模板包包含可复用的笔记类型定义、模板文本、CSS,以及显式声明的全部媒体依赖。NoteType::from_bundle(directory) 校验完整模板包并保存模板包及媒体数据的快照,再返回与内联编写相同的不可变 NoteType。它不会部分修改项目。
运行模板包示例
章节:运行模板包示例cargo run --locked -p ankiforge --example docs_workflow -- target/docs-examples该流程加载自定义 Cloze 测试模板,写出 target/docs-examples/template.apkg。模板目标为 Languages::Cloze,一条笔记生成两张卡片。
Manifest 与模板引用
章节:Manifest 与模板引用Manifest 文件名为 anki-template.yaml。只接受新版格式:
format_version: template-bundle-v2note_type: key: language-cloze name: Language Cloze cloze_field: text fields: - key: text name: Sentence sort: true required: true - key: extra name: Extra templates: - key: cloze name: Cloze front_file: front.html back_file: back.html browser_front_file: browser-front.html target_deck: Languages::Clozecss_file: style.cssnote_type.key、字段键和模板键是必填稳定标识符。name 可选,默认为对应键。cloze_field 选择 Cloze 模型,不需要单独声明 kind。未设置 required: true 的字段可以省略。未知 manifest 属性会被拒绝。
所有模板都使用字段键,包括正面、背面和浏览器变体:{{cloze:text}}、{{extra}} 以及 {{#extra}} 等条件区块。Sentence 和 Extra 等显示名称在绑定完成后为 Anki 生成。加载器在错误中保留原始文件路径和模板字节范围。
加载与复用模型
章节:加载与复用模型fn bundle(output: &Path, template_directory: &Path) -> anyhow::Result<()> { let model = NoteType::from_bundle(template_directory)?; let mut project = Project::new("docs-languages")?; project.add( "capital", model .note() .field("text", "{{c1::Madrid}} is in {{c2::Spain}}.") .field("extra", "A city and its country."), )?; let built = project.build(BuildOptions::to(output.join("template.apkg")))?; verify(&built, 1, 2, 0)?; Ok(())}在独立应用中,以下完整示例要求把同一模板包复制到 fixtures/template-bundle:
use ankiforge::{BuildOptions, NoteType, Project};
fn main() -> anyhow::Result<()> { let model = NoteType::from_bundle("fixtures/template-bundle")?; let mut project = Project::new("bundle-example")?; project.add("capital", model.note() .field("text", "{{c1::Madrid}} is in {{c2::Spain}}.") .field("extra", "A city and its country."))?; let output = project.build(BuildOptions::to("bundle-example.apkg"))?; assert_eq!(output.report().counts().cards, 2); Ok(())}返回的模型不依赖源目录。可以与另一项目共享,也可在源目录删除后继续使用。加载失败不会返回部分模型,并会清理未共享的快照。
资源与预算
章节:资源与预算显式声明所有原始模板、CSS 和脚本资源。每个 assets 条目使用相对于模板包的路径和固定导出名称:
assets: - path: assets/badge.png export_as: badge.png - path: assets/labels.woff export_as: labels.woff然后模板可使用 <img src="badge.png">,CSS 可使用 url('labels.woff')。css_file 指定 CSS 源文件;资源不会从任意 HTML、CSS 或 JavaScript 中自动推断。声明的资源即使没有检测到静态引用也保留在 APKG 中。导出名称遵守与媒体相同的可移植名称及 Unicode/大小写冲突规则。包括扩展名在内的完整导出名称最多为 255 个 UTF-8 字节。JSON Schema 检查语法和字符数;NoteType::from_bundle 还会在返回已校验模型前执行字节预算、文件内容与引用检查。仅通过 schema 检查并不能保证加载成功。
绝对路径、目录穿越和指向模板包外部的符号链接会被拒绝。Manifest 上限为 256 KiB;每个模板或 CSS 文本文件上限为 2 MiB,且必须为 UTF-8。资源快照使用默认媒体预算,也可由调用方通过 NoteType::from_bundle_with_limits 提供 MediaLimits。预检长度和实际读取字节数都会检查。
模板包错误保留 kind/code、源路径、适用时的字节偏移,以及底层 schema、I/O、YAML、UTF-8 或媒体错误。模型完成时校验 schema;添加笔记时另外检查项目级冲突。参见自定义笔记类型和排查问题。