-
Notifications
You must be signed in to change notification settings - Fork 0
Add markdown to Anki collection workflow #4
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Closed
Closed
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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:  - 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>
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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
collectsubcommand to process markdown files containing flashcards, convert them to HTML with proper formatting, and create or update notes in Anki collections.Key Features
Usage
Markdown Format
Implementation Details
Test Coverage
Related
Completes the migration from the Python inka2 prototype to native Rust implementation with improved test coverage and architecture.