panatgithub_AnkiHeadingSync/docs/settings-anki-template-cache-status-gap-report.md
Dusk ffad94dd18 feat: 优化设置页模板缓存状态 / improve settings template cache status
- 设置页在有缓存时立即显示缓存摘要,并在打开时只做一次轻量 note type freshness 检查
- 手动读取仍负责完整模板与字段刷新,不会在页面打开时读取所有字段
- Add tests and docs for cache summary, stale-cache warning, and failed background checks
2026-04-25 11:18:11 +08:00

5.7 KiB
Raw Permalink Blame History

设置页 Anki 模板缓存状态差距审计

本文基于当前仓库真实实现,审计“设置页模板缓存状态显示 + 后台轻量 freshness 检测”的落地差距。

1. 当前设置页已是 cache-first 下拉,但不是 cache-first 状态

  • 入口在 src/presentation/settings/PluginSettingTab.ts
  • renderCardTypesCard() 打开时会先执行 hydrateVisibleCardTypeCaches()
  • note type 下拉来自:
    • availableNoteModels,若为空则退回 settings.ankiNoteTypeCache
  • 字段下拉来自:
    • settings.ankiModelFieldCache 水合出的 loadedModelDetails
    • 再经 getCurrentMappingForConfig() 生成 draft mapping

这说明缓存实际上已经参与首屏渲染,当前问题不是“缓存没被用”,而是“状态文案没有反映缓存可用性”。

2. 当前首屏状态文本仍固定为 idle

  • 当前类内状态是:
    • cardTypeStatus: UserFacingMessage = NOTE_TYPE_STATUS_IDLE
  • NOTE_TYPE_STATUS_IDLE 指向 settings.mapping.status.idle
  • renderCardTypesCard() 直接渲染 renderUserFacingMessage(this.cardTypeStatus)
  • cardTypeStatus 只会在以下情况变化:
    • saveCardTypeConfig() 校验失败
    • loadAnkiCardTypeConfig() 开始 / 成功 / 失败

结果:即使已有 ankiNoteTypeCacheankiModelFieldCache,首次打开设置页仍显示“请先从 Anki 刷新”语义,而不是缓存摘要。

3. 当前设置页打开时不会做任何后台 freshness 检测

  • display() -> renderCard("card-types") -> renderCardTypesCard() 的当前路径里没有自动调用:
    • listNoteModels()
    • getModelFieldNamesByModelNames()
    • getNoteModelDetails()
  • 唯一主动访问 Anki 的入口仍是点击“手动读取 Anki 里的模板配置”。

这与目标方案的差距是:当前没有“只跑一次 modelNames 的轻量后台检测”。

4. 当前完整刷新流程已经符合“手动触发全量字段读取”约束

  • loadAnkiCardTypeConfig() 当前真实流程是:
    • listNoteModels() 一次
    • getModelFieldNamesByModelNames() 一次
    • updateSettings({ ankiNoteTypeCache, ankiModelFieldCache })
    • hydrateVisibleCardTypeCaches()
    • syncQaGroupMappingFromCache()
    • renderCard("card-types")
  • 当前不会在设置页打开时读取所有字段。

这意味着本轮应保留该完整刷新流程,只新增轻量 listNoteModels() 检测,不改 full refresh 语义。

5. 当前 card 1 已具备局部刷新壳层,可直接复用

  • display() 只在首次进入时 containerEl.empty() 和初始化 card shells。
  • 后续都是 renderCard(cardId) 的局部刷新。
  • loadAnkiCardTypeConfig()saveCardTypeConfig()saveFieldMapping() 等都只刷新 card-types 卡片。

这与目标方案一致:后台检测完成后只需 renderCard("card-types"),不需要整页重建。

6. 当前没有“一次性检测状态”或“override 状态”的分层模型

  • 当前只有一个 cardTypeStatus 字段,同时承载:
    • idle
    • manual refresh loading
    • manual refresh success
    • manual refresh failure
    • validation failure
  • 没有单独的:
    • cache freshness check 状态
    • status override
    • detected live note type list
    • one-shot lifecycle promise

结果:如果直接把后台检测结果写回 cardTypeStatus,会和手动刷新成功/失败状态互相覆盖。

7. 当前没有防重入机制来保证“一次生命周期只检测一次”

  • 现在 renderCard("card-types") 会在很多交互后被反复调用:
    • display 初始渲染
    • toggle/heading/note type 变更
    • QA Group warning acceptance
    • manual refresh 前后
  • 但类内没有任何 Promisestatus 标记来阻止重复启动 live 检测。

因此如果要加后台检测,必须把“是否已检测过/是否检测中”挂在 settings tab 实例上,而不是放在 render 逻辑里临时判断。

8. 当前测试已证明缓存渲染存在,但未覆盖状态与后台检测

  • src/presentation/settings/PluginSettingTab.test.ts 当前已经覆盖:
    • ankiNoteTypeCache 渲染 note type 下拉
    • ankiModelFieldCache 渲染字段下拉
    • 手动完整刷新只触发 listNoteModels() + getModelFieldNamesByModelNames()
    • getNoteModelDetails() 在设置页刷新路径中不应被调用
  • 但未覆盖:
    • 有缓存时首屏状态摘要
    • 首屏后台 listNoteModels() 轻量检测
    • 检测 changed / failed 分支
    • 无缓存时不启动后台检测
    • 手动 refresh 成功后清除 changed 提示

9. 当前 i18n 里缺少缓存状态分层文案

  • 当前 card types 相关文案已有:
    • loadedSummary
    • failedLoad
    • failedSave
    • loadAnki.loading
  • 但没有:
    • cacheEmpty
    • cacheSummary
    • cacheChanged
    • cacheCheckFailed

结果:即使实现了 cache-aware status也没有现成 i18n key 可用。

10. 结论

当前仓库与目标方案的差距主要集中在状态模型,不在缓存渲染主链:

  1. 缓存已经用于下拉和字段渲染,但状态文本仍是固定 idle。
  2. 设置页打开时没有任何轻量 modelNames freshness check。
  3. 当前缺少 cache status 与 manual refresh status 的分层模型。
  4. 当前缺少防止重复后台检测的实例级状态。
  5. 当前测试和 i18n 都还不知道这套缓存状态语义。

因此最安全的仓库兼容路径是:

  1. 保留现有缓存渲染和 manual full refresh 流程。
  2. 新增实例级的一次性 listNoteModels() 后台检测状态。
  3. 用统一的 getCardTypeStatusMessage() 计算首屏状态与 override 状态。
  4. 让后台检测只影响 card 1 的状态提示,不触碰 data.json 和字段缓存。