this repo has no description
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at canon 204 lines 8.2 kB view raw view rendered
1# CLAUDE.md 2 3Guidance for Claude Code when working in this repository. 4 5## Commands 6 7```bash 8cargo build # build everything 9cargo check # check without artifacts 10cargo test # all tests 11cargo test -p tala-format card_count # single test 12cargo run -p tala-cli -- check <deck-dir> 13cargo run -p tala-cli -- review <deck-dir> 14cargo run -p tala # GUI (uses cwd or ~/.config/tala/dir) 15``` 16 17## Architecture 18 19Cargo workspace, edition 2024. All crates under `crates/`. 20 21``` 22tala-format → tala-srs → tala-typst → tala (GUI) 23 24 tala-cli 25``` 26 27--- 28 29### `tala-format` 30 31Parses `.typ` card files using `typst-syntax`. Entry point: `parse_cards(source: &str) -> Vec<CardEntry>`. 32 33Uses `LinkedNode` (not `SyntaxNode`) — `LinkedNode::range()` returns `Range<usize>`; `SyntaxNode` only exposes an opaque `Span`. Walker matches `SyntaxKind::FuncCall`, dispatches on function name. 34 35**Card syntax:** 36``` 37#card(tags: ("t1",), dir: "bi")[front][back] → CardKind::FrontBack 38#cloze(tags: ("t1",))[body #blank[hidden] ...] → CardKind::Cloze 39``` 40 41No `#img_cloze` — removed. Image region overlays live in the sidecar as `RectEntry`. 42No `id:` field — cards are identified by **position index** in the file ("0", "1", ...). 43`dir` defaults to `Forward`; `"bi"``Bidirectional` (two review items: forward + reverse). 44 45**Key types:** 46```rust 47pub struct CardEntry { 48 pub tags: Vec<String>, 49 pub kind: CardKind, 50 pub span: Range<usize>, // byte range of the full #card(...)[...] call 51} 52 53pub enum CardKind { 54 FrontBack { front_span, back_span, dir: Direction }, 55 Cloze { body_span, blanks: Vec<BlankEntry> }, 56} 57 58pub struct BlankEntry { pub index: usize, pub span: Range<usize>, pub content_span: Range<usize> } 59pub enum Direction { Forward, Bidirectional } 60``` 61 62**Span gotcha:** the `#` sigil is outside the `FuncCall` AST node. `card.span` points to `card(...)` not `#card(...)`. Use `span.start - 1` when byte-splicing to include the sigil. 63 64--- 65 66### `tala-srs` 67 68Loads/saves `<cards>.srs.json` sidecar alongside `<cards>.typ`. All FSRS schedule data lives here, never in the typst source. Image rect overlays also live here. 69 70**Primary API:** 71```rust 72Sidecar::load_or_empty_for(typ_path: &Path) -> Result<Self> 73Sidecar::save_for(&self, typ_path: &Path) -> Result<()> 74Sidecar::path_for(typ_path: &Path) -> PathBuf // derives .srs.json path 75Sidecar::empty() -> Self 76sidecar.get(card_id: &str) -> Option<&CardSchedule> 77sidecar.orphaned(known_ids: &[String]) -> Vec<&str> 78sidecar.missing(known_ids: &[String]) -> Vec<&str> 79``` 80 81`card_id` is always a stringified position index ("0", "1", ...). 82 83**Key types:** 84```rust 85// Tagged-union JSON: #[serde(tag = "type", rename_all = "snake_case")] 86pub enum CardSchedule { 87 FrontBack { 88 forward_schedule: Option<Schedule>, 89 reverse_schedule: Option<Schedule>, 90 }, 91 Cloze { 92 blanks: HashMap<String, Schedule>, // keys: "b0", "b1", ... 93 rects: Vec<RectEntry>, // image region overlays 94 }, 95} 96 97pub struct Schedule { pub due: String, pub stability: f32, pub difficulty: f32 } 98// due is "YYYY-MM-DD" 99 100pub struct RectEntry { 101 pub id: String, // "r0", "r1", ... 102 pub src: String, // bare image stem (no path, no extension) 103 pub rect: [f32; 4], // normalized [x, y, w, h] in [0.0, 1.0] 104 pub schedule: Schedule, 105} 106``` 107 108**FSRS helpers:** `is_due(sched: &Schedule) -> bool`, `next_schedule(current, days_elapsed, grade) -> Schedule`, `today_str() -> String`. 109 110--- 111 112### `tala-typst` 113 114Renders typst source strings to RGBA pixels via the `typst` crate. Implements `typst::World`. Font loading must provide at least one valid font family or typst panics. 115 116```rust 117pub fn render(deck_dir: &Path, fragment: &str, preamble: Preamble, blank_spans: &[Range<usize>]) -> Result<RenderResult, Error> 118 119pub enum Preamble { 120 Authoring, // full card, all content visible 121 ReviewFront, // FrontBack: show front content block only 122 ReviewBack, // FrontBack: show back content block only (for bidirectional reverse) 123 ReviewCloze, // Cloze: replace all blank[] with opaque boxes 124} 125 126pub struct RenderResult { 127 pub rgba: Vec<u8>, // premultiplied alpha, row-major 128 pub width: u32, pub height: u32, 129 pub blank_boxes: Vec<[f32; 4]>, // pixel [x,y,w,h] per blank (requires blank_spans) 130 pub glyph_map: Vec<([f32; 4], Range<usize>, bool)>, // (px rect, frag range, is_math) 131 pub math_spans: Vec<Range<usize>>, // fragment-relative Equation byte ranges 132 pub image_boxes: Vec<[f32; 4]>, // pixel [x,y,w,h] per image, document order 133} 134``` 135 136The RGBA output is **premultiplied alpha**. Convert to straight alpha before encoding to PNG (divide RGB by alpha). 137 138--- 139 140### `tala-cli` 141 142Two subcommands: 143 144- **`check <path>`** — parses all `.typ` files, reports card counts, sidecar orphans/missing entries. 145- **`review <path> [--tag TAG]`** — interactive terminal review session. Collects all due items across files, prompts grade (1-4), saves to sidecar, git-commits each modified sidecar with message `"review session YYYY-MM-DD"`. 146 147The CLI uses `git2` for commits; the GUI does not. 148 149--- 150 151### `tala` (GUI) 152 153Dioxus desktop app. Single binary. Routes: `Home`, `Editor`, `Images`, `Review`, `Settings`. 154 155**Global state:** 156```rust 157static CARD_DIR: GlobalSignal<PathBuf> 158fn card_dir() -> PathBuf 159fn cards_path() -> PathBuf // card_dir().join("cards.typ") 160fn set_card_dir(path: PathBuf) // persists to ~/.config/tala/dir 161``` 162 163Deck dir is loaded from: CLI arg → `~/.config/tala/dir` → cwd. 164 165**Editor** (`Editor` component): 166- Segments source file by blank lines: `SegKind::Card` or `SegKind::Text`, identified by prefix (`#card` / `#cloze`). 167- Each card segment rendered independently. Active card has an editable textarea; inactive cards show rendered preview. 168- Auto-saves to disk after 1s debounce. **No git commits from the GUI.** 169- Card identification: `active_card_key()` counts `Card` segments up to active index → stringified count → sidecar key. 170- **Draw mode**: drag to create rect overlays on images within a card. Saved to sidecar as `RectEntry`. Existing rects are draggable/resizable. Delete via button. 171- Image paste from clipboard (arboard, not web_sys) → saves to `<deck>/images/`. 172- Keyboard nav: Up/Down at textarea boundary switches active segment. 173 174**Images** (`Images` component): 175- Grid of all files in `<deck>/images/`. 176- Rename: updates filename on disk and rewrites matching `src` fields in all sidecar `RectEntry` records. 177- Copy-name button, lightbox preview. 178 179**Review** (`Review` component): 180- Setup screen: clickable tag chips (OR/AND toggle for 2+ tags), "Start review →" builds filtered queue. 181- Queue items: `ReviewKind::FrontBack`, `ReviewKind::Cloze`, `ReviewKind::ImageRect { src, rect }`. 182- Question → reveal → grade (1=Again 2=Hard 3=Good 4=Easy). Keyboard: Space/Enter=reveal, 1-4=grade, S=skip. 183- Skip: moves item to back of queue with `skipped: true`; terminates when all remaining are skipped. 184- Image rect review: renders full card via `Authoring`, then pixel-fills the rect region for the question image. 185- Crossfade between question/answer images via CSS grid overlap + `opacity` transition. Click image to toggle. 186- Saves sidecar to disk after each grade. No git commit. 187 188**Settings** (`Settings` component): 189- Change deck directory (file picker). 190- "Reset all schedules" — clears all schedule data from sidecar (preserves rect geometry), requires confirm dialog. 191 192--- 193 194## Deck directory layout 195 196``` 197<deck>/ 198 cards.typ # human-authored typst source, git-tracked 199 cards.srs.json # machine-managed schedule + rect overlay data 200 images/ # referenced via #img("stem") or #image("images/stem.ext") 201``` 202 203Card edits are byte-span splices — never reformat the whole file. 204`extract_img_names(source)` normalizes both `#img("stem")` and `#image("images/stem.ext")` to bare stems, matching sidecar `RectEntry.src`.