panatgithub_AnkiHeadingSync/docs/obsidian-backlink-config-gap-report.md
Dusk b82e1dd53d feat: 支持可配置 Obsidian 回链显示名称与位置 / configurable Obsidian backlink label and placement
覆盖普通卡、Cloze、语义 QA 与 QA Group 12,新增回链名称与放置位置设置、渲染位置控制、QA Group 模板漂移更新与 renderConfigHash 失效。

Cover normal cards, cloze, semantic QA, and QA Group 12 with configurable backlink label and placement, placement-aware rendering, QA Group template drift updates, and renderConfigHash invalidation.
2026-04-23 23:08:00 +08:00

4.7 KiB
Raw Blame History

Obsidian Backlink Config Gap Report

Scope

本报告基于当前仓库真实代码审查,聚焦这五条链路与计划差距:

  • settings 默认值、校验、持久化与旧 data.json 兼容
  • 普通 QA / Cloze / 语义 QA 的回链渲染入口
  • Cloze title/body 合并影响面
  • QA Group 12 模板生成与 model drift 更新链路
  • renderConfigHash 对已同步卡片的失效机制

Confirmed Gaps

1. 普通卡回链仍硬编码在 ManualCardRenderer

src/domain/manual-sync/services/ManualCardRenderer.ts 当前只在一个分支里决定回链行为:

  • addObsidianBacklink = true 时始终向 renderedFields.body 末尾追加 <p><a ...>Open in Obsidian</a></p>
  • 没有 label 配置入口
  • 没有 placement 配置入口

这意味着普通 QA、Cloze、语义 QA 目前共享同一个“答案末尾 + 固定文案”硬编码。

2. Cloze 兼容点不在 renderer而在现有字段映射合并逻辑

当前仓库结构里Cloze 仍然由 renderer 先产出 title / body,再由 note field mapping 把它们合并进主字段。

因此实现时不需要改 Cloze 的字段结构;只要保证 question-last-line 改的是 titleanswer-first-line / answer-last-line 改的是 body,现有 title<br><br>body 合并形状就会自然继承新位置。

3. QA Group 12 也仍然硬编码默认回链

src/application/services/QaGroupModelDefinition.ts 当前在模板生成里写死:

  • back 模板固定把 <a class="anki-heading-sync-backlink" href="{{Src}}">Open in Obsidian</a> 放在答案容器后
  • buildQaGroupModelDefinition() 没有接收 settings 或其他配置参数

src/application/services/QaGroupModelService.tsensureModel() 也始终调用无参 buildQaGroupModelDefinition(),所以模板漂移更新机制存在,但暂时无法感知用户配置。

4. QA Group note 字段链路已经独立,不需要改字段结构

src/application/services/QaGroupSyncService.ts 当前行为已符合计划的结构约束:

  • Src 仍由 buildGroupBacklink() 生成并写入 note fields
  • buildQaGroupNoteFields() 仍固定写 Stem / GroupId / Src / S01..S12
  • 没有通过普通 note field mapping 去映射 QA Group 字段

因此本轮不需要新增字段,也不需要改变 Src 含义,只需让 model template 生成使用当前 settings。

5. settings 持久化只做默认值合并,没有空 label 归一

src/infrastructure/persistence/DataJsonPluginConfigRepository.ts 当前对 settings 的处理是:

  • load 时直接用 DEFAULT_SETTINGS 合并旧 snapshot
  • save 时直接校验后原样写回

这能处理“缺字段”,但不能处理:

  • 旧 / 异常 data.json 中 obsidianBacklinkLabel 为空字符串或全空格
  • UI 保存时把全空格文案写进 settings

计划要求的空 label 归一,需要引入一个显式 normalize 层。

6. renderConfigHash 目前还不感知新配置

src/application/services/RenderConfigService.ts 当前 hash 只包含:

  • note model / mapping
  • addObsidianBacklink
  • convertHighlightsToCloze
  • keepPureTagLinesInCardBody

如果只修改 label 或 placement当前 diff planner 不会把已同步卡片判定为需要更新。

7. settings UI 与 i18n 尚未暴露新配置

src/presentation/settings/PluginSettingTab.ts 当前只在 sync options 区域渲染了 addObsidianBacklink toggle没有

  • label 文本输入
  • placement 下拉

中英文消息文件里也尚未存在这两组设置文案与 placement 校验错误文案。

8. 现有测试覆盖还缺四个关键面

当前仓库已有:

  • PluginSettings.test.ts
  • PluginSettingTab.test.ts
  • ManualCardRenderer.test.ts
  • QaGroupModelService.test.ts
  • QaGroupSyncService.test.ts

但还缺或不足:

  • settings 归一化与 placement 校验
  • label / placement 改变引起的 renderConfigHash 变化
  • 普通卡三种 placement 的断言
  • QA Group 模板三种 placement 与 drift 更新断言

Repository-Compatible Fix Direction

最安全的仓库兼容方案是:

  1. 在 settings 层新增集中 normalize 函数,供 load/save/UI 共用。
  2. 把普通卡回链渲染收口成共享 helper并在 ManualCardRenderer 内按 placement 分流到 titlebody
  3. buildQaGroupModelDefinition() 接收 label/placement并让 QaGroupModelService.ensureModel() 按 settings 生成模板。
  4. 把 label/placement 纳入 RenderConfigService hash。
  5. 只扩展现有测试锚点,不改 note 字段结构、不改 Src 语义、不做无关重构。

Out Of Scope

本轮不扩张到:

  • 新增 note 字段
  • 改写 QA Group field contract
  • 修改 Cloze 合并结构
  • 新增独立的 QA Group settings 面板
  • 调整现有 Obsidian backlink 开关语义