mirror of
https://github.com/panatgithub/AnkiHeadingSync.git
synced 2026-07-22 06:51:43 +00:00
EN: Implement one-way Obsidian to Anki tag sync, nested tag normalization, pure tag-line body cleanup, and QA Group coverage.\nZH: 实现 Obsidian 到 Anki 的单向标签覆盖、嵌套标签规范化、正文纯标签行清理,并补齐 QA Group 覆盖。
4.4 KiB
4.4 KiB
Tag Sync And Pure Tag Line Decisions
1. 单向覆盖语义
本次实现统一遵守以下所有权规则:
- Obsidian 是唯一真源
- Anki 是被覆盖目标端
- 标题、正文、tags、deck、QA Group 字段都以当前 Obsidian 计算结果为准
- 不保留 Anki 手工 tags
- 不做正文冲突合并
- 不给 QA Group 单独的所有权例外
2. 文件级标签来源
文件级标签统一从 Obsidian 官方 metadata cache 提取:
app.metadataCache.getFileCache(file)getAllTags(cache)
如果 cache 不存在或没有 tags,则返回空数组。
3. 标签规范化规则
规范化规则固定为:
- 去掉前导
# - 以
/分段 - 过滤空段
- 再以
::拼接输出到 Anki - 去重并保留首次出现顺序
- 保留 emoji
- 保留中文
- 保留原始大小写
示例固定为:
#3地区 -> 3地区#📖/一人公司 -> 📖::一人公司#a/b/c -> a::b::c
4. 纯标签行清理的接入位置
纯标签行清理不会放在 metadata tags 提取中,也不会写回源文件。
实现位置固定为共享正文预处理函数,输入输出都是 markdown 文本:
- 普通卡在
ManualCardRenderer渲染 body 前调用 - QA Group 在构造
Sxx_A前调用
这样可以满足:
- basic / cloze / semantic QA 复用同一规则
- QA Group 复用同一规则
- 关闭 tags 同步时仍可单独清理正文
5. 纯标签行清理规则
纯标签行定义固定为:
- 行首尾 trim 后
- 整行只包含一个或多个合法 tag token
- token 之间允许空白
- 不允许其他普通文字或说明性标点
处理规则固定为:
- 删除全文所有纯标签行
- 不删除行内标签
- 不删除含普通文字的标签行
- 不影响标题 trailing hashtags
- 删除后压缩因删除产生的多余连续空行,保留自然段间距
6. 设置与 fingerprint 策略
新增两个布尔设置:
syncObsidianTagsToAnki = truekeepPureTagLinesInCardBody = true
并把两者纳入 createDeckRulesFingerprint() 的 payload,同时提升 fingerprint version。
决策理由:
- 这两个设置都会影响同步产物
- 现有仓库已经用这个 fingerprint 触发文件重读
- 复用现有失效机制比引入第二套 fingerprint 更小更稳
7. 索引与状态策略
普通卡路径:
SourceFile.tags承载文件级 tagsCardIndexingService为 basic / cloze / semantic QA 写入tagsHint- 当
syncObsidianTagsToAnki = false时统一写空数组
QA Group 路径:
IndexedGroupCardBlock增加tagsHintGroupBlockState增加tagsHint- QA Group 的 tags 也完全由文件级 tags 决定
8. Diff 策略
普通卡更新判定新增一条:
existingState.tagsHint与card.tagsHint不同,则进入toUpdate
同时继续依赖 renderConfigHash 覆盖纯标签行保留开关变化。
QA Group 更新判定不新增独立 planner,而是在 QaGroupSyncService 内部比较:
- 字段变化
- deck 变化
- tags 变化
只要任一变化存在,就执行覆盖更新。
9. Anki 标签同步策略
接口层新增:
AnkiNoteSummary.tags?: string[]SyncAnkiNoteTagsInputAnkiGateway.syncNoteTags()
执行规则固定为:
- 新建 note 继续在
addNote/addNotes时直接写入 tags - 更新已有 note 时读取当前 tags
- 计算
removeTags与addTags - 同一 note 固定先 remove 后 add
- 无差异时不发额外请求
10. QA Group 仓库兼容决策
计划要求 QA Group 也应用纯标签行清理与 tags 覆盖。
基于当前仓库结构,本次不把 QA Group 重构进普通 renderer,而是:
- 保持
QaGroupSyncService作为专用同步器 - 在其字段构造阶段调用共享正文预处理
- 在其 note 同步阶段调用共享 tag diff 逻辑
这是本次唯一的仓库兼容偏差,目的是最小改动完成同等产品语义。
11. 测试范围锁定
本次必须补齐以下测试:
- Obsidian metadata tag 提取与规范化
- pure-tag-line cleanup 工具
- CardIndexingService 的
tagsHint - DiffPlannerService 的 tag-only / setting-only 更新
- QA Group 的 tags 与正文清理
- AnkiConnectGateway 的 note tags 读写
- AnkiBatchExecutor 的新建 / 更新 tag 行为
- settings 持久化与设置页 toggle 文案
不扩展以下范围:
- 反向同步
- 正文 merge 策略
- 新的 notice 流程
- QA Group 的 markdown 渲染重构