Skip to content

Conversation

@sysid
Copy link
Owner

@sysid sysid commented Oct 26, 2025

Summary

Implements a complete workflow for collecting markdown flashcards into Anki collections, enabling users to write flashcards in markdown format and sync them to Anki.

This adds the collect subcommand to process markdown files containing flashcards, convert them to HTML with proper formatting, and create or update notes in Anki collections.

Key Features

  • Markdown parsing: Parse sections with deck and tag metadata
  • Card type detection: Support for Basic (Q&A) and Cloze deletion cards
  • Markdown to HTML conversion: Full CommonMark support with MathJax for LaTeX math
  • Bidirectional sync: Inject note IDs back into markdown for future updates
  • Media handling: Copy and reference images in Anki media directory
  • Incremental updates: Hash-based change detection to avoid reprocessing unchanged files
  • Configuration: TOML config file support for defaults and settings
  • Recursive processing: Scan directories for markdown files

Usage

# Process single file
ankiview collect notes.md

# Process directory recursively
ankiview collect ./my-notes --recursive

# With specific collection
ankiview -c /path/to/collection.anki2 collect notes.md

Markdown Format

---
Deck: Programming
Tags: rust basics

1. What is Rust?
> A systems programming language focused on safety

2. Rust's ownership system prevents {data races} at compile time
---

Implementation Details

  • Architecture: Follows hexagonal/ports-and-adapters pattern
  • Components:
    • Domain models for Card trait, BasicCard, ClozeCard
    • Section parser for metadata and note extraction
    • Card type detection and field parsing
    • Cloze syntax transformation ({text} to {{c1::text}})
    • MathJax plugin for inline/block math rendering
    • File I/O with ID injection
    • Anki repository integration for note CRUD
    • CardCollector use case orchestrating the workflow
    • CLI integration with collect subcommand

Test Coverage

  • 171 total tests (126 library + 45 integration)
  • End-to-end integration tests covering full workflow
  • Test coverage for all components from parsing to Anki storage

Related

Completes the migration from the Python inka2 prototype to native Rust implementation with improved test coverage and architecture.

sysid and others added 30 commits October 21, 2025 18:49
Create hexagonal architecture module structure for inka2 migration:
- domain: pure business logic
- application: use cases
- infrastructure: external adapters (markdown processing)
- cli: command line interface

Add dependencies:
- markdown-it v0.6: Rust port of markdown-it.js for parsing
- toml v0.8: config file management
- sha2 v0.10: file hashing for change detection
- walkdir v2.4: recursive directory scanning
- reqwest v0.11: HTTP client for highlight.js downloads
- lazy_static v1.4: static regex compilation

Phase 1.1 of TDD migration plan complete.
Add inline math recognition for markdown-it parser:
- Detects $...$ patterns for inline LaTeX math
- Converts to MathJax \(...\) delimiters for rendering
- Validates no whitespace after opening $ or before closing $
- Implements InlineRule trait from markdown-it parser

Test coverage:
- given_inline_math_when_parsing_then_creates_math_token

Note: Avoid regex lookahead/lookbehind (not supported by Rust regex)
using manual character-by-character scanning instead.

Phase 1.2 of TDD migration plan complete.
Implement BlockRule for $$...$$ block math delimiters:
- Scans for opening $$ line
- Extracts multi-line content until closing $$
- Renders as MathJax \[...\] delimiters

Test coverage:
- given_block_math_when_parsing_then_creates_block_math_token
- given_mixed_math_when_parsing_then_handles_both_types

Phase 1.3 and 1.4 of TDD migration plan complete.
Phase 1 (Foundation & MathJax Plugin) fully implemented.
Add pure business logic domain entities:
- Card trait: common interface for all card types
- BasicCard: front/back question-answer cards
- ClozeCard: fill-in-the-blank cards with cloze deletions

Features:
- Builder pattern (with_deck, with_tags, with_id)
- Separate markdown and HTML storage
- Anki ID tracking for updates
- Support for tags and deck assignment

Test coverage:
- given_card_trait_when_implemented_then_provides_common_interface
- given_front_and_back_when_creating_basic_card_then_stores_fields
- given_basic_card_when_setting_deck_then_updates
- given_basic_card_when_setting_tags_then_stores
- given_text_with_cloze_when_creating_then_stores_text
- given_cloze_card_when_implementing_trait_then_provides_interface

Phase 2 (Domain Models) of TDD migration plan complete.
Add section extraction and metadata parsing:
- SectionParser: extract --- delimited sections from markdown
- extract_deck_name: parse Deck: metadata from sections
- extract_tags: parse Tags: metadata (space-separated)
- extract_note_strings: split sections into individual note strings

Features:
- Regex-based section detection with multiline/dotall support
- Manual parsing for note extraction (avoids unsupported lookahead)
- Handles ID comments (<!--ID:...-->) before notes
- Preserves note boundaries correctly

Test coverage:
- given_markdown_with_section_when_parsing_then_finds_section
- given_markdown_with_multiple_sections_when_parsing_then_finds_all
- given_markdown_without_sections_when_parsing_then_returns_empty
- given_section_with_deck_when_extracting_then_returns_deck_name
- given_section_without_deck_when_extracting_then_returns_none
- given_section_with_deck_and_whitespace_when_extracting_then_trims
- given_section_with_tags_when_extracting_then_returns_tag_vec
- given_section_without_tags_when_extracting_then_returns_empty
- given_section_with_empty_tags_when_extracting_then_returns_empty
- given_section_with_two_notes_when_extracting_then_returns_two_strings
- given_section_with_id_comments_when_extracting_then_includes_ids
- given_section_with_cloze_and_basic_when_extracting_then_finds_both

Phase 3 (Markdown Section Parser) of TDD migration plan complete.
Add card detection and parsing logic:
- is_basic_card: detect Q&A cards with > answer markers
- is_cloze_card: detect cloze cards with {} syntax
- parse_basic_card_fields: extract front/back from basic cards
- parse_cloze_card_field: extract text from cloze cards
- extract_anki_id: parse <!--ID:...--

> comment IDs

Features:
- Manual line-by-line parsing (avoids regex lookahead)
- Handles ID comments correctly
- Cleans answer prefixes (> markers)
- Preserves multiline content
- Error handling for malformed cards

Test coverage:
- given_note_with_answer_when_checking_type_then_is_basic
- given_note_with_multiline_answer_when_checking_then_is_basic
- given_note_without_answer_when_checking_then_not_basic
- given_basic_note_string_when_parsing_then_extracts_front_and_back
- given_basic_with_multiline_when_parsing_then_preserves_lines
- given_basic_with_id_when_parsing_then_extracts_without_id
- given_note_without_answer_when_parsing_then_returns_error
- given_cloze_note_string_when_parsing_then_extracts_text
- given_cloze_with_id_when_parsing_then_excludes_id
- given_cloze_with_short_syntax_when_parsing_then_extracts
- given_note_with_id_when_parsing_then_extracts_id
- given_note_without_id_when_parsing_then_returns_none
- given_note_with_invalid_id_when_parsing_then_returns_none

Phase 4 (Card Type Detection & Parsing) of TDD migration plan complete.
Add cloze syntax converter with three format support:
- Anki format: {{c1::text}} (passes through unchanged)
- Explicit short: {1::text} or {c1::text} → {{c1::text}}
- Implicit short: {text} → {{c1::text}}, {{c2::text}}, etc.

Protection system for code and math blocks:
- Temporarily replaces code blocks with placeholders
- Temporarily replaces math blocks with placeholders
- Prevents cloze conversion inside protected regions
- Restores blocks after transformation

Manual brace matching to find cloze patterns:
- Character-by-character scanning with brace counting
- Handles nested braces correctly
- Skips Anki format (double braces)

Test coverage (7/7 tests passing):
- given_anki_format_cloze_when_checking_then_returns_true
- given_explicit_short_cloze_when_converting_then_transforms_to_anki
- given_already_anki_format_when_converting_then_unchanged
- given_implicit_short_cloze_when_converting_then_numbers_sequentially
- given_cloze_with_code_block_when_converting_then_preserves_code
- given_cloze_with_inline_code_when_converting_then_preserves_code
- given_cloze_with_math_when_converting_then_preserves_math

Phase 5 of TDD migration plan complete.
Add full markdown rendering pipeline:
- Integrates cmark (CommonMark), extra plugins, and custom MathJax plugin
- Converts inline math $f(x)$ to \(f(x)\) delimiters
- Converts block math $$...$$ to \[...\] delimiters
- Removes newlines around HTML tags (Anki rendering quirk)
- Uses syntect for syntax highlighting with inline styles

Fixes:
- BlockMathScanner: return end line instead of end+1 to avoid index bounds error
- Section parser regex: use [ \t]* instead of \s* to prevent matching newlines
  (was incorrectly capturing next line content for empty tags)

Test coverage (6/6 converter tests + 3/3 mathjax tests passing):
- given_markdown_text_when_converting_then_renders_html
- given_markdown_with_newlines_around_tags_when_converting_then_removes_them
- given_markdown_with_math_when_converting_then_uses_mathjax_delimiters
- given_complex_math_when_converting_then_preserves_latex
- given_code_block_when_converting_then_preserves_for_highlightjs
- given_inline_code_when_converting_then_wraps_in_code_tag

Phase 6 (Markdown to HTML Conversion) of TDD migration plan complete.
All 41 markdown infrastructure tests passing.
Add file_writer module with markdown file reading:
- read_markdown_file: reads file content preserving IDs
- Uses anyhow for error handling with context
- Handles nonexistent files with errors

Test coverage:
- given_markdown_file_when_reading_then_returns_content
- given_file_with_ids_when_reading_then_preserves_ids
- given_nonexistent_file_when_reading_then_returns_error

Phase 7.1 (File Reader) of TDD migration plan complete.
Total tests: 66 (3 new file_writer tests)
Add inject_anki_id function:
- Injects <!--ID:...-->  comment before note pattern
- Skips injection if ID already exists
- Preserves formatting and whitespace
- Handles multiple notes correctly

Test coverage:
- given_note_without_id_when_injecting_then_adds_id
- given_note_with_existing_id_when_injecting_then_unchanged
- given_multiple_notes_when_injecting_then_targets_correct_note
- given_note_pattern_when_injecting_then_preserves_formatting

Phase 7.2 (ID Injection) of TDD migration plan complete.
Total tests: 70 (7 file_writer tests)
Add write_markdown_file function:
- Writes content to markdown file
- Overwrites existing files
- Preserves formatting in round-trip read/write

Test coverage:
- given_content_when_writing_then_creates_file
- given_existing_file_when_writing_then_overwrites
- given_round_trip_when_reading_and_writing_then_preserves_content

Phase 7 (File Writing with ID Injection) complete.
Total tests: 73 (10 file_writer tests)
Add AnkiRepository methods:
- find_or_create_basic_notetype: finds existing Basic notetype
- find_or_create_cloze_notetype: finds existing Cloze notetype
- Returns notetype ID for use in note creation
- Uses NotetypeKind to distinguish between types

Test coverage:
- given_new_collection_when_finding_basic_notetype_then_creates_and_returns_id
- given_existing_basic_notetype_when_finding_then_returns_same_id
- given_new_collection_when_finding_cloze_notetype_then_creates_and_returns_id
- given_existing_cloze_notetype_when_finding_then_returns_same_id

Phase 8.1 (Note Type Finder) of TDD migration plan complete.
Total tests: 77 (4 new anki infrastructure tests)
Add AnkiRepository methods:
- create_basic_note: creates Basic note with front/back fields
- create_cloze_note: creates Cloze note with cloze deletions
- Both methods handle deck creation and tag assignment
- Return generated note IDs for ID injection

Test coverage:
- given_basic_card_fields_when_creating_note_then_returns_note_id
- given_basic_note_when_created_then_can_retrieve
- given_cloze_text_when_creating_note_then_returns_note_id
- given_cloze_note_when_created_then_can_retrieve

Phase 8.2 (Create Note in Anki) of TDD migration plan complete.
Total tests: 81 (8 anki infrastructure tests)
Add AnkiRepository methods:
- update_note: updates existing note fields by ID
- note_exists: checks if note exists for duplicate detection
- Handles note not found errors gracefully

Test coverage:
- given_existing_note_when_updating_then_fields_change
- given_nonexistent_note_when_updating_then_returns_error
- given_existing_note_when_checking_exists_then_returns_true
- given_nonexistent_note_when_checking_exists_then_returns_false

Phase 8.3 & 8.4 (Update Note & Duplicate Detection) complete.
Total tests: 85 (12 anki infrastructure tests)
Add media_handler module with image path extraction:
- extract_image_paths: extracts local image references from markdown
- Supports markdown syntax: ![alt](path)
- Supports HTML img tags: <img src="path">
- Filters out HTTP(S) URLs (external images)
- Uses regex for pattern matching

Test coverage:
- given_markdown_image_when_extracting_then_returns_path
- given_multiple_images_when_extracting_then_returns_all_paths
- given_html_img_tag_when_extracting_then_returns_path
- given_mixed_formats_when_extracting_then_returns_all
- given_no_images_when_extracting_then_returns_empty
- given_absolute_urls_when_extracting_then_excludes_them

Task 9.1 (Image Extraction) of Week 5 TDD migration plan complete.
Total tests: 91 (6 new media_handler tests)
Add copy_media_to_anki function with deduplication:
- Extracts filename from source path
- Copies file to Anki's flat media directory
- Skips copying if file already exists (no overwrite)
- Returns filename only (not full path) for Anki references

Features:
- Handles nested source paths, flattens to media root
- Error handling for nonexistent source files
- Uses anyhow for error context

Test coverage (4 new tests):
- given_source_file_when_copying_then_file_appears_in_media_dir
- given_existing_file_when_copying_then_skips_duplicate
- given_nonexistent_source_when_copying_then_returns_error
- given_file_with_path_when_copying_then_returns_basename

Phase 9.2 (Media File Copying) of TDD migration plan complete.
Total tests: 95 (10 media_handler tests)
Add update_media_paths_in_html function:
- Takes mapping of original paths to Anki filenames
- Replaces all occurrences of original paths in HTML
- Handles both markdown and HTML img tag syntax
- Preserves unmapped paths unchanged

Features:
- Simple string replacement for maximum compatibility
- Supports multiple images in single HTML document
- Returns unchanged HTML when no mappings apply

Test coverage (5 new tests):
- given_html_with_image_src_when_updating_then_replaces_path
- given_html_with_multiple_images_when_updating_then_replaces_all
- given_html_with_no_matching_paths_when_updating_then_unchanged
- given_html_with_unmapped_image_when_updating_then_leaves_unchanged
- given_markdown_img_syntax_when_updating_then_replaces_path

Phase 9.3 (Media Path Updating) of TDD migration plan complete.
Total tests: 100 (15 media_handler tests)
Add Config struct with TOML serialization:
- Supports three sections: defaults, anki, highlight
- Loads config from inka.toml file
- Saves config to TOML file with pretty formatting
- Creates default config file
- All fields have sensible defaults via serde attributes

Config structure:
- defaults: profile, deck, folder
- anki: path, basic_type, front_field, back_field, cloze_type, cloze_field
- highlight: style

Features:
- Partial TOML support (missing fields use defaults)
- Round-trip serialization preserves values
- Error handling with anyhow context

Test coverage (6 new tests):
- given_no_file_when_creating_default_then_creates_with_defaults
- given_config_when_saving_then_writes_toml_file
- given_toml_file_when_loading_then_reads_values
- given_partial_toml_when_loading_then_uses_defaults
- given_nonexistent_file_when_loading_then_returns_error
- given_round_trip_when_saving_and_loading_then_preserves_values

Phase 10.1 (TOML Config) of TDD migration plan complete.
Total tests: 106 (6 config tests)
Add hasher module for file change detection:
- calculate_file_hash: computes SHA256 hash of file content
- has_file_changed: compares current hash with previous hash
- Uses sha2 crate for cryptographic hashing
- Returns lowercase hex string (64 characters)

Features:
- Detects file modifications by content changes
- Same content produces identical hashes
- Different content produces different hashes
- Error handling for nonexistent files
- Handles multiline content correctly

Use case:
- Enables incremental updates (only process changed markdown files)
- Will be combined with hash cache in Task 10.3

Test coverage (8 new tests):
- given_file_when_calculating_hash_then_returns_sha256
- given_same_content_when_calculating_hash_then_returns_same_value
- given_different_content_when_calculating_hash_then_returns_different_values
- given_nonexistent_file_when_calculating_hash_then_returns_error
- given_multiline_content_when_calculating_hash_then_handles_correctly
- given_matching_hash_when_checking_change_then_returns_false
- given_different_hash_when_checking_change_then_returns_true
- given_file_modified_when_checking_then_detects_change

Phase 10.2 (SHA256 Hashing) of TDD migration plan complete.
Total tests: 114 (8 hasher tests)
Add HashCache struct for file change tracking:
- Loads hash cache from JSON file (creates empty if missing)
- Saves hash cache to JSON file with pretty formatting
- Checks if file has changed (new files return true)
- Updates hash for a file in the cache
- Clears all hashes from cache
- Uses HashMap<filepath, hash> internally

Features:
- Persistent storage enables incremental updates across sessions
- JSON format for human readability and debugging
- Handles nonexistent cache files gracefully
- Tracks multiple files independently
- Detects both new files and modified files

Integration:
- Builds on calculate_file_hash and has_file_changed from Task 10.2
- Completes change detection infrastructure for inka collect command

Test coverage (7 new HashCache tests):
- given_nonexistent_cache_when_loading_then_creates_empty
- given_cache_when_saving_then_creates_json_file
- given_new_file_when_checking_then_returns_changed
- given_unchanged_file_when_checking_then_returns_false
- given_modified_file_when_checking_then_returns_changed
- given_cache_with_hashes_when_clearing_then_removes_all
- given_multiple_files_when_updating_then_tracks_all

Phase 10.3 (Hash Cache Persistence) of TDD migration plan complete.
Week 5 (Media & Config) fully implemented.
Total tests: 121 (15 hasher tests including HashCache)
Add CardCollector application use case that orchestrates the entire workflow:
- Reads markdown files with section_parser
- Extracts deck names, tags, and note strings from sections
- Detects card types (basic vs cloze)
- Parses card fields with card_parser
- Converts markdown to HTML with converter
- Transforms cloze syntax
- Creates new notes or updates existing notes in Anki
- Injects Anki IDs back into markdown files
- Handles media directory creation

Implementation details:
- Uses SectionParser::new().parse() for section extraction
- Converts sections to owned Strings to avoid borrow checker issues
- Supports both numbered basic cards ("1. Question? > Answer")
  and numbered cloze cards ("1. Text with {cloze deletion}")
- Maintains AnkiRepository instance for database operations

Test coverage (5 new tests):
- given_markdown_with_basic_card_when_processing_then_creates_note
- given_markdown_with_cloze_card_when_processing_then_creates_note
- given_markdown_with_multiple_cards_when_processing_then_creates_all
- given_markdown_with_id_when_processing_second_time_then_updates_note
- given_empty_markdown_when_processing_then_returns_zero

Task 11.1 (CardCollector orchestration) complete.
Total tests: 126 (5 new application tests)
Add 'collect' subcommand to ankiview CLI:
- Accepts file or directory path
- Supports --recursive flag for subdirectory processing
- Processes single markdown file, directory, or directory tree
- Prints summary of cards processed

CLI implementation (lib.rs):
- handle_collect_command: routes to CardCollector
- Handles file vs directory detection
- Non-recursive: processes only .md files in directory
- Recursive: delegates to process_directory method

CardCollector enhancements:
- process_directory: recursive markdown file discovery
- Uses walkdir crate for directory traversal
- Filters for .md extension
- Accumulates card count across all files

Test coverage (1 new test):
- given_directory_with_markdown_files_when_processing_recursively_then_processes_all
  - Creates nested directory structure
  - Verifies both files processed
  - Confirms non-markdown files ignored

Usage examples:
  ankiview collect notes.md                 # Single file
  ankiview collect ./notes                  # Directory (non-recursive)
  ankiview collect ./notes --recursive      # Directory tree
  ankiview -c /path/collection.anki2 collect notes.md

Tasks 11.2 (CLI subcommand) and 11.3 (recursive scanning) complete.
Total tests: 127 (6 card_collector tests)
Add comprehensive integration tests for the collect workflow:
- given_markdown_file_when_collecting_then_creates_notes_in_anki
  - Processes basic cards from markdown file
  - Verifies ID injection into markdown
  - Confirms correct count of IDs

- given_directory_when_collecting_recursively_then_processes_all_files
  - Creates nested directory structure
  - Processes multiple files in subdirectories
  - Verifies IDs injected in all processed files

- given_file_with_existing_ids_when_collecting_then_updates_notes
  - First run creates notes with IDs
  - Modifies content and runs again
  - Verifies update (not duplicate creation)
  - Confirms same ID maintained

- given_mixed_card_types_when_collecting_then_creates_correct_note_types
  - Processes basic and cloze cards together
  - Verifies all cards get unique IDs
  - Validates ID extraction and uniqueness

All tests use TestCollection helper for isolated temporary collections.
Tests verify the full workflow from markdown to Anki database.

Task 11.4 (End-to-end integration tests) complete.
4 new integration tests, all passing.
Add comprehensive example file demonstrating:
- Section structure with --- delimiters
- Deck and Tags metadata
- Basic card format (Q&A with > marker)
- Cloze deletion format ({} syntax)
- LaTeX math support ($...$ and 58120...58120)
- Usage examples for collect command

Provides users with a complete reference for writing
markdown flashcards compatible with ankiview collect.

Phase 11.5 (Documentation) of TDD migration plan complete.
All Week 6 tasks complete.
Address all clippy warnings across the codebase:

CardCollector:
- Remove unused note_id variables (just execute side effects)
- Prefix unused fields with _ (collection_path, media_dir)

Infrastructure:
- Use strip_prefix() instead of manual string slicing (card_parser)
- Return expression directly without intermediate let (cloze_converter)
- Derive Default instead of manual impl (config)
- Remove trivial assert!(true) test (inka/mod)

Tests:
- Replace vec![] with &[] slice syntax (anki.rs, test helpers)
- Use assert!(bool) instead of assert_eq!(bool, true/false) (test_cli)
- Use !is_empty() instead of len() > 0 (test_anki)
- Remove unused NoteRepository import (test_collect)
- Remove needless borrows for generic args (build_test_collection)
- Mark unused helper method with #[allow(dead_code)]

All 171 tests passing. Zero clippy warnings.
Implement Phase 1 of inka2 collect parity by adding file comparison
logic and --force flag to handle media file conflicts.

Changes:
- Add --force and --ignore-errors CLI flags to collect command
- Implement byte-by-byte file comparison in media_handler
- Add error on media file conflict without --force flag
- Skip copy optimization for identical files
- Update CardCollector to accept and thread force parameter
- Add comprehensive tests for all conflict scenarios

This is a breaking change: previously, conflicting media files were
silently skipped. Now they error unless --force is specified.

Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
Implement Phase 2 of inka2 collect parity by integrating hash-based
change detection to skip processing unchanged markdown files.

Changes:
- Add --full-sync CLI flag to bypass hash checking
- Integrate HashCache into CardCollector with conditional loading
- Add hash check at start of process_file to skip unchanged files
- Update hash after successful file processing
- Implement Drop trait to automatically save cache on exit
- Create ankiview_hashes.json in collection directory
- Add debug logging for skipped files

Behavior:
- Without --full-sync: Skips files that haven't changed since last run
- With --full-sync: Processes all files regardless of changes
- Hash cache persists between runs for performance optimization
- Uses SHA256 hashing (more secure than inka2's MD5)

Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
Implement Phase 3 of inka2 collect parity by adding ability to find
and inject missing/incorrect note IDs by searching Anki.

Changes:
- Add --update-ids CLI flag (-u short flag)
- Implement search_by_html in AnkiRepository to find notes by content
- Integrate update_ids mode into CardCollector
- When no ID exists and --update-ids is set:
  - Search Anki for note with matching HTML fields
  - If found: inject ID and update note
  - If not found: create new note as usual
- Add debug logging for ID recovery

Behavior:
- Without --update-ids: Normal collection (creates new notes)
- With --update-ids: Searches for existing notes before creating
- Useful for recovering from lost/incorrect IDs in markdown files
- Matches both basic cards (front+back) and cloze cards (text field)

Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
Implement Phase 4 of inka2 parity plan: error handling to continue
processing when individual files fail.

Key changes:
- Add `--ignore-errors` CLI flag to collect command
- Extend CardCollector to collect errors instead of failing immediately
- Add error summary reporting at end of processing
- Implement error collection for both file and directory processing

Implementation details:
- CardCollector now has `ignore_errors: bool` and `errors: Vec<String>` fields
- Refactored `process_file` to wrap implementation with error handling
- When `ignore_errors` is true, errors are collected and 0 cards returned
- When `ignore_errors` is false, errors propagate normally (existing behavior)
- lib.rs prints error summary to stderr after processing completes

Testing:
- Added tests for error collection when ignore_errors is true
- Added tests for error propagation when ignore_errors is false
- All existing tests updated for new constructor parameter
- Tests use missing media files to trigger realistic errors

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
Enhance CLI help text with clearer descriptions and add comprehensive
documentation for the collect command.

CLI Help Improvements (args.rs):
- Expanded flag descriptions with context and use cases
- Clarified what "conflicts" and "unchanged files" mean
- Added explanations of hash cache and ID injection mechanisms
- Improved readability with multi-line docstrings

README.md Updates:
- Added collect command to features list
- Created comprehensive "Collect markdown cards" section
- Included markdown format examples (basic, cloze, images)
- Documented common workflows and flag combinations
- Added flag reference table
- Expanded troubleshooting with collect-specific issues

Key Documentation Additions:
- Markdown format specification with examples
- How ID injection works (<!--ID:--> comments)
- Hash cache explanation and performance notes
- Media file handling workflow
- Flag interaction guidance
- Troubleshooting for common collect errors

The documentation now provides users with complete guidance for
importing markdown flashcards into Anki without needing external
resources.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
@sysid sysid closed this Nov 2, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants