panatgithub_AnkiHeadingSync/docs/card-answer-cutoff-decisions.md
Dusk 4052eff0e7 feat: 新增 cardAnswerCutoffMode 卡片正文截止模式,支持 heading-block / double-blank-lines,并统一 marker/GI 写回归整规则
feat: add cardAnswerCutoffMode with heading-block / double-blank-lines and unify marker/GI writeback normalization.
2026-04-23 10:16:41 +08:00

4.9 KiB
Raw Blame History

Card Answer Cutoff Decisions

Date: 2026-04-23

Decision 1: Add a new explicit settings enum

  • Add cardAnswerCutoffMode to PluginSettings as:
    • heading-block
    • double-blank-lines
  • Default value is heading-block.
  • Validation rejects anything else.

Reason:

  • This matches the plan and keeps the new behavior fully explicit in persistence, tests, and fingerprint invalidation.

Decision 2: Use one shared boundary helper for all three parsing routes

  • Introduce a shared helper under manual-sync parsing services.
  • The helper will accept:
    • source lines for the current region
    • absolute start line
    • fallback line for empty content
    • cutoff mode
    • marker matcher/parser callbacks
  • The helper will return at least:
    • contentLines
    • trailingLines
    • contentEndLine
    • markerLine
    • markerIndent
    • marker parse result when valid
    • marker presence state

Reason:

  • The actual repository currently duplicates this logic three times. Consolidation is the smallest change that enforces plan-wide consistency.

Decision 3: Marker precedence scans the whole region, not only the last non-empty line

  • The helper will search for the first valid marker candidate inside the region.
  • A valid marker always wins over blank-line cutoff.
  • Only valid markers lock the cutoff boundary.
  • Invalid marker candidates do not lock the boundary.
  • If no valid marker exists and mode is double-blank-lines, the first run of 2 or more consecutive blank lines ends the content.
  • If neither applies, fallback remains the full heading block / child region / group block.

Reason:

  • The plan requires old layout compatibility and new layout support where marker/GI is moved before remarks.

Decision 4: Preserve current hashing semantics except for intentional cutoff changes

  • Normal QA/Cloze rawBlockText stays heading + trimmed content lines.
  • Semantic QA rawBlockText stays semantic-qa child identity + parent display title + child label + bodyMarkdown.
  • QA Group rawBlockText stays based on stem, parsed items, and normalized body lines.
  • The only intended hash shifts are those caused by the new cutoff rule excluding remarks/trailing content from the answer region.

Reason:

  • This keeps state recovery behavior as stable as possible while still honoring the new contract.

Decision 5: Normalize marker/GI writeback with a shared blank-tail routine

  • Card and group marker services will share a common normalization helper.
  • The writeback contract is:
    • trim blank lines immediately after the answer region
    • remove existing marker line when replacing
    • insert the new marker at contentEndLine
    • remove any blank lines between marker and trailing content
    • if trailing non-blank content exists, insert exactly two blank lines after the marker
    • preserve trailing non-blank content exactly as scanned
  • QA Group writeback will continue to strip legacy inner ID markers.

Reason:

  • The current services already do most of this. Sharing the normalization step reduces divergence and makes the plans rule explicit.

Decision 6: Thread cutoff mode through indexing context only

  • Add the setting to CardIndexingContext.
  • FileIndexerService passes it into CardIndexingService.index() for both vault and single-file indexing.
  • No rendering-layer contract changes are needed.

Reason:

  • The feature affects indexing and writeback placement, not render output formatting.

Decision 7: Put cutoff mode into the deck-rules fingerprint path

  • Update createDeckRulesFingerprint() to include cardAnswerCutoffMode.
  • Keep the existing fingerprint path rather than adding a parallel reindex fingerprint.

Reason:

  • The repository already uses this fingerprint to force rereads on unchanged files. Reusing it is the smallest compatible integration.

Decision 8: Keep the settings UI shape consistent with the repository

  • Add one dropdown setting labeled with the two cutoff modes.
  • Add concise zh/en descriptions that mirror the user-facing semantics from the plan.
  • Cover both locales in existing settings tab tests.

Reason:

  • The existing settings page relies on dropdown/toggle controls and is already tested that way.

Decision 9: Test coverage will be expanded at the service level first

  • Primary behavior tests go into:
    • CardIndexingService.test.ts
    • SemanticQaListParser.test.ts
    • CardMarkerService.test.ts
    • GroupMarkerService.test.ts
    • FileIndexerService.test.ts
    • ManualSyncService.test.ts
    • PluginSettings.test.ts
    • PluginSettingTab.test.ts
  • Add QA Group parser/service tests only where the cutoff or writeback behavior actually changes.

Reason:

  • These files already exercise the real runtime path and minimize new test scaffolding.

Confirmed Non-Goals

  • No expansion of QA Group answer modeling beyond the existing “first second-level item” contract.
  • No new sync pipeline or new persistence store shape.
  • No unrelated deck, render, or note-field-mapping refactor.