Commit graph halkyon/halkyon_learn_flutter/lib/l10n/app_localizations.dart
Author SHA1 Message Date
d42afb078a
feat(app): the library's options move, import into and set how a studyset is studied
A studyset's options in the library now offer, as on its own screen,
Edit, Move to…, Study options, Import from Anki, Archive and Delete.
Moving goes through the same sheet and says where the studyset went;
importing reads an Anki deck into it as Add material does.

Study options say how a studyset is studied, on every device: how many
new cards it starts a day (by default as the person's study settings
say), how many reviews it brings a day (no limit by default; cards in
their learning steps come anyway), and whether reviews or new cards
come first, or the new ones spread evenly among the reviews. They live
in the studyset's payload, schema 3; a studyset without them is still
written in schema 2, so that older apps, which skip a payload in a
newer schema whole, keep seeing every studyset nobody gave options.
Editing a studyset keeps its options, and Reset to defaults takes them
away.

The study queue takes each studyset's limits for its own cards and the
order of the studyset studied; a card's schedule now knows when it was
last reviewed, to count the reviews of the day. Sessions, the round's
end and the library's badges all count the same way, the badges now
with the person's own number of new cards a day too.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-07 12:28:01 +02:00
3ee9bce0c5
feat(app): the app speaks British and American English and German
Settings offers English (UK), English (US) and Deutsch after the
device's language, each named in its own language. app_en.arb, the
template, is now British English (colour, centre) and the fallback for
any language the app doesn't speak; app_en_US.arb overrides the five
messages American English spells differently, and app_en_GB.arb is
empty, there so that Material's texts and dates are British too. The
German translation says "du" and keeps one set of terms throughout
(Lernset, Karteikarte, Lernpfad; Anki's own Stapel and Unterstapel in
the Anki import). arb_files_test checks that German has every message
of the template and that every translation keeps its placeholders.

resolveAppLocale picks the first language the device prefers that the
app speaks, in its country's variant where there is one; other English
is British, and a country alone never picks a language (Spanish in the
United States isn't American English), which Flutter's own resolution
would do.

Both MaterialApps take material_ui's GlobalMaterialLocalizations
delegates: gen-l10n's list brings flutter_localizations', which serve
only package:flutter/material.dart, so material_ui's widgets found no
Material texts in German and threw.

The editor's own text blocks show our placeholders while empty (list
item, to-do, quote, heading, and the desktop's / hint): the editor's
own follow a locale of their own, process-wide, in few languages. The
last texts written into widgets (the person's data failing to open,
the cards failing to read, the About dialog) moved into the arb files.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-07 12:28:01 +02:00
55a0a7fdf0
feat(app): appearance, language and version in Settings, and Settings from Home
Settings gets a card for how this device shows the app: its appearance
(as the device, light or dark) and its language (the device's, or one
the app speaks, each named in its own language), side by side from
medium windows on, as two outlined dropdowns. Both stay on the device
rather than following the person: they are kept in
<app support>/device_settings.json (written to a temporary file and
renamed into place), read before the app shows, so its first frame
already looks as chosen, and a file that can't be read falls back to
what the device does. DeviceSettingsBloc sits above SessionGate's keyed
subtree, since the settings outlive every person, and both the plain
app and the person's app take their theme mode and locale from it.

At the bottom of Settings, the release and its build, read once at
start together with the build the server is asked about.

Home has a Settings cog beside the streak, with the sync trouble dot as
on Profile. The library's create button reads "Create studyset", and
Delete is red in every menu that offers it: the studyset's card, screen
and rail, and the composer's draft.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-07 12:27:56 +02:00
e35ce5c81d
feat: the app tells when sync fails, and when it is too old
Published apps can't be made to update, so the app now notices when it
no longer matches the server: the server names the oldest app build it
works with (instance.info's minimumAppBuild), asked before the first
sync of each launch, and the sync engine's refusal of differing synced
tables counts the same. Either way the app says "update the app; if it
is up to date, the server needs an update" in a banner on Home and in
the new Sync card in Settings, and what the person does meanwhile stays
on the device.

The Sync card shows where sync stands since the app started (syncing,
everything synced, sync failing, with the last completion and failure)
and offers Sync now. While the last sync failed, a dot sits on the
Profile tab and the Settings cog. One sync that goes through clears it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-05 13:00:38 +02:00
b96db731c1
feat: PDF sources: read, take notes and cards from pages, deadlines
Some checks failed
CI / check (push) Failing after 26s
CI / deploy (push) Has been skipped
PDFs live in a studyset's new Files tab: uploaded from Add material
(a file the studyset holds already asks first), each row with its
pages, cards, notes and size, how far it is read and its reading
deadline; renamed, given a deadline (a page or a chapter by a day),
marked as read, started afresh, deleted with a moment to undo.

The reader (/pdfs/:id) opens where reading left off: on wide windows
a rail of tools (search, fit, zoom, fullscreen), a page counter with
the chapter, and a sidebar; on phones a top bar, a page scrubber and
a sheet. The sidebar holds the PDF's flashcards, by page or chapter,
made from selected text or written there, each tied to its page and
tagged with its chapter; and the page's note, an ordinary note tied
to the page. Screenshots of a region become images that know their
page. Cards and screenshots lead back to their page; reading on past
a chapter with cards suggests studying it. Home shows the deadlines
running and to come, and a timeline of them.

Server: pdf_source rows (content in the payload, last writer wins)
and an append-only pdf_reading log, the furthest page derived from
it so devices never conflict. Files up to 50 MB for PDFs (20 MB for
others; a larger file that isn't a PDF is deleted once stored). A
deleted PDF takes its file's asset row along unless another PDF has
the same file. Card payload 4 and note payload 2 keep the page.

The reader draws with pdfrx (PDFium) behind PdfEngine in
Dependencies; widget tests use a fake, and
integration_test/pdf_engine_test.dart checks the real engine on a
device or the desktop.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-03 14:40:08 +02:00
ec34bb2334
feat(app): editor blocks: callout, spoiler, formulas, files, video, study button
All checks were successful
CI / check (push) Successful in 8m34s
CI / deploy (push) Successful in 1m1s
Blocks beyond the editor's own, each built once for editor and view:
- callout: eight kinds in theme colours, kind, title and "folded at
  first" from its header; written in place, left with Enter on its
  empty last line; never inside another
- spoiler: written in plain sight, hidden behind Reveal read-only
- formula on its own line, written on its own screen with a preview
  and snippet buttons, and formulas within a line (Σ in the format
  bar); set by flutter_math_fork behind MathView, LaTeX's numbered
  environments read as their aligned forms
- image and sound picked from the device, kept as synced files
- YouTube video from its link, with its start and a caption: a card
  until played, nothing fetched before; played in place where a web
  view exists (youtube_player_iframe, privacy-enhanced), else opened
  in the browser, behind YoutubeVideos in Dependencies
- study button: a studyset, a tag or all cards, with or without its
  topics, back first; its StudyFocus rides on the study route and
  narrows that session only
- tables, already in the editor, now insertable everywhere

An Insert button opens on the format bar, so blocks can be inserted on
phones too; the desktop's / menu lists the same blocks, translated.
Card faces now dock the format bar while written, so cards get blocks
and formatting on phones as well. The blocks are listed in
EditorBlocks: the core's, plus the features' through
EditorBlocksScope. Blocks of a type the app doesn't know say they need
a newer version and stay as they are, so formatVersion stays 1.

Their types and keys are in content_blocks.dart, which RichContent
reads: a block's text counts as text, and a video or study button
isn't blank. The path import now makes formulas real ones.

integration_test/youtube_player_test.dart plays a real video through
the app's player on a device, which no widget test can: YouTube
refuses embeds that don't say where they are embedded.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-03 12:20:41 +02:00
e4bfe3bdeb
feat(app): learning paths in the studyset screen: worked through and written
- A Paths tab before Flashcards and Notes: each path with its title and
  secondary title, dimmed once completed; Edit, Reset progress (with an
  undo) and Delete (with an undo) behind its options. The studyset opens
  on its paths while one is unfinished, on its flashcards otherwise.
- "Create a path" in Add material asks for the titles in a sheet and
  opens the new path to write; "Import a path (JSON)" reads a file in
  the import format, says what came in and how many blocks were left out
  (images and animations: the app never fetches from the web).
- The player: one step at a time at reading width, a bar of the steps
  above, Back and "Check answers" / "Continue" / "Finish" below. It
  opens at the first unfinished step; an exercise passes only once
  answered right, as often as it takes; after a check, only the
  learner's own picks are marked, with the author's feedback; sources
  behind a chip; a finish screen leading to the next path. Words and
  items are dragged into place, or tapped into place for phones.
- Writing: the path's screen (titles, description, tags, steps added by
  kind, reordered, deleted with an undo) and the step's (title, text
  with the format bar, its kind's exercise, feedback for right and
  wrong in the whole window, sources). The step saves on what it
  started from, as notes do, read-only while devices disagree until a
  version is kept.
- The library and the topics count paths; steps done count on the
  dashboard, for the streak and the activity.
- The rich-text screen for a studyset's description moves to core, for
  the steps' feedback too, and gains the format bar.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-03 09:25:08 +02:00
502ce36a30
feat(app): notes in the studyset screen, written in the whole window
- A Notes tab beside Flashcards lists a studyset's notes in order: the
  title ("Untitled note" without one) and when each was last edited
  ("Edited 5 minutes ago", by the calendar from yesterday on, the date
  after a week); Edit and Delete behind its options. Deleting asks,
  then offers Undo for a moment (`NoteDeletions`). With none, the tab
  says so.
- "Create a note" in Add material opens a new note over the tabs; so
  does a note of the list. The note: a title, its tags once it exists,
  and its text at reading width; the app bar says how saving stands.
- `NoteEditorBloc` saves after 5 seconds without typing, when the app
  goes to the background, and when the note is closed (the bloc saves
  as it closes, so leaving never waits, and the write outlives the
  screen). Each save builds on the revisions the editor started from;
  another device's change takes over a note with nothing unsaved, and
  merges with one that has. A note changed differently on two devices
  is read-only until the person keeps a version of each field in
  conflict. A new note is created by its first save, never when left
  blank; a note deleted elsewhere says it no longer exists.
- The editor gains a format bar, docked below it while it has the
  focus (`RichContentEditor(formatBar: true)`): alignment, bold,
  italic, underline, superscript and subscript. It takes no focus, so
  the selection stays. Superscript and subscript are inline marks of
  their own, drawn by the font's raised and lowered glyphs in editors
  and read-only text alike.
- The library's and the topics' counts include notes (a studyset with
  what is inside it, as for cards).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-02 22:30:53 +02:00
079179d2ed
Fix what the first Android run found
All checks were successful
CI / check (push) Successful in 6m44s
CI / deploy (push) Successful in 59s
The flow ran on a Pixel 9a and its emulator (2026-10-02); what it found:

- The composer stayed on the card just written after "New flashcard", and
  on the original after "Duplicate": leaving a card changed the state
  before the new one was in it, so the screen took the new card for gone.
  It now waits for the card it asked for.
- Bottom sheets reached under the status bar with the keyboard open
  (useSafeArea).
- The field being written stayed behind the keyboard: the editor scrolls
  only within itself, so the card face now keeps it in view when it opens
  and when the keyboard rises, as a text field does.
- A tap on read-only rich text put a caret in it and opened the keyboard on
  phones, and in the composer never opened the field. RichContentView is
  display only now: no selection, taps go to what surrounds it, a sound's
  button still plays (decided 2026-10-02).
- The card types broke their names on a phone; they are choice chips that
  wrap (decided 2026-10-02).
- The week overview marked days before the person's first activity as
  missed; they show nothing now. An import starts the activity too, without
  counting as studying (decided 2026-10-02).
- A composer preview cut short gave no sign of it; it fades out over its
  last 32 dp, only when cut short (decided 2026-10-02).
- "Isn't on this device yet" showed for any image or sound that failed;
  a file that is here but can't be decoded now says so, and offers no retry.
- The signed-in account card was narrower than the anonymous one.
- The composer's title said "Create flashcards" also when opened on a
  stored card; it says "Edit flashcards in …" then (decided 2026-10-02).
- The Anki fixtures carry a valid PNG and a silent MP3, so they also serve
  to try the import by hand (decided 2026-10-02); regenerated with Anki
  26.9.3.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-02 19:52:39 +02:00
ca69f74fd9
Limit how much room people's files take
All checks were successful
CI / check (push) Successful in 6m41s
CI / deploy (push) Successful in 1m4s
An anonymous person may keep 100 MB of images and sounds, an identified one
500 MB, and one person more by a file_allowance row the operator adds; all
of them together 15 GB, since every file is in the database and in each dump
the server keeps (decided 2026-10-02). Each comes from the environment
(FILE_LIMIT_*_MB), with these defaults, documented in deploy/.env.example.

The server checks when an upload starts, counting unfinished uploads so they
can't fill the disk unnoticed, and again when it finishes, counting what is
kept, so uploads started side by side can't overshoot; a refusal is a
FileLimitExceededException, and an upload refused as it finishes is deleted.
deletePerson deletes the allowance with the person. Migration: the new
table file_allowance, only added.

The app keeps a refused file waiting on the device and stops that round of
uploads; the Anki import warns when a deck's files may not fit in the room
left (asset.usage()).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-02 06:02:15 +02:00
a6d5769a2e
Import Anki decks with their files and history
Files in content: the person's images and sounds as asset rows synced like
all content, their files kept by hash on each device, uploaded after each
sync, downloaded on demand and checked against their hash
(core/asset/AssetRepository). Rich text names them as asset:<id> URLs, in
the editor's image block and an audio block of ours; any other URL is never
fetched. Sounds play through audioplayers, one at a time. Content holding
only an image or a sound isn't blank, and lists show it as such.

The Anki import ("Import from Anki" under Add material): .apkg and .colpkg
packages in all three formats Anki has written, read on an isolate of their
own. Modern packages compress everything with zstd in frames that don't
record their size, which pure_zstd v1.0.0 refuses; our fork decodes them.
Fields become rich text by a converter of our own (lines, lists, formatting,
images, sounds, cloze deletions hidden on the front). One card per note,
the files first, studysets as chosen (flatten, sub-studysets, or tags by
subdeck), the notes' tags, and, if wanted, the review history, which FSRS
replays: the first template's reviews to the card's schedule, a reversed
note type's second to the reverse one.

Imported cards say where they came from in their first revision (card
schema 3); the dashboard doesn't count them as written. The test fixtures
are exported by Anki itself (tool/anki/make_fixtures.py).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-02 05:11:50 +02:00
111532435f
feat(app): the home dashboard: streak, goal, week, activity
Home opens on a study dashboard. The streak's flame sits in the app bar
and opens a sheet of how it stands this week. Below: the study goal (a
ring of the day's or week's progress, set and removed in its sheet), the
week (each day studied, rested, missed or to come with the cards falling
due; days to go back and on; today or a day to come made a rest day),
and a year of activity as a heatmap, its weekdays staying in view while
the weeks scroll. A long press arranges the dashboard: drag a widget
elsewhere, hide or show it; the arrangement is synced.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-01 22:17:28 +02:00
5a5b2feead
feat(app): map questions on Japan's prefectures
A map question is a multiple-choice card drawn on a map: the composer's
Map kind takes the region tapped as its answer, and studying it means
tapping a region and checking, the right one green and a wrong pick red,
each named. The map of Japan's 47 prefectures comes from Natural Earth
(public domain), built by tool/maps/japan_prefectures.py from release
v5.1.2 (its download checked by hash): simplified, small islands
dropped, rounded; about 107 KB. Maps are drawn to fit, tappable and
zoomable for small regions.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-01 21:10:07 +02:00
f922fceb47
feat(app): reset a studyset's progress, with an undo
The studyset's options offer Reset progress: after asking, every card
in it and in the studysets inside it starts afresh as new (a 'reset'
entry each), its history kept; an undo for a few seconds deletes those
entries again.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-01 21:10:07 +02:00
46855ab251
feat(app): study sessions in rounds, by card type, cramming and reversed
A session studies a round of up to the settings' cards per round. In
spaced repetition a card leaves the round once it is a day or more away,
one rated lower comes back when due; cramming walks every card once,
round after round, its reviews moving no schedule, with going back and
on. Asking the back first (a toggle in the header, saved in the
settings) asks front/back and vocabulary cards their back, on their own
schedule. Each type studies its way: a word shows its reading and
meanings, a multiple-choice card is picked and checked, a matching card
sorted by dragging or tapping and checked; then the person rates (Bad,
OK, Good, Perfect, each with when the card comes back). Progress shows
as pills, or a tally bar while cramming; a round ends with how it went.
The session settings gain the mode, cards per round, new cards per day
and the order.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-01 21:10:06 +02:00
8cf7749ed6
feat(app): card tiles: the front, the tags, the options
A studyset's cards list as bordered rows of one height showing the
front, the card's tags above it and a badge when devices changed it
differently; tapping opens the composer, "…" offers editing and
deleting (after asking).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-01 21:10:04 +02:00
0afc22d454
feat(app): the card composer, for many cards in one sitting
Create flashcards, and tapping a card, open the composer: the stack of
cards on the left (New flashcard on top, the new ones, then the stored
ones; collapsible), the card being written on the right, saved as the
person leaves it (another card, the pane, the screen, the app). The
title says how saving stands; leaving with cards that couldn't be saved
asks first. A card has its tags and their pin, an action menu
(duplicate, clear, delete), the type switch (flashcard, vocabulary,
matching), the front, and below a line its answer side: one answer or
several to mark right or wrong, the reading and meanings, or categories
and items. Fields changed differently on two devices show every version
to keep one. The single-card editor goes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-01 21:10:03 +02:00
f66595a1d6
feat(app): study by tag, chosen in synced settings
All checks were successful
CI / check (push) Successful in 5m44s
CI / deploy (push) Successful in 1m2s
The study screen's session settings choose tags to study a studyset by:
its cards with any of them. The choice lives in the person's settings
row, synced, by tag id and name, so a renamed tag stays chosen and so
does one folded into another of its name. Two settings rows made offline
fold into the one with the smallest id, which gets what only the other
has.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-01 17:40:11 +02:00
9dc9b6a5ed
feat(app): choose, manage and filter by tags
The card editor offers its tags: a picker that searches by the start of
a name, creates a tag (name and colour) and chooses it, and saves the
choice with the card. A sheet lists every tag with its count, to add,
rename (merging into a tag of that name), recolour and delete them, with
an undo. The card list shows each card's tags and narrows to one tag.

The editor closed twice once tags changed after saving; it now closes
on becoming done only.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-01 17:40:10 +02:00
31d87e8415
feat(app): the studyset screen with its topics, the tree's own screen
All checks were successful
CI / check (push) Successful in 5m28s
CI / deploy (push) Successful in 7s
A studyset's screen, as decided with the tree (2026-09-30/10-01):

- A breadcrumb from the library down to the studyset; its steps go up
  (replacing the location, so back leads further up), and steps that
  don't fit fold into a "…" listing them, nearest first.
- From expanded windows on, two columns: the description over the topics,
  the flashcards beside them, Study and Add material in the app bar. Below
  that, the topics are in a drawer, opened from a bar at the bottom that
  also holds Study and Add material and slides away while reading onwards;
  the back button stays where a drawer would have taken its place.
- The topics: "All topics" for the whole studyset, then the studysets
  inside it in their order, each with the study it invites, what it holds
  and its options (edit, delete), opening on top of this screen; a switch
  rearranges them by dragging; "New sub-studyset" creates one inside.
- Study covers the studyset with everything inside it ("Learn 12" while
  something is due). Add material offers flashcards for now.
- Options: Edit (the sheet, now with the description, edited in the whole
  window, and for a studyset inside another the colour of the one it is
  in, which a new one takes at first), Move to… (the tree and the top
  level, without the studyset itself and what is inside it), Archive or
  Unarchive with Undo, Delete with Undo, which closes the screen.
- The description cut short when long, the whole a tap away; the licence
  as the last line, linking to its text where that is known.

The list's bloc, scoped to a parent, lists its children, works out the
studyset's own progress, creates inside it, reorders and moves. The card
list leaves adding and studying to the screen. The inline editing of the
title and description, and its bloc, are gone.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-01 15:14:57 +02:00
41f536bbe2
feat(app): deleting a studyset can be undone for a moment
Some checks failed
CI / check (push) Failing after 24s
CI / deploy (push) Has been skipped
Decided 2026-10-01. Deleting still asks first; once confirmed, the studyset
leaves the library at once and "Studyset deleted" offers Undo for three
seconds. Only when the time to undo is over (four seconds: the message's
three and a moment for a late tap) are its cards and then the studyset
blanked and deleted, since blanking can't be undone (ADR 0002, rule 2). A
deletion still waiting when the library closes (the app quits, the person
changes) is dropped, and the studyset stays.

The library's bloc keeps the waiting deletions; the event that ends the
time to undo is private, so no screen can skip it. AGENTS.md now describes
the library's shared widgets, the sheets (and why a sheet that asks before
discarding can't be swiped away), the order of studysets, and this Undo.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-01 09:59:47 +02:00
e2e088ce92
feat(app): the library: a grid of studyset cards, search and archive
The library tab becomes a grid of studyset cards (up to 400 dp wide, all
equally tall), starred ones first, and the archive as a section of its own
below, closed until opened and counting what rests there. It lists the top
level: studysets without a parent, those whose parent is gone, and those in
a cycle (ADR 0004). Each card's progress follows the studysets and every
change of a card, here or synced, and is worked out again after a study
session; a slow, older working out never overwrites a newer one.

- Search: a field below the app bar filters by title and secondary title,
  opens the archive while it finds something there, and says when nothing
  matches; closing it shows everything again.
- Creating and editing: a sheet with the title, the secondary title (typed
  in capitals, as every surface shows it) and the colour, picked from the
  palette's twenty, whose names are localised; a new studyset is cobalt,
  and an existing one keeps its colour, even one only a newer app version
  knows, until another is picked. Create waits for a title. Closing the
  sheet with changes asks first. It can't be swiped away: a bottom sheet
  swiped away closes without asking its PopScope (Flutter issue 152120,
  still open), so such sheets have no drag handle and close by their close
  button, the back gesture or a tap beside them, which all ask.
- Archiving doesn't ask, since nothing is lost; it offers to undo, as does
  unarchiving. Deleting asks, then deletes the studyset with its cards, each
  blanked first.
- The create button floats in compact windows and slides away while
  reading onwards; from medium windows on it sits in the app bar.

The old list and its title-only dialog are gone; the studyset screen's
tests create studysets through the sheet.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-01 09:59:46 +02:00
6dc1e863ea
feat(app): the library's studyset card
A studyset in the library: a bar in its colour, its title with the secondary
title as a kicker, what it holds, and beside the title the action it
invites inside a ring its mature cards fill, with a badge counting the due
cards (at most "99+", growing inwards so it never passes the card's edge).
The action is a review when reviews are due, studying when only new cards
are, and adding content otherwise, which opens the studyset: the card only
invites a session that has something to study. A long press stars it, as
does the star, which a hovering pointer reveals and which stays while
starred; the options edit, archive or unarchive, and delete it. An archived
studyset rests: dimmed, neutral, paused. Until its progress is known, a
muted bar stands in for "No content yet", which it might not be.

A starred star is amber, dark enough in the light theme to stand out from
the cards as a graphic must (3:1), a colour the theme extras now hold. It is
Material's filled star: Lucide has no filled one, and the fill is what says
the star is on.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-01 09:59:45 +02:00
c21ce6232b
feat(app): the UI foundation: theme, tabs and routes, strings
The app gets its look and navigation before more features land on them.

- Theme (core/theme/): hand-written light and dark Material 3 colour schemes around
  cobalt and copper; ThemeExtras for the colours Material has no role for (states, the
  four FSRS ratings); the 20 colours a person can give content. Tests hold text to WCAG
  AA contrast on each palette colour and on the dark theme's primary colour and surface.
- Layout and shared widgets: Material 3's window-size classes (core/layout/);
  SectionCard, AppLoadingIndicator, confirmDestructiveAction and FoundById
  (core/widget/). Lucide icons.
- Navigation with go_router: four tabs (Home, Library, Explore, Profile), each keeping
  its own stack of screens, as a navigation rail with the app's icon from 600 dp and as
  a navigation bar below; choosing the open tab again returns to its first screen. A
  studyset's screen sits in the library; studying, cards and settings cover the tabs.
- Routes carry ids: FoundById finds the item in the repository's live list, so a
  studyset deleted on another device, or an old link, says it no longer exists. A
  screen that deletes its own item keeps showing it while it closes, rather than
  flashing that message.
- Every person gets an app of their own (PersonApp, lib/src/app/): a
  MaterialApp.router with its own GoRouter and PersonScope around the navigator, built
  by SessionGate inside the person's keyed subtree. Screens read the person's
  repositories wherever they are built, so the static route() helpers and the listener
  that closed every pushed screen on a change of person are gone: a change of person
  now disposes the whole router and starts again at home.
- The account dialog becomes the Profile tab, with Settings (sign out, delete the
  account, About) behind a gear. A waiting GitHub sign-in now keeps waiting while
  another tab shows; a change of person still cancels it.
- The new screens' strings come from Flutter's gen-l10n (English for now). The
  generated files are committed, left out of analysis and formatting, and written
  anew by `flutter pub get`, so CI's check of generated code also catches stale ones.

Widget tests now start the whole app (test/support/pump_app.dart) at the screen's path
and move through it as a person would, by tabs and taps.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-01 09:59:44 +02:00