# Podcards

[Open the demo](https://slc.reneandkiaramyhome.goip.de/example_app/podcards/).

An Anki-inspired study workspace built with Semantic Linked Components. It opens
with invented, editable notes, a generated seedling illustration and a synthesized
A4 tone, plus six weeks of invented study history so the activity views have
something to show. The sibling `anki-web` project is unchanged.

The interface follows the Linen design shared with Urðarbrunnr and Solid Tasks
(Space Grotesk, Courier Prime, JetBrains Mono). **Dark mode** in the sidebar
redefines the same colour tokens. Inside Urðarbrunnr the views move to a bottom
tab bar and the desktop owns the account.

## Study and authoring

- Nested decks have individual daily limits and FSRS options. Parent sessions
  include descendants with each deck's own limits.
- Basic, reversed, Cloze and typed-answer notes generate independently scheduled
  cards. Edit fields, front/back templates and shared CSS with live previews.
  Removed templates/cloze numbers retire cards; restoring the same identifiers
  restores schedules. Deleted notes retain retired cards and review history.
- **Decks** shows today's New / Learning / Due counts, a deck table (New, Learn,
  Due, last studied) and a 12-week activity heatmap with streak and 30-day
  retention. Arrow keys move between decks, Enter studies, o opens options, /
  searches.
- Reveal with Space/Enter; rate with 1–4. Undo, flag, bury and suspend are in the
  study **More** menu; Ctrl/Cmd+Z undoes the latest unchanged
  review, including sibling burial. E edits the note. Esc leaves the session. Bury lasts until tomorrow;
  suspension lasts until resumed. Typed comparison is advisory.
- **Add a note** is its own page with a live preview of every generated card; tags
  are chips, and “Keep deck and type” remembers your last choice in this browser.
- **Browse** searches with `deck:`, `tag:`, `type:`, `is:due|new|learning|review|
  suspended|buried|flagged|leech`, `added:<days>` and free words (quote values with
  spaces). Filters show as removable chips; quick chips toggle common states. The
  side panel shows the note, its cards, reviews, lapses and stability. Bulk actions
  (change deck, add tags, suspend, resume, unbury, edit, delete) appear on
  selection. Double-click edits a note.
- **Statistics** filter by deck and range (1 month, 3 months, 1 year, all): reviews
  and time today, 30-day retention against your target, streaks, card counts,
  reviews per day, a 14-day due forecast, card maturity and answer-button shares.
  Undone reviews never count, and legacy review events are not invented.

`ts-fsrs` is pinned to **5.4.2 (FSRS-6)**. Defaults: 90% retention, 1m/10m learning,
10m relearning, 20 new/200 review cards per deck/day. Due learning cards bypass
limits. Each collection stores an IANA timezone and configurable 04:00 day start,
with DST handling. All four outcomes are captured once; grading commits exactly
the displayed choice. Option changes affect future reviews, without silently
rescheduling existing due dates. Forecasts show current dates, not future grades.
No optimizer, AnkiWeb sync or image occlusion is included.

## Your Solid pod

The collection document is chosen with the shared `<slc-pod-document>` control: it
suggests a place on your pod, lets you browse, tells you whether the file is new,
found or unreadable, and opens its access rules. Saved collections are listed in
your private type index, so signing in on another device offers the same address.
Turn on **Enable automatic sync** and the collection follows the pod: edits are
saved after a short pause, changes from other devices are merged in, and the pill
in the corner says where things stand. *Sync now* does it immediately.

## Import and export

Imports are previewed before anything is written. **Anki packages** (`.apkg`,
`.colpkg`) are read in the browser with a lazily loaded WASM SQLite engine: both
the older `collection.anki2/anki21` layout and the zstd-compressed `anki21b`
layout of current Anki. Note types, fields, templates (unknown filters such as
`hint:` keep their field; special fields such as `{{Tags}}` are dropped), CSS,
decks, tags, media (PNG, JPEG, WebP, GIF, MP3, Ogg, WAV, WebM), suspension, flags,
FSRS memory (or an estimate from SM-2 interval and ease) and review history come
along. Choose the file's decks, an existing deck or a new deck, and whether
duplicates (same Anki note GUID, or the same sort field, deck and type) update,
are skipped or are imported as new. Updated notes keep their Podcards progress.
Anki may create fewer cards than Podcards for optional templates; Podcards makes a
card for every template of a filled note.

Export writes an Anki package in the widely supported legacy layout (opens in
Anki desktop and AnkiMobile, scheduling and history included), Turtle, a complete
ZIP backup or CSV (fields only). CSV imports add notes and skip duplicates;
Turtle and ZIP backups replace the collection. Recent transfers are listed per
browser and account.

## Local drafts and Solid

Collection/editor drafts and media use atomic IndexedDB transactions. Scopes are
local guest, or WebID plus document URL. Account changes clear private views,
abort resource work and reject late results. Drafts remain in the browser profile;
export ZIP before clearing site storage.

Sync uses the shared [synced document](../../../../packages/pod-kit/docs/synced-document.md)
with `merge.js`: records merge by id, reviews are unioned, a card reviewed on two
devices keeps the newer review's scheduling, and the same note edited twice becomes
a choice in the Solid view. Remote changes wait while a note is being edited or a
study session runs. The first sync with an existing pod collection asks whether to
use it or merge the local cards into it. The public SLC RDF document client uses strong ETag
`If-Match`, or `If-None-Match: *` for a new file. Conflicts/permission failures keep
the local draft. A missing/weak ETag prevents overwrite. JSON-LD providers use
the existing representation fallback. The owned WASM snapshot query reports
record counts without fetching arbitrary linked resources.

Attachments are SHA-256 addressed, uploaded before the manifest to a sibling
`<collection-file>.media/` folder. It inherits parent access; choose a private
parent for private cards. Failed saves may leave orphan media, never automatically
deleted. Existing immutable files are verified by hash. Media reads use the
authenticated resource port, are restricted to the collection origin, and verify
size/hash before creating blob URLs. Audio never auto-plays.

## Formats and migration

RDF v2 uses `https://podcards.app/vocab#`: Collection, Deck, NoteType, Template,
Note, Card, Review, Media. Links/scalar schedules are RDF properties; field maps,
options and history snapshots use `rdf:JSON`; template strings use `rdf:HTML`.
Positions preserve order. Unknown predicates and unrelated triples survive
normal sync.

CSV accepts front/back or term/translation; optional deck, tags, example,
exampleTranslation, type, stage. Duplicate text/deck pairs are skipped without
resetting progress. CSV and legacy text are escaped. Legacy Turtle and same-origin
`localStorage.podcards` imports preserve IDs, due dates, counters, examples and
metadata. FSRS memory is estimated and marked `legacy.estimated`, using the
[upstream FSRS-6 SM2 conversion](https://github.com/open-spaced-repetition/fsrs-rs/blob/v3.0.0/src/inference.rs).
No historical reviews are invented.

ZIP contains collection.ttl and media/<sha256>. Import validates paths, expanded
size, collection and hashes before replacement. Turtle alone contains media
references. Bounds: 10,000 notes, 20,000 cards, 100,000 reviews, 16 MiB collection/
CSV, 25 MiB per attachment, 200 MiB backup/attachments.

## Architecture and verification

Reusable additions: `slc-flashcard`, `slc-rich-text-editor`,
`slc-card-template-editor`. The app owns generation, FSRS, review/account machines,
persistence and imports. It reuses SLC auth, resource ports, RDF client, WASM query,
table, chart and state-machine kernel. Libraries never import this application.

DOMPurify sanitizes markup. A scriptless iframe sandbox and restrictive CSP
isolate CSS and allow only authorized blob images/audio. Forms, navigation,
scripts, external styles/fonts/media and autoplay are blocked. ProseMirror loads
lazily. HTML source is preserved until a visual edit normalizes it to the schema.

Run `npx vitest run apps/demo/test/podcards-*.test.js packages/demo/test/podcards-editors.test.js`.
After building, `node scripts/verify-podcards.mjs` verifies desktop/mobile,
review/undo, editor recovery, ZIP, actual WASM worker and account isolation.
`DEMO_URL` targets an existing deployment. Repository gate: `npm test`, then
`npm run build`; authenticated E2E and live smoke are separate.
