this repo has no description
1
fork

Configure Feed

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

fix: cloze review hides one blank at a time, not all simultaneously

Each cloze ReviewItem already had an independent sub_id ("b0", "b1", …).
The renderer was ignoring it and blanking everything. Now ReviewCloze
carries the target blank index and the Typst preamble uses a counter to
hide only that blank, showing all others.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

+47 -19
+36 -17
crates/tala-typst/src/lib.rs
··· 20 20 ReviewFront, 21 21 /// FrontBack: back side only (for bidirectional reverse review). 22 22 ReviewBack, 23 - /// Cloze: body rendered with #blank[] contents replaced by empty boxes. 24 - ReviewCloze, 23 + /// Cloze: body rendered with only the target blank (0-indexed) replaced by 24 + /// an empty box; all other blanks show their content. 25 + ReviewCloze(usize), 25 26 } 26 27 27 28 pub struct RenderResult { ··· 56 57 preamble: Preamble, 57 58 blank_spans: &[Range<usize>], 58 59 ) -> Result<RenderResult, Error> { 59 - let preamble_str = preamble.as_str(); 60 + let preamble_str = preamble.to_preamble_string(); 60 61 let source_text = format!("{preamble_str}\n{fragment}"); 61 62 let world = TalaWorld::new(deck_dir, source_text); 62 63 ··· 381 382 // ── Preamble strings ────────────────────────────────────────────────────────── 382 383 383 384 impl Preamble { 384 - fn as_str(self) -> &'static str { 385 + fn to_preamble_string(self) -> String { 385 386 match self { 386 - Preamble::Authoring => PREAMBLE_AUTHORING, 387 - Preamble::ReviewFront => PREAMBLE_REVIEW_FRONT, 388 - Preamble::ReviewBack => PREAMBLE_REVIEW_BACK, 389 - Preamble::ReviewCloze => PREAMBLE_REVIEW_CLOZE, 387 + Preamble::Authoring => PREAMBLE_AUTHORING.to_owned(), 388 + Preamble::ReviewFront => PREAMBLE_REVIEW_FRONT.to_owned(), 389 + Preamble::ReviewBack => PREAMBLE_REVIEW_BACK.to_owned(), 390 + Preamble::ReviewCloze(target) => preamble_review_cloze_one(target), 390 391 } 391 392 } 392 393 } ··· 426 427 #let img(name) = image("images/" + name) 427 428 "#; 428 429 429 - /// Cloze review: blank contents replaced by fixed-width underline boxes. 430 - const PREAMBLE_REVIEW_CLOZE: &str = r#" 430 + /// Cloze review: only the `target` blank (0-indexed) is replaced by a box; 431 + /// all other blanks show their content. 432 + fn preamble_review_cloze_one(target: usize) -> String { 433 + format!( 434 + r#" 431 435 #set page(width: 300pt, height: auto, margin: 10pt) 432 - #let card(dir: "fwd", tags: (), ..args) = { 436 + #let card(dir: "fwd", tags: (), ..args) = {{ 433 437 let sides = args.pos() 434 438 sides.at(0, default: []) 435 - if sides.len() > 1 { 439 + if sides.len() > 1 {{ 436 440 line(length: 100%) 437 441 sides.at(1) 438 - } 439 - } 440 - #let blank(..args) = box(width: 4em, stroke: (bottom: 0.5pt), inset: (bottom: 2pt))[] 442 + }} 443 + }} 444 + #let _tala_blank_ctr = counter("tala_blank") 445 + #let blank(..args) = {{ 446 + _tala_blank_ctr.step() 447 + context if _tala_blank_ctr.get().first() == {n} {{ 448 + box(width: 4em, stroke: (bottom: 0.5pt), inset: (bottom: 2pt))[] 449 + }} else {{ 450 + args.pos().at(0, default: []) 451 + }} 452 + }} 441 453 #let cloze(tags: (), ..args) = args.pos().at(0, default: []) 442 454 #let img(name) = image("images/" + name) 443 - "#; 455 + "#, 456 + n = target + 1 457 + ) 458 + } 444 459 445 460 // ── World implementation ────────────────────────────────────────────────────── 446 461 ··· 607 622 #[test] 608 623 fn render_review_cloze_blanks_hidden() { 609 624 let tmp = std::env::temp_dir(); 610 - let result = render(&tmp, CLOZE, Preamble::ReviewCloze, &[]).unwrap(); 625 + // blank 0 hidden, blank 1 visible 626 + let result = render(&tmp, CLOZE, Preamble::ReviewCloze(0), &[]).unwrap(); 627 + assert!(result.width > 0); 628 + // blank 1 hidden, blank 0 visible 629 + let result = render(&tmp, CLOZE, Preamble::ReviewCloze(1), &[]).unwrap(); 611 630 assert!(result.width > 0); 612 631 } 613 632
+11 -2
crates/tala/src/review.rs
··· 405 405 render_review_b64(&dir, &item.source, preamble).unwrap_or_default() 406 406 } 407 407 ReviewKind::Cloze => { 408 - render_review_b64(&dir, &item.source, tala_typst::Preamble::ReviewCloze) 409 - .unwrap_or_default() 408 + let blank_idx = item 409 + .sub_id 410 + .strip_prefix('b') 411 + .and_then(|s| s.parse().ok()) 412 + .unwrap_or(0); 413 + render_review_b64( 414 + &dir, 415 + &item.source, 416 + tala_typst::Preamble::ReviewCloze(blank_idx), 417 + ) 418 + .unwrap_or_default() 410 419 } 411 420 ReviewKind::ImageRect { src, rect } => { 412 421 render_rect_question_b64(&dir, &item.source, &src, rect).unwrap_or_default()