mirror of
https://github.com/panatgithub/AnkiHeadingSync.git
synced 2026-07-22 06:51:43 +00:00
在 include 模式下允许选中文件夹改用与全局 folderDeckMode 相反的映射规则 Add include-scope folder overrides that apply the opposite global folderDeckMode mapping 取消勾选文件夹时会清理该路径及其子树的覆盖记录,并把规则接入设置页与 deck fingerprint Clears override records for deselected folders and descendants, and wires the rules into the settings UI and deck fingerprint
65 lines
No EOL
4.7 KiB
Markdown
65 lines
No EOL
4.7 KiB
Markdown
# 文件夹级牌组模式覆盖 gap report
|
||
|
||
## 当前运行范围文件树结构
|
||
|
||
- 运行范围卡片由 `src/presentation/settings/PluginSettingTab.ts` 的 `renderScopeCard()` 渲染。
|
||
- scope 模式切换保存在 `PluginSettings.scopeMode`,文件夹选择保存在 `includeFolders` / `excludeFolders`。
|
||
- 文件树数据来自插件接口 `listFolderTree()`,在设置页内通过 `ensureFolderTreeLoaded()` 懒加载。
|
||
- 文件树选择状态由 `src/presentation/settings/FolderScopeTree.ts` 的 `buildFolderTreeSelection()` 与 `toggleFolderTreeSelection()` 计算。
|
||
- 单行 UI 由 `renderFolderNode()` 负责,现有 dataset 包括:`folderRow`、`folderToggle`、`folderPath`、`folderPathLabel`、`folderChildren`。
|
||
- 现有文件夹行是三列 grid:展开按钮、运行范围 checkbox、文件夹标签。
|
||
|
||
## 当前文件夹牌组映射流程
|
||
|
||
- 全局设置字段是 `PluginSettings.folderDeckMode`,取值 `off | folder | folder-and-file`。
|
||
- 普通卡片路径在 `src/application/services/RenderConfigService.ts` 中通过 `DeckResolutionService.resolve(card, defaultDeck, folderDeckMode)` 决定最终 deck。
|
||
- QA Group 路径在 `src/application/services/QaGroupSyncService.ts` 中也直接调用同一个 `DeckResolutionService.resolve(...)`。
|
||
- `src/domain/manual-sync/services/DeckResolutionService.ts` 当前优先顺序是:
|
||
1. `card.deckHint`
|
||
2. `FolderDeckMappingService.mapFilePathToDeck(filePath, folderDeckMode)`
|
||
3. `defaultDeck`
|
||
- `src/domain/manual-sync/services/FolderDeckMappingService.ts` 只按 `filePath` 和 `mode` 计算 deck,不知道运行范围或文件夹覆盖。
|
||
|
||
## 当前牌组优先级实现
|
||
|
||
- 文件级显式 deck hint 仍然是最高优先级,这一层已经在 `DeckResolutionService.resolve()` 最前面短路返回。
|
||
- 文件夹映射层完全依赖 `FolderDeckMappingService`。
|
||
- 只有当前两层都没有产出 deck 时才回退到默认牌组并附加 `deck_fallback_default` warning。
|
||
|
||
## 覆盖设置应存放的位置
|
||
|
||
- 新字段应加入 `src/application/config/PluginSettings.ts` 的 `PluginSettings` 接口与 `DEFAULT_SETTINGS`。
|
||
- 加载/保存路径通过 `mergePluginSettings()` 与 `normalizePluginSettings()` 进入 `DataJsonPluginConfigRepository`。
|
||
- 现有 include/exclude 文件夹列表只做“数组 + 字符串”校验,没有统一去重或路径规范化 helper;新字段应沿用同样约定,避免引入与仓库现状不一致的新归一化语义。
|
||
|
||
## UI 应插入的位置
|
||
|
||
- 只应插入到 `renderFolderNode()` 的 include 模式行内。
|
||
- 行内现有顺序是 toggle、scope checkbox、label;新增控件应追加在 label 后侧,并保持现有 dataset 不变。
|
||
- 仅当 `scopeMode === "include"`、当前行已选中、且全局 `folderDeckMode !== "off"` 时显示。
|
||
- 取消 include 勾选的清理逻辑不应放在 UI 层分散处理,而应合并进 `updateFolderSelection()`,这样任何同一路径的取消动作都能统一清理子树覆盖记录。
|
||
|
||
## 指纹更新位置
|
||
|
||
- deck 规则指纹在 `src/application/services/FileIndexerService.ts` 的 `createDeckRulesFingerprint()` 中生成。
|
||
- `indexVault()` 通过比较 `existingFileState.deckRulesFingerprint !== deckRulesFingerprint` 强制重读;因此覆盖规则必须进入该指纹。
|
||
- 当前版本号为 `deck-rules-v4`,实现覆盖后需要升级版本。
|
||
|
||
## 预计修改的测试文件
|
||
|
||
- `src/application/config/PluginSettings.test.ts`
|
||
- `src/infrastructure/persistence/DataJsonPluginConfigRepository.test.ts`
|
||
- `src/presentation/settings/PluginSettingTab.test.ts`
|
||
- `src/domain/manual-sync/services/FolderDeckMappingService.test.ts`
|
||
- `src/domain/manual-sync/services/DeckResolutionService.test.ts`
|
||
- `src/application/services/FileIndexerService.test.ts`
|
||
- 可能还要更新 `src/test-support/manualSyncFakes.ts` 的默认设置工厂,以便新字段默认值在各类测试夹具中可用。
|
||
|
||
## 风险点
|
||
|
||
- `toggleFolderTreeSelection()` 会压缩选择结果;覆盖清理必须基于“被取消的目标路径及其后代”,不能误删其他分支。
|
||
- 父文件夹覆盖应对子孙路径生效,但不能把 `notes` 误匹配到 `notes2`;应复用仓库里现有的 `path === folder || path.startsWith(folder + "/")` 规则。
|
||
- `DeckResolutionService.resolve()` 当前签名被多处调用,若扩展参数需要同步更新 `RenderConfigService`、`QaGroupSyncService` 和直接单测。
|
||
- 作用范围树的测试大量依赖现有 dataset;新增控件不能破坏 `folderRow`、`folderToggle`、`folderPath`、`folderPathLabel` 的查找方式。
|
||
- 设置页行布局当前是固定 grid,新增圆形 checkbox 后需要补 CSS,避免标签挤压或破坏缩进对齐。
|
||
- 当全局 `folderDeckMode` 为 `off` 时必须忽略覆盖列表,但不能清空已保存数据。 |