Skip to content

Stats dashboard, template engine, and UI overhaul - #3

Merged
Emadab merged 13 commits into
mainfrom
feature/stats-dashboard
Jul 3, 2026
Merged

Stats dashboard, template engine, and UI overhaul#3
Emadab merged 13 commits into
mainfrom
feature/stats-dashboard

Conversation

@Emadab

@Emadab Emadab commented Jul 3, 2026

Copy link
Copy Markdown
Owner

Summary

  • Statistics dashboard (filters, retention, FSRS panels), async import with progress, deck/study page merge, day-rollover config
  • Full Anki template engine + modern card design + rich editor
  • UI/UX shell overhaul: slimmer glassy chrome, study focus mode, customizable home (list/grid/hero)

Test plan

  • pnpm lint, pnpm test, pnpm build pass (verified for the UI overhaul commit)
  • Manual: window drag/controls, sidebar dock/hover modes, theme toggle (palette + settings)
  • Manual: study flow shortcuts, focus mode (F/Esc), typed-answer field no longer triggers shortcuts
  • Manual: home layout switcher (list/grid/hero) persists and renders correctly

Emadab added 13 commits July 3, 2026 14:10
…RS panels

Add per-deck (with subdeck rollup) and time-range filtering to the stats
screen, backed by a much richer StatsDto: true weekly retention, answer-button
distribution, hourly breakdown, FSRS stability/difficulty/retrievability, a
year heatmap with streaks, forecast + backlog, and a hierarchical deck
breakdown table. Filters live in the URL so views are shareable/reloadable.

Frontend charts follow a single-axis-only, fixed-categorical-color
methodology, with count-up stat tiles, staggered entrance, skeleton loading,
and CSV export per panel.
The stats-dashboard rebuild made get_stats the heaviest read command
in the app, but it was still sync, so Tauri ran it on the main
thread and blocked the UI event loop while it churned through the
revlog aggregates. Made it async, matching every other heavy command
(optimize_fsrs, create_backup, etc).
import_package blocked the UI thread and did per-note/per-card
SELECT+INSERT/UPDATE round trips, dominating time on large decks.
Async-ify the command (matches the get_stats fix), pre-load existing
notes/cards into HashMaps before merging, and use prepare_cached for
the remaining statements. Also surfaces live import progress to the
UI via a new synapse://import-progress event.
Add a per-deck, per-day new-card limit override (day_limit_overrides
table + Collection::increase_today_new_limit), wired through the
scheduler so it actually raises today's new-card cap. Expose it via
a new + control on each deck row and on the "all done" screen.

Also removes the old "study at most N cards this session" box, which
was unrelated to the daily new-card limit and looked/behaved poorly.
Decks and Study were separate pages over the same deck-list data, with
Study duplicating its own picker and no rename UI despite the backend
already supporting it. The deck list is now the study launcher: click a
deck to enter /study/$deckId (route-param session, so refresh and back
both work), manage decks (create, rename, delete, options, filtered
rebuild/empty, increase-today's-limit) from the same row via hover
actions and an overflow menu, and see today's workload at a glance in
the header.

Studying or counting a parent deck now rolls up its whole non-filtered
subtree (Anki behavior) instead of only its own cards, since the merged
UI makes parent rows far more clickable than before. Each subdeck still
respects its own daily limits; cross-deck learning-queue merges sort by
due time. Fixed a related bug this surfaced: answer_card computed
remaining counts from the answered card's own (leaf) deck instead of
the session's root deck, which would have shown wrong header counts
and broken next-card fetch for any parent-deck session.
Header checkbox now renders a dash when some but not all visible
rows are selected, instead of only checked/unchecked.
Tauri's setup() closure ran DB backup zip, full search-index rebuild,
and plugin install synchronously before the window became responsive,
causing "Not Responding" on every launch. Only the DB open/migration
(cheap, required for command state) stays sync; the rest now runs on
a background thread after collection state is managed.
…ounts

Day boundary was a fixed 24h block anchored to collection creation
instant, with no timezone or rollover-hour concept. Added an
Anki-style rollover hour (default 4am, local time) in Settings, and
made the day-number/day-cutoff math in Collection::today() honor it.

Also fixes new-card counts going stale in the deck browser when the
app is left open across a day boundary: the deck list query only
invalidated on mutation events, never on the passage of time, so it
now polls every 60s.
…itor

Rewrites synapse-render as a proper tokenizer/AST parser (fixing nested
conditionals) and adds every remaining Anki template feature: hint,
cloze-only, type:cloze, tts/tts-voices, furigana/kana/kanji filters, and
special fields (Tags/Deck/Subdeck/Type/Card/CardFlag).

Renders cards inside a shadow DOM (CardFace) so per-notetype CSS from
imported decks or user edits stays isolated from app chrome, with a new
"modern editorial" default stylesheet and six auto-seeded stock note types
(Basic, +reversed, optional-reversed, type-in, Cloze, Image Occlusion).

Rebuilds the field editor with a full formatting toolbar (colors, sub/sup,
lists, cloze shortcuts, image/audio insert, LaTeX helper), adds
SpeechSynthesis-backed TTS unified into the sound playback queue, and a
drag-to-draw image occlusion editor with mode-aware shape rendering.

Also fixes a same-name notetype collision where importing a .apkg could
silently keep an unused stock notetype's templates instead of the
imported deck's own.
…zable home

Collapse the stacked titlebar/header/screen-header chrome into two slim
layers, apply a glassy translucent chrome/panel treatment with a cooler
accent, add a manual focus mode for study (F/Esc, progress bar, vignette),
and let the deck browser switch between list/grid/hero layouts. Flashcard
rendering itself is untouched. Also fixes a latent bug where study
shortcuts (s/b/r) could fire while typing into the type-answer field.
cargo fmt --all had accumulated formatting drift across several crates;
renderCard.ts assigned walker.currentNode to a variable that was
immediately overwritten by the loop condition, tripping eslint's
no-useless-assignment rule.
prettier --write across the workspace (pre-existing formatting drift,
mostly in files this branch never touched); satisfy two new clippy
lints (manual_is_multiple_of, redundant_guards) surfaced by a newer
clippy version.
@Emadab
Emadab merged commit 5755d14 into main Jul 3, 2026
3 checks passed
@Emadab
Emadab deleted the feature/stats-dashboard branch July 3, 2026 21:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant