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 00:18:31 +02:00
// ignore: unused_import
import ' package:intl/intl.dart ' as intl ;
import ' app_localizations.dart ' ;
// ignore_for_file: type=lint
/// The translations for English (`en`).
class AppLocalizationsEn extends AppLocalizations {
AppLocalizationsEn ( [ String locale = ' en ' ] ) : super ( locale ) ;
@ override
String get appTitle = > ' Halkyon Learn ' ;
@ override
String get shellTabHome = > ' Home ' ;
@ override
String get shellTabLibrary = > ' Library ' ;
@ override
String get shellTabExplore = > ' Explore ' ;
@ override
String get shellTabProfile = > ' Profile ' ;
@ override
String get homeTitle = > ' Home ' ;
@ override
String get homeLibraryIntro = >
' Your studysets and cards are in your library. ' ;
@ override
String get homeOpenLibrary = > ' Open library ' ;
@ override
String get libraryTitle = > ' Library ' ;
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 00:53:51 +02:00
@ override
String get libraryNewStudyset = > ' New studyset ' ;
@ override
String get libraryEmpty = > ' No studysets yet ' ;
@ override
String get libraryUnreadable = > ' Couldn \' t read your studysets. ' ;
@ override
String get libraryArchivedSection = > ' Archived ' ;
@ override
String get librarySearchTooltip = > ' Search ' ;
@ override
String get librarySearchHint = > ' Search studysets ' ;
@ override
String get librarySearchClearTooltip = > ' Clear query ' ;
@ override
String get librarySearchCloseTooltip = > ' Close search ' ;
@ override
String get librarySearchNoMatches = > ' No studysets match your search ' ;
@ override
String get libraryStudysetArchived = > ' Studyset archived ' ;
@ override
String get libraryStudysetUnarchived = > ' Studyset unarchived ' ;
2026-10-01 08:55:39 +02:00
@ override
String get libraryStudysetDeleted = > ' Studyset deleted ' ;
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 00:53:51 +02:00
@ override
String get libraryDeleteTitle = > ' Delete studyset? ' ;
@ override
String get libraryDeleteMessage = >
2026-10-01 08:55:39 +02:00
' It is deleted with its cards, on all your devices. You can undo this for a few seconds. ' ;
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 00:53:51 +02:00
@ override
String get libraryDeleteConfirm = > ' Delete ' ;
@ override
String get studysetSheetCreateTitle = > ' Create studyset ' ;
@ override
String get studysetSheetEditTitle = > ' Edit studyset ' ;
@ override
String get studysetSheetCreate = > ' Create ' ;
@ override
String get studysetSheetSave = > ' Save ' ;
@ override
String get studysetSheetTitleLabel = > ' Title ' ;
@ override
String get studysetSheetSecondaryLabel = > ' Secondary title ' ;
@ override
String get studysetSheetSecondaryHelper = >
' Shown as a small label above the title. ' ;
@ override
String get studysetSheetColor = > ' Color ' ;
@ override
String get studysetSheetDiscardTitle = > ' Discard changes? ' ;
@ override
String get studysetSheetDiscardMessage = >
' You have unsaved changes to this studyset. Discard them and close the form? ' ;
@ override
String get studysetSheetDiscardConfirm = > ' Discard ' ;
@ override
String paletteColorName ( String name ) {
String _temp0 = intl . Intl . selectLogic (
name ,
{
' carrot ' : ' Carrot ' ,
' cerise ' : ' Cerise ' ,
' cobalt ' : ' Cobalt ' ,
' copper ' : ' Copper ' ,
' enzian ' : ' Enzian ' ,
' fern ' : ' Fern ' ,
' forest ' : ' Forest ' ,
' ocean ' : ' Ocean ' ,
' olive ' : ' Olive ' ,
' pacific ' : ' Pacific ' ,
' pickle ' : ' Pickle ' ,
' pine ' : ' Pine ' ,
' pink ' : ' Pink ' ,
' plum ' : ' Plum ' ,
' purple ' : ' Purple ' ,
' reef ' : ' Reef ' ,
' sahara ' : ' Sahara ' ,
' slateblue ' : ' Slate blue ' ,
' soil ' : ' Soil ' ,
' strawberry ' : ' Strawberry ' ,
' other ' : ' $ name ' ,
} ,
) ;
return ' $ _temp0 ' ;
}
@ override
String colorPickerLabel ( String color ) {
return ' Color: $ color ' ;
}
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 00:40:52 +02:00
@ override
String studysetCountFlashcards ( int count ) {
String _temp0 = intl . Intl . pluralLogic (
count ,
locale: localeName ,
other: ' $ count flashcards ' ,
one: ' 1 flashcard ' ,
) ;
return ' $ _temp0 ' ;
}
@ override
String get studysetNoContent = > ' No content yet ' ;
@ override
String get studysetActionReview = > ' Review ' ;
@ override
String get studysetActionStudy = > ' Study ' ;
@ override
String get studysetActionAdd = > ' Add content ' ;
@ override
String get studysetArchived = > ' Archived ' ;
@ override
String get studysetStar = > ' Star ' ;
@ override
String get studysetUnstar = > ' Unstar ' ;
@ override
String get studysetOptions = > ' Options ' ;
@ override
String get studysetMenuEdit = > ' Edit ' ;
@ override
String get studysetMenuArchive = > ' Archive ' ;
@ override
String get studysetMenuUnarchive = > ' Unarchive ' ;
@ override
String get studysetMenuDelete = > ' Delete ' ;
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 00:18:31 +02:00
@ override
String get exploreTitle = > ' Explore ' ;
@ override
String get exploreEmpty = > ' Studysets that others share will appear here. ' ;
@ override
String get profileTitle = > ' Profile ' ;
@ override
String get profileSettingsTooltip = > ' Settings ' ;
@ override
String get profileIdentified = > ' Your progress is kept with your account. ' ;
@ override
String get profileAnonymous = >
' Progress is saved on this device. Sign in to keep it. ' ;
@ override
String get profileSignInWithGitHub = > ' Sign in with GitHub ' ;
@ override
String get profileSignInFailed = > ' Couldn \' t sign in with GitHub. ' ;
@ override
String get profileContinueInBrowser = > ' Continue in your browser. ' ;
@ override
String get profileSwitchTitle = > ' Switch to your GitHub account? ' ;
@ override
String get profileSwitchMessage = >
' This GitHub account already has progress from another device. The studysets on this device can \' t be combined with it yet: if you switch, they are deleted. ' ;
@ override
String get profileSwitchStay = > ' Stay anonymous ' ;
@ override
String get profileSwitchConfirm = > ' Switch ' ;
@ override
String get settingsTitle = > ' Settings ' ;
@ override
String get settingsAccount = > ' Account ' ;
@ override
String get settingsSignOut = > ' Sign out ' ;
@ override
String get settingsSignOutTitle = > ' Sign out? ' ;
@ override
String get settingsSignOutAnonymousMessage = >
' This account can \' t be signed into again, so signing out deletes it with its progress. ' ;
@ override
String get settingsDeleteAccount = > ' Delete account ' ;
@ override
String get settingsDeleteTitle = > ' Delete your account? ' ;
@ override
String get settingsDeleteMessage = >
' Everything you have, your studysets, cards and reviews, is deleted on the server and on all your devices. This can \' t be undone. ' ;
@ override
String get settingsAbout = > ' About Halkyon Learn ' ;
@ override
String get commonCancel = > ' Cancel ' ;
@ override
String get commonClose = > ' Close ' ;
@ override
String get commonUndo = > ' Undo ' ;
@ override
String get commonTryAgain = > ' Try again ' ;
@ override
String get sessionUnreachable = > ' Couldn \' t reach the server. ' ;
@ override
String get notFoundGone = >
' This no longer exists. It may have been deleted on another device. ' ;
}