/* Language review session — Stroke shape mid-flow Parallel to ReviewSession (math). Item shapes are language-specific. Stroke is the active item: write the kanji as feedback evaluates the trace. */ const LanguageReviewSession = () => { const queue = [ { n:1, shape:"recognize", concept:"kanji.jp.食", done:true, rating:"good" }, { n:2, shape:"recall", concept:"vocab.jp.飲む", done:true, rating:"hard" }, { n:3, shape:"listen", concept:"vocab.jp.行く", done:true, rating:"again" }, { n:4, shape:"stroke", concept:"kanji.jp.動", done:false, current:true }, { n:5, shape:"cloze", concept:"grammar.jp.particle.を", done:false }, { n:6, shape:"recognize", concept:"kanji.jp.達", done:false }, { n:7, shape:"recall", concept:"vocab.jp.美味しい", done:false }, { n:8, shape:"listen", concept:"vocab.jp.友達", done:false }, ]; const shapeMeta = { recognize: { label:"Recognize", glyph:"視", desc:"kanji → reading" }, recall: { label:"Recall", glyph:"記", desc:"meaning → word" }, listen: { label:"Listen", glyph:"聴", desc:"audio → transcription" }, stroke: { label:"Stroke", glyph:"書", desc:"write the kanji" }, cloze: { label:"Cloze", glyph:"空", desc:"fill the gap" }, }; // 動 stroke skeleton — abbreviated for the trace target const strokes = [ "M 90 60 L 180 60", "M 110 60 L 110 100 L 165 100 L 165 60", "M 95 100 L 180 100", "M 138 30 L 138 200", "M 80 130 L 195 130", "M 95 160 L 180 160", "M 110 130 L 110 195", "M 165 130 L 165 195", "M 70 195 L 200 195", "M 230 65 L 230 170 L 290 170", "M 230 90 L 285 90", ]; const strokesDone = 7; return (
REVIEW · 日本語 · Session 217 · started 09:42
Pace avg 24s · target 15 items

Queue
{queue.map((q, i) => (
{q.n.toString().padStart(2,"0")} {shapeMeta[q.shape].glyph} {q.current && NOW} {q.done && {q.rating}}
))}
03 / 08 · ~03:10 left

Stroke · review item
item.review.stroke.0301 · concept · kanji.jp.動
Last seen8d ago
Retention52%
Stability6.2d

Write the kanji for うご-く (to move). The system tracks stroke order, direction, and proportion in real time.

{/* Trace canvas */}
{/* completed user strokes */} {strokes.slice(0, strokesDone).map((d, i) => ( ))} {/* active stroke — partial trace in accent */} {/* future strokes — ghosted */} {strokes.slice(strokesDone+1).map((d, i) => ( ))} trace target · 11 strokes
Live feedback

Stroke{strokesDone+1} / 11
Order✓ correct
Direction✓ top→bottom
Proportion! 重 too narrow
Match71% / 80% target

Action

Self-rate after submit
FSRS-6 · stability 6.2d → 9.0d on Hard · proportion warning weighted in
); }; window.LanguageReviewSession = LanguageReviewSession;