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
import ' dart:async ' ;
import ' package:flutter/foundation.dart ' ;
import ' package:flutter/widgets.dart ' ;
import ' package:flutter_localizations/flutter_localizations.dart ' ;
import ' package:intl/intl.dart ' as intl ;
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 02:01:01 +02:00
import ' app_localizations_de.dart ' ;
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
import ' app_localizations_en.dart ' ;
// ignore_for_file: type=lint
/// Callers can lookup localized strings with an instance of AppLocalizations
/// returned by `AppLocalizations.of(context)`.
///
/// Applications need to include `AppLocalizations.delegate()` in their app's
/// `localizationDelegates` list, and the locales they support in the app's
/// `supportedLocales` list. For example:
///
/// ```dart
/// import 'l10n/app_localizations.dart';
///
/// return MaterialApp(
/// localizationsDelegates: AppLocalizations.localizationsDelegates,
/// supportedLocales: AppLocalizations.supportedLocales,
/// home: MyApplicationHome(),
/// );
/// ```
///
/// ## Update pubspec.yaml
///
/// Please make sure to update your pubspec.yaml to include the following
/// packages:
///
/// ```yaml
/// dependencies:
/// # Internationalization support.
/// flutter_localizations:
/// sdk: flutter
/// intl: any # Use the pinned version from flutter_localizations
///
/// # Rest of dependencies
/// ```
///
/// ## iOS Applications
///
/// iOS applications define key application metadata, including supported
/// locales, in an Info.plist file that is built into the application bundle.
/// To configure the locales supported by your app, you’ ll need to edit this
/// file.
///
/// First, open your project’ s ios/Runner.xcworkspace Xcode workspace file.
/// Then, in the Project Navigator, open the Info.plist file under the Runner
/// project’ s Runner folder.
///
/// Next, select the Information Property List item, select Add Item from the
/// Editor menu, then select Localizations from the pop-up menu.
///
/// Select and expand the newly-created Localizations item then, for each
/// locale your application supports, add a new item and select the locale
/// you wish to add from the pop-up menu in the Value field. This list should
/// be consistent with the languages listed in the AppLocalizations.supportedLocales
/// property.
abstract class AppLocalizations {
AppLocalizations ( String locale )
: localeName = intl . Intl . canonicalizedLocale ( locale . toString ( ) ) ;
final String localeName ;
static AppLocalizations of ( BuildContext context ) {
return Localizations . of < AppLocalizations > ( context , AppLocalizations ) ! ;
}
static const LocalizationsDelegate < AppLocalizations > delegate =
_AppLocalizationsDelegate ( ) ;
/// A list of this localizations delegate along with the default localizations
/// delegates.
///
/// Returns a list of localizations delegates containing this delegate along with
/// GlobalMaterialLocalizations.delegate, GlobalCupertinoLocalizations.delegate,
/// and GlobalWidgetsLocalizations.delegate.
///
/// Additional delegates can be added by appending to this list in
/// MaterialApp. This list does not have to be used at all if a custom list
/// of delegates is preferred or required.
static const List < LocalizationsDelegate < dynamic > > localizationsDelegates =
< LocalizationsDelegate < dynamic > > [
delegate ,
GlobalMaterialLocalizations . delegate ,
GlobalCupertinoLocalizations . delegate ,
GlobalWidgetsLocalizations . delegate ,
] ;
/// A list of this localizations delegate's supported locales.
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 02:01:01 +02:00
static const List < Locale > supportedLocales = < Locale > [
Locale ( ' de ' ) ,
Locale ( ' en ' ) ,
Locale ( ' en ' , ' GB ' ) ,
Locale ( ' en ' , ' US ' ) ,
] ;
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
/// The app's name, e.g. in the window title and the rail's logo.
///
/// In en, this message translates to:
/// **'Halkyon Learn'**
String get appTitle ;
feat: a launch screen with the kingfisher and "Halkyon" over "Learn"
While the app opens (the instance, the session, the person's data), it
showed a spinner, in three ways. It now shows the kingfisher, and beneath
it "Halkyon" over "Learn", cobalt and copper as the logo has them, in the
app's own Noto Sans (your call, with JetBrains Mono to compare: the
logo's ITC Avant Garde isn't free to ship), and a quiet line of progress.
AGENTS.md now has the rule on text fields' limits, busy sheets and the
launch screen.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MMm7usAo6q9ZZRoPkWjoxi
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-11 14:26:03 +02:00
/// No description provided for @launchProduct.
///
/// In en, this message translates to:
/// **'Learn'**
String get launchProduct ;
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
/// No description provided for @shellTabHome.
///
/// In en, this message translates to:
/// **'Home'**
String get shellTabHome ;
/// No description provided for @shellTabLibrary.
///
/// In en, this message translates to:
/// **'Library'**
String get shellTabLibrary ;
/// No description provided for @shellTabExplore.
///
/// In en, this message translates to:
/// **'Explore'**
String get shellTabExplore ;
/// No description provided for @shellTabProfile.
///
/// In en, this message translates to:
/// **'Profile'**
String get shellTabProfile ;
/// No description provided for @homeTitle.
///
/// In en, this message translates to:
/// **'Home'**
String get homeTitle ;
/// No description provided for @libraryTitle.
///
/// In en, this message translates to:
/// **'Library'**
String get libraryTitle ;
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 01:43:28 +02:00
/// No description provided for @libraryCreateStudyset.
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
///
/// In en, this message translates to:
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 01:43:28 +02:00
/// **'Create studyset'**
String get libraryCreateStudyset ;
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
/// No description provided for @libraryEmpty.
///
/// In en, this message translates to:
/// **'No studysets yet'**
String get libraryEmpty ;
2026-10-17 14:09:59 +02:00
/// An empty library while this device's first sync brings the person's studysets.
///
/// In en, this message translates to:
/// **'Your library is on its way from {instance}…'**
String libraryOnItsWay ( String instance ) ;
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
/// No description provided for @libraryUnreadable.
///
/// In en, this message translates to:
/// **'Couldn\'t read your studysets.'**
String get libraryUnreadable ;
/// No description provided for @libraryArchivedSection.
///
/// In en, this message translates to:
/// **'Archived'**
String get libraryArchivedSection ;
/// No description provided for @librarySearchTooltip.
///
/// In en, this message translates to:
/// **'Search'**
String get librarySearchTooltip ;
/// No description provided for @librarySearchHint.
///
/// In en, this message translates to:
/// **'Search studysets'**
String get librarySearchHint ;
/// No description provided for @librarySearchClearTooltip.
///
/// In en, this message translates to:
/// **'Clear query'**
String get librarySearchClearTooltip ;
/// No description provided for @librarySearchCloseTooltip.
///
/// In en, this message translates to:
/// **'Close search'**
String get librarySearchCloseTooltip ;
/// No description provided for @librarySearchNoMatches.
///
/// In en, this message translates to:
/// **'No studysets match your search'**
String get librarySearchNoMatches ;
/// No description provided for @libraryStudysetArchived.
///
/// In en, this message translates to:
/// **'Studyset archived'**
String get libraryStudysetArchived ;
/// No description provided for @libraryStudysetUnarchived.
///
/// In en, this message translates to:
/// **'Studyset unarchived'**
String get libraryStudysetUnarchived ;
2026-10-01 08:55:39 +02:00
/// No description provided for @libraryStudysetDeleted.
///
/// In en, this message translates to:
2026-10-12 18:50:10 +02:00
/// **'Moved to Recently deleted'**
2026-10-01 08:55:39 +02:00
String get libraryStudysetDeleted ;
2026-10-12 18:50:10 +02:00
/// No description provided for @libraryDeletedSection.
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
///
/// In en, this message translates to:
2026-10-12 18:50:10 +02:00
/// **'Recently deleted'**
String get libraryDeletedSection ;
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
2026-10-12 18:50:10 +02:00
/// No description provided for @libraryDeletedNote.
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
///
/// In en, this message translates to:
2026-10-12 18:50:10 +02:00
/// **'Deleted studysets are kept for 14 days, with everything inside them. After that, they are deleted for good.'**
String get libraryDeletedNote ;
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
2026-10-12 18:50:10 +02:00
/// No description provided for @libraryDeletedDaysLeft.
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
///
/// In en, this message translates to:
2026-10-12 18:50:10 +02:00
/// **'{days, plural, =0{Deleted for good soon} =1{1 day left} other{{days} days left}}'**
String libraryDeletedDaysLeft ( int days ) ;
/// No description provided for @libraryRestore.
///
/// In en, this message translates to:
/// **'Restore'**
String get libraryRestore ;
/// No description provided for @libraryDeleteNow.
///
/// In en, this message translates to:
/// **'Delete now'**
String get libraryDeleteNow ;
/// No description provided for @libraryStudysetRestored.
///
/// In en, this message translates to:
/// **'Studyset restored'**
String get libraryStudysetRestored ;
/// No description provided for @libraryDeleteForGoodTitle.
///
/// In en, this message translates to:
/// **'Delete for good?'**
String get libraryDeleteForGoodTitle ;
/// No description provided for @libraryDeleteForGoodMessage.
///
/// In en, this message translates to:
/// **'“{title}” is deleted with everything inside it and all their cards, on all your devices. This can\'t be undone.'**
String libraryDeleteForGoodMessage ( String title ) ;
/// No description provided for @libraryDeleteForGoodConfirm.
///
/// In en, this message translates to:
/// **'Delete for good'**
String get libraryDeleteForGoodConfirm ;
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
/// No description provided for @studysetSheetCreateTitle.
///
/// In en, this message translates to:
/// **'Create studyset'**
String get studysetSheetCreateTitle ;
/// No description provided for @studysetSheetEditTitle.
///
/// In en, this message translates to:
/// **'Edit studyset'**
String get studysetSheetEditTitle ;
/// No description provided for @studysetSheetCreate.
///
/// In en, this message translates to:
/// **'Create'**
String get studysetSheetCreate ;
/// No description provided for @studysetSheetSave.
///
/// In en, this message translates to:
/// **'Save'**
String get studysetSheetSave ;
/// No description provided for @studysetSheetTitleLabel.
///
/// In en, this message translates to:
/// **'Title'**
String get studysetSheetTitleLabel ;
/// No description provided for @studysetSheetSecondaryLabel.
///
/// In en, this message translates to:
/// **'Secondary title'**
String get studysetSheetSecondaryLabel ;
/// No description provided for @studysetSheetSecondaryHelper.
///
/// In en, this message translates to:
feat(app): Home, the Library and the studyset sheets laid out as before
Home: plain cards on the surface, 16 dp apart, the goal first and the
Pomodoro timer after it in a new dashboard. The streak chip sits centred
in the app bar, and its sheet shows the days inside a big flame and the
week as marks over the weekdays' initials, with a word on today in a box.
The week names its day as "Thursday, Oct 8" and its days carry no
tooltip. The heatmap scrolls its weekdays with the weeks, outlines the
days to come, and names a day's activities with its date. The goal card
has a ring of 5 dp, a check once met and a pencil, and its sheet saves
across its width with Remove goal below. Arranging puts an eye on each
widget's corner and lifts the one picked up, with a tap the hand feels;
with every widget hidden, Customise brings arranging back. The stepper
moves to core, where the goal's sheet takes it too.
Library: the counts line includes files (PDFs); Refresh in the app bar
and pulling the list down sync now; a list that can't be read offers
Retry, which reads it again; the star keeps its square corners.
Studyset sheet: "Title *", the secondary title explained behind a
question mark, Inherit as a box beside the colour, and the description
in up to three lines with a chevron. Move sheet: round marks, the
studyset itself marked as current, the top level only while it is
inside another, archived studysets last under their heading, the
destination named in the footer, and a new studyset to move into, made
only once the move is.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-08 11:12:23 +02:00
/// **'Shown as a small label above the title on cards and the studyset\'s header. Paths use it to label tiers (Challenge, Milestone, Lesson).'**
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
String get studysetSheetSecondaryHelper ;
/// No description provided for @studysetSheetColor.
///
/// In en, this message translates to:
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 02:01:01 +02:00
/// **'Colour'**
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
String get studysetSheetColor ;
/// No description provided for @studysetSheetDiscardTitle.
///
/// In en, this message translates to:
/// **'Discard changes?'**
String get studysetSheetDiscardTitle ;
/// No description provided for @studysetSheetDiscardMessage.
///
/// In en, this message translates to:
/// **'You have unsaved changes to this studyset. Discard them and close the form?'**
String get studysetSheetDiscardMessage ;
/// No description provided for @studysetSheetDiscardConfirm.
///
/// In en, this message translates to:
/// **'Discard'**
String get studysetSheetDiscardConfirm ;
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
/// No description provided for @studysetCountFlashcards.
///
/// In en, this message translates to:
/// **'{count, plural, =1{1 flashcard} other{{count} flashcards}}'**
String studysetCountFlashcards ( int count ) ;
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
/// No description provided for @studysetCountNotes.
///
/// In en, this message translates to:
/// **'{count, plural, =1{1 note} other{{count} notes}}'**
String studysetCountNotes ( int count ) ;
feat(app): Home, the Library and the studyset sheets laid out as before
Home: plain cards on the surface, 16 dp apart, the goal first and the
Pomodoro timer after it in a new dashboard. The streak chip sits centred
in the app bar, and its sheet shows the days inside a big flame and the
week as marks over the weekdays' initials, with a word on today in a box.
The week names its day as "Thursday, Oct 8" and its days carry no
tooltip. The heatmap scrolls its weekdays with the weeks, outlines the
days to come, and names a day's activities with its date. The goal card
has a ring of 5 dp, a check once met and a pencil, and its sheet saves
across its width with Remove goal below. Arranging puts an eye on each
widget's corner and lifts the one picked up, with a tap the hand feels;
with every widget hidden, Customise brings arranging back. The stepper
moves to core, where the goal's sheet takes it too.
Library: the counts line includes files (PDFs); Refresh in the app bar
and pulling the list down sync now; a list that can't be read offers
Retry, which reads it again; the star keeps its square corners.
Studyset sheet: "Title *", the secondary title explained behind a
question mark, Inherit as a box beside the colour, and the description
in up to three lines with a chevron. Move sheet: round marks, the
studyset itself marked as current, the top level only while it is
inside another, archived studysets last under their heading, the
destination named in the footer, and a new studyset to move into, made
only once the move is.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-08 11:12:23 +02:00
/// No description provided for @studysetCountFiles.
///
/// In en, this message translates to:
/// **'{count, plural, =1{1 file} other{{count} files}}'**
String studysetCountFiles ( int count ) ;
/// No description provided for @libraryRefresh.
///
/// In en, this message translates to:
/// **'Refresh'**
String get libraryRefresh ;
2026-10-12 06:51:16 +02:00
/// No description provided for @libraryOptions.
///
/// In en, this message translates to:
/// **'Library options'**
String get libraryOptions ;
2026-10-08 14:47:23 +02:00
/// No description provided for @libraryImport.
///
/// In en, this message translates to:
/// **'Import'**
String get libraryImport ;
/// No description provided for @libraryImportAnkiHint.
///
/// In en, this message translates to:
/// **'Becomes a studyset of its own, from an .apkg or .colpkg file'**
String get libraryImportAnkiHint ;
feat(archive): import a studyset with everything in it and its progress
Your call (2026-10-11): an archive, imported from the Library's Import, to
bring a library from elsewhere as one's own, and later to restore backups
and move studysets between instances before federation.
An archive is a .zip: archive.json says its format; the studyset is laid
out as a release (ADR 0005, rule 3), so the release copier writes it, now
also unlinked from any publication (ReleaseCopier.write), its cards marked
"archive" as imported ones are, handing back each item's new id. A card may
name a dictionary entry instead of its payload: the import makes it from
the instance's dictionary as adding from it does, with today's entry,
examples and tags, and leaves out (counting) one the instance lacks; for
that, DictionaryEndpoint.entries gives entries by their ids, at most 1,000
at once. reviews.json lists reviews of the archive's cards; they go into
the person's review log once the cards are in, so every card's schedule
goes on from them.
The sheet says what the archive holds ("Japanese" holds: 12 studysets,
7,745 flashcards, 3 paths, 20,317 reviews), imports once confirmed, busy
meanwhile, and opens the studyset made. Not an archive, a newer format or
an unreadable one says so and imports nothing. No export into the format
yet.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MMm7usAo6q9ZZRoPkWjoxi
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-11 16:39:07 +02:00
/// No description provided for @libraryImportArchive.
///
/// In en, this message translates to:
/// **'Halkyon archive'**
String get libraryImportArchive ;
/// No description provided for @libraryImportArchiveHint.
///
/// In en, this message translates to:
/// **'A studyset with everything in it and its progress, from a .zip file'**
String get libraryImportArchiveHint ;
/// No description provided for @archiveFileType.
///
/// In en, this message translates to:
/// **'Halkyon archive'**
String get archiveFileType ;
/// No description provided for @archiveImportTitle.
///
/// In en, this message translates to:
/// **'Import archive'**
String get archiveImportTitle ;
/// No description provided for @archiveImportReading.
///
/// In en, this message translates to:
/// **'Reading the archive…'**
String get archiveImportReading ;
/// No description provided for @archiveImportHolds.
///
/// In en, this message translates to:
/// **'“{title}” holds:'**
String archiveImportHolds ( String title ) ;
/// No description provided for @archiveCountStudysets.
///
/// In en, this message translates to:
/// **'{count, plural, =1{1 studyset} other{{count} studysets}}'**
String archiveCountStudysets ( int count ) ;
/// No description provided for @archiveCountReviews.
///
/// In en, this message translates to:
/// **'{count, plural, =1{1 review} other{{count} reviews}}'**
String archiveCountReviews ( int count ) ;
/// No description provided for @archiveImportConfirm.
///
/// In en, this message translates to:
/// **'Import'**
String get archiveImportConfirm ;
/// No description provided for @archiveImported.
///
/// In en, this message translates to:
/// **'Imported: {cards} and {reviews}.'**
String archiveImported ( String cards , String reviews ) ;
/// No description provided for @archiveImportMissingEntries.
///
/// In en, this message translates to:
/// **'{count, plural, =1{1 word isn\'t} other{{count} words aren\'t}} in this instance\'s dictionary and stayed out.'**
String archiveImportMissingEntries ( int count ) ;
/// No description provided for @archiveNotAnArchive.
///
/// In en, this message translates to:
/// **'This file isn\'t a Halkyon archive.'**
String get archiveNotAnArchive ;
/// No description provided for @archiveNewerFormat.
///
/// In en, this message translates to:
/// **'This archive comes from a newer version of the app. Update the app to import it.'**
String get archiveNewerFormat ;
/// No description provided for @archiveUnreadable.
///
/// In en, this message translates to:
/// **'This archive can\'t be read.'**
String get archiveUnreadable ;
/// No description provided for @archiveImportBrokeOff.
///
/// In en, this message translates to:
/// **'The import broke off. Check the connection and try again.'**
String get archiveImportBrokeOff ;
feat(app): Home, the Library and the studyset sheets laid out as before
Home: plain cards on the surface, 16 dp apart, the goal first and the
Pomodoro timer after it in a new dashboard. The streak chip sits centred
in the app bar, and its sheet shows the days inside a big flame and the
week as marks over the weekdays' initials, with a word on today in a box.
The week names its day as "Thursday, Oct 8" and its days carry no
tooltip. The heatmap scrolls its weekdays with the weeks, outlines the
days to come, and names a day's activities with its date. The goal card
has a ring of 5 dp, a check once met and a pencil, and its sheet saves
across its width with Remove goal below. Arranging puts an eye on each
widget's corner and lifts the one picked up, with a tap the hand feels;
with every widget hidden, Customise brings arranging back. The stepper
moves to core, where the goal's sheet takes it too.
Library: the counts line includes files (PDFs); Refresh in the app bar
and pulling the list down sync now; a list that can't be read offers
Retry, which reads it again; the star keeps its square corners.
Studyset sheet: "Title *", the secondary title explained behind a
question mark, Inherit as a box beside the colour, and the description
in up to three lines with a chevron. Move sheet: round marks, the
studyset itself marked as current, the top level only while it is
inside another, archived studysets last under their heading, the
destination named in the footer, and a new studyset to move into, made
only once the move is.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-08 11:12:23 +02:00
/// No description provided for @libraryRetry.
///
/// In en, this message translates to:
/// **'Retry'**
String get libraryRetry ;
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
/// No description provided for @studysetNoContent.
///
/// In en, this message translates to:
/// **'No content yet'**
String get studysetNoContent ;
feat(app): the studyset screen laid out as before
The Study pill reads "Learn 12 flashcards" in the app bar and "Learn 12"
floating, "Study" with nothing due, and is off with nothing to study,
saying so. With a path not finished, a chevron beside it offers to start
or continue the path: a menu in the app bar, a Study mode sheet on
phones. The floating bar rises above the licence line, which takes two
lines at most.
Add material offers a path, flashcards, a note and a PDF, in that order;
imports moved to the options' Import content, which asks for a learning
path or an Anki deck. The options read Edit, Move, Study options, Import
content, Reset progress, Archive, Delete. The bibliography is an outlined
round button above the content, and on phones at the foot of the topics.
The tabs sit on a line of their own across the card, what the tab shown
offers beside them: the flashcards' filter and options, and on the paths
Reorder once there are two, which turns them into handles to drag. An
empty tab says so in its middle; tapping the open tab again returns to
its top, and pulling a tab down syncs. The breadcrumb's steps are plain
links between "›", the studyset itself bold. A long description is cut at
a fixed height, measured, with Read more opening it whole under the
studyset's title. A studyset that no longer exists leads back to the
library.
The topics: All topics on the primary colour, in view also while they
are rearranged; each topic with the study it invites in a round icon,
what it holds ("Empty" with nothing, files counted too), and its options
(now with Reset progress) behind "…" or a long press where the finger
is; New sub-studyset at the foot below a line, the list with its
scrollbar always shown. All topics opens Add material while nothing is
due.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-08 11:42:50 +02:00
/// No description provided for @studysetEmpty.
///
/// In en, this message translates to:
/// **'Empty'**
String get studysetEmpty ;
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
/// No description provided for @studysetActionReview.
///
/// In en, this message translates to:
/// **'Review'**
String get studysetActionReview ;
/// No description provided for @studysetActionStudy.
///
/// In en, this message translates to:
/// **'Study'**
String get studysetActionStudy ;
/// No description provided for @studysetActionAdd.
///
/// In en, this message translates to:
/// **'Add content'**
String get studysetActionAdd ;
/// No description provided for @studysetArchived.
///
/// In en, this message translates to:
/// **'Archived'**
String get studysetArchived ;
/// No description provided for @studysetStar.
///
/// In en, this message translates to:
/// **'Star'**
String get studysetStar ;
/// No description provided for @studysetUnstar.
///
/// In en, this message translates to:
/// **'Unstar'**
String get studysetUnstar ;
2026-10-10 05:17:10 +02:00
/// No description provided for @studysetByYou.
///
/// In en, this message translates to:
/// **'By you'**
String get studysetByYou ;
/// No description provided for @studysetByAuthor.
///
/// In en, this message translates to:
/// **'By {author}'**
String studysetByAuthor ( String author ) ;
/// No description provided for @studysetByOthers.
///
/// In en, this message translates to:
/// **'A copy'**
String get studysetByOthers ;
/// No description provided for @studysetVisibilityPrivate.
///
/// In en, this message translates to:
/// **'Private'**
String get studysetVisibilityPrivate ;
/// No description provided for @studysetVisibilityPublic.
///
/// In en, this message translates to:
/// **'Public'**
String get studysetVisibilityPublic ;
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
/// No description provided for @studysetOptions.
///
/// In en, this message translates to:
/// **'Options'**
String get studysetOptions ;
feat(app): cite sources in APA, MLA or Chicago, and give studysets a bibliography
A source is now shared by learning paths and studysets (core/citation/):
authors as full names, year, title, what it is a part of, publisher,
ISBN, link, and a citation kept as a file brought it. formatCitation
cites it in APA 7, MLA 9 or Chicago 18: it inverts and abbreviates
names itself (particles, suffixes, names written as already cited stay),
sets a work of its own in italics and a part of something apart from
the whole, and writes a missing year as "n.d." in APA and Chicago.
- Settings: a Citations card per device, with a preview and a link to
the style's own guide.
- Studysets: a Bibliography button opens the works cited, to read and,
behind its pencil, to change (studyset schema 4).
- Steps: sources edited through the same sheet, opened from an
"N sources" button in the runner (step schema 2; schema 1 reads on).
- Path import: steps' sources, text kept as written, the old format's
"citation" read as what a source is part of.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-07 17:07:04 +02:00
/// No description provided for @studysetBibliography.
///
/// In en, this message translates to:
/// **'Bibliography'**
String get studysetBibliography ;
/// No description provided for @studysetBibliographyIntro.
///
/// In en, this message translates to:
/// **'Works cited across this studyset, in the citation style chosen in Settings.'**
String get studysetBibliographyIntro ;
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
/// No description provided for @studysetMenuEdit.
///
/// In en, this message translates to:
/// **'Edit'**
String get studysetMenuEdit ;
/// No description provided for @studysetMenuArchive.
///
/// In en, this message translates to:
/// **'Archive'**
String get studysetMenuArchive ;
/// No description provided for @studysetMenuUnarchive.
///
/// In en, this message translates to:
/// **'Unarchive'**
String get studysetMenuUnarchive ;
/// No description provided for @studysetMenuDelete.
///
/// In en, this message translates to:
/// **'Delete'**
String get studysetMenuDelete ;
feat(app): the studyset screen with its topics, the tree's own screen
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 12:59:20 +02:00
/// No description provided for @studysetMenuMove.
///
/// In en, this message translates to:
/// **'Move to…'**
String get studysetMenuMove ;
/// No description provided for @studysetTabFlashcards.
///
/// In en, this message translates to:
/// **'Flashcards'**
String get studysetTabFlashcards ;
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
/// No description provided for @studysetTabNotes.
///
/// In en, this message translates to:
/// **'Notes'**
String get studysetTabNotes ;
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
/// No description provided for @studysetTabPaths.
///
/// In en, this message translates to:
/// **'Paths'**
String get studysetTabPaths ;
/// No description provided for @studysetCreatePath.
///
/// In en, this message translates to:
/// **'Create a path'**
String get studysetCreatePath ;
/// No description provided for @pathImportMenu.
///
/// In en, this message translates to:
/// **'Import a path (JSON)'**
String get pathImportMenu ;
/// No description provided for @pathImportFileType.
///
/// In en, this message translates to:
/// **'Learning path (JSON)'**
String get pathImportFileType ;
/// No description provided for @pathImported.
///
/// In en, this message translates to:
/// **'{count, plural, =1{Imported \"{title}\" with 1 step.} other{Imported \"{title}\" with {count} steps.}}'**
String pathImported ( String title , int count ) ;
/// No description provided for @pathImportSkipped.
///
/// In en, this message translates to:
/// **'{count, plural, =1{1 block was left out: images and animations aren\'t imported.} other{{count} blocks were left out: images and animations aren\'t imported.}}'**
String pathImportSkipped ( int count ) ;
/// No description provided for @pathImportFailed.
///
/// In en, this message translates to:
/// **'Couldn\'t import this file. {reason}'**
String pathImportFailed ( String reason ) ;
feat(paths): path files carry tables, callouts and furigana, and several import at once
The import format gains the blocks lessons need: tables (rows of cells),
callouts (a kind, a title, blocks inside, but no table or callout),
spoilers, quotes and dividers; and runs of text gain a reading shown as
furigana, a colour, underline and strikethrough. A line break is a "\n"
in the text. Still simple blocks turned into rich text here, so a change
of the editor's own format never breaks a file written before. A table's
columns are as wide as their longest line, narrowed alike to fit a
phone, where the cells wrap.
A studyset's Import › Learning paths now takes several files at once,
one path each, stored in the order of their names, so a chapter's
lessons come in with one pick. Every file is read before anything is
stored: one that can't be read is named, and nothing comes in.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MMm7usAo6q9ZZRoPkWjoxi
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-11 19:31:39 +02:00
/// No description provided for @pathImportFileFailed.
///
/// In en, this message translates to:
/// **'Couldn\'t import {file}. {reason}'**
String pathImportFileFailed ( String file , String reason ) ;
feat(paths): confetti for a finished path, and an import's progress
Finishing a path bursts confetti over the view saying so: pieces in the
palette's brightest colours fall once from the top, turning, and fade
out in under two seconds, so the screen settles. Drawn by hand rather
than with a package; nothing falls with animations switched off on the
device.
Importing paths into a studyset shows its Paths tab at once, and above
the paths, as they come in, how far the import has come: which path of
how many, by its title, and a bar filling with every path and step
stored. Before, the paths appeared one by one in whatever tab showed,
with nothing saying how many were still to come.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MMm7usAo6q9ZZRoPkWjoxi
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-11 21:23:11 +02:00
/// No description provided for @pathImportReading.
///
/// In en, this message translates to:
/// **'{count, plural, =1{Reading the file…} other{Reading {count} files…}}'**
String pathImportReading ( int count ) ;
/// No description provided for @pathImportStoring.
///
/// In en, this message translates to:
/// **'{count, plural, =1{Importing “{title}”…} other{Importing path {index} of {count}: “{title}”…}}'**
String pathImportStoring ( int index , int count , String title ) ;
feat(paths): path files carry tables, callouts and furigana, and several import at once
The import format gains the blocks lessons need: tables (rows of cells),
callouts (a kind, a title, blocks inside, but no table or callout),
spoilers, quotes and dividers; and runs of text gain a reading shown as
furigana, a colour, underline and strikethrough. A line break is a "\n"
in the text. Still simple blocks turned into rich text here, so a change
of the editor's own format never breaks a file written before. A table's
columns are as wide as their longest line, narrowed alike to fit a
phone, where the cells wrap.
A studyset's Import › Learning paths now takes several files at once,
one path each, stored in the order of their names, so a chapter's
lessons come in with one pick. Every file is read before anything is
stored: one that can't be read is named, and nothing comes in.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MMm7usAo6q9ZZRoPkWjoxi
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-11 19:31:39 +02:00
/// No description provided for @pathsImported.
///
/// In en, this message translates to:
/// **'{steps, plural, =0{Imported {count} paths without steps.} =1{Imported {count} paths with 1 step.} other{Imported {count} paths with {steps} steps.}}'**
String pathsImported ( int count , int steps ) ;
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
/// No description provided for @pathImportUnreadable.
///
/// In en, this message translates to:
/// **'It isn\'t text in UTF-8.'**
String get pathImportUnreadable ;
/// No description provided for @pathsEmpty.
///
/// In en, this message translates to:
/// **'No paths yet'**
String get pathsEmpty ;
/// No description provided for @pathsEmptyHint.
///
/// In en, this message translates to:
feat(app): every empty studyset tab says how to add some, as Files did
Paths, flashcards and notes now show what Files showed when empty: an
icon for what the tab holds, "No … yet", and the menus that add some
("Add material → Create a path", "⋮ → Import content", …), through one
shared widget, EmptyListMessage. The icons are those the app uses for
each already: the route, the study screen's card stack, the note pen,
the folder.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MMm7usAo6q9ZZRoPkWjoxi
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-08 20:24:22 +02:00
/// **'A path leads through a lesson, step by step. Use \"Add material → Create a path\" to write one, or \"⋮ → Import content\" to import one.'**
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
String get pathsEmptyHint ;
/// No description provided for @pathOptions.
///
/// In en, this message translates to:
/// **'Path options'**
String get pathOptions ;
/// No description provided for @pathEdit.
///
/// In en, this message translates to:
/// **'Edit'**
String get pathEdit ;
/// No description provided for @pathResetProgress.
///
/// In en, this message translates to:
/// **'Reset progress'**
String get pathResetProgress ;
/// No description provided for @pathProgressReset.
///
/// In en, this message translates to:
/// **'Progress reset'**
String get pathProgressReset ;
2026-10-08 11:52:14 +02:00
/// No description provided for @pathResetTitle.
///
/// In en, this message translates to:
/// **'Reset progress?'**
String get pathResetTitle ;
/// No description provided for @pathResetMessage.
///
/// In en, this message translates to:
/// **'All your progress through this path will be cleared. You can undo this for a few seconds.'**
String get pathResetMessage ;
/// No description provided for @pathResetConfirm.
///
/// In en, this message translates to:
/// **'Reset'**
String get pathResetConfirm ;
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
/// No description provided for @pathDelete.
///
/// In en, this message translates to:
/// **'Delete'**
String get pathDelete ;
/// No description provided for @pathDeleteTitle.
///
/// In en, this message translates to:
/// **'Delete path?'**
String get pathDeleteTitle ;
/// No description provided for @pathDeleteMessage.
///
/// In en, this message translates to:
/// **'This path and its steps will be removed from the studyset. You can undo this for a few seconds.'**
String get pathDeleteMessage ;
/// No description provided for @pathDeleteConfirm.
///
/// In en, this message translates to:
/// **'Delete'**
String get pathDeleteConfirm ;
/// No description provided for @pathDeleted.
///
/// In en, this message translates to:
/// **'Path deleted'**
String get pathDeleted ;
/// No description provided for @pathCreateTitle.
///
/// In en, this message translates to:
/// **'New path'**
String get pathCreateTitle ;
/// No description provided for @pathCreate.
///
/// In en, this message translates to:
/// **'Create'**
String get pathCreate ;
/// No description provided for @pathTitleLabel.
///
/// In en, this message translates to:
/// **'Title'**
String get pathTitleLabel ;
/// No description provided for @pathSecondaryTitleLabel.
///
/// In en, this message translates to:
/// **'Secondary title'**
String get pathSecondaryTitleLabel ;
/// No description provided for @pathSecondaryTitleHint.
///
/// In en, this message translates to:
/// **'E.g. the title in the language you learn'**
String get pathSecondaryTitleHint ;
/// No description provided for @pathEditTitle.
///
/// In en, this message translates to:
/// **'Edit path'**
String get pathEditTitle ;
/// No description provided for @pathDescription.
///
/// In en, this message translates to:
/// **'Description'**
String get pathDescription ;
/// No description provided for @pathAddDescription.
///
/// In en, this message translates to:
/// **'Add a description'**
String get pathAddDescription ;
/// No description provided for @pathDescriptionEditTitle.
///
/// In en, this message translates to:
/// **'Path description'**
String get pathDescriptionEditTitle ;
/// No description provided for @pathAddTags.
///
/// In en, this message translates to:
/// **'Add tags'**
String get pathAddTags ;
/// No description provided for @pathTags.
///
/// In en, this message translates to:
/// **'Tags'**
String get pathTags ;
2026-10-08 14:33:32 +02:00
/// No description provided for @pathEditTags.
///
/// In en, this message translates to:
/// **'Edit tags'**
String get pathEditTags ;
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
/// No description provided for @pathSteps.
///
/// In en, this message translates to:
/// **'Steps'**
String get pathSteps ;
/// No description provided for @pathStepsEmpty.
///
/// In en, this message translates to:
/// **'No steps yet'**
String get pathStepsEmpty ;
/// No description provided for @pathStepsEmptyHint.
///
/// In en, this message translates to:
/// **'Pick a step type to start writing the lesson.'**
String get pathStepsEmptyHint ;
/// No description provided for @pathAddStep.
///
/// In en, this message translates to:
/// **'Add step'**
String get pathAddStep ;
/// No description provided for @pathReorder.
///
/// In en, this message translates to:
/// **'Reorder'**
String get pathReorder ;
/// No description provided for @pathReorderDone.
///
/// In en, this message translates to:
/// **'Done'**
String get pathReorderDone ;
/// No description provided for @pathStepDelete.
///
/// In en, this message translates to:
/// **'Delete step'**
String get pathStepDelete ;
/// No description provided for @pathStepDeleted.
///
/// In en, this message translates to:
/// **'Step deleted'**
String get pathStepDeleted ;
/// No description provided for @stepUntitled.
///
/// In en, this message translates to:
/// **'Untitled step'**
String get stepUntitled ;
/// No description provided for @stepKindInfo.
///
/// In en, this message translates to:
/// **'Info'**
String get stepKindInfo ;
/// No description provided for @stepKindSingleChoice.
///
/// In en, this message translates to:
/// **'Single choice'**
String get stepKindSingleChoice ;
/// No description provided for @stepKindMultipleChoice.
///
/// In en, this message translates to:
/// **'Multiple choice'**
String get stepKindMultipleChoice ;
/// No description provided for @stepKindDragAndDrop.
///
/// In en, this message translates to:
/// **'Drag and drop'**
String get stepKindDragAndDrop ;
/// No description provided for @stepKindFillInTheBlank.
///
/// In en, this message translates to:
/// **'Fill in the blank'**
String get stepKindFillInTheBlank ;
/// No description provided for @stepEditTitle.
///
/// In en, this message translates to:
/// **'Edit step'**
String get stepEditTitle ;
/// No description provided for @stepTitleHint.
///
/// In en, this message translates to:
/// **'Title'**
String get stepTitleHint ;
/// No description provided for @stepOptions.
///
/// In en, this message translates to:
/// **'Options'**
String get stepOptions ;
/// No description provided for @stepOptionHint.
///
/// In en, this message translates to:
/// **'Option {number}'**
String stepOptionHint ( int number ) ;
/// No description provided for @stepAddOption.
///
/// In en, this message translates to:
/// **'Add option'**
String get stepAddOption ;
/// No description provided for @stepRemoveOption.
///
/// In en, this message translates to:
/// **'Remove option'**
String get stepRemoveOption ;
/// No description provided for @stepCorrectOption.
///
/// In en, this message translates to:
/// **'The right one'**
String get stepCorrectOption ;
/// No description provided for @stepCorrectOptions.
///
/// In en, this message translates to:
/// **'Right ones'**
String get stepCorrectOptions ;
/// No description provided for @stepCategories.
///
/// In en, this message translates to:
/// **'Categories'**
String get stepCategories ;
/// No description provided for @stepCategoryHint.
///
/// In en, this message translates to:
/// **'Category {number}'**
String stepCategoryHint ( int number ) ;
/// No description provided for @stepAddCategory.
///
/// In en, this message translates to:
/// **'Add category'**
String get stepAddCategory ;
/// No description provided for @stepRemoveCategory.
///
/// In en, this message translates to:
/// **'Remove category'**
String get stepRemoveCategory ;
/// No description provided for @stepItems.
///
/// In en, this message translates to:
/// **'Items'**
String get stepItems ;
/// No description provided for @stepItemHint.
///
/// In en, this message translates to:
/// **'Item {number}'**
String stepItemHint ( int number ) ;
/// No description provided for @stepAddItem.
///
/// In en, this message translates to:
/// **'Add item'**
String get stepAddItem ;
/// No description provided for @stepRemoveItem.
///
/// In en, this message translates to:
/// **'Remove item'**
String get stepRemoveItem ;
/// No description provided for @stepItemCategory.
///
/// In en, this message translates to:
/// **'Belongs to'**
String get stepItemCategory ;
/// No description provided for @stepPassage.
///
/// In en, this message translates to:
/// **'Passage'**
String get stepPassage ;
/// No description provided for @stepPassageHint.
///
/// In en, this message translates to:
/// **'Type ___ (three underscores) wherever a word should be dropped in.'**
String get stepPassageHint ;
/// No description provided for @stepBlankHint.
///
/// In en, this message translates to:
/// **'Word for gap {number}'**
String stepBlankHint ( int number ) ;
/// No description provided for @stepIncomplete.
///
/// In en, this message translates to:
/// **'Not ready to answer yet: fill in every field and choose what is right.'**
String get stepIncomplete ;
/// No description provided for @stepFeedbackRight.
///
/// In en, this message translates to:
/// **'Feedback after a right answer'**
String get stepFeedbackRight ;
/// No description provided for @stepFeedbackWrong.
///
/// In en, this message translates to:
/// **'Feedback after a wrong answer'**
String get stepFeedbackWrong ;
/// No description provided for @stepFeedbackNone.
///
/// In en, this message translates to:
/// **'None'**
String get stepFeedbackNone ;
feat(app): cite sources in APA, MLA or Chicago, and give studysets a bibliography
A source is now shared by learning paths and studysets (core/citation/):
authors as full names, year, title, what it is a part of, publisher,
ISBN, link, and a citation kept as a file brought it. formatCitation
cites it in APA 7, MLA 9 or Chicago 18: it inverts and abbreviates
names itself (particles, suffixes, names written as already cited stay),
sets a work of its own in italics and a part of something apart from
the whole, and writes a missing year as "n.d." in APA and Chicago.
- Settings: a Citations card per device, with a preview and a link to
the style's own guide.
- Studysets: a Bibliography button opens the works cited, to read and,
behind its pencil, to change (studyset schema 4).
- Steps: sources edited through the same sheet, opened from an
"N sources" button in the runner (step schema 2; schema 1 reads on).
- Path import: steps' sources, text kept as written, the old format's
"citation" read as what a source is part of.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-07 17:07:04 +02:00
/// No description provided for @sources.
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
///
/// In en, this message translates to:
/// **'Sources'**
feat(app): cite sources in APA, MLA or Chicago, and give studysets a bibliography
A source is now shared by learning paths and studysets (core/citation/):
authors as full names, year, title, what it is a part of, publisher,
ISBN, link, and a citation kept as a file brought it. formatCitation
cites it in APA 7, MLA 9 or Chicago 18: it inverts and abbreviates
names itself (particles, suffixes, names written as already cited stay),
sets a work of its own in italics and a part of something apart from
the whole, and writes a missing year as "n.d." in APA and Chicago.
- Settings: a Citations card per device, with a preview and a link to
the style's own guide.
- Studysets: a Bibliography button opens the works cited, to read and,
behind its pencil, to change (studyset schema 4).
- Steps: sources edited through the same sheet, opened from an
"N sources" button in the runner (step schema 2; schema 1 reads on).
- Path import: steps' sources, text kept as written, the old format's
"citation" read as what a source is part of.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-07 17:07:04 +02:00
String get sources ;
/// No description provided for @sourcesCount.
///
/// In en, this message translates to:
/// **'{count, plural, =1{1 source} other{{count} sources}}'**
String sourcesCount ( int count ) ;
/// No description provided for @sourcesNone.
///
/// In en, this message translates to:
/// **'No sources cited yet.'**
String get sourcesNone ;
/// No description provided for @sourcesEdit.
///
/// In en, this message translates to:
/// **'Edit sources'**
String get sourcesEdit ;
/// No description provided for @sourcesEditDone.
///
/// In en, this message translates to:
/// **'Done editing'**
String get sourcesEditDone ;
/// No description provided for @sourceAdd.
///
/// In en, this message translates to:
/// **'Add a source'**
String get sourceAdd ;
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
feat(app): cite sources in APA, MLA or Chicago, and give studysets a bibliography
A source is now shared by learning paths and studysets (core/citation/):
authors as full names, year, title, what it is a part of, publisher,
ISBN, link, and a citation kept as a file brought it. formatCitation
cites it in APA 7, MLA 9 or Chicago 18: it inverts and abbreviates
names itself (particles, suffixes, names written as already cited stay),
sets a work of its own in italics and a part of something apart from
the whole, and writes a missing year as "n.d." in APA and Chicago.
- Settings: a Citations card per device, with a preview and a link to
the style's own guide.
- Studysets: a Bibliography button opens the works cited, to read and,
behind its pencil, to change (studyset schema 4).
- Steps: sources edited through the same sheet, opened from an
"N sources" button in the runner (step schema 2; schema 1 reads on).
- Path import: steps' sources, text kept as written, the old format's
"citation" read as what a source is part of.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-07 17:07:04 +02:00
/// No description provided for @sourceEdit.
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
///
/// In en, this message translates to:
feat(app): cite sources in APA, MLA or Chicago, and give studysets a bibliography
A source is now shared by learning paths and studysets (core/citation/):
authors as full names, year, title, what it is a part of, publisher,
ISBN, link, and a citation kept as a file brought it. formatCitation
cites it in APA 7, MLA 9 or Chicago 18: it inverts and abbreviates
names itself (particles, suffixes, names written as already cited stay),
sets a work of its own in italics and a part of something apart from
the whole, and writes a missing year as "n.d." in APA and Chicago.
- Settings: a Citations card per device, with a preview and a link to
the style's own guide.
- Studysets: a Bibliography button opens the works cited, to read and,
behind its pencil, to change (studyset schema 4).
- Steps: sources edited through the same sheet, opened from an
"N sources" button in the runner (step schema 2; schema 1 reads on).
- Path import: steps' sources, text kept as written, the old format's
"citation" read as what a source is part of.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-07 17:07:04 +02:00
/// **'Edit source'**
String get sourceEdit ;
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
feat(app): cite sources in APA, MLA or Chicago, and give studysets a bibliography
A source is now shared by learning paths and studysets (core/citation/):
authors as full names, year, title, what it is a part of, publisher,
ISBN, link, and a citation kept as a file brought it. formatCitation
cites it in APA 7, MLA 9 or Chicago 18: it inverts and abbreviates
names itself (particles, suffixes, names written as already cited stay),
sets a work of its own in italics and a part of something apart from
the whole, and writes a missing year as "n.d." in APA and Chicago.
- Settings: a Citations card per device, with a preview and a link to
the style's own guide.
- Studysets: a Bibliography button opens the works cited, to read and,
behind its pencil, to change (studyset schema 4).
- Steps: sources edited through the same sheet, opened from an
"N sources" button in the runner (step schema 2; schema 1 reads on).
- Path import: steps' sources, text kept as written, the old format's
"citation" read as what a source is part of.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-07 17:07:04 +02:00
/// No description provided for @sourceRemove.
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
///
/// In en, this message translates to:
/// **'Remove source'**
feat(app): cite sources in APA, MLA or Chicago, and give studysets a bibliography
A source is now shared by learning paths and studysets (core/citation/):
authors as full names, year, title, what it is a part of, publisher,
ISBN, link, and a citation kept as a file brought it. formatCitation
cites it in APA 7, MLA 9 or Chicago 18: it inverts and abbreviates
names itself (particles, suffixes, names written as already cited stay),
sets a work of its own in italics and a part of something apart from
the whole, and writes a missing year as "n.d." in APA and Chicago.
- Settings: a Citations card per device, with a preview and a link to
the style's own guide.
- Studysets: a Bibliography button opens the works cited, to read and,
behind its pencil, to change (studyset schema 4).
- Steps: sources edited through the same sheet, opened from an
"N sources" button in the runner (step schema 2; schema 1 reads on).
- Path import: steps' sources, text kept as written, the old format's
"citation" read as what a source is part of.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-07 17:07:04 +02:00
String get sourceRemove ;
/// No description provided for @sourceRemoved.
///
/// In en, this message translates to:
/// **'Source removed'**
String get sourceRemoved ;
/// No description provided for @sourceSheetAdd.
///
/// In en, this message translates to:
/// **'Add'**
String get sourceSheetAdd ;
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
feat(app): cite sources in APA, MLA or Chicago, and give studysets a bibliography
A source is now shared by learning paths and studysets (core/citation/):
authors as full names, year, title, what it is a part of, publisher,
ISBN, link, and a citation kept as a file brought it. formatCitation
cites it in APA 7, MLA 9 or Chicago 18: it inverts and abbreviates
names itself (particles, suffixes, names written as already cited stay),
sets a work of its own in italics and a part of something apart from
the whole, and writes a missing year as "n.d." in APA and Chicago.
- Settings: a Citations card per device, with a preview and a link to
the style's own guide.
- Studysets: a Bibliography button opens the works cited, to read and,
behind its pencil, to change (studyset schema 4).
- Steps: sources edited through the same sheet, opened from an
"N sources" button in the runner (step schema 2; schema 1 reads on).
- Path import: steps' sources, text kept as written, the old format's
"citation" read as what a source is part of.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-07 17:07:04 +02:00
/// No description provided for @sourceAuthors.
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
///
/// In en, this message translates to:
/// **'Authors'**
feat(app): cite sources in APA, MLA or Chicago, and give studysets a bibliography
A source is now shared by learning paths and studysets (core/citation/):
authors as full names, year, title, what it is a part of, publisher,
ISBN, link, and a citation kept as a file brought it. formatCitation
cites it in APA 7, MLA 9 or Chicago 18: it inverts and abbreviates
names itself (particles, suffixes, names written as already cited stay),
sets a work of its own in italics and a part of something apart from
the whole, and writes a missing year as "n.d." in APA and Chicago.
- Settings: a Citations card per device, with a preview and a link to
the style's own guide.
- Studysets: a Bibliography button opens the works cited, to read and,
behind its pencil, to change (studyset schema 4).
- Steps: sources edited through the same sheet, opened from an
"N sources" button in the runner (step schema 2; schema 1 reads on).
- Path import: steps' sources, text kept as written, the old format's
"citation" read as what a source is part of.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-07 17:07:04 +02:00
String get sourceAuthors ;
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
feat(app): cite sources in APA, MLA or Chicago, and give studysets a bibliography
A source is now shared by learning paths and studysets (core/citation/):
authors as full names, year, title, what it is a part of, publisher,
ISBN, link, and a citation kept as a file brought it. formatCitation
cites it in APA 7, MLA 9 or Chicago 18: it inverts and abbreviates
names itself (particles, suffixes, names written as already cited stay),
sets a work of its own in italics and a part of something apart from
the whole, and writes a missing year as "n.d." in APA and Chicago.
- Settings: a Citations card per device, with a preview and a link to
the style's own guide.
- Studysets: a Bibliography button opens the works cited, to read and,
behind its pencil, to change (studyset schema 4).
- Steps: sources edited through the same sheet, opened from an
"N sources" button in the runner (step schema 2; schema 1 reads on).
- Path import: steps' sources, text kept as written, the old format's
"citation" read as what a source is part of.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-07 17:07:04 +02:00
/// No description provided for @sourceAuthorsHint.
///
/// In en, this message translates to:
/// **'Full names, one per line, e.g. Mark A. McDaniel'**
String get sourceAuthorsHint ;
/// No description provided for @sourceYear.
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
///
/// In en, this message translates to:
/// **'Year'**
feat(app): cite sources in APA, MLA or Chicago, and give studysets a bibliography
A source is now shared by learning paths and studysets (core/citation/):
authors as full names, year, title, what it is a part of, publisher,
ISBN, link, and a citation kept as a file brought it. formatCitation
cites it in APA 7, MLA 9 or Chicago 18: it inverts and abbreviates
names itself (particles, suffixes, names written as already cited stay),
sets a work of its own in italics and a part of something apart from
the whole, and writes a missing year as "n.d." in APA and Chicago.
- Settings: a Citations card per device, with a preview and a link to
the style's own guide.
- Studysets: a Bibliography button opens the works cited, to read and,
behind its pencil, to change (studyset schema 4).
- Steps: sources edited through the same sheet, opened from an
"N sources" button in the runner (step schema 2; schema 1 reads on).
- Path import: steps' sources, text kept as written, the old format's
"citation" read as what a source is part of.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-07 17:07:04 +02:00
String get sourceYear ;
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
feat(app): cite sources in APA, MLA or Chicago, and give studysets a bibliography
A source is now shared by learning paths and studysets (core/citation/):
authors as full names, year, title, what it is a part of, publisher,
ISBN, link, and a citation kept as a file brought it. formatCitation
cites it in APA 7, MLA 9 or Chicago 18: it inverts and abbreviates
names itself (particles, suffixes, names written as already cited stay),
sets a work of its own in italics and a part of something apart from
the whole, and writes a missing year as "n.d." in APA and Chicago.
- Settings: a Citations card per device, with a preview and a link to
the style's own guide.
- Studysets: a Bibliography button opens the works cited, to read and,
behind its pencil, to change (studyset schema 4).
- Steps: sources edited through the same sheet, opened from an
"N sources" button in the runner (step schema 2; schema 1 reads on).
- Path import: steps' sources, text kept as written, the old format's
"citation" read as what a source is part of.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-07 17:07:04 +02:00
/// No description provided for @sourceYearHint.
///
/// In en, this message translates to:
/// **'e.g. 2014'**
String get sourceYearHint ;
/// No description provided for @sourceTitle.
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
///
/// In en, this message translates to:
/// **'Title'**
feat(app): cite sources in APA, MLA or Chicago, and give studysets a bibliography
A source is now shared by learning paths and studysets (core/citation/):
authors as full names, year, title, what it is a part of, publisher,
ISBN, link, and a citation kept as a file brought it. formatCitation
cites it in APA 7, MLA 9 or Chicago 18: it inverts and abbreviates
names itself (particles, suffixes, names written as already cited stay),
sets a work of its own in italics and a part of something apart from
the whole, and writes a missing year as "n.d." in APA and Chicago.
- Settings: a Citations card per device, with a preview and a link to
the style's own guide.
- Studysets: a Bibliography button opens the works cited, to read and,
behind its pencil, to change (studyset schema 4).
- Steps: sources edited through the same sheet, opened from an
"N sources" button in the runner (step schema 2; schema 1 reads on).
- Path import: steps' sources, text kept as written, the old format's
"citation" read as what a source is part of.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-07 17:07:04 +02:00
String get sourceTitle ;
/// No description provided for @sourceTitleHint.
///
/// In en, this message translates to:
/// **'Title of the work'**
String get sourceTitleHint ;
/// No description provided for @sourceContainer.
///
/// In en, this message translates to:
/// **'Published in'**
String get sourceContainer ;
/// No description provided for @sourceContainerHint.
///
/// In en, this message translates to:
/// **'Journal, book or website it is part of, with pages'**
String get sourceContainerHint ;
/// No description provided for @sourceContainerHelper.
///
/// In en, this message translates to:
/// **'Leave empty for a work of its own, such as a book.'**
String get sourceContainerHelper ;
/// No description provided for @sourcePublisher.
///
/// In en, this message translates to:
/// **'Publisher'**
String get sourcePublisher ;
/// No description provided for @sourcePublisherHint.
///
/// In en, this message translates to:
/// **'e.g. Belknap Press'**
String get sourcePublisherHint ;
/// No description provided for @sourceIsbn.
///
/// In en, this message translates to:
/// **'ISBN'**
String get sourceIsbn ;
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
feat(app): cite sources in APA, MLA or Chicago, and give studysets a bibliography
A source is now shared by learning paths and studysets (core/citation/):
authors as full names, year, title, what it is a part of, publisher,
ISBN, link, and a citation kept as a file brought it. formatCitation
cites it in APA 7, MLA 9 or Chicago 18: it inverts and abbreviates
names itself (particles, suffixes, names written as already cited stay),
sets a work of its own in italics and a part of something apart from
the whole, and writes a missing year as "n.d." in APA and Chicago.
- Settings: a Citations card per device, with a preview and a link to
the style's own guide.
- Studysets: a Bibliography button opens the works cited, to read and,
behind its pencil, to change (studyset schema 4).
- Steps: sources edited through the same sheet, opened from an
"N sources" button in the runner (step schema 2; schema 1 reads on).
- Path import: steps' sources, text kept as written, the old format's
"citation" read as what a source is part of.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-07 17:07:04 +02:00
/// No description provided for @sourceIsbnHint.
///
/// In en, this message translates to:
/// **'e.g. 978-0-674-72901-8'**
String get sourceIsbnHint ;
/// No description provided for @sourceUrl.
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
///
/// In en, this message translates to:
/// **'Link'**
feat(app): cite sources in APA, MLA or Chicago, and give studysets a bibliography
A source is now shared by learning paths and studysets (core/citation/):
authors as full names, year, title, what it is a part of, publisher,
ISBN, link, and a citation kept as a file brought it. formatCitation
cites it in APA 7, MLA 9 or Chicago 18: it inverts and abbreviates
names itself (particles, suffixes, names written as already cited stay),
sets a work of its own in italics and a part of something apart from
the whole, and writes a missing year as "n.d." in APA and Chicago.
- Settings: a Citations card per device, with a preview and a link to
the style's own guide.
- Studysets: a Bibliography button opens the works cited, to read and,
behind its pencil, to change (studyset schema 4).
- Steps: sources edited through the same sheet, opened from an
"N sources" button in the runner (step schema 2; schema 1 reads on).
- Path import: steps' sources, text kept as written, the old format's
"citation" read as what a source is part of.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-07 17:07:04 +02:00
String get sourceUrl ;
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
feat(app): cite sources in APA, MLA or Chicago, and give studysets a bibliography
A source is now shared by learning paths and studysets (core/citation/):
authors as full names, year, title, what it is a part of, publisher,
ISBN, link, and a citation kept as a file brought it. formatCitation
cites it in APA 7, MLA 9 or Chicago 18: it inverts and abbreviates
names itself (particles, suffixes, names written as already cited stay),
sets a work of its own in italics and a part of something apart from
the whole, and writes a missing year as "n.d." in APA and Chicago.
- Settings: a Citations card per device, with a preview and a link to
the style's own guide.
- Studysets: a Bibliography button opens the works cited, to read and,
behind its pencil, to change (studyset schema 4).
- Steps: sources edited through the same sheet, opened from an
"N sources" button in the runner (step schema 2; schema 1 reads on).
- Path import: steps' sources, text kept as written, the old format's
"citation" read as what a source is part of.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-07 17:07:04 +02:00
/// No description provided for @sourceUrlHint.
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
///
/// In en, this message translates to:
feat(app): cite sources in APA, MLA or Chicago, and give studysets a bibliography
A source is now shared by learning paths and studysets (core/citation/):
authors as full names, year, title, what it is a part of, publisher,
ISBN, link, and a citation kept as a file brought it. formatCitation
cites it in APA 7, MLA 9 or Chicago 18: it inverts and abbreviates
names itself (particles, suffixes, names written as already cited stay),
sets a work of its own in italics and a part of something apart from
the whole, and writes a missing year as "n.d." in APA and Chicago.
- Settings: a Citations card per device, with a preview and a link to
the style's own guide.
- Studysets: a Bibliography button opens the works cited, to read and,
behind its pencil, to change (studyset schema 4).
- Steps: sources edited through the same sheet, opened from an
"N sources" button in the runner (step schema 2; schema 1 reads on).
- Path import: steps' sources, text kept as written, the old format's
"citation" read as what a source is part of.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-07 17:07:04 +02:00
/// **'https://…'**
String get sourceUrlHint ;
/// No description provided for @sourceImportedAs.
///
/// In en, this message translates to:
/// **'Imported as: {citation}'**
String sourceImportedAs ( String citation ) ;
/// No description provided for @sourceIsbnLine.
///
/// In en, this message translates to:
/// **'ISBN: {isbn}'**
String sourceIsbnLine ( String isbn ) ;
/// No description provided for @sourceAvailableAt.
///
/// In en, this message translates to:
/// **'Available at:'**
String get sourceAvailableAt ;
/// No description provided for @sourceEmpty.
///
/// In en, this message translates to:
/// **'Empty reference'**
String get sourceEmpty ;
/// No description provided for @sourceDiscardTitle.
///
/// In en, this message translates to:
/// **'Discard changes?'**
String get sourceDiscardTitle ;
/// No description provided for @sourceDiscardMessage.
///
/// In en, this message translates to:
/// **'What you typed for this source will be lost.'**
String get sourceDiscardMessage ;
/// No description provided for @sourceDiscardConfirm.
///
/// In en, this message translates to:
/// **'Discard'**
String get sourceDiscardConfirm ;
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
feat(citation): sources are books and articles, websites or YouTube videos
A source was a book's or an article's parts, however a website or a
video had to be squeezed into them. Adding one to a bibliography or a
step now asks what it is, and its form asks for what cites it: a book
or an article its parts as before; a website the page's link, title,
the site's name, authors and year; a YouTube video its link (checked to
be one), title, channel and year. Websites and videos need their link,
marked as required. A video is cited as APA cites one ("[Video]", a
part of YouTube) and plays in the app where it can, else on YouTube.
The kind is kept in the source's JSON, left out for a book or an
article; a kind this app doesn't know reads as a work. The studyset's
payload goes to schema 6 and a step's to schema 4 for it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MMm7usAo6q9ZZRoPkWjoxi
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-12 23:55:39 +02:00
/// No description provided for @sourceKind.
///
/// In en, this message translates to:
/// **'Kind of source'**
String get sourceKind ;
/// No description provided for @sourceKindWork.
///
/// In en, this message translates to:
/// **'Book or article'**
String get sourceKindWork ;
/// No description provided for @sourceKindWebsite.
///
/// In en, this message translates to:
/// **'Website'**
String get sourceKindWebsite ;
/// No description provided for @sourceKindVideo.
///
/// In en, this message translates to:
/// **'YouTube video'**
String get sourceKindVideo ;
/// No description provided for @sourceWebsiteName.
///
/// In en, this message translates to:
/// **'Website'**
String get sourceWebsiteName ;
/// No description provided for @sourceWebsiteNameHint.
///
/// In en, this message translates to:
/// **'Its name, e.g. Wikipedia'**
String get sourceWebsiteNameHint ;
/// No description provided for @sourcePageTitleHint.
///
/// In en, this message translates to:
/// **'Title of the page'**
String get sourcePageTitleHint ;
/// No description provided for @sourceChannel.
///
/// In en, this message translates to:
/// **'Channel'**
String get sourceChannel ;
/// No description provided for @sourceChannelHint.
///
/// In en, this message translates to:
/// **'Who published it on YouTube'**
String get sourceChannelHint ;
/// No description provided for @sourceVideoTitleHint.
///
/// In en, this message translates to:
/// **'Title of the video'**
String get sourceVideoTitleHint ;
/// No description provided for @sourceVideoLink.
///
/// In en, this message translates to:
/// **'Link to the video'**
String get sourceVideoLink ;
/// No description provided for @sourceVideoLinkInvalid.
///
/// In en, this message translates to:
/// **'Not a link to a YouTube video'**
String get sourceVideoLinkInvalid ;
/// No description provided for @sourcePlay.
///
/// In en, this message translates to:
/// **'Play video'**
String get sourcePlay ;
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
/// No description provided for @stepConflict.
///
/// In en, this message translates to:
/// **'This step was changed differently on another device. Keep one version to go on writing.'**
String get stepConflict ;
/// No description provided for @stepFieldTitle.
///
/// In en, this message translates to:
/// **'Title'**
String get stepFieldTitle ;
/// No description provided for @stepFieldBody.
///
/// In en, this message translates to:
/// **'Text'**
String get stepFieldBody ;
/// No description provided for @stepFieldExercise.
///
/// In en, this message translates to:
/// **'Exercise'**
String get stepFieldExercise ;
/// No description provided for @stepFieldRightFeedback.
///
/// In en, this message translates to:
/// **'Feedback after a right answer'**
String get stepFieldRightFeedback ;
/// No description provided for @stepFieldWrongFeedback.
///
/// In en, this message translates to:
/// **'Feedback after a wrong answer'**
String get stepFieldWrongFeedback ;
/// No description provided for @stepFieldSources.
///
/// In en, this message translates to:
/// **'Sources'**
String get stepFieldSources ;
/// No description provided for @runnerClose.
///
/// In en, this message translates to:
/// **'Close'**
String get runnerClose ;
/// No description provided for @runnerEditStep.
///
/// In en, this message translates to:
/// **'Edit step'**
String get runnerEditStep ;
/// No description provided for @runnerBack.
///
/// In en, this message translates to:
/// **'Back'**
String get runnerBack ;
/// No description provided for @runnerCheck.
///
/// In en, this message translates to:
/// **'Check answers'**
String get runnerCheck ;
/// No description provided for @runnerContinue.
///
/// In en, this message translates to:
/// **'Continue'**
String get runnerContinue ;
/// No description provided for @runnerFinish.
///
/// In en, this message translates to:
/// **'Finish'**
String get runnerFinish ;
/// No description provided for @runnerRight.
///
/// In en, this message translates to:
/// **'Correct!'**
String get runnerRight ;
/// No description provided for @runnerWrong.
///
/// In en, this message translates to:
/// **'Not quite'**
String get runnerWrong ;
/// No description provided for @runnerAlreadyRight.
///
/// In en, this message translates to:
/// **'You already answered this correctly.'**
String get runnerAlreadyRight ;
/// No description provided for @runnerSelectAll.
///
/// In en, this message translates to:
/// **'Select all that apply.'**
String get runnerSelectAll ;
/// No description provided for @runnerDragHint.
///
/// In en, this message translates to:
/// **'Drag each item into the category it belongs to, or tap it and then the category. Tap a placed item to take it back.'**
String get runnerDragHint ;
/// No description provided for @runnerBlankHint.
///
/// In en, this message translates to:
/// **'Drag a word into each gap, or tap it and then the gap. Tap a placed word to take it back.'**
String get runnerBlankHint ;
/// No description provided for @runnerEmpty.
///
/// In en, this message translates to:
/// **'This path has no steps yet.'**
String get runnerEmpty ;
/// No description provided for @runnerFinished.
///
/// In en, this message translates to:
/// **'You finished this path'**
String get runnerFinished ;
/// No description provided for @runnerFinishedSteps.
///
/// In en, this message translates to:
/// **'{count, plural, =1{1 step complete.} other{{count} steps complete.}}'**
String runnerFinishedSteps ( int count ) ;
/// No description provided for @runnerNextPath.
///
/// In en, this message translates to:
/// **'Continue to next path'**
String get runnerNextPath ;
/// No description provided for @runnerBackToStudyset.
///
/// In en, this message translates to:
/// **'Back to studyset'**
String get runnerBackToStudyset ;
/// No description provided for @studysetCountPaths.
///
/// In en, this message translates to:
/// **'{count, plural, =1{1 path} other{{count} paths}}'**
String studysetCountPaths ( int count ) ;
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
/// No description provided for @studysetCreateNote.
///
/// In en, this message translates to:
/// **'Create a note'**
String get studysetCreateNote ;
/// No description provided for @notesEmpty.
///
/// In en, this message translates to:
/// **'No notes yet'**
String get notesEmpty ;
/// No description provided for @notesEmptyHint.
///
/// In en, this message translates to:
feat(app): every empty studyset tab says how to add some, as Files did
Paths, flashcards and notes now show what Files showed when empty: an
icon for what the tab holds, "No … yet", and the menus that add some
("Add material → Create a path", "⋮ → Import content", …), through one
shared widget, EmptyListMessage. The icons are those the app uses for
each already: the route, the study screen's card stack, the note pen,
the folder.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MMm7usAo6q9ZZRoPkWjoxi
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-08 20:24:22 +02:00
/// **'Use \"Add material → Create a note\" to write one.'**
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
String get notesEmptyHint ;
/// No description provided for @noteUntitled.
///
/// In en, this message translates to:
/// **'Untitled note'**
String get noteUntitled ;
/// No description provided for @noteEdited.
///
/// In en, this message translates to:
/// **'Edited {when}'**
String noteEdited ( String when ) ;
/// No description provided for @timeJustNow.
///
/// In en, this message translates to:
/// **'just now'**
String get timeJustNow ;
/// No description provided for @timeMinutesAgo.
///
/// In en, this message translates to:
/// **'{count, plural, =1{1 minute ago} other{{count} minutes ago}}'**
String timeMinutesAgo ( int count ) ;
/// No description provided for @timeHoursAgo.
///
/// In en, this message translates to:
/// **'{count, plural, =1{1 hour ago} other{{count} hours ago}}'**
String timeHoursAgo ( int count ) ;
/// No description provided for @timeDaysAgo.
///
/// In en, this message translates to:
/// **'{count, plural, =1{yesterday} other{{count} days ago}}'**
String timeDaysAgo ( int count ) ;
/// No description provided for @noteOptions.
///
/// In en, this message translates to:
/// **'Note options'**
String get noteOptions ;
/// No description provided for @noteEdit.
///
/// In en, this message translates to:
/// **'Edit'**
String get noteEdit ;
/// No description provided for @noteDelete.
///
/// In en, this message translates to:
/// **'Delete'**
String get noteDelete ;
/// No description provided for @noteDeleteTitle.
///
/// In en, this message translates to:
/// **'Delete note?'**
String get noteDeleteTitle ;
/// No description provided for @noteDeleteMessage.
///
/// In en, this message translates to:
/// **'This note will be removed from the studyset. You can undo this for a few seconds.'**
String get noteDeleteMessage ;
/// No description provided for @noteDeleteConfirm.
///
/// In en, this message translates to:
/// **'Delete'**
String get noteDeleteConfirm ;
/// No description provided for @noteDeleted.
///
/// In en, this message translates to:
/// **'Note deleted'**
String get noteDeleted ;
/// No description provided for @noteTitleHint.
///
/// In en, this message translates to:
/// **'Title'**
String get noteTitleHint ;
/// No description provided for @noteEditing.
///
/// In en, this message translates to:
/// **'Editing…'**
String get noteEditing ;
/// No description provided for @noteSaving.
///
/// In en, this message translates to:
/// **'Saving…'**
String get noteSaving ;
/// No description provided for @noteSaved.
///
/// In en, this message translates to:
/// **'Saved'**
String get noteSaved ;
/// No description provided for @noteSaveFailed.
///
/// In en, this message translates to:
/// **'Save failed'**
String get noteSaveFailed ;
/// No description provided for @noteConflict.
///
/// In en, this message translates to:
/// **'This note was changed differently on another device. Keep one version to go on writing.'**
String get noteConflict ;
/// No description provided for @noteFieldTitle.
///
/// In en, this message translates to:
/// **'Title'**
String get noteFieldTitle ;
/// No description provided for @noteFieldBody.
///
/// In en, this message translates to:
/// **'Text'**
String get noteFieldBody ;
/// No description provided for @noteKeepThis.
///
/// In en, this message translates to:
/// **'Keep this'**
String get noteKeepThis ;
/// No description provided for @noteAddTags.
///
/// In en, this message translates to:
/// **'Add tags'**
String get noteAddTags ;
2026-10-08 14:30:30 +02:00
/// No description provided for @noteEditTags.
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
///
/// In en, this message translates to:
2026-10-08 14:30:30 +02:00
/// **'Edit tags'**
String get noteEditTags ;
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
feat(app): the studyset screen with its topics, the tree's own screen
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 12:59:20 +02:00
/// No description provided for @studysetFlashcardsEmpty.
///
/// In en, this message translates to:
/// **'No flashcards yet'**
String get studysetFlashcardsEmpty ;
/// No description provided for @studysetFlashcardsEmptyHint.
///
/// In en, this message translates to:
feat(app): every empty studyset tab says how to add some, as Files did
Paths, flashcards and notes now show what Files showed when empty: an
icon for what the tab holds, "No … yet", and the menus that add some
("Add material → Create a path", "⋮ → Import content", …), through one
shared widget, EmptyListMessage. The icons are those the app uses for
each already: the route, the study screen's card stack, the note pen,
the folder.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MMm7usAo6q9ZZRoPkWjoxi
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-08 20:24:22 +02:00
/// **'Use \"Add material → Create flashcards\" to write some, or \"⋮ → Import content\" to bring in an Anki deck or a list.'**
feat(app): the studyset screen with its topics, the tree's own screen
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 12:59:20 +02:00
String get studysetFlashcardsEmptyHint ;
/// No description provided for @studysetAddMaterial.
///
/// In en, this message translates to:
/// **'Add material'**
String get studysetAddMaterial ;
2026-10-14 13:37:55 +02:00
/// No description provided for @studysetPathsInTopics.
///
/// In en, this message translates to:
/// **'The paths are in its topics'**
String get studysetPathsInTopics ;
/// No description provided for @studysetPathsInTopicsHint.
///
/// In en, this message translates to:
/// **'Or write one here: \"Add material → Create a path\".'**
String get studysetPathsInTopicsHint ;
/// No description provided for @studysetFlashcardsInTopics.
///
/// In en, this message translates to:
/// **'The flashcards are in its topics'**
String get studysetFlashcardsInTopics ;
/// No description provided for @studysetFlashcardsInTopicsHint.
///
/// In en, this message translates to:
/// **'Or add some here: \"Add material → Create flashcards\".'**
String get studysetFlashcardsInTopicsHint ;
/// No description provided for @studysetNotesInTopics.
///
/// In en, this message translates to:
/// **'The notes are in its topics'**
String get studysetNotesInTopics ;
/// No description provided for @studysetNotesInTopicsHint.
///
/// In en, this message translates to:
/// **'Or write one here: \"Add material → Create a note\".'**
String get studysetNotesInTopicsHint ;
/// No description provided for @studysetFilesInTopics.
///
/// In en, this message translates to:
/// **'The files are in its topics'**
String get studysetFilesInTopics ;
/// No description provided for @studysetFilesInTopicsHint.
///
/// In en, this message translates to:
/// **'Or add one here: \"Add material → Upload a PDF\".'**
String get studysetFilesInTopicsHint ;
/// Opens the card browser on every flashcard of a studyset's topics.
///
/// In en, this message translates to:
/// **'{count, plural, =1{Browse it} other{Browse all {count}}}'**
String studysetBrowseAllFlashcards ( int count ) ;
feat(app): the studyset screen with its topics, the tree's own screen
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 12:59:20 +02:00
/// No description provided for @studysetCreateFlashcards.
///
/// In en, this message translates to:
/// **'Create flashcards'**
String get studysetCreateFlashcards ;
/// No description provided for @studysetStudy.
///
/// In en, this message translates to:
/// **'Study'**
String get studysetStudy ;
/// No description provided for @studysetLearnCount.
///
/// In en, this message translates to:
/// **'Learn {count}'**
String studysetLearnCount ( int count ) ;
feat(app): the studyset screen laid out as before
The Study pill reads "Learn 12 flashcards" in the app bar and "Learn 12"
floating, "Study" with nothing due, and is off with nothing to study,
saying so. With a path not finished, a chevron beside it offers to start
or continue the path: a menu in the app bar, a Study mode sheet on
phones. The floating bar rises above the licence line, which takes two
lines at most.
Add material offers a path, flashcards, a note and a PDF, in that order;
imports moved to the options' Import content, which asks for a learning
path or an Anki deck. The options read Edit, Move, Study options, Import
content, Reset progress, Archive, Delete. The bibliography is an outlined
round button above the content, and on phones at the foot of the topics.
The tabs sit on a line of their own across the card, what the tab shown
offers beside them: the flashcards' filter and options, and on the paths
Reorder once there are two, which turns them into handles to drag. An
empty tab says so in its middle; tapping the open tab again returns to
its top, and pulling a tab down syncs. The breadcrumb's steps are plain
links between "›", the studyset itself bold. A long description is cut at
a fixed height, measured, with Read more opening it whole under the
studyset's title. A studyset that no longer exists leads back to the
library.
The topics: All topics on the primary colour, in view also while they
are rearranged; each topic with the study it invites in a round icon,
what it holds ("Empty" with nothing, files counted too), and its options
(now with Reset progress) behind "…" or a long press where the finger
is; New sub-studyset at the foot below a line, the list with its
scrollbar always shown. All topics opens Add material while nothing is
due.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-08 11:42:50 +02:00
/// No description provided for @studysetLearnFlashcards.
///
/// In en, this message translates to:
/// **'{count, plural, =1{Learn 1 flashcard} other{Learn {count} flashcards}}'**
String studysetLearnFlashcards ( int count ) ;
/// No description provided for @studysetNothingToStudy.
///
/// In en, this message translates to:
/// **'Nothing to study yet'**
String get studysetNothingToStudy ;
/// No description provided for @studysetStudyMode.
///
/// In en, this message translates to:
/// **'Study mode'**
String get studysetStudyMode ;
/// No description provided for @studysetStartPath.
///
/// In en, this message translates to:
/// **'Start: {title}'**
String studysetStartPath ( String title ) ;
/// No description provided for @studysetContinuePath.
///
/// In en, this message translates to:
/// **'Continue: {title}'**
String studysetContinuePath ( String title ) ;
/// No description provided for @studysetStartPathHint.
///
/// In en, this message translates to:
/// **'Begin the first step of this path.'**
String get studysetStartPathHint ;
/// No description provided for @studysetContinuePathHint.
///
/// In en, this message translates to:
/// **'Pick up the next path step where you left off.'**
String get studysetContinuePathHint ;
/// No description provided for @studysetSpacedRepetition.
///
/// In en, this message translates to:
/// **'Spaced repetition'**
String get studysetSpacedRepetition ;
/// No description provided for @studysetSpacedRepetitionHint.
///
/// In en, this message translates to:
/// **'Review the cards that are due, scheduled by FSRS.'**
String get studysetSpacedRepetitionHint ;
/// No description provided for @studysetImportContent.
///
/// In en, this message translates to:
/// **'Import content'**
String get studysetImportContent ;
feat(app): export a studyset as Markdown
Export content, in the studyset's options, writes the studyset and
everything inside it as one readable Markdown document (decided
2026-10-08: no re-importable archive before the public tier's release
files): its title, secondary title, description and licence, then its
flashcards (answers below their questions, choices as task lists),
notes, learning paths (steps with their kinds, answers, feedback and
sources), PDFs and bibliography, then each sub-studyset a level deeper.
Rich text keeps its formatting; formulas go between dollars, callouts
become quotes, spoilers HTML details, YouTube videos links. With files
(images, sounds, PDFs) it keeps a .zip of the document and them under
files/; a file this device can't have is named and counted.
Saving goes through a FileSaver: file_selector's dialog on the desktop,
file_picker's (Android's Storage Access Framework) on phones, a direct
dependency now at the version the editor already brings.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MMm7usAo6q9ZZRoPkWjoxi
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-08 15:02:22 +02:00
/// No description provided for @studysetMenuExport.
///
/// In en, this message translates to:
/// **'Export content'**
String get studysetMenuExport ;
/// No description provided for @exportRunning.
///
/// In en, this message translates to:
/// **'Exporting…'**
String get exportRunning ;
/// No description provided for @exportedMarkdown.
///
/// In en, this message translates to:
/// **'Exported as Markdown.'**
String get exportedMarkdown ;
/// No description provided for @exportedZip.
///
/// In en, this message translates to:
/// **'{count, plural, =1{Exported as Markdown with 1 file (.zip).} other{Exported as Markdown with {count} files (.zip).}}'**
String exportedZip ( int count ) ;
/// No description provided for @exportMissingFiles.
///
/// In en, this message translates to:
/// **'{count, plural, =1{1 file wasn\'t on this device and is left out.} other{{count} files weren\'t on this device and are left out.}}'**
String exportMissingFiles ( int count ) ;
/// No description provided for @exportFailed.
///
/// In en, this message translates to:
/// **'Couldn\'t export the studyset.'**
String get exportFailed ;
feat(archive): export a studyset as an archive to import again
Export content now asks what to export as: an archive first, a .zip
the Library's Import takes back as the person's own, for a backup or a
move to another instance; Markdown, to read elsewhere, below it. The
archive holds the studyset with everything inside it but what waits in
Recently deleted, laid out as the instance writes a release, unlinked
from Explore; dictionary cards go whole, with the person's edits. Every
file its content names is downloaded first, and an archive missing one
is not kept: the person is told how many and to try again online.
With my progress (ticked at first) adds the reviews, the answers
to learning paths' steps, the pages read in PDFs, and what a release
leaves out as only its author's: the exam prepared for, a card's report,
a PDF's reading deadline. Stars, archived studysets and the tags'
colours come along either way.
The import of v0.17.0 refuses a file it doesn't know, so what it doesn't
read goes into the files it reads, as keys it skips, and the format
stays 1: archive.json's tags, a studyset.json's archived and starred, a
step's answers, a PDF's readings. This import reads them; an archive it
can't read all of still writes nothing, its tags included.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-15 18:57:16 +02:00
/// No description provided for @exportTitle.
///
/// In en, this message translates to:
/// **'Export'**
String get exportTitle ;
/// No description provided for @exportArchive.
///
/// In en, this message translates to:
/// **'Archive'**
String get exportArchive ;
/// No description provided for @exportArchiveSubtitle.
///
/// In en, this message translates to:
/// **'A .zip to import again: a backup, or a move to another instance.'**
String get exportArchiveSubtitle ;
/// No description provided for @exportMarkdown.
///
/// In en, this message translates to:
/// **'Markdown'**
String get exportMarkdown ;
/// No description provided for @exportMarkdownSubtitle.
///
/// In en, this message translates to:
/// **'To read elsewhere. It can\'t be imported again.'**
String get exportMarkdownSubtitle ;
/// No description provided for @exportWithProgress.
///
/// In en, this message translates to:
/// **'With my progress'**
String get exportWithProgress ;
/// No description provided for @exportWithProgressSubtitle.
///
/// In en, this message translates to:
/// **'Reviews, answers, pages read, the exam you\'re preparing for, your reports and reading deadlines'**
String get exportWithProgressSubtitle ;
/// No description provided for @exportConfirm.
///
/// In en, this message translates to:
/// **'Export'**
String get exportConfirm ;
/// No description provided for @exportedArchive.
///
/// In en, this message translates to:
/// **'Exported as an archive (.zip).'**
String get exportedArchive ;
/// No description provided for @exportArchiveFilesMissing.
///
/// In en, this message translates to:
/// **'{count, plural, =1{1 file isn\'t on this device and couldn\'t be downloaded.} other{{count} files aren\'t on this device and couldn\'t be downloaded.}} Connect to the internet and try again.'**
String exportArchiveFilesMissing ( int count ) ;
feat(app): export a studyset as Markdown
Export content, in the studyset's options, writes the studyset and
everything inside it as one readable Markdown document (decided
2026-10-08: no re-importable archive before the public tier's release
files): its title, secondary title, description and licence, then its
flashcards (answers below their questions, choices as task lists),
notes, learning paths (steps with their kinds, answers, feedback and
sources), PDFs and bibliography, then each sub-studyset a level deeper.
Rich text keeps its formatting; formulas go between dollars, callouts
become quotes, spoilers HTML details, YouTube videos links. With files
(images, sounds, PDFs) it keeps a .zip of the document and them under
files/; a file this device can't have is named and counted.
Saving goes through a FileSaver: file_selector's dialog on the desktop,
file_picker's (Android's Storage Access Framework) on phones, a direct
dependency now at the version the editor already brings.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MMm7usAo6q9ZZRoPkWjoxi
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-08 15:02:22 +02:00
/// No description provided for @exportQuestion.
///
/// In en, this message translates to:
/// **'Q'**
String get exportQuestion ;
/// No description provided for @exportAnswer.
///
/// In en, this message translates to:
/// **'A'**
String get exportAnswer ;
/// No description provided for @exportHint.
///
/// In en, this message translates to:
/// **'Hint'**
String get exportHint ;
/// No description provided for @exportIfRight.
///
/// In en, this message translates to:
/// **'If right'**
String get exportIfRight ;
/// No description provided for @exportIfWrong.
///
/// In en, this message translates to:
/// **'If wrong'**
String get exportIfWrong ;
/// No description provided for @exportSound.
///
/// In en, this message translates to:
/// **'Sound'**
String get exportSound ;
/// No description provided for @exportVideo.
///
/// In en, this message translates to:
/// **'YouTube video'**
String get exportVideo ;
/// No description provided for @exportSpoiler.
///
/// In en, this message translates to:
/// **'Spoiler'**
String get exportSpoiler ;
/// No description provided for @exportUnavailable.
///
/// In en, this message translates to:
/// **'not available on this device'**
String get exportUnavailable ;
feat(app): the studyset screen laid out as before
The Study pill reads "Learn 12 flashcards" in the app bar and "Learn 12"
floating, "Study" with nothing due, and is off with nothing to study,
saying so. With a path not finished, a chevron beside it offers to start
or continue the path: a menu in the app bar, a Study mode sheet on
phones. The floating bar rises above the licence line, which takes two
lines at most.
Add material offers a path, flashcards, a note and a PDF, in that order;
imports moved to the options' Import content, which asks for a learning
path or an Anki deck. The options read Edit, Move, Study options, Import
content, Reset progress, Archive, Delete. The bibliography is an outlined
round button above the content, and on phones at the foot of the topics.
The tabs sit on a line of their own across the card, what the tab shown
offers beside them: the flashcards' filter and options, and on the paths
Reorder once there are two, which turns them into handles to drag. An
empty tab says so in its middle; tapping the open tab again returns to
its top, and pulling a tab down syncs. The breadcrumb's steps are plain
links between "›", the studyset itself bold. A long description is cut at
a fixed height, measured, with Read more opening it whole under the
studyset's title. A studyset that no longer exists leads back to the
library.
The topics: All topics on the primary colour, in view also while they
are rearranged; each topic with the study it invites in a round icon,
what it holds ("Empty" with nothing, files counted too), and its options
(now with Reset progress) behind "…" or a long press where the finger
is; New sub-studyset at the foot below a line, the list with its
scrollbar always shown. All topics opens Add material while nothing is
due.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-08 11:42:50 +02:00
/// No description provided for @studysetImportPath.
///
/// In en, this message translates to:
feat(paths): path files carry tables, callouts and furigana, and several import at once
The import format gains the blocks lessons need: tables (rows of cells),
callouts (a kind, a title, blocks inside, but no table or callout),
spoilers, quotes and dividers; and runs of text gain a reading shown as
furigana, a colour, underline and strikethrough. A line break is a "\n"
in the text. Still simple blocks turned into rich text here, so a change
of the editor's own format never breaks a file written before. A table's
columns are as wide as their longest line, narrowed alike to fit a
phone, where the cells wrap.
A studyset's Import › Learning paths now takes several files at once,
one path each, stored in the order of their names, so a chapter's
lessons come in with one pick. Every file is read before anything is
stored: one that can't be read is named, and nothing comes in.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MMm7usAo6q9ZZRoPkWjoxi
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-11 19:31:39 +02:00
/// **'Learning paths'**
feat(app): the studyset screen laid out as before
The Study pill reads "Learn 12 flashcards" in the app bar and "Learn 12"
floating, "Study" with nothing due, and is off with nothing to study,
saying so. With a path not finished, a chevron beside it offers to start
or continue the path: a menu in the app bar, a Study mode sheet on
phones. The floating bar rises above the licence line, which takes two
lines at most.
Add material offers a path, flashcards, a note and a PDF, in that order;
imports moved to the options' Import content, which asks for a learning
path or an Anki deck. The options read Edit, Move, Study options, Import
content, Reset progress, Archive, Delete. The bibliography is an outlined
round button above the content, and on phones at the foot of the topics.
The tabs sit on a line of their own across the card, what the tab shown
offers beside them: the flashcards' filter and options, and on the paths
Reorder once there are two, which turns them into handles to drag. An
empty tab says so in its middle; tapping the open tab again returns to
its top, and pulling a tab down syncs. The breadcrumb's steps are plain
links between "›", the studyset itself bold. A long description is cut at
a fixed height, measured, with Read more opening it whole under the
studyset's title. A studyset that no longer exists leads back to the
library.
The topics: All topics on the primary colour, in view also while they
are rearranged; each topic with the study it invites in a round icon,
what it holds ("Empty" with nothing, files counted too), and its options
(now with Reset progress) behind "…" or a long press where the finger
is; New sub-studyset at the foot below a line, the list with its
scrollbar always shown. All topics opens Add material while nothing is
due.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-08 11:42:50 +02:00
String get studysetImportPath ;
/// No description provided for @studysetImportPathHint.
///
/// In en, this message translates to:
feat(paths): path files carry tables, callouts and furigana, and several import at once
The import format gains the blocks lessons need: tables (rows of cells),
callouts (a kind, a title, blocks inside, but no table or callout),
spoilers, quotes and dividers; and runs of text gain a reading shown as
furigana, a colour, underline and strikethrough. A line break is a "\n"
in the text. Still simple blocks turned into rich text here, so a change
of the editor's own format never breaks a file written before. A table's
columns are as wide as their longest line, narrowed alike to fit a
phone, where the cells wrap.
A studyset's Import › Learning paths now takes several files at once,
one path each, stored in the order of their names, so a chapter's
lessons come in with one pick. Every file is read before anything is
stored: one that can't be read is named, and nothing comes in.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MMm7usAo6q9ZZRoPkWjoxi
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-11 19:31:39 +02:00
/// **'From Halkyon path JSON files, one path each'**
feat(app): the studyset screen laid out as before
The Study pill reads "Learn 12 flashcards" in the app bar and "Learn 12"
floating, "Study" with nothing due, and is off with nothing to study,
saying so. With a path not finished, a chevron beside it offers to start
or continue the path: a menu in the app bar, a Study mode sheet on
phones. The floating bar rises above the licence line, which takes two
lines at most.
Add material offers a path, flashcards, a note and a PDF, in that order;
imports moved to the options' Import content, which asks for a learning
path or an Anki deck. The options read Edit, Move, Study options, Import
content, Reset progress, Archive, Delete. The bibliography is an outlined
round button above the content, and on phones at the foot of the topics.
The tabs sit on a line of their own across the card, what the tab shown
offers beside them: the flashcards' filter and options, and on the paths
Reorder once there are two, which turns them into handles to drag. An
empty tab says so in its middle; tapping the open tab again returns to
its top, and pulling a tab down syncs. The breadcrumb's steps are plain
links between "›", the studyset itself bold. A long description is cut at
a fixed height, measured, with Read more opening it whole under the
studyset's title. A studyset that no longer exists leads back to the
library.
The topics: All topics on the primary colour, in view also while they
are rearranged; each topic with the study it invites in a round icon,
what it holds ("Empty" with nothing, files counted too), and its options
(now with Reset progress) behind "…" or a long press where the finger
is; New sub-studyset at the foot below a line, the list with its
scrollbar always shown. All topics opens Add material while nothing is
due.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-08 11:42:50 +02:00
String get studysetImportPathHint ;
/// No description provided for @studysetImportAnki.
///
/// In en, this message translates to:
/// **'Anki deck'**
String get studysetImportAnki ;
/// No description provided for @studysetImportAnkiHint.
///
/// In en, this message translates to:
/// **'From an .apkg or .colpkg file'**
String get studysetImportAnkiHint ;
2026-10-08 14:43:49 +02:00
/// No description provided for @studysetImportCards.
///
/// In en, this message translates to:
/// **'Flashcards'**
String get studysetImportCards ;
/// No description provided for @studysetImportCardsHint.
///
/// In en, this message translates to:
/// **'Pasted, or from a CSV or TSV file'**
String get studysetImportCardsHint ;
/// No description provided for @cardTextImportTitle.
///
/// In en, this message translates to:
/// **'Import flashcards'**
String get cardTextImportTitle ;
/// No description provided for @cardTextImportDescription.
///
/// In en, this message translates to:
fix(app): Reorder among older items, export and text import that hold up
- Reorder: rows from before an order existed all share one position, and
one moved among them landed at their end; the run it goes into now
gets positions of its own (placeAmong), for notes, files, paths,
steps and sub-studysets. Rows whose deletion can still be undone count
for nothing, as the list leaves them out, and a dropped row takes its
place at once instead of snapping back until the order is read again.
- Export: two studysets inside each other (moved so on two devices
offline) no longer make it run forever; links to files whose names
hold #, % or brackets work; PDFs, images and sounds go into the zip
without being compressed again; any error says the export failed.
- Text import: a real CSV reader (quoted fields over several lines, a
back over several quoted fields, a stray quote spoiling only its own
line), the separator guessed from the text (semicolons from German
spreadsheets), and files in UTF-16 or Windows-1252 read as meant.
- A tab whose notes, paths or files couldn't be read says so rather than
"No … yet"; an Anki import from the Library that fails leaves no empty
studyset behind.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MMm7usAo6q9ZZRoPkWjoxi
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-09 13:02:32 +02:00
/// **'One card per line: its front, a separator, then its back. Each line is split at its first separator; lines without one are left out. A field in double quotes, as spreadsheets write them, may hold separators and line breaks.'**
2026-10-08 14:43:49 +02:00
String get cardTextImportDescription ;
/// No description provided for @cardTextImportHint.
///
/// In en, this message translates to:
/// **'Paste the cards here'**
String get cardTextImportHint ;
/// No description provided for @cardTextImportLoadFile.
///
/// In en, this message translates to:
/// **'Load a CSV or TSV file'**
String get cardTextImportLoadFile ;
/// No description provided for @cardTextImportFileType.
///
/// In en, this message translates to:
/// **'CSV or TSV files'**
String get cardTextImportFileType ;
/// No description provided for @cardTextImportUnreadable.
///
/// In en, this message translates to:
fix(app): Reorder among older items, export and text import that hold up
- Reorder: rows from before an order existed all share one position, and
one moved among them landed at their end; the run it goes into now
gets positions of its own (placeAmong), for notes, files, paths,
steps and sub-studysets. Rows whose deletion can still be undone count
for nothing, as the list leaves them out, and a dropped row takes its
place at once instead of snapping back until the order is read again.
- Export: two studysets inside each other (moved so on two devices
offline) no longer make it run forever; links to files whose names
hold #, % or brackets work; PDFs, images and sounds go into the zip
without being compressed again; any error says the export failed.
- Text import: a real CSV reader (quoted fields over several lines, a
back over several quoted fields, a stray quote spoiling only its own
line), the separator guessed from the text (semicolons from German
spreadsheets), and files in UTF-16 or Windows-1252 read as meant.
- A tab whose notes, paths or files couldn't be read says so rather than
"No … yet"; an Anki import from the Library that fails leaves no empty
studyset behind.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MMm7usAo6q9ZZRoPkWjoxi
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-09 13:02:32 +02:00
/// **'This file doesn\'t hold text.'**
2026-10-08 14:43:49 +02:00
String get cardTextImportUnreadable ;
/// No description provided for @cardTextImportSeparator.
///
/// In en, this message translates to:
/// **'Separator'**
String get cardTextImportSeparator ;
/// No description provided for @cardTextSeparatorTab.
///
/// In en, this message translates to:
/// **'Tab'**
String get cardTextSeparatorTab ;
/// No description provided for @cardTextSeparatorComma.
///
/// In en, this message translates to:
/// **'Comma'**
String get cardTextSeparatorComma ;
/// No description provided for @cardTextSeparatorSemicolon.
///
/// In en, this message translates to:
/// **'Semicolon'**
String get cardTextSeparatorSemicolon ;
/// No description provided for @cardTextImportNone.
///
/// In en, this message translates to:
/// **'No cards found yet.'**
String get cardTextImportNone ;
/// No description provided for @cardTextImportReady.
///
/// In en, this message translates to:
/// **'{count, plural, =1{1 card ready to import.} other{{count} cards ready to import.}}'**
String cardTextImportReady ( int count ) ;
/// No description provided for @cardTextImportSubmit.
///
/// In en, this message translates to:
/// **'Import'**
String get cardTextImportSubmit ;
/// No description provided for @cardTextImported.
///
/// In en, this message translates to:
/// **'{count, plural, =1{Imported 1 flashcard.} other{Imported {count} flashcards.}}'**
String cardTextImported ( int count ) ;
feat(app): the studyset screen laid out as before
The Study pill reads "Learn 12 flashcards" in the app bar and "Learn 12"
floating, "Study" with nothing due, and is off with nothing to study,
saying so. With a path not finished, a chevron beside it offers to start
or continue the path: a menu in the app bar, a Study mode sheet on
phones. The floating bar rises above the licence line, which takes two
lines at most.
Add material offers a path, flashcards, a note and a PDF, in that order;
imports moved to the options' Import content, which asks for a learning
path or an Anki deck. The options read Edit, Move, Study options, Import
content, Reset progress, Archive, Delete. The bibliography is an outlined
round button above the content, and on phones at the foot of the topics.
The tabs sit on a line of their own across the card, what the tab shown
offers beside them: the flashcards' filter and options, and on the paths
Reorder once there are two, which turns them into handles to drag. An
empty tab says so in its middle; tapping the open tab again returns to
its top, and pulling a tab down syncs. The breadcrumb's steps are plain
links between "›", the studyset itself bold. A long description is cut at
a fixed height, measured, with Read more opening it whole under the
studyset's title. A studyset that no longer exists leads back to the
library.
The topics: All topics on the primary colour, in view also while they
are rearranged; each topic with the study it invites in a round icon,
what it holds ("Empty" with nothing, files counted too), and its options
(now with Reset progress) behind "…" or a long press where the finger
is; New sub-studyset at the foot below a line, the list with its
scrollbar always shown. All topics opens Add material while nothing is
due.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-08 11:42:50 +02:00
/// No description provided for @studysetReorder.
///
/// In en, this message translates to:
/// **'Reorder'**
String get studysetReorder ;
/// No description provided for @studysetReorderPaths.
///
/// In en, this message translates to:
/// **'Reorder paths'**
String get studysetReorderPaths ;
2026-10-08 18:49:36 +02:00
/// No description provided for @studysetReorderNotes.
///
/// In en, this message translates to:
/// **'Reorder notes'**
String get studysetReorderNotes ;
feat: files in an order of one's own, with Reorder beside the tabs
A PDF now has a position among its studyset's files, as notes and paths
have: a fractional index in a column of its own (so moving it on one
device and renaming it on another don't overwrite each other), last
writer wins. The migration adds it to pdf_source on the server and on
each device with 'a0', so PDFs from before stand in the order they were
added (ties sort by id), and new ones go at the end.
The Files tab lists them in that order by default (Custom order, first
in the Sort menu; newest first was the default before). With two files
or more, Reorder beside the tabs shows the custom order whatever they
were sorted by, a grip in place of each file's options and the Sort menu
put away, until Done.
The next release changes synced tables anyway (card.buriedAt, since
v0.2.2), so older apps have to update for that already.
Also: card.position's doc no longer speaks of moving cards.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MMm7usAo6q9ZZRoPkWjoxi
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-08 19:28:05 +02:00
/// No description provided for @studysetReorderFiles.
///
/// In en, this message translates to:
/// **'Reorder files'**
String get studysetReorderFiles ;
feat(app): the studyset screen laid out as before
The Study pill reads "Learn 12 flashcards" in the app bar and "Learn 12"
floating, "Study" with nothing due, and is off with nothing to study,
saying so. With a path not finished, a chevron beside it offers to start
or continue the path: a menu in the app bar, a Study mode sheet on
phones. The floating bar rises above the licence line, which takes two
lines at most.
Add material offers a path, flashcards, a note and a PDF, in that order;
imports moved to the options' Import content, which asks for a learning
path or an Anki deck. The options read Edit, Move, Study options, Import
content, Reset progress, Archive, Delete. The bibliography is an outlined
round button above the content, and on phones at the foot of the topics.
The tabs sit on a line of their own across the card, what the tab shown
offers beside them: the flashcards' filter and options, and on the paths
Reorder once there are two, which turns them into handles to drag. An
empty tab says so in its middle; tapping the open tab again returns to
its top, and pulling a tab down syncs. The breadcrumb's steps are plain
links between "›", the studyset itself bold. A long description is cut at
a fixed height, measured, with Read more opening it whole under the
studyset's title. A studyset that no longer exists leads back to the
library.
The topics: All topics on the primary colour, in view also while they
are rearranged; each topic with the study it invites in a round icon,
what it holds ("Empty" with nothing, files counted too), and its options
(now with Reset progress) behind "…" or a long press where the finger
is; New sub-studyset at the foot below a line, the list with its
scrollbar always shown. All topics opens Add material while nothing is
due.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-08 11:42:50 +02:00
/// No description provided for @studysetReorderDone.
///
/// In en, this message translates to:
/// **'Done'**
String get studysetReorderDone ;
feat(app): the studyset screen with its topics, the tree's own screen
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 12:59:20 +02:00
/// No description provided for @studysetOpenTopics.
///
/// In en, this message translates to:
/// **'Show topics'**
String get studysetOpenTopics ;
feat(studyset): the screens behind a studyset are its path
Deeper studysets, on iOS and Android alike (decided 2026-10-11, after
Material 3's and Apple's guidelines, Android's principles of navigation and
Nielsen Norman Group's on breadcrumbs):
- The screens behind a studyset are its path: below "Japanese › Grammar ›
Tae Kim's Grammar Guide" lie Grammar's screen, then Japanese's, then the
library. A topic at any depth opens with the levels between pushed
beneath it; the path's steps go up by popping to that level's page (they
replaced the location before, so back could skip levels); a studyset
opened from elsewhere gets its levels beneath it. Back, Android's
predictive back, iOS's swipe from the edge and the path all lead one
level up.
- The path's steps are targets 48 dp high; a long press on back lists the
levels above, the nearest first and the library last, as iOS's back
buttons do.
- Below expanded windows the topics open in a sheet from "3 topics ▾" at
the end of the path's line, where Vaia puts "All topics"; no drawer,
whose swipe from the edge is iOS's way back and which Material 3
Expressive deprecates. Topics with topics of their own unfold them, at
any depth, in the sheet and the side column alike.
- The navigation bar steps aside inside a studyset (its screen, notes,
paths and dictionary): Material 3 puts it on the top-level screens and
toolbars below, never both. Apple's guidelines keep the tab bar, lest
people forget where they are; the path, starting at Library, says where.
A strip as high as the system's bar keeps those screens clear of it.
- The bar at the bottom keeps Add material and Study.
A trial for review, on its own branch, on top of the floating navigation
bar.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MMm7usAo6q9ZZRoPkWjoxi
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-11 13:03:27 +02:00
/// No description provided for @studysetTopicCount.
///
/// In en, this message translates to:
/// **'{count, plural, =1{1 topic} other{{count} topics}}'**
String studysetTopicCount ( int count ) ;
/// No description provided for @studysetShowInnerTopics.
///
/// In en, this message translates to:
/// **'Show the topics inside'**
String get studysetShowInnerTopics ;
/// No description provided for @studysetHideInnerTopics.
///
/// In en, this message translates to:
/// **'Hide the topics inside'**
String get studysetHideInnerTopics ;
feat(app): the studyset screen with its topics, the tree's own screen
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 12:59:20 +02:00
/// No description provided for @studysetTopics.
///
/// In en, this message translates to:
/// **'Topics'**
String get studysetTopics ;
/// No description provided for @studysetAllTopics.
///
/// In en, this message translates to:
/// **'All topics'**
String get studysetAllTopics ;
/// No description provided for @studysetNewSubStudyset.
///
/// In en, this message translates to:
/// **'New sub-studyset'**
String get studysetNewSubStudyset ;
/// No description provided for @studysetSubStudysetsEmpty.
///
/// In en, this message translates to:
/// **'No sub-studysets yet.'**
String get studysetSubStudysetsEmpty ;
/// No description provided for @studysetSubStudysetsEmptyHint.
///
/// In en, this message translates to:
/// **'Break this studyset into milestones or lessons.'**
String get studysetSubStudysetsEmptyHint ;
/// No description provided for @studysetDragToReorder.
///
/// In en, this message translates to:
/// **'Drag to reorder'**
String get studysetDragToReorder ;
/// No description provided for @studysetMoveTitle.
///
/// In en, this message translates to:
/// **'Move studyset to'**
String get studysetMoveTitle ;
/// No description provided for @studysetMoveTopLevel.
///
/// In en, this message translates to:
/// **'Top level'**
String get studysetMoveTopLevel ;
/// No description provided for @studysetMoveCurrent.
///
/// In en, this message translates to:
feat(app): Home, the Library and the studyset sheets laid out as before
Home: plain cards on the surface, 16 dp apart, the goal first and the
Pomodoro timer after it in a new dashboard. The streak chip sits centred
in the app bar, and its sheet shows the days inside a big flame and the
week as marks over the weekdays' initials, with a word on today in a box.
The week names its day as "Thursday, Oct 8" and its days carry no
tooltip. The heatmap scrolls its weekdays with the weeks, outlines the
days to come, and names a day's activities with its date. The goal card
has a ring of 5 dp, a check once met and a pencil, and its sheet saves
across its width with Remove goal below. Arranging puts an eye on each
widget's corner and lifts the one picked up, with a tap the hand feels;
with every widget hidden, Customise brings arranging back. The stepper
moves to core, where the goal's sheet takes it too.
Library: the counts line includes files (PDFs); Refresh in the app bar
and pulling the list down sync now; a list that can't be read offers
Retry, which reads it again; the star keeps its square corners.
Studyset sheet: "Title *", the secondary title explained behind a
question mark, Inherit as a box beside the colour, and the description
in up to three lines with a chevron. Move sheet: round marks, the
studyset itself marked as current, the top level only while it is
inside another, archived studysets last under their heading, the
destination named in the footer, and a new studyset to move into, made
only once the move is.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-08 11:12:23 +02:00
/// **'(current)'**
feat(app): the studyset screen with its topics, the tree's own screen
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 12:59:20 +02:00
String get studysetMoveCurrent ;
feat(app): Home, the Library and the studyset sheets laid out as before
Home: plain cards on the surface, 16 dp apart, the goal first and the
Pomodoro timer after it in a new dashboard. The streak chip sits centred
in the app bar, and its sheet shows the days inside a big flame and the
week as marks over the weekdays' initials, with a word on today in a box.
The week names its day as "Thursday, Oct 8" and its days carry no
tooltip. The heatmap scrolls its weekdays with the weeks, outlines the
days to come, and names a day's activities with its date. The goal card
has a ring of 5 dp, a check once met and a pencil, and its sheet saves
across its width with Remove goal below. Arranging puts an eye on each
widget's corner and lifts the one picked up, with a tap the hand feels;
with every widget hidden, Customise brings arranging back. The stepper
moves to core, where the goal's sheet takes it too.
Library: the counts line includes files (PDFs); Refresh in the app bar
and pulling the list down sync now; a list that can't be read offers
Retry, which reads it again; the star keeps its square corners.
Studyset sheet: "Title *", the secondary title explained behind a
question mark, Inherit as a box beside the colour, and the description
in up to three lines with a chevron. Move sheet: round marks, the
studyset itself marked as current, the top level only while it is
inside another, archived studysets last under their heading, the
destination named in the footer, and a new studyset to move into, made
only once the move is.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-08 11:12:23 +02:00
/// No description provided for @studysetMoveTopLevelRow.
///
/// In en, this message translates to:
/// **'Top level (no parent)'**
String get studysetMoveTopLevelRow ;
/// No description provided for @studysetMoveArchived.
///
/// In en, this message translates to:
/// **'Archived'**
String get studysetMoveArchived ;
/// No description provided for @studysetMoveNew.
///
/// In en, this message translates to:
/// **'New studyset'**
String get studysetMoveNew ;
/// No description provided for @studysetMoveSelectDestination.
///
/// In en, this message translates to:
/// **'Select a destination'**
String get studysetMoveSelectDestination ;
/// No description provided for @studysetMoveDestination.
///
/// In en, this message translates to:
/// **'Destination: {name}'**
String studysetMoveDestination ( String name ) ;
feat(app): the studyset screen with its topics, the tree's own screen
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 12:59:20 +02:00
/// No description provided for @studysetMoveConfirm.
///
/// In en, this message translates to:
/// **'Move'**
String get studysetMoveConfirm ;
/// No description provided for @studysetMoveShowInside.
///
/// In en, this message translates to:
feat(app): Home, the Library and the studyset sheets laid out as before
Home: plain cards on the surface, 16 dp apart, the goal first and the
Pomodoro timer after it in a new dashboard. The streak chip sits centred
in the app bar, and its sheet shows the days inside a big flame and the
week as marks over the weekdays' initials, with a word on today in a box.
The week names its day as "Thursday, Oct 8" and its days carry no
tooltip. The heatmap scrolls its weekdays with the weeks, outlines the
days to come, and names a day's activities with its date. The goal card
has a ring of 5 dp, a check once met and a pencil, and its sheet saves
across its width with Remove goal below. Arranging puts an eye on each
widget's corner and lifts the one picked up, with a tap the hand feels;
with every widget hidden, Customise brings arranging back. The stepper
moves to core, where the goal's sheet takes it too.
Library: the counts line includes files (PDFs); Refresh in the app bar
and pulling the list down sync now; a list that can't be read offers
Retry, which reads it again; the star keeps its square corners.
Studyset sheet: "Title *", the secondary title explained behind a
question mark, Inherit as a box beside the colour, and the description
in up to three lines with a chevron. Move sheet: round marks, the
studyset itself marked as current, the top level only while it is
inside another, archived studysets last under their heading, the
destination named in the footer, and a new studyset to move into, made
only once the move is.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-08 11:12:23 +02:00
/// **'Show sub-studysets'**
feat(app): the studyset screen with its topics, the tree's own screen
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 12:59:20 +02:00
String get studysetMoveShowInside ;
/// No description provided for @studysetMoveHideInside.
///
/// In en, this message translates to:
feat(app): Home, the Library and the studyset sheets laid out as before
Home: plain cards on the surface, 16 dp apart, the goal first and the
Pomodoro timer after it in a new dashboard. The streak chip sits centred
in the app bar, and its sheet shows the days inside a big flame and the
week as marks over the weekdays' initials, with a word on today in a box.
The week names its day as "Thursday, Oct 8" and its days carry no
tooltip. The heatmap scrolls its weekdays with the weeks, outlines the
days to come, and names a day's activities with its date. The goal card
has a ring of 5 dp, a check once met and a pencil, and its sheet saves
across its width with Remove goal below. Arranging puts an eye on each
widget's corner and lifts the one picked up, with a tap the hand feels;
with every widget hidden, Customise brings arranging back. The stepper
moves to core, where the goal's sheet takes it too.
Library: the counts line includes files (PDFs); Refresh in the app bar
and pulling the list down sync now; a list that can't be read offers
Retry, which reads it again; the star keeps its square corners.
Studyset sheet: "Title *", the secondary title explained behind a
question mark, Inherit as a box beside the colour, and the description
in up to three lines with a chevron. Move sheet: round marks, the
studyset itself marked as current, the top level only while it is
inside another, archived studysets last under their heading, the
destination named in the footer, and a new studyset to move into, made
only once the move is.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-08 11:12:23 +02:00
/// **'Hide sub-studysets'**
feat(app): the studyset screen with its topics, the tree's own screen
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 12:59:20 +02:00
String get studysetMoveHideInside ;
/// No description provided for @studysetMoved.
///
/// In en, this message translates to:
feat(app): Home, the Library and the studyset sheets laid out as before
Home: plain cards on the surface, 16 dp apart, the goal first and the
Pomodoro timer after it in a new dashboard. The streak chip sits centred
in the app bar, and its sheet shows the days inside a big flame and the
week as marks over the weekdays' initials, with a word on today in a box.
The week names its day as "Thursday, Oct 8" and its days carry no
tooltip. The heatmap scrolls its weekdays with the weeks, outlines the
days to come, and names a day's activities with its date. The goal card
has a ring of 5 dp, a check once met and a pencil, and its sheet saves
across its width with Remove goal below. Arranging puts an eye on each
widget's corner and lifts the one picked up, with a tap the hand feels;
with every widget hidden, Customise brings arranging back. The stepper
moves to core, where the goal's sheet takes it too.
Library: the counts line includes files (PDFs); Refresh in the app bar
and pulling the list down sync now; a list that can't be read offers
Retry, which reads it again; the star keeps its square corners.
Studyset sheet: "Title *", the secondary title explained behind a
question mark, Inherit as a box beside the colour, and the description
in up to three lines with a chevron. Move sheet: round marks, the
studyset itself marked as current, the top level only while it is
inside another, archived studysets last under their heading, the
destination named in the footer, and a new studyset to move into, made
only once the move is.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-08 11:12:23 +02:00
/// **'Moved \"{title}\" to {destination}.'**
String studysetMoved ( String title , String destination ) ;
feat(app): the studyset screen with its topics, the tree's own screen
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 12:59:20 +02:00
/// No description provided for @studysetReadMore.
///
/// In en, this message translates to:
/// **'Read more'**
String get studysetReadMore ;
/// No description provided for @studysetDescriptionTitle.
///
/// In en, this message translates to:
/// **'Description'**
String get studysetDescriptionTitle ;
/// No description provided for @studysetDescriptionEditTitle.
///
/// In en, this message translates to:
/// **'Edit description'**
String get studysetDescriptionEditTitle ;
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
/// No description provided for @richTextSave.
feat(app): the studyset screen with its topics, the tree's own screen
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 12:59:20 +02:00
///
/// In en, this message translates to:
/// **'Save'**
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
String get richTextSave ;
feat(app): the studyset screen with its topics, the tree's own screen
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 12:59:20 +02:00
/// No description provided for @studysetSheetDescription.
///
/// In en, this message translates to:
/// **'Description'**
String get studysetSheetDescription ;
/// No description provided for @studysetSheetDescriptionAdd.
///
/// In en, this message translates to:
feat(app): Home, the Library and the studyset sheets laid out as before
Home: plain cards on the surface, 16 dp apart, the goal first and the
Pomodoro timer after it in a new dashboard. The streak chip sits centred
in the app bar, and its sheet shows the days inside a big flame and the
week as marks over the weekdays' initials, with a word on today in a box.
The week names its day as "Thursday, Oct 8" and its days carry no
tooltip. The heatmap scrolls its weekdays with the weeks, outlines the
days to come, and names a day's activities with its date. The goal card
has a ring of 5 dp, a check once met and a pencil, and its sheet saves
across its width with Remove goal below. Arranging puts an eye on each
widget's corner and lifts the one picked up, with a tap the hand feels;
with every widget hidden, Customise brings arranging back. The stepper
moves to core, where the goal's sheet takes it too.
Library: the counts line includes files (PDFs); Refresh in the app bar
and pulling the list down sync now; a list that can't be read offers
Retry, which reads it again; the star keeps its square corners.
Studyset sheet: "Title *", the secondary title explained behind a
question mark, Inherit as a box beside the colour, and the description
in up to three lines with a chevron. Move sheet: round marks, the
studyset itself marked as current, the top level only while it is
inside another, archived studysets last under their heading, the
destination named in the footer, and a new studyset to move into, made
only once the move is.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-08 11:12:23 +02:00
/// **'Tap to add a description'**
feat(app): the studyset screen with its topics, the tree's own screen
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 12:59:20 +02:00
String get studysetSheetDescriptionAdd ;
feat(app): Home, the Library and the studyset sheets laid out as before
Home: plain cards on the surface, 16 dp apart, the goal first and the
Pomodoro timer after it in a new dashboard. The streak chip sits centred
in the app bar, and its sheet shows the days inside a big flame and the
week as marks over the weekdays' initials, with a word on today in a box.
The week names its day as "Thursday, Oct 8" and its days carry no
tooltip. The heatmap scrolls its weekdays with the weeks, outlines the
days to come, and names a day's activities with its date. The goal card
has a ring of 5 dp, a check once met and a pencil, and its sheet saves
across its width with Remove goal below. Arranging puts an eye on each
widget's corner and lifts the one picked up, with a tap the hand feels;
with every widget hidden, Customise brings arranging back. The stepper
moves to core, where the goal's sheet takes it too.
Library: the counts line includes files (PDFs); Refresh in the app bar
and pulling the list down sync now; a list that can't be read offers
Retry, which reads it again; the star keeps its square corners.
Studyset sheet: "Title *", the secondary title explained behind a
question mark, Inherit as a box beside the colour, and the description
in up to three lines with a chevron. Move sheet: round marks, the
studyset itself marked as current, the top level only while it is
inside another, archived studysets last under their heading, the
destination named in the footer, and a new studyset to move into, made
only once the move is.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-08 11:12:23 +02:00
/// No description provided for @studysetSheetDescriptionAdded.
///
/// In en, this message translates to:
/// **'Description added — tap to edit'**
String get studysetSheetDescriptionAdded ;
feat(app): the studyset screen with its topics, the tree's own screen
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 12:59:20 +02:00
/// No description provided for @studysetSheetInheritColor.
///
/// In en, this message translates to:
feat(app): Home, the Library and the studyset sheets laid out as before
Home: plain cards on the surface, 16 dp apart, the goal first and the
Pomodoro timer after it in a new dashboard. The streak chip sits centred
in the app bar, and its sheet shows the days inside a big flame and the
week as marks over the weekdays' initials, with a word on today in a box.
The week names its day as "Thursday, Oct 8" and its days carry no
tooltip. The heatmap scrolls its weekdays with the weeks, outlines the
days to come, and names a day's activities with its date. The goal card
has a ring of 5 dp, a check once met and a pencil, and its sheet saves
across its width with Remove goal below. Arranging puts an eye on each
widget's corner and lifts the one picked up, with a tap the hand feels;
with every widget hidden, Customise brings arranging back. The stepper
moves to core, where the goal's sheet takes it too.
Library: the counts line includes files (PDFs); Refresh in the app bar
and pulling the list down sync now; a list that can't be read offers
Retry, which reads it again; the star keeps its square corners.
Studyset sheet: "Title *", the secondary title explained behind a
question mark, Inherit as a box beside the colour, and the description
in up to three lines with a chevron. Move sheet: round marks, the
studyset itself marked as current, the top level only while it is
inside another, archived studysets last under their heading, the
destination named in the footer, and a new studyset to move into, made
only once the move is.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-08 11:12:23 +02:00
/// **'Inherit'**
feat(app): the studyset screen with its topics, the tree's own screen
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 12:59:20 +02:00
String get studysetSheetInheritColor ;
feat(app): a studyset's languages, and Chinese and Korean fonts
The studyset sheet gains Languages, as before: off until "Set
languages", then the language of the cards' fronts and of their backs
(22 languages, Chinese in its two scripts). A studyset without its own
takes those of the nearest studyset above it, both together; set for the
first time, they start from those it took. The payload held both fields
already.
They set its text: Noto Sans SC, TC and KR join Noto Sans JP (bundled,
never fetched; 34 MB more, fonts 42 MB in all, your call), and since
Han characters take the shapes of the first font that has them, every
screen inside a studyset (its screen, cards, notes, paths, studying it)
puts the font of its front's language first, else of its back's,
Japanese otherwise. Every studyset route goes through one helper now.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MMm7usAo6q9ZZRoPkWjoxi
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-08 23:30:31 +02:00
/// No description provided for @studysetSheetLanguages.
///
/// In en, this message translates to:
/// **'Languages'**
String get studysetSheetLanguages ;
/// No description provided for @studysetSheetSetLanguages.
///
/// In en, this message translates to:
/// **'Set languages'**
String get studysetSheetSetLanguages ;
/// No description provided for @studysetSheetLanguagesHelper.
///
/// In en, this message translates to:
/// **'Which language each side of the cards is in. Chinese, Japanese and Korean then show in their own character shapes.'**
String get studysetSheetLanguagesHelper ;
/// No description provided for @studysetSheetLanguagesInherited.
///
/// In en, this message translates to:
fix(app): glyph shapes, languages, licences and the side bar's foot
- A Japanese front keeps Japanese shapes whatever the back's language
(Japanese learnt in Chinese or Korean took Chinese or Korean shapes).
- A change of languages no longer rebuilds the screen below: the scope
keeps one shape, so a study session keeps its Undo.
- The PDF reader, with the cards and notes written beside it, takes its
studyset's languages too.
- Licences: GPL-3.0 and AGPL-3.0 mean "only", as SPDX defines them (not
"or later"); GPLv3 and "public domain" stay as typed; words around a
Creative Commons licence ("International", "Unported", "Deed") and its
name written out are read; 4.0 takes no jurisdiction.
- A new sub-studyset takes its parent's licence, not its holder or year.
- The year says it is wrong once typed whole or left; full-width digits
count; the year's range comes from clock.
- New sub-studyset shows whole in the side bar, no longer cut at half.
- The languages switch says that neither side set means inheriting;
"license" as the British verb.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MMm7usAo6q9ZZRoPkWjoxi
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-09 12:45:19 +02:00
/// **'Off, or with neither side set, it takes the languages of the studyset it is in.'**
feat(app): a studyset's languages, and Chinese and Korean fonts
The studyset sheet gains Languages, as before: off until "Set
languages", then the language of the cards' fronts and of their backs
(22 languages, Chinese in its two scripts). A studyset without its own
takes those of the nearest studyset above it, both together; set for the
first time, they start from those it took. The payload held both fields
already.
They set its text: Noto Sans SC, TC and KR join Noto Sans JP (bundled,
never fetched; 34 MB more, fonts 42 MB in all, your call), and since
Han characters take the shapes of the first font that has them, every
screen inside a studyset (its screen, cards, notes, paths, studying it)
puts the font of its front's language first, else of its back's,
Japanese otherwise. Every studyset route goes through one helper now.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MMm7usAo6q9ZZRoPkWjoxi
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-08 23:30:31 +02:00
String get studysetSheetLanguagesInherited ;
/// No description provided for @studysetSheetFrontLanguage.
///
/// In en, this message translates to:
/// **'Front language'**
String get studysetSheetFrontLanguage ;
/// No description provided for @studysetSheetBackLanguage.
///
/// In en, this message translates to:
/// **'Back language'**
String get studysetSheetBackLanguage ;
/// No description provided for @studysetSheetLanguageNotSet.
///
/// In en, this message translates to:
/// **'Not set'**
String get studysetSheetLanguageNotSet ;
2026-10-08 23:48:43 +02:00
/// No description provided for @studysetSheetLicense.
///
/// In en, this message translates to:
/// **'Licence'**
String get studysetSheetLicense ;
/// No description provided for @studysetSheetLicenseHelp.
///
/// In en, this message translates to:
/// **'Help me choose'**
String get studysetSheetLicenseHelp ;
/// No description provided for @studysetSheetLicenseLabel.
///
/// In en, this message translates to:
/// **'Licence'**
String get studysetSheetLicenseLabel ;
/// No description provided for @studysetSheetLicenseHelper.
///
/// In en, this message translates to:
/// **'The terms others may use the content under, shown below the studyset with a link to them. Examples: CC-BY-4.0, CC-BY-SA-4.0, MIT, All Rights Reserved.'**
String get studysetSheetLicenseHelper ;
/// No description provided for @studysetSheetLicenseYear.
///
/// In en, this message translates to:
/// **'Year'**
String get studysetSheetLicenseYear ;
/// No description provided for @studysetSheetLicenseYearHelper.
///
/// In en, this message translates to:
/// **'The copyright year, shown as “© 2019 Name”. Optional.'**
String get studysetSheetLicenseYearHelper ;
/// No description provided for @studysetSheetLicenseYearInvalid.
///
/// In en, this message translates to:
/// **'Enter a year between {min} and {max}.'**
String studysetSheetLicenseYearInvalid ( int min , int max ) ;
/// No description provided for @studysetSheetLicenseHolder.
///
/// In en, this message translates to:
/// **'Licence holder'**
String get studysetSheetLicenseHolder ;
/// No description provided for @studysetSheetLicenseHolderHelper.
///
/// In en, this message translates to:
/// **'Who to credit: a name or an organisation.'**
String get studysetSheetLicenseHolderHelper ;
/// No description provided for @studysetSheetLicenseNewer.
///
/// In en, this message translates to:
/// **'{license} is an older Creative Commons licence. Its current version, {current}, may fit better.'**
String studysetSheetLicenseNewer ( String license , String current ) ;
/// No description provided for @licenseChooserTitle.
///
/// In en, this message translates to:
/// **'Choose a licence'**
String get licenseChooserTitle ;
/// No description provided for @licenseChooserSubtitle.
///
/// In en, this message translates to:
/// **'A few questions to find the Creative Commons licence that fits how you want your work shared.'**
String get licenseChooserSubtitle ;
/// No description provided for @licenseChooserIntroPrompt.
///
/// In en, this message translates to:
/// **'Do you want to keep any rights to your work?'**
String get licenseChooserIntroPrompt ;
/// No description provided for @licenseChooserIntroYes.
///
/// In en, this message translates to:
/// **'Yes, share it under a licence'**
String get licenseChooserIntroYes ;
/// No description provided for @licenseChooserIntroYesDetail.
///
/// In en, this message translates to:
/// **'Others must follow the conditions you choose next.'**
String get licenseChooserIntroYesDetail ;
/// No description provided for @licenseChooserIntroNo.
///
/// In en, this message translates to:
/// **'No, dedicate it to the public domain'**
String get licenseChooserIntroNo ;
/// No description provided for @licenseChooserIntroNoDetail.
///
/// In en, this message translates to:
/// **'CC0: anyone may use it for anything, without conditions.'**
String get licenseChooserIntroNoDetail ;
/// No description provided for @licenseChooserCommercialPrompt.
///
/// In en, this message translates to:
/// **'Allow others to use your work commercially?'**
String get licenseChooserCommercialPrompt ;
/// No description provided for @licenseChooserYes.
///
/// In en, this message translates to:
/// **'Yes'**
String get licenseChooserYes ;
/// No description provided for @licenseChooserCommercialYesDetail.
///
/// In en, this message translates to:
/// **'Including for profit.'**
String get licenseChooserCommercialYesDetail ;
/// No description provided for @licenseChooserNo.
///
/// In en, this message translates to:
/// **'No'**
String get licenseChooserNo ;
/// No description provided for @licenseChooserCommercialNoDetail.
///
/// In en, this message translates to:
/// **'Non-commercial uses only (NC).'**
String get licenseChooserCommercialNoDetail ;
/// No description provided for @licenseChooserAdaptationsPrompt.
///
/// In en, this message translates to:
/// **'Allow others to adapt or build upon your work?'**
String get licenseChooserAdaptationsPrompt ;
/// No description provided for @licenseChooserAdaptationsYesDetail.
///
/// In en, this message translates to:
/// **'Remixes and adaptations are allowed.'**
String get licenseChooserAdaptationsYesDetail ;
/// No description provided for @licenseChooserAdaptationsShareAlike.
///
/// In en, this message translates to:
/// **'Yes, if they share alike'**
String get licenseChooserAdaptationsShareAlike ;
/// No description provided for @licenseChooserAdaptationsShareAlikeDetail.
///
/// In en, this message translates to:
/// **'Adaptations carry this same licence (SA).'**
String get licenseChooserAdaptationsShareAlikeDetail ;
/// No description provided for @licenseChooserAdaptationsNoDetail.
///
/// In en, this message translates to:
/// **'Sharing only, no adaptations (ND).'**
String get licenseChooserAdaptationsNoDetail ;
/// No description provided for @licenseChooserBack.
///
/// In en, this message translates to:
/// **'Back'**
String get licenseChooserBack ;
/// No description provided for @licenseChooserRecommended.
///
/// In en, this message translates to:
/// **'Recommended licence'**
String get licenseChooserRecommended ;
/// No description provided for @licenseChooserUse.
///
/// In en, this message translates to:
/// **'Use this licence'**
String get licenseChooserUse ;
/// No description provided for @licenseSummaryCc0.
///
/// In en, this message translates to:
/// **'Anyone may use it for anything, without conditions or credit.'**
String get licenseSummaryCc0 ;
/// No description provided for @licenseSummaryBy.
///
/// In en, this message translates to:
/// **'Others may share and adapt it, even commercially, as long as they credit you.'**
String get licenseSummaryBy ;
/// No description provided for @licenseSummaryBySa.
///
/// In en, this message translates to:
fix(app): glyph shapes, languages, licences and the side bar's foot
- A Japanese front keeps Japanese shapes whatever the back's language
(Japanese learnt in Chinese or Korean took Chinese or Korean shapes).
- A change of languages no longer rebuilds the screen below: the scope
keeps one shape, so a study session keeps its Undo.
- The PDF reader, with the cards and notes written beside it, takes its
studyset's languages too.
- Licences: GPL-3.0 and AGPL-3.0 mean "only", as SPDX defines them (not
"or later"); GPLv3 and "public domain" stay as typed; words around a
Creative Commons licence ("International", "Unported", "Deed") and its
name written out are read; 4.0 takes no jurisdiction.
- A new sub-studyset takes its parent's licence, not its holder or year.
- The year says it is wrong once typed whole or left; full-width digits
count; the year's range comes from clock.
- New sub-studyset shows whole in the side bar, no longer cut at half.
- The languages switch says that neither side set means inheriting;
"license" as the British verb.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MMm7usAo6q9ZZRoPkWjoxi
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-09 12:45:19 +02:00
/// **'Others may share and adapt it, even commercially, if they credit you and license their adaptations the same way.'**
2026-10-08 23:48:43 +02:00
String get licenseSummaryBySa ;
/// No description provided for @licenseSummaryByNc.
///
/// In en, this message translates to:
/// **'Others may share and adapt it for non-commercial purposes, with credit to you.'**
String get licenseSummaryByNc ;
/// No description provided for @licenseSummaryByNcSa.
///
/// In en, this message translates to:
/// **'Others may share and adapt it non-commercially, with credit and the same licence on adaptations.'**
String get licenseSummaryByNcSa ;
/// No description provided for @licenseSummaryByNd.
///
/// In en, this message translates to:
/// **'Others may share it, even commercially, with credit, but may not share adaptations.'**
String get licenseSummaryByNd ;
/// No description provided for @licenseSummaryByNcNd.
///
/// In en, this message translates to:
/// **'Others may share it non-commercially, with credit, but may not adapt it: the strictest Creative Commons licence.'**
String get licenseSummaryByNcNd ;
feat(app): a studyset's languages, and Chinese and Korean fonts
The studyset sheet gains Languages, as before: off until "Set
languages", then the language of the cards' fronts and of their backs
(22 languages, Chinese in its two scripts). A studyset without its own
takes those of the nearest studyset above it, both together; set for the
first time, they start from those it took. The payload held both fields
already.
They set its text: Noto Sans SC, TC and KR join Noto Sans JP (bundled,
never fetched; 34 MB more, fonts 42 MB in all, your call), and since
Han characters take the shapes of the first font that has them, every
screen inside a studyset (its screen, cards, notes, paths, studying it)
puts the font of its front's language first, else of its back's,
Japanese otherwise. Every studyset route goes through one helper now.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MMm7usAo6q9ZZRoPkWjoxi
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-08 23:30:31 +02:00
/// No description provided for @languageArabic.
///
/// In en, this message translates to:
/// **'Arabic'**
String get languageArabic ;
/// No description provided for @languageGerman.
///
/// In en, this message translates to:
/// **'German'**
String get languageGerman ;
/// No description provided for @languageGreek.
///
/// In en, this message translates to:
/// **'Greek'**
String get languageGreek ;
/// No description provided for @languageEnglish.
///
/// In en, this message translates to:
/// **'English'**
String get languageEnglish ;
/// No description provided for @languageSpanish.
///
/// In en, this message translates to:
/// **'Spanish'**
String get languageSpanish ;
/// No description provided for @languageFrench.
///
/// In en, this message translates to:
/// **'French'**
String get languageFrench ;
/// No description provided for @languageHebrew.
///
/// In en, this message translates to:
/// **'Hebrew'**
String get languageHebrew ;
/// No description provided for @languageHindi.
///
/// In en, this message translates to:
/// **'Hindi'**
String get languageHindi ;
/// No description provided for @languageItalian.
///
/// In en, this message translates to:
/// **'Italian'**
String get languageItalian ;
/// No description provided for @languageJapanese.
///
/// In en, this message translates to:
/// **'Japanese'**
String get languageJapanese ;
/// No description provided for @languageKorean.
///
/// In en, this message translates to:
/// **'Korean'**
String get languageKorean ;
/// No description provided for @languageDutch.
///
/// In en, this message translates to:
/// **'Dutch'**
String get languageDutch ;
/// No description provided for @languagePolish.
///
/// In en, this message translates to:
/// **'Polish'**
String get languagePolish ;
/// No description provided for @languagePortuguese.
///
/// In en, this message translates to:
/// **'Portuguese'**
String get languagePortuguese ;
/// No description provided for @languageRussian.
///
/// In en, this message translates to:
/// **'Russian'**
String get languageRussian ;
/// No description provided for @languageSwedish.
///
/// In en, this message translates to:
/// **'Swedish'**
String get languageSwedish ;
/// No description provided for @languageThai.
///
/// In en, this message translates to:
/// **'Thai'**
String get languageThai ;
/// No description provided for @languageTurkish.
///
/// In en, this message translates to:
/// **'Turkish'**
String get languageTurkish ;
/// No description provided for @languageUkrainian.
///
/// In en, this message translates to:
/// **'Ukrainian'**
String get languageUkrainian ;
/// No description provided for @languageVietnamese.
///
/// In en, this message translates to:
/// **'Vietnamese'**
String get languageVietnamese ;
/// No description provided for @languageChineseSimplified.
///
/// In en, this message translates to:
/// **'Chinese (Simplified)'**
String get languageChineseSimplified ;
/// No description provided for @languageChineseTraditional.
///
/// In en, this message translates to:
/// **'Chinese (Traditional)'**
String get languageChineseTraditional ;
feat(app): Home, the Library and the studyset sheets laid out as before
Home: plain cards on the surface, 16 dp apart, the goal first and the
Pomodoro timer after it in a new dashboard. The streak chip sits centred
in the app bar, and its sheet shows the days inside a big flame and the
week as marks over the weekdays' initials, with a word on today in a box.
The week names its day as "Thursday, Oct 8" and its days carry no
tooltip. The heatmap scrolls its weekdays with the weeks, outlines the
days to come, and names a day's activities with its date. The goal card
has a ring of 5 dp, a check once met and a pencil, and its sheet saves
across its width with Remove goal below. Arranging puts an eye on each
widget's corner and lifts the one picked up, with a tap the hand feels;
with every widget hidden, Customise brings arranging back. The stepper
moves to core, where the goal's sheet takes it too.
Library: the counts line includes files (PDFs); Refresh in the app bar
and pulling the list down sync now; a list that can't be read offers
Retry, which reads it again; the star keeps its square corners.
Studyset sheet: "Title *", the secondary title explained behind a
question mark, Inherit as a box beside the colour, and the description
in up to three lines with a chevron. Move sheet: round marks, the
studyset itself marked as current, the top level only while it is
inside another, archived studysets last under their heading, the
destination named in the footer, and a new studyset to move into, made
only once the move is.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-08 11:12:23 +02:00
/// No description provided for @fieldRequired.
///
/// In en, this message translates to:
/// **'required'**
String get fieldRequired ;
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
/// No description provided for @exploreTitle.
///
/// In en, this message translates to:
/// **'Explore'**
String get exploreTitle ;
/// No description provided for @exploreEmpty.
///
/// In en, this message translates to:
/// **'Studysets that others share will appear here.'**
String get exploreEmpty ;
/// No description provided for @profileTitle.
///
/// In en, this message translates to:
/// **'Profile'**
String get profileTitle ;
feat(profile): statistics beside the profile, a review counting a minute at most
The Profile tab has two tabs, Profile and Statistics, the latter for
anonymous people too: the time studied in all, per day and the most in
one day, and by days, weeks or months the study time and the ratings as
bars, a tap on a bar saying what it holds. The app draws the charts
itself, as the Progress part's sparkline, rather than taking a chart
package.
A review's time was however long its card was shown: one left on the
screen over lunch counted as an hour of study. It now counts at most a
minute, as Anki does by default, here and in a studyset's Progress; the
stored durations stay as they were.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MMm7usAo6q9ZZRoPkWjoxi
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-12 18:56:40 +02:00
/// No description provided for @profileTabProfile.
///
/// In en, this message translates to:
/// **'Profile'**
String get profileTabProfile ;
/// No description provided for @profileTabStatistics.
///
/// In en, this message translates to:
/// **'Statistics'**
String get profileTabStatistics ;
/// No description provided for @statisticsDays.
///
/// In en, this message translates to:
/// **'Days'**
String get statisticsDays ;
/// No description provided for @statisticsWeeks.
///
/// In en, this message translates to:
/// **'Weeks'**
String get statisticsWeeks ;
/// No description provided for @statisticsMonths.
///
/// In en, this message translates to:
/// **'Months'**
String get statisticsMonths ;
/// No description provided for @statisticsTotal.
///
/// In en, this message translates to:
/// **'In total'**
String get statisticsTotal ;
/// No description provided for @statisticsPerDay.
///
/// In en, this message translates to:
/// **'Per day'**
String get statisticsPerDay ;
/// No description provided for @statisticsRecord.
///
/// In en, this message translates to:
/// **'Daily record'**
String get statisticsRecord ;
/// No description provided for @statisticsStudyTime.
///
/// In en, this message translates to:
/// **'Study time'**
String get statisticsStudyTime ;
/// No description provided for @statisticsMinutesInfo.
///
/// In en, this message translates to:
/// **'In minutes, each card counted for at most a minute'**
String get statisticsMinutesInfo ;
/// No description provided for @statisticsHoursInfo.
///
/// In en, this message translates to:
/// **'In hours, each card counted for at most a minute'**
String get statisticsHoursInfo ;
/// No description provided for @statisticsRatings.
///
/// In en, this message translates to:
/// **'Ratings'**
String get statisticsRatings ;
/// No description provided for @statisticsRatingsInfo.
///
/// In en, this message translates to:
/// **'How you rated the flashcards you studied'**
String get statisticsRatingsInfo ;
/// No description provided for @statisticsRated.
///
/// In en, this message translates to:
/// **'{count, plural, =0{No cards rated} =1{1 card rated} other{{count} cards rated}}'**
String statisticsRated ( int count ) ;
/// No description provided for @statisticsToday.
///
/// In en, this message translates to:
/// **'Today'**
String get statisticsToday ;
/// No description provided for @statisticsWeekOf.
///
/// In en, this message translates to:
/// **'Week of {date}'**
String statisticsWeekOf ( String date ) ;
/// No description provided for @statisticsNothingYet.
///
/// In en, this message translates to:
/// **'Nothing studied yet. Your study time and ratings show here once you study flashcards.'**
String get statisticsNothingYet ;
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
/// No description provided for @profileSettingsTooltip.
///
/// In en, this message translates to:
/// **'Settings'**
String get profileSettingsTooltip ;
/// No description provided for @profileIdentified.
///
/// In en, this message translates to:
/// **'Your progress is kept with your account.'**
String get profileIdentified ;
/// No description provided for @profileAnonymous.
///
/// In en, this message translates to:
/// **'Progress is saved on this device. Sign in to keep it.'**
String get profileAnonymous ;
/// No description provided for @profileSignInWithGitHub.
///
/// In en, this message translates to:
/// **'Sign in with GitHub'**
String get profileSignInWithGitHub ;
/// No description provided for @profileSignInFailed.
///
/// In en, this message translates to:
/// **'Couldn\'t sign in with GitHub.'**
String get profileSignInFailed ;
/// No description provided for @profileContinueInBrowser.
///
/// In en, this message translates to:
/// **'Continue in your browser.'**
String get profileContinueInBrowser ;
/// No description provided for @profileSwitchTitle.
///
/// In en, this message translates to:
/// **'Switch to your GitHub account?'**
String get profileSwitchTitle ;
/// No description provided for @profileSwitchMessage.
///
/// In en, this message translates to:
/// **'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.'**
String get profileSwitchMessage ;
/// No description provided for @profileSwitchStay.
///
/// In en, this message translates to:
/// **'Stay anonymous'**
String get profileSwitchStay ;
/// No description provided for @profileSwitchConfirm.
///
/// In en, this message translates to:
/// **'Switch'**
String get profileSwitchConfirm ;
feat(auth): sign in with an email address, keeping only a keyed hash of it
People without GitHub can now keep an anonymous account, and sign in on
another device, with an email address and a code mailed to it each time;
no password. The instance keeps only HMAC-SHA256 of the address, keyed
with its emailSignInPepper (ADR 0010): from what someone types it finds
the person and tells whether an address is free, it mails only an address
someone has just typed, and it can't write to anybody otherwise or show
anybody their address. The device remembers the address it signed in
with, in the person's folder, to show it.
It works as GitHub does. A code for a new address makes it the signed-in
person's, who becomes identified and keeps everything. A code for another
person's address gives a switch token, which signs in as them without a
session; a device holding studysets asks first. Asking for a code answers
alike for every address. Codes are eight Crockford characters, valid 15
minutes and three tries (Serverpod's code challenges); three codes an
hour per address, ten per network. In development the code goes to the
server's log; elsewhere there is no email sign-in until a mail service
is set up (instance.info().emailSignIn).
Someone identified may add the other way in: the Profile tab lists how
they sign in and adds GitHub or an address. They are never switched to
another account: an address or GitHub account that is another's is said
to be so. Switching now deletes the account left behind only when it was
anonymous; it deleted any, which only anonymous people could reach so far.
The migration adds email_identity and email_code_request with their
indexes and foreign keys to Serverpod's challenges; nothing else changes.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-15 19:42:21 +02:00
/// No description provided for @profileSignInWays.
///
/// In en, this message translates to:
/// **'Ways to sign in'**
String get profileSignInWays ;
/// No description provided for @profileWayGitHub.
///
/// In en, this message translates to:
/// **'GitHub'**
String get profileWayGitHub ;
/// No description provided for @profileWayEmail.
///
/// In en, this message translates to:
/// **'Email address'**
String get profileWayEmail ;
/// No description provided for @profileWayLinked.
///
/// In en, this message translates to:
/// **'Signs you in'**
String get profileWayLinked ;
/// No description provided for @profileAddGitHub.
///
/// In en, this message translates to:
/// **'Add GitHub'**
String get profileAddGitHub ;
/// No description provided for @profileGitHubTaken.
///
/// In en, this message translates to:
/// **'This GitHub account already signs in to another account.'**
String get profileGitHubTaken ;
/// No description provided for @emailSignInSwitchMessage.
///
/// In en, this message translates to:
/// **'This address already has progress from another device. The studysets on this device can\'t be combined with it yet: if you switch, they are deleted.'**
String get emailSignInSwitchMessage ;
feat: public profiles, a handle on the account domain with name, bio, links and picture
Stage 1 of the public tier (ADR 0005). A person with an identity has a
public profile: a handle by Mastodon's rules (1 to 30 letters, digits or
underscores, unique whatever its case, a few kept for the instance),
changeable until they first publish, on the instance's account domain
(ACCOUNT_DOMAIN, else the server's host; the instance info names it), so
`raisondetredev@halkyon.eu`; a display name, a bio, up to four links and
an avatar. The table is server only: the app gets a Profile without the
person's id. ProfileEndpoint (identified only) reads, saves and removes
the caller's own; ProfilesEndpoint finds anyone's by handle.
An avatar is a PNG of at most 1 MB that the app draws anew from the
picture chosen, its middle square at 400 pixels, so a photo's metadata
never leaves the device. The server keeps it in the public storage named
by its content, so its URL tells nothing about whose it is, and deletes it
once no profile shows it. Deleting a person deletes their profile and its
picture.
In the app, the Profile tab holds the profile as a form, laid out as the
old app's; anonymous people are told GitHub sign-in gives them one.
Migration 20261009184924904-public-profiles creates public_profile with a
unique index on the person and one on the handle in lower case; it touches
no other table, and the old server never sees it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MMm7usAo6q9ZZRoPkWjoxi
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-09 20:55:16 +02:00
/// No description provided for @publicProfileTitle.
///
/// In en, this message translates to:
/// **'Public profile'**
String get publicProfileTitle ;
2026-10-14 18:14:04 +02:00
/// No description provided for @profileSheetTitle.
///
/// In en, this message translates to:
/// **'Profile'**
String get profileSheetTitle ;
/// No description provided for @profileSheetNotFound.
///
/// In en, this message translates to:
/// **'Nobody on this instance has this handle.'**
String get profileSheetNotFound ;
feat(profile): profile pages on the account domain, and links verified by rel="me"
A profile now has a page on the web, at the root of the account domain
(https://halkyon.eu/@raisondetredev where the operator set ACCOUNT_DOMAIN,
else on the instance's web server), so the address belongs to the
identity rather than to Learn and stays when other services share it
(ADR 0011). The page shows what the app's profile sheet shows, in English
or German, without JavaScript or cookies, asks search engines not to list
it, marks every link rel="me" so other sites can verify links to it, and
sends a visitor who asked on another host to its own address. Caddy
forwards /@… to the web server, and serves nothing else on the account
domain; compose now passes ACCOUNT_DOMAIN on, which it never did.
A link is verified as Mastodon verifies one: its page links back to the
profile's with rel="me". profile.checkLinks fetches each link's page, at
most ten times an hour, over HTTPS on port 443 and to public addresses
only, connecting to the address it checked and verifying the certificate
for the host, with three redirects, 1 MiB and ten seconds at most, and
keeps what links back on the profile (verifiedLinks, a column of a table
no device syncs). A changed link loses its check, a new handle all of
them. The app shows the page's address to copy, checks a link saved by
itself and on Check links, and marks verified links in the form and in
others' profiles; a check answered after a newer save only adds its
checks.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-15 20:29:30 +02:00
/// No description provided for @profileSheetLinkVerified.
///
/// In en, this message translates to:
/// **'Links back to this profile'**
String get profileSheetLinkVerified ;
2026-10-14 18:14:04 +02:00
/// No description provided for @profileSheetUnreachable.
///
/// In en, this message translates to:
/// **'The profile couldn’ t be loaded. Check your connection.'**
String get profileSheetUnreachable ;
feat: public profiles, a handle on the account domain with name, bio, links and picture
Stage 1 of the public tier (ADR 0005). A person with an identity has a
public profile: a handle by Mastodon's rules (1 to 30 letters, digits or
underscores, unique whatever its case, a few kept for the instance),
changeable until they first publish, on the instance's account domain
(ACCOUNT_DOMAIN, else the server's host; the instance info names it), so
`raisondetredev@halkyon.eu`; a display name, a bio, up to four links and
an avatar. The table is server only: the app gets a Profile without the
person's id. ProfileEndpoint (identified only) reads, saves and removes
the caller's own; ProfilesEndpoint finds anyone's by handle.
An avatar is a PNG of at most 1 MB that the app draws anew from the
picture chosen, its middle square at 400 pixels, so a photo's metadata
never leaves the device. The server keeps it in the public storage named
by its content, so its URL tells nothing about whose it is, and deletes it
once no profile shows it. Deleting a person deletes their profile and its
picture.
In the app, the Profile tab holds the profile as a form, laid out as the
old app's; anonymous people are told GitHub sign-in gives them one.
Migration 20261009184924904-public-profiles creates public_profile with a
unique index on the person and one on the handle in lower case; it touches
no other table, and the old server never sees it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MMm7usAo6q9ZZRoPkWjoxi
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-09 20:55:16 +02:00
/// No description provided for @publicProfileIntro.
///
/// In en, this message translates to:
2026-10-12 19:07:42 +02:00
/// **'How others see you once you publish: the handle they find you by, your name, a few words and links. It saves as you write, once you have chosen your handle.'**
feat: public profiles, a handle on the account domain with name, bio, links and picture
Stage 1 of the public tier (ADR 0005). A person with an identity has a
public profile: a handle by Mastodon's rules (1 to 30 letters, digits or
underscores, unique whatever its case, a few kept for the instance),
changeable until they first publish, on the instance's account domain
(ACCOUNT_DOMAIN, else the server's host; the instance info names it), so
`raisondetredev@halkyon.eu`; a display name, a bio, up to four links and
an avatar. The table is server only: the app gets a Profile without the
person's id. ProfileEndpoint (identified only) reads, saves and removes
the caller's own; ProfilesEndpoint finds anyone's by handle.
An avatar is a PNG of at most 1 MB that the app draws anew from the
picture chosen, its middle square at 400 pixels, so a photo's metadata
never leaves the device. The server keeps it in the public storage named
by its content, so its URL tells nothing about whose it is, and deletes it
once no profile shows it. Deleting a person deletes their profile and its
picture.
In the app, the Profile tab holds the profile as a form, laid out as the
old app's; anonymous people are told GitHub sign-in gives them one.
Migration 20261009184924904-public-profiles creates public_profile with a
unique index on the person and one on the handle in lower case; it touches
no other table, and the old server never sees it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MMm7usAo6q9ZZRoPkWjoxi
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-09 20:55:16 +02:00
String get publicProfileIntro ;
/// No description provided for @publicProfileAnonymousHint.
///
/// In en, this message translates to:
feat(auth): sign in with an email address, keeping only a keyed hash of it
People without GitHub can now keep an anonymous account, and sign in on
another device, with an email address and a code mailed to it each time;
no password. The instance keeps only HMAC-SHA256 of the address, keyed
with its emailSignInPepper (ADR 0010): from what someone types it finds
the person and tells whether an address is free, it mails only an address
someone has just typed, and it can't write to anybody otherwise or show
anybody their address. The device remembers the address it signed in
with, in the person's folder, to show it.
It works as GitHub does. A code for a new address makes it the signed-in
person's, who becomes identified and keeps everything. A code for another
person's address gives a switch token, which signs in as them without a
session; a device holding studysets asks first. Asking for a code answers
alike for every address. Codes are eight Crockford characters, valid 15
minutes and three tries (Serverpod's code challenges); three codes an
hour per address, ten per network. In development the code goes to the
server's log; elsewhere there is no email sign-in until a mail service
is set up (instance.info().emailSignIn).
Someone identified may add the other way in: the Profile tab lists how
they sign in and adds GitHub or an address. They are never switched to
another account: an address or GitHub account that is another's is said
to be so. Switching now deletes the account left behind only when it was
anonymous; it deleted any, which only anonymous people could reach so far.
The migration adds email_identity and email_code_request with their
indexes and foreign keys to Serverpod's challenges; nothing else changes.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-15 19:42:21 +02:00
/// **'Sign in to have a public profile and publish.'**
feat: public profiles, a handle on the account domain with name, bio, links and picture
Stage 1 of the public tier (ADR 0005). A person with an identity has a
public profile: a handle by Mastodon's rules (1 to 30 letters, digits or
underscores, unique whatever its case, a few kept for the instance),
changeable until they first publish, on the instance's account domain
(ACCOUNT_DOMAIN, else the server's host; the instance info names it), so
`raisondetredev@halkyon.eu`; a display name, a bio, up to four links and
an avatar. The table is server only: the app gets a Profile without the
person's id. ProfileEndpoint (identified only) reads, saves and removes
the caller's own; ProfilesEndpoint finds anyone's by handle.
An avatar is a PNG of at most 1 MB that the app draws anew from the
picture chosen, its middle square at 400 pixels, so a photo's metadata
never leaves the device. The server keeps it in the public storage named
by its content, so its URL tells nothing about whose it is, and deletes it
once no profile shows it. Deleting a person deletes their profile and its
picture.
In the app, the Profile tab holds the profile as a form, laid out as the
old app's; anonymous people are told GitHub sign-in gives them one.
Migration 20261009184924904-public-profiles creates public_profile with a
unique index on the person and one on the handle in lower case; it touches
no other table, and the old server never sees it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MMm7usAo6q9ZZRoPkWjoxi
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-09 20:55:16 +02:00
String get publicProfileAnonymousHint ;
/// No description provided for @publicProfileDisplayName.
///
/// In en, this message translates to:
/// **'Display name'**
String get publicProfileDisplayName ;
/// No description provided for @publicProfileDisplayNameHint.
///
/// In en, this message translates to:
/// **'The name shown beside your handle'**
String get publicProfileDisplayNameHint ;
/// No description provided for @publicProfileHandle.
///
/// In en, this message translates to:
/// **'Handle'**
String get publicProfileHandle ;
/// No description provided for @publicProfileHandleHint.
///
/// In en, this message translates to:
/// **'Letters, digits and underscores, up to 30'**
String get publicProfileHandleHint ;
/// No description provided for @publicProfileAddress.
///
/// In en, this message translates to:
/// **'Others find you as @{address}'**
String publicProfileAddress ( String address ) ;
/// No description provided for @publicProfileHandleFixed.
///
/// In en, this message translates to:
/// **'Fixed since you published: others refer to it.'**
String get publicProfileHandleFixed ;
/// No description provided for @publicProfileBio.
///
/// In en, this message translates to:
/// **'Bio'**
String get publicProfileBio ;
/// No description provided for @publicProfileLinks.
///
/// In en, this message translates to:
/// **'Links'**
String get publicProfileLinks ;
/// No description provided for @publicProfileLinksHint.
///
/// In en, this message translates to:
/// **'Your site or other accounts, up to 4.'**
String get publicProfileLinksHint ;
/// No description provided for @publicProfileLinkHint.
///
/// In en, this message translates to:
/// **'https://…'**
String get publicProfileLinkHint ;
/// No description provided for @publicProfileAddLink.
///
/// In en, this message translates to:
/// **'Add link'**
String get publicProfileAddLink ;
feat(profile): profile pages on the account domain, and links verified by rel="me"
A profile now has a page on the web, at the root of the account domain
(https://halkyon.eu/@raisondetredev where the operator set ACCOUNT_DOMAIN,
else on the instance's web server), so the address belongs to the
identity rather than to Learn and stays when other services share it
(ADR 0011). The page shows what the app's profile sheet shows, in English
or German, without JavaScript or cookies, asks search engines not to list
it, marks every link rel="me" so other sites can verify links to it, and
sends a visitor who asked on another host to its own address. Caddy
forwards /@… to the web server, and serves nothing else on the account
domain; compose now passes ACCOUNT_DOMAIN on, which it never did.
A link is verified as Mastodon verifies one: its page links back to the
profile's with rel="me". profile.checkLinks fetches each link's page, at
most ten times an hour, over HTTPS on port 443 and to public addresses
only, connecting to the address it checked and verifying the certificate
for the host, with three redirects, 1 MiB and ten seconds at most, and
keeps what links back on the profile (verifiedLinks, a column of a table
no device syncs). A changed link loses its check, a new handle all of
them. The app shows the page's address to copy, checks a link saved by
itself and on Check links, and marks verified links in the form and in
others' profiles; a check answered after a newer save only adds its
checks.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-15 20:29:30 +02:00
/// No description provided for @publicProfilePage.
///
/// In en, this message translates to:
/// **'Your profile page: {url}'**
String publicProfilePage ( String url ) ;
/// No description provided for @publicProfilePageCopy.
///
/// In en, this message translates to:
/// **'Copy the address'**
String get publicProfilePageCopy ;
/// No description provided for @publicProfilePageCopied.
///
/// In en, this message translates to:
/// **'Address copied.'**
String get publicProfilePageCopied ;
/// No description provided for @publicProfileVerifyHint.
///
/// In en, this message translates to:
/// **'A link gets a checkmark once its page links back to your profile page with rel=\"me\", as Mastodon checks links.'**
String get publicProfileVerifyHint ;
/// No description provided for @publicProfileCheckLinks.
///
/// In en, this message translates to:
/// **'Check links'**
String get publicProfileCheckLinks ;
/// No description provided for @publicProfileLinkVerified.
///
/// In en, this message translates to:
/// **'Links back to your profile'**
String get publicProfileLinkVerified ;
/// No description provided for @publicProfileLinksChecked.
///
/// In en, this message translates to:
/// **'{count, plural, =1{Checked: {verified} of 1 link links back to your profile.} other{Checked: {verified} of {count} links link back to your profile.}}'**
String publicProfileLinksChecked ( int count , int verified ) ;
feat: public profiles, a handle on the account domain with name, bio, links and picture
Stage 1 of the public tier (ADR 0005). A person with an identity has a
public profile: a handle by Mastodon's rules (1 to 30 letters, digits or
underscores, unique whatever its case, a few kept for the instance),
changeable until they first publish, on the instance's account domain
(ACCOUNT_DOMAIN, else the server's host; the instance info names it), so
`raisondetredev@halkyon.eu`; a display name, a bio, up to four links and
an avatar. The table is server only: the app gets a Profile without the
person's id. ProfileEndpoint (identified only) reads, saves and removes
the caller's own; ProfilesEndpoint finds anyone's by handle.
An avatar is a PNG of at most 1 MB that the app draws anew from the
picture chosen, its middle square at 400 pixels, so a photo's metadata
never leaves the device. The server keeps it in the public storage named
by its content, so its URL tells nothing about whose it is, and deletes it
once no profile shows it. Deleting a person deletes their profile and its
picture.
In the app, the Profile tab holds the profile as a form, laid out as the
old app's; anonymous people are told GitHub sign-in gives them one.
Migration 20261009184924904-public-profiles creates public_profile with a
unique index on the person and one on the handle in lower case; it touches
no other table, and the old server never sees it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MMm7usAo6q9ZZRoPkWjoxi
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-09 20:55:16 +02:00
/// No description provided for @publicProfileRemoveLink.
///
/// In en, this message translates to:
/// **'Remove link'**
String get publicProfileRemoveLink ;
2026-10-12 19:07:42 +02:00
/// No description provided for @publicProfileSaving.
feat: public profiles, a handle on the account domain with name, bio, links and picture
Stage 1 of the public tier (ADR 0005). A person with an identity has a
public profile: a handle by Mastodon's rules (1 to 30 letters, digits or
underscores, unique whatever its case, a few kept for the instance),
changeable until they first publish, on the instance's account domain
(ACCOUNT_DOMAIN, else the server's host; the instance info names it), so
`raisondetredev@halkyon.eu`; a display name, a bio, up to four links and
an avatar. The table is server only: the app gets a Profile without the
person's id. ProfileEndpoint (identified only) reads, saves and removes
the caller's own; ProfilesEndpoint finds anyone's by handle.
An avatar is a PNG of at most 1 MB that the app draws anew from the
picture chosen, its middle square at 400 pixels, so a photo's metadata
never leaves the device. The server keeps it in the public storage named
by its content, so its URL tells nothing about whose it is, and deletes it
once no profile shows it. Deleting a person deletes their profile and its
picture.
In the app, the Profile tab holds the profile as a form, laid out as the
old app's; anonymous people are told GitHub sign-in gives them one.
Migration 20261009184924904-public-profiles creates public_profile with a
unique index on the person and one on the handle in lower case; it touches
no other table, and the old server never sees it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MMm7usAo6q9ZZRoPkWjoxi
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-09 20:55:16 +02:00
///
/// In en, this message translates to:
2026-10-12 19:07:42 +02:00
/// **'Saving…'**
String get publicProfileSaving ;
/// No description provided for @publicProfileSavedNow.
///
/// In en, this message translates to:
/// **'Saved'**
String get publicProfileSavedNow ;
/// No description provided for @publicProfileHandleChoose.
///
/// In en, this message translates to:
/// **'Choose a handle'**
String get publicProfileHandleChoose ;
/// No description provided for @publicProfileHandleFirst.
///
/// In en, this message translates to:
/// **'Choose your handle first: your profile is made with it, and saves as you write from then on.'**
String get publicProfileHandleFirst ;
/// No description provided for @publicProfileHandleChooseTitle.
///
/// In en, this message translates to:
/// **'Choose your handle'**
String get publicProfileHandleChooseTitle ;
/// No description provided for @publicProfileHandleChangeTitle.
///
/// In en, this message translates to:
/// **'Change your handle'**
String get publicProfileHandleChangeTitle ;
/// No description provided for @publicProfileHandleDialogText.
///
/// In en, this message translates to:
/// **'Others find you by your handle. You can change it until you first publish a studyset.'**
String get publicProfileHandleDialogText ;
/// No description provided for @publicProfileHandleChecking.
///
/// In en, this message translates to:
/// **'Checking…'**
String get publicProfileHandleChecking ;
/// No description provided for @publicProfileHandleFree.
///
/// In en, this message translates to:
/// **'@{handle} is free'**
String publicProfileHandleFree ( String handle ) ;
/// No description provided for @publicProfileHandleYours.
///
/// In en, this message translates to:
/// **'This is your handle'**
String get publicProfileHandleYours ;
/// No description provided for @publicProfileHandleConfirmChoose.
///
/// In en, this message translates to:
/// **'Choose'**
String get publicProfileHandleConfirmChoose ;
/// No description provided for @publicProfileHandleConfirmChange.
///
/// In en, this message translates to:
/// **'Change'**
String get publicProfileHandleConfirmChange ;
feat: public profiles, a handle on the account domain with name, bio, links and picture
Stage 1 of the public tier (ADR 0005). A person with an identity has a
public profile: a handle by Mastodon's rules (1 to 30 letters, digits or
underscores, unique whatever its case, a few kept for the instance),
changeable until they first publish, on the instance's account domain
(ACCOUNT_DOMAIN, else the server's host; the instance info names it), so
`raisondetredev@halkyon.eu`; a display name, a bio, up to four links and
an avatar. The table is server only: the app gets a Profile without the
person's id. ProfileEndpoint (identified only) reads, saves and removes
the caller's own; ProfilesEndpoint finds anyone's by handle.
An avatar is a PNG of at most 1 MB that the app draws anew from the
picture chosen, its middle square at 400 pixels, so a photo's metadata
never leaves the device. The server keeps it in the public storage named
by its content, so its URL tells nothing about whose it is, and deletes it
once no profile shows it. Deleting a person deletes their profile and its
picture.
In the app, the Profile tab holds the profile as a form, laid out as the
old app's; anonymous people are told GitHub sign-in gives them one.
Migration 20261009184924904-public-profiles creates public_profile with a
unique index on the person and one on the handle in lower case; it touches
no other table, and the old server never sees it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MMm7usAo6q9ZZRoPkWjoxi
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-09 20:55:16 +02:00
/// No description provided for @publicProfileRemove.
///
/// In en, this message translates to:
/// **'Remove profile'**
String get publicProfileRemove ;
/// No description provided for @publicProfileRemoveTitle.
///
/// In en, this message translates to:
/// **'Remove your public profile?'**
String get publicProfileRemoveTitle ;
/// No description provided for @publicProfileRemoveMessage.
///
/// In en, this message translates to:
/// **'Your handle becomes free for others to take.'**
String get publicProfileRemoveMessage ;
/// No description provided for @publicProfileRemoved.
///
/// In en, this message translates to:
/// **'Profile removed.'**
String get publicProfileRemoved ;
/// No description provided for @publicProfileFailed.
///
/// In en, this message translates to:
/// **'Couldn\'t reach the instance. Try again.'**
String get publicProfileFailed ;
/// No description provided for @publicProfileUnreachable.
///
/// In en, this message translates to:
/// **'Your public profile can\'t be reached right now.'**
String get publicProfileUnreachable ;
/// No description provided for @publicProfileRetry.
///
/// In en, this message translates to:
/// **'Retry'**
String get publicProfileRetry ;
/// No description provided for @profileProblemHandleInvalid.
///
/// In en, this message translates to:
/// **'A handle is 1 to 30 letters (a to z), digits or underscores.'**
String get profileProblemHandleInvalid ;
/// No description provided for @profileProblemHandleReserved.
///
/// In en, this message translates to:
/// **'This handle is kept for the instance.'**
String get profileProblemHandleReserved ;
/// No description provided for @profileProblemHandleTaken.
///
/// In en, this message translates to:
/// **'Someone else has this handle.'**
String get profileProblemHandleTaken ;
/// No description provided for @profileProblemPublished.
///
/// In en, this message translates to:
/// **'You have published: your handle can\'t change, nor your profile go.'**
String get profileProblemPublished ;
/// No description provided for @profileProblemDisplayNameTooLong.
///
/// In en, this message translates to:
/// **'At most 30 characters.'**
String get profileProblemDisplayNameTooLong ;
/// No description provided for @profileProblemBioTooLong.
///
/// In en, this message translates to:
/// **'At most 500 characters.'**
String get profileProblemBioTooLong ;
/// No description provided for @profileProblemTooManyLinks.
///
/// In en, this message translates to:
/// **'At most 4 links.'**
String get profileProblemTooManyLinks ;
/// No description provided for @profileProblemLinkInvalid.
///
/// In en, this message translates to:
/// **'Links start with https:// or http://.'**
String get profileProblemLinkInvalid ;
/// No description provided for @profileProblemAvatarInvalid.
///
/// In en, this message translates to:
/// **'This picture can\'t be used: choose a PNG, JPEG, WebP or GIF image.'**
String get profileProblemAvatarInvalid ;
feat(profile): profile pages on the account domain, and links verified by rel="me"
A profile now has a page on the web, at the root of the account domain
(https://halkyon.eu/@raisondetredev where the operator set ACCOUNT_DOMAIN,
else on the instance's web server), so the address belongs to the
identity rather than to Learn and stays when other services share it
(ADR 0011). The page shows what the app's profile sheet shows, in English
or German, without JavaScript or cookies, asks search engines not to list
it, marks every link rel="me" so other sites can verify links to it, and
sends a visitor who asked on another host to its own address. Caddy
forwards /@… to the web server, and serves nothing else on the account
domain; compose now passes ACCOUNT_DOMAIN on, which it never did.
A link is verified as Mastodon verifies one: its page links back to the
profile's with rel="me". profile.checkLinks fetches each link's page, at
most ten times an hour, over HTTPS on port 443 and to public addresses
only, connecting to the address it checked and verifying the certificate
for the host, with three redirects, 1 MiB and ten seconds at most, and
keeps what links back on the profile (verifiedLinks, a column of a table
no device syncs). A changed link loses its check, a new handle all of
them. The app shows the page's address to copy, checks a link saved by
itself and on Check links, and marks verified links in the form and in
others' profiles; a check answered after a newer save only adds its
checks.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-15 20:29:30 +02:00
/// No description provided for @profileProblemTooManyLinkChecks.
///
/// In en, this message translates to:
/// **'The links were checked too often. Try again in an hour.'**
String get profileProblemTooManyLinkChecks ;
feat: public profiles, a handle on the account domain with name, bio, links and picture
Stage 1 of the public tier (ADR 0005). A person with an identity has a
public profile: a handle by Mastodon's rules (1 to 30 letters, digits or
underscores, unique whatever its case, a few kept for the instance),
changeable until they first publish, on the instance's account domain
(ACCOUNT_DOMAIN, else the server's host; the instance info names it), so
`raisondetredev@halkyon.eu`; a display name, a bio, up to four links and
an avatar. The table is server only: the app gets a Profile without the
person's id. ProfileEndpoint (identified only) reads, saves and removes
the caller's own; ProfilesEndpoint finds anyone's by handle.
An avatar is a PNG of at most 1 MB that the app draws anew from the
picture chosen, its middle square at 400 pixels, so a photo's metadata
never leaves the device. The server keeps it in the public storage named
by its content, so its URL tells nothing about whose it is, and deletes it
once no profile shows it. Deleting a person deletes their profile and its
picture.
In the app, the Profile tab holds the profile as a form, laid out as the
old app's; anonymous people are told GitHub sign-in gives them one.
Migration 20261009184924904-public-profiles creates public_profile with a
unique index on the person and one on the handle in lower case; it touches
no other table, and the old server never sees it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MMm7usAo6q9ZZRoPkWjoxi
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-09 20:55:16 +02:00
/// No description provided for @publicProfilePicture.
///
/// In en, this message translates to:
/// **'Picture'**
String get publicProfilePicture ;
/// No description provided for @publicProfileChoosePicture.
///
/// In en, this message translates to:
/// **'Choose picture'**
String get publicProfileChoosePicture ;
/// No description provided for @publicProfileRemovePicture.
///
/// In en, this message translates to:
/// **'Remove picture'**
String get publicProfileRemovePicture ;
/// No description provided for @publicProfilePictureType.
///
/// In en, this message translates to:
/// **'Images'**
String get publicProfilePictureType ;
/// No description provided for @publicProfilePictureLater.
///
/// In en, this message translates to:
2026-10-12 19:07:42 +02:00
/// **'Choose your handle to add a picture.'**
feat: public profiles, a handle on the account domain with name, bio, links and picture
Stage 1 of the public tier (ADR 0005). A person with an identity has a
public profile: a handle by Mastodon's rules (1 to 30 letters, digits or
underscores, unique whatever its case, a few kept for the instance),
changeable until they first publish, on the instance's account domain
(ACCOUNT_DOMAIN, else the server's host; the instance info names it), so
`raisondetredev@halkyon.eu`; a display name, a bio, up to four links and
an avatar. The table is server only: the app gets a Profile without the
person's id. ProfileEndpoint (identified only) reads, saves and removes
the caller's own; ProfilesEndpoint finds anyone's by handle.
An avatar is a PNG of at most 1 MB that the app draws anew from the
picture chosen, its middle square at 400 pixels, so a photo's metadata
never leaves the device. The server keeps it in the public storage named
by its content, so its URL tells nothing about whose it is, and deletes it
once no profile shows it. Deleting a person deletes their profile and its
picture.
In the app, the Profile tab holds the profile as a form, laid out as the
old app's; anonymous people are told GitHub sign-in gives them one.
Migration 20261009184924904-public-profiles creates public_profile with a
unique index on the person and one on the handle in lower case; it touches
no other table, and the old server never sees it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MMm7usAo6q9ZZRoPkWjoxi
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-09 20:55:16 +02:00
String get publicProfilePictureLater ;
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
/// No description provided for @settingsTitle.
///
/// In en, this message translates to:
/// **'Settings'**
String get settingsTitle ;
/// No description provided for @settingsAccount.
///
/// In en, this message translates to:
/// **'Account'**
String get settingsAccount ;
feat(instances): choose the instance, and keep accounts on several
A new install asks first which instance to keep the person's studysets
on, as Mastodon's apps do: an address, checked to answer as an instance
of Halkyon Learn, or the build's own (learn.halkyon.eu) in one tap.
Nothing is made anywhere before that, not even an anonymous account. An
install from before goes on with the build's instance without asking.
As Moshidon does, the device keeps an account on each instance it was
used with, each signed in with its own folder and session, one in use at
a time. Settings › Switch account lists them: another one switches at
once, anonymous accounts included; Add account chooses another instance;
removing one not in use deletes what the device holds of it. Signing out
ends only the account in use.
The build's own instance keeps its data where installs kept it before
(`people/`, Serverpod's own session key), so they keep everything; other
instances get folders and keys of their own. What the device shares
across instances (its settings, sounds, the Pomodoro timer's place in the
system) is made once and outlives every switch.
The F-Droid listing drops TetheredNet: the app is no longer tied to the
project's instance.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MMm7usAo6q9ZZRoPkWjoxi
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-10 06:00:47 +02:00
/// No description provided for @instanceWelcome.
///
/// In en, this message translates to:
/// **'Welcome to Halkyon Learn'**
String get instanceWelcome ;
feat: a round in the order cards come, shared daily limits, and a start screen
Studying:
- A round is one list in the order its cards come: those done, the one
shown, those to come. A card due again within the day goes back in
after 4 more cards when rated Bad, at the end otherwise, so the pills
above the card follow it; the buttons say "in 4 cards", "at the end",
"now" (round_order.dart).
- The studied studyset's daily limits hold for everything inside it
together, as Anki's do for subdecks: twenty topics start 20 new cards
a day in all, not 20 each.
- The study header's studyset name shows whole on hover or long press.
- The assistant's button sits at the lower left: in the Library across
from Create studyset, in a studyset above its bar, and higher while
studying, above the buttons that answer the card.
A studyset:
- Progress also counts the learning paths' steps done and lists the
PDFs not read to their end, begun first, each opening the reader,
across its topics (MaterialProgressCubit).
- Next up starts with studying for the exam while one is ahead: its
reviews due and the new cards its pace leaves for the day.
- Learning paths tagged with a level count towards its automatic exam
plan and offer it, as cards do.
- A step's first heading equal to its title is dropped on import and
hidden in paths imported before.
Signing out, or a session the server ended, leads to a start screen
(GitHub, email, without an account, another instance) rather than a new
anonymous account; only the very first start makes one by itself.
Settings names the kind of account and its instance.
Also: the Watch screen reads the cards once for held words and their
progress; its ±10 s buttons tick as the navigation bar does; a map card
keeps its view as its caption changes and brings a picked region
towards the middle; an empty or failed library can be pulled to sync.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-17 00:39:34 +02:00
/// No description provided for @startSignedOut.
///
/// In en, this message translates to:
/// **'Signed out'**
String get startSignedOut ;
/// No description provided for @startOn.
///
/// In en, this message translates to:
/// **'Start on {instance}:'**
String startOn ( String instance ) ;
/// No description provided for @startWithoutAccount.
///
/// In en, this message translates to:
/// **'Start without an account'**
String get startWithoutAccount ;
2026-10-19 21:32:14 +02:00
/// No description provided for @startThenAddGitHub.
feat: a round in the order cards come, shared daily limits, and a start screen
Studying:
- A round is one list in the order its cards come: those done, the one
shown, those to come. A card due again within the day goes back in
after 4 more cards when rated Bad, at the end otherwise, so the pills
above the card follow it; the buttons say "in 4 cards", "at the end",
"now" (round_order.dart).
- The studied studyset's daily limits hold for everything inside it
together, as Anki's do for subdecks: twenty topics start 20 new cards
a day in all, not 20 each.
- The study header's studyset name shows whole on hover or long press.
- The assistant's button sits at the lower left: in the Library across
from Create studyset, in a studyset above its bar, and higher while
studying, above the buttons that answer the card.
A studyset:
- Progress also counts the learning paths' steps done and lists the
PDFs not read to their end, begun first, each opening the reader,
across its topics (MaterialProgressCubit).
- Next up starts with studying for the exam while one is ahead: its
reviews due and the new cards its pace leaves for the day.
- Learning paths tagged with a level count towards its automatic exam
plan and offer it, as cards do.
- A step's first heading equal to its title is dropped on import and
hidden in paths imported before.
Signing out, or a session the server ended, leads to a start screen
(GitHub, email, without an account, another instance) rather than a new
anonymous account; only the very first start makes one by itself.
Settings names the kind of account and its instance.
Also: the Watch screen reads the cards once for held words and their
progress; its ±10 s buttons tick as the navigation bar does; a map card
keeps its view as its caption changes and brings a picked region
towards the middle; an empty or failed library can be pulled to sync.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-17 00:39:34 +02:00
///
/// In en, this message translates to:
2026-10-19 21:32:14 +02:00
/// **'Start without an account, then add GitHub in Profile.'**
String get startThenAddGitHub ;
feat: a round in the order cards come, shared daily limits, and a start screen
Studying:
- A round is one list in the order its cards come: those done, the one
shown, those to come. A card due again within the day goes back in
after 4 more cards when rated Bad, at the end otherwise, so the pills
above the card follow it; the buttons say "in 4 cards", "at the end",
"now" (round_order.dart).
- The studied studyset's daily limits hold for everything inside it
together, as Anki's do for subdecks: twenty topics start 20 new cards
a day in all, not 20 each.
- The study header's studyset name shows whole on hover or long press.
- The assistant's button sits at the lower left: in the Library across
from Create studyset, in a studyset above its bar, and higher while
studying, above the buttons that answer the card.
A studyset:
- Progress also counts the learning paths' steps done and lists the
PDFs not read to their end, begun first, each opening the reader,
across its topics (MaterialProgressCubit).
- Next up starts with studying for the exam while one is ahead: its
reviews due and the new cards its pace leaves for the day.
- Learning paths tagged with a level count towards its automatic exam
plan and offer it, as cards do.
- A step's first heading equal to its title is dropped on import and
hidden in paths imported before.
Signing out, or a session the server ended, leads to a start screen
(GitHub, email, without an account, another instance) rather than a new
anonymous account; only the very first start makes one by itself.
Settings names the kind of account and its instance.
Also: the Watch screen reads the cards once for held words and their
progress; its ±10 s buttons tick as the navigation bar does; a map card
keeps its view as its caption changes and brings a picked region
towards the middle; an empty or failed library can be pulled to sync.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-17 00:39:34 +02:00
/// No description provided for @startOtherInstance.
///
/// In en, this message translates to:
/// **'Use another instance'**
String get startOtherInstance ;
feat(instances): choose the instance, and keep accounts on several
A new install asks first which instance to keep the person's studysets
on, as Mastodon's apps do: an address, checked to answer as an instance
of Halkyon Learn, or the build's own (learn.halkyon.eu) in one tap.
Nothing is made anywhere before that, not even an anonymous account. An
install from before goes on with the build's instance without asking.
As Moshidon does, the device keeps an account on each instance it was
used with, each signed in with its own folder and session, one in use at
a time. Settings › Switch account lists them: another one switches at
once, anonymous accounts included; Add account chooses another instance;
removing one not in use deletes what the device holds of it. Signing out
ends only the account in use.
The build's own instance keeps its data where installs kept it before
(`people/`, Serverpod's own session key), so they keep everything; other
instances get folders and keys of their own. What the device shares
across instances (its settings, sounds, the Pomodoro timer's place in the
system) is made once and outlives every switch.
The F-Droid listing drops TetheredNet: the app is no longer tied to the
project's instance.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MMm7usAo6q9ZZRoPkWjoxi
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-10 06:00:47 +02:00
/// No description provided for @instanceFirstStartSubtitle.
///
/// In en, this message translates to:
/// **'Choose the instance to keep your studysets on. Someone runs each one; you can add others later.'**
String get instanceFirstStartSubtitle ;
/// No description provided for @instanceAddress.
///
/// In en, this message translates to:
/// **'Instance address'**
String get instanceAddress ;
/// No description provided for @instanceUseBuiltIn.
///
/// In en, this message translates to:
/// **'Use {name}'**
String instanceUseBuiltIn ( String name ) ;
2026-10-18 20:33:33 +02:00
/// No description provided for @instanceBuiltInTagline.
///
/// In en, this message translates to:
/// **'Comes with the app'**
String get instanceBuiltInTagline ;
/// No description provided for @instanceAnother.
///
/// In en, this message translates to:
/// **'Another instance'**
String get instanceAnother ;
/// No description provided for @instanceAnotherTagline.
///
/// In en, this message translates to:
/// **'Enter its address'**
String get instanceAnotherTagline ;
2026-10-18 20:35:21 +02:00
/// No description provided for @instanceDataAndBackupsIn.
///
/// In en, this message translates to:
/// **'Data and backups: {place}'**
String instanceDataAndBackupsIn ( String place ) ;
/// No description provided for @instanceDataIn.
///
/// In en, this message translates to:
/// **'Data: {place}'**
String instanceDataIn ( String place ) ;
/// No description provided for @instanceBackupsIn.
///
/// In en, this message translates to:
/// **'Backups: {place}'**
String instanceBackupsIn ( String place ) ;
/// No description provided for @instanceNoBackups.
///
/// In en, this message translates to:
/// **'No backups'**
String get instanceNoBackups ;
/// No description provided for @instanceEuropeanUnion.
///
/// In en, this message translates to:
/// **'European Union'**
String get instanceEuropeanUnion ;
feat(instances): choose the instance, and keep accounts on several
A new install asks first which instance to keep the person's studysets
on, as Mastodon's apps do: an address, checked to answer as an instance
of Halkyon Learn, or the build's own (learn.halkyon.eu) in one tap.
Nothing is made anywhere before that, not even an anonymous account. An
install from before goes on with the build's instance without asking.
As Moshidon does, the device keeps an account on each instance it was
used with, each signed in with its own folder and session, one in use at
a time. Settings › Switch account lists them: another one switches at
once, anonymous accounts included; Add account chooses another instance;
removing one not in use deletes what the device holds of it. Signing out
ends only the account in use.
The build's own instance keeps its data where installs kept it before
(`people/`, Serverpod's own session key), so they keep everything; other
instances get folders and keys of their own. What the device shares
across instances (its settings, sounds, the Pomodoro timer's place in the
system) is made once and outlives every switch.
The F-Droid listing drops TetheredNet: the app is no longer tied to the
project's instance.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MMm7usAo6q9ZZRoPkWjoxi
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-10 06:00:47 +02:00
/// No description provided for @instanceContinue.
///
/// In en, this message translates to:
/// **'Continue'**
String get instanceContinue ;
/// No description provided for @instanceAddressRequired.
///
/// In en, this message translates to:
/// **'Enter the address of an instance.'**
String get instanceAddressRequired ;
/// No description provided for @instanceAddressInvalid.
///
/// In en, this message translates to:
/// **'This isn\'t an address.'**
String get instanceAddressInvalid ;
/// No description provided for @instanceUnreachable.
///
/// In en, this message translates to:
/// **'No instance of Halkyon Learn answers at {name}.'**
String instanceUnreachable ( String name ) ;
/// No description provided for @instanceOpenFailed.
///
/// In en, this message translates to:
/// **'Couldn\'t open the app on {name}.'**
String instanceOpenFailed ( String name ) ;
feat: a round in the order cards come, shared daily limits, and a start screen
Studying:
- A round is one list in the order its cards come: those done, the one
shown, those to come. A card due again within the day goes back in
after 4 more cards when rated Bad, at the end otherwise, so the pills
above the card follow it; the buttons say "in 4 cards", "at the end",
"now" (round_order.dart).
- The studied studyset's daily limits hold for everything inside it
together, as Anki's do for subdecks: twenty topics start 20 new cards
a day in all, not 20 each.
- The study header's studyset name shows whole on hover or long press.
- The assistant's button sits at the lower left: in the Library across
from Create studyset, in a studyset above its bar, and higher while
studying, above the buttons that answer the card.
A studyset:
- Progress also counts the learning paths' steps done and lists the
PDFs not read to their end, begun first, each opening the reader,
across its topics (MaterialProgressCubit).
- Next up starts with studying for the exam while one is ahead: its
reviews due and the new cards its pace leaves for the day.
- Learning paths tagged with a level count towards its automatic exam
plan and offer it, as cards do.
- A step's first heading equal to its title is dropped on import and
hidden in paths imported before.
Signing out, or a session the server ended, leads to a start screen
(GitHub, email, without an account, another instance) rather than a new
anonymous account; only the very first start makes one by itself.
Settings names the kind of account and its instance.
Also: the Watch screen reads the cards once for held words and their
progress; its ±10 s buttons tick as the navigation bar does; a map card
keeps its view as its caption changes and brings a picked region
towards the middle; an empty or failed library can be pulled to sync.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-17 00:39:34 +02:00
/// No description provided for @accountAnonymous.
///
/// In en, this message translates to:
/// **'Anonymous'**
String get accountAnonymous ;
/// No description provided for @accountSignedIn.
///
/// In en, this message translates to:
/// **'Signed in'**
String get accountSignedIn ;
feat(instances): choose the instance, and keep accounts on several
A new install asks first which instance to keep the person's studysets
on, as Mastodon's apps do: an address, checked to answer as an instance
of Halkyon Learn, or the build's own (learn.halkyon.eu) in one tap.
Nothing is made anywhere before that, not even an anonymous account. An
install from before goes on with the build's instance without asking.
As Moshidon does, the device keeps an account on each instance it was
used with, each signed in with its own folder and session, one in use at
a time. Settings › Switch account lists them: another one switches at
once, anonymous accounts included; Add account chooses another instance;
removing one not in use deletes what the device holds of it. Signing out
ends only the account in use.
The build's own instance keeps its data where installs kept it before
(`people/`, Serverpod's own session key), so they keep everything; other
instances get folders and keys of their own. What the device shares
across instances (its settings, sounds, the Pomodoro timer's place in the
system) is made once and outlives every switch.
The F-Droid listing drops TetheredNet: the app is no longer tied to the
project's instance.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MMm7usAo6q9ZZRoPkWjoxi
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-10 06:00:47 +02:00
/// No description provided for @accountsTitle.
///
/// In en, this message translates to:
/// **'Accounts'**
String get accountsTitle ;
/// No description provided for @accountAdd.
///
/// In en, this message translates to:
/// **'Add account'**
String get accountAdd ;
/// No description provided for @accountAddSubtitle.
///
/// In en, this message translates to:
/// **'Choose the instance for the account to add.'**
String get accountAddSubtitle ;
/// No description provided for @accountRemoveTooltip.
///
/// In en, this message translates to:
/// **'Remove account'**
String get accountRemoveTooltip ;
/// No description provided for @accountRemoveTitle.
///
/// In en, this message translates to:
/// **'Remove account?'**
String get accountRemoveTitle ;
/// No description provided for @accountRemoveMessage.
///
/// In en, this message translates to:
feat(auth): sign in with an email address, keeping only a keyed hash of it
People without GitHub can now keep an anonymous account, and sign in on
another device, with an email address and a code mailed to it each time;
no password. The instance keeps only HMAC-SHA256 of the address, keyed
with its emailSignInPepper (ADR 0010): from what someone types it finds
the person and tells whether an address is free, it mails only an address
someone has just typed, and it can't write to anybody otherwise or show
anybody their address. The device remembers the address it signed in
with, in the person's folder, to show it.
It works as GitHub does. A code for a new address makes it the signed-in
person's, who becomes identified and keeps everything. A code for another
person's address gives a switch token, which signs in as them without a
session; a device holding studysets asks first. Asking for a code answers
alike for every address. Codes are eight Crockford characters, valid 15
minutes and three tries (Serverpod's code challenges); three codes an
hour per address, ten per network. In development the code goes to the
server's log; elsewhere there is no email sign-in until a mail service
is set up (instance.info().emailSignIn).
Someone identified may add the other way in: the Profile tab lists how
they sign in and adds GitHub or an address. They are never switched to
another account: an address or GitHub account that is another's is said
to be so. Switching now deletes the account left behind only when it was
anonymous; it deleted any, which only anonymous people could reach so far.
The migration adds email_identity and email_code_request with their
indexes and foreign keys to Serverpod's challenges; nothing else changes.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-15 19:42:21 +02:00
/// **'Removes the account on {name} from this device, with all the device holds of it. What it synced stays on {name}: sign in there again to have it. An anonymous account can\'t be signed into again.'**
feat(instances): choose the instance, and keep accounts on several
A new install asks first which instance to keep the person's studysets
on, as Mastodon's apps do: an address, checked to answer as an instance
of Halkyon Learn, or the build's own (learn.halkyon.eu) in one tap.
Nothing is made anywhere before that, not even an anonymous account. An
install from before goes on with the build's instance without asking.
As Moshidon does, the device keeps an account on each instance it was
used with, each signed in with its own folder and session, one in use at
a time. Settings › Switch account lists them: another one switches at
once, anonymous accounts included; Add account chooses another instance;
removing one not in use deletes what the device holds of it. Signing out
ends only the account in use.
The build's own instance keeps its data where installs kept it before
(`people/`, Serverpod's own session key), so they keep everything; other
instances get folders and keys of their own. What the device shares
across instances (its settings, sounds, the Pomodoro timer's place in the
system) is made once and outlives every switch.
The F-Droid listing drops TetheredNet: the app is no longer tied to the
project's instance.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MMm7usAo6q9ZZRoPkWjoxi
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-10 06:00:47 +02:00
String accountRemoveMessage ( String name ) ;
/// No description provided for @accountRemoveConfirm.
///
/// In en, this message translates to:
/// **'Remove'**
String get accountRemoveConfirm ;
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
/// No description provided for @settingsSignOutAnonymousMessage.
///
/// In en, this message translates to:
/// **'This account can\'t be signed into again, so signing out deletes it with its progress.'**
String get settingsSignOutAnonymousMessage ;
/// No description provided for @settingsDeleteMessage.
///
/// In en, this message translates to:
/// **'Everything you have, your studysets, cards and reviews, is deleted on the server and on all your devices. This can\'t be undone.'**
String get settingsDeleteMessage ;
/// No description provided for @settingsAbout.
///
/// In en, this message translates to:
/// **'About Halkyon Learn'**
String get settingsAbout ;
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 02:01:01 +02:00
/// No description provided for @aboutLegalese.
///
/// In en, this message translates to:
/// **'Free software: the app is under the GNU Affero General Public License, version 3 or any later version, the server under the Server Side Public License, version 1.'**
String get aboutLegalese ;
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 01:43:28 +02:00
/// No description provided for @settingsAppearance.
///
/// In en, this message translates to:
/// **'Appearance'**
String get settingsAppearance ;
/// No description provided for @settingsAppearanceDevice.
///
/// In en, this message translates to:
/// **'Device theme'**
String get settingsAppearanceDevice ;
/// No description provided for @settingsAppearanceLight.
///
/// In en, this message translates to:
/// **'Light'**
String get settingsAppearanceLight ;
/// No description provided for @settingsAppearanceDark.
///
/// In en, this message translates to:
/// **'Dark'**
String get settingsAppearanceDark ;
/// No description provided for @settingsLanguage.
///
/// In en, this message translates to:
/// **'Language'**
String get settingsLanguage ;
/// No description provided for @settingsLanguageDevice.
///
/// In en, this message translates to:
/// **'Device language'**
String get settingsLanguageDevice ;
feat(app): cite sources in APA, MLA or Chicago, and give studysets a bibliography
A source is now shared by learning paths and studysets (core/citation/):
authors as full names, year, title, what it is a part of, publisher,
ISBN, link, and a citation kept as a file brought it. formatCitation
cites it in APA 7, MLA 9 or Chicago 18: it inverts and abbreviates
names itself (particles, suffixes, names written as already cited stay),
sets a work of its own in italics and a part of something apart from
the whole, and writes a missing year as "n.d." in APA and Chicago.
- Settings: a Citations card per device, with a preview and a link to
the style's own guide.
- Studysets: a Bibliography button opens the works cited, to read and,
behind its pencil, to change (studyset schema 4).
- Steps: sources edited through the same sheet, opened from an
"N sources" button in the runner (step schema 2; schema 1 reads on).
- Path import: steps' sources, text kept as written, the old format's
"citation" read as what a source is part of.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-07 17:07:04 +02:00
/// No description provided for @settingsCitations.
///
/// In en, this message translates to:
/// **'Citations'**
String get settingsCitations ;
/// No description provided for @settingsCitationsDescription.
///
/// In en, this message translates to:
/// **'How sources and studysets\' bibliographies are cited on this device. Type authors\' full names: each style abbreviates and orders them itself.'**
String get settingsCitationsDescription ;
/// No description provided for @settingsCitationStyle.
///
/// In en, this message translates to:
/// **'Style'**
String get settingsCitationStyle ;
/// No description provided for @settingsCitationStyleApa.
///
/// In en, this message translates to:
/// **'APA (7th edition)'**
String get settingsCitationStyleApa ;
/// No description provided for @settingsCitationStyleMla.
///
/// In en, this message translates to:
/// **'MLA (9th edition)'**
String get settingsCitationStyleMla ;
/// No description provided for @settingsCitationStyleChicago.
///
/// In en, this message translates to:
/// **'Chicago (18th edition)'**
String get settingsCitationStyleChicago ;
/// No description provided for @settingsCitationPreview.
///
/// In en, this message translates to:
/// **'Preview'**
String get settingsCitationPreview ;
/// No description provided for @settingsCitationGuide.
///
/// In en, this message translates to:
/// **'Official guide'**
String get settingsCitationGuide ;
2026-10-07 21:10:33 +02:00
/// No description provided for @settingsStudy.
///
/// In en, this message translates to:
/// **'Study'**
String get settingsStudy ;
/// No description provided for @settingsStudyPomodoroTitle.
///
/// In en, this message translates to:
/// **'Show the Pomodoro timer while studying'**
String get settingsStudyPomodoroTitle ;
/// No description provided for @settingsStudyPomodoroSubtitle.
///
/// In en, this message translates to:
/// **'Show the focus / break countdown inside the study session, so you can see the time left without opening the notification.'**
String get settingsStudyPomodoroSubtitle ;
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 01:43:28 +02:00
/// No description provided for @settingsVersion.
///
/// In en, this message translates to:
/// **'Version {version} (build {build})'**
String settingsVersion ( String version , String build ) ;
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-03 23:26:47 +02:00
/// No description provided for @syncTitle.
///
/// In en, this message translates to:
/// **'Sync'**
String get syncTitle ;
/// No description provided for @syncStateSyncing.
///
/// In en, this message translates to:
/// **'Syncing…'**
String get syncStateSyncing ;
/// No description provided for @syncStateSynced.
///
/// In en, this message translates to:
/// **'Everything synced'**
String get syncStateSynced ;
/// No description provided for @syncStateNotYet.
///
/// In en, this message translates to:
/// **'Not synced yet'**
String get syncStateNotYet ;
/// No description provided for @syncStateFailing.
///
/// In en, this message translates to:
/// **'Sync is failing'**
String get syncStateFailing ;
/// No description provided for @syncStateMismatch.
///
/// In en, this message translates to:
/// **'This app can\'t sync with {server}'**
String syncStateMismatch ( String server ) ;
/// No description provided for @syncMismatchDetail.
///
/// In en, this message translates to:
/// **'Update the app. If it is up to date, the server needs an update. What you do meanwhile stays on this device and syncs afterwards.'**
String get syncMismatchDetail ;
/// No description provided for @syncLastCompleted.
///
/// In en, this message translates to:
/// **'Last completed at {time}'**
String syncLastCompleted ( String time ) ;
/// No description provided for @syncNoRunYet.
///
/// In en, this message translates to:
/// **'No sync has completed since the app started'**
String get syncNoRunYet ;
/// No description provided for @syncLastFailed.
///
/// In en, this message translates to:
/// **'Last attempt failed at {time}'**
String syncLastFailed ( String time ) ;
/// No description provided for @syncNow.
///
/// In en, this message translates to:
/// **'Sync now'**
String get syncNow ;
2026-10-10 05:29:43 +02:00
/// No description provided for @syncPending.
///
/// In en, this message translates to:
/// **'{count, plural, =1{1 change waits to sync} other{{count} changes wait to sync}}'**
String syncPending ( int count ) ;
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-03 23:26:47 +02:00
/// No description provided for @syncTroubleLabel.
///
/// In en, this message translates to:
/// **'Sync needs attention'**
String get syncTroubleLabel ;
/// No description provided for @syncMismatchBanner.
///
/// In en, this message translates to:
/// **'This app can\'t sync with {server} any more. Update it to keep syncing.'**
String syncMismatchBanner ( String server ) ;
/// No description provided for @syncMismatchMore.
///
/// In en, this message translates to:
/// **'More'**
String get syncMismatchMore ;
2026-10-01 16:57:57 +02:00
/// No description provided for @tagsReadFailed.
///
/// In en, this message translates to:
/// **'Couldn\'t read the tags.'**
String get tagsReadFailed ;
fix(app): Reorder among older items, export and text import that hold up
- Reorder: rows from before an order existed all share one position, and
one moved among them landed at their end; the run it goes into now
gets positions of its own (placeAmong), for notes, files, paths,
steps and sub-studysets. Rows whose deletion can still be undone count
for nothing, as the list leaves them out, and a dropped row takes its
place at once instead of snapping back until the order is read again.
- Export: two studysets inside each other (moved so on two devices
offline) no longer make it run forever; links to files whose names
hold #, % or brackets work; PDFs, images and sounds go into the zip
without being compressed again; any error says the export failed.
- Text import: a real CSV reader (quoted fields over several lines, a
back over several quoted fields, a stray quote spoiling only its own
line), the separator guessed from the text (semicolons from German
spreadsheets), and files in UTF-16 or Windows-1252 read as meant.
- A tab whose notes, paths or files couldn't be read says so rather than
"No … yet"; an Anki import from the Library that fails leaves no empty
studyset behind.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MMm7usAo6q9ZZRoPkWjoxi
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-09 13:02:32 +02:00
/// No description provided for @notesReadFailed.
///
/// In en, this message translates to:
/// **'Couldn\'t read the notes.'**
String get notesReadFailed ;
/// No description provided for @pathsReadFailed.
///
/// In en, this message translates to:
/// **'Couldn\'t read the learning paths.'**
String get pathsReadFailed ;
/// No description provided for @filesReadFailed.
///
/// In en, this message translates to:
/// **'Couldn\'t read the files.'**
String get filesReadFailed ;
2026-10-01 16:57:57 +02:00
/// No description provided for @tagPickerTitle.
///
/// In en, this message translates to:
/// **'Tags'**
String get tagPickerTitle ;
/// No description provided for @tagPickerManage.
///
/// In en, this message translates to:
/// **'Manage tags'**
String get tagPickerManage ;
/// No description provided for @tagPickerSearchLabel.
///
/// In en, this message translates to:
/// **'Search or create a tag'**
String get tagPickerSearchLabel ;
/// No description provided for @tagPickerCreate.
///
/// In en, this message translates to:
/// **'Create \"{name}\"'**
String tagPickerCreate ( String name ) ;
/// No description provided for @tagSheetCreateTitle.
///
/// In en, this message translates to:
/// **'Create tag'**
String get tagSheetCreateTitle ;
/// No description provided for @tagSheetEditTitle.
///
/// In en, this message translates to:
/// **'Edit tag'**
String get tagSheetEditTitle ;
/// No description provided for @tagSheetNameLabel.
///
/// In en, this message translates to:
/// **'Name'**
String get tagSheetNameLabel ;
/// No description provided for @tagSheetNameHelper.
///
/// In en, this message translates to:
feat(app): tags keep their case; dictionary tags read JLPT-N5, Noun
A tag is still its name, but no longer in lowercase: the name keeps the
case it was written in, and names that differ only in case are one tag
(ADR 0004, rule 2, amended). Two such tags from two devices fold into the
one with the smaller id, its case with it. Tags stored before stay
lowercase until renamed.
Words from the dictionary are tagged JLPT-N5 to JLPT-N1 and with their
word class (Noun, Verb, i-Adjective, na-Adjective, ...). Anki tags and
PDF chapter tags arrive as written.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MMm7usAo6q9ZZRoPkWjoxi
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-09 10:48:06 +02:00
/// **'Capitals don\'t make another tag: \'Noun\' and \'noun\' are one.'**
2026-10-01 16:57:57 +02:00
String get tagSheetNameHelper ;
/// No description provided for @tagSheetNameExists.
///
/// In en, this message translates to:
/// **'The tag \'{name}\' exists already.'**
String tagSheetNameExists ( String name ) ;
/// No description provided for @tagSheetNameMerges.
///
/// In en, this message translates to:
/// **'There is a tag \'{name}\' already: saving merges this one into it.'**
String tagSheetNameMerges ( String name ) ;
/// No description provided for @tagSheetColor.
///
/// In en, this message translates to:
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 02:01:01 +02:00
/// **'Colour'**
2026-10-01 16:57:57 +02:00
String get tagSheetColor ;
/// No description provided for @tagSheetCreate.
///
/// In en, this message translates to:
/// **'Create'**
String get tagSheetCreate ;
/// No description provided for @tagSheetSave.
///
/// In en, this message translates to:
/// **'Save'**
String get tagSheetSave ;
/// No description provided for @tagManageTitle.
///
/// In en, this message translates to:
/// **'Manage tags'**
String get tagManageTitle ;
/// No description provided for @tagManageEmpty.
///
/// In en, this message translates to:
/// **'No tags yet. Add one below.'**
String get tagManageEmpty ;
/// No description provided for @tagManageAdd.
///
/// In en, this message translates to:
/// **'Add tag'**
String get tagManageAdd ;
/// A tag in the list of tags, with how many flashcards have it.
///
/// In en, this message translates to:
/// **'{name} ({count})'**
String tagManageRow ( String name , int count ) ;
/// No description provided for @tagManageEdit.
///
/// In en, this message translates to:
/// **'Edit tag'**
String get tagManageEdit ;
feat(tag): one switch hides the tags from Jellyfin while studying
Every tag the app makes for where words were met on Jellyfin (the show
and the episode, the artist and the song) now says so (`origin` in tag
schema 2, written only for such a tag), in place of a tag hidden one by
one. Manage tags, where there are such tags, has one switch, Hide tags
from Jellyfin while studying (`hideJellyfinTags`, settings schema 9,
following the person): with it on, cards being studied leave them out of
their row at once, and Tags and Manage tags list them faint. They still
filter and narrow a session. The per-tag eye button is gone.
A word added while watching no longer takes the line it was met in as
its first hint: its hints are the dictionary's examples alone. Tatoeba's
sentences are checked, a subtitle line may be wrong or mistyped, a word
met again in a better line wouldn't get it, and every episode's line
would pile up on a common word.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-13 20:52:22 +02:00
/// No description provided for @tagManageHideJellyfin.
2026-10-13 19:27:11 +02:00
///
/// In en, this message translates to:
feat(tag): one switch hides the tags from Jellyfin while studying
Every tag the app makes for where words were met on Jellyfin (the show
and the episode, the artist and the song) now says so (`origin` in tag
schema 2, written only for such a tag), in place of a tag hidden one by
one. Manage tags, where there are such tags, has one switch, Hide tags
from Jellyfin while studying (`hideJellyfinTags`, settings schema 9,
following the person): with it on, cards being studied leave them out of
their row at once, and Tags and Manage tags list them faint. They still
filter and narrow a session. The per-tag eye button is gone.
A word added while watching no longer takes the line it was met in as
its first hint: its hints are the dictionary's examples alone. Tatoeba's
sentences are checked, a subtitle line may be wrong or mistyped, a word
met again in a better line wouldn't get it, and every episode's line
would pile up on a common word.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-13 20:52:22 +02:00
/// **'Hide tags from Jellyfin while studying'**
String get tagManageHideJellyfin ;
2026-10-13 19:27:11 +02:00
feat(tag): one switch hides the tags from Jellyfin while studying
Every tag the app makes for where words were met on Jellyfin (the show
and the episode, the artist and the song) now says so (`origin` in tag
schema 2, written only for such a tag), in place of a tag hidden one by
one. Manage tags, where there are such tags, has one switch, Hide tags
from Jellyfin while studying (`hideJellyfinTags`, settings schema 9,
following the person): with it on, cards being studied leave them out of
their row at once, and Tags and Manage tags list them faint. They still
filter and narrow a session. The per-tag eye button is gone.
A word added while watching no longer takes the line it was met in as
its first hint: its hints are the dictionary's examples alone. Tatoeba's
sentences are checked, a subtitle line may be wrong or mistyped, a word
met again in a better line wouldn't get it, and every episode's line
would pile up on a common word.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-13 20:52:22 +02:00
/// No description provided for @tagManageHideJellyfinHint.
2026-10-13 19:27:11 +02:00
///
/// In en, this message translates to:
feat(tag): one switch hides the tags from Jellyfin while studying
Every tag the app makes for where words were met on Jellyfin (the show
and the episode, the artist and the song) now says so (`origin` in tag
schema 2, written only for such a tag), in place of a tag hidden one by
one. Manage tags, where there are such tags, has one switch, Hide tags
from Jellyfin while studying (`hideJellyfinTags`, settings schema 9,
following the person): with it on, cards being studied leave them out of
their row at once, and Tags and Manage tags list them faint. They still
filter and narrow a session. The per-tag eye button is gone.
A word added while watching no longer takes the line it was met in as
its first hint: its hints are the dictionary's examples alone. Tatoeba's
sentences are checked, a subtitle line may be wrong or mistyped, a word
met again in a better line wouldn't get it, and every episode's line
would pile up on a common word.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-13 20:52:22 +02:00
/// **'The shows, episodes, artists and songs words were met in. They stay on the cards, to filter and study by.'**
String get tagManageHideJellyfinHint ;
2026-10-13 19:27:11 +02:00
2026-10-01 16:57:57 +02:00
/// No description provided for @tagManageDelete.
///
/// In en, this message translates to:
/// **'Delete tag'**
String get tagManageDelete ;
/// No description provided for @tagDeleteTitle.
///
/// In en, this message translates to:
/// **'Delete the tag \'{name}\'?'**
String tagDeleteTitle ( String name ) ;
/// No description provided for @tagDeleteMessage.
///
/// In en, this message translates to:
/// **'{count, plural, =0{It isn\'t on any flashcards.} =1{It is removed from 1 flashcard, on all your devices.} other{It is removed from {count} flashcards, on all your devices.}}'**
String tagDeleteMessage ( int count ) ;
/// No description provided for @tagDeleteConfirm.
///
/// In en, this message translates to:
/// **'Delete'**
String get tagDeleteConfirm ;
/// No description provided for @tagDeleted.
///
/// In en, this message translates to:
/// **'Deleted the tag \'{name}\''**
String tagDeleted ( String name ) ;
2026-10-01 17:30:24 +02:00
/// No description provided for @studySettingsTitle.
///
/// In en, this message translates to:
/// **'Session settings'**
String get studySettingsTitle ;
/// No description provided for @studySettingsFilterByTags.
///
/// In en, this message translates to:
/// **'Filter by tags'**
String get studySettingsFilterByTags ;
/// No description provided for @studySettingsNoTags.
///
/// In en, this message translates to:
feat(app): tag, edit, delete and swipe cards while studying, with tidier session sheets
- Above every studied card (but a matching one): the PDF it came from,
its tags on one line ("+2 tags" for the rest) or "Add tags", and a
menu to tag, edit or delete it; the session follows (an edited card
shows as saved, a deleted one leaves the round).
- On phones, a front/back or vocabulary card swiped right is rated
Good, left Bad; it tilts over the colour it heads for, and springs
back when let go early.
- The session's settings: "Settings", a 48 dp Save that warns mid-round
("Heads up!"), steppers in an outlined box ("20 / day", "5 / 50"),
the order in an outlined dropdown, the chosen tags with the rest
behind "Filter by tags" / "+3 more".
- A studyset's study options: Save above Reset to defaults, the
description small, reviews a day in steps of 25.
- One stepper look everywhere (study, Pomodoro profiles), its buttons
"Decrease" and "Increase".
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-07 18:13:32 +02:00
/// **'No content tags yet. Add tags to your cards to filter them here.'**
2026-10-01 17:30:24 +02:00
String get studySettingsNoTags ;
/// No description provided for @studySettingsReset.
///
/// In en, this message translates to:
/// **'Reset'**
String get studySettingsReset ;
/// No description provided for @studySettingsSave.
///
/// In en, this message translates to:
/// **'Save'**
String get studySettingsSave ;
feat(app): tag, edit, delete and swipe cards while studying, with tidier session sheets
- Above every studied card (but a matching one): the PDF it came from,
its tags on one line ("+2 tags" for the rest) or "Add tags", and a
menu to tag, edit or delete it; the session follows (an edited card
shows as saved, a deleted one leaves the round).
- On phones, a front/back or vocabulary card swiped right is rated
Good, left Bad; it tilts over the colour it heads for, and springs
back when let go early.
- The session's settings: "Settings", a 48 dp Save that warns mid-round
("Heads up!"), steppers in an outlined box ("20 / day", "5 / 50"),
the order in an outlined dropdown, the chosen tags with the rest
behind "Filter by tags" / "+3 more".
- A studyset's study options: Save above Reset to defaults, the
description small, reviews a day in steps of 25.
- One stepper look everywhere (study, Pomodoro profiles), its buttons
"Decrease" and "Increase".
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-07 18:13:32 +02:00
/// No description provided for @studySettingsHeadsUpTitle.
///
/// In en, this message translates to:
/// **'Heads up!'**
String get studySettingsHeadsUpTitle ;
/// No description provided for @studySettingsHeadsUpBody.
///
/// In en, this message translates to:
/// **'The changes you’ ve made will reload the session, and you’ ll lose your current progress.'**
String get studySettingsHeadsUpBody ;
/// No description provided for @studySettingsApplyChanges.
///
/// In en, this message translates to:
/// **'Apply changes'**
String get studySettingsApplyChanges ;
/// No description provided for @studySettingsPerDay.
///
/// In en, this message translates to:
/// **' / day'**
String get studySettingsPerDay ;
/// No description provided for @studySettingsMoreTags.
///
/// In en, this message translates to:
/// **'+{count} more'**
String studySettingsMoreTags ( int count ) ;
2026-10-01 16:57:57 +02:00
/// No description provided for @cardTagsLabel.
///
/// In en, this message translates to:
/// **'Tags'**
String get cardTagsLabel ;
/// No description provided for @cardTagsAdd.
///
/// In en, this message translates to:
/// **'Add tags'**
String get cardTagsAdd ;
/// No description provided for @cardTagsEdit.
///
/// In en, this message translates to:
/// **'Edit tags'**
String get cardTagsEdit ;
feat(app): tag, edit, delete and swipe cards while studying, with tidier session sheets
- Above every studied card (but a matching one): the PDF it came from,
its tags on one line ("+2 tags" for the rest) or "Add tags", and a
menu to tag, edit or delete it; the session follows (an edited card
shows as saved, a deleted one leaves the round).
- On phones, a front/back or vocabulary card swiped right is rated
Good, left Bad; it tilts over the colour it heads for, and springs
back when let go early.
- The session's settings: "Settings", a 48 dp Save that warns mid-round
("Heads up!"), steppers in an outlined box ("20 / day", "5 / 50"),
the order in an outlined dropdown, the chosen tags with the rest
behind "Filter by tags" / "+3 more".
- A studyset's study options: Save above Reset to defaults, the
description small, reviews a day in steps of 25.
- One stepper look everywhere (study, Pomodoro profiles), its buttons
"Decrease" and "Increase".
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-07 18:13:32 +02:00
/// No description provided for @tagChipsOverflow.
///
/// In en, this message translates to:
/// **'+{count, plural, =1{1 tag} other{{count} tags}}'**
String tagChipsOverflow ( int count ) ;
feat(app): give cards hints and an explanation
A card of any type can carry up to six hints and an explanation, both
rich text. They are written in the composer, added from its menu (or
Ctrl+Alt+H / Ctrl+Alt+E), each removed on its own; blank ones aren't
kept. While studying, "Hints" under the question opens them one at a
time, and "Explanation" comes with the answer.
Card schema 5 holds them (`hints`, `explanation`), and with them what a
card needs next: a report that something on it is wrong (`report`:
category, note, when) and a word's senses with their grammar codes
(`senses`; a word's meanings then come from them, and its first sense
says whether it is usually written in kana). Each merges as a field of
its own.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-07 21:24:41 +02:00
/// No description provided for @cardHints.
///
/// In en, this message translates to:
/// **'Hints'**
String get cardHints ;
/// No description provided for @cardHint.
///
/// In en, this message translates to:
/// **'Hint {number}'**
String cardHint ( int number ) ;
/// No description provided for @cardAddHint.
///
/// In en, this message translates to:
/// **'Add hint'**
String get cardAddHint ;
/// No description provided for @cardRemoveHint.
///
/// In en, this message translates to:
/// **'Remove hint'**
String get cardRemoveHint ;
/// No description provided for @cardExplanation.
///
/// In en, this message translates to:
/// **'Explanation'**
String get cardExplanation ;
/// No description provided for @cardAddExplanation.
///
/// In en, this message translates to:
/// **'Add explanation'**
String get cardAddExplanation ;
/// No description provided for @cardRemoveExplanation.
///
/// In en, this message translates to:
/// **'Remove explanation'**
String get cardRemoveExplanation ;
/// No description provided for @cardHintsSheetTitle.
///
/// In en, this message translates to:
/// **'{count, plural, =1{Hint} other{Hints {count}}}'**
String cardHintsSheetTitle ( int count ) ;
/// No description provided for @cardPreviousHint.
///
/// In en, this message translates to:
/// **'Previous hint'**
String get cardPreviousHint ;
/// No description provided for @cardNextHint.
///
/// In en, this message translates to:
/// **'Next hint'**
String get cardNextHint ;
/// No description provided for @cardReport.
///
/// In en, this message translates to:
/// **'Report'**
String get cardReport ;
/// No description provided for @cardSenses.
///
/// In en, this message translates to:
/// **'Senses'**
String get cardSenses ;
feat(app): the dictionary in the app: look words up, add them as cards
A studyset's dictionary follows from its language, as decided: the
instance's dictionary of its front's language, with meanings in its
back's (else English). Nothing to attach or detach; nothing of it shows
where there is none.
- Add material › From the dictionary: search a word (kanji, kana,
romaji, meaning) and add it as a flashcard, or add a whole level
(JLPT N5…N1, common words) at once, asked first above 500 words, its
progress shown. Words already in the studyset show as such and are
left out. Each card is a vocabulary card (headword, reading, senses
with their codes), tagged `jlpt-n5` and with its word class, and
marked as imported from the dictionary.
- Look up beside a vocabulary card's front fills the card with the word
chosen, keeping its hints, explanation and tags.
- A card keeps the dictionary entry it was made from (card schema 6:
`dictionary` {source, id}), to be found again and not added twice.
- The dictionary's credits show at its foot and in About, as its licence
asks.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MMm7usAo6q9ZZRoPkWjoxi
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-09 01:33:12 +02:00
/// No description provided for @cardDictionaryEntry.
///
/// In en, this message translates to:
/// **'Dictionary entry'**
String get cardDictionaryEntry ;
feat: a dictionary's corrections reach the cards made from it, by offer
Every dictionary entry now has a version: 16 hex digits of the SHA-256 of
what cards take of it (headword, reading, entry), made by the loader and
carried on every hit. A card keeps the version it took (card schema 8).
DictionaryEndpoint.changed returns, of the entries named with the
versions cards keep (1,000 at most a call; the app asks 500 at a time),
those whose version is another now.
The card browser says how many cards are behind their dictionary and takes
the corrections at a tap (decided 2026-10-09: by offer, never by
themselves). Taking one writes the entry as it is now over the revision
in which the card last took it, beside whatever came after, so the
revisions merge per side as two devices' edits do (ADR 0003): a side only
the dictionary changed takes the correction, a side the person changed
too is a conflict settled in the composer, whose words now name that
case. Cards made before versions are never offered one.
The server rereads its dictionary folder hourly now, not daily: the
dictionaries service fills its volume after an instance's first start,
and an hour with nothing changed costs a hash of each file.
Migration 20261009145434785-dictionary-entry-version: adds
dictionary_entry.version, text NOT NULL DEFAULT '' (rows loaded before
have none and are never reported as corrected), and by hand empties
dictionary_source."fileHash", so every source loads once more and its
entries get their versions; the schema stays as definition.sql says. The
old server goes on working until the switch.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MMm7usAo6q9ZZRoPkWjoxi
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-09 17:32:00 +02:00
/// No description provided for @cardDictionaryVersion.
///
/// In en, this message translates to:
/// **'Dictionary version'**
String get cardDictionaryVersion ;
feat(app): the dictionary in the app: look words up, add them as cards
A studyset's dictionary follows from its language, as decided: the
instance's dictionary of its front's language, with meanings in its
back's (else English). Nothing to attach or detach; nothing of it shows
where there is none.
- Add material › From the dictionary: search a word (kanji, kana,
romaji, meaning) and add it as a flashcard, or add a whole level
(JLPT N5…N1, common words) at once, asked first above 500 words, its
progress shown. Words already in the studyset show as such and are
left out. Each card is a vocabulary card (headword, reading, senses
with their codes), tagged `jlpt-n5` and with its word class, and
marked as imported from the dictionary.
- Look up beside a vocabulary card's front fills the card with the word
chosen, keeping its hints, explanation and tags.
- A card keeps the dictionary entry it was made from (card schema 6:
`dictionary` {source, id}), to be found again and not added twice.
- The dictionary's credits show at its foot and in About, as its licence
asks.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MMm7usAo6q9ZZRoPkWjoxi
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-09 01:33:12 +02:00
/// No description provided for @studysetFromDictionary.
///
/// In en, this message translates to:
/// **'From the dictionary'**
String get studysetFromDictionary ;
/// No description provided for @dictionaryTitle.
///
/// In en, this message translates to:
/// **'Dictionary'**
String get dictionaryTitle ;
/// No description provided for @dictionarySearchHint.
///
/// In en, this message translates to:
/// **'A word in kanji, kana or romaji, or a meaning'**
String get dictionarySearchHint ;
feat(dictionary): German, Greek, English, Chinese and Korean words
A studyset in German, Greek, English, Chinese or Korean finds its
dictionary as one in Japanese does (ADR 0008), and its subtitles are cut
into words by its language (`WordCutting`): Chinese by its longest
words as they stand, the longer words over each cut offered as for
Japanese; Korean at its spaces, a word the dictionary lacks as it stands
taken without the particle it ends with (집에서 is 집, 아이 stays a
child); every other language at its spaces, a word written exactly so
first (the noun Essen before the verb essen). A studyset finds the
dictionary of its language tag, else of its language alone (zh for
zh-Hans).
A Wiktionary word shows its genders, principal forms, IPA, romanisation
and Hanja; a German or Greek noun of one gender goes onto its card with
its article (das Haus, το σπίτι); its tag is its word class, named in
full (Noun, Adjective, Preposition), as Wiktionary's adj and adv would
read as JMdict's codes. Articles, pronouns, prepositions and
conjunctions are left out of a video's words. Chinese words show their
pinyin over each character on the Watch screen, carry their HSK level
as a tag (HSK-1 … HSK-7-9), and the dictionary adds a level whole; the
Watch screen filters by HSK levels for Chinese, JLPT's for Japanese, and
by none for a language without. A reading is typed in a session only
when it is kana, so pinyin isn't. Words are looked up on the web on
Jisho in Japanese, on Wiktionary at the language's section otherwise,
and a search's hint speaks of kana and romaji only in Japanese.
`DICTIONARY_LANGUAGES` in deploy/.env names the languages an instance
keeps (unset: de,el,en,ko,zh; empty: none).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-14 07:45:15 +02:00
/// No description provided for @dictionarySearchHintWords.
///
/// In en, this message translates to:
/// **'A word, any form of it, or a meaning'**
String get dictionarySearchHintWords ;
feat(app): the dictionary in the app: look words up, add them as cards
A studyset's dictionary follows from its language, as decided: the
instance's dictionary of its front's language, with meanings in its
back's (else English). Nothing to attach or detach; nothing of it shows
where there is none.
- Add material › From the dictionary: search a word (kanji, kana,
romaji, meaning) and add it as a flashcard, or add a whole level
(JLPT N5…N1, common words) at once, asked first above 500 words, its
progress shown. Words already in the studyset show as such and are
left out. Each card is a vocabulary card (headword, reading, senses
with their codes), tagged `jlpt-n5` and with its word class, and
marked as imported from the dictionary.
- Look up beside a vocabulary card's front fills the card with the word
chosen, keeping its hints, explanation and tags.
- A card keeps the dictionary entry it was made from (card schema 6:
`dictionary` {source, id}), to be found again and not added twice.
- The dictionary's credits show at its foot and in About, as its licence
asks.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MMm7usAo6q9ZZRoPkWjoxi
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-09 01:33:12 +02:00
/// No description provided for @dictionaryNothingFound.
///
/// In en, this message translates to:
/// **'Nothing found for “{query}”.'**
String dictionaryNothingFound ( String query ) ;
/// No description provided for @dictionaryUnreachable.
///
/// In en, this message translates to:
/// **'The dictionary can\'t be reached right now.'**
String get dictionaryUnreachable ;
/// No description provided for @dictionaryRetry.
///
/// In en, this message translates to:
/// **'Retry'**
String get dictionaryRetry ;
fix(app): the dictionary adds each word once, says when it can't, and Look up tags hold
- A word tapped twice, or added while a level is being added, makes one
card: adds run one at a time, and the word counts as in the studyset
at once. The adder reads the studyset's cards once rather than for
every word.
- Adding that fails says so, and a level shows "Making the cards…" once
its words have come.
- Finding a studyset's dictionary tells finding, none and unreachable
apart: the dictionary screen says there is none or offers Retry, and
an instance unreachable at start is asked again once a sync goes
through (Look up and From the dictionary appear then).
- Look up's tags wait on the card by name and are made when it is saved:
leaving the card at once no longer saves it untagged, a word replaced
leaves no tag behind, and a new word replaces any level or word class
on the card, pinned ones too. Look up marks words already made into
cards of the studyset.
- The tag picker's Create keeps the case typed.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MMm7usAo6q9ZZRoPkWjoxi
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-09 12:45:18 +02:00
/// No description provided for @dictionaryAddFailed.
///
/// In en, this message translates to:
/// **'Couldn\'t add the words. Try again.'**
String get dictionaryAddFailed ;
fix(dictionary): words held in topics, a chosen destination, faster adds
A word counted as held only in the studyset itself, so adding from a
studyset whose topics held it (Vocabulary, its words in N3) made a
second card, and watching from a top-level studyset put every new word
into it. Now a word is held anywhere in the studyset and its topics,
archived ones too, and its progress shows from that card; new words go
where the person chose for the studyset ("Adds to N3" in the dictionary
and the Watch screen, the studyset and its topics but archived ones,
kept in their settings, schema 10), the studyset itself until then.
Adding a word took about two seconds on 17,000 card tags: each of its
four tags read every card's tags twice. They are now written with one
read, the Watch screen shows the word added at once and takes it back
should writing fail, and it no longer reads the studyset's progress
again after each word. On a copy of a real database an Add takes 0.3 to
0.4 s.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-16 20:41:32 +02:00
/// No description provided for @addDestination.
///
/// In en, this message translates to:
/// **'Adds to {studyset}'**
String addDestination ( String studyset ) ;
/// No description provided for @addDestinationHint.
///
/// In en, this message translates to:
/// **'Where new cards go: this studyset or one of its topics'**
String get addDestinationHint ;
fix(app): the dictionary adds each word once, says when it can't, and Look up tags hold
- A word tapped twice, or added while a level is being added, makes one
card: adds run one at a time, and the word counts as in the studyset
at once. The adder reads the studyset's cards once rather than for
every word.
- Adding that fails says so, and a level shows "Making the cards…" once
its words have come.
- Finding a studyset's dictionary tells finding, none and unreachable
apart: the dictionary screen says there is none or offers Retry, and
an instance unreachable at start is asked again once a sync goes
through (Look up and From the dictionary appear then).
- Look up's tags wait on the card by name and are made when it is saved:
leaving the card at once no longer saves it untagged, a word replaced
leaves no tag behind, and a new word replaces any level or word class
on the card, pinned ones too. Look up marks words already made into
cards of the studyset.
- The tag picker's Create keeps the case typed.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MMm7usAo6q9ZZRoPkWjoxi
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-09 12:45:18 +02:00
/// No description provided for @dictionaryMakingCards.
///
/// In en, this message translates to:
/// **'Making the cards…'**
String get dictionaryMakingCards ;
/// No description provided for @dictionaryNone.
///
/// In en, this message translates to:
/// **'This studyset\'s language has no dictionary on this instance.'**
String get dictionaryNone ;
feat(app): the dictionary in the app: look words up, add them as cards
A studyset's dictionary follows from its language, as decided: the
instance's dictionary of its front's language, with meanings in its
back's (else English). Nothing to attach or detach; nothing of it shows
where there is none.
- Add material › From the dictionary: search a word (kanji, kana,
romaji, meaning) and add it as a flashcard, or add a whole level
(JLPT N5…N1, common words) at once, asked first above 500 words, its
progress shown. Words already in the studyset show as such and are
left out. Each card is a vocabulary card (headword, reading, senses
with their codes), tagged `jlpt-n5` and with its word class, and
marked as imported from the dictionary.
- Look up beside a vocabulary card's front fills the card with the word
chosen, keeping its hints, explanation and tags.
- A card keeps the dictionary entry it was made from (card schema 6:
`dictionary` {source, id}), to be found again and not added twice.
- The dictionary's credits show at its foot and in About, as its licence
asks.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MMm7usAo6q9ZZRoPkWjoxi
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-09 01:33:12 +02:00
/// No description provided for @dictionaryAdd.
///
/// In en, this message translates to:
/// **'Add'**
String get dictionaryAdd ;
/// No description provided for @dictionaryInStudyset.
///
/// In en, this message translates to:
/// **'In this studyset'**
String get dictionaryInStudyset ;
/// No description provided for @dictionaryAdded.
///
/// In en, this message translates to:
/// **'Added “{word}”.'**
String dictionaryAdded ( String word ) ;
/// No description provided for @dictionaryLevels.
///
/// In en, this message translates to:
/// **'Add a whole level'**
String get dictionaryLevels ;
/// No description provided for @dictionaryLevelsHint.
///
/// In en, this message translates to:
/// **'Every word of a level becomes a flashcard, in the order of the level\'s list, tagged with its level and word class. Words already in this studyset are left out.'**
String get dictionaryLevelsHint ;
/// No description provided for @dictionaryLabelCommon.
///
/// In en, this message translates to:
/// **'Common'**
String get dictionaryLabelCommon ;
/// No description provided for @dictionaryAddLevel.
///
/// In en, this message translates to:
/// **'{count, plural, =1{Add 1 word} other{Add {count} words}}'**
String dictionaryAddLevel ( int count ) ;
/// No description provided for @dictionaryAddingLevel.
///
/// In en, this message translates to:
/// **'Adding… {done} of {count}'**
String dictionaryAddingLevel ( int done , int count ) ;
/// No description provided for @dictionaryWordsAdded.
///
/// In en, this message translates to:
/// **'{count, plural, =0{No new words.} =1{Added 1 word.} other{Added {count} words.}}'**
String dictionaryWordsAdded ( int count ) ;
/// No description provided for @dictionaryWordsSkipped.
///
/// In en, this message translates to:
/// **'{count, plural, =1{1 was in this studyset already.} other{{count} were in this studyset already.}}'**
String dictionaryWordsSkipped ( int count ) ;
/// No description provided for @dictionaryConfirmTitle.
///
/// In en, this message translates to:
/// **'Add {count} words?'**
String dictionaryConfirmTitle ( int count ) ;
/// No description provided for @dictionaryConfirmMessage.
///
/// In en, this message translates to:
/// **'Each becomes a flashcard in this studyset.'**
String get dictionaryConfirmMessage ;
/// No description provided for @dictionaryConfirmAdd.
///
/// In en, this message translates to:
/// **'Add'**
String get dictionaryConfirmAdd ;
/// No description provided for @dictionaryCredits.
///
/// In en, this message translates to:
/// **'Words from {sources}.'**
String dictionaryCredits ( String sources ) ;
2026-10-12 22:41:09 +02:00
/// No description provided for @dictionarySourcesTitle.
///
/// In en, this message translates to:
/// **'Sources'**
String get dictionarySourcesTitle ;
feat(app): the dictionary in the app: look words up, add them as cards
A studyset's dictionary follows from its language, as decided: the
instance's dictionary of its front's language, with meanings in its
back's (else English). Nothing to attach or detach; nothing of it shows
where there is none.
- Add material › From the dictionary: search a word (kanji, kana,
romaji, meaning) and add it as a flashcard, or add a whole level
(JLPT N5…N1, common words) at once, asked first above 500 words, its
progress shown. Words already in the studyset show as such and are
left out. Each card is a vocabulary card (headword, reading, senses
with their codes), tagged `jlpt-n5` and with its word class, and
marked as imported from the dictionary.
- Look up beside a vocabulary card's front fills the card with the word
chosen, keeping its hints, explanation and tags.
- A card keeps the dictionary entry it was made from (card schema 6:
`dictionary` {source, id}), to be found again and not added twice.
- The dictionary's credits show at its foot and in About, as its licence
asks.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MMm7usAo6q9ZZRoPkWjoxi
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-09 01:33:12 +02:00
/// No description provided for @dictionaryLookUp.
///
/// In en, this message translates to:
/// **'Look up in the dictionary'**
String get dictionaryLookUp ;
feat(app): kanji and kana in the dictionary, beside the words
Where the instance holds the characters of a studyset's language, the
dictionary screen has a tab for them, Kanji & kana: search by character,
reading (kana or romaji) or meaning, and whole levels to add, under their
names: the kana (hiragana, katakana), the JLPT levels and the school
grades. A character becomes a character card, its meanings (a kana's
romaji) and readings, tagged with its level (JLPT-N5), school grade
(Grade 1 … Grade 6, Secondary school) and script (Hiragana, Katakana). A
character card's Look up searches the characters, and an entry looked up
again on a card swaps the tags the one before brought, as words do.
A dictionary's entries are now words or characters (DictionaryItem), as
its kind says.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MMm7usAo6q9ZZRoPkWjoxi
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-09 15:19:22 +02:00
/// No description provided for @dictionaryCharacterSearchHint.
///
/// In en, this message translates to:
/// **'A kanji or kana, a reading in kana or romaji, or a meaning'**
String get dictionaryCharacterSearchHint ;
/// No description provided for @dictionaryTabWords.
///
/// In en, this message translates to:
/// **'Words'**
String get dictionaryTabWords ;
/// No description provided for @dictionaryTabCharacters.
///
/// In en, this message translates to:
/// **'Kanji & kana'**
String get dictionaryTabCharacters ;
feat(dictionary): characters of Chinese, Korean and Greek, written by hand; pinyin typed
Parity with Japanese's kanji and kana:
- The characters' tab is named by its language (Hanzi, Hangul & Hanja,
Alphabet), its groups too: HSK levels and the characters in common use
(Hong Kong's grades for traditional ones); Hangul and the Hanja taught
in schools; Greek's letters. Radicals for Chinese and Korean in their
own forms (讠 for 言 in simplified Chinese), none for Greek.
- A character's readings are named as its language names them: On and
Kun, Pinyin, Eum and Hun (음, 훈); a letter means its sound and name.
- How characters are written comes by language: Chinese from Make Me a
Hanzi (its medians in KanjiVG's square, Arphic Public License, a data
file beside the app, never in cards), the Hangul letters drawn by the
curator; a Korean word is written letter by letter (닭: ㄷ ㅏ ㄺ). Words
of alphabets (German, Greek) aren't written by hand.
- A Chinese word's pinyin is typed in a session, tone numbers turning
into marks (ni3 → nǐ); KRDict's levels (초급, 중급, 고급) tag Korean
words; a Korean word's pronunciation shows in brackets ([가치]).
- Smaller: Look up in the composer keeps a noun's article; the Watch
filter names its level list; hints and placeholders by language.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-14 12:35:17 +02:00
/// The tab of Chinese characters in a dictionary of Chinese.
///
/// In en, this message translates to:
/// **'Hanzi'**
String get dictionaryTabCharactersChinese ;
/// The tab of Korean letters and Hanja in a dictionary of Korean.
///
/// In en, this message translates to:
/// **'Hangul & Hanja'**
String get dictionaryTabCharactersKorean ;
/// The tab of Greek's letters in a dictionary of Greek.
///
/// In en, this message translates to:
/// **'Alphabet'**
String get dictionaryTabCharactersGreek ;
/// No description provided for @dictionaryCharacterSearchHintChinese.
///
/// In en, this message translates to:
/// **'A character, its pinyin, or a meaning'**
String get dictionaryCharacterSearchHintChinese ;
/// No description provided for @dictionaryCharacterSearchHintKorean.
///
/// In en, this message translates to:
/// **'A Hanja or Hangul letter, its sound, or a meaning'**
String get dictionaryCharacterSearchHintKorean ;
/// No description provided for @dictionaryCharacterSearchHintGreek.
///
/// In en, this message translates to:
/// **'A letter or its name'**
String get dictionaryCharacterSearchHintGreek ;
/// No description provided for @dictionaryGroupHsk.
///
/// In en, this message translates to:
/// **'HSK levels'**
String get dictionaryGroupHsk ;
/// No description provided for @dictionaryGroupCommonCharacters.
///
/// In en, this message translates to:
/// **'In common use'**
String get dictionaryGroupCommonCharacters ;
/// No description provided for @dictionaryGroupHongKongGrades.
///
/// In en, this message translates to:
/// **'Hong Kong school grades'**
String get dictionaryGroupHongKongGrades ;
/// No description provided for @dictionaryGroupHangul.
///
/// In en, this message translates to:
/// **'Hangul'**
String get dictionaryGroupHangul ;
/// No description provided for @dictionaryGroupEducationHanja.
///
/// In en, this message translates to:
/// **'Hanja taught in schools'**
String get dictionaryGroupEducationHanja ;
/// No description provided for @dictionaryGroupGreek.
///
/// In en, this message translates to:
/// **'Letters'**
String get dictionaryGroupGreek ;
/// No description provided for @dictionaryLabelHangul.
///
/// In en, this message translates to:
/// **'Hangul letters'**
String get dictionaryLabelHangul ;
/// No description provided for @dictionaryLabelEducationHanja.
///
/// In en, this message translates to:
/// **'1,800 basic Hanja'**
String get dictionaryLabelEducationHanja ;
/// No description provided for @dictionaryLabelGreek.
///
/// In en, this message translates to:
/// **'Greek alphabet'**
String get dictionaryLabelGreek ;
/// No description provided for @dictionaryLabelCommonCharacters.
///
/// In en, this message translates to:
/// **'3,500 common characters'**
String get dictionaryLabelCommonCharacters ;
feat(app): kanji and kana in the dictionary, beside the words
Where the instance holds the characters of a studyset's language, the
dictionary screen has a tab for them, Kanji & kana: search by character,
reading (kana or romaji) or meaning, and whole levels to add, under their
names: the kana (hiragana, katakana), the JLPT levels and the school
grades. A character becomes a character card, its meanings (a kana's
romaji) and readings, tagged with its level (JLPT-N5), school grade
(Grade 1 … Grade 6, Secondary school) and script (Hiragana, Katakana). A
character card's Look up searches the characters, and an entry looked up
again on a card swaps the tags the one before brought, as words do.
A dictionary's entries are now words or characters (DictionaryItem), as
its kind says.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MMm7usAo6q9ZZRoPkWjoxi
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-09 15:19:22 +02:00
/// No description provided for @dictionaryLabelHiragana.
///
/// In en, this message translates to:
/// **'Hiragana'**
String get dictionaryLabelHiragana ;
/// No description provided for @dictionaryLabelKatakana.
///
/// In en, this message translates to:
/// **'Katakana'**
String get dictionaryLabelKatakana ;
/// No description provided for @dictionaryGrade.
///
/// In en, this message translates to:
/// **'Grade {grade}'**
String dictionaryGrade ( int grade ) ;
/// No description provided for @dictionaryLabelSecondary.
///
/// In en, this message translates to:
/// **'Secondary school'**
String get dictionaryLabelSecondary ;
/// No description provided for @dictionaryGroupKana.
///
/// In en, this message translates to:
/// **'Kana'**
String get dictionaryGroupKana ;
/// No description provided for @dictionaryGroupJlpt.
///
/// In en, this message translates to:
/// **'JLPT levels'**
String get dictionaryGroupJlpt ;
/// No description provided for @dictionaryGroupGrades.
///
/// In en, this message translates to:
/// **'School grades'**
String get dictionaryGroupGrades ;
/// No description provided for @dictionaryStrokes.
///
/// In en, this message translates to:
/// **'{count, plural, =1{1 stroke} other{{count} strokes}}'**
String dictionaryStrokes ( int count ) ;
feat(dictionary): the radicals, each listing the kanji written with it
Beside Words and Kanji & kana, a Radicals tab lists the 214 Kangxi
radicals by their strokes, with the forms Japanese writes them as inside
kanji (亻, 氵, 艹, …). One chosen lists the kanji KANJIDIC2 files under
it, in the dictionary's order, to add or to see how one is written; the
tab shares the characters' bloc, so a kanji added there shows as held in
a search too.
The curator labels each kanji with its classical radical (`radical:N`),
so the server's existing label queries serve the tab unchanged. Its
files now say which conversion made them (`.conversion`): an instance
running a curator that converts differently converts again at once,
rather than at its weekly update, so the radicals appear right after a
deploy.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MMm7usAo6q9ZZRoPkWjoxi
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-10 02:13:06 +02:00
/// No description provided for @dictionaryTabRadicals.
///
/// In en, this message translates to:
/// **'Radicals'**
String get dictionaryTabRadicals ;
/// No description provided for @dictionaryRadicalsHint.
///
/// In en, this message translates to:
/// **'The 214 radicals that dictionaries order kanji by, grouped by their strokes. Choose one for the kanji written with it.'**
String get dictionaryRadicalsHint ;
/// No description provided for @dictionaryAllRadicals.
///
/// In en, this message translates to:
/// **'All radicals'**
String get dictionaryAllRadicals ;
/// No description provided for @dictionaryRadicalNumber.
///
/// In en, this message translates to:
/// **'Radical {number}'**
String dictionaryRadicalNumber ( int number ) ;
/// No description provided for @dictionaryRadicalKanjiCount.
///
/// In en, this message translates to:
/// **'{count} kanji'**
String dictionaryRadicalKanjiCount ( int count ) ;
/// No description provided for @dictionaryRadicalNone.
///
/// In en, this message translates to:
/// **'The instance\'s dictionary has no kanji under this radical yet.'**
String get dictionaryRadicalNone ;
feat(app): kanji and kana in the dictionary, beside the words
Where the instance holds the characters of a studyset's language, the
dictionary screen has a tab for them, Kanji & kana: search by character,
reading (kana or romaji) or meaning, and whole levels to add, under their
names: the kana (hiragana, katakana), the JLPT levels and the school
grades. A character becomes a character card, its meanings (a kana's
romaji) and readings, tagged with its level (JLPT-N5), school grade
(Grade 1 … Grade 6, Secondary school) and script (Hiragana, Katakana). A
character card's Look up searches the characters, and an entry looked up
again on a card swaps the tags the one before brought, as words do.
A dictionary's entries are now words or characters (DictionaryItem), as
its kind says.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MMm7usAo6q9ZZRoPkWjoxi
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-09 15:19:22 +02:00
/// No description provided for @dictionaryCharacterLevelsHint.
///
/// In en, this message translates to:
/// **'Every character of a level becomes a flashcard, in the order of the level\'s list, tagged with its level, school grade or script. Characters already in this studyset are left out.'**
String get dictionaryCharacterLevelsHint ;
/// No description provided for @dictionaryAddCharacters.
///
/// In en, this message translates to:
/// **'{count, plural, =1{Add 1 character} other{Add {count} characters}}'**
String dictionaryAddCharacters ( int count ) ;
/// No description provided for @dictionaryCharactersAdded.
///
/// In en, this message translates to:
/// **'{count, plural, =0{No new characters.} =1{Added 1 character.} other{Added {count} characters.}}'**
String dictionaryCharactersAdded ( int count ) ;
/// No description provided for @dictionaryConfirmCharactersTitle.
///
/// In en, this message translates to:
/// **'Add {count} characters?'**
String dictionaryConfirmCharactersTitle ( int count ) ;
/// No description provided for @dictionaryCharacterCredits.
///
/// In en, this message translates to:
/// **'Characters from {sources}.'**
String dictionaryCharacterCredits ( String sources ) ;
feat: a dictionary's corrections reach the cards made from it, by offer
Every dictionary entry now has a version: 16 hex digits of the SHA-256 of
what cards take of it (headword, reading, entry), made by the loader and
carried on every hit. A card keeps the version it took (card schema 8).
DictionaryEndpoint.changed returns, of the entries named with the
versions cards keep (1,000 at most a call; the app asks 500 at a time),
those whose version is another now.
The card browser says how many cards are behind their dictionary and takes
the corrections at a tap (decided 2026-10-09: by offer, never by
themselves). Taking one writes the entry as it is now over the revision
in which the card last took it, beside whatever came after, so the
revisions merge per side as two devices' edits do (ADR 0003): a side only
the dictionary changed takes the correction, a side the person changed
too is a conflict settled in the composer, whose words now name that
case. Cards made before versions are never offered one.
The server rereads its dictionary folder hourly now, not daily: the
dictionaries service fills its volume after an instance's first start,
and an hour with nothing changed costs a hash of each file.
Migration 20261009145434785-dictionary-entry-version: adds
dictionary_entry.version, text NOT NULL DEFAULT '' (rows loaded before
have none and are never reported as corrected), and by hand empties
dictionary_source."fileHash", so every source loads once more and its
entries get their versions; the schema stays as definition.sql says. The
old server goes on working until the switch.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MMm7usAo6q9ZZRoPkWjoxi
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-09 17:32:00 +02:00
/// No description provided for @dictionaryUpdatesBehind.
///
/// In en, this message translates to:
/// **'{count, plural, =1{1 card is behind the dictionary: its entry was corrected since it was made.} other{{count} cards are behind the dictionary: their entries were corrected since they were made.}}'**
String dictionaryUpdatesBehind ( int count ) ;
/// No description provided for @dictionaryUpdatesTake.
///
/// In en, this message translates to:
/// **'Take the corrections'**
String get dictionaryUpdatesTake ;
/// No description provided for @dictionaryUpdatesTaken.
///
/// In en, this message translates to:
/// **'{count, plural, =1{1 card updated.} other{{count} cards updated.}}'**
String dictionaryUpdatesTaken ( int count ) ;
/// No description provided for @dictionaryUpdatesToSettle.
///
/// In en, this message translates to:
/// **'{count, plural, =1{One has a change of yours there too: open it to choose.} other{{count} have a change of yours there too: open them to choose.}}'**
String dictionaryUpdatesToSettle ( int count ) ;
/// No description provided for @dictionaryUpdatesFailed.
///
/// In en, this message translates to:
/// **'Couldn\'t take the corrections. Try again.'**
String get dictionaryUpdatesFailed ;
feat(app): stroke order for kanji and kana, from KanjiVG
KanjiVG's strokes come with the app (assets/kanjivg/, CC BY-SA 3.0, its
notice in the licence list): 94 gzipped files, one per 256 code points,
2.7 MB in all, a file read the first time a character of it is asked for
(StrokeRepository, app-wide beside the maps). The strokes are SVG path
data, read with path_parsing (the Flutter team's, pure Dart, already in
the build through flutter_svg), so the model can flatten them into points
without dart:ui, as checking handwriting will.
A character's sheet traces its strokes in order over those to come, with
its stroke count, meanings, readings, level and the components KanjiVG
splits it into, each a tap away. It opens from a studied character's
answer (Stroke order) and from a character found in the dictionary.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MMm7usAo6q9ZZRoPkWjoxi
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-09 15:35:00 +02:00
/// No description provided for @kanjiStrokeOrder.
///
/// In en, this message translates to:
/// **'Stroke order'**
String get kanjiStrokeOrder ;
/// No description provided for @kanjiPlayAgain.
///
/// In en, this message translates to:
/// **'Play again'**
String get kanjiPlayAgain ;
/// No description provided for @kanjiNoStrokes.
///
/// In en, this message translates to:
/// **'There\'s no stroke order for this character.'**
String get kanjiNoStrokes ;
/// No description provided for @kanjiComponents.
///
/// In en, this message translates to:
/// **'Components'**
String get kanjiComponents ;
feat(app): write kanji, kana and words by hand, each stroke checked
The study option Write by hand (settings schema 7) has character cards
and words written in a session, on a schedule of their own (review mode
`write`; older apps skip a mode they don't know): what to write on top, a
character's meanings and readings or a word's reading and meanings, and
below it a square to write it in, character by character.
Each stroke is checked as it ends against KanjiVG's: both resampled to 22
points, then their shape once centred, where they lie, how long they are
and which way they go, each against what a loose hand strays by; a stroke
closer to one still to come is out of order. One that counts is drawn as
KanjiVG draws it, and the hand's offset from the middle is forgiven in the
strokes after; any other flashes red, says why and counts as a mistake, as
does a hint. Two misses in a row show the next stroke faintly. Once
written, or given up on (Show it), the rating the mistakes suggest is
outlined: none Good, one or two OK, more Bad.
The square takes every touch on it at once, so writing never scrolls the
card or swipes to rate. Characters without a stroke order (in a word) are
written for the person.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MMm7usAo6q9ZZRoPkWjoxi
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-09 15:59:40 +02:00
/// No description provided for @studySettingsCharacters.
///
/// In en, this message translates to:
feat(dictionary): characters of Chinese, Korean and Greek, written by hand; pinyin typed
Parity with Japanese's kanji and kana:
- The characters' tab is named by its language (Hanzi, Hangul & Hanja,
Alphabet), its groups too: HSK levels and the characters in common use
(Hong Kong's grades for traditional ones); Hangul and the Hanja taught
in schools; Greek's letters. Radicals for Chinese and Korean in their
own forms (讠 for 言 in simplified Chinese), none for Greek.
- A character's readings are named as its language names them: On and
Kun, Pinyin, Eum and Hun (음, 훈); a letter means its sound and name.
- How characters are written comes by language: Chinese from Make Me a
Hanzi (its medians in KanjiVG's square, Arphic Public License, a data
file beside the app, never in cards), the Hangul letters drawn by the
curator; a Korean word is written letter by letter (닭: ㄷ ㅏ ㄺ). Words
of alphabets (German, Greek) aren't written by hand.
- A Chinese word's pinyin is typed in a session, tone numbers turning
into marks (ni3 → nǐ); KRDict's levels (초급, 중급, 고급) tag Korean
words; a Korean word's pronunciation shows in brackets ([가치]).
- Smaller: Look up in the composer keeps a noun's article; the Watch
filter names its level list; hints and placeholders by language.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-14 12:35:17 +02:00
/// **'Characters'**
feat(app): write kanji, kana and words by hand, each stroke checked
The study option Write by hand (settings schema 7) has character cards
and words written in a session, on a schedule of their own (review mode
`write`; older apps skip a mode they don't know): what to write on top, a
character's meanings and readings or a word's reading and meanings, and
below it a square to write it in, character by character.
Each stroke is checked as it ends against KanjiVG's: both resampled to 22
points, then their shape once centred, where they lie, how long they are
and which way they go, each against what a loose hand strays by; a stroke
closer to one still to come is out of order. One that counts is drawn as
KanjiVG draws it, and the hand's offset from the middle is forgiven in the
strokes after; any other flashes red, says why and counts as a mistake, as
does a hint. Two misses in a row show the next stroke faintly. Once
written, or given up on (Show it), the rating the mistakes suggest is
outlined: none Good, one or two OK, more Bad.
The square takes every touch on it at once, so writing never scrolls the
card or swipes to rate. Characters without a stroke order (in a word) are
written for the person.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MMm7usAo6q9ZZRoPkWjoxi
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-09 15:59:40 +02:00
String get studySettingsCharacters ;
/// No description provided for @studySettingsWriteByHand.
///
/// In en, this message translates to:
/// **'Write by hand'**
String get studySettingsWriteByHand ;
/// No description provided for @studySettingsWriteByHandDescription.
///
/// In en, this message translates to:
feat(dictionary): characters of Chinese, Korean and Greek, written by hand; pinyin typed
Parity with Japanese's kanji and kana:
- The characters' tab is named by its language (Hanzi, Hangul & Hanja,
Alphabet), its groups too: HSK levels and the characters in common use
(Hong Kong's grades for traditional ones); Hangul and the Hanja taught
in schools; Greek's letters. Radicals for Chinese and Korean in their
own forms (讠 for 言 in simplified Chinese), none for Greek.
- A character's readings are named as its language names them: On and
Kun, Pinyin, Eum and Hun (음, 훈); a letter means its sound and name.
- How characters are written comes by language: Chinese from Make Me a
Hanzi (its medians in KanjiVG's square, Arphic Public License, a data
file beside the app, never in cards), the Hangul letters drawn by the
curator; a Korean word is written letter by letter (닭: ㄷ ㅏ ㄺ). Words
of alphabets (German, Greek) aren't written by hand.
- A Chinese word's pinyin is typed in a session, tone numbers turning
into marks (ni3 → nǐ); KRDict's levels (초급, 중급, 고급) tag Korean
words; a Korean word's pronunciation shows in brackets ([가치]).
- Smaller: Look up in the composer keeps a noun's article; the Watch
filter names its level list; hints and placeholders by language.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-14 12:35:17 +02:00
/// **'Kanji, hanzi, Hanja, kana, Hangul and words written with them are written stroke by stroke and checked as you go, on a schedule of their own (a Korean word letter by letter). Words written aren\'t quizzed or typed.'**
feat(app): write kanji, kana and words by hand, each stroke checked
The study option Write by hand (settings schema 7) has character cards
and words written in a session, on a schedule of their own (review mode
`write`; older apps skip a mode they don't know): what to write on top, a
character's meanings and readings or a word's reading and meanings, and
below it a square to write it in, character by character.
Each stroke is checked as it ends against KanjiVG's: both resampled to 22
points, then their shape once centred, where they lie, how long they are
and which way they go, each against what a loose hand strays by; a stroke
closer to one still to come is out of order. One that counts is drawn as
KanjiVG draws it, and the hand's offset from the middle is forgiven in the
strokes after; any other flashes red, says why and counts as a mistake, as
does a hint. Two misses in a row show the next stroke faintly. Once
written, or given up on (Show it), the rating the mistakes suggest is
outlined: none Good, one or two OK, more Bad.
The square takes every touch on it at once, so writing never scrolls the
card or swipes to rate. Characters without a stroke order (in a word) are
written for the person.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MMm7usAo6q9ZZRoPkWjoxi
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-09 15:59:40 +02:00
String get studySettingsWriteByHandDescription ;
/// No description provided for @writingHint.
///
/// In en, this message translates to:
/// **'Hint'**
String get writingHint ;
/// No description provided for @writingShow.
///
/// In en, this message translates to:
/// **'Show it'**
String get writingShow ;
/// No description provided for @writingMistakes.
///
/// In en, this message translates to:
/// **'{count, plural, =0{No mistakes} =1{1 mistake} other{{count} mistakes}}'**
String writingMistakes ( int count ) ;
/// No description provided for @writingWrongOrder.
///
/// In en, this message translates to:
/// **'Not yet: that stroke comes later.'**
String get writingWrongOrder ;
/// No description provided for @writingWrong.
///
/// In en, this message translates to:
/// **'That isn\'t the next stroke.'**
String get writingWrong ;
/// No description provided for @writingSuggested.
///
/// In en, this message translates to:
/// **'{rating} suggested'**
String writingSuggested ( String rating ) ;
/// No description provided for @writingNoStrokes.
///
/// In en, this message translates to:
/// **'There\'s no stroke order for these characters: rate how well you knew them.'**
String get writingNoStrokes ;
feat(app): the dictionary in the app: look words up, add them as cards
A studyset's dictionary follows from its language, as decided: the
instance's dictionary of its front's language, with meanings in its
back's (else English). Nothing to attach or detach; nothing of it shows
where there is none.
- Add material › From the dictionary: search a word (kanji, kana,
romaji, meaning) and add it as a flashcard, or add a whole level
(JLPT N5…N1, common words) at once, asked first above 500 words, its
progress shown. Words already in the studyset show as such and are
left out. Each card is a vocabulary card (headword, reading, senses
with their codes), tagged `jlpt-n5` and with its word class, and
marked as imported from the dictionary.
- Look up beside a vocabulary card's front fills the card with the word
chosen, keeping its hints, explanation and tags.
- A card keeps the dictionary entry it was made from (card schema 6:
`dictionary` {source, id}), to be found again and not added twice.
- The dictionary's credits show at its foot and in About, as its licence
asks.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MMm7usAo6q9ZZRoPkWjoxi
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-09 01:33:12 +02:00
/// No description provided for @aboutDictionaries.
///
/// In en, this message translates to:
/// **'Dictionaries on this instance'**
String get aboutDictionaries ;
2026-10-09 16:05:05 +02:00
/// No description provided for @aboutAppData.
///
/// In en, this message translates to:
/// **'Data in the app'**
String get aboutAppData ;
2026-10-07 21:31:48 +02:00
/// No description provided for @cardReportTitle.
///
/// In en, this message translates to:
/// **'Report an error'**
String get cardReportTitle ;
/// No description provided for @cardReportSubtitle.
///
/// In en, this message translates to:
/// **'Flag this card so you can find and fix it later.'**
String get cardReportSubtitle ;
/// No description provided for @cardReportTypo.
///
/// In en, this message translates to:
/// **'Typo'**
String get cardReportTypo ;
/// No description provided for @cardReportGrammar.
///
/// In en, this message translates to:
/// **'Grammar'**
String get cardReportGrammar ;
/// No description provided for @cardReportContent.
///
/// In en, this message translates to:
/// **'Content'**
String get cardReportContent ;
/// No description provided for @cardReportOther.
///
/// In en, this message translates to:
/// **'Other'**
String get cardReportOther ;
/// No description provided for @cardReportNote.
///
/// In en, this message translates to:
/// **'Note'**
String get cardReportNote ;
/// No description provided for @cardReportNoteHint.
///
/// In en, this message translates to:
/// **'What needs fixing?'**
String get cardReportNoteHint ;
/// No description provided for @cardReportSubmit.
///
/// In en, this message translates to:
/// **'Report'**
String get cardReportSubmit ;
/// No description provided for @cardReported.
///
/// In en, this message translates to:
/// **'Flagged for fixing'**
String get cardReported ;
/// No description provided for @cardResolveReport.
///
/// In en, this message translates to:
/// **'Resolve report'**
String get cardResolveReport ;
/// No description provided for @cardReportResolved.
///
/// In en, this message translates to:
/// **'Report resolved'**
String get cardReportResolved ;
/// No description provided for @cardBuriedBadge.
///
/// In en, this message translates to:
/// **'Buried'**
String get cardBuriedBadge ;
/// No description provided for @cardUnbury.
///
/// In en, this message translates to:
/// **'Unbury'**
String get cardUnbury ;
/// No description provided for @cardTileBuried.
///
/// In en, this message translates to:
/// **'Flashcard buried until tomorrow'**
String get cardTileBuried ;
/// No description provided for @cardTileUnburied.
///
/// In en, this message translates to:
/// **'Flashcard unburied'**
String get cardTileUnburied ;
2026-10-07 21:52:03 +02:00
/// No description provided for @cardDeleteTitle.
///
/// In en, this message translates to:
/// **'Delete flashcard?'**
String get cardDeleteTitle ;
/// No description provided for @cardDeleteMessage.
///
/// In en, this message translates to:
/// **'This flashcard will be removed from the studyset. You can undo this for a few seconds.'**
String get cardDeleteMessage ;
/// No description provided for @cardDeleted.
///
/// In en, this message translates to:
/// **'Flashcard deleted'**
String get cardDeleted ;
2026-10-01 16:57:57 +02:00
/// No description provided for @cardFilterTooltip.
///
/// In en, this message translates to:
/// **'Filter'**
String get cardFilterTooltip ;
/// No description provided for @cardFilterTitle.
///
/// In en, this message translates to:
/// **'Filter'**
String get cardFilterTitle ;
/// No description provided for @cardFilterTag.
///
/// In en, this message translates to:
/// **'Tag'**
String get cardFilterTag ;
/// No description provided for @cardFilterAllTags.
///
/// In en, this message translates to:
/// **'All tags'**
String get cardFilterAllTags ;
/// No description provided for @cardFilterNoTags.
///
/// In en, this message translates to:
/// **'None of these flashcards has a tag yet.'**
String get cardFilterNoTags ;
/// No description provided for @cardFilterClear.
///
/// In en, this message translates to:
/// **'Clear all'**
String get cardFilterClear ;
/// No description provided for @cardFilterApply.
///
/// In en, this message translates to:
/// **'Apply'**
String get cardFilterApply ;
/// No description provided for @cardFilterNothing.
///
/// In en, this message translates to:
/// **'No flashcards have this tag.'**
String get cardFilterNothing ;
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 02:01:01 +02:00
/// No description provided for @cardListFailed.
///
/// In en, this message translates to:
/// **'Couldn\'t read the cards.'**
String get cardListFailed ;
2026-10-01 16:57:57 +02:00
/// No description provided for @cardListMore.
///
/// In en, this message translates to:
/// **'More'**
String get cardListMore ;
/// No description provided for @cardListManageTags.
///
/// In en, this message translates to:
/// **'Manage tags'**
String get cardListManageTags ;
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 18:42:25 +02:00
/// No description provided for @composerTitle.
///
/// In en, this message translates to:
/// **'Create flashcards'**
String get composerTitle ;
/// No description provided for @composerTitleFor.
///
/// In en, this message translates to:
/// **'Create flashcards for \"{title}\"'**
String composerTitleFor ( String title ) ;
Fix what the first Android run found
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
/// No description provided for @composerEditTitle.
///
/// In en, this message translates to:
/// **'Edit flashcards'**
String get composerEditTitle ;
/// No description provided for @composerEditTitleFor.
///
/// In en, this message translates to:
/// **'Edit flashcards in \"{title}\"'**
String composerEditTitleFor ( String title ) ;
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 18:42:25 +02:00
/// No description provided for @composerSaving.
///
/// In en, this message translates to:
/// **'Saving…'**
String get composerSaving ;
/// No description provided for @composerSaveFailed.
///
/// In en, this message translates to:
/// **'Save failed — retry'**
String get composerSaveFailed ;
/// No description provided for @composerAllSaved.
///
/// In en, this message translates to:
/// **'All changes saved'**
String get composerAllSaved ;
/// No description provided for @composerNewFlashcard.
///
/// In en, this message translates to:
/// **'New flashcard'**
String get composerNewFlashcard ;
/// No description provided for @composerCollapseStack.
///
/// In en, this message translates to:
/// **'Collapse stack'**
String get composerCollapseStack ;
/// No description provided for @composerExpandStack.
///
/// In en, this message translates to:
/// **'Expand stack'**
String get composerExpandStack ;
/// No description provided for @composerStatusSaving.
///
/// In en, this message translates to:
/// **'Saving'**
String get composerStatusSaving ;
/// No description provided for @composerStatusSaved.
///
/// In en, this message translates to:
/// **'Saved'**
String get composerStatusSaved ;
/// No description provided for @composerStatusFailed.
///
/// In en, this message translates to:
/// **'Save failed'**
String get composerStatusFailed ;
/// No description provided for @composerSaveFailedInline.
///
/// In en, this message translates to:
/// **'Save failed.'**
String get composerSaveFailedInline ;
/// No description provided for @composerRetry.
///
/// In en, this message translates to:
/// **'Retry'**
String get composerRetry ;
/// No description provided for @composerEmptyPane.
///
/// In en, this message translates to:
/// **'Add a flashcard to get started.'**
String get composerEmptyPane ;
/// No description provided for @composerKindFlashcard.
///
/// In en, this message translates to:
/// **'Flashcard'**
String get composerKindFlashcard ;
/// No description provided for @composerKindVocabulary.
///
/// In en, this message translates to:
/// **'Vocabulary'**
String get composerKindVocabulary ;
/// No description provided for @composerKindMatching.
///
/// In en, this message translates to:
/// **'Matching'**
String get composerKindMatching ;
/// No description provided for @composerKindMap.
///
/// In en, this message translates to:
/// **'Map'**
String get composerKindMap ;
2026-10-09 15:00:21 +02:00
/// No description provided for @composerKindCharacter.
///
/// In en, this message translates to:
/// **'Character'**
String get composerKindCharacter ;
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 18:42:25 +02:00
/// No description provided for @composerLabelFront.
///
/// In en, this message translates to:
/// **'Front'**
String get composerLabelFront ;
/// No description provided for @composerLabelQuestion.
///
/// In en, this message translates to:
/// **'Question'**
String get composerLabelQuestion ;
/// No description provided for @composerLabelExpression.
///
/// In en, this message translates to:
/// **'Expression'**
String get composerLabelExpression ;
/// No description provided for @composerLabelPrompt.
///
/// In en, this message translates to:
/// **'Prompt'**
String get composerLabelPrompt ;
2026-10-09 15:00:21 +02:00
/// No description provided for @composerLabelCharacter.
///
/// In en, this message translates to:
/// **'Character'**
String get composerLabelCharacter ;
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 18:42:25 +02:00
/// No description provided for @composerLabelBack.
///
/// In en, this message translates to:
/// **'Back'**
String get composerLabelBack ;
/// No description provided for @composerLabelAnswers.
///
/// In en, this message translates to:
/// **'Answers'**
String get composerLabelAnswers ;
/// No description provided for @composerLabelAnswer.
///
/// In en, this message translates to:
/// **'Answer'**
String get composerLabelAnswer ;
/// No description provided for @composerPlaceholderFront.
///
/// In en, this message translates to:
/// **'Add question'**
String get composerPlaceholderFront ;
/// No description provided for @composerPlaceholderExpression.
///
/// In en, this message translates to:
/// **'Add expression'**
String get composerPlaceholderExpression ;
/// No description provided for @composerPlaceholderPrompt.
///
/// In en, this message translates to:
/// **'Add prompt'**
String get composerPlaceholderPrompt ;
2026-10-09 15:00:21 +02:00
/// No description provided for @composerPlaceholderCharacter.
///
/// In en, this message translates to:
/// **'Add a kanji or kana'**
String get composerPlaceholderCharacter ;
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 18:42:25 +02:00
/// No description provided for @composerAddAnswer.
///
/// In en, this message translates to:
/// **'Add answer'**
String get composerAddAnswer ;
/// No description provided for @composerCorrectAnswer.
///
/// In en, this message translates to:
/// **'Correct answer'**
String get composerCorrectAnswer ;
/// No description provided for @composerWrongAnswer.
///
/// In en, this message translates to:
/// **'Wrong answer'**
String get composerWrongAnswer ;
/// No description provided for @composerRemoveAnswer.
///
/// In en, this message translates to:
/// **'Remove answer'**
String get composerRemoveAnswer ;
/// No description provided for @composerPin.
///
/// In en, this message translates to:
/// **'Pin {field}: keep it for the next card'**
String composerPin ( String field ) ;
/// No description provided for @composerUnpin.
///
/// In en, this message translates to:
/// **'Unpin {field}: clear it for the next card'**
String composerUnpin ( String field ) ;
/// No description provided for @composerTags.
///
/// In en, this message translates to:
/// **'Tags'**
String get composerTags ;
/// No description provided for @composerAddTags.
///
/// In en, this message translates to:
/// **'Add tags'**
String get composerAddTags ;
/// No description provided for @composerMoreActions.
///
/// In en, this message translates to:
/// **'More actions'**
String get composerMoreActions ;
/// No description provided for @composerDuplicate.
///
/// In en, this message translates to:
/// **'Duplicate'**
String get composerDuplicate ;
/// No description provided for @composerClearFields.
///
/// In en, this message translates to:
/// **'Clear fields'**
String get composerClearFields ;
/// No description provided for @composerDelete.
///
/// In en, this message translates to:
/// **'Delete'**
String get composerDelete ;
/// No description provided for @composerDeleteTitle.
///
/// In en, this message translates to:
/// **'Delete this flashcard?'**
String get composerDeleteTitle ;
/// No description provided for @composerDeleteDraftMessage.
///
/// In en, this message translates to:
/// **'The draft will be removed from this session. It has not been saved.'**
String get composerDeleteDraftMessage ;
/// No description provided for @composerDeleteSavedMessage.
///
/// In en, this message translates to:
/// **'The flashcard will be removed from the studyset, on all your devices.'**
String get composerDeleteSavedMessage ;
/// No description provided for @composerDiscardTitle.
///
/// In en, this message translates to:
/// **'{count, plural, =1{Discard unsaved flashcard?} other{Discard unsaved flashcards?}}'**
String composerDiscardTitle ( int count ) ;
/// No description provided for @composerDiscardMessage.
///
/// In en, this message translates to:
/// **'{count, plural, =1{1 flashcard hasn\'t been saved yet. Leaving now will discard it.} other{{count} flashcards haven\'t been saved yet. Leaving now will discard them.}}'**
String composerDiscardMessage ( int count ) ;
/// No description provided for @composerDiscardConfirm.
///
/// In en, this message translates to:
/// **'Discard'**
String get composerDiscardConfirm ;
/// No description provided for @composerConflict.
///
/// In en, this message translates to:
feat(explore): take a later release into a copy, at a tap
A copy's top studyset says when a newer release of what it came from is
out, and what it would bring: changed, new and withdrawn items, counted
against the release the copy last took. Update takes it, never by
itself (ADR 0005, rule 4): cards, notes and steps take the release as a
revision on the one where they last took a release and merge per side,
as a dictionary's corrections do, so what the learner changed stays and
a side both changed is a conflict to settle; studysets, paths and PDFs
take each field the learner left as it was. New items come in where
they belong, unless the learner deleted that part; what the author
withdrew stays, marked, and the card browser filters for it; what the
learner deleted stays deleted; progress is never touched.
The instance gives out any release by its version, for the app to
compare the two. UpdateTaken moves into the shared revision merge, now
that notes and steps take updates too.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MMm7usAo6q9ZZRoPkWjoxi
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-09 23:47:41 +02:00
/// **'This flashcard was changed in two ways at once: on two of your devices, or by you and by a correction from the dictionary or a new release of what it was copied from. Choose the version to keep of each part below.'**
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 18:42:25 +02:00
String get composerConflict ;
/// No description provided for @composerKeepThis.
///
/// In en, this message translates to:
/// **'Keep this'**
String get composerKeepThis ;
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
/// No description provided for @formatAlignLeft.
///
/// In en, this message translates to:
/// **'Align left'**
String get formatAlignLeft ;
/// No description provided for @formatAlignCenter.
///
/// In en, this message translates to:
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 02:01:01 +02:00
/// **'Centre'**
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
String get formatAlignCenter ;
/// No description provided for @formatAlignRight.
///
/// In en, this message translates to:
/// **'Align right'**
String get formatAlignRight ;
/// No description provided for @formatBold.
///
/// In en, this message translates to:
/// **'Bold'**
String get formatBold ;
/// No description provided for @formatItalic.
///
/// In en, this message translates to:
/// **'Italic'**
String get formatItalic ;
/// No description provided for @formatUnderline.
///
/// In en, this message translates to:
/// **'Underline'**
String get formatUnderline ;
/// No description provided for @formatSuperscript.
///
/// In en, this message translates to:
/// **'Superscript'**
String get formatSuperscript ;
/// No description provided for @formatSubscript.
///
/// In en, this message translates to:
/// **'Subscript'**
String get formatSubscript ;
feat(app): furigana in rich text
Text within a line can carry its reading now (`ruby` on the text's
attributes, `かんじ` on `漢字`), word by word, in lessons, example
sentences, notes and cards alike. Shown, the reading sits above the text,
smaller and centred, the text level with the line around it; while
editing, the text is dotted underneath and the caret moves through it as
text, as with formulas. The format bar's Furigana asks for the reading of
the text selected, starting from the one it has, and takes it away.
An Anki field's `<ruby>` keeps its reading as furigana now, each base its
own, instead of "漢(かん)"; Anki's `漢字[かんじ]` stays as written, as
telling it apart from other brackets is guesswork. The Markdown export
writes furigana as HTML's `<ruby>漢字<rt>かんじ</rt></ruby>`.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MMm7usAo6q9ZZRoPkWjoxi
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-09 01:51:28 +02:00
/// No description provided for @formatRuby.
///
/// In en, this message translates to:
feat(editor): text above is named by the language, shown on hover or a long press
Furigana was the only name for text set above text, though it carries
pinyin, a pronunciation or a translation as well. Its button and dialog
now say furigana in a Japanese studyset, pinyin in a Chinese one, and
text above elsewhere, each with an example of its own: the studyset's
languages reach the core's editors through ContentLanguages, which the
studyset's language scope now sets.
Read-only, the text above shows only under the pointer or after a long
press (another hides it), so the text can be read without it first; its
room stays, so no line moves. While editing, it shows over the pointer
above its dotted text; with the caret inside such text, the button
selects all of it and edits what it has, so it can be seen and changed
without finding its ends.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MMm7usAo6q9ZZRoPkWjoxi
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-12 23:14:04 +02:00
/// **'Text above (a pronunciation, a translation)'**
feat(app): furigana in rich text
Text within a line can carry its reading now (`ruby` on the text's
attributes, `かんじ` on `漢字`), word by word, in lessons, example
sentences, notes and cards alike. Shown, the reading sits above the text,
smaller and centred, the text level with the line around it; while
editing, the text is dotted underneath and the caret moves through it as
text, as with formulas. The format bar's Furigana asks for the reading of
the text selected, starting from the one it has, and takes it away.
An Anki field's `<ruby>` keeps its reading as furigana now, each base its
own, instead of "漢(かん)"; Anki's `漢字[かんじ]` stays as written, as
telling it apart from other brackets is guesswork. The Markdown export
writes furigana as HTML's `<ruby>漢字<rt>かんじ</rt></ruby>`.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MMm7usAo6q9ZZRoPkWjoxi
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-09 01:51:28 +02:00
String get formatRuby ;
feat(editor): text above is named by the language, shown on hover or a long press
Furigana was the only name for text set above text, though it carries
pinyin, a pronunciation or a translation as well. Its button and dialog
now say furigana in a Japanese studyset, pinyin in a Chinese one, and
text above elsewhere, each with an example of its own: the studyset's
languages reach the core's editors through ContentLanguages, which the
studyset's language scope now sets.
Read-only, the text above shows only under the pointer or after a long
press (another hides it), so the text can be read without it first; its
room stays, so no line moves. While editing, it shows over the pointer
above its dotted text; with the caret inside such text, the button
selects all of it and edits what it has, so it can be seen and changed
without finding its ends.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MMm7usAo6q9ZZRoPkWjoxi
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-12 23:14:04 +02:00
/// No description provided for @formatRubyFurigana.
///
/// In en, this message translates to:
/// **'Furigana (a reading above the text)'**
String get formatRubyFurigana ;
/// No description provided for @formatRubyPinyin.
///
/// In en, this message translates to:
/// **'Pinyin (above the characters)'**
String get formatRubyPinyin ;
feat(editor): links, and copying and pasting with every format
Text gets a link from the format bar's new link button or with Ctrl+K
(⌘K): the address for the selection, an empty one taking it away, or at
a caret the text as well, written there linked. An address without its
scheme leads to the web. This replaces the editor's own link menu, which
had no button and spoke English only.
Copying and pasting between the app's editors keeps every format: bold,
links, furigana, formulas, callouts, spoilers. The system's clipboard
holds plain text only (the editor's own copy drops the HTML it makes),
so the blocks copied last stay in memory beside it, and a paste takes
them while the clipboard still holds their text; anything else pastes as
plain text, as before, and other apps get plain text. On phones, which
had no way to copy at all, a bar over selected text copies, cuts, pastes
and selects all (the editor's MobileFloatingToolbar), never over a block
taken whole, whose own buttons a tap is for.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MMm7usAo6q9ZZRoPkWjoxi
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-11 23:30:47 +02:00
/// No description provided for @formatLink.
///
/// In en, this message translates to:
/// **'Link'**
String get formatLink ;
/// No description provided for @linkTitle.
///
/// In en, this message translates to:
/// **'Link'**
String get linkTitle ;
/// No description provided for @linkText.
///
/// In en, this message translates to:
/// **'Text'**
String get linkText ;
/// No description provided for @linkAddress.
///
/// In en, this message translates to:
/// **'Address'**
String get linkAddress ;
/// No description provided for @linkAddressHint.
///
/// In en, this message translates to:
/// **'https://…'**
String get linkAddressHint ;
/// No description provided for @linkSave.
///
/// In en, this message translates to:
/// **'Save'**
String get linkSave ;
/// No description provided for @linkRemove.
///
/// In en, this message translates to:
/// **'Remove link'**
String get linkRemove ;
feat(app): furigana in rich text
Text within a line can carry its reading now (`ruby` on the text's
attributes, `かんじ` on `漢字`), word by word, in lessons, example
sentences, notes and cards alike. Shown, the reading sits above the text,
smaller and centred, the text level with the line around it; while
editing, the text is dotted underneath and the caret moves through it as
text, as with formulas. The format bar's Furigana asks for the reading of
the text selected, starting from the one it has, and takes it away.
An Anki field's `<ruby>` keeps its reading as furigana now, each base its
own, instead of "漢(かん)"; Anki's `漢字[かんじ]` stays as written, as
telling it apart from other brackets is guesswork. The Markdown export
writes furigana as HTML's `<ruby>漢字<rt>かんじ</rt></ruby>`.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MMm7usAo6q9ZZRoPkWjoxi
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-09 01:51:28 +02:00
/// No description provided for @rubyTitle.
///
/// In en, this message translates to:
feat(editor): text above is named by the language, shown on hover or a long press
Furigana was the only name for text set above text, though it carries
pinyin, a pronunciation or a translation as well. Its button and dialog
now say furigana in a Japanese studyset, pinyin in a Chinese one, and
text above elsewhere, each with an example of its own: the studyset's
languages reach the core's editors through ContentLanguages, which the
studyset's language scope now sets.
Read-only, the text above shows only under the pointer or after a long
press (another hides it), so the text can be read without it first; its
room stays, so no line moves. While editing, it shows over the pointer
above its dotted text; with the caret inside such text, the button
selects all of it and edits what it has, so it can be seen and changed
without finding its ends.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MMm7usAo6q9ZZRoPkWjoxi
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-12 23:14:04 +02:00
/// **'Text above'**
feat(app): furigana in rich text
Text within a line can carry its reading now (`ruby` on the text's
attributes, `かんじ` on `漢字`), word by word, in lessons, example
sentences, notes and cards alike. Shown, the reading sits above the text,
smaller and centred, the text level with the line around it; while
editing, the text is dotted underneath and the caret moves through it as
text, as with formulas. The format bar's Furigana asks for the reading of
the text selected, starting from the one it has, and takes it away.
An Anki field's `<ruby>` keeps its reading as furigana now, each base its
own, instead of "漢(かん)"; Anki's `漢字[かんじ]` stays as written, as
telling it apart from other brackets is guesswork. The Markdown export
writes furigana as HTML's `<ruby>漢字<rt>かんじ</rt></ruby>`.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MMm7usAo6q9ZZRoPkWjoxi
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-09 01:51:28 +02:00
String get rubyTitle ;
feat(editor): text above is named by the language, shown on hover or a long press
Furigana was the only name for text set above text, though it carries
pinyin, a pronunciation or a translation as well. Its button and dialog
now say furigana in a Japanese studyset, pinyin in a Chinese one, and
text above elsewhere, each with an example of its own: the studyset's
languages reach the core's editors through ContentLanguages, which the
studyset's language scope now sets.
Read-only, the text above shows only under the pointer or after a long
press (another hides it), so the text can be read without it first; its
room stays, so no line moves. While editing, it shows over the pointer
above its dotted text; with the caret inside such text, the button
selects all of it and edits what it has, so it can be seen and changed
without finding its ends.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MMm7usAo6q9ZZRoPkWjoxi
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-12 23:14:04 +02:00
/// No description provided for @rubyTitleFurigana.
///
/// In en, this message translates to:
/// **'Furigana'**
String get rubyTitleFurigana ;
/// No description provided for @rubyTitlePinyin.
///
/// In en, this message translates to:
/// **'Pinyin'**
String get rubyTitlePinyin ;
/// No description provided for @rubyText.
///
/// In en, this message translates to:
/// **'Text'**
String get rubyText ;
/// No description provided for @rubyHint.
///
/// In en, this message translates to:
/// **'e.g. a pronunciation or a translation'**
String get rubyHint ;
feat(app): furigana in rich text
Text within a line can carry its reading now (`ruby` on the text's
attributes, `かんじ` on `漢字`), word by word, in lessons, example
sentences, notes and cards alike. Shown, the reading sits above the text,
smaller and centred, the text level with the line around it; while
editing, the text is dotted underneath and the caret moves through it as
text, as with formulas. The format bar's Furigana asks for the reading of
the text selected, starting from the one it has, and takes it away.
An Anki field's `<ruby>` keeps its reading as furigana now, each base its
own, instead of "漢(かん)"; Anki's `漢字[かんじ]` stays as written, as
telling it apart from other brackets is guesswork. The Markdown export
writes furigana as HTML's `<ruby>漢字<rt>かんじ</rt></ruby>`.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MMm7usAo6q9ZZRoPkWjoxi
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-09 01:51:28 +02:00
/// No description provided for @rubyReading.
///
/// In en, this message translates to:
/// **'Reading'**
String get rubyReading ;
/// No description provided for @rubyReadingHint.
///
/// In en, this message translates to:
/// **'In kana, e.g. かんじ'**
String get rubyReadingHint ;
feat(editor): text above is named by the language, shown on hover or a long press
Furigana was the only name for text set above text, though it carries
pinyin, a pronunciation or a translation as well. Its button and dialog
now say furigana in a Japanese studyset, pinyin in a Chinese one, and
text above elsewhere, each with an example of its own: the studyset's
languages reach the core's editors through ContentLanguages, which the
studyset's language scope now sets.
Read-only, the text above shows only under the pointer or after a long
press (another hides it), so the text can be read without it first; its
room stays, so no line moves. While editing, it shows over the pointer
above its dotted text; with the caret inside such text, the button
selects all of it and edits what it has, so it can be seen and changed
without finding its ends.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MMm7usAo6q9ZZRoPkWjoxi
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-12 23:14:04 +02:00
/// No description provided for @rubyHintPinyin.
///
/// In en, this message translates to:
/// **'With tone marks, e.g. hànzì'**
String get rubyHintPinyin ;
feat(app): furigana in rich text
Text within a line can carry its reading now (`ruby` on the text's
attributes, `かんじ` on `漢字`), word by word, in lessons, example
sentences, notes and cards alike. Shown, the reading sits above the text,
smaller and centred, the text level with the line around it; while
editing, the text is dotted underneath and the caret moves through it as
text, as with formulas. The format bar's Furigana asks for the reading of
the text selected, starting from the one it has, and takes it away.
An Anki field's `<ruby>` keeps its reading as furigana now, each base its
own, instead of "漢(かん)"; Anki's `漢字[かんじ]` stays as written, as
telling it apart from other brackets is guesswork. The Markdown export
writes furigana as HTML's `<ruby>漢字<rt>かんじ</rt></ruby>`.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MMm7usAo6q9ZZRoPkWjoxi
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-09 01:51:28 +02:00
/// No description provided for @rubySave.
///
/// In en, this message translates to:
/// **'Save'**
String get rubySave ;
/// No description provided for @rubyRemove.
///
/// In en, this message translates to:
/// **'Remove'**
String get rubyRemove ;
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 18:42:25 +02:00
/// No description provided for @vocabularyReading.
///
/// In en, this message translates to:
/// **'Reading'**
String get vocabularyReading ;
/// No description provided for @vocabularyReadingHint.
///
/// In en, this message translates to:
/// **'Kana reading'**
String get vocabularyReadingHint ;
/// No description provided for @vocabularyMeanings.
///
/// In en, this message translates to:
/// **'Meanings'**
String get vocabularyMeanings ;
/// No description provided for @vocabularyPrimaryMeaningHint.
///
/// In en, this message translates to:
/// **'Primary meaning'**
String get vocabularyPrimaryMeaningHint ;
/// No description provided for @vocabularyMeaningHint.
///
/// In en, this message translates to:
/// **'Meaning'**
String get vocabularyMeaningHint ;
/// No description provided for @vocabularyAddMeaning.
///
/// In en, this message translates to:
/// **'Add meaning'**
String get vocabularyAddMeaning ;
/// No description provided for @vocabularyRemoveMeaning.
///
/// In en, this message translates to:
/// **'Remove meaning'**
String get vocabularyRemoveMeaning ;
2026-10-07 21:38:41 +02:00
/// No description provided for @vocabularyGlossHint.
///
/// In en, this message translates to:
/// **'Translation'**
String get vocabularyGlossHint ;
/// No description provided for @vocabularyAddGloss.
///
/// In en, this message translates to:
/// **'Add translation'**
String get vocabularyAddGloss ;
/// No description provided for @vocabularyRemoveGloss.
///
/// In en, this message translates to:
/// **'Remove translation'**
String get vocabularyRemoveGloss ;
/// No description provided for @vocabularyAddSense.
///
/// In en, this message translates to:
/// **'Add sense'**
String get vocabularyAddSense ;
/// No description provided for @vocabularyRemoveSense.
///
/// In en, this message translates to:
/// **'Remove sense'**
String get vocabularyRemoveSense ;
2026-10-09 15:00:21 +02:00
/// No description provided for @characterPrimaryMeaningHint.
///
/// In en, this message translates to:
/// **'Primary meaning, or a kana\'s romaji'**
String get characterPrimaryMeaningHint ;
/// No description provided for @characterOnReadings.
///
/// In en, this message translates to:
/// **'On readings'**
String get characterOnReadings ;
/// No description provided for @characterOnReadingsHint.
///
/// In en, this message translates to:
/// **'From Chinese, in katakana: ショク、ジキ'**
String get characterOnReadingsHint ;
/// No description provided for @characterKunReadings.
///
/// In en, this message translates to:
/// **'Kun readings'**
String get characterKunReadings ;
/// No description provided for @characterKunReadingsHint.
///
/// In en, this message translates to:
/// **'Japanese, in hiragana, okurigana after a dot: た.べる'**
String get characterKunReadingsHint ;
/// No description provided for @characterOn.
///
/// In en, this message translates to:
/// **'On'**
String get characterOn ;
/// No description provided for @characterKun.
///
/// In en, this message translates to:
/// **'Kun'**
String get characterKun ;
feat(dictionary): characters of Chinese, Korean and Greek, written by hand; pinyin typed
Parity with Japanese's kanji and kana:
- The characters' tab is named by its language (Hanzi, Hangul & Hanja,
Alphabet), its groups too: HSK levels and the characters in common use
(Hong Kong's grades for traditional ones); Hangul and the Hanja taught
in schools; Greek's letters. Radicals for Chinese and Korean in their
own forms (讠 for 言 in simplified Chinese), none for Greek.
- A character's readings are named as its language names them: On and
Kun, Pinyin, Eum and Hun (음, 훈); a letter means its sound and name.
- How characters are written comes by language: Chinese from Make Me a
Hanzi (its medians in KanjiVG's square, Arphic Public License, a data
file beside the app, never in cards), the Hangul letters drawn by the
curator; a Korean word is written letter by letter (닭: ㄷ ㅏ ㄺ). Words
of alphabets (German, Greek) aren't written by hand.
- A Chinese word's pinyin is typed in a session, tone numbers turning
into marks (ni3 → nǐ); KRDict's levels (초급, 중급, 고급) tag Korean
words; a Korean word's pronunciation shows in brackets ([가치]).
- Smaller: Look up in the composer keeps a noun's article; the Watch
filter names its level list; hints and placeholders by language.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-14 12:35:17 +02:00
/// No description provided for @characterPinyin.
///
/// In en, this message translates to:
/// **'Pinyin'**
String get characterPinyin ;
/// No description provided for @characterEum.
///
/// In en, this message translates to:
/// **'Eum'**
String get characterEum ;
/// No description provided for @characterHun.
///
/// In en, this message translates to:
/// **'Hun'**
String get characterHun ;
/// No description provided for @characterPinyinReadings.
///
/// In en, this message translates to:
/// **'Pinyin'**
String get characterPinyinReadings ;
/// No description provided for @characterPinyinReadingsHint.
///
/// In en, this message translates to:
/// **'With tone marks or numbers: shuǐ, shui3'**
String get characterPinyinReadingsHint ;
/// No description provided for @characterEumReadings.
///
/// In en, this message translates to:
/// **'Sounds (eum)'**
String get characterEumReadings ;
/// No description provided for @characterEumReadingsHint.
///
/// In en, this message translates to:
/// **'Sino-Korean, in Hangul: 수'**
String get characterEumReadingsHint ;
/// No description provided for @characterHunReadings.
///
/// In en, this message translates to:
/// **'Native meanings (hun)'**
String get characterHunReadings ;
/// No description provided for @characterHunReadingsHint.
///
/// In en, this message translates to:
/// **'Korean words for its meaning, in Hangul: 물'**
String get characterHunReadingsHint ;
/// No description provided for @characterPrimaryMeaningHintLetter.
///
/// In en, this message translates to:
/// **'Primary meaning, or a letter\'s sound'**
String get characterPrimaryMeaningHintLetter ;
/// No description provided for @composerPlaceholderCharacterChinese.
///
/// In en, this message translates to:
/// **'Add a character'**
String get composerPlaceholderCharacterChinese ;
/// No description provided for @composerPlaceholderCharacterKorean.
///
/// In en, this message translates to:
/// **'Add a Hanja or Hangul letter'**
String get composerPlaceholderCharacterKorean ;
/// No description provided for @composerPlaceholderCharacterGreek.
///
/// In en, this message translates to:
/// **'Add a letter'**
String get composerPlaceholderCharacterGreek ;
/// No description provided for @vocabularyReadingHintPinyin.
///
/// In en, this message translates to:
/// **'Pinyin, with tone marks or numbers'**
String get vocabularyReadingHintPinyin ;
/// No description provided for @vocabularyReadingHintPronunciation.
///
/// In en, this message translates to:
/// **'Pronunciation, where it differs from the spelling'**
String get vocabularyReadingHintPronunciation ;
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 18:42:25 +02:00
/// No description provided for @matchingCategories.
///
/// In en, this message translates to:
/// **'Categories'**
String get matchingCategories ;
/// No description provided for @matchingCategoriesHint.
///
/// In en, this message translates to:
/// **'The drop-zone cards learners drag items onto. Each category needs at least one item, so a card can\'t have more categories than items.'**
String get matchingCategoriesHint ;
/// No description provided for @matchingCategory.
///
/// In en, this message translates to:
/// **'Category {number}'**
String matchingCategory ( int number ) ;
/// No description provided for @matchingAddCategory.
///
/// In en, this message translates to:
/// **'Add category'**
String get matchingAddCategory ;
/// No description provided for @matchingAddCategoryBlocked.
///
/// In en, this message translates to:
/// **'Add an item first: a card can\'t have more categories than items'**
String get matchingAddCategoryBlocked ;
/// No description provided for @matchingRemoveCategory.
///
/// In en, this message translates to:
/// **'Remove category'**
String get matchingRemoveCategory ;
/// No description provided for @matchingMinCategories.
///
/// In en, this message translates to:
/// **'At least two categories required'**
String get matchingMinCategories ;
/// No description provided for @matchingItems.
///
/// In en, this message translates to:
/// **'Items'**
String get matchingItems ;
/// No description provided for @matchingItemsHint.
///
/// In en, this message translates to:
/// **'The draggable items. Assign every item to its correct category: an unassigned item can never be placed right.'**
String get matchingItemsHint ;
/// No description provided for @matchingItem.
///
/// In en, this message translates to:
/// **'Item {number}'**
String matchingItem ( int number ) ;
/// No description provided for @matchingAddItem.
///
/// In en, this message translates to:
/// **'Add item'**
String get matchingAddItem ;
/// No description provided for @matchingUnassigned.
///
/// In en, this message translates to:
/// **'Unassigned'**
String get matchingUnassigned ;
/// No description provided for @matchingRemoveItem.
///
/// In en, this message translates to:
/// **'Remove item'**
String get matchingRemoveItem ;
/// No description provided for @matchingMinItems.
///
/// In en, this message translates to:
/// **'At least two items required'**
String get matchingMinItems ;
2026-10-01 18:58:54 +02:00
/// No description provided for @cardTileOptions.
///
/// In en, this message translates to:
/// **'Options'**
String get cardTileOptions ;
/// No description provided for @cardTileEdit.
///
/// In en, this message translates to:
/// **'Edit'**
String get cardTileEdit ;
/// No description provided for @cardTileDelete.
///
/// In en, this message translates to:
/// **'Delete'**
String get cardTileDelete ;
/// No description provided for @cardTileUntitled.
///
/// In en, this message translates to:
/// **'Untitled'**
String get cardTileUntitled ;
/// No description provided for @cardTileConflict.
///
/// In en, this message translates to:
/// **'Changed on two devices'**
String get cardTileConflict ;
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 19:29:21 +02:00
/// No description provided for @studyClose.
///
/// In en, this message translates to:
/// **'Close session'**
String get studyClose ;
2026-10-07 22:03:27 +02:00
/// No description provided for @studyReviewsDueTitle.
///
/// In en, this message translates to:
/// **'You have reviews due'**
String get studyReviewsDueTitle ;
/// No description provided for @studyReviewsDueBody.
///
/// In en, this message translates to:
/// **'{reviews, plural, =1{1 card is} other{{reviews} cards are}} due for review. Learning {news, plural, =1{1 new card} other{{news} new cards}} now will add to your review load.'**
String studyReviewsDueBody ( int reviews , int news ) ;
/// No description provided for @studyReviewsDueAllTagsNote.
///
/// In en, this message translates to:
/// **'“Review first” studies the whole backlog across every tag — your tag filter doesn\'t apply until it\'s clear.'**
String get studyReviewsDueAllTagsNote ;
/// No description provided for @studyReviewFirst.
///
/// In en, this message translates to:
/// **'Review first'**
String get studyReviewFirst ;
/// No description provided for @studyLearnAnyway.
///
/// In en, this message translates to:
/// **'Learn anyway'**
String get studyLearnAnyway ;
/// No description provided for @studySettingsTagFilterPausedForBacklog.
///
/// In en, this message translates to:
/// **'This session is clearing your review backlog across every tag, so these tags aren\'t narrowing it. They apply again to your next session.'**
String get studySettingsTagFilterPausedForBacklog ;
2026-10-07 21:15:51 +02:00
/// No description provided for @studyBuryCard.
///
/// In en, this message translates to:
/// **'Bury until tomorrow'**
String get studyBuryCard ;
/// No description provided for @studyCardBuried.
///
/// In en, this message translates to:
/// **'Card buried until tomorrow'**
String get studyCardBuried ;
2026-10-07 22:30:44 +02:00
/// No description provided for @studyUndo.
2026-10-07 21:06:58 +02:00
///
/// In en, this message translates to:
2026-10-07 22:30:44 +02:00
/// **'Undo'**
String get studyUndo ;
2026-10-07 21:06:58 +02:00
/// No description provided for @studyLockScreen.
///
/// In en, this message translates to:
/// **'Lock screen'**
String get studyLockScreen ;
/// No description provided for @studyUnlock.
///
/// In en, this message translates to:
/// **'Unlock'**
String get studyUnlock ;
/// No description provided for @studyTapToUnlock.
///
/// In en, this message translates to:
/// **'Tap to unlock'**
String get studyTapToUnlock ;
2026-10-07 21:00:42 +02:00
/// No description provided for @studyLoadFailed.
///
/// In en, this message translates to:
/// **'Couldn\'t read this session.'**
String get studyLoadFailed ;
/// No description provided for @studyRatingFailed.
///
/// In en, this message translates to:
/// **'Couldn\'t keep your rating. Rate the card again.'**
String get studyRatingFailed ;
/// No description provided for @studyRetry.
///
/// In en, this message translates to:
/// **'Retry'**
String get studyRetry ;
/// No description provided for @studyScopeTitle.
///
/// In en, this message translates to:
/// **'Study scope'**
String get studyScopeTitle ;
/// No description provided for @studyScopeDescription.
///
/// In en, this message translates to:
/// **'This studyset has sub-studysets. Pick which cards to review.'**
String get studyScopeDescription ;
/// No description provided for @studyScopeEverythingTitle.
///
/// In en, this message translates to:
/// **'Everything'**
String get studyScopeEverythingTitle ;
/// No description provided for @studyScopeEverythingSubtitle.
///
/// In en, this message translates to:
/// **'This studyset and all sub-studysets'**
String get studyScopeEverythingSubtitle ;
/// No description provided for @studyScopeSelfTitle.
///
/// In en, this message translates to:
/// **'This studyset only'**
String get studyScopeSelfTitle ;
/// No description provided for @studyScopeSelfSubtitle.
///
/// In en, this message translates to:
/// **'Skip cards in sub-studysets'**
String get studyScopeSelfSubtitle ;
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 19:29:21 +02:00
/// No description provided for @studyBackFirstOn.
///
/// In en, this message translates to:
/// **'Showing the back of cards first: tap to revert'**
String get studyBackFirstOn ;
/// No description provided for @studyBackFirstOff.
///
/// In en, this message translates to:
/// **'Show the back of cards first'**
String get studyBackFirstOff ;
/// No description provided for @studyShowAnswer.
///
/// In en, this message translates to:
/// **'Show answer'**
String get studyShowAnswer ;
/// No description provided for @studyCheck.
///
/// In en, this message translates to:
/// **'Check'**
String get studyCheck ;
feat: a round in the order cards come, shared daily limits, and a start screen
Studying:
- A round is one list in the order its cards come: those done, the one
shown, those to come. A card due again within the day goes back in
after 4 more cards when rated Bad, at the end otherwise, so the pills
above the card follow it; the buttons say "in 4 cards", "at the end",
"now" (round_order.dart).
- The studied studyset's daily limits hold for everything inside it
together, as Anki's do for subdecks: twenty topics start 20 new cards
a day in all, not 20 each.
- The study header's studyset name shows whole on hover or long press.
- The assistant's button sits at the lower left: in the Library across
from Create studyset, in a studyset above its bar, and higher while
studying, above the buttons that answer the card.
A studyset:
- Progress also counts the learning paths' steps done and lists the
PDFs not read to their end, begun first, each opening the reader,
across its topics (MaterialProgressCubit).
- Next up starts with studying for the exam while one is ahead: its
reviews due and the new cards its pace leaves for the day.
- Learning paths tagged with a level count towards its automatic exam
plan and offer it, as cards do.
- A step's first heading equal to its title is dropped on import and
hidden in paths imported before.
Signing out, or a session the server ended, leads to a start screen
(GitHub, email, without an account, another instance) rather than a new
anonymous account; only the very first start makes one by itself.
Settings names the kind of account and its instance.
Also: the Watch screen reads the cards once for held words and their
progress; its ±10 s buttons tick as the navigation bar does; a map card
keeps its view as its caption changes and brings a picked region
towards the middle; an empty or failed library can be pulled to sync.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-17 00:39:34 +02:00
/// No description provided for @studyComesBackIn.
///
/// In en, this message translates to:
/// **'{count, plural, =1{in 1 card} other{in {count} cards}}'**
String studyComesBackIn ( int count ) ;
/// No description provided for @studyComesBackAtEnd.
///
/// In en, this message translates to:
/// **'at the end'**
String get studyComesBackAtEnd ;
/// No description provided for @studyComesBackNow.
///
/// In en, this message translates to:
/// **'now'**
String get studyComesBackNow ;
feat(app): study with faces on the ratings, centred cards and the session's figures
The study session, reworked:
- Ratings are coloured tiles with a face each (frown, meh, smile,
laugh), when the card comes back below them with a clock ("in 1
min", "in 3 days"), always shown, so a card can be rated without
revealing it; at most 540 dp wide, centred.
- Cards are centred in a section card: the question large, then a line
with 24 dp on either side and the answer a size smaller, or "Show
answer" 40 dp below; a tap anywhere on the card shows or hides the
answer. Read-only rich text takes a text style and an alignment.
- Words show their reading small above the expression once answered,
and several meanings numbered.
- Choice, matching and map cards: Check below the card, the ratings in
its place once checked; options marked before their text; matching
lists the categories first, then what is left to place; the map
fills the card with one caption below it.
- Header and progress on a band of their own; title start-aligned,
the Pomodoro timer only to watch, the back-first toggle in every
state; leaving a round under way asks first.
- Cramming goes round the round; back is there with two cards or more,
skipping always.
- The end of a round: the cards done of those due, accuracy (OK counts
half) and time, over the whole session.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-07 17:40:32 +02:00
/// No description provided for @studySelectAll.
///
/// In en, this message translates to:
/// **'Select all that apply'**
String get studySelectAll ;
/// No description provided for @studySelectOne.
///
/// In en, this message translates to:
/// **'Select the correct answer'**
String get studySelectOne ;
/// No description provided for @studyMatchingInstructions.
///
/// In en, this message translates to:
/// **'DRAG AND DROP ONTO CARDS ABOVE'**
String get studyMatchingInstructions ;
/// No description provided for @studyMatchingAllPlaced.
///
/// In en, this message translates to:
/// **'All placed'**
String get studyMatchingAllPlaced ;
/// No description provided for @studyMatchingDropHere.
///
/// In en, this message translates to:
/// **'Drop here'**
String get studyMatchingDropHere ;
/// No description provided for @studyMapTapPrompt.
///
/// In en, this message translates to:
/// **'Tap a region to answer'**
String get studyMapTapPrompt ;
/// No description provided for @studyMapSelected.
///
/// In en, this message translates to:
/// **'Selected: {label}'**
String studyMapSelected ( String label ) ;
/// No description provided for @studyCorrect.
///
/// In en, this message translates to:
/// **'Correct'**
String get studyCorrect ;
/// No description provided for @studyCorrectLabel.
///
/// In en, this message translates to:
/// **'Correct — {label}'**
String studyCorrectLabel ( String label ) ;
/// No description provided for @studyMapAnswer.
///
/// In en, this message translates to:
/// **'Answer: {answer}'**
String studyMapAnswer ( String answer ) ;
/// No description provided for @studyMapPickedAnswer.
///
/// In en, this message translates to:
/// **'You picked {picked} · Answer: {answer}'**
String studyMapPickedAnswer ( String picked , String answer ) ;
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 19:29:21 +02:00
/// No description provided for @studyRatingBad.
///
/// In en, this message translates to:
/// **'Bad'**
String get studyRatingBad ;
/// No description provided for @studyRatingOk.
///
/// In en, this message translates to:
/// **'OK'**
String get studyRatingOk ;
/// No description provided for @studyRatingGood.
///
/// In en, this message translates to:
/// **'Good'**
String get studyRatingGood ;
/// No description provided for @studyRatingPerfect.
///
/// In en, this message translates to:
/// **'Perfect'**
String get studyRatingPerfect ;
/// No description provided for @studyNothingTitle.
///
/// In en, this message translates to:
/// **'Nothing to study right now'**
String get studyNothingTitle ;
/// No description provided for @studyNothingBody.
///
/// In en, this message translates to:
/// **'Come back later when more cards are due.'**
String get studyNothingBody ;
/// No description provided for @studyNextDue.
///
/// In en, this message translates to:
/// **'The next card is due in {interval}.'**
String studyNextDue ( String interval ) ;
/// No description provided for @studyDone.
///
/// In en, this message translates to:
/// **'Done'**
String get studyDone ;
feat(app): study with faces on the ratings, centred cards and the session's figures
The study session, reworked:
- Ratings are coloured tiles with a face each (frown, meh, smile,
laugh), when the card comes back below them with a clock ("in 1
min", "in 3 days"), always shown, so a card can be rated without
revealing it; at most 540 dp wide, centred.
- Cards are centred in a section card: the question large, then a line
with 24 dp on either side and the answer a size smaller, or "Show
answer" 40 dp below; a tap anywhere on the card shows or hides the
answer. Read-only rich text takes a text style and an alignment.
- Words show their reading small above the expression once answered,
and several meanings numbered.
- Choice, matching and map cards: Check below the card, the ratings in
its place once checked; options marked before their text; matching
lists the categories first, then what is left to place; the map
fills the card with one caption below it.
- Header and progress on a band of their own; title start-aligned,
the Pomodoro timer only to watch, the back-first toggle in every
state; leaving a round under way asks first.
- Cramming goes round the round; back is there with two cards or more,
skipping always.
- The end of a round: the cards done of those due, accuracy (OK counts
half) and time, over the whole session.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-07 17:40:32 +02:00
/// No description provided for @studySessionComplete.
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 19:29:21 +02:00
///
/// In en, this message translates to:
feat(app): study with faces on the ratings, centred cards and the session's figures
The study session, reworked:
- Ratings are coloured tiles with a face each (frown, meh, smile,
laugh), when the card comes back below them with a clock ("in 1
min", "in 3 days"), always shown, so a card can be rated without
revealing it; at most 540 dp wide, centred.
- Cards are centred in a section card: the question large, then a line
with 24 dp on either side and the answer a size smaller, or "Show
answer" 40 dp below; a tap anywhere on the card shows or hides the
answer. Read-only rich text takes a text style and an alignment.
- Words show their reading small above the expression once answered,
and several meanings numbered.
- Choice, matching and map cards: Check below the card, the ratings in
its place once checked; options marked before their text; matching
lists the categories first, then what is left to place; the map
fills the card with one caption below it.
- Header and progress on a band of their own; title start-aligned,
the Pomodoro timer only to watch, the back-first toggle in every
state; leaving a round under way asks first.
- Cramming goes round the round; back is there with two cards or more,
skipping always.
- The end of a round: the cards done of those due, accuracy (OK counts
half) and time, over the whole session.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-07 17:40:32 +02:00
/// **'Session complete'**
String get studySessionComplete ;
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 19:29:21 +02:00
feat(app): study with faces on the ratings, centred cards and the session's figures
The study session, reworked:
- Ratings are coloured tiles with a face each (frown, meh, smile,
laugh), when the card comes back below them with a clock ("in 1
min", "in 3 days"), always shown, so a card can be rated without
revealing it; at most 540 dp wide, centred.
- Cards are centred in a section card: the question large, then a line
with 24 dp on either side and the answer a size smaller, or "Show
answer" 40 dp below; a tap anywhere on the card shows or hides the
answer. Read-only rich text takes a text style and an alignment.
- Words show their reading small above the expression once answered,
and several meanings numbered.
- Choice, matching and map cards: Check below the card, the ratings in
its place once checked; options marked before their text; matching
lists the categories first, then what is left to place; the map
fills the card with one caption below it.
- Header and progress on a band of their own; title start-aligned,
the Pomodoro timer only to watch, the back-first toggle in every
state; leaving a round under way asks first.
- Cramming goes round the round; back is there with two cards or more,
skipping always.
- The end of a round: the cards done of those due, accuracy (OK counts
half) and time, over the whole session.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-07 17:40:32 +02:00
/// No description provided for @studyProgressReviewed.
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 19:29:21 +02:00
///
/// In en, this message translates to:
feat(app): study with faces on the ratings, centred cards and the session's figures
The study session, reworked:
- Ratings are coloured tiles with a face each (frown, meh, smile,
laugh), when the card comes back below them with a clock ("in 1
min", "in 3 days"), always shown, so a card can be rated without
revealing it; at most 540 dp wide, centred.
- Cards are centred in a section card: the question large, then a line
with 24 dp on either side and the answer a size smaller, or "Show
answer" 40 dp below; a tap anywhere on the card shows or hides the
answer. Read-only rich text takes a text style and an alignment.
- Words show their reading small above the expression once answered,
and several meanings numbered.
- Choice, matching and map cards: Check below the card, the ratings in
its place once checked; options marked before their text; matching
lists the categories first, then what is left to place; the map
fills the card with one caption below it.
- Header and progress on a band of their own; title start-aligned,
the Pomodoro timer only to watch, the back-first toggle in every
state; leaving a round under way asks first.
- Cramming goes round the round; back is there with two cards or more,
skipping always.
- The end of a round: the cards done of those due, accuracy (OK counts
half) and time, over the whole session.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-07 17:40:32 +02:00
/// **'{reviewed} / {total} reviewed'**
String studyProgressReviewed ( int reviewed , int total ) ;
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 19:29:21 +02:00
feat(app): study with faces on the ratings, centred cards and the session's figures
The study session, reworked:
- Ratings are coloured tiles with a face each (frown, meh, smile,
laugh), when the card comes back below them with a clock ("in 1
min", "in 3 days"), always shown, so a card can be rated without
revealing it; at most 540 dp wide, centred.
- Cards are centred in a section card: the question large, then a line
with 24 dp on either side and the answer a size smaller, or "Show
answer" 40 dp below; a tap anywhere on the card shows or hides the
answer. Read-only rich text takes a text style and an alignment.
- Words show their reading small above the expression once answered,
and several meanings numbered.
- Choice, matching and map cards: Check below the card, the ratings in
its place once checked; options marked before their text; matching
lists the categories first, then what is left to place; the map
fills the card with one caption below it.
- Header and progress on a band of their own; title start-aligned,
the Pomodoro timer only to watch, the back-first toggle in every
state; leaving a round under way asks first.
- Cramming goes round the round; back is there with two cards or more,
skipping always.
- The end of a round: the cards done of those due, accuracy (OK counts
half) and time, over the whole session.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-07 17:40:32 +02:00
/// No description provided for @studyReviewedCount.
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 19:29:21 +02:00
///
/// In en, this message translates to:
feat(app): study with faces on the ratings, centred cards and the session's figures
The study session, reworked:
- Ratings are coloured tiles with a face each (frown, meh, smile,
laugh), when the card comes back below them with a clock ("in 1
min", "in 3 days"), always shown, so a card can be rated without
revealing it; at most 540 dp wide, centred.
- Cards are centred in a section card: the question large, then a line
with 24 dp on either side and the answer a size smaller, or "Show
answer" 40 dp below; a tap anywhere on the card shows or hides the
answer. Read-only rich text takes a text style and an alignment.
- Words show their reading small above the expression once answered,
and several meanings numbered.
- Choice, matching and map cards: Check below the card, the ratings in
its place once checked; options marked before their text; matching
lists the categories first, then what is left to place; the map
fills the card with one caption below it.
- Header and progress on a band of their own; title start-aligned,
the Pomodoro timer only to watch, the back-first toggle in every
state; leaving a round under way asks first.
- Cramming goes round the round; back is there with two cards or more,
skipping always.
- The end of a round: the cards done of those due, accuracy (OK counts
half) and time, over the whole session.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-07 17:40:32 +02:00
/// **'{reviewed} reviewed'**
String studyReviewedCount ( int reviewed ) ;
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 19:29:21 +02:00
feat(app): study with faces on the ratings, centred cards and the session's figures
The study session, reworked:
- Ratings are coloured tiles with a face each (frown, meh, smile,
laugh), when the card comes back below them with a clock ("in 1
min", "in 3 days"), always shown, so a card can be rated without
revealing it; at most 540 dp wide, centred.
- Cards are centred in a section card: the question large, then a line
with 24 dp on either side and the answer a size smaller, or "Show
answer" 40 dp below; a tap anywhere on the card shows or hides the
answer. Read-only rich text takes a text style and an alignment.
- Words show their reading small above the expression once answered,
and several meanings numbered.
- Choice, matching and map cards: Check below the card, the ratings in
its place once checked; options marked before their text; matching
lists the categories first, then what is left to place; the map
fills the card with one caption below it.
- Header and progress on a band of their own; title start-aligned,
the Pomodoro timer only to watch, the back-first toggle in every
state; leaving a round under way asks first.
- Cramming goes round the round; back is there with two cards or more,
skipping always.
- The end of a round: the cards done of those due, accuracy (OK counts
half) and time, over the whole session.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-07 17:40:32 +02:00
/// No description provided for @studyStatCards.
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 19:29:21 +02:00
///
/// In en, this message translates to:
feat(app): study with faces on the ratings, centred cards and the session's figures
The study session, reworked:
- Ratings are coloured tiles with a face each (frown, meh, smile,
laugh), when the card comes back below them with a clock ("in 1
min", "in 3 days"), always shown, so a card can be rated without
revealing it; at most 540 dp wide, centred.
- Cards are centred in a section card: the question large, then a line
with 24 dp on either side and the answer a size smaller, or "Show
answer" 40 dp below; a tap anywhere on the card shows or hides the
answer. Read-only rich text takes a text style and an alignment.
- Words show their reading small above the expression once answered,
and several meanings numbered.
- Choice, matching and map cards: Check below the card, the ratings in
its place once checked; options marked before their text; matching
lists the categories first, then what is left to place; the map
fills the card with one caption below it.
- Header and progress on a band of their own; title start-aligned,
the Pomodoro timer only to watch, the back-first toggle in every
state; leaving a round under way asks first.
- Cramming goes round the round; back is there with two cards or more,
skipping always.
- The end of a round: the cards done of those due, accuracy (OK counts
half) and time, over the whole session.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-07 17:40:32 +02:00
/// **'Cards'**
String get studyStatCards ;
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 19:29:21 +02:00
feat(app): study with faces on the ratings, centred cards and the session's figures
The study session, reworked:
- Ratings are coloured tiles with a face each (frown, meh, smile,
laugh), when the card comes back below them with a clock ("in 1
min", "in 3 days"), always shown, so a card can be rated without
revealing it; at most 540 dp wide, centred.
- Cards are centred in a section card: the question large, then a line
with 24 dp on either side and the answer a size smaller, or "Show
answer" 40 dp below; a tap anywhere on the card shows or hides the
answer. Read-only rich text takes a text style and an alignment.
- Words show their reading small above the expression once answered,
and several meanings numbered.
- Choice, matching and map cards: Check below the card, the ratings in
its place once checked; options marked before their text; matching
lists the categories first, then what is left to place; the map
fills the card with one caption below it.
- Header and progress on a band of their own; title start-aligned,
the Pomodoro timer only to watch, the back-first toggle in every
state; leaving a round under way asks first.
- Cramming goes round the round; back is there with two cards or more,
skipping always.
- The end of a round: the cards done of those due, accuracy (OK counts
half) and time, over the whole session.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-07 17:40:32 +02:00
/// No description provided for @studyStatAccuracy.
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 19:29:21 +02:00
///
/// In en, this message translates to:
feat(app): study with faces on the ratings, centred cards and the session's figures
The study session, reworked:
- Ratings are coloured tiles with a face each (frown, meh, smile,
laugh), when the card comes back below them with a clock ("in 1
min", "in 3 days"), always shown, so a card can be rated without
revealing it; at most 540 dp wide, centred.
- Cards are centred in a section card: the question large, then a line
with 24 dp on either side and the answer a size smaller, or "Show
answer" 40 dp below; a tap anywhere on the card shows or hides the
answer. Read-only rich text takes a text style and an alignment.
- Words show their reading small above the expression once answered,
and several meanings numbered.
- Choice, matching and map cards: Check below the card, the ratings in
its place once checked; options marked before their text; matching
lists the categories first, then what is left to place; the map
fills the card with one caption below it.
- Header and progress on a band of their own; title start-aligned,
the Pomodoro timer only to watch, the back-first toggle in every
state; leaving a round under way asks first.
- Cramming goes round the round; back is there with two cards or more,
skipping always.
- The end of a round: the cards done of those due, accuracy (OK counts
half) and time, over the whole session.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-07 17:40:32 +02:00
/// **'Accuracy'**
String get studyStatAccuracy ;
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 19:29:21 +02:00
feat(app): study with faces on the ratings, centred cards and the session's figures
The study session, reworked:
- Ratings are coloured tiles with a face each (frown, meh, smile,
laugh), when the card comes back below them with a clock ("in 1
min", "in 3 days"), always shown, so a card can be rated without
revealing it; at most 540 dp wide, centred.
- Cards are centred in a section card: the question large, then a line
with 24 dp on either side and the answer a size smaller, or "Show
answer" 40 dp below; a tap anywhere on the card shows or hides the
answer. Read-only rich text takes a text style and an alignment.
- Words show their reading small above the expression once answered,
and several meanings numbered.
- Choice, matching and map cards: Check below the card, the ratings in
its place once checked; options marked before their text; matching
lists the categories first, then what is left to place; the map
fills the card with one caption below it.
- Header and progress on a band of their own; title start-aligned,
the Pomodoro timer only to watch, the back-first toggle in every
state; leaving a round under way asks first.
- Cramming goes round the round; back is there with two cards or more,
skipping always.
- The end of a round: the cards done of those due, accuracy (OK counts
half) and time, over the whole session.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-07 17:40:32 +02:00
/// No description provided for @studyStatTime.
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 19:29:21 +02:00
///
/// In en, this message translates to:
feat(app): study with faces on the ratings, centred cards and the session's figures
The study session, reworked:
- Ratings are coloured tiles with a face each (frown, meh, smile,
laugh), when the card comes back below them with a clock ("in 1
min", "in 3 days"), always shown, so a card can be rated without
revealing it; at most 540 dp wide, centred.
- Cards are centred in a section card: the question large, then a line
with 24 dp on either side and the answer a size smaller, or "Show
answer" 40 dp below; a tap anywhere on the card shows or hides the
answer. Read-only rich text takes a text style and an alignment.
- Words show their reading small above the expression once answered,
and several meanings numbered.
- Choice, matching and map cards: Check below the card, the ratings in
its place once checked; options marked before their text; matching
lists the categories first, then what is left to place; the map
fills the card with one caption below it.
- Header and progress on a band of their own; title start-aligned,
the Pomodoro timer only to watch, the back-first toggle in every
state; leaving a round under way asks first.
- Cramming goes round the round; back is there with two cards or more,
skipping always.
- The end of a round: the cards done of those due, accuracy (OK counts
half) and time, over the whole session.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-07 17:40:32 +02:00
/// **'Time'**
String get studyStatTime ;
/// No description provided for @studyDurationSec.
///
/// In en, this message translates to:
/// **'{sec} sec'**
String studyDurationSec ( int sec ) ;
/// No description provided for @studyDurationMin.
///
/// In en, this message translates to:
/// **'{min} min'**
String studyDurationMin ( int min ) ;
/// No description provided for @studyDurationMinSec.
///
/// In en, this message translates to:
/// **'{min} min {sec} sec'**
String studyDurationMinSec ( int min , int sec ) ;
/// No description provided for @studyLeaveTitle.
///
/// In en, this message translates to:
/// **'Leave this study session?'**
String get studyLeaveTitle ;
/// No description provided for @studyLeaveRated.
///
/// In en, this message translates to:
/// **'{count, plural, =1{Your ratings so far are saved, but the last card in this round will be discarded if you leave now.} other{Your ratings so far are saved, but the {count} cards still left in this round will be discarded if you leave now.}}'**
String studyLeaveRated ( int count ) ;
/// No description provided for @studyLeaveUnrated.
///
/// In en, this message translates to:
/// **'{count, plural, =1{You haven’ t rated this card yet — leaving ends the session without recording any progress for this round.} other{You haven’ t rated any of the {count} cards in this round yet — leaving ends the session without recording any progress for it.}}'**
String studyLeaveUnrated ( int count ) ;
/// No description provided for @studyLeaveConfirm.
///
/// In en, this message translates to:
/// **'Leave'**
String get studyLeaveConfirm ;
/// No description provided for @studyKeepStudying.
///
/// In en, this message translates to:
/// **'Keep studying'**
String get studyKeepStudying ;
/// No description provided for @studyInMinutes.
///
/// In en, this message translates to:
/// **'{count, plural, =1{in 1 min} other{in {count} min}}'**
String studyInMinutes ( int count ) ;
/// No description provided for @studyInHours.
///
/// In en, this message translates to:
/// **'{count, plural, =1{in 1 hour} other{in {count} hours}}'**
String studyInHours ( int count ) ;
/// No description provided for @studyInDays.
///
/// In en, this message translates to:
/// **'{count, plural, =1{in 1 day} other{in {count} days}}'**
String studyInDays ( int count ) ;
/// No description provided for @studyInMonths.
///
/// In en, this message translates to:
/// **'{count, plural, =1{in 1 month} other{in {count} months}}'**
String studyInMonths ( int count ) ;
/// No description provided for @studyInYears.
///
/// In en, this message translates to:
/// **'{count, plural, =1{in 1 year} other{in {count} years}}'**
String studyInYears ( int count ) ;
/// No description provided for @studyContinue.
///
/// In en, this message translates to:
/// **'Continue learning'**
String get studyContinue ;
/// No description provided for @studyPrevious.
///
/// In en, this message translates to:
/// **'Previous card'**
String get studyPrevious ;
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 19:29:21 +02:00
feat(app): study with faces on the ratings, centred cards and the session's figures
The study session, reworked:
- Ratings are coloured tiles with a face each (frown, meh, smile,
laugh), when the card comes back below them with a clock ("in 1
min", "in 3 days"), always shown, so a card can be rated without
revealing it; at most 540 dp wide, centred.
- Cards are centred in a section card: the question large, then a line
with 24 dp on either side and the answer a size smaller, or "Show
answer" 40 dp below; a tap anywhere on the card shows or hides the
answer. Read-only rich text takes a text style and an alignment.
- Words show their reading small above the expression once answered,
and several meanings numbered.
- Choice, matching and map cards: Check below the card, the ratings in
its place once checked; options marked before their text; matching
lists the categories first, then what is left to place; the map
fills the card with one caption below it.
- Header and progress on a band of their own; title start-aligned,
the Pomodoro timer only to watch, the back-first toggle in every
state; leaving a round under way asks first.
- Cramming goes round the round; back is there with two cards or more,
skipping always.
- The end of a round: the cards done of those due, accuracy (OK counts
half) and time, over the whole session.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-07 17:40:32 +02:00
/// No description provided for @studySkipCard.
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 19:29:21 +02:00
///
/// In en, this message translates to:
feat(app): study with faces on the ratings, centred cards and the session's figures
The study session, reworked:
- Ratings are coloured tiles with a face each (frown, meh, smile,
laugh), when the card comes back below them with a clock ("in 1
min", "in 3 days"), always shown, so a card can be rated without
revealing it; at most 540 dp wide, centred.
- Cards are centred in a section card: the question large, then a line
with 24 dp on either side and the answer a size smaller, or "Show
answer" 40 dp below; a tap anywhere on the card shows or hides the
answer. Read-only rich text takes a text style and an alignment.
- Words show their reading small above the expression once answered,
and several meanings numbered.
- Choice, matching and map cards: Check below the card, the ratings in
its place once checked; options marked before their text; matching
lists the categories first, then what is left to place; the map
fills the card with one caption below it.
- Header and progress on a band of their own; title start-aligned,
the Pomodoro timer only to watch, the back-first toggle in every
state; leaving a round under way asks first.
- Cramming goes round the round; back is there with two cards or more,
skipping always.
- The end of a round: the cards done of those due, accuracy (OK counts
half) and time, over the whole session.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-07 17:40:32 +02:00
/// **'Skip card'**
String get studySkipCard ;
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 19:29:21 +02:00
/// No description provided for @studyMinutes.
///
/// In en, this message translates to:
/// **'{count} min'**
String studyMinutes ( int count ) ;
/// No description provided for @studyHours.
///
/// In en, this message translates to:
/// **'{count} h'**
String studyHours ( int count ) ;
/// No description provided for @studyDays.
///
/// In en, this message translates to:
/// **'{count, plural, =1{1 day} other{{count} days}}'**
String studyDays ( int count ) ;
/// No description provided for @studyMonths.
///
/// In en, this message translates to:
/// **'{count, plural, =1{1 month} other{{count} months}}'**
String studyMonths ( int count ) ;
/// No description provided for @studyYears.
///
/// In en, this message translates to:
/// **'{count, plural, =1{1 year} other{{count} years}}'**
String studyYears ( int count ) ;
/// No description provided for @studyFilteredBy.
///
/// In en, this message translates to:
/// **'{count, plural, =1{Narrowed to 1 tag} other{Narrowed to {count} tags}}'**
String studyFilteredBy ( int count ) ;
/// No description provided for @studySettingsCardsPerRound.
///
/// In en, this message translates to:
/// **'Cards per round'**
String get studySettingsCardsPerRound ;
/// No description provided for @studySettingsNewPerDay.
///
/// In en, this message translates to:
/// **'New cards per day'**
String get studySettingsNewPerDay ;
/// No description provided for @studySettingsNewPerDayHint.
///
/// In en, this message translates to:
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 02:18:42 +02:00
/// **'How many never-studied cards each studyset starts a day, unless its own study options say otherwise.'**
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 19:29:21 +02:00
String get studySettingsNewPerDayHint ;
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 02:18:42 +02:00
/// No description provided for @studyOptionsTitle.
///
/// In en, this message translates to:
/// **'Study options'**
String get studyOptionsTitle ;
/// No description provided for @studyOptionsDescription.
///
/// In en, this message translates to:
/// **'How many cards this studyset brings a day, and which come first, on every device. The studysets inside it keep their own.'**
String get studyOptionsDescription ;
/// No description provided for @studyOptionsReviewsPerDay.
///
/// In en, this message translates to:
/// **'Reviews per day'**
String get studyOptionsReviewsPerDay ;
/// No description provided for @studyOptionsOrder.
///
/// In en, this message translates to:
/// **'Order'**
String get studyOptionsOrder ;
/// No description provided for @studyOptionsReviewsFirst.
///
/// In en, this message translates to:
/// **'Reviews first'**
String get studyOptionsReviewsFirst ;
/// No description provided for @studyOptionsMixed.
///
/// In en, this message translates to:
/// **'Mixed'**
String get studyOptionsMixed ;
/// No description provided for @studyOptionsNewFirst.
///
/// In en, this message translates to:
/// **'New first'**
String get studyOptionsNewFirst ;
/// No description provided for @studyOptionsReset.
///
/// In en, this message translates to:
/// **'Reset to defaults'**
String get studyOptionsReset ;
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 19:29:21 +02:00
/// No description provided for @studySettingsUnlimited.
///
/// In en, this message translates to:
/// **'Unlimited'**
String get studySettingsUnlimited ;
/// No description provided for @studySettingsSortBy.
///
/// In en, this message translates to:
/// **'Sort by'**
String get studySettingsSortBy ;
/// No description provided for @studySettingsLearnMode.
///
/// In en, this message translates to:
/// **'Learn mode'**
String get studySettingsLearnMode ;
/// No description provided for @studyModeSpaced.
///
/// In en, this message translates to:
/// **'Spaced repetition'**
String get studyModeSpaced ;
/// No description provided for @studyModeSpacedHint.
///
/// In en, this message translates to:
/// **'Due cards on their schedules; ratings move them.'**
String get studyModeSpacedHint ;
/// No description provided for @studyModeCram.
///
/// In en, this message translates to:
/// **'Cram'**
String get studyModeCram ;
/// No description provided for @studyModeCramHint.
///
/// In en, this message translates to:
/// **'Walk every card without touching the schedules.'**
String get studyModeCramHint ;
2026-10-07 21:57:04 +02:00
/// No description provided for @studySettingsVocabulary.
///
/// In en, this message translates to:
/// **'Vocabulary'**
String get studySettingsVocabulary ;
/// No description provided for @studySettingsQuizMc.
///
/// In en, this message translates to:
/// **'Quiz with multiple choice'**
String get studySettingsQuizMc ;
/// No description provided for @studySettingsQuizMcDescription.
///
/// In en, this message translates to:
/// **'Show vocabulary cards as a multiple-choice question, with wrong answers drawn from other vocabulary in this set.'**
String get studySettingsQuizMcDescription ;
feat(app): type a word's reading while studying
A new study option, Type the reading (settings schema 6, beside the
vocabulary quiz; turning one on turns the other off): a word with kanji
shows alone, and its reading is typed, in kana; romaji turn into kana as
they are typed (doubled consonants, n before a consonant, nn and n'), and
the device's own Japanese input works as it does. Check, or Enter, says
whether it was right (katakana counts as hiragana) and what was typed
beside the reading, then shows the meanings and the ratings; the person
still rates, as Anki's typed answers have it. A word written in kana, one
usually written so, and a card asked back first flip as before.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MMm7usAo6q9ZZRoPkWjoxi
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-09 02:09:10 +02:00
/// No description provided for @studySettingsTypeReading.
///
/// In en, this message translates to:
/// **'Type the reading'**
String get studySettingsTypeReading ;
/// No description provided for @studySettingsTypeReadingDescription.
///
/// In en, this message translates to:
feat(dictionary): characters of Chinese, Korean and Greek, written by hand; pinyin typed
Parity with Japanese's kanji and kana:
- The characters' tab is named by its language (Hanzi, Hangul & Hanja,
Alphabet), its groups too: HSK levels and the characters in common use
(Hong Kong's grades for traditional ones); Hangul and the Hanja taught
in schools; Greek's letters. Radicals for Chinese and Korean in their
own forms (讠 for 言 in simplified Chinese), none for Greek.
- A character's readings are named as its language names them: On and
Kun, Pinyin, Eum and Hun (음, 훈); a letter means its sound and name.
- How characters are written comes by language: Chinese from Make Me a
Hanzi (its medians in KanjiVG's square, Arphic Public License, a data
file beside the app, never in cards), the Hangul letters drawn by the
curator; a Korean word is written letter by letter (닭: ㄷ ㅏ ㄺ). Words
of alphabets (German, Greek) aren't written by hand.
- A Chinese word's pinyin is typed in a session, tone numbers turning
into marks (ni3 → nǐ); KRDict's levels (초급, 중급, 고급) tag Korean
words; a Korean word's pronunciation shows in brackets ([가치]).
- Smaller: Look up in the composer keeps a noun's article; the Watch
filter names its level list; hints and placeholders by language.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-14 12:35:17 +02:00
/// **'Words with kanji ask for their reading, typed in kana (romaji turn into kana as you type); Chinese words for their pinyin (tone numbers turn into marks: ni3 becomes nǐ).'**
feat(app): type a word's reading while studying
A new study option, Type the reading (settings schema 6, beside the
vocabulary quiz; turning one on turns the other off): a word with kanji
shows alone, and its reading is typed, in kana; romaji turn into kana as
they are typed (doubled consonants, n before a consonant, nn and n'), and
the device's own Japanese input works as it does. Check, or Enter, says
whether it was right (katakana counts as hiragana) and what was typed
beside the reading, then shows the meanings and the ratings; the person
still rates, as Anki's typed answers have it. A word written in kana, one
usually written so, and a card asked back first flip as before.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MMm7usAo6q9ZZRoPkWjoxi
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-09 02:09:10 +02:00
String get studySettingsTypeReadingDescription ;
/// No description provided for @studyTypedHint.
///
/// In en, this message translates to:
/// **'The reading, in kana or romaji'**
String get studyTypedHint ;
feat(dictionary): characters of Chinese, Korean and Greek, written by hand; pinyin typed
Parity with Japanese's kanji and kana:
- The characters' tab is named by its language (Hanzi, Hangul & Hanja,
Alphabet), its groups too: HSK levels and the characters in common use
(Hong Kong's grades for traditional ones); Hangul and the Hanja taught
in schools; Greek's letters. Radicals for Chinese and Korean in their
own forms (讠 for 言 in simplified Chinese), none for Greek.
- A character's readings are named as its language names them: On and
Kun, Pinyin, Eum and Hun (음, 훈); a letter means its sound and name.
- How characters are written comes by language: Chinese from Make Me a
Hanzi (its medians in KanjiVG's square, Arphic Public License, a data
file beside the app, never in cards), the Hangul letters drawn by the
curator; a Korean word is written letter by letter (닭: ㄷ ㅏ ㄺ). Words
of alphabets (German, Greek) aren't written by hand.
- A Chinese word's pinyin is typed in a session, tone numbers turning
into marks (ni3 → nǐ); KRDict's levels (초급, 중급, 고급) tag Korean
words; a Korean word's pronunciation shows in brackets ([가치]).
- Smaller: Look up in the composer keeps a noun's article; the Watch
filter names its level list; hints and placeholders by language.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-14 12:35:17 +02:00
/// No description provided for @studyTypedHintPinyin.
///
/// In en, this message translates to:
/// **'The pinyin, with tone numbers or marks'**
String get studyTypedHintPinyin ;
feat(app): type a word's reading while studying
A new study option, Type the reading (settings schema 6, beside the
vocabulary quiz; turning one on turns the other off): a word with kanji
shows alone, and its reading is typed, in kana; romaji turn into kana as
they are typed (doubled consonants, n before a consonant, nn and n'), and
the device's own Japanese input works as it does. Check, or Enter, says
whether it was right (katakana counts as hiragana) and what was typed
beside the reading, then shows the meanings and the ratings; the person
still rates, as Anki's typed answers have it. A word written in kana, one
usually written so, and a card asked back first flip as before.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MMm7usAo6q9ZZRoPkWjoxi
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-09 02:09:10 +02:00
/// No description provided for @studyTypedRight.
///
/// In en, this message translates to:
/// **'Right'**
String get studyTypedRight ;
/// No description provided for @studyTypedWrong.
///
/// In en, this message translates to:
/// **'You typed {answer}'**
String studyTypedWrong ( String answer ) ;
2026-10-07 21:57:04 +02:00
/// No description provided for @studyAspectMeaning.
///
/// In en, this message translates to:
/// **'Meaning'**
String get studyAspectMeaning ;
/// No description provided for @studyAspectReading.
///
/// In en, this message translates to:
/// **'Reading'**
String get studyAspectReading ;
/// No description provided for @studyAspectExpression.
///
/// In en, this message translates to:
/// **'Expression'**
String get studyAspectExpression ;
/// No description provided for @studyAspectMixed.
///
/// In en, this message translates to:
/// **'Mixed'**
String get studyAspectMixed ;
/// No description provided for @studySettingsAspectDescription.
///
/// In en, this message translates to:
feat(dictionary): characters of Chinese, Korean and Greek, written by hand; pinyin typed
Parity with Japanese's kanji and kana:
- The characters' tab is named by its language (Hanzi, Hangul & Hanja,
Alphabet), its groups too: HSK levels and the characters in common use
(Hong Kong's grades for traditional ones); Hangul and the Hanja taught
in schools; Greek's letters. Radicals for Chinese and Korean in their
own forms (讠 for 言 in simplified Chinese), none for Greek.
- A character's readings are named as its language names them: On and
Kun, Pinyin, Eum and Hun (음, 훈); a letter means its sound and name.
- How characters are written comes by language: Chinese from Make Me a
Hanzi (its medians in KanjiVG's square, Arphic Public License, a data
file beside the app, never in cards), the Hangul letters drawn by the
curator; a Korean word is written letter by letter (닭: ㄷ ㅏ ㄺ). Words
of alphabets (German, Greek) aren't written by hand.
- A Chinese word's pinyin is typed in a session, tone numbers turning
into marks (ni3 → nǐ); KRDict's levels (초급, 중급, 고급) tag Korean
words; a Korean word's pronunciation shows in brackets ([가치]).
- Smaller: Look up in the composer keeps a noun's article; the Watch
filter names its level list; hints and placeholders by language.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-14 12:35:17 +02:00
/// **'What to recall — the meaning, the reading (kana, pinyin), or the expression itself (shown the meaning). Mixed varies meaning and reading per card; cards that can’ t be quizzed stay a normal flip.'**
2026-10-07 21:57:04 +02:00
String get studySettingsAspectDescription ;
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 19:29:21 +02:00
/// No description provided for @studySortOldestFirst.
///
/// In en, this message translates to:
/// **'Oldest first'**
String get studySortOldestFirst ;
/// No description provided for @studySortRandom.
///
/// In en, this message translates to:
/// **'Random'**
String get studySortRandom ;
/// No description provided for @studySortNewestFirst.
///
/// In en, this message translates to:
/// **'Newest first'**
String get studySortNewestFirst ;
feat(study): limits switch on and off, step by 5, and take any number typed
Your calls (2026-10-11): a switch per limit, and steps of 5 with any
number typed.
- A daily limit (new cards, reviews; in a studyset's study options and the
study settings) is a switch, "Limit new cards per day", and its number
while on. Counting down to 0 for no limit had to be guessed, and a goal
has nothing like it. Switched off and on again, the limit comes back as
it was; the first time, at the defaults' 20 new cards, or 200 reviews as
Anki starts them. Stored as before: 0 still stands for none.
- The study goal counts in steps of 5 as the limits do (reviews: 25).
Plus and minus go to the next multiple, so 12 steps to 15 or 10, never
below the first step.
- Tapping the number lets any other be typed, from 1 (CountStepper.onTyped).
Pomodoro minutes and cards per round keep their own steps.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MMm7usAo6q9ZZRoPkWjoxi
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-11 14:14:49 +02:00
/// No description provided for @studyLimitNewPerDay.
///
/// In en, this message translates to:
/// **'Limit new cards per day'**
String get studyLimitNewPerDay ;
/// No description provided for @studyLimitReviewsPerDay.
///
/// In en, this message translates to:
/// **'Limit reviews per day'**
String get studyLimitReviewsPerDay ;
2026-10-01 19:34:44 +02:00
/// No description provided for @studysetMenuReset.
///
/// In en, this message translates to:
/// **'Reset progress'**
String get studysetMenuReset ;
/// No description provided for @studysetResetTitle.
///
/// In en, this message translates to:
/// **'Reset progress?'**
String get studysetResetTitle ;
/// No description provided for @studysetResetMessage.
///
/// In en, this message translates to:
2026-10-12 23:19:31 +02:00
/// **'What you choose starts afresh, in it and in the studysets inside it, on all your devices. Your study history stays. You can undo this for a few seconds.'**
2026-10-01 19:34:44 +02:00
String get studysetResetMessage ;
2026-10-12 23:19:31 +02:00
/// No description provided for @studysetResetCards.
///
/// In en, this message translates to:
/// **'Flashcards'**
String get studysetResetCards ;
/// No description provided for @studysetResetCardsDetail.
///
/// In en, this message translates to:
/// **'{count, plural, =1{1 flashcard starts again as new} other{{count} flashcards start again as new}}'**
String studysetResetCardsDetail ( int count ) ;
/// No description provided for @studysetResetPdfs.
///
/// In en, this message translates to:
/// **'Reading of PDFs'**
String get studysetResetPdfs ;
/// No description provided for @studysetResetPdfsDetail.
///
/// In en, this message translates to:
/// **'{count, plural, =1{1 PDF is read from its first page again} other{{count} PDFs are read from their first page again}}'**
String studysetResetPdfsDetail ( int count ) ;
/// No description provided for @studysetResetPaths.
///
/// In en, this message translates to:
/// **'Learning paths'**
String get studysetResetPaths ;
/// No description provided for @studysetResetPathsDetail.
///
/// In en, this message translates to:
/// **'{count, plural, =1{1 learning path starts from its first step again} other{{count} learning paths start from their first step again}}'**
String studysetResetPathsDetail ( int count ) ;
/// No description provided for @studysetResetNothing.
///
/// In en, this message translates to:
/// **'Nothing in it has progress yet.'**
String get studysetResetNothing ;
2026-10-01 19:34:44 +02:00
/// No description provided for @studysetResetConfirm.
///
/// In en, this message translates to:
/// **'Reset'**
String get studysetResetConfirm ;
/// No description provided for @studysetResetDone.
///
/// In en, this message translates to:
/// **'Progress reset'**
String get studysetResetDone ;
2026-10-01 19:56:29 +02:00
/// No description provided for @mapUnknown.
///
/// In en, this message translates to:
/// **'This map needs a newer version of the app.'**
String get mapUnknown ;
/// No description provided for @mapPickRegion.
///
/// In en, this message translates to:
feat(map): one map, Japan's prefectures within Japan
A map question is picked on one map: the world's countries in layers
(Europe, East Asia, ...), and within Japan its prefectures, in layers too
(Kantō, Kansai, ...); Back goes up one. The composer's choice between two
maps is gone. A card still names the map its answer is a region of
(`japan`, `world`), as versions up to v0.11.0 did, so they still show it.
In the composer a tap on Japan picks the country, and the "Prefectures"
chip opens its prefectures. Studying, the card decides: a prefecture's
question opens Japan at a tap, a country's picks it.
The map opens on the deepest layer the map questions share (studying, the
round's; writing, the studyset's): Japan for prefectures all over it,
Kantō for Tokyo and Chiba, Europe for European countries.
A tap no longer zooms in when it could mean several regions: it picks the
ring it is on (the nearest of several), else the region under it, else the
nearest ring or outline within reach. Zooming is pinching, or Ctrl (⌘ on a
Mac) and the wheel, as maps on web pages do; the wheel alone scrolls the
page, and a note over the map says how to zoom.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MMm7usAo6q9ZZRoPkWjoxi
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-12 12:19:03 +02:00
/// **'Choose a part of the map, then tap the region that answers the question. Tiny places have a ring; where rings crowd, zoom in.'**
2026-10-01 19:56:29 +02:00
String get mapPickRegion ;
/// No description provided for @mapNonePicked.
///
/// In en, this message translates to:
/// **'No region picked yet'**
String get mapNonePicked ;
2026-10-12 18:02:15 +02:00
/// No description provided for @mapPickTitle.
///
/// In en, this message translates to:
/// **'Pick the region'**
String get mapPickTitle ;
feat(map): one map, Japan's prefectures within Japan
A map question is picked on one map: the world's countries in layers
(Europe, East Asia, ...), and within Japan its prefectures, in layers too
(Kantō, Kansai, ...); Back goes up one. The composer's choice between two
maps is gone. A card still names the map its answer is a region of
(`japan`, `world`), as versions up to v0.11.0 did, so they still show it.
In the composer a tap on Japan picks the country, and the "Prefectures"
chip opens its prefectures. Studying, the card decides: a prefecture's
question opens Japan at a tap, a country's picks it.
The map opens on the deepest layer the map questions share (studying, the
round's; writing, the studyset's): Japan for prefectures all over it,
Kantō for Tokyo and Chiba, Europe for European countries.
A tap no longer zooms in when it could mean several regions: it picks the
ring it is on (the nearest of several), else the region under it, else the
nearest ring or outline within reach. Zooming is pinching, or Ctrl (⌘ on a
Mac) and the wheel, as maps on web pages do; the wheel alone scrolls the
page, and a note over the map says how to zoom.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MMm7usAo6q9ZZRoPkWjoxi
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-12 12:19:03 +02:00
/// No description provided for @mapNameJapan.
feat(map): every country on earth, picked continent first
Map questions can now be on the world's countries besides Japan's
prefectures: a new question chooses its map above it.
The world is Natural Earth's 1:50m countries (public domain), as they are
on the ground, one region per ISO 3166-1 code (239; Gibraltar and the US
Outlying Islands from the 1:10m layer, which has them), named in every
language a studyset can be in from the Unicode CLDR (Unicode License v3,
its notice in the licence list). tool/maps/world_countries.py builds it;
the outline code it shares with the Japan script moved to outlines.py,
which still writes Japan's file byte for byte. The 1.8 MB file is parsed
off the screen's isolate.
Picking goes in layers on both maps: on the whole map one taps a group
(a continent, or Kantō, Kansai …; chips name them too) and the map flies
there, then one taps the region; Back returns. A group of one (Hokkaidō,
Okinawa) and what is in none (Antarctica) are picked at once. A country
in two groups (Russia, Türkiye, Cyprus) opens the one nearer the tap.
Oceania's frame runs past 180° so the Pacific stays in one piece.
What is still too small to hit gets a ring, 48 dp of reach as Material's
touch target: on the ring it is picked; near it, where the tap could mean
it or its neighbours, the map zooms in rather than guess. Pinching and a
mouse's wheel zoom as before.
Names show in the app's language, beside them the map's own (Japanese for
Japan) or the studyset's (キプロス beside Cyprus in a Japanese studyset).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MMm7usAo6q9ZZRoPkWjoxi
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-12 07:18:20 +02:00
///
/// In en, this message translates to:
feat(map): one map, Japan's prefectures within Japan
A map question is picked on one map: the world's countries in layers
(Europe, East Asia, ...), and within Japan its prefectures, in layers too
(Kantō, Kansai, ...); Back goes up one. The composer's choice between two
maps is gone. A card still names the map its answer is a region of
(`japan`, `world`), as versions up to v0.11.0 did, so they still show it.
In the composer a tap on Japan picks the country, and the "Prefectures"
chip opens its prefectures. Studying, the card decides: a prefecture's
question opens Japan at a tap, a country's picks it.
The map opens on the deepest layer the map questions share (studying, the
round's; writing, the studyset's): Japan for prefectures all over it,
Kantō for Tokyo and Chiba, Europe for European countries.
A tap no longer zooms in when it could mean several regions: it picks the
ring it is on (the nearest of several), else the region under it, else the
nearest ring or outline within reach. Zooming is pinching, or Ctrl (⌘ on a
Mac) and the wheel, as maps on web pages do; the wheel alone scrolls the
page, and a note over the map says how to zoom.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MMm7usAo6q9ZZRoPkWjoxi
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-12 12:19:03 +02:00
/// **'Japan'**
String get mapNameJapan ;
feat(map): every country on earth, picked continent first
Map questions can now be on the world's countries besides Japan's
prefectures: a new question chooses its map above it.
The world is Natural Earth's 1:50m countries (public domain), as they are
on the ground, one region per ISO 3166-1 code (239; Gibraltar and the US
Outlying Islands from the 1:10m layer, which has them), named in every
language a studyset can be in from the Unicode CLDR (Unicode License v3,
its notice in the licence list). tool/maps/world_countries.py builds it;
the outline code it shares with the Japan script moved to outlines.py,
which still writes Japan's file byte for byte. The 1.8 MB file is parsed
off the screen's isolate.
Picking goes in layers on both maps: on the whole map one taps a group
(a continent, or Kantō, Kansai …; chips name them too) and the map flies
there, then one taps the region; Back returns. A group of one (Hokkaidō,
Okinawa) and what is in none (Antarctica) are picked at once. A country
in two groups (Russia, Türkiye, Cyprus) opens the one nearer the tap.
Oceania's frame runs past 180° so the Pacific stays in one piece.
What is still too small to hit gets a ring, 48 dp of reach as Material's
touch target: on the ring it is picked; near it, where the tap could mean
it or its neighbours, the map zooms in rather than guess. Pinching and a
mouse's wheel zoom as before.
Names show in the app's language, beside them the map's own (Japanese for
Japan) or the studyset's (キプロス beside Cyprus in a Japanese studyset).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MMm7usAo6q9ZZRoPkWjoxi
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-12 07:18:20 +02:00
feat(map): one map, Japan's prefectures within Japan
A map question is picked on one map: the world's countries in layers
(Europe, East Asia, ...), and within Japan its prefectures, in layers too
(Kantō, Kansai, ...); Back goes up one. The composer's choice between two
maps is gone. A card still names the map its answer is a region of
(`japan`, `world`), as versions up to v0.11.0 did, so they still show it.
In the composer a tap on Japan picks the country, and the "Prefectures"
chip opens its prefectures. Studying, the card decides: a prefecture's
question opens Japan at a tap, a country's picks it.
The map opens on the deepest layer the map questions share (studying, the
round's; writing, the studyset's): Japan for prefectures all over it,
Kantō for Tokyo and Chiba, Europe for European countries.
A tap no longer zooms in when it could mean several regions: it picks the
ring it is on (the nearest of several), else the region under it, else the
nearest ring or outline within reach. Zooming is pinching, or Ctrl (⌘ on a
Mac) and the wheel, as maps on web pages do; the wheel alone scrolls the
page, and a note over the map says how to zoom.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MMm7usAo6q9ZZRoPkWjoxi
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-12 12:19:03 +02:00
/// No description provided for @mapNameWorld.
feat(map): every country on earth, picked continent first
Map questions can now be on the world's countries besides Japan's
prefectures: a new question chooses its map above it.
The world is Natural Earth's 1:50m countries (public domain), as they are
on the ground, one region per ISO 3166-1 code (239; Gibraltar and the US
Outlying Islands from the 1:10m layer, which has them), named in every
language a studyset can be in from the Unicode CLDR (Unicode License v3,
its notice in the licence list). tool/maps/world_countries.py builds it;
the outline code it shares with the Japan script moved to outlines.py,
which still writes Japan's file byte for byte. The 1.8 MB file is parsed
off the screen's isolate.
Picking goes in layers on both maps: on the whole map one taps a group
(a continent, or Kantō, Kansai …; chips name them too) and the map flies
there, then one taps the region; Back returns. A group of one (Hokkaidō,
Okinawa) and what is in none (Antarctica) are picked at once. A country
in two groups (Russia, Türkiye, Cyprus) opens the one nearer the tap.
Oceania's frame runs past 180° so the Pacific stays in one piece.
What is still too small to hit gets a ring, 48 dp of reach as Material's
touch target: on the ring it is picked; near it, where the tap could mean
it or its neighbours, the map zooms in rather than guess. Pinching and a
mouse's wheel zoom as before.
Names show in the app's language, beside them the map's own (Japanese for
Japan) or the studyset's (キプロス beside Cyprus in a Japanese studyset).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MMm7usAo6q9ZZRoPkWjoxi
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-12 07:18:20 +02:00
///
/// In en, this message translates to:
feat(map): one map, Japan's prefectures within Japan
A map question is picked on one map: the world's countries in layers
(Europe, East Asia, ...), and within Japan its prefectures, in layers too
(Kantō, Kansai, ...); Back goes up one. The composer's choice between two
maps is gone. A card still names the map its answer is a region of
(`japan`, `world`), as versions up to v0.11.0 did, so they still show it.
In the composer a tap on Japan picks the country, and the "Prefectures"
chip opens its prefectures. Studying, the card decides: a prefecture's
question opens Japan at a tap, a country's picks it.
The map opens on the deepest layer the map questions share (studying, the
round's; writing, the studyset's): Japan for prefectures all over it,
Kantō for Tokyo and Chiba, Europe for European countries.
A tap no longer zooms in when it could mean several regions: it picks the
ring it is on (the nearest of several), else the region under it, else the
nearest ring or outline within reach. Zooming is pinching, or Ctrl (⌘ on a
Mac) and the wheel, as maps on web pages do; the wheel alone scrolls the
page, and a note over the map says how to zoom.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MMm7usAo6q9ZZRoPkWjoxi
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-12 12:19:03 +02:00
/// **'World'**
String get mapNameWorld ;
feat(map): every country on earth, picked continent first
Map questions can now be on the world's countries besides Japan's
prefectures: a new question chooses its map above it.
The world is Natural Earth's 1:50m countries (public domain), as they are
on the ground, one region per ISO 3166-1 code (239; Gibraltar and the US
Outlying Islands from the 1:10m layer, which has them), named in every
language a studyset can be in from the Unicode CLDR (Unicode License v3,
its notice in the licence list). tool/maps/world_countries.py builds it;
the outline code it shares with the Japan script moved to outlines.py,
which still writes Japan's file byte for byte. The 1.8 MB file is parsed
off the screen's isolate.
Picking goes in layers on both maps: on the whole map one taps a group
(a continent, or Kantō, Kansai …; chips name them too) and the map flies
there, then one taps the region; Back returns. A group of one (Hokkaidō,
Okinawa) and what is in none (Antarctica) are picked at once. A country
in two groups (Russia, Türkiye, Cyprus) opens the one nearer the tap.
Oceania's frame runs past 180° so the Pacific stays in one piece.
What is still too small to hit gets a ring, 48 dp of reach as Material's
touch target: on the ring it is picked; near it, where the tap could mean
it or its neighbours, the map zooms in rather than guess. Pinching and a
mouse's wheel zoom as before.
Names show in the app's language, beside them the map's own (Japanese for
Japan) or the studyset's (キプロス beside Cyprus in a Japanese studyset).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MMm7usAo6q9ZZRoPkWjoxi
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-12 07:18:20 +02:00
feat(map): one map, Japan's prefectures within Japan
A map question is picked on one map: the world's countries in layers
(Europe, East Asia, ...), and within Japan its prefectures, in layers too
(Kantō, Kansai, ...); Back goes up one. The composer's choice between two
maps is gone. A card still names the map its answer is a region of
(`japan`, `world`), as versions up to v0.11.0 did, so they still show it.
In the composer a tap on Japan picks the country, and the "Prefectures"
chip opens its prefectures. Studying, the card decides: a prefecture's
question opens Japan at a tap, a country's picks it.
The map opens on the deepest layer the map questions share (studying, the
round's; writing, the studyset's): Japan for prefectures all over it,
Kantō for Tokyo and Chiba, Europe for European countries.
A tap no longer zooms in when it could mean several regions: it picks the
ring it is on (the nearest of several), else the region under it, else the
nearest ring or outline within reach. Zooming is pinching, or Ctrl (⌘ on a
Mac) and the wheel, as maps on web pages do; the wheel alone scrolls the
page, and a note over the map says how to zoom.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MMm7usAo6q9ZZRoPkWjoxi
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-12 12:19:03 +02:00
/// No description provided for @mapPrefectures.
feat(map): every country on earth, picked continent first
Map questions can now be on the world's countries besides Japan's
prefectures: a new question chooses its map above it.
The world is Natural Earth's 1:50m countries (public domain), as they are
on the ground, one region per ISO 3166-1 code (239; Gibraltar and the US
Outlying Islands from the 1:10m layer, which has them), named in every
language a studyset can be in from the Unicode CLDR (Unicode License v3,
its notice in the licence list). tool/maps/world_countries.py builds it;
the outline code it shares with the Japan script moved to outlines.py,
which still writes Japan's file byte for byte. The 1.8 MB file is parsed
off the screen's isolate.
Picking goes in layers on both maps: on the whole map one taps a group
(a continent, or Kantō, Kansai …; chips name them too) and the map flies
there, then one taps the region; Back returns. A group of one (Hokkaidō,
Okinawa) and what is in none (Antarctica) are picked at once. A country
in two groups (Russia, Türkiye, Cyprus) opens the one nearer the tap.
Oceania's frame runs past 180° so the Pacific stays in one piece.
What is still too small to hit gets a ring, 48 dp of reach as Material's
touch target: on the ring it is picked; near it, where the tap could mean
it or its neighbours, the map zooms in rather than guess. Pinching and a
mouse's wheel zoom as before.
Names show in the app's language, beside them the map's own (Japanese for
Japan) or the studyset's (キプロス beside Cyprus in a Japanese studyset).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MMm7usAo6q9ZZRoPkWjoxi
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-12 07:18:20 +02:00
///
/// In en, this message translates to:
feat(map): one map, Japan's prefectures within Japan
A map question is picked on one map: the world's countries in layers
(Europe, East Asia, ...), and within Japan its prefectures, in layers too
(Kantō, Kansai, ...); Back goes up one. The composer's choice between two
maps is gone. A card still names the map its answer is a region of
(`japan`, `world`), as versions up to v0.11.0 did, so they still show it.
In the composer a tap on Japan picks the country, and the "Prefectures"
chip opens its prefectures. Studying, the card decides: a prefecture's
question opens Japan at a tap, a country's picks it.
The map opens on the deepest layer the map questions share (studying, the
round's; writing, the studyset's): Japan for prefectures all over it,
Kantō for Tokyo and Chiba, Europe for European countries.
A tap no longer zooms in when it could mean several regions: it picks the
ring it is on (the nearest of several), else the region under it, else the
nearest ring or outline within reach. Zooming is pinching, or Ctrl (⌘ on a
Mac) and the wheel, as maps on web pages do; the wheel alone scrolls the
page, and a note over the map says how to zoom.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MMm7usAo6q9ZZRoPkWjoxi
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-12 12:19:03 +02:00
/// **'Prefectures'**
String get mapPrefectures ;
feat(map): every country on earth, picked continent first
Map questions can now be on the world's countries besides Japan's
prefectures: a new question chooses its map above it.
The world is Natural Earth's 1:50m countries (public domain), as they are
on the ground, one region per ISO 3166-1 code (239; Gibraltar and the US
Outlying Islands from the 1:10m layer, which has them), named in every
language a studyset can be in from the Unicode CLDR (Unicode License v3,
its notice in the licence list). tool/maps/world_countries.py builds it;
the outline code it shares with the Japan script moved to outlines.py,
which still writes Japan's file byte for byte. The 1.8 MB file is parsed
off the screen's isolate.
Picking goes in layers on both maps: on the whole map one taps a group
(a continent, or Kantō, Kansai …; chips name them too) and the map flies
there, then one taps the region; Back returns. A group of one (Hokkaidō,
Okinawa) and what is in none (Antarctica) are picked at once. A country
in two groups (Russia, Türkiye, Cyprus) opens the one nearer the tap.
Oceania's frame runs past 180° so the Pacific stays in one piece.
What is still too small to hit gets a ring, 48 dp of reach as Material's
touch target: on the ring it is picked; near it, where the tap could mean
it or its neighbours, the map zooms in rather than guess. Pinching and a
mouse's wheel zoom as before.
Names show in the app's language, beside them the map's own (Japanese for
Japan) or the studyset's (キプロス beside Cyprus in a Japanese studyset).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MMm7usAo6q9ZZRoPkWjoxi
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-12 07:18:20 +02:00
feat(map): one map, Japan's prefectures within Japan
A map question is picked on one map: the world's countries in layers
(Europe, East Asia, ...), and within Japan its prefectures, in layers too
(Kantō, Kansai, ...); Back goes up one. The composer's choice between two
maps is gone. A card still names the map its answer is a region of
(`japan`, `world`), as versions up to v0.11.0 did, so they still show it.
In the composer a tap on Japan picks the country, and the "Prefectures"
chip opens its prefectures. Studying, the card decides: a prefecture's
question opens Japan at a tap, a country's picks it.
The map opens on the deepest layer the map questions share (studying, the
round's; writing, the studyset's): Japan for prefectures all over it,
Kantō for Tokyo and Chiba, Europe for European countries.
A tap no longer zooms in when it could mean several regions: it picks the
ring it is on (the nearest of several), else the region under it, else the
nearest ring or outline within reach. Zooming is pinching, or Ctrl (⌘ on a
Mac) and the wheel, as maps on web pages do; the wheel alone scrolls the
page, and a note over the map says how to zoom.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MMm7usAo6q9ZZRoPkWjoxi
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-12 12:19:03 +02:00
/// No description provided for @mapZoomNote.
feat(map): every country on earth, picked continent first
Map questions can now be on the world's countries besides Japan's
prefectures: a new question chooses its map above it.
The world is Natural Earth's 1:50m countries (public domain), as they are
on the ground, one region per ISO 3166-1 code (239; Gibraltar and the US
Outlying Islands from the 1:10m layer, which has them), named in every
language a studyset can be in from the Unicode CLDR (Unicode License v3,
its notice in the licence list). tool/maps/world_countries.py builds it;
the outline code it shares with the Japan script moved to outlines.py,
which still writes Japan's file byte for byte. The 1.8 MB file is parsed
off the screen's isolate.
Picking goes in layers on both maps: on the whole map one taps a group
(a continent, or Kantō, Kansai …; chips name them too) and the map flies
there, then one taps the region; Back returns. A group of one (Hokkaidō,
Okinawa) and what is in none (Antarctica) are picked at once. A country
in two groups (Russia, Türkiye, Cyprus) opens the one nearer the tap.
Oceania's frame runs past 180° so the Pacific stays in one piece.
What is still too small to hit gets a ring, 48 dp of reach as Material's
touch target: on the ring it is picked; near it, where the tap could mean
it or its neighbours, the map zooms in rather than guess. Pinching and a
mouse's wheel zoom as before.
Names show in the app's language, beside them the map's own (Japanese for
Japan) or the studyset's (キプロス beside Cyprus in a Japanese studyset).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MMm7usAo6q9ZZRoPkWjoxi
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-12 07:18:20 +02:00
///
/// In en, this message translates to:
feat(map): one map, Japan's prefectures within Japan
A map question is picked on one map: the world's countries in layers
(Europe, East Asia, ...), and within Japan its prefectures, in layers too
(Kantō, Kansai, ...); Back goes up one. The composer's choice between two
maps is gone. A card still names the map its answer is a region of
(`japan`, `world`), as versions up to v0.11.0 did, so they still show it.
In the composer a tap on Japan picks the country, and the "Prefectures"
chip opens its prefectures. Studying, the card decides: a prefecture's
question opens Japan at a tap, a country's picks it.
The map opens on the deepest layer the map questions share (studying, the
round's; writing, the studyset's): Japan for prefectures all over it,
Kantō for Tokyo and Chiba, Europe for European countries.
A tap no longer zooms in when it could mean several regions: it picks the
ring it is on (the nearest of several), else the region under it, else the
nearest ring or outline within reach. Zooming is pinching, or Ctrl (⌘ on a
Mac) and the wheel, as maps on web pages do; the wheel alone scrolls the
page, and a note over the map says how to zoom.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MMm7usAo6q9ZZRoPkWjoxi
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-12 12:19:03 +02:00
/// **'Ctrl + scroll to zoom'**
String get mapZoomNote ;
feat(map): every country on earth, picked continent first
Map questions can now be on the world's countries besides Japan's
prefectures: a new question chooses its map above it.
The world is Natural Earth's 1:50m countries (public domain), as they are
on the ground, one region per ISO 3166-1 code (239; Gibraltar and the US
Outlying Islands from the 1:10m layer, which has them), named in every
language a studyset can be in from the Unicode CLDR (Unicode License v3,
its notice in the licence list). tool/maps/world_countries.py builds it;
the outline code it shares with the Japan script moved to outlines.py,
which still writes Japan's file byte for byte. The 1.8 MB file is parsed
off the screen's isolate.
Picking goes in layers on both maps: on the whole map one taps a group
(a continent, or Kantō, Kansai …; chips name them too) and the map flies
there, then one taps the region; Back returns. A group of one (Hokkaidō,
Okinawa) and what is in none (Antarctica) are picked at once. A country
in two groups (Russia, Türkiye, Cyprus) opens the one nearer the tap.
Oceania's frame runs past 180° so the Pacific stays in one piece.
What is still too small to hit gets a ring, 48 dp of reach as Material's
touch target: on the ring it is picked; near it, where the tap could mean
it or its neighbours, the map zooms in rather than guess. Pinching and a
mouse's wheel zoom as before.
Names show in the app's language, beside them the map's own (Japanese for
Japan) or the studyset's (キプロス beside Cyprus in a Japanese studyset).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MMm7usAo6q9ZZRoPkWjoxi
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-12 07:18:20 +02:00
feat(map): one map, Japan's prefectures within Japan
A map question is picked on one map: the world's countries in layers
(Europe, East Asia, ...), and within Japan its prefectures, in layers too
(Kantō, Kansai, ...); Back goes up one. The composer's choice between two
maps is gone. A card still names the map its answer is a region of
(`japan`, `world`), as versions up to v0.11.0 did, so they still show it.
In the composer a tap on Japan picks the country, and the "Prefectures"
chip opens its prefectures. Studying, the card decides: a prefecture's
question opens Japan at a tap, a country's picks it.
The map opens on the deepest layer the map questions share (studying, the
round's; writing, the studyset's): Japan for prefectures all over it,
Kantō for Tokyo and Chiba, Europe for European countries.
A tap no longer zooms in when it could mean several regions: it picks the
ring it is on (the nearest of several), else the region under it, else the
nearest ring or outline within reach. Zooming is pinching, or Ctrl (⌘ on a
Mac) and the wheel, as maps on web pages do; the wheel alone scrolls the
page, and a note over the map says how to zoom.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MMm7usAo6q9ZZRoPkWjoxi
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-12 12:19:03 +02:00
/// No description provided for @mapZoomNoteMac.
feat(map): every country on earth, picked continent first
Map questions can now be on the world's countries besides Japan's
prefectures: a new question chooses its map above it.
The world is Natural Earth's 1:50m countries (public domain), as they are
on the ground, one region per ISO 3166-1 code (239; Gibraltar and the US
Outlying Islands from the 1:10m layer, which has them), named in every
language a studyset can be in from the Unicode CLDR (Unicode License v3,
its notice in the licence list). tool/maps/world_countries.py builds it;
the outline code it shares with the Japan script moved to outlines.py,
which still writes Japan's file byte for byte. The 1.8 MB file is parsed
off the screen's isolate.
Picking goes in layers on both maps: on the whole map one taps a group
(a continent, or Kantō, Kansai …; chips name them too) and the map flies
there, then one taps the region; Back returns. A group of one (Hokkaidō,
Okinawa) and what is in none (Antarctica) are picked at once. A country
in two groups (Russia, Türkiye, Cyprus) opens the one nearer the tap.
Oceania's frame runs past 180° so the Pacific stays in one piece.
What is still too small to hit gets a ring, 48 dp of reach as Material's
touch target: on the ring it is picked; near it, where the tap could mean
it or its neighbours, the map zooms in rather than guess. Pinching and a
mouse's wheel zoom as before.
Names show in the app's language, beside them the map's own (Japanese for
Japan) or the studyset's (キプロス beside Cyprus in a Japanese studyset).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MMm7usAo6q9ZZRoPkWjoxi
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-12 07:18:20 +02:00
///
/// In en, this message translates to:
feat(map): one map, Japan's prefectures within Japan
A map question is picked on one map: the world's countries in layers
(Europe, East Asia, ...), and within Japan its prefectures, in layers too
(Kantō, Kansai, ...); Back goes up one. The composer's choice between two
maps is gone. A card still names the map its answer is a region of
(`japan`, `world`), as versions up to v0.11.0 did, so they still show it.
In the composer a tap on Japan picks the country, and the "Prefectures"
chip opens its prefectures. Studying, the card decides: a prefecture's
question opens Japan at a tap, a country's picks it.
The map opens on the deepest layer the map questions share (studying, the
round's; writing, the studyset's): Japan for prefectures all over it,
Kantō for Tokyo and Chiba, Europe for European countries.
A tap no longer zooms in when it could mean several regions: it picks the
ring it is on (the nearest of several), else the region under it, else the
nearest ring or outline within reach. Zooming is pinching, or Ctrl (⌘ on a
Mac) and the wheel, as maps on web pages do; the wheel alone scrolls the
page, and a note over the map says how to zoom.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MMm7usAo6q9ZZRoPkWjoxi
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-12 12:19:03 +02:00
/// **'⌘ + scroll to zoom'**
String get mapZoomNoteMac ;
feat(map): every country on earth, picked continent first
Map questions can now be on the world's countries besides Japan's
prefectures: a new question chooses its map above it.
The world is Natural Earth's 1:50m countries (public domain), as they are
on the ground, one region per ISO 3166-1 code (239; Gibraltar and the US
Outlying Islands from the 1:10m layer, which has them), named in every
language a studyset can be in from the Unicode CLDR (Unicode License v3,
its notice in the licence list). tool/maps/world_countries.py builds it;
the outline code it shares with the Japan script moved to outlines.py,
which still writes Japan's file byte for byte. The 1.8 MB file is parsed
off the screen's isolate.
Picking goes in layers on both maps: on the whole map one taps a group
(a continent, or Kantō, Kansai …; chips name them too) and the map flies
there, then one taps the region; Back returns. A group of one (Hokkaidō,
Okinawa) and what is in none (Antarctica) are picked at once. A country
in two groups (Russia, Türkiye, Cyprus) opens the one nearer the tap.
Oceania's frame runs past 180° so the Pacific stays in one piece.
What is still too small to hit gets a ring, 48 dp of reach as Material's
touch target: on the ring it is picked; near it, where the tap could mean
it or its neighbours, the map zooms in rather than guess. Pinching and a
mouse's wheel zoom as before.
Names show in the app's language, beside them the map's own (Japanese for
Japan) or the studyset's (キプロス beside Cyprus in a Japanese studyset).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MMm7usAo6q9ZZRoPkWjoxi
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-12 07:18:20 +02:00
/// No description provided for @mapGroupHokkaido.
///
/// In en, this message translates to:
/// **'Hokkaidō'**
String get mapGroupHokkaido ;
/// No description provided for @mapGroupTohoku.
///
/// In en, this message translates to:
/// **'Tōhoku'**
String get mapGroupTohoku ;
/// No description provided for @mapGroupKanto.
///
/// In en, this message translates to:
/// **'Kantō'**
String get mapGroupKanto ;
/// No description provided for @mapGroupChubu.
///
/// In en, this message translates to:
/// **'Chūbu'**
String get mapGroupChubu ;
/// No description provided for @mapGroupKansai.
///
/// In en, this message translates to:
/// **'Kansai'**
String get mapGroupKansai ;
/// No description provided for @mapGroupChugoku.
///
/// In en, this message translates to:
/// **'Chūgoku'**
String get mapGroupChugoku ;
/// No description provided for @mapGroupShikoku.
///
/// In en, this message translates to:
/// **'Shikoku'**
String get mapGroupShikoku ;
/// No description provided for @mapGroupKyushu.
///
/// In en, this message translates to:
/// **'Kyūshū'**
String get mapGroupKyushu ;
/// No description provided for @mapGroupOkinawa.
///
/// In en, this message translates to:
/// **'Okinawa'**
String get mapGroupOkinawa ;
/// No description provided for @mapGroupEurope.
///
/// In en, this message translates to:
/// **'Europe'**
String get mapGroupEurope ;
/// No description provided for @mapGroupAfrica.
///
/// In en, this message translates to:
/// **'Africa'**
String get mapGroupAfrica ;
/// No description provided for @mapGroupWestAsia.
///
/// In en, this message translates to:
/// **'Western Asia'**
String get mapGroupWestAsia ;
/// No description provided for @mapGroupSouthAsia.
///
/// In en, this message translates to:
/// **'Central and South Asia'**
String get mapGroupSouthAsia ;
/// No description provided for @mapGroupEastAsia.
///
/// In en, this message translates to:
/// **'East Asia'**
String get mapGroupEastAsia ;
/// No description provided for @mapGroupSoutheastAsia.
///
/// In en, this message translates to:
/// **'Southeast Asia'**
String get mapGroupSoutheastAsia ;
/// No description provided for @mapGroupNorthAmerica.
///
/// In en, this message translates to:
/// **'North America'**
String get mapGroupNorthAmerica ;
/// No description provided for @mapGroupCentralAmerica.
///
/// In en, this message translates to:
/// **'Central America and the Caribbean'**
String get mapGroupCentralAmerica ;
/// No description provided for @mapGroupSouthAmerica.
///
/// In en, this message translates to:
/// **'South America'**
String get mapGroupSouthAmerica ;
/// No description provided for @mapGroupOceania.
///
/// In en, this message translates to:
/// **'Oceania'**
String get mapGroupOceania ;
2026-10-01 19:56:29 +02:00
/// No description provided for @composerPlaceholderQuestion.
///
/// In en, this message translates to:
/// **'Add question'**
String get composerPlaceholderQuestion ;
/// No description provided for @composerLabelRegion.
///
/// In en, this message translates to:
/// **'Answer'**
String get composerLabelRegion ;
2026-10-12 18:02:15 +02:00
/// No description provided for @composerRegionPick.
///
/// In en, this message translates to:
/// **'Tap to pick the region on the map'**
String get composerRegionPick ;
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:13:34 +02:00
/// No description provided for @homeStreakTooltip.
///
/// In en, this message translates to:
/// **'Study streak'**
String get homeStreakTooltip ;
/// No description provided for @homeArrange.
///
/// In en, this message translates to:
/// **'Arrange widgets'**
String get homeArrange ;
/// No description provided for @homeDone.
///
/// In en, this message translates to:
/// **'Done'**
String get homeDone ;
/// No description provided for @homeDragToReorder.
///
/// In en, this message translates to:
/// **'Drag to reorder'**
String get homeDragToReorder ;
/// No description provided for @homeShowWidget.
///
/// In en, this message translates to:
/// **'Show {label}'**
String homeShowWidget ( String label ) ;
/// No description provided for @homeHideWidget.
///
/// In en, this message translates to:
/// **'Hide {label}'**
String homeHideWidget ( String label ) ;
/// No description provided for @homeAllHidden.
///
/// In en, this message translates to:
/// **'All widgets are hidden'**
String get homeAllHidden ;
feat(app): Home, the Library and the studyset sheets laid out as before
Home: plain cards on the surface, 16 dp apart, the goal first and the
Pomodoro timer after it in a new dashboard. The streak chip sits centred
in the app bar, and its sheet shows the days inside a big flame and the
week as marks over the weekdays' initials, with a word on today in a box.
The week names its day as "Thursday, Oct 8" and its days carry no
tooltip. The heatmap scrolls its weekdays with the weeks, outlines the
days to come, and names a day's activities with its date. The goal card
has a ring of 5 dp, a check once met and a pencil, and its sheet saves
across its width with Remove goal below. Arranging puts an eye on each
widget's corner and lifts the one picked up, with a tap the hand feels;
with every widget hidden, Customise brings arranging back. The stepper
moves to core, where the goal's sheet takes it too.
Library: the counts line includes files (PDFs); Refresh in the app bar
and pulling the list down sync now; a list that can't be read offers
Retry, which reads it again; the star keeps its square corners.
Studyset sheet: "Title *", the secondary title explained behind a
question mark, Inherit as a box beside the colour, and the description
in up to three lines with a chevron. Move sheet: round marks, the
studyset itself marked as current, the top level only while it is
inside another, archived studysets last under their heading, the
destination named in the footer, and a new studyset to move into, made
only once the move is.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-08 11:12:23 +02:00
/// No description provided for @homeAllHiddenBody.
///
/// In en, this message translates to:
/// **'Bring some back to fill your dashboard.'**
String get homeAllHiddenBody ;
/// No description provided for @homeCustomise.
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:13:34 +02:00
///
/// In en, this message translates to:
feat(app): Home, the Library and the studyset sheets laid out as before
Home: plain cards on the surface, 16 dp apart, the goal first and the
Pomodoro timer after it in a new dashboard. The streak chip sits centred
in the app bar, and its sheet shows the days inside a big flame and the
week as marks over the weekdays' initials, with a word on today in a box.
The week names its day as "Thursday, Oct 8" and its days carry no
tooltip. The heatmap scrolls its weekdays with the weeks, outlines the
days to come, and names a day's activities with its date. The goal card
has a ring of 5 dp, a check once met and a pencil, and its sheet saves
across its width with Remove goal below. Arranging puts an eye on each
widget's corner and lifts the one picked up, with a tap the hand feels;
with every widget hidden, Customise brings arranging back. The stepper
moves to core, where the goal's sheet takes it too.
Library: the counts line includes files (PDFs); Refresh in the app bar
and pulling the list down sync now; a list that can't be read offers
Retry, which reads it again; the star keeps its square corners.
Studyset sheet: "Title *", the secondary title explained behind a
question mark, Inherit as a box beside the colour, and the description
in up to three lines with a chevron. Move sheet: round marks, the
studyset itself marked as current, the top level only while it is
inside another, archived studysets last under their heading, the
destination named in the footer, and a new studyset to move into, made
only once the move is.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-08 11:12:23 +02:00
/// **'Customise'**
String get homeCustomise ;
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:13:34 +02:00
/// No description provided for @homeWidgetGoal.
///
/// In en, this message translates to:
feat(app): Home, the Library and the studyset sheets laid out as before
Home: plain cards on the surface, 16 dp apart, the goal first and the
Pomodoro timer after it in a new dashboard. The streak chip sits centred
in the app bar, and its sheet shows the days inside a big flame and the
week as marks over the weekdays' initials, with a word on today in a box.
The week names its day as "Thursday, Oct 8" and its days carry no
tooltip. The heatmap scrolls its weekdays with the weeks, outlines the
days to come, and names a day's activities with its date. The goal card
has a ring of 5 dp, a check once met and a pencil, and its sheet saves
across its width with Remove goal below. Arranging puts an eye on each
widget's corner and lifts the one picked up, with a tap the hand feels;
with every widget hidden, Customise brings arranging back. The stepper
moves to core, where the goal's sheet takes it too.
Library: the counts line includes files (PDFs); Refresh in the app bar
and pulling the list down sync now; a list that can't be read offers
Retry, which reads it again; the star keeps its square corners.
Studyset sheet: "Title *", the secondary title explained behind a
question mark, Inherit as a box beside the colour, and the description
in up to three lines with a chevron. Move sheet: round marks, the
studyset itself marked as current, the top level only while it is
inside another, archived studysets last under their heading, the
destination named in the footer, and a new studyset to move into, made
only once the move is.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-08 11:12:23 +02:00
/// **'Daily goal'**
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:13:34 +02:00
String get homeWidgetGoal ;
/// No description provided for @homeWidgetWeek.
///
/// In en, this message translates to:
/// **'Week overview'**
String get homeWidgetWeek ;
/// No description provided for @homeWidgetActivity.
///
/// In en, this message translates to:
/// **'Activity heatmap'**
String get homeWidgetActivity ;
/// No description provided for @homeActivityTitle.
///
/// In en, this message translates to:
/// **'Your activity'**
String get homeActivityTitle ;
/// No description provided for @streakDays.
///
/// In en, this message translates to:
/// **'{count, plural, =1{1 day streak} other{{count} day streak}}'**
String streakDays ( num count ) ;
/// No description provided for @streakStart.
///
/// In en, this message translates to:
/// **'Study today to start your streak.'**
String get streakStart ;
/// No description provided for @streakGrow.
///
/// In en, this message translates to:
/// **'Keep it going: study at least once a day to grow your streak.'**
String get streakGrow ;
/// No description provided for @streakFirstDay.
///
/// In en, this message translates to:
/// **'Review some cards today to light the first day.'**
String get streakFirstDay ;
/// No description provided for @streakComeBack.
///
/// In en, this message translates to:
/// **'Come back tomorrow to keep your streak.'**
String get streakComeBack ;
/// No description provided for @streakKeepAlive.
///
/// In en, this message translates to:
/// **'Study today to keep your streak alive.'**
String get streakKeepAlive ;
/// No description provided for @goalEditTitle.
///
/// In en, this message translates to:
/// **'Edit study goal'**
String get goalEditTitle ;
/// No description provided for @goalSetTitle.
///
/// In en, this message translates to:
/// **'Set a study goal'**
String get goalSetTitle ;
/// No description provided for @goalDescriptionDaily.
///
/// In en, this message translates to:
/// **'Review a number of flashcards each day. Every card you rate counts.'**
String get goalDescriptionDaily ;
/// No description provided for @goalDescriptionWeekly.
///
/// In en, this message translates to:
/// **'Review a number of flashcards each week, from Monday. Every card you rate counts.'**
String get goalDescriptionWeekly ;
/// No description provided for @goalPeriod.
///
/// In en, this message translates to:
/// **'Period'**
String get goalPeriod ;
/// No description provided for @goalPerDay.
///
/// In en, this message translates to:
/// **'Per day'**
String get goalPerDay ;
/// No description provided for @goalPerWeek.
///
/// In en, this message translates to:
/// **'Per week'**
String get goalPerWeek ;
/// No description provided for @goalCardsPerDay.
///
/// In en, this message translates to:
/// **'Cards per day'**
String get goalCardsPerDay ;
/// No description provided for @goalCardsPerWeek.
///
/// In en, this message translates to:
/// **'Cards per week'**
String get goalCardsPerWeek ;
/// No description provided for @goalSave.
///
/// In en, this message translates to:
/// **'Save goal'**
String get goalSave ;
/// No description provided for @goalRemove.
///
/// In en, this message translates to:
/// **'Remove goal'**
String get goalRemove ;
/// No description provided for @goalEmptySubtitle.
///
/// In en, this message translates to:
/// **'Review a set number of cards each day or week.'**
String get goalEmptySubtitle ;
/// No description provided for @goalDailyHeadline.
///
/// In en, this message translates to:
/// **'Daily goal'**
String get goalDailyHeadline ;
/// No description provided for @goalWeeklyHeadline.
///
/// In en, this message translates to:
/// **'Weekly goal'**
String get goalWeeklyHeadline ;
/// No description provided for @goalProgressToday.
///
/// In en, this message translates to:
/// **'{reviewed} of {target} cards today'**
String goalProgressToday ( int reviewed , int target ) ;
/// No description provided for @goalProgressThisWeek.
///
/// In en, this message translates to:
/// **'{reviewed} of {target} cards this week'**
String goalProgressThisWeek ( int reviewed , int target ) ;
/// No description provided for @goalMet.
///
/// In en, this message translates to:
/// **'Goal met: nice work!'**
String get goalMet ;
/// No description provided for @goalRemaining.
///
/// In en, this message translates to:
/// **'{count} to go'**
String goalRemaining ( int count ) ;
/// No description provided for @weekToday.
///
/// In en, this message translates to:
/// **'Today'**
String get weekToday ;
/// No description provided for @weekPreviousDay.
///
/// In en, this message translates to:
/// **'Previous day'**
String get weekPreviousDay ;
/// No description provided for @weekNextDay.
///
/// In en, this message translates to:
/// **'Next day'**
String get weekNextDay ;
/// No description provided for @weekRestDay.
///
/// In en, this message translates to:
/// **'Rest day'**
String get weekRestDay ;
/// No description provided for @heatmapLess.
///
/// In en, this message translates to:
/// **'Less'**
String get heatmapLess ;
/// No description provided for @heatmapMore.
///
/// In en, this message translates to:
/// **'More'**
String get heatmapMore ;
/// No description provided for @heatmapTotal.
///
/// In en, this message translates to:
/// **'{count, plural, =1{1 activity in the last year} other{{count} activities in the last year}}'**
String heatmapTotal ( int count ) ;
/// No description provided for @heatmapDay.
///
/// In en, this message translates to:
feat(app): Home, the Library and the studyset sheets laid out as before
Home: plain cards on the surface, 16 dp apart, the goal first and the
Pomodoro timer after it in a new dashboard. The streak chip sits centred
in the app bar, and its sheet shows the days inside a big flame and the
week as marks over the weekdays' initials, with a word on today in a box.
The week names its day as "Thursday, Oct 8" and its days carry no
tooltip. The heatmap scrolls its weekdays with the weeks, outlines the
days to come, and names a day's activities with its date. The goal card
has a ring of 5 dp, a check once met and a pencil, and its sheet saves
across its width with Remove goal below. Arranging puts an eye on each
widget's corner and lifts the one picked up, with a tap the hand feels;
with every widget hidden, Customise brings arranging back. The stepper
moves to core, where the goal's sheet takes it too.
Library: the counts line includes files (PDFs); Refresh in the app bar
and pulling the list down sync now; a list that can't be read offers
Retry, which reads it again; the star keeps its square corners.
Studyset sheet: "Title *", the secondary title explained behind a
question mark, Inherit as a box beside the colour, and the description
in up to three lines with a chevron. Move sheet: round marks, the
studyset itself marked as current, the top level only while it is
inside another, archived studysets last under their heading, the
destination named in the footer, and a new studyset to move into, made
only once the move is.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-08 11:12:23 +02:00
/// **'{count, plural, =1{1 activity} other{{count} activities}} · {date}'**
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:13:34 +02:00
String heatmapDay ( int count , String date ) ;
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
/// No description provided for @commonCancel.
///
/// In en, this message translates to:
/// **'Cancel'**
String get commonCancel ;
feat(app): editor blocks: callout, spoiler, formulas, files, video, study button
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
/// No description provided for @commonSave.
///
/// In en, this message translates to:
/// **'Save'**
String get commonSave ;
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
/// No description provided for @commonClose.
///
/// In en, this message translates to:
/// **'Close'**
String get commonClose ;
2026-10-07 21:43:38 +02:00
/// No description provided for @moreInfo.
///
/// In en, this message translates to:
/// **'More info'**
String get moreInfo ;
/// No description provided for @moreInfoCreated.
///
/// In en, this message translates to:
/// **'Created'**
String get moreInfoCreated ;
/// No description provided for @moreInfoModified.
///
/// In en, this message translates to:
/// **'Last modified'**
String get moreInfoModified ;
/// No description provided for @moreInfoItemId.
///
/// In en, this message translates to:
/// **'Item ID'**
String get moreInfoItemId ;
/// No description provided for @moreInfoStudysetId.
///
/// In en, this message translates to:
/// **'Studyset ID'**
String get moreInfoStudysetId ;
/// No description provided for @moreInfoPdfId.
///
/// In en, this message translates to:
/// **'PDF source ID'**
String get moreInfoPdfId ;
feat(app): More info for studysets, paths, notes and PDFs
Wherever the old menus offered it: the studyset's options, a path's
(in the list and in the runner), a note's, and a PDF's, all on the
shared sheet the cards use: when it was made (from its UUID v7 id) and
last changed where that is kept, its ids to copy (parent, studyset,
PDF), and for a PDF its file.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MMm7usAo6q9ZZRoPkWjoxi
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-08 14:38:55 +02:00
/// No description provided for @moreInfoParentId.
///
/// In en, this message translates to:
/// **'Parent ID'**
String get moreInfoParentId ;
/// No description provided for @moreInfoFile.
///
/// In en, this message translates to:
/// **'File'**
String get moreInfoFile ;
2026-10-07 21:43:38 +02:00
/// No description provided for @moreInfoReviews.
///
/// In en, this message translates to:
/// **'Reviews'**
String get moreInfoReviews ;
/// No description provided for @moreInfoReviewCount.
///
/// In en, this message translates to:
/// **'Review count'**
String get moreInfoReviewCount ;
/// No description provided for @moreInfoLastReviewed.
///
/// In en, this message translates to:
/// **'Last reviewed'**
String get moreInfoLastReviewed ;
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
/// No description provided for @commonTryAgain.
///
/// In en, this message translates to:
/// **'Try again'**
String get commonTryAgain ;
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 02:01:01 +02:00
/// No description provided for @personDataFailed.
///
/// In en, this message translates to:
/// **'Couldn\'t open this device\'s data.'**
String get personDataFailed ;
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
/// No description provided for @sessionUnreachable.
///
/// In en, this message translates to:
/// **'Couldn\'t reach the server.'**
String get sessionUnreachable ;
/// No description provided for @notFoundGone.
///
/// In en, this message translates to:
/// **'This no longer exists. It may have been deleted on another device.'**
String get notFoundGone ;
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 04:52:02 +02:00
feat(app): the studyset screen laid out as before
The Study pill reads "Learn 12 flashcards" in the app bar and "Learn 12"
floating, "Study" with nothing due, and is off with nothing to study,
saying so. With a path not finished, a chevron beside it offers to start
or continue the path: a menu in the app bar, a Study mode sheet on
phones. The floating bar rises above the licence line, which takes two
lines at most.
Add material offers a path, flashcards, a note and a PDF, in that order;
imports moved to the options' Import content, which asks for a learning
path or an Anki deck. The options read Edit, Move, Study options, Import
content, Reset progress, Archive, Delete. The bibliography is an outlined
round button above the content, and on phones at the foot of the topics.
The tabs sit on a line of their own across the card, what the tab shown
offers beside them: the flashcards' filter and options, and on the paths
Reorder once there are two, which turns them into handles to drag. An
empty tab says so in its middle; tapping the open tab again returns to
its top, and pulling a tab down syncs. The breadcrumb's steps are plain
links between "›", the studyset itself bold. A long description is cut at
a fixed height, measured, with Read more opening it whole under the
studyset's title. A studyset that no longer exists leads back to the
library.
The topics: All topics on the primary colour, in view also while they
are rearranged; each topic with the study it invites in a round icon,
what it holds ("Empty" with nothing, files counted too), and its options
(now with Reset progress) behind "…" or a long press where the finger
is; New sub-studyset at the foot below a line, the list with its
scrollbar always shown. All topics opens Add material while nothing is
due.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-08 11:42:50 +02:00
/// No description provided for @studysetBackToLibrary.
///
/// In en, this message translates to:
/// **'Back to library'**
String get studysetBackToLibrary ;
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 04:52:02 +02:00
/// No description provided for @fileImage.
///
/// In en, this message translates to:
/// **'Image'**
String get fileImage ;
/// No description provided for @fileSound.
///
/// In en, this message translates to:
/// **'Sound'**
String get fileSound ;
/// No description provided for @fileImageUnavailable.
///
/// In en, this message translates to:
/// **'This image isn\'t on this device yet. It shows once it\'s uploaded and you\'re online.'**
String get fileImageUnavailable ;
Fix what the first Android run found
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
/// No description provided for @fileImageUnreadable.
///
/// In en, this message translates to:
/// **'This image can\'t be shown. Its file may be damaged, or in a format this device can\'t read.'**
String get fileImageUnreadable ;
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 04:52:02 +02:00
/// No description provided for @fileFromElsewhere.
///
/// In en, this message translates to:
/// **'Files from the web aren\'t shown.'**
String get fileFromElsewhere ;
/// No description provided for @filePlaySound.
///
/// In en, this message translates to:
/// **'Play sound'**
String get filePlaySound ;
/// No description provided for @fileStopSound.
///
/// In en, this message translates to:
/// **'Stop sound'**
String get fileStopSound ;
/// No description provided for @fileSoundUnavailable.
///
/// In en, this message translates to:
/// **'This sound isn\'t on this device yet. It plays once it\'s uploaded and you\'re online.'**
String get fileSoundUnavailable ;
Fix what the first Android run found
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
/// No description provided for @fileSoundUnplayable.
///
/// In en, this message translates to:
/// **'This sound can\'t be played. Its file may be damaged, or in a format this device can\'t play.'**
String get fileSoundUnplayable ;
feat(app): editor blocks: callout, spoiler, formulas, files, video, study button
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
/// No description provided for @fileImages.
///
/// In en, this message translates to:
/// **'Images'**
String get fileImages ;
/// No description provided for @fileSounds.
///
/// In en, this message translates to:
/// **'Sounds'**
String get fileSounds ;
/// No description provided for @fileNotAnImage.
///
/// In en, this message translates to:
/// **'This file isn\'t an image the app can show.'**
String get fileNotAnImage ;
/// No description provided for @fileNotASound.
///
/// In en, this message translates to:
/// **'This file isn\'t a sound the app can play.'**
String get fileNotASound ;
/// No description provided for @fileTooLarge.
///
/// In en, this message translates to:
/// **'This file is larger than {megabytes} MB.'**
String fileTooLarge ( int megabytes ) ;
/// No description provided for @fileNotKept.
///
/// In en, this message translates to:
/// **'The file couldn\'t be kept on this device.'**
String get fileNotKept ;
/// No description provided for @fileRemoveImage.
///
/// In en, this message translates to:
/// **'Remove image'**
String get fileRemoveImage ;
/// No description provided for @fileRemoveSound.
///
/// In en, this message translates to:
/// **'Remove sound'**
String get fileRemoveSound ;
/// No description provided for @editorInsert.
///
/// In en, this message translates to:
/// **'Insert'**
String get editorInsert ;
/// No description provided for @blockGroupText.
///
/// In en, this message translates to:
/// **'Text'**
String get blockGroupText ;
/// No description provided for @blockGroupBlocks.
///
/// In en, this message translates to:
/// **'Blocks'**
String get blockGroupBlocks ;
/// No description provided for @blockGroupMedia.
///
/// In en, this message translates to:
/// **'Media'**
String get blockGroupMedia ;
/// No description provided for @blockHeading.
///
/// In en, this message translates to:
/// **'Heading {level}'**
String blockHeading ( int level ) ;
/// No description provided for @blockBulletedList.
///
/// In en, this message translates to:
/// **'Bulleted list'**
String get blockBulletedList ;
/// No description provided for @blockNumberedList.
///
/// In en, this message translates to:
/// **'Numbered list'**
String get blockNumberedList ;
/// No description provided for @blockChecklist.
///
/// In en, this message translates to:
/// **'Checklist'**
String get blockChecklist ;
/// No description provided for @blockQuote.
///
/// In en, this message translates to:
/// **'Quote'**
String get blockQuote ;
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 02:01:01 +02:00
/// No description provided for @blockListItem.
///
/// In en, this message translates to:
/// **'List item'**
String get blockListItem ;
/// No description provided for @blockChecklistItem.
///
/// In en, this message translates to:
/// **'To-do'**
String get blockChecklistItem ;
/// No description provided for @editorSlashHint.
///
/// In en, this message translates to:
/// **'Type / to insert a block, or just start typing'**
String get editorSlashHint ;
feat(app): editor blocks: callout, spoiler, formulas, files, video, study button
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
/// No description provided for @blockDivider.
///
/// In en, this message translates to:
/// **'Divider'**
String get blockDivider ;
/// No description provided for @blockTable.
///
/// In en, this message translates to:
/// **'Table'**
String get blockTable ;
/// No description provided for @blockRemove.
///
/// In en, this message translates to:
/// **'Remove'**
String get blockRemove ;
/// No description provided for @blockUnknown.
///
/// In en, this message translates to:
/// **'This part needs a newer version of the app.'**
String get blockUnknown ;
/// No description provided for @blockCallout.
///
/// In en, this message translates to:
/// **'Callout'**
String get blockCallout ;
/// No description provided for @blockSpoiler.
///
/// In en, this message translates to:
/// **'Spoiler'**
String get blockSpoiler ;
/// No description provided for @calloutNote.
///
/// In en, this message translates to:
/// **'Note'**
String get calloutNote ;
/// No description provided for @calloutInfo.
///
/// In en, this message translates to:
/// **'Info'**
String get calloutInfo ;
/// No description provided for @calloutTip.
///
/// In en, this message translates to:
/// **'Tip'**
String get calloutTip ;
/// No description provided for @calloutSuccess.
///
/// In en, this message translates to:
/// **'Success'**
String get calloutSuccess ;
/// No description provided for @calloutQuestion.
///
/// In en, this message translates to:
/// **'Question'**
String get calloutQuestion ;
/// No description provided for @calloutWarning.
///
/// In en, this message translates to:
/// **'Warning'**
String get calloutWarning ;
/// No description provided for @calloutDanger.
///
/// In en, this message translates to:
/// **'Danger'**
String get calloutDanger ;
/// No description provided for @calloutQuote.
///
/// In en, this message translates to:
/// **'Quote'**
String get calloutQuote ;
/// No description provided for @calloutOptions.
///
/// In en, this message translates to:
/// **'Callout options'**
String get calloutOptions ;
/// No description provided for @calloutEditTitle.
///
/// In en, this message translates to:
/// **'Edit title…'**
String get calloutEditTitle ;
/// No description provided for @calloutFoldedAtFirst.
///
/// In en, this message translates to:
/// **'Folded at first'**
String get calloutFoldedAtFirst ;
/// No description provided for @calloutRemove.
///
/// In en, this message translates to:
/// **'Remove callout'**
String get calloutRemove ;
/// No description provided for @calloutShow.
///
/// In en, this message translates to:
/// **'Show'**
String get calloutShow ;
/// No description provided for @calloutHide.
///
/// In en, this message translates to:
/// **'Hide'**
String get calloutHide ;
/// No description provided for @calloutWriteInside.
///
/// In en, this message translates to:
/// **'Write inside'**
String get calloutWriteInside ;
/// No description provided for @calloutTitleDialog.
///
/// In en, this message translates to:
/// **'Callout title'**
String get calloutTitleDialog ;
/// No description provided for @calloutTitleLabel.
///
/// In en, this message translates to:
/// **'Title'**
String get calloutTitleLabel ;
/// No description provided for @calloutTitleHelper.
///
/// In en, this message translates to:
/// **'Leave it empty to show the kind\'s name.'**
String get calloutTitleHelper ;
/// No description provided for @spoilerReveal.
///
/// In en, this message translates to:
/// **'Reveal'**
String get spoilerReveal ;
/// No description provided for @spoilerPlaceholder.
///
/// In en, this message translates to:
/// **'Hidden text, revealed on a tap'**
String get spoilerPlaceholder ;
/// No description provided for @blockFormula.
///
/// In en, this message translates to:
/// **'Formula'**
String get blockFormula ;
/// No description provided for @formatInlineMath.
///
/// In en, this message translates to:
/// **'Formula in the line'**
String get formatInlineMath ;
/// No description provided for @formulaTitle.
///
/// In en, this message translates to:
/// **'Formula'**
String get formulaTitle ;
/// No description provided for @formulaInLineTitle.
///
/// In en, this message translates to:
/// **'Formula in the line'**
String get formulaInLineTitle ;
/// No description provided for @formulaPreview.
///
/// In en, this message translates to:
/// **'Preview'**
String get formulaPreview ;
/// No description provided for @formulaPreviewEmpty.
///
/// In en, this message translates to:
/// **'The formula shows here'**
String get formulaPreviewEmpty ;
/// No description provided for @formulaLatex.
///
/// In en, this message translates to:
/// **'LaTeX'**
String get formulaLatex ;
/// No description provided for @formulaLatexHelper.
///
/// In en, this message translates to:
/// **'\\\\ starts a new line in aligned, cases or a matrix.'**
String get formulaLatexHelper ;
/// No description provided for @formulaEmpty.
///
/// In en, this message translates to:
/// **'An empty formula. Tap to write it.'**
String get formulaEmpty ;
/// No description provided for @formulaEdit.
///
/// In en, this message translates to:
/// **'Edit formula'**
String get formulaEdit ;
/// No description provided for @formulaRemove.
///
/// In en, this message translates to:
/// **'Remove formula'**
String get formulaRemove ;
/// No description provided for @blockYoutube.
///
/// In en, this message translates to:
/// **'YouTube video'**
String get blockYoutube ;
/// No description provided for @youtubeLink.
///
/// In en, this message translates to:
/// **'YouTube link'**
String get youtubeLink ;
/// No description provided for @youtubeLinkHelper.
///
/// In en, this message translates to:
/// **'Paste a link from YouTube.'**
String get youtubeLinkHelper ;
/// No description provided for @youtubeNotFound.
///
/// In en, this message translates to:
/// **'No YouTube video in this link.'**
String get youtubeNotFound ;
/// No description provided for @youtubeFound.
///
/// In en, this message translates to:
/// **'Found the video.'**
String get youtubeFound ;
/// No description provided for @youtubeFoundFrom.
///
/// In en, this message translates to:
/// **'Found the video, starting at {time}.'**
String youtubeFoundFrom ( String time ) ;
/// No description provided for @youtubeCaption.
///
/// In en, this message translates to:
/// **'Caption'**
String get youtubeCaption ;
/// No description provided for @youtubeCaptionHelper.
///
/// In en, this message translates to:
/// **'Shown in place of the video until it plays.'**
String get youtubeCaptionHelper ;
/// No description provided for @youtubeVideo.
///
/// In en, this message translates to:
/// **'YouTube video'**
String get youtubeVideo ;
/// No description provided for @youtubeSource.
///
/// In en, this message translates to:
/// **'YouTube'**
String get youtubeSource ;
/// No description provided for @youtubeFrom.
///
/// In en, this message translates to:
/// **'YouTube · from {time}'**
String youtubeFrom ( String time ) ;
/// No description provided for @youtubeEdit.
///
/// In en, this message translates to:
/// **'Edit video'**
String get youtubeEdit ;
/// No description provided for @youtubePlay.
///
/// In en, this message translates to:
/// **'Play video'**
String get youtubePlay ;
/// No description provided for @youtubeOpen.
///
/// In en, this message translates to:
/// **'Open on YouTube'**
String get youtubeOpen ;
/// No description provided for @youtubeRemove.
///
/// In en, this message translates to:
/// **'Remove video'**
String get youtubeRemove ;
/// No description provided for @youtubeBroken.
///
/// In en, this message translates to:
/// **'This video\'s link can\'t be read.'**
String get youtubeBroken ;
/// No description provided for @youtubeNotOpened.
///
/// In en, this message translates to:
/// **'Couldn\'t open YouTube.'**
String get youtubeNotOpened ;
/// No description provided for @blockStudyButton.
///
/// In en, this message translates to:
/// **'Study button'**
String get blockStudyButton ;
/// No description provided for @studyButtonDefaultLabel.
///
/// In en, this message translates to:
/// **'Study'**
String get studyButtonDefaultLabel ;
/// No description provided for @studyButtonStudyset.
///
/// In en, this message translates to:
/// **'Studyset to study'**
String get studyButtonStudyset ;
/// No description provided for @studyButtonTag.
///
/// In en, this message translates to:
/// **'Cards'**
String get studyButtonTag ;
/// No description provided for @studyButtonAllCards.
///
/// In en, this message translates to:
/// **'All cards'**
String get studyButtonAllCards ;
/// No description provided for @studyButtonLabel.
///
/// In en, this message translates to:
/// **'Label'**
String get studyButtonLabel ;
/// No description provided for @studyButtonTopics.
///
/// In en, this message translates to:
/// **'With its topics'**
String get studyButtonTopics ;
/// No description provided for @studyButtonBackFirst.
///
/// In en, this message translates to:
/// **'Back first'**
String get studyButtonBackFirst ;
/// No description provided for @studyButtonBackFirstHelper.
///
/// In en, this message translates to:
/// **'Front/back and vocabulary cards ask their back.'**
String get studyButtonBackFirstHelper ;
/// No description provided for @studyButtonWithTag.
///
/// In en, this message translates to:
/// **'Tagged {tag}'**
String studyButtonWithTag ( String tag ) ;
/// No description provided for @studyButtonAlone.
///
/// In en, this message translates to:
/// **'Without its topics'**
String get studyButtonAlone ;
/// No description provided for @studyButtonEdit.
///
/// In en, this message translates to:
/// **'Edit study button'**
String get studyButtonEdit ;
/// No description provided for @studyButtonRemove.
///
/// In en, this message translates to:
/// **'Remove study button'**
String get studyButtonRemove ;
/// No description provided for @studyButtonBroken.
///
/// In en, this message translates to:
/// **'This study button doesn\'t say what to study.'**
String get studyButtonBroken ;
/// No description provided for @studyButtonGone.
///
/// In en, this message translates to:
/// **'That studyset no longer exists.'**
String get studyButtonGone ;
feat: PDF sources: read, take notes and cards from pages, deadlines
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
/// No description provided for @pdfPageField.
///
/// In en, this message translates to:
/// **'PDF page'**
String get pdfPageField ;
/// No description provided for @studysetTabFiles.
///
/// In en, this message translates to:
/// **'Files'**
String get studysetTabFiles ;
/// No description provided for @pdfUploadMenu.
///
/// In en, this message translates to:
/// **'Upload a PDF'**
String get pdfUploadMenu ;
/// No description provided for @pdfFileType.
///
/// In en, this message translates to:
/// **'PDFs'**
String get pdfFileType ;
/// No description provided for @pdfAdded.
///
/// In en, this message translates to:
/// **'Added \"{title}\".'**
String pdfAdded ( String title ) ;
/// No description provided for @pdfNotPdf.
///
/// In en, this message translates to:
/// **'This file isn\'t a PDF.'**
String get pdfNotPdf ;
/// No description provided for @pdfTooLarge.
///
/// In en, this message translates to:
/// **'This PDF is larger than {megabytes} MB.'**
String pdfTooLarge ( int megabytes ) ;
/// No description provided for @pdfDuplicateTitle.
///
/// In en, this message translates to:
/// **'PDF already here'**
String get pdfDuplicateTitle ;
/// No description provided for @pdfDuplicateMessage.
///
/// In en, this message translates to:
/// **'This file is \"{title}\", which this studyset holds already. Add it as a copy, or keep the one there?'**
String pdfDuplicateMessage ( String title ) ;
/// No description provided for @pdfDuplicateKeep.
///
/// In en, this message translates to:
/// **'Keep the one there'**
String get pdfDuplicateKeep ;
/// No description provided for @pdfDuplicateAdd.
///
/// In en, this message translates to:
/// **'Add a copy'**
String get pdfDuplicateAdd ;
/// No description provided for @filesEmpty.
///
/// In en, this message translates to:
/// **'No files yet'**
String get filesEmpty ;
/// No description provided for @filesEmptyHint.
///
/// In en, this message translates to:
/// **'Use \"Add material → Upload a PDF\" to add one.'**
String get filesEmptyHint ;
/// No description provided for @filesSort.
///
/// In en, this message translates to:
/// **'Sort'**
String get filesSort ;
feat: files in an order of one's own, with Reorder beside the tabs
A PDF now has a position among its studyset's files, as notes and paths
have: a fractional index in a column of its own (so moving it on one
device and renaming it on another don't overwrite each other), last
writer wins. The migration adds it to pdf_source on the server and on
each device with 'a0', so PDFs from before stand in the order they were
added (ties sort by id), and new ones go at the end.
The Files tab lists them in that order by default (Custom order, first
in the Sort menu; newest first was the default before). With two files
or more, Reorder beside the tabs shows the custom order whatever they
were sorted by, a grip in place of each file's options and the Sort menu
put away, until Done.
The next release changes synced tables anyway (card.buriedAt, since
v0.2.2), so older apps have to update for that already.
Also: card.position's doc no longer speaks of moving cards.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MMm7usAo6q9ZZRoPkWjoxi
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-08 19:28:05 +02:00
/// No description provided for @filesSortCustom.
///
/// In en, this message translates to:
/// **'Custom order'**
String get filesSortCustom ;
feat: PDF sources: read, take notes and cards from pages, deadlines
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
/// No description provided for @filesSortName.
///
/// In en, this message translates to:
/// **'Name (A– Z)'**
String get filesSortName ;
/// No description provided for @filesSortNewest.
///
/// In en, this message translates to:
/// **'Newest first'**
String get filesSortNewest ;
/// No description provided for @filesSortOldest.
///
/// In en, this message translates to:
/// **'Oldest first'**
String get filesSortOldest ;
/// No description provided for @filesSortLargest.
///
/// In en, this message translates to:
/// **'Largest first'**
String get filesSortLargest ;
/// No description provided for @pdfPages.
///
/// In en, this message translates to:
/// **'{count, plural, =1{1 page} other{{count} pages}}'**
String pdfPages ( int count ) ;
/// No description provided for @pdfPercentRead.
///
/// In en, this message translates to:
/// **'{percent}% read'**
String pdfPercentRead ( int percent ) ;
/// No description provided for @pdfDeadlineMet.
///
/// In en, this message translates to:
/// **'Reading deadline met'**
String get pdfDeadlineMet ;
/// No description provided for @pdfDeadlineUpcoming.
///
/// In en, this message translates to:
/// **'Starts in {days, plural, =1{1 day} other{{days} days}} — {pages}/day planned'**
String pdfDeadlineUpcoming ( int days , int pages ) ;
/// No description provided for @pdfDeadlinePassed.
///
/// In en, this message translates to:
/// **'Deadline passed — {pages, plural, =1{1 page} other{{pages} pages}} left'**
String pdfDeadlinePassed ( int pages ) ;
/// No description provided for @pdfDeadlineToday.
///
/// In en, this message translates to:
/// **'Due today — {pages, plural, =1{1 page} other{{pages} pages}} left'**
String pdfDeadlineToday ( int pages ) ;
/// No description provided for @pdfDeadlinePace.
///
/// In en, this message translates to:
/// **'{pages}/day to reach page {target} in {days, plural, =1{1 day} other{{days} days}}'**
String pdfDeadlinePace ( int pages , int target , int days ) ;
/// No description provided for @pdfOptions.
///
/// In en, this message translates to:
/// **'Actions'**
String get pdfOptions ;
/// No description provided for @pdfRename.
///
/// In en, this message translates to:
/// **'Rename'**
String get pdfRename ;
/// No description provided for @pdfRenameTitle.
///
/// In en, this message translates to:
/// **'Rename PDF'**
String get pdfRenameTitle ;
/// No description provided for @pdfRenameLabel.
///
/// In en, this message translates to:
/// **'Title'**
String get pdfRenameLabel ;
/// No description provided for @pdfSetDeadline.
///
/// In en, this message translates to:
/// **'Set reading deadline'**
String get pdfSetDeadline ;
/// No description provided for @pdfEditDeadline.
///
/// In en, this message translates to:
/// **'Edit reading deadline'**
String get pdfEditDeadline ;
/// No description provided for @pdfMarkFinished.
///
/// In en, this message translates to:
/// **'Mark as finished'**
String get pdfMarkFinished ;
/// No description provided for @pdfResetReading.
///
/// In en, this message translates to:
/// **'Reset reading progress'**
String get pdfResetReading ;
/// No description provided for @pdfResetTitle.
///
/// In en, this message translates to:
/// **'Reset reading progress?'**
String get pdfResetTitle ;
/// No description provided for @pdfResetMessage.
///
/// In en, this message translates to:
/// **'How far you\'ve read \"{title}\" is forgotten. The PDF, its flashcards and notes stay.'**
String pdfResetMessage ( String title ) ;
/// No description provided for @pdfResetConfirm.
///
/// In en, this message translates to:
/// **'Reset'**
String get pdfResetConfirm ;
/// No description provided for @pdfMoreInfo.
///
/// In en, this message translates to:
/// **'More info'**
String get pdfMoreInfo ;
/// No description provided for @pdfInfoFileName.
///
/// In en, this message translates to:
/// **'File name'**
String get pdfInfoFileName ;
/// No description provided for @pdfInfoSize.
///
/// In en, this message translates to:
/// **'Size'**
String get pdfInfoSize ;
/// No description provided for @pdfInfoPages.
///
/// In en, this message translates to:
/// **'Pages'**
String get pdfInfoPages ;
/// No description provided for @pdfInfoUnknown.
///
/// In en, this message translates to:
/// **'Known once it is opened'**
String get pdfInfoUnknown ;
/// No description provided for @pdfDelete.
///
/// In en, this message translates to:
/// **'Delete'**
String get pdfDelete ;
/// No description provided for @pdfDeleteTitle.
///
/// In en, this message translates to:
/// **'Delete PDF?'**
String get pdfDeleteTitle ;
/// No description provided for @pdfDeleteMessage.
///
/// In en, this message translates to:
/// **'\"{title}\" is removed from this studyset, with how far it was read. Flashcards and notes made from it stay.'**
String pdfDeleteMessage ( String title ) ;
/// No description provided for @pdfDeleteConfirm.
///
/// In en, this message translates to:
/// **'Delete'**
String get pdfDeleteConfirm ;
/// No description provided for @pdfDeleted.
///
/// In en, this message translates to:
/// **'PDF deleted'**
String get pdfDeleted ;
/// No description provided for @deadlineSheetSet.
///
/// In en, this message translates to:
/// **'Set a reading deadline'**
String get deadlineSheetSet ;
/// No description provided for @deadlineSheetEdit.
///
/// In en, this message translates to:
/// **'Edit reading deadline'**
String get deadlineSheetEdit ;
/// No description provided for @deadlineSheetHelp.
///
/// In en, this message translates to:
/// **'Reach a page by a day. Progress follows the furthest page you reach as you read.'**
String get deadlineSheetHelp ;
/// No description provided for @deadlineReachPageOf.
///
/// In en, this message translates to:
/// **'Reach page (of {count})'**
String deadlineReachPageOf ( int count ) ;
/// No description provided for @deadlineReachPage.
///
/// In en, this message translates to:
/// **'Reach page'**
String get deadlineReachPage ;
/// No description provided for @deadlineDecrease.
///
/// In en, this message translates to:
/// **'Decrease'**
String get deadlineDecrease ;
/// No description provided for @deadlineIncrease.
///
/// In en, this message translates to:
/// **'Increase'**
String get deadlineIncrease ;
/// No description provided for @deadlineChapter.
///
/// In en, this message translates to:
/// **'Or finish a chapter'**
String get deadlineChapter ;
/// No description provided for @deadlineChapterEntry.
///
/// In en, this message translates to:
/// **'{title} · to p.{page}'**
String deadlineChapterEntry ( String title , int page ) ;
/// No description provided for @deadlineBy.
///
/// In en, this message translates to:
/// **'By'**
String get deadlineBy ;
/// No description provided for @deadlineStart.
///
/// In en, this message translates to:
/// **'Start on'**
String get deadlineStart ;
/// No description provided for @deadlineStartToday.
///
/// In en, this message translates to:
/// **'Today'**
String get deadlineStartToday ;
/// No description provided for @deadlineStartReset.
///
/// In en, this message translates to:
/// **'Start today'**
String get deadlineStartReset ;
/// No description provided for @deadlineStartHelp.
///
/// In en, this message translates to:
/// **'The daily pace counts from this day on. Until then the PDF waits under \"Upcoming\".'**
String get deadlineStartHelp ;
/// No description provided for @deadlineSave.
///
/// In en, this message translates to:
/// **'Save deadline'**
String get deadlineSave ;
/// No description provided for @deadlineRemove.
///
/// In en, this message translates to:
/// **'Remove deadline'**
String get deadlineRemove ;
/// No description provided for @readerMissing.
///
/// In en, this message translates to:
/// **'This PDF isn\'t on this device yet. It opens once the device that added it has uploaded it and you\'re online.'**
String get readerMissing ;
/// No description provided for @readerUnreadable.
///
/// In en, this message translates to:
/// **'This file can\'t be read as a PDF.'**
String get readerUnreadable ;
/// No description provided for @readerBack.
///
/// In en, this message translates to:
/// **'Back to studyset'**
String get readerBack ;
/// No description provided for @readerSearch.
///
/// In en, this message translates to:
/// **'Search PDF'**
String get readerSearch ;
/// No description provided for @readerFitWidth.
///
/// In en, this message translates to:
/// **'Fit width'**
String get readerFitWidth ;
/// No description provided for @readerFitHeight.
///
/// In en, this message translates to:
/// **'Fit height'**
String get readerFitHeight ;
/// No description provided for @readerZoomIn.
///
/// In en, this message translates to:
/// **'Zoom in'**
String get readerZoomIn ;
/// No description provided for @readerZoomOut.
///
/// In en, this message translates to:
/// **'Zoom out'**
String get readerZoomOut ;
/// No description provided for @readerSetZoom.
///
/// In en, this message translates to:
/// **'Set zoom'**
String get readerSetZoom ;
/// No description provided for @readerZoomTitle.
///
/// In en, this message translates to:
/// **'Zoom'**
String get readerZoomTitle ;
/// No description provided for @readerZoomApply.
///
/// In en, this message translates to:
/// **'Apply'**
String get readerZoomApply ;
/// No description provided for @readerFullscreen.
///
/// In en, this message translates to:
/// **'Fullscreen'**
String get readerFullscreen ;
/// No description provided for @readerExitFullscreen.
///
/// In en, this message translates to:
/// **'Exit fullscreen'**
String get readerExitFullscreen ;
/// No description provided for @readerPreviousPage.
///
/// In en, this message translates to:
/// **'Previous page'**
String get readerPreviousPage ;
/// No description provided for @readerNextPage.
///
/// In en, this message translates to:
/// **'Next page'**
String get readerNextPage ;
/// No description provided for @readerPageOf.
///
/// In en, this message translates to:
/// **'/ {count}'**
String readerPageOf ( int count ) ;
/// No description provided for @readerFindHint.
///
/// In en, this message translates to:
/// **'Find in PDF'**
String get readerFindHint ;
/// No description provided for @readerMatchOf.
///
/// In en, this message translates to:
/// **'{current} of {total}'**
String readerMatchOf ( int current , int total ) ;
/// No description provided for @readerPreviousMatch.
///
/// In en, this message translates to:
/// **'Previous match'**
String get readerPreviousMatch ;
/// No description provided for @readerNextMatch.
///
/// In en, this message translates to:
/// **'Next match'**
String get readerNextMatch ;
/// No description provided for @readerCloseSearch.
///
/// In en, this message translates to:
/// **'Close search'**
String get readerCloseSearch ;
/// No description provided for @readerExpand.
///
/// In en, this message translates to:
/// **'Expand'**
String get readerExpand ;
/// No description provided for @readerCollapse.
///
/// In en, this message translates to:
/// **'Collapse'**
String get readerCollapse ;
/// No description provided for @readerTabFlashcards.
///
/// In en, this message translates to:
/// **'Flashcards'**
String get readerTabFlashcards ;
/// No description provided for @readerTabNotes.
///
/// In en, this message translates to:
/// **'Notes'**
String get readerTabNotes ;
/// No description provided for @readerCardCreated.
///
/// In en, this message translates to:
/// **'Flashcard created'**
String get readerCardCreated ;
/// No description provided for @readerCardDeleteTitle.
///
/// In en, this message translates to:
/// **'Delete flashcard?'**
String get readerCardDeleteTitle ;
/// No description provided for @readerCardDeleteMessage.
///
/// In en, this message translates to:
/// **'The flashcard is deleted, with how it was studied.'**
String get readerCardDeleteMessage ;
/// No description provided for @readerCardDeleteConfirm.
///
/// In en, this message translates to:
/// **'Delete'**
String get readerCardDeleteConfirm ;
/// No description provided for @readerFrontMatter.
///
/// In en, this message translates to:
/// **'Front matter'**
String get readerFrontMatter ;
/// No description provided for @readerPage.
///
/// In en, this message translates to:
/// **'Page {page}'**
String readerPage ( int page ) ;
/// No description provided for @readerByPage.
///
/// In en, this message translates to:
/// **'Page'**
String get readerByPage ;
/// No description provided for @readerByChapter.
///
/// In en, this message translates to:
/// **'Chapter'**
String get readerByChapter ;
/// No description provided for @readerCardsEmpty.
///
/// In en, this message translates to:
/// **'Select text to make flashcards in no time'**
String get readerCardsEmpty ;
/// No description provided for @readerCardsEmptyHint.
///
/// In en, this message translates to:
/// **'Select the lines that matter and send them to a question or an answer, or press \"Create flashcard\".'**
String get readerCardsEmptyHint ;
/// No description provided for @readerCreateCard.
///
/// In en, this message translates to:
/// **'Create flashcard'**
String get readerCreateCard ;
/// No description provided for @readerEditCard.
///
/// In en, this message translates to:
/// **'Edit flashcard'**
String get readerEditCard ;
/// No description provided for @readerBackToCards.
///
/// In en, this message translates to:
/// **'Back to flashcards'**
String get readerBackToCards ;
/// No description provided for @readerReferencedPage.
///
/// In en, this message translates to:
/// **'Referenced page: {page}'**
String readerReferencedPage ( int page ) ;
/// No description provided for @readerUseCurrentPage.
///
/// In en, this message translates to:
/// **'Use current ({page})'**
String readerUseCurrentPage ( int page ) ;
/// No description provided for @readerQuestion.
///
/// In en, this message translates to:
/// **'Question'**
String get readerQuestion ;
/// No description provided for @readerAnswer.
///
/// In en, this message translates to:
/// **'Answer'**
String get readerAnswer ;
/// No description provided for @readerKeepOpen.
///
/// In en, this message translates to:
/// **'Keep creation mode open'**
String get readerKeepOpen ;
/// No description provided for @readerClear.
///
/// In en, this message translates to:
/// **'Clear'**
String get readerClear ;
/// No description provided for @readerCreate.
///
/// In en, this message translates to:
/// **'Create'**
String get readerCreate ;
/// No description provided for @readerUntitledCard.
///
/// In en, this message translates to:
/// **'Untitled flashcard'**
String get readerUntitledCard ;
/// No description provided for @readerCardOptions.
///
/// In en, this message translates to:
/// **'Card options'**
String get readerCardOptions ;
/// No description provided for @readerNoteSaved.
///
/// In en, this message translates to:
/// **'Saved'**
String get readerNoteSaved ;
/// No description provided for @readerNoteEditing.
///
/// In en, this message translates to:
/// **'Editing…'**
String get readerNoteEditing ;
/// No description provided for @readerNoteSaving.
///
/// In en, this message translates to:
/// **'Saving…'**
String get readerNoteSaving ;
/// No description provided for @readerNoteFailed.
///
/// In en, this message translates to:
/// **'Save failed'**
String get readerNoteFailed ;
/// No description provided for @readerToQuestion.
///
/// In en, this message translates to:
/// **'Question'**
String get readerToQuestion ;
/// No description provided for @readerToAnswer.
///
/// In en, this message translates to:
/// **'Answer'**
String get readerToAnswer ;
/// No description provided for @readerToNote.
///
/// In en, this message translates to:
/// **'Notes'**
String get readerToNote ;
/// No description provided for @readerLineBreaksJoined.
///
/// In en, this message translates to:
/// **'Line breaks joined (tap to keep)'**
String get readerLineBreaksJoined ;
/// No description provided for @readerLineBreaksKept.
///
/// In en, this message translates to:
/// **'Line breaks kept (tap to join)'**
String get readerLineBreaksKept ;
/// No description provided for @blockScreenshot.
///
/// In en, this message translates to:
/// **'Screenshot of the page'**
String get blockScreenshot ;
/// No description provided for @readerCaptureCancel.
///
/// In en, this message translates to:
/// **'Cancel screenshot'**
String get readerCaptureCancel ;
/// No description provided for @readerCaptureHint.
///
/// In en, this message translates to:
/// **'Drag over the part of the page to keep'**
String get readerCaptureHint ;
/// No description provided for @readerCaptureFailed.
///
/// In en, this message translates to:
/// **'The page couldn\'t be drawn. Try again once it has loaded.'**
String get readerCaptureFailed ;
/// No description provided for @readerScreenshotSaved.
///
/// In en, this message translates to:
/// **'Screenshot saved'**
String get readerScreenshotSaved ;
/// No description provided for @imageOpenPdfPage.
///
/// In en, this message translates to:
/// **'Open the PDF at this page'**
String get imageOpenPdfPage ;
/// No description provided for @homeWidgetDeadlines.
///
/// In en, this message translates to:
feat(app): Home, the Library and the studyset sheets laid out as before
Home: plain cards on the surface, 16 dp apart, the goal first and the
Pomodoro timer after it in a new dashboard. The streak chip sits centred
in the app bar, and its sheet shows the days inside a big flame and the
week as marks over the weekdays' initials, with a word on today in a box.
The week names its day as "Thursday, Oct 8" and its days carry no
tooltip. The heatmap scrolls its weekdays with the weeks, outlines the
days to come, and names a day's activities with its date. The goal card
has a ring of 5 dp, a check once met and a pencil, and its sheet saves
across its width with Remove goal below. Arranging puts an eye on each
widget's corner and lifts the one picked up, with a tap the hand feels;
with every widget hidden, Customise brings arranging back. The stepper
moves to core, where the goal's sheet takes it too.
Library: the counts line includes files (PDFs); Refresh in the app bar
and pulling the list down sync now; a list that can't be read offers
Retry, which reads it again; the star keeps its square corners.
Studyset sheet: "Title *", the secondary title explained behind a
question mark, Inherit as a box beside the colour, and the description
in up to three lines with a chevron. Move sheet: round marks, the
studyset itself marked as current, the top level only while it is
inside another, archived studysets last under their heading, the
destination named in the footer, and a new studyset to move into, made
only once the move is.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-08 11:12:23 +02:00
/// **'PDF reading deadlines'**
feat: PDF sources: read, take notes and cards from pages, deadlines
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
String get homeWidgetDeadlines ;
/// No description provided for @deadlinesTitle.
///
/// In en, this message translates to:
/// **'Reading deadlines'**
String get deadlinesTitle ;
/// No description provided for @deadlinesCurrent.
///
/// In en, this message translates to:
/// **'Current'**
String get deadlinesCurrent ;
/// No description provided for @deadlinesUpcoming.
///
/// In en, this message translates to:
/// **'Upcoming'**
String get deadlinesUpcoming ;
/// No description provided for @deadlinesProgress.
///
/// In en, this message translates to:
/// **'p.{furthest}/{target} · {when}'**
String deadlinesProgress ( int furthest , int target , String when ) ;
/// No description provided for @deadlinePassedShort.
///
/// In en, this message translates to:
/// **'deadline passed'**
String get deadlinePassedShort ;
/// No description provided for @deadlineDueTodayShort.
///
/// In en, this message translates to:
/// **'due today'**
String get deadlineDueTodayShort ;
/// No description provided for @deadlineDaysLeft.
///
/// In en, this message translates to:
/// **'{days, plural, =1{1 day left} other{{days} days left}}'**
String deadlineDaysLeft ( int days ) ;
/// No description provided for @deadlineToPageToday.
///
/// In en, this message translates to:
/// **'to p.{page} today'**
String deadlineToPageToday ( int page ) ;
/// No description provided for @deadlineStartsIn.
///
/// In en, this message translates to:
/// **'Starts in {days, plural, =1{1 day} other{{days} days}}'**
String deadlineStartsIn ( int days ) ;
/// No description provided for @deadlinesPlaceholder.
///
/// In en, this message translates to:
/// **'Appears when you have reading deadlines'**
String get deadlinesPlaceholder ;
/// No description provided for @deadlinesOpenTimeline.
///
/// In en, this message translates to:
/// **'Open the timeline'**
String get deadlinesOpenTimeline ;
/// No description provided for @timelineTitle.
///
/// In en, this message translates to:
/// **'Reading timeline'**
String get timelineTitle ;
/// No description provided for @timelineEmpty.
///
/// In en, this message translates to:
/// **'No reading deadlines yet'**
String get timelineEmpty ;
/// No description provided for @timelineEmptyHint.
///
/// In en, this message translates to:
/// **'Set a reading deadline on a PDF to see it here.'**
String get timelineEmptyHint ;
/// No description provided for @timelineToday.
///
/// In en, this message translates to:
/// **'Today'**
String get timelineToday ;
/// No description provided for @timelinePages.
///
/// In en, this message translates to:
/// **'p.{furthest}/{target}'**
String timelinePages ( int furthest , int target ) ;
/// No description provided for @chapterFinished.
///
/// In en, this message translates to:
/// **'Finished “{chapter}” — study the {count, plural, =1{card} other{{count} cards}} you made?'**
String chapterFinished ( String chapter , int count ) ;
/// No description provided for @chapterStudyNow.
///
/// In en, this message translates to:
/// **'Study now'**
String get chapterStudyNow ;
/// No description provided for @chapterDismiss.
///
/// In en, this message translates to:
/// **'Dismiss'**
String get chapterDismiss ;
/// No description provided for @deadlineChapterLoading.
///
/// In en, this message translates to:
/// **'Loading chapters…'**
String get deadlineChapterLoading ;
/// No description provided for @fileSizeKb.
///
/// In en, this message translates to:
/// **'{size} KB'**
String fileSizeKb ( int size ) ;
/// No description provided for @fileSizeMb.
///
/// In en, this message translates to:
/// **'{size} MB'**
String fileSizeMb ( String size ) ;
/// No description provided for @pdfPageShort.
///
/// In en, this message translates to:
/// **'p. {page}'**
String pdfPageShort ( int page ) ;
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 04:52:02 +02:00
/// No description provided for @ankiImportMenu.
///
/// In en, this message translates to:
/// **'Import from Anki'**
String get ankiImportMenu ;
/// No description provided for @ankiImportFileType.
///
/// In en, this message translates to:
/// **'Anki decks'**
String get ankiImportFileType ;
/// No description provided for @ankiImportTitle.
///
/// In en, this message translates to:
/// **'Import Anki deck'**
String get ankiImportTitle ;
/// No description provided for @ankiImportOptionsTitle.
///
/// In en, this message translates to:
/// **'How should subdecks be imported?'**
String get ankiImportOptionsTitle ;
/// No description provided for @ankiImportReading.
///
/// In en, this message translates to:
/// **'Reading the deck…'**
String get ankiImportReading ;
/// No description provided for @ankiImportSummary.
///
/// In en, this message translates to:
/// **'{cards, plural, =1{1 card.} other{{cards} cards.}}'**
String ankiImportSummary ( int cards ) ;
/// No description provided for @ankiImportSummaryDecks.
///
/// In en, this message translates to:
/// **'{cards, plural, =1{1 card} other{{cards} cards}} across {decks} subdecks.'**
String ankiImportSummaryDecks ( int cards , int decks ) ;
/// No description provided for @ankiImportModeFlatten.
///
/// In en, this message translates to:
/// **'Flatten into this studyset'**
String get ankiImportModeFlatten ;
/// No description provided for @ankiImportModeFlattenSubtitle.
///
/// In en, this message translates to:
/// **'Import every card here and ignore the subdecks.'**
String get ankiImportModeFlattenSubtitle ;
/// No description provided for @ankiImportModeSubStudysets.
///
/// In en, this message translates to:
/// **'Recreate as sub-studysets'**
String get ankiImportModeSubStudysets ;
/// No description provided for @ankiImportModeSubStudysetsSubtitle.
///
/// In en, this message translates to:
/// **'Mirror the subdeck tree: each subdeck becomes a nested studyset.'**
String get ankiImportModeSubStudysetsSubtitle ;
/// No description provided for @ankiImportModeTag.
///
/// In en, this message translates to:
/// **'Tag cards by subdeck'**
String get ankiImportModeTag ;
/// No description provided for @ankiImportModeTagSubtitle.
///
/// In en, this message translates to:
/// **'Keep every card here, tagged with each subdeck it came from.'**
String get ankiImportModeTagSubtitle ;
/// No description provided for @ankiImportProgress.
///
/// In en, this message translates to:
/// **'Import study progress'**
String get ankiImportProgress ;
/// No description provided for @ankiImportProgressSubtitle.
///
/// In en, this message translates to:
/// **'Bring Anki\'s review history over: cards continue where they left off. New cards stay new.'**
String get ankiImportProgressSubtitle ;
/// No description provided for @ankiImportMedia.
///
/// In en, this message translates to:
/// **'{count, plural, =1{Include media (1 file)} other{Include media ({count} files)}}'**
String ankiImportMedia ( int count ) ;
/// No description provided for @ankiImportMediaSubtitle.
///
/// In en, this message translates to:
/// **'Keep the deck\'s images and sounds on the cards.'**
String get ankiImportMediaSubtitle ;
Limit how much room people's files take
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 05:57:57 +02:00
/// No description provided for @ankiImportMediaMayNotFit.
///
/// In en, this message translates to:
/// **'Its files take {size} MB, more than the {left} MB you have left: those that don\'t fit stay on this device, and your other devices won\'t show them.'**
String ankiImportMediaMayNotFit ( String size , String left ) ;
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 04:52:02 +02:00
/// No description provided for @ankiImportRunning.
///
/// In en, this message translates to:
/// **'Importing…'**
String get ankiImportRunning ;
/// No description provided for @ankiImportConfirm.
///
/// In en, this message translates to:
/// **'Import'**
String get ankiImportConfirm ;
/// No description provided for @ankiImportNotAPackage.
///
/// In en, this message translates to:
/// **'This isn\'t an Anki deck. In Anki, export a deck as an .apkg file and import that.'**
String get ankiImportNotAPackage ;
/// No description provided for @ankiImportNewerFormat.
///
/// In en, this message translates to:
/// **'This deck comes from a newer Anki than this app knows. Export it again with “Support older Anki versions”.'**
String get ankiImportNewerFormat ;
/// No description provided for @ankiImportUnreadable.
///
/// In en, this message translates to:
/// **'This deck is damaged and can\'t be read.'**
String get ankiImportUnreadable ;
/// No description provided for @ankiImportBrokeOff.
///
/// In en, this message translates to:
/// **'The import broke off. The cards it brought in so far stay.'**
String get ankiImportBrokeOff ;
/// No description provided for @ankiImported.
///
/// In en, this message translates to:
/// **'{count, plural, =1{Imported 1 flashcard from Anki.} other{Imported {count} flashcards from Anki.}}'**
String ankiImported ( int count ) ;
/// No description provided for @ankiImportedSubStudysets.
///
/// In en, this message translates to:
/// **'{count, plural, =1{Imported 1 card into sub-studysets.} other{Imported {count} cards into sub-studysets.}}'**
String ankiImportedSubStudysets ( int count ) ;
/// No description provided for @ankiImportedTagged.
///
/// In en, this message translates to:
/// **'{count, plural, =1{Imported 1 card, tagged by subdeck.} other{Imported {count} cards, tagged by subdeck.}}'**
String ankiImportedTagged ( int count ) ;
/// No description provided for @ankiImportFilesSkipped.
///
/// In en, this message translates to:
/// **'{count, plural, =1{1 media file couldn\'t be imported.} other{{count} media files couldn\'t be imported.}}'**
String ankiImportFilesSkipped ( int count ) ;
feat(app): a Pomodoro timer on Home, in the notification and in the top bar
Home has a Pomodoro timer (decided 2026-10-07, after Goodtime): the
profile it runs, chosen there while no timer is set, the time left of
the phase, and Start, Pause or Resume, Skip and Stop. While studying, a
small countdown sits in the study screen's header, and a tap pauses it
or goes on. A phase that ends leads to the next, which starts by itself
where the profile says so and otherwise waits, paused at its full
length.
Profiles follow the person to every device, in their settings (schema
4, still written as schema 3 while the profiles are as they come, so
older apps go on reading the settings): Classic, 25 minutes of focus,
5 of break and 15 after four, and 52/17 without long breaks, built in;
both can be changed, more added and deleted, from Home's card or from
Settings. A profile can start breaks and focus by themselves, and keep
the device quiet while focusing. The running timer is the device's own,
kept in the person's folder: closed and opened again, the app catches
up with the phases that ran out meanwhile.
Android: the app's own foreground service (Kotlin, specialUse, as
Goodtime does) shows an ongoing notification counting the phase down,
with Pause or Resume, Skip and Stop; on Android 16 it is a Live Update,
its progress bar the profile's cycle, each phase a segment, focus in
cobalt and breaks in copper. An exact alarm (USE_EXACT_ALARM) alerts
with sound and vibration when the phase ends, also with the app
closed, and Do Not Disturb goes on while focusing once the person
allowed it, the filter before given back afterwards. No notification
plugin: flutter_local_notifications has Live Updates only in a
prerelease that needs compileSdk 37; androidx.core 1.18, which the
build resolved already, has every API needed.
Linux, over the session bus (package:dbus, already in the build): where
the desktop shows icons in its top bar (GNOME with the AppIndicator
extension, which Ubuntu has built in; KDE) the timer counts down there,
the buttons in the icon's menu, as a StatusNotifierItem written in Dart
on the names and paths the snap's desktop plug allows; elsewhere a
notification with the buttons says until when the phase runs. An alert
with sound ends each phase while the app runs, and GNOME's banners stay
off while focusing, through gsettings. xdg-desktop-portal's Background
status, the planned fallback, serves Flatpaks only.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-07 02:54:52 +02:00
/// No description provided for @pomodoroTitle.
///
/// In en, this message translates to:
/// **'Pomodoro'**
String get pomodoroTitle ;
/// No description provided for @pomodoroFocus.
///
/// In en, this message translates to:
/// **'Focus'**
String get pomodoroFocus ;
/// No description provided for @pomodoroShortBreak.
///
/// In en, this message translates to:
/// **'Short break'**
String get pomodoroShortBreak ;
/// No description provided for @pomodoroLongBreak.
///
/// In en, this message translates to:
/// **'Long break'**
String get pomodoroLongBreak ;
/// No description provided for @pomodoroFocusOf.
///
/// In en, this message translates to:
/// **'Focus {done} of {total}'**
String pomodoroFocusOf ( int done , int total ) ;
/// No description provided for @pomodoroStart.
///
/// In en, this message translates to:
/// **'Start'**
String get pomodoroStart ;
/// No description provided for @pomodoroPause.
///
/// In en, this message translates to:
/// **'Pause'**
String get pomodoroPause ;
/// No description provided for @pomodoroResume.
///
/// In en, this message translates to:
/// **'Resume'**
String get pomodoroResume ;
/// No description provided for @pomodoroSkip.
///
/// In en, this message translates to:
/// **'Skip'**
String get pomodoroSkip ;
/// No description provided for @pomodoroStop.
///
/// In en, this message translates to:
/// **'Stop'**
String get pomodoroStop ;
/// No description provided for @pomodoroPaused.
///
/// In en, this message translates to:
/// **'Paused'**
String get pomodoroPaused ;
/// No description provided for @pomodoroPausedProfile.
///
/// In en, this message translates to:
/// **'Paused · {profile}'**
String pomodoroPausedProfile ( String profile ) ;
/// No description provided for @pomodoroClassic.
///
/// In en, this message translates to:
/// **'Classic'**
String get pomodoroClassic ;
/// No description provided for @pomodoroUnnamed.
///
/// In en, this message translates to:
/// **'Unnamed profile'**
String get pomodoroUnnamed ;
/// No description provided for @pomodoroProfiles.
///
/// In en, this message translates to:
/// **'Pomodoro profiles'**
String get pomodoroProfiles ;
/// No description provided for @pomodoroChooseProfile.
///
/// In en, this message translates to:
/// **'Choose a profile'**
String get pomodoroChooseProfile ;
/// No description provided for @pomodoroAddProfile.
///
/// In en, this message translates to:
/// **'Add a profile'**
String get pomodoroAddProfile ;
/// No description provided for @pomodoroEditProfile.
///
/// In en, this message translates to:
/// **'Edit profile'**
String get pomodoroEditProfile ;
/// No description provided for @pomodoroName.
///
/// In en, this message translates to:
/// **'Name'**
String get pomodoroName ;
/// No description provided for @pomodoroFocusLength.
///
/// In en, this message translates to:
/// **'Focus'**
String get pomodoroFocusLength ;
/// No description provided for @pomodoroShortBreakLength.
///
/// In en, this message translates to:
/// **'Short break'**
String get pomodoroShortBreakLength ;
/// No description provided for @pomodoroLongBreakLength.
///
/// In en, this message translates to:
/// **'Long break'**
String get pomodoroLongBreakLength ;
/// No description provided for @pomodoroMinutes.
///
/// In en, this message translates to:
/// **'{count} min'**
String pomodoroMinutes ( int count ) ;
/// No description provided for @pomodoroNoLongBreak.
///
/// In en, this message translates to:
/// **'None'**
String get pomodoroNoLongBreak ;
/// No description provided for @pomodoroLongBreakAfter.
///
/// In en, this message translates to:
/// **'Focus phases before a long break'**
String get pomodoroLongBreakAfter ;
/// No description provided for @pomodoroAutoStartBreaks.
///
/// In en, this message translates to:
/// **'Start breaks by themselves'**
String get pomodoroAutoStartBreaks ;
/// No description provided for @pomodoroAutoStartFocus.
///
/// In en, this message translates to:
/// **'Start focus by itself after a break'**
String get pomodoroAutoStartFocus ;
/// No description provided for @pomodoroDoNotDisturb.
///
/// In en, this message translates to:
/// **'Do Not Disturb while focusing'**
String get pomodoroDoNotDisturb ;
/// No description provided for @pomodoroDoNotDisturbHint.
///
/// In en, this message translates to:
/// **'The device keeps quiet while you focus; the alert at its end still sounds.'**
String get pomodoroDoNotDisturbHint ;
/// No description provided for @pomodoroDoNotDisturbDenied.
///
/// In en, this message translates to:
/// **'The app may not keep this device quiet. Allow it in the system\'s settings.'**
String get pomodoroDoNotDisturbDenied ;
/// No description provided for @pomodoroDeleteProfile.
///
/// In en, this message translates to:
/// **'Delete profile'**
String get pomodoroDeleteProfile ;
/// No description provided for @pomodoroSummary.
///
/// In en, this message translates to:
/// **'{focus} min focus, {shortBreak} min break'**
String pomodoroSummary ( int focus , int shortBreak ) ;
/// No description provided for @pomodoroSummaryLong.
///
/// In en, this message translates to:
/// **'{focus} min focus, {shortBreak} min break, {longBreak} min after {after}'**
String pomodoroSummaryLong (
int focus ,
int shortBreak ,
int longBreak ,
int after ,
) ;
/// No description provided for @pomodoroAlertFocusDone.
///
/// In en, this message translates to:
/// **'Time for a break'**
String get pomodoroAlertFocusDone ;
/// No description provided for @pomodoroAlertBreakDone.
///
/// In en, this message translates to:
/// **'Back to focus'**
String get pomodoroAlertBreakDone ;
/// No description provided for @pomodoroAlertStarts.
///
/// In en, this message translates to:
/// **'{phase} has started.'**
String pomodoroAlertStarts ( String phase ) ;
/// No description provided for @pomodoroAlertWaits.
///
/// In en, this message translates to:
/// **'{phase} is ready when you are.'**
String pomodoroAlertWaits ( String phase ) ;
/// No description provided for @homeWidgetPomodoro.
///
/// In en, this message translates to:
/// **'Pomodoro timer'**
String get homeWidgetPomodoro ;
/// No description provided for @pomodoroAlerts.
///
/// In en, this message translates to:
/// **'Pomodoro alerts'**
String get pomodoroAlerts ;
/// No description provided for @pomodoroUntil.
///
/// In en, this message translates to:
/// **'Until {time}'**
String pomodoroUntil ( String time ) ;
/// No description provided for @pomodoroLeft.
///
/// In en, this message translates to:
/// **'{left} left'**
String pomodoroLeft ( String left ) ;
2026-10-07 03:01:12 +02:00
/// No description provided for @pathImportAtStep.
///
/// In en, this message translates to:
/// **'Step {step}: {problem}'**
String pathImportAtStep ( int step , String problem ) ;
/// No description provided for @pathImportNotJson.
///
/// In en, this message translates to:
/// **'This is no JSON: {detail}'**
String pathImportNotJson ( String detail ) ;
/// No description provided for @pathImportNoPath.
///
/// In en, this message translates to:
/// **'a \"path\" object is missing.'**
String get pathImportNoPath ;
/// No description provided for @pathImportNoTitle.
///
/// In en, this message translates to:
/// **'the path has no \"title\".'**
String get pathImportNoTitle ;
/// No description provided for @pathImportDescriptionNotText.
///
/// In en, this message translates to:
/// **'the path\'s \"description\" is no text.'**
String get pathImportDescriptionNotText ;
/// No description provided for @pathImportNotAnObject.
///
/// In en, this message translates to:
/// **'no object.'**
String get pathImportNotAnObject ;
/// No description provided for @pathImportUnknownStepType.
///
/// In en, this message translates to:
/// **'unknown \"type\": {detail}.'**
String pathImportUnknownStepType ( String detail ) ;
/// No description provided for @pathImportRightOptionMissing.
///
/// In en, this message translates to:
/// **'its right option {detail} is not among its options.'**
String pathImportRightOptionMissing ( String detail ) ;
/// No description provided for @pathImportRightOptionsMissing.
///
/// In en, this message translates to:
/// **'it names a right option that is not among its options.'**
String get pathImportRightOptionsMissing ;
/// No description provided for @pathImportNoPairing.
///
/// In en, this message translates to:
/// **'no \"correct_pairing\".'**
String get pathImportNoPairing ;
/// No description provided for @pathImportPairingTargetMissing.
///
/// In en, this message translates to:
/// **'it pairs items with a target that isn\'t there: {detail}.'**
String pathImportPairingTargetMissing ( String detail ) ;
/// No description provided for @pathImportPairingIncomplete.
///
/// In en, this message translates to:
/// **'every item must be paired with one target.'**
String get pathImportPairingIncomplete ;
/// No description provided for @pathImportNoPassage.
///
/// In en, this message translates to:
/// **'no \"passage\".'**
String get pathImportNoPassage ;
/// No description provided for @pathImportBlanksMismatch.
///
/// In en, this message translates to:
/// **'it needs one word in \"blanks\" for each ___ in its passage.'**
String get pathImportBlanksMismatch ;
/// No description provided for @pathImportBlockWithoutType.
///
/// In en, this message translates to:
/// **'a block without \"type\".'**
String get pathImportBlockWithoutType ;
/// No description provided for @pathImportMalformedLevel.
///
/// In en, this message translates to:
/// **'a heading with a malformed level.'**
String get pathImportMalformedLevel ;
/// No description provided for @pathImportMalformedListItem.
///
/// In en, this message translates to:
/// **'a malformed list item.'**
String get pathImportMalformedListItem ;
/// No description provided for @pathImportMalformedMath.
///
/// In en, this message translates to:
/// **'a malformed math block.'**
String get pathImportMalformedMath ;
/// No description provided for @pathImportMalformedCode.
///
/// In en, this message translates to:
/// **'a malformed code block.'**
String get pathImportMalformedCode ;
/// No description provided for @pathImportUnknownBlock.
///
/// In en, this message translates to:
/// **'an unknown block: {detail}.'**
String pathImportUnknownBlock ( String detail ) ;
/// No description provided for @pathImportTextlessBlock.
///
/// In en, this message translates to:
/// **'a {detail} without \"text\" or \"runs\".'**
String pathImportTextlessBlock ( String detail ) ;
/// No description provided for @pathImportMalformedRun.
///
/// In en, this message translates to:
/// **'a malformed run of text.'**
String get pathImportMalformedRun ;
/// No description provided for @pathImportListExpected.
///
/// In en, this message translates to:
/// **'a list was expected.'**
String get pathImportListExpected ;
/// No description provided for @pathImportNotText.
///
/// In en, this message translates to:
/// **'\"{detail}\" must be text.'**
String pathImportNotText ( String detail ) ;
/// No description provided for @pathImportTooFew.
///
/// In en, this message translates to:
/// **'it needs at least two \"{detail}\".'**
String pathImportTooFew ( String detail ) ;
/// No description provided for @pathImportMalformedNumber.
///
/// In en, this message translates to:
/// **'a malformed number.'**
String get pathImportMalformedNumber ;
/// No description provided for @pathImportTitleNotText.
///
/// In en, this message translates to:
/// **'a title must be text.'**
String get pathImportTitleNotText ;
2026-10-07 10:27:31 +02:00
feat(app): cite sources in APA, MLA or Chicago, and give studysets a bibliography
A source is now shared by learning paths and studysets (core/citation/):
authors as full names, year, title, what it is a part of, publisher,
ISBN, link, and a citation kept as a file brought it. formatCitation
cites it in APA 7, MLA 9 or Chicago 18: it inverts and abbreviates
names itself (particles, suffixes, names written as already cited stay),
sets a work of its own in italics and a part of something apart from
the whole, and writes a missing year as "n.d." in APA and Chicago.
- Settings: a Citations card per device, with a preview and a link to
the style's own guide.
- Studysets: a Bibliography button opens the works cited, to read and,
behind its pencil, to change (studyset schema 4).
- Steps: sources edited through the same sheet, opened from an
"N sources" button in the runner (step schema 2; schema 1 reads on).
- Path import: steps' sources, text kept as written, the old format's
"citation" read as what a source is part of.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-07 17:07:04 +02:00
/// No description provided for @pathImportMalformedSource.
///
/// In en, this message translates to:
/// **'a source must be an object or text, and its parts text.'**
String get pathImportMalformedSource ;
feat(paths): path files carry tables, callouts and furigana, and several import at once
The import format gains the blocks lessons need: tables (rows of cells),
callouts (a kind, a title, blocks inside, but no table or callout),
spoilers, quotes and dividers; and runs of text gain a reading shown as
furigana, a colour, underline and strikethrough. A line break is a "\n"
in the text. Still simple blocks turned into rich text here, so a change
of the editor's own format never breaks a file written before. A table's
columns are as wide as their longest line, narrowed alike to fit a
phone, where the cells wrap.
A studyset's Import › Learning paths now takes several files at once,
one path each, stored in the order of their names, so a chapter's
lessons come in with one pick. Every file is read before anything is
stored: one that can't be read is named, and nothing comes in.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MMm7usAo6q9ZZRoPkWjoxi
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-11 19:31:39 +02:00
/// No description provided for @pathImportMalformedTable.
///
/// In en, this message translates to:
/// **'a table needs \"rows\" of cells, each as long as the first.'**
String get pathImportMalformedTable ;
/// No description provided for @pathImportMalformedCallout.
///
/// In en, this message translates to:
/// **'a callout with a malformed {detail}.'**
String pathImportMalformedCallout ( String detail ) ;
/// No description provided for @pathImportNotInCallout.
///
/// In en, this message translates to:
/// **'a {detail} can\'t be inside a callout.'**
String pathImportNotInCallout ( String detail ) ;
2026-10-07 10:27:31 +02:00
/// No description provided for @pomodoroTopBarHint.
///
/// In en, this message translates to:
/// **'The timer can count down in GNOME\'s top bar, with its buttons, once the AppIndicator extension is on.'**
String get pomodoroTopBarHint ;
/// No description provided for @pomodoroTopBarSetUp.
///
/// In en, this message translates to:
/// **'Set up'**
String get pomodoroTopBarSetUp ;
/// No description provided for @pomodoroTopBarDismiss.
///
/// In en, this message translates to:
/// **'Don\'t suggest again'**
String get pomodoroTopBarDismiss ;
feat(app): a card browser to search, filter, sort and change many cards at once
Browse beside the flashcards' tab opens the studyset's cards over the
tabs (with everything inside it when it has sub-studysets), laid out as
the older design had it:
- a grid of tiles (each card's two sides) or one card at a time beside
the list (on a phone above a strip of them, swiped through), as last
left for the studyset on this device;
- a search over front, back, reading, meanings, hints, explanation and
tag names, katakana found as hiragana and kana by romaji;
- sorting by own order, newest, oldest, A–Z (words by reading), due
first; a filter sheet by kind, tag and state (new, learning, due,
buried, reported);
- choosing cards (a toggle; shift-click or long-press for a range,
select all) to move into another or a new studyset, add or remove
tags, start afresh, or delete with Undo; the actions in the row on
wide windows, in a bar at the foot below.
Moving cards between studysets is new in the card repository and its
gateway (studysetId and position written, named columns).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MMm7usAo6q9ZZRoPkWjoxi
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-09 14:21:01 +02:00
/// No description provided for @cardBrowserTitle.
///
/// In en, this message translates to:
/// **'Flashcards'**
String get cardBrowserTitle ;
/// No description provided for @cardBrowserTitleWithName.
///
/// In en, this message translates to:
/// **'{title} — flashcards'**
String cardBrowserTitleWithName ( String title ) ;
2026-10-12 20:57:13 +02:00
/// No description provided for @cardBrowserWithSubstudysets.
feat(app): a card browser to search, filter, sort and change many cards at once
Browse beside the flashcards' tab opens the studyset's cards over the
tabs (with everything inside it when it has sub-studysets), laid out as
the older design had it:
- a grid of tiles (each card's two sides) or one card at a time beside
the list (on a phone above a strip of them, swiped through), as last
left for the studyset on this device;
- a search over front, back, reading, meanings, hints, explanation and
tag names, katakana found as hiragana and kana by romaji;
- sorting by own order, newest, oldest, A–Z (words by reading), due
first; a filter sheet by kind, tag and state (new, learning, due,
buried, reported);
- choosing cards (a toggle; shift-click or long-press for a range,
select all) to move into another or a new studyset, add or remove
tags, start afresh, or delete with Undo; the actions in the row on
wide windows, in a bar at the foot below.
Moving cards between studysets is new in the card repository and its
gateway (studysetId and position written, named columns).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MMm7usAo6q9ZZRoPkWjoxi
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-09 14:21:01 +02:00
///
/// In en, this message translates to:
2026-10-12 20:57:13 +02:00
/// **'With its sub-studysets'**
String get cardBrowserWithSubstudysets ;
feat(app): a card browser to search, filter, sort and change many cards at once
Browse beside the flashcards' tab opens the studyset's cards over the
tabs (with everything inside it when it has sub-studysets), laid out as
the older design had it:
- a grid of tiles (each card's two sides) or one card at a time beside
the list (on a phone above a strip of them, swiped through), as last
left for the studyset on this device;
- a search over front, back, reading, meanings, hints, explanation and
tag names, katakana found as hiragana and kana by romaji;
- sorting by own order, newest, oldest, A–Z (words by reading), due
first; a filter sheet by kind, tag and state (new, learning, due,
buried, reported);
- choosing cards (a toggle; shift-click or long-press for a range,
select all) to move into another or a new studyset, add or remove
tags, start afresh, or delete with Undo; the actions in the row on
wide windows, in a bar at the foot below.
Moving cards between studysets is new in the card repository and its
gateway (studysetId and position written, named columns).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MMm7usAo6q9ZZRoPkWjoxi
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-09 14:21:01 +02:00
/// No description provided for @cardBrowserBrowse.
///
/// In en, this message translates to:
/// **'Browse'**
String get cardBrowserBrowse ;
/// No description provided for @cardBrowserBrowseTooltip.
///
/// In en, this message translates to:
/// **'Browse flashcards'**
String get cardBrowserBrowseTooltip ;
/// No description provided for @cardBrowserStudy.
///
/// In en, this message translates to:
/// **'Study'**
String get cardBrowserStudy ;
/// No description provided for @cardBrowserEditor.
///
/// In en, this message translates to:
/// **'Editor'**
String get cardBrowserEditor ;
/// No description provided for @cardBrowserMore.
///
/// In en, this message translates to:
/// **'More'**
String get cardBrowserMore ;
/// No description provided for @cardBrowserImport.
///
/// In en, this message translates to:
/// **'Import flashcards'**
String get cardBrowserImport ;
/// No description provided for @cardBrowserGrid.
///
/// In en, this message translates to:
/// **'Grid'**
String get cardBrowserGrid ;
/// No description provided for @cardBrowserDetail.
///
/// In en, this message translates to:
/// **'Detail'**
String get cardBrowserDetail ;
/// No description provided for @cardBrowserSearch.
///
/// In en, this message translates to:
/// **'Search'**
String get cardBrowserSearch ;
/// No description provided for @cardBrowserSort.
///
/// In en, this message translates to:
/// **'Sort'**
String get cardBrowserSort ;
/// No description provided for @cardBrowserFilter.
///
/// In en, this message translates to:
/// **'Filter'**
String get cardBrowserFilter ;
/// No description provided for @cardBrowserSelect.
///
/// In en, this message translates to:
/// **'Select'**
String get cardBrowserSelect ;
/// No description provided for @cardBrowserSortManual.
///
/// In en, this message translates to:
/// **'Manual order'**
String get cardBrowserSortManual ;
/// No description provided for @cardBrowserSortNewest.
///
/// In en, this message translates to:
/// **'Newest first'**
String get cardBrowserSortNewest ;
/// No description provided for @cardBrowserSortOldest.
///
/// In en, this message translates to:
/// **'Oldest first'**
String get cardBrowserSortOldest ;
/// No description provided for @cardBrowserSortAlphabetical.
///
/// In en, this message translates to:
/// **'A– Z (front)'**
String get cardBrowserSortAlphabetical ;
/// No description provided for @cardBrowserSortDue.
///
/// In en, this message translates to:
/// **'Due first'**
String get cardBrowserSortDue ;
/// No description provided for @cardBrowserSearchHint.
///
/// In en, this message translates to:
/// **'Search flashcards'**
String get cardBrowserSearchHint ;
/// No description provided for @cardBrowserCloseSearch.
///
/// In en, this message translates to:
/// **'Close search'**
String get cardBrowserCloseSearch ;
/// No description provided for @cardBrowserClearQuery.
///
/// In en, this message translates to:
/// **'Clear query'**
String get cardBrowserClearQuery ;
/// No description provided for @cardBrowserFilterTitle.
///
/// In en, this message translates to:
/// **'Filter flashcards'**
String get cardBrowserFilterTitle ;
/// No description provided for @cardBrowserFilterType.
///
/// In en, this message translates to:
/// **'Type'**
String get cardBrowserFilterType ;
/// No description provided for @cardBrowserTypeAll.
///
/// In en, this message translates to:
/// **'All cards'**
String get cardBrowserTypeAll ;
/// No description provided for @cardBrowserTypeFrontBack.
///
/// In en, this message translates to:
/// **'Q&A only'**
String get cardBrowserTypeFrontBack ;
/// No description provided for @cardBrowserTypeMultipleChoice.
///
/// In en, this message translates to:
/// **'Multiple-choice only'**
String get cardBrowserTypeMultipleChoice ;
/// No description provided for @cardBrowserTypeVocabulary.
///
/// In en, this message translates to:
/// **'Words only'**
String get cardBrowserTypeVocabulary ;
/// No description provided for @cardBrowserTypeMatching.
///
/// In en, this message translates to:
/// **'Matching only'**
String get cardBrowserTypeMatching ;
/// No description provided for @cardBrowserTypeMap.
///
/// In en, this message translates to:
/// **'Maps only'**
String get cardBrowserTypeMap ;
2026-10-09 15:00:21 +02:00
/// No description provided for @cardBrowserTypeCharacter.
///
/// In en, this message translates to:
/// **'Characters only'**
String get cardBrowserTypeCharacter ;
feat(app): a card browser to search, filter, sort and change many cards at once
Browse beside the flashcards' tab opens the studyset's cards over the
tabs (with everything inside it when it has sub-studysets), laid out as
the older design had it:
- a grid of tiles (each card's two sides) or one card at a time beside
the list (on a phone above a strip of them, swiped through), as last
left for the studyset on this device;
- a search over front, back, reading, meanings, hints, explanation and
tag names, katakana found as hiragana and kana by romaji;
- sorting by own order, newest, oldest, A–Z (words by reading), due
first; a filter sheet by kind, tag and state (new, learning, due,
buried, reported);
- choosing cards (a toggle; shift-click or long-press for a range,
select all) to move into another or a new studyset, add or remove
tags, start afresh, or delete with Undo; the actions in the row on
wide windows, in a bar at the foot below.
Moving cards between studysets is new in the card repository and its
gateway (studysetId and position written, named columns).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MMm7usAo6q9ZZRoPkWjoxi
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-09 14:21:01 +02:00
/// No description provided for @cardBrowserFilterTag.
///
/// In en, this message translates to:
/// **'Tag'**
String get cardBrowserFilterTag ;
/// No description provided for @cardBrowserAllTags.
///
/// In en, this message translates to:
/// **'All tags'**
String get cardBrowserAllTags ;
/// No description provided for @cardBrowserFilterStatus.
///
/// In en, this message translates to:
/// **'Status'**
String get cardBrowserFilterStatus ;
/// No description provided for @cardBrowserStatusNew.
///
/// In en, this message translates to:
/// **'New'**
String get cardBrowserStatusNew ;
/// No description provided for @cardBrowserStatusLearning.
///
/// In en, this message translates to:
/// **'Learning'**
String get cardBrowserStatusLearning ;
/// No description provided for @cardBrowserStatusDue.
///
/// In en, this message translates to:
/// **'Due'**
String get cardBrowserStatusDue ;
/// No description provided for @cardBrowserStatusBuried.
///
/// In en, this message translates to:
/// **'Buried'**
String get cardBrowserStatusBuried ;
/// No description provided for @cardBrowserStatusReported.
///
/// In en, this message translates to:
/// **'Reported'**
String get cardBrowserStatusReported ;
/// No description provided for @cardBrowserClearAll.
///
/// In en, this message translates to:
/// **'Clear all'**
String get cardBrowserClearAll ;
/// No description provided for @cardBrowserApply.
///
/// In en, this message translates to:
/// **'Apply'**
String get cardBrowserApply ;
/// No description provided for @cardBrowserEmptyTitle.
///
/// In en, this message translates to:
/// **'No flashcards yet'**
String get cardBrowserEmptyTitle ;
/// No description provided for @cardBrowserEmptyHint.
///
/// In en, this message translates to:
/// **'Create some to browse them here.'**
String get cardBrowserEmptyHint ;
/// No description provided for @cardBrowserCreate.
///
/// In en, this message translates to:
/// **'Create flashcards'**
String get cardBrowserCreate ;
/// No description provided for @cardBrowserNoMatches.
///
/// In en, this message translates to:
/// **'No flashcards match'**
String get cardBrowserNoMatches ;
/// No description provided for @cardBrowserNoMatchesHint.
///
/// In en, this message translates to:
/// **'Try a different search or filter.'**
String get cardBrowserNoMatchesHint ;
/// No description provided for @cardBrowserClearFilters.
///
/// In en, this message translates to:
/// **'Clear search & filters'**
String get cardBrowserClearFilters ;
/// No description provided for @cardBrowserRetry.
///
/// In en, this message translates to:
/// **'Retry'**
String get cardBrowserRetry ;
/// No description provided for @cardBrowserPrevious.
///
/// In en, this message translates to:
/// **'Previous'**
String get cardBrowserPrevious ;
/// No description provided for @cardBrowserNext.
///
/// In en, this message translates to:
/// **'Next'**
String get cardBrowserNext ;
/// No description provided for @cardBrowserBuried.
///
/// In en, this message translates to:
/// **'Buried'**
String get cardBrowserBuried ;
/// No description provided for @cardBrowserSelectAll.
///
/// In en, this message translates to:
/// **'Select all'**
String get cardBrowserSelectAll ;
/// No description provided for @cardBrowserDeselectAll.
///
/// In en, this message translates to:
/// **'Deselect all'**
String get cardBrowserDeselectAll ;
/// No description provided for @cardBrowserSelected.
///
/// In en, this message translates to:
/// **'{count} selected'**
String cardBrowserSelected ( int count ) ;
/// No description provided for @cardBrowserRangeHintTouch.
///
/// In en, this message translates to:
/// **'Long-press another card to select everything in between.'**
String get cardBrowserRangeHintTouch ;
/// No description provided for @cardBrowserRangeHintPointer.
///
/// In en, this message translates to:
/// **'Shift-click another card to select everything in between.'**
String get cardBrowserRangeHintPointer ;
/// No description provided for @cardBrowserMove.
///
/// In en, this message translates to:
/// **'Move'**
String get cardBrowserMove ;
/// No description provided for @cardBrowserAddTags.
///
/// In en, this message translates to:
/// **'Add tags'**
String get cardBrowserAddTags ;
/// No description provided for @cardBrowserRemoveTags.
///
/// In en, this message translates to:
/// **'Remove tags'**
String get cardBrowserRemoveTags ;
/// No description provided for @cardBrowserResetProgress.
///
/// In en, this message translates to:
/// **'Reset progress'**
String get cardBrowserResetProgress ;
/// No description provided for @cardBrowserDelete.
///
/// In en, this message translates to:
/// **'Delete'**
String get cardBrowserDelete ;
/// No description provided for @cardBrowserMoreActions.
///
/// In en, this message translates to:
/// **'More actions'**
String get cardBrowserMoreActions ;
/// No description provided for @cardBrowserDone.
///
/// In en, this message translates to:
/// **'Done'**
String get cardBrowserDone ;
/// No description provided for @cardBrowserDeleted.
///
/// In en, this message translates to:
/// **'{count, plural, =1{1 flashcard deleted} other{{count} flashcards deleted}}'**
String cardBrowserDeleted ( int count ) ;
/// No description provided for @cardBrowserMoveTitle.
///
/// In en, this message translates to:
/// **'{count, plural, =1{Move 1 flashcard to} other{Move {count} flashcards to}}'**
String cardBrowserMoveTitle ( int count ) ;
/// No description provided for @cardBrowserMoved.
///
/// In en, this message translates to:
/// **'{count, plural, =1{Moved 1 flashcard to “{title}”} other{Moved {count} flashcards to “{title}”}}'**
String cardBrowserMoved ( int count , String title ) ;
/// No description provided for @cardBrowserAddTagsTitle.
///
/// In en, this message translates to:
/// **'{count, plural, =1{Add tags to 1 flashcard} other{Add tags to {count} flashcards}}'**
String cardBrowserAddTagsTitle ( int count ) ;
/// No description provided for @cardBrowserAddTo.
///
/// In en, this message translates to:
/// **'{count, plural, =1{Add to 1 flashcard} other{Add to {count} flashcards}}'**
String cardBrowserAddTo ( int count ) ;
/// No description provided for @cardBrowserRemoveTagsTitle.
///
/// In en, this message translates to:
/// **'{count, plural, =1{Remove tags from 1 flashcard} other{Remove tags from {count} flashcards}}'**
String cardBrowserRemoveTagsTitle ( int count ) ;
/// No description provided for @cardBrowserRemoveFrom.
///
/// In en, this message translates to:
/// **'{count, plural, =1{Remove from 1 flashcard} other{Remove from {count} flashcards}}'**
String cardBrowserRemoveFrom ( int count ) ;
/// No description provided for @cardBrowserNoTags.
///
/// In en, this message translates to:
/// **'No tags yet. Create tags from a flashcard\'s editor, then apply them here.'**
String get cardBrowserNoTags ;
/// No description provided for @cardBrowserNoTagsOnCards.
///
/// In en, this message translates to:
/// **'The flashcards chosen have no tags.'**
String get cardBrowserNoTagsOnCards ;
/// No description provided for @cardBrowserTagsAdded.
///
/// In en, this message translates to:
/// **'{tags, plural, =1{Added 1 tag to {count, plural, =1{1 flashcard} other{{count} flashcards}}} other{Added {tags} tags to {count, plural, =1{1 flashcard} other{{count} flashcards}}}}'**
String cardBrowserTagsAdded ( int tags , int count ) ;
/// No description provided for @cardBrowserTagsRemoved.
///
/// In en, this message translates to:
/// **'{tags, plural, =1{Removed 1 tag from {count, plural, =1{1 flashcard} other{{count} flashcards}}} other{Removed {tags} tags from {count, plural, =1{1 flashcard} other{{count} flashcards}}}}'**
String cardBrowserTagsRemoved ( int tags , int count ) ;
/// No description provided for @cardBrowserResetTitle.
///
/// In en, this message translates to:
/// **'{count, plural, =1{Start 1 flashcard afresh?} other{Start {count} flashcards afresh?}}'**
String cardBrowserResetTitle ( int count ) ;
/// No description provided for @cardBrowserResetMessage.
///
/// In en, this message translates to:
/// **'They count as new again. Their reviews stay in your history and streaks.'**
String get cardBrowserResetMessage ;
/// No description provided for @cardBrowserResetConfirm.
///
/// In en, this message translates to:
/// **'Reset'**
String get cardBrowserResetConfirm ;
/// No description provided for @cardBrowserReset.
///
/// In en, this message translates to:
/// **'{count, plural, =1{1 flashcard starts afresh} other{{count} flashcards start afresh}}'**
String cardBrowserReset ( int count ) ;
feat(publish): releases in git (public tier, stage 2)
A person with an identity and a public profile publishes a studyset with
everything inside it as its next release (ADR 0005, rule 3): the server
reads their synced rows and writes them through the git command as a
commit on main and the annotated tag vN, in a bare repository of the
studyset's own, named by a random public id made at the first publish.
A release is a directory per studyset with a file per card, note, path,
step and PDF (its id, position, tag names and payload as synced, keys
sorted and indented) and the files the content names, without what is
only the author's (a card's report, a PDF's deadline). Publishing is
refused without a licence allowing passing it on in every studyset ("no
derivatives" allowed), with revisions to settle, with files not uploaded
yet, when nothing changed, or for a name that won't do; the name is
fixed from the first release, as is the person's handle.
In the app, Share in a studyset's action row opens the Publish sheet;
publishing syncs first, and settles by itself the revisions that merge
cleanly, listing only real conflicts. The server's image gets git, the
repositories a volume of their own, and the hourly backup a git bundle of
each.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MMm7usAo6q9ZZRoPkWjoxi
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-09 22:01:00 +02:00
/// No description provided for @studysetShare.
///
/// In en, this message translates to:
/// **'Share'**
String get studysetShare ;
/// No description provided for @studysetSharePrivate.
///
/// In en, this message translates to:
/// **'Share: private'**
String get studysetSharePrivate ;
/// No description provided for @studysetSharePublished.
///
/// In en, this message translates to:
/// **'Share: published'**
String get studysetSharePublished ;
/// No description provided for @publishTitle.
///
/// In en, this message translates to:
/// **'Publish'**
String get publishTitle ;
/// No description provided for @publishPrivate.
///
/// In en, this message translates to:
/// **'Private · only you can see it'**
String get publishPrivate ;
/// No description provided for @publishPublished.
///
/// In en, this message translates to:
/// **'Published · v{version} · {date}'**
String publishPublished ( int version , String date ) ;
/// No description provided for @publishIntro.
///
/// In en, this message translates to:
/// **'A release is this studyset with everything inside it, as it is now. Others can find it, learn from it and copy it. Each release stays as it was; later changes go into the next one.'**
String get publishIntro ;
/// No description provided for @publishAnonymous.
///
/// In en, this message translates to:
feat(auth): sign in with an email address, keeping only a keyed hash of it
People without GitHub can now keep an anonymous account, and sign in on
another device, with an email address and a code mailed to it each time;
no password. The instance keeps only HMAC-SHA256 of the address, keyed
with its emailSignInPepper (ADR 0010): from what someone types it finds
the person and tells whether an address is free, it mails only an address
someone has just typed, and it can't write to anybody otherwise or show
anybody their address. The device remembers the address it signed in
with, in the person's folder, to show it.
It works as GitHub does. A code for a new address makes it the signed-in
person's, who becomes identified and keeps everything. A code for another
person's address gives a switch token, which signs in as them without a
session; a device holding studysets asks first. Asking for a code answers
alike for every address. Codes are eight Crockford characters, valid 15
minutes and three tries (Serverpod's code challenges); three codes an
hour per address, ten per network. In development the code goes to the
server's log; elsewhere there is no email sign-in until a mail service
is set up (instance.info().emailSignIn).
Someone identified may add the other way in: the Profile tab lists how
they sign in and adds GitHub or an address. They are never switched to
another account: an address or GitHub account that is another's is said
to be so. Switching now deletes the account left behind only when it was
anonymous; it deleted any, which only anonymous people could reach so far.
The migration adds email_identity and email_code_request with their
indexes and foreign keys to Serverpod's challenges; nothing else changes.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-15 19:42:21 +02:00
/// **'Sign in to have a public profile and publish.'**
feat(publish): releases in git (public tier, stage 2)
A person with an identity and a public profile publishes a studyset with
everything inside it as its next release (ADR 0005, rule 3): the server
reads their synced rows and writes them through the git command as a
commit on main and the annotated tag vN, in a bare repository of the
studyset's own, named by a random public id made at the first publish.
A release is a directory per studyset with a file per card, note, path,
step and PDF (its id, position, tag names and payload as synced, keys
sorted and indented) and the files the content names, without what is
only the author's (a card's report, a PDF's deadline). Publishing is
refused without a licence allowing passing it on in every studyset ("no
derivatives" allowed), with revisions to settle, with files not uploaded
yet, when nothing changed, or for a name that won't do; the name is
fixed from the first release, as is the person's handle.
In the app, Share in a studyset's action row opens the Publish sheet;
publishing syncs first, and settles by itself the revisions that merge
cleanly, listing only real conflicts. The server's image gets git, the
repositories a volume of their own, and the hourly backup a git bundle of
each.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MMm7usAo6q9ZZRoPkWjoxi
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-09 22:01:00 +02:00
String get publishAnonymous ;
/// No description provided for @publishName.
///
/// In en, this message translates to:
/// **'Name'**
String get publishName ;
/// No description provided for @publishNameHint.
///
/// In en, this message translates to:
/// **'japanese'**
String get publishNameHint ;
/// No description provided for @publishLocation.
///
/// In en, this message translates to:
/// **'Found at {location}'**
String publishLocation ( String location ) ;
/// No description provided for @publishNameFixed.
///
/// In en, this message translates to:
/// **'Fixed since you first published: others refer to it.'**
String get publishNameFixed ;
/// No description provided for @publishLicence.
///
/// In en, this message translates to:
/// **'Licence'**
String get publishLicence ;
/// No description provided for @publishLicenceNone.
///
/// In en, this message translates to:
/// **'None'**
String get publishLicenceNone ;
/// No description provided for @publishLicenceOpen.
///
/// In en, this message translates to:
/// **'Allows publishing.'**
String get publishLicenceOpen ;
/// No description provided for @publishLicenceNoDerivatives.
///
/// In en, this message translates to:
/// **'Allows publishing as it is: learners keep their changes to themselves.'**
String get publishLicenceNoDerivatives ;
/// No description provided for @publishLicenceClosed.
///
/// In en, this message translates to:
/// **'Doesn\'t allow passing it on. Choose another under Edit.'**
String get publishLicenceClosed ;
/// No description provided for @publishLicenceNotOpen.
///
/// In en, this message translates to:
/// **'These studysets\' licences don\'t allow passing them on: {titles}'**
String publishLicenceNotOpen ( String titles ) ;
/// No description provided for @publishNotes.
///
/// In en, this message translates to:
/// **'What changed'**
String get publishNotes ;
/// No description provided for @publishNotesHint.
///
/// In en, this message translates to:
/// **'New in this release…'**
String get publishNotesHint ;
feat(explore): cards as the library's, in its colour, with a description of their own
Explore lists its studysets in a grid as the library does: each card in
the studyset's colour along the top, its title under its secondary
title, the start of its description and what it holds, then, quieter,
its author's handle, the release and the licence, and a compact Add.
A tap opens the summary, which shows the colour too.
The Publish sheet has a Description on Explore, starting from the
latest release's, else the studyset's own, and sent with the release:
a studyset may introduce itself to strangers while its own description
stays empty in the library.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-14 17:47:29 +02:00
/// No description provided for @publishDescription.
///
/// In en, this message translates to:
/// **'Description on Explore'**
String get publishDescription ;
/// No description provided for @publishDescriptionAdd.
///
/// In en, this message translates to:
/// **'Add a description'**
String get publishDescriptionAdd ;
/// No description provided for @publishDescriptionHelp.
///
/// In en, this message translates to:
/// **'What people see on Explore before adding it. It starts as the studyset’ s own; the studyset keeps its own as it is.'**
String get publishDescriptionHelp ;
feat(publish): releases in git (public tier, stage 2)
A person with an identity and a public profile publishes a studyset with
everything inside it as its next release (ADR 0005, rule 3): the server
reads their synced rows and writes them through the git command as a
commit on main and the annotated tag vN, in a bare repository of the
studyset's own, named by a random public id made at the first publish.
A release is a directory per studyset with a file per card, note, path,
step and PDF (its id, position, tag names and payload as synced, keys
sorted and indented) and the files the content names, without what is
only the author's (a card's report, a PDF's deadline). Publishing is
refused without a licence allowing passing it on in every studyset ("no
derivatives" allowed), with revisions to settle, with files not uploaded
yet, when nothing changed, or for a name that won't do; the name is
fixed from the first release, as is the person's handle.
In the app, Share in a studyset's action row opens the Publish sheet;
publishing syncs first, and settles by itself the revisions that merge
cleanly, listing only real conflicts. The server's image gets git, the
repositories a volume of their own, and the hourly backup a git bundle of
each.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MMm7usAo6q9ZZRoPkWjoxi
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-09 22:01:00 +02:00
/// No description provided for @publishReleases.
///
/// In en, this message translates to:
/// **'Releases'**
String get publishReleases ;
/// No description provided for @publishRelease.
///
/// In en, this message translates to:
/// **'v{version} · {date}'**
String publishRelease ( int version , String date ) ;
/// No description provided for @publishButton.
///
/// In en, this message translates to:
/// **'Publish v{version}'**
String publishButton ( int version ) ;
/// No description provided for @publishNoProfile.
///
/// In en, this message translates to:
/// **'A release names you by your public profile. Make one first.'**
String get publishNoProfile ;
/// No description provided for @publishMakeProfile.
///
/// In en, this message translates to:
/// **'Make a public profile'**
String get publishMakeProfile ;
/// No description provided for @publishConflicts.
///
/// In en, this message translates to:
/// **'These were changed differently on two devices. Settle them first:'**
String get publishConflicts ;
/// No description provided for @publishConflictsElsewhere.
///
/// In en, this message translates to:
/// **'Some cards, notes or steps were changed differently on two devices. Sync, then try again.'**
String get publishConflictsElsewhere ;
/// No description provided for @publishUntitled.
///
/// In en, this message translates to:
/// **'Untitled'**
String get publishUntitled ;
/// No description provided for @publishFilesMissing.
///
/// In en, this message translates to:
/// **'Some files haven\'t reached your instance yet. Try again in a moment.'**
String get publishFilesMissing ;
/// No description provided for @publishNothingChanged.
///
/// In en, this message translates to:
/// **'Nothing has changed since the last release.'**
String get publishNothingChanged ;
/// No description provided for @publishBusy.
///
/// In en, this message translates to:
/// **'This studyset is being published already. Try again in a moment.'**
String get publishBusy ;
/// No description provided for @publishSlugInvalid.
///
/// In en, this message translates to:
/// **'Lower-case letters, digits and hyphens, starting with a letter or digit.'**
String get publishSlugInvalid ;
/// No description provided for @publishSlugTaken.
///
/// In en, this message translates to:
/// **'Another of your studysets has this name.'**
String get publishSlugTaken ;
/// No description provided for @publishNotesTooLong.
///
/// In en, this message translates to:
/// **'At most 5,000 characters.'**
String get publishNotesTooLong ;
/// No description provided for @publishDone.
///
/// In en, this message translates to:
/// **'Published v{version}.'**
String publishDone ( int version ) ;
/// No description provided for @publishFailed.
///
/// In en, this message translates to:
/// **'Couldn\'t reach your instance. Nothing was published.'**
String get publishFailed ;
/// No description provided for @publishUnreachable.
///
/// In en, this message translates to:
/// **'Publishing can\'t be reached right now.'**
String get publishUnreachable ;
/// No description provided for @publishRetry.
///
/// In en, this message translates to:
/// **'Retry'**
String get publishRetry ;
/// No description provided for @publishCopyLocation.
///
/// In en, this message translates to:
/// **'Copy address'**
String get publishCopyLocation ;
/// No description provided for @publishLocationCopied.
///
/// In en, this message translates to:
/// **'Address copied.'**
String get publishLocationCopied ;
feat(explore): the Explore tab, and adding a published studyset as a copy
Explore lists what is published on the instance as the old app's feed:
search over titles and authors, filters for languages and kinds of
content, the newest release first, the next page only when asked for;
each studyset opens a summary with all of it. Anonymous people explore
too.
Add to library makes a copy in the app, as an import: through the
person's own repositories, the files among their own files, the tags by
name among their own tags, everything read first, so a release in a
schema this app doesn't know is refused whole. The server never writes
into a learner's space. Every item of a copy names where it came from
(publication, item, release: card schema 9, note 3, step 3, path 2,
PDF 2, studyset 5), merged per side like the rest, for later releases to
be taken from there.
The Publish sheet takes a studyset off Explore again, after asking.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MMm7usAo6q9ZZRoPkWjoxi
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-09 23:15:28 +02:00
/// No description provided for @publishedField.
///
/// In en, this message translates to:
/// **'Release'**
String get publishedField ;
/// No description provided for @publishedVersion.
///
/// In en, this message translates to:
/// **'v{version}'**
String publishedVersion ( int version ) ;
/// No description provided for @exploreSearch.
///
/// In en, this message translates to:
/// **'Search titles and authors'**
String get exploreSearch ;
/// No description provided for @exploreLanguages.
///
/// In en, this message translates to:
/// **'Languages'**
String get exploreLanguages ;
/// No description provided for @exploreAnyLanguage.
///
/// In en, this message translates to:
/// **'Any'**
String get exploreAnyLanguage ;
/// No description provided for @exploreLanguagePair.
///
/// In en, this message translates to:
/// **'{front} → {back}'**
String exploreLanguagePair ( String front , String back ) ;
/// No description provided for @exploreKinds.
///
/// In en, this message translates to:
/// **'Content'**
String get exploreKinds ;
/// No description provided for @exploreKindsChosen.
///
/// In en, this message translates to:
/// **'{count, plural, =1{1 kind of content} other{{count} kinds of content}}'**
String exploreKindsChosen ( int count ) ;
/// No description provided for @exploreKindCards.
///
/// In en, this message translates to:
/// **'Flashcards'**
String get exploreKindCards ;
/// No description provided for @exploreKindNotes.
///
/// In en, this message translates to:
/// **'Notes'**
String get exploreKindNotes ;
/// No description provided for @exploreKindPaths.
///
/// In en, this message translates to:
/// **'Learning paths'**
String get exploreKindPaths ;
/// No description provided for @exploreKindPdfs.
///
/// In en, this message translates to:
/// **'PDFs'**
String get exploreKindPdfs ;
/// No description provided for @exploreKindsIntro.
///
/// In en, this message translates to:
/// **'Only studysets holding each of these.'**
String get exploreKindsIntro ;
/// No description provided for @exploreClearFilters.
///
/// In en, this message translates to:
/// **'Clear filters'**
String get exploreClearFilters ;
/// No description provided for @exploreNothingMatches.
///
/// In en, this message translates to:
/// **'Nothing published here matches.'**
String get exploreNothingMatches ;
/// No description provided for @exploreBy.
///
/// In en, this message translates to:
/// **'by {author}'**
String exploreBy ( String author ) ;
/// No description provided for @exploreRelease.
///
/// In en, this message translates to:
/// **'v{version} · {date}'**
String exploreRelease ( int version , String date ) ;
2026-10-14 18:14:04 +02:00
/// No description provided for @exploreMaterial.
fix(explore): the summary in blocks: what it is, what it holds, its release
The summary sheet ran its parts together, each aligned its own way. Now
it groups them in cards, as the Profile tab does its parts: what the
studyset is (its colour along the top, its secondary title, author and
description), what it holds (languages, contents, learners), and its
latest release with its notes and Reports and suggestions; its credits
centred beneath them, as at the foot of a studyset.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-14 18:03:19 +02:00
///
/// In en, this message translates to:
2026-10-14 18:14:04 +02:00
/// **'Material'**
String get exploreMaterial ;
fix(explore): the summary in blocks: what it is, what it holds, its release
The summary sheet ran its parts together, each aligned its own way. Now
it groups them in cards, as the Profile tab does its parts: what the
studyset is (its colour along the top, its secondary title, author and
description), what it holds (languages, contents, learners), and its
latest release with its notes and Reports and suggestions; its credits
centred beneath them, as at the foot of a studyset.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-14 18:03:19 +02:00
feat(explore): the Explore tab, and adding a published studyset as a copy
Explore lists what is published on the instance as the old app's feed:
search over titles and authors, filters for languages and kinds of
content, the newest release first, the next page only when asked for;
each studyset opens a summary with all of it. Anonymous people explore
too.
Add to library makes a copy in the app, as an import: through the
person's own repositories, the files among their own files, the tags by
name among their own tags, everything read first, so a release in a
schema this app doesn't know is refused whole. The server never writes
into a learner's space. Every item of a copy names where it came from
(publication, item, release: card schema 9, note 3, step 3, path 2,
PDF 2, studyset 5), merged per side like the rest, for later releases to
be taken from there.
The Publish sheet takes a studyset off Explore again, after asking.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MMm7usAo6q9ZZRoPkWjoxi
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-09 23:15:28 +02:00
/// No description provided for @exploreLatestRelease.
///
/// In en, this message translates to:
/// **'Latest release: v{version} · {date}'**
String exploreLatestRelease ( int version , String date ) ;
/// No description provided for @exploreAddToLibrary.
///
/// In en, this message translates to:
/// **'Add to library'**
String get exploreAddToLibrary ;
feat(explore): cards as the library's, in its colour, with a description of their own
Explore lists its studysets in a grid as the library does: each card in
the studyset's colour along the top, its title under its secondary
title, the start of its description and what it holds, then, quieter,
its author's handle, the release and the licence, and a compact Add.
A tap opens the summary, which shows the colour too.
The Publish sheet has a Description on Explore, starting from the
latest release's, else the studyset's own, and sent with the release:
a studyset may introduce itself to strangers while its own description
stays empty in the library.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-14 17:47:29 +02:00
/// No description provided for @exploreAdd.
///
/// In en, this message translates to:
/// **'Add'**
String get exploreAdd ;
/// No description provided for @exploreInLibraryShort.
///
/// In en, this message translates to:
/// **'In library'**
String get exploreInLibraryShort ;
feat(explore): the Explore tab, and adding a published studyset as a copy
Explore lists what is published on the instance as the old app's feed:
search over titles and authors, filters for languages and kinds of
content, the newest release first, the next page only when asked for;
each studyset opens a summary with all of it. Anonymous people explore
too.
Add to library makes a copy in the app, as an import: through the
person's own repositories, the files among their own files, the tags by
name among their own tags, everything read first, so a release in a
schema this app doesn't know is refused whole. The server never writes
into a learner's space. Every item of a copy names where it came from
(publication, item, release: card schema 9, note 3, step 3, path 2,
PDF 2, studyset 5), merged per side like the rest, for later releases to
be taken from there.
The Publish sheet takes a studyset off Explore again, after asking.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MMm7usAo6q9ZZRoPkWjoxi
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-09 23:15:28 +02:00
/// No description provided for @exploreInLibrary.
///
/// In en, this message translates to:
/// **'In your library'**
String get exploreInLibrary ;
2026-10-16 22:34:28 +02:00
/// No description provided for @exploreYours.
///
/// In en, this message translates to:
/// **'Yours'**
String get exploreYours ;
/// No description provided for @exploreAddCopyAnyway.
///
/// In en, this message translates to:
/// **'Add a copy anyway'**
String get exploreAddCopyAnyway ;
feat(explore): the Explore tab, and adding a published studyset as a copy
Explore lists what is published on the instance as the old app's feed:
search over titles and authors, filters for languages and kinds of
content, the newest release first, the next page only when asked for;
each studyset opens a summary with all of it. Anonymous people explore
too.
Add to library makes a copy in the app, as an import: through the
person's own repositories, the files among their own files, the tags by
name among their own tags, everything read first, so a release in a
schema this app doesn't know is refused whole. The server never writes
into a learner's space. Every item of a copy names where it came from
(publication, item, release: card schema 9, note 3, step 3, path 2,
PDF 2, studyset 5), merged per side like the rest, for later releases to
be taken from there.
The Publish sheet takes a studyset off Explore again, after asking.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MMm7usAo6q9ZZRoPkWjoxi
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-09 23:15:28 +02:00
/// No description provided for @explorePercent.
///
/// In en, this message translates to:
/// **'{percent} %'**
String explorePercent ( int percent ) ;
/// No description provided for @exploreAdded.
///
/// In en, this message translates to:
/// **'Added to your library.'**
String get exploreAdded ;
/// No description provided for @exploreOpen.
///
/// In en, this message translates to:
/// **'Open'**
String get exploreOpen ;
/// No description provided for @exploreNeedsNewerApp.
///
/// In en, this message translates to:
/// **'This studyset needs a newer version of the app.'**
String get exploreNeedsNewerApp ;
/// No description provided for @exploreFailed.
///
/// In en, this message translates to:
/// **'Couldn\'t reach your instance. Try again.'**
String get exploreFailed ;
2026-10-16 21:27:55 +02:00
/// No description provided for @exploreNotGivenOut.
///
/// In en, this message translates to:
/// **'Your instance couldn\'t give out this studyset. Try again later.'**
String get exploreNotGivenOut ;
/// No description provided for @exploreNotWritten.
///
/// In en, this message translates to:
/// **'Couldn\'t add it to your library on this device.'**
String get exploreNotWritten ;
feat(explore): the Explore tab, and adding a published studyset as a copy
Explore lists what is published on the instance as the old app's feed:
search over titles and authors, filters for languages and kinds of
content, the newest release first, the next page only when asked for;
each studyset opens a summary with all of it. Anonymous people explore
too.
Add to library makes a copy in the app, as an import: through the
person's own repositories, the files among their own files, the tags by
name among their own tags, everything read first, so a release in a
schema this app doesn't know is refused whole. The server never writes
into a learner's space. Every item of a copy names where it came from
(publication, item, release: card schema 9, note 3, step 3, path 2,
PDF 2, studyset 5), merged per side like the rest, for later releases to
be taken from there.
The Publish sheet takes a studyset off Explore again, after asking.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MMm7usAo6q9ZZRoPkWjoxi
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-09 23:15:28 +02:00
/// No description provided for @exploreUnreachable.
///
/// In en, this message translates to:
/// **'Explore can\'t be reached right now.'**
String get exploreUnreachable ;
/// No description provided for @exploreRetry.
///
/// In en, this message translates to:
/// **'Retry'**
String get exploreRetry ;
/// No description provided for @exploreMore.
///
/// In en, this message translates to:
/// **'Show more'**
String get exploreMore ;
/// No description provided for @exploreNoDescription.
///
/// In en, this message translates to:
/// **'No description.'**
String get exploreNoDescription ;
/// No description provided for @exploreReadLicence.
///
/// In en, this message translates to:
/// **'Read the licence'**
String get exploreReadLicence ;
/// No description provided for @publishWithdraw.
///
/// In en, this message translates to:
/// **'Take off Explore'**
String get publishWithdraw ;
/// No description provided for @publishWithdrawTitle.
///
/// In en, this message translates to:
/// **'Take it off Explore?'**
String get publishWithdrawTitle ;
/// No description provided for @publishWithdrawMessage.
///
/// In en, this message translates to:
/// **'Nobody can add it any more, and copies get no updates. Its releases stay as they are; publishing it again brings it back.'**
String get publishWithdrawMessage ;
/// No description provided for @publishWithdrawn.
///
/// In en, this message translates to:
/// **'Withdrawn · copies get no updates'**
String get publishWithdrawn ;
/// No description provided for @publishWithdrawnDone.
///
/// In en, this message translates to:
/// **'Taken off Explore.'**
String get publishWithdrawnDone ;
feat(explore): take a later release into a copy, at a tap
A copy's top studyset says when a newer release of what it came from is
out, and what it would bring: changed, new and withdrawn items, counted
against the release the copy last took. Update takes it, never by
itself (ADR 0005, rule 4): cards, notes and steps take the release as a
revision on the one where they last took a release and merge per side,
as a dictionary's corrections do, so what the learner changed stays and
a side both changed is a conflict to settle; studysets, paths and PDFs
take each field the learner left as it was. New items come in where
they belong, unless the learner deleted that part; what the author
withdrew stays, marked, and the card browser filters for it; what the
learner deleted stays deleted; progress is never touched.
The instance gives out any release by its version, for the app to
compare the two. UpdateTaken moves into the shared revision merge, now
that notes and steps take updates too.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MMm7usAo6q9ZZRoPkWjoxi
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-09 23:47:41 +02:00
/// No description provided for @copyUpdateOut.
///
/// In en, this message translates to:
/// **'v{version} is out: {summary}.'**
String copyUpdateOut ( int version , String summary ) ;
/// No description provided for @copyUpdateChanged.
///
/// In en, this message translates to:
/// **'{count} changed'**
String copyUpdateChanged ( int count ) ;
/// No description provided for @copyUpdateAdded.
///
/// In en, this message translates to:
/// **'{count} new'**
String copyUpdateAdded ( int count ) ;
/// No description provided for @copyUpdateWithdrawn.
///
/// In en, this message translates to:
/// **'{count} withdrawn'**
String copyUpdateWithdrawn ( int count ) ;
/// No description provided for @copyUpdateNothing.
///
/// In en, this message translates to:
/// **'nothing in your copy changes'**
String get copyUpdateNothing ;
/// No description provided for @copyUpdate.
///
/// In en, this message translates to:
/// **'Update'**
String get copyUpdate ;
/// No description provided for @copyUpdated.
///
/// In en, this message translates to:
/// **'Updated to v{version}.'**
String copyUpdated ( int version ) ;
/// No description provided for @copyUpdatedToSettle.
///
/// In en, this message translates to:
/// **'{count, plural, =1{One has a change of yours there too: open it to choose.} other{{count} have a change of yours there too: open them to choose.}}'**
String copyUpdatedToSettle ( int count ) ;
/// No description provided for @copyUpdateFailed.
///
/// In en, this message translates to:
/// **'Couldn\'t reach your instance. Try again.'**
String get copyUpdateFailed ;
/// No description provided for @cardBrowserStatusWithdrawn.
///
/// In en, this message translates to:
/// **'Withdrawn by the author'**
String get cardBrowserStatusWithdrawn ;
feat(explore): how many learn it here, opt-in; copies published within their licence
Explore says how many people learn a published studyset on this instance
(ADR 0005, rule 8): shown, never ranked, counted daily by the server from
the people with an identity who chose to be counted (Settings, Privacy;
settings schema 8) and studied a copy on 3 or more days of the last 30,
rounded down from 5 on, so that nobody can be told apart. Only a number
the instance computes itself counts; votes count for nothing. The author
sees the same rounded number in the Publish sheet.
A copy of a published studyset is published within the original's
licence: never under "no derivatives", under a licence that keeps
adaptations under it only under that one, in every studyset of the copy.
Its releases say what they are based on, and Explore shows it.
ADR 0005 gets an addendum with stage 3's decisions.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MMm7usAo6q9ZZRoPkWjoxi
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-10 00:18:29 +02:00
/// No description provided for @settingsPrivacy.
///
/// In en, this message translates to:
/// **'Privacy'**
String get settingsPrivacy ;
/// No description provided for @settingsCountLearning.
///
/// In en, this message translates to:
/// **'Count my learning in Explore'**
String get settingsCountLearning ;
/// No description provided for @settingsCountLearningSubtitle.
///
/// In en, this message translates to:
/// **'Explore shows how many people on this instance learn a published studyset: those with an identity who chose this and studied it on 3 or more days in the last 30, rounded, from 5 on. Nothing about you is shown, and nothing leaves the instance.'**
String get settingsCountLearningSubtitle ;
/// No description provided for @exploreLearners.
///
/// In en, this message translates to:
/// **'{count}+ learn this here'**
String exploreLearners ( int count ) ;
/// No description provided for @publishLearnersFew.
///
/// In en, this message translates to:
/// **'Fewer than 5 learn it here, of those who chose to be counted.'**
String get publishLearnersFew ;
/// No description provided for @publishForkForbidden.
///
/// In en, this message translates to:
/// **'Its original forbids passing on adaptations: only its author publishes it.'**
String get publishForkForbidden ;
/// No description provided for @publishLicenseMustStay.
///
/// In en, this message translates to:
/// **'Its original asks adaptations to stay under {license}: choose it under Edit, for every studyset in it.'**
String publishLicenseMustStay ( String license ) ;
/// No description provided for @exploreBasedOn.
///
/// In en, this message translates to:
/// **'based on {location}'**
String exploreBasedOn ( String location ) ;
feat(feedback): report, suggest, and an inbox in the Profile tab
The app's side of stage 4 (ADR 0005, rule 11). The card's Report sheet
offers "Send to the author too" on a card from a release; a copy's menu
reports the studyset as a whole and lists what is reported and
suggested on it, as Explore's summary does; the editors of cards, notes
and steps suggest them to the author as the learner has them, never
under "no derivatives". The Profile tab holds the inbox: the threads on
the person's published studysets and those they wrote, the newest
activity first, with a count on the tab. A thread shows a suggestion
beside the author's own item and its replies; its writer and the author
reply, and the author accepts (the content becomes their own edit, its
files fetched from the writer's), declines, closes, hides or blocks.
Writing needs an identity; reading what is public doesn't.
ADR 0005 gets an addendum for stage 4.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MMm7usAo6q9ZZRoPkWjoxi
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-10 01:29:09 +02:00
/// No description provided for @feedbackReport.
///
/// In en, this message translates to:
/// **'Report'**
String get feedbackReport ;
/// No description provided for @feedbackSuggestion.
///
/// In en, this message translates to:
/// **'Suggestion'**
String get feedbackSuggestion ;
/// No description provided for @feedbackOnStudyset.
///
/// In en, this message translates to:
/// **'On the studyset'**
String get feedbackOnStudyset ;
/// No description provided for @feedbackOnCard.
///
/// In en, this message translates to:
/// **'On a flashcard'**
String get feedbackOnCard ;
/// No description provided for @feedbackOnNote.
///
/// In en, this message translates to:
/// **'On a note'**
String get feedbackOnNote ;
/// No description provided for @feedbackOnStep.
///
/// In en, this message translates to:
/// **'On a step'**
String get feedbackOnStep ;
/// No description provided for @feedbackByOn.
///
/// In en, this message translates to:
/// **'{name} · {date}'**
String feedbackByOn ( String name , String date ) ;
/// No description provided for @feedbackStatusClosed.
///
/// In en, this message translates to:
/// **'Closed'**
String get feedbackStatusClosed ;
/// No description provided for @feedbackStatusAccepted.
///
/// In en, this message translates to:
/// **'Accepted'**
String get feedbackStatusAccepted ;
/// No description provided for @feedbackStatusDeclined.
///
/// In en, this message translates to:
/// **'Declined'**
String get feedbackStatusDeclined ;
/// No description provided for @feedbackStatusHidden.
///
/// In en, this message translates to:
/// **'Hidden'**
String get feedbackStatusHidden ;
/// No description provided for @feedbackReplyHint.
///
/// In en, this message translates to:
/// **'Write a reply…'**
String get feedbackReplyHint ;
/// No description provided for @feedbackSend.
///
/// In en, this message translates to:
/// **'Send'**
String get feedbackSend ;
/// No description provided for @feedbackAccept.
///
/// In en, this message translates to:
/// **'Accept'**
String get feedbackAccept ;
/// No description provided for @feedbackDecline.
///
/// In en, this message translates to:
/// **'Decline'**
String get feedbackDecline ;
/// No description provided for @feedbackClose.
///
/// In en, this message translates to:
/// **'Close'**
String get feedbackClose ;
/// No description provided for @feedbackReopen.
///
/// In en, this message translates to:
/// **'Reopen'**
String get feedbackReopen ;
/// No description provided for @feedbackHide.
///
/// In en, this message translates to:
/// **'Hide'**
String get feedbackHide ;
/// No description provided for @feedbackShow.
///
/// In en, this message translates to:
/// **'Show'**
String get feedbackShow ;
/// No description provided for @feedbackBlock.
///
/// In en, this message translates to:
/// **'Block {name}'**
String feedbackBlock ( String name ) ;
/// No description provided for @feedbackBlockTitle.
///
/// In en, this message translates to:
/// **'Block {name}?'**
String feedbackBlockTitle ( String name ) ;
/// No description provided for @feedbackBlockMessage.
///
/// In en, this message translates to:
/// **'What they wrote on your studysets, and write, shows to nobody but them.'**
String get feedbackBlockMessage ;
/// No description provided for @feedbackProposed.
///
/// In en, this message translates to:
/// **'Proposed'**
String get feedbackProposed ;
/// No description provided for @feedbackYours.
///
/// In en, this message translates to:
/// **'Yours now'**
String get feedbackYours ;
/// No description provided for @feedbackAuthor.
///
/// In en, this message translates to:
/// **'Author'**
String get feedbackAuthor ;
/// No description provided for @feedbackAccepted.
///
/// In en, this message translates to:
/// **'Taken into your studyset. The next release credits its writer.'**
String get feedbackAccepted ;
/// No description provided for @feedbackDecided.
///
/// In en, this message translates to:
/// **'Done.'**
String get feedbackDecided ;
/// No description provided for @feedbackReplied.
///
/// In en, this message translates to:
/// **'Reply sent.'**
String get feedbackReplied ;
/// No description provided for @feedbackBlocked.
///
/// In en, this message translates to:
/// **'Blocked.'**
String get feedbackBlocked ;
/// No description provided for @feedbackNotTaken.
///
/// In en, this message translates to:
/// **'This can\'t be taken: what it is about isn\'t in your studyset any more.'**
String get feedbackNotTaken ;
/// No description provided for @feedbackFailed.
///
/// In en, this message translates to:
/// **'Couldn\'t reach your instance. Try again.'**
String get feedbackFailed ;
/// No description provided for @feedbackUnreachable.
///
/// In en, this message translates to:
/// **'Reports and suggestions can\'t be reached right now.'**
String get feedbackUnreachable ;
/// No description provided for @feedbackRetry.
///
/// In en, this message translates to:
/// **'Retry'**
String get feedbackRetry ;
/// No description provided for @inboxTitle.
///
/// In en, this message translates to:
/// **'Inbox'**
String get inboxTitle ;
/// No description provided for @inboxIntro.
///
/// In en, this message translates to:
feat(discussions): questions on published studysets, answered by learners
A learner asks about a published studyset, a topic in it, or one card,
note or step of a release, and others answer, as on Stack Exchange
(ADR 0013): questions and answers in the app's editor without images,
sounds or videos, comments of up to 600 characters under the question
and each answer. There are no votes: the asker accepts an answer, which
comes first, then the oldest; the author's posts are marked. The
instance reads from git's file list which topics a question's target
sits in, so a topic lists what is asked within it and below it.
Posts live in two server-only tables, written through endpoints, so the
synced tables stay as they are and v0.17.0 keeps syncing. Everyone
signed in reads; writing needs an identity and a public profile; the
studyset's author hides posts and blocks people with feedback's blocks;
a withdrawn studyset takes no new questions; anyone writes at most 30
posts an hour. A post with others' replies under it is emptied when
deleted, else deleted; deletePerson deletes what a person wrote the same
way, and a published studyset's deletion takes its discussions with it.
The migration adds Serverpod's gen_random_uuid_v7(), discussion_post
and discussion_seen with their indexes and cascading foreign keys, and
changes nothing else.
In the app, discussions open from a copy's studyset or topic menu, a
studied card's "Ask about this", Explore's summary and the Publish
sheet; the Profile tab's inbox lists those the person follows, and its
count includes what is new in them.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-15 23:52:40 +02:00
/// **'Questions and answers, reports and suggestions on your published studysets, and those you wrote in.'**
feat(feedback): report, suggest, and an inbox in the Profile tab
The app's side of stage 4 (ADR 0005, rule 11). The card's Report sheet
offers "Send to the author too" on a card from a release; a copy's menu
reports the studyset as a whole and lists what is reported and
suggested on it, as Explore's summary does; the editors of cards, notes
and steps suggest them to the author as the learner has them, never
under "no derivatives". The Profile tab holds the inbox: the threads on
the person's published studysets and those they wrote, the newest
activity first, with a count on the tab. A thread shows a suggestion
beside the author's own item and its replies; its writer and the author
reply, and the author accepts (the content becomes their own edit, its
files fetched from the writer's), declines, closes, hides or blocks.
Writing needs an identity; reading what is public doesn't.
ADR 0005 gets an addendum for stage 4.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MMm7usAo6q9ZZRoPkWjoxi
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-10 01:29:09 +02:00
String get inboxIntro ;
/// No description provided for @inboxEmpty.
///
/// In en, this message translates to:
/// **'Nothing yet.'**
String get inboxEmpty ;
/// No description provided for @studysetFeedback.
///
/// In en, this message translates to:
/// **'Reports and suggestions'**
String get studysetFeedback ;
/// No description provided for @studysetFeedbackEmpty.
///
/// In en, this message translates to:
/// **'Nobody has reported or suggested anything yet.'**
String get studysetFeedbackEmpty ;
/// No description provided for @reportToAuthor.
///
/// In en, this message translates to:
/// **'Report to the author'**
String get reportToAuthor ;
feat(discussions): questions on published studysets, answered by learners
A learner asks about a published studyset, a topic in it, or one card,
note or step of a release, and others answer, as on Stack Exchange
(ADR 0013): questions and answers in the app's editor without images,
sounds or videos, comments of up to 600 characters under the question
and each answer. There are no votes: the asker accepts an answer, which
comes first, then the oldest; the author's posts are marked. The
instance reads from git's file list which topics a question's target
sits in, so a topic lists what is asked within it and below it.
Posts live in two server-only tables, written through endpoints, so the
synced tables stay as they are and v0.17.0 keeps syncing. Everyone
signed in reads; writing needs an identity and a public profile; the
studyset's author hides posts and blocks people with feedback's blocks;
a withdrawn studyset takes no new questions; anyone writes at most 30
posts an hour. A post with others' replies under it is emptied when
deleted, else deleted; deletePerson deletes what a person wrote the same
way, and a published studyset's deletion takes its discussions with it.
The migration adds Serverpod's gen_random_uuid_v7(), discussion_post
and discussion_seen with their indexes and cascading foreign keys, and
changes nothing else.
In the app, discussions open from a copy's studyset or topic menu, a
studied card's "Ask about this", Explore's summary and the Publish
sheet; the Profile tab's inbox lists those the person follows, and its
count includes what is new in them.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-15 23:52:40 +02:00
/// No description provided for @discussionsTitle.
///
/// In en, this message translates to:
/// **'Discussions'**
String get discussionsTitle ;
/// No description provided for @discussionsSearch.
///
/// In en, this message translates to:
/// **'Search questions'**
String get discussionsSearch ;
/// No description provided for @discussionsAsk.
///
/// In en, this message translates to:
/// **'Ask a question'**
String get discussionsAsk ;
/// No description provided for @discussionsEmpty.
///
/// In en, this message translates to:
/// **'No questions yet'**
String get discussionsEmpty ;
/// No description provided for @discussionsEmptyHint.
///
/// In en, this message translates to:
/// **'Ask the first: about the studyset, a topic or one of its flashcards.'**
String get discussionsEmptyHint ;
/// No description provided for @discussionsNoMatch.
///
/// In en, this message translates to:
/// **'No question\'s title says that.'**
String get discussionsNoMatch ;
/// No description provided for @discussionsMore.
///
/// In en, this message translates to:
/// **'Show more'**
String get discussionsMore ;
/// No description provided for @discussionsUnreachable.
///
/// In en, this message translates to:
/// **'The discussions can\'t be reached right now.'**
String get discussionsUnreachable ;
/// No description provided for @discussionsAnswers.
///
/// In en, this message translates to:
/// **'{count, plural, =0{No answers} =1{1 answer} other{{count} answers}}'**
String discussionsAnswers ( int count ) ;
/// No description provided for @discussionsBy.
///
/// In en, this message translates to:
/// **'{name} · {date}'**
String discussionsBy ( String name , String date ) ;
/// No description provided for @discussionsSomeone.
///
/// In en, this message translates to:
/// **'Someone'**
String get discussionsSomeone ;
/// No description provided for @discussionsDeletedQuestion.
///
/// In en, this message translates to:
/// **'Deleted question'**
String get discussionsDeletedQuestion ;
/// No description provided for @discussionsHidden.
///
/// In en, this message translates to:
/// **'Hidden'**
String get discussionsHidden ;
/// No description provided for @discussionsAboutCard.
///
/// In en, this message translates to:
/// **'About a flashcard'**
String get discussionsAboutCard ;
/// No description provided for @discussionsAboutNote.
///
/// In en, this message translates to:
/// **'About a note'**
String get discussionsAboutNote ;
/// No description provided for @discussionsAboutStep.
///
/// In en, this message translates to:
/// **'About a step'**
String get discussionsAboutStep ;
/// No description provided for @discussionsAccepted.
///
/// In en, this message translates to:
/// **'Accepted'**
String get discussionsAccepted ;
/// No description provided for @discussionsOn.
///
/// In en, this message translates to:
/// **'On {title}'**
String discussionsOn ( String title ) ;
/// No description provided for @discussionRefusalNoIdentity.
///
/// In en, this message translates to:
/// **'To ask and answer, sign in with an identity in Profile: posts show who wrote them.'**
String get discussionRefusalNoIdentity ;
/// No description provided for @discussionRefusalNoProfile.
///
/// In en, this message translates to:
/// **'To ask and answer, make a public profile in Profile: posts show who wrote them.'**
String get discussionRefusalNoProfile ;
/// No description provided for @discussionRefusalBlocked.
///
/// In en, this message translates to:
/// **'The author of this studyset keeps you from writing on their studysets.'**
String get discussionRefusalBlocked ;
/// No description provided for @discussionRefusalWithdrawn.
///
/// In en, this message translates to:
/// **'The author took this studyset off Explore: what is here stays, but nothing new is asked.'**
String get discussionRefusalWithdrawn ;
/// No description provided for @discussionRefusalNotInRelease.
///
/// In en, this message translates to:
/// **'That isn\'t in the studyset\'s release any more.'**
String get discussionRefusalNotInRelease ;
/// No description provided for @discussionRefusalEmpty.
///
/// In en, this message translates to:
/// **'Write something first.'**
String get discussionRefusalEmpty ;
/// No description provided for @discussionRefusalTooLong.
///
/// In en, this message translates to:
/// **'That\'s longer than allowed.'**
String get discussionRefusalTooLong ;
/// No description provided for @discussionRefusalTooMany.
///
/// In en, this message translates to:
/// **'You\'ve written a lot in the last hour. Try again a little later.'**
String get discussionRefusalTooMany ;
/// No description provided for @discussionRefusalWithFiles.
///
/// In en, this message translates to:
/// **'Images and sounds can\'t be posted: your files are yours alone, so nobody else would see them.'**
String get discussionRefusalWithFiles ;
/// No description provided for @discussionFailed.
///
/// In en, this message translates to:
/// **'Couldn\'t reach the instance. Try again once you are online.'**
String get discussionFailed ;
/// No description provided for @discussionTitle.
///
/// In en, this message translates to:
/// **'Question'**
String get discussionTitle ;
/// No description provided for @discussionMissing.
///
/// In en, this message translates to:
/// **'This question isn\'t there any more.'**
String get discussionMissing ;
/// No description provided for @discussionDeleted.
///
/// In en, this message translates to:
/// **'This was deleted.'**
String get discussionDeleted ;
/// No description provided for @discussionEdited.
///
/// In en, this message translates to:
/// **'edited'**
String get discussionEdited ;
/// No description provided for @discussionWriteAnswer.
///
/// In en, this message translates to:
/// **'Write an answer'**
String get discussionWriteAnswer ;
/// No description provided for @discussionYourAnswer.
///
/// In en, this message translates to:
/// **'Your answer'**
String get discussionYourAnswer ;
/// No description provided for @discussionEditHeading.
///
/// In en, this message translates to:
/// **'Edit'**
String get discussionEditHeading ;
/// No description provided for @discussionTitleField.
///
/// In en, this message translates to:
/// **'Title'**
String get discussionTitleField ;
/// No description provided for @discussionTitleHint.
///
/// In en, this message translates to:
/// **'What do you want to know?'**
String get discussionTitleHint ;
/// No description provided for @discussionPost.
///
/// In en, this message translates to:
/// **'Post'**
String get discussionPost ;
/// No description provided for @discussionComment.
///
/// In en, this message translates to:
/// **'Add a comment'**
String get discussionComment ;
/// No description provided for @discussionCommentHint.
///
/// In en, this message translates to:
/// **'A short remark'**
String get discussionCommentHint ;
/// No description provided for @discussionAccept.
///
/// In en, this message translates to:
/// **'Accept'**
String get discussionAccept ;
/// No description provided for @discussionUnaccept.
///
/// In en, this message translates to:
/// **'Take back'**
String get discussionUnaccept ;
/// No description provided for @discussionMenu.
///
/// In en, this message translates to:
/// **'More'**
String get discussionMenu ;
/// No description provided for @discussionMenuEdit.
///
/// In en, this message translates to:
/// **'Edit'**
String get discussionMenuEdit ;
/// No description provided for @discussionMenuDelete.
///
/// In en, this message translates to:
/// **'Delete'**
String get discussionMenuDelete ;
/// No description provided for @discussionMenuHide.
///
/// In en, this message translates to:
/// **'Hide'**
String get discussionMenuHide ;
/// No description provided for @discussionMenuShow.
///
/// In en, this message translates to:
/// **'Show again'**
String get discussionMenuShow ;
/// No description provided for @discussionMenuBlock.
///
/// In en, this message translates to:
/// **'Block {name}'**
String discussionMenuBlock ( String name ) ;
/// No description provided for @discussionDeleteTitle.
///
/// In en, this message translates to:
/// **'Delete this?'**
String get discussionDeleteTitle ;
/// No description provided for @discussionDeleteBody.
///
/// In en, this message translates to:
/// **'What others replied under it stays, without your text and name.'**
String get discussionDeleteBody ;
/// No description provided for @discussionAnswered.
///
/// In en, this message translates to:
/// **'Answer posted.'**
String get discussionAnswered ;
/// No description provided for @discussionCommented.
///
/// In en, this message translates to:
/// **'Comment posted.'**
String get discussionCommented ;
/// No description provided for @discussionSaved.
///
/// In en, this message translates to:
/// **'Saved.'**
String get discussionSaved ;
/// No description provided for @discussionDeletedDone.
///
/// In en, this message translates to:
/// **'Deleted.'**
String get discussionDeletedDone ;
/// No description provided for @discussionAcceptedDone.
///
/// In en, this message translates to:
/// **'Answer accepted.'**
String get discussionAcceptedDone ;
/// No description provided for @discussionUnacceptedDone.
///
/// In en, this message translates to:
/// **'No answer accepted now.'**
String get discussionUnacceptedDone ;
/// No description provided for @discussionHiddenDone.
///
/// In en, this message translates to:
/// **'Hidden: only its writer and you see it.'**
String get discussionHiddenDone ;
/// No description provided for @discussionShownDone.
///
/// In en, this message translates to:
/// **'Shown again.'**
String get discussionShownDone ;
/// No description provided for @discussionBlockedDone.
///
/// In en, this message translates to:
/// **'Blocked: what they write on your studysets shows to them alone.'**
String get discussionBlockedDone ;
/// No description provided for @discussionAskAbout.
///
/// In en, this message translates to:
/// **'Ask about this'**
String get discussionAskAbout ;
/// No description provided for @inboxDiscussions.
///
/// In en, this message translates to:
/// **'Discussions'**
String get inboxDiscussions ;
/// No description provided for @inboxFeedback.
///
/// In en, this message translates to:
/// **'Reports and suggestions'**
String get inboxFeedback ;
/// No description provided for @inboxDiscussionsEmpty.
///
/// In en, this message translates to:
/// **'No discussions yet.'**
String get inboxDiscussionsEmpty ;
feat(feedback): report, suggest, and an inbox in the Profile tab
The app's side of stage 4 (ADR 0005, rule 11). The card's Report sheet
offers "Send to the author too" on a card from a release; a copy's menu
reports the studyset as a whole and lists what is reported and
suggested on it, as Explore's summary does; the editors of cards, notes
and steps suggest them to the author as the learner has them, never
under "no derivatives". The Profile tab holds the inbox: the threads on
the person's published studysets and those they wrote, the newest
activity first, with a count on the tab. A thread shows a suggestion
beside the author's own item and its replies; its writer and the author
reply, and the author accepts (the content becomes their own edit, its
files fetched from the writer's), declines, closes, hides or blocks.
Writing needs an identity; reading what is public doesn't.
ADR 0005 gets an addendum for stage 4.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MMm7usAo6q9ZZRoPkWjoxi
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-10 01:29:09 +02:00
/// No description provided for @cardReportToAuthor.
///
/// In en, this message translates to:
/// **'Send to the author too'**
String get cardReportToAuthor ;
/// No description provided for @cardReportToAuthorHint.
///
/// In en, this message translates to:
/// **'Public: everyone learning it reads it, with your public profile.'**
String get cardReportToAuthorHint ;
/// No description provided for @feedbackReportSent.
///
/// In en, this message translates to:
/// **'Sent to the author.'**
String get feedbackReportSent ;
/// No description provided for @suggestToAuthor.
///
/// In en, this message translates to:
/// **'Suggest to the author'**
String get suggestToAuthor ;
/// No description provided for @suggestHint.
///
/// In en, this message translates to:
/// **'What it changes, and why'**
String get suggestHint ;
/// No description provided for @suggestIntro.
///
/// In en, this message translates to:
/// **'The author sees it as you have it now, beside theirs. Public: everyone learning it reads it, with your public profile.'**
String get suggestIntro ;
/// No description provided for @suggestSent.
///
/// In en, this message translates to:
/// **'Suggestion sent.'**
String get suggestSent ;
/// No description provided for @suggestNoDerivatives.
///
/// In en, this message translates to:
/// **'Its licence forbids adaptations: no suggestions.'**
String get suggestNoDerivatives ;
/// No description provided for @feedbackNeedsIdentity.
///
/// In en, this message translates to:
feat(auth): sign in with an email address, keeping only a keyed hash of it
People without GitHub can now keep an anonymous account, and sign in on
another device, with an email address and a code mailed to it each time;
no password. The instance keeps only HMAC-SHA256 of the address, keyed
with its emailSignInPepper (ADR 0010): from what someone types it finds
the person and tells whether an address is free, it mails only an address
someone has just typed, and it can't write to anybody otherwise or show
anybody their address. The device remembers the address it signed in
with, in the person's folder, to show it.
It works as GitHub does. A code for a new address makes it the signed-in
person's, who becomes identified and keeps everything. A code for another
person's address gives a switch token, which signs in as them without a
session; a device holding studysets asks first. Asking for a code answers
alike for every address. Codes are eight Crockford characters, valid 15
minutes and three tries (Serverpod's code challenges); three codes an
hour per address, ten per network. In development the code goes to the
server's log; elsewhere there is no email sign-in until a mail service
is set up (instance.info().emailSignIn).
Someone identified may add the other way in: the Profile tab lists how
they sign in and adds GitHub or an address. They are never switched to
another account: an address or GitHub account that is another's is said
to be so. Switching now deletes the account left behind only when it was
anonymous; it deleted any, which only anonymous people could reach so far.
The migration adds email_identity and email_code_request with their
indexes and foreign keys to Serverpod's challenges; nothing else changes.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-15 19:42:21 +02:00
/// **'Sign in to report to the author and suggest.'**
feat(feedback): report, suggest, and an inbox in the Profile tab
The app's side of stage 4 (ADR 0005, rule 11). The card's Report sheet
offers "Send to the author too" on a card from a release; a copy's menu
reports the studyset as a whole and lists what is reported and
suggested on it, as Explore's summary does; the editors of cards, notes
and steps suggest them to the author as the learner has them, never
under "no derivatives". The Profile tab holds the inbox: the threads on
the person's published studysets and those they wrote, the newest
activity first, with a count on the tab. A thread shows a suggestion
beside the author's own item and its replies; its writer and the author
reply, and the author accepts (the content becomes their own edit, its
files fetched from the writer's), declines, closes, hides or blocks.
Writing needs an identity; reading what is public doesn't.
ADR 0005 gets an addendum for stage 4.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MMm7usAo6q9ZZRoPkWjoxi
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-10 01:29:09 +02:00
String get feedbackNeedsIdentity ;
2026-10-10 01:51:20 +02:00
/// No description provided for @kanjiExampleWords.
///
/// In en, this message translates to:
/// **'Words with it'**
String get kanjiExampleWords ;
feat(assistant): ask Claude from Home, the Library, a studyset and a session
A device setting, off unless the person turns it on (Settings ›
Assistant), shows a field at the foot of Home and a button that unfolds
into one: above Create studyset in the Library (smaller), in the free lane
on the right of a studyset's bottom bar (floating alone from expanded
windows on), and in the card's lower right corner while studying.
A question is handed to Claude, not answered here: the app opens
claude.ai/new with the question written in, followed by what it is about
(the person's learning on the instance, a studyset by name, or the studied
card's front and back as text), cut at 12,000 characters, for the person
to read and send in Claude. A Claude plan can't drive a chat inside
another app, as Anthropic allows no third party to offer sign-in with it;
Claude is to read the studysets through a connector on the instance
instead (next). Nothing leaves the app until the person sends it there.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MMm7usAo6q9ZZRoPkWjoxi
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-10 11:51:58 +02:00
/// No description provided for @settingsAssistant.
///
/// In en, this message translates to:
/// **'Assistant'**
String get settingsAssistant ;
feat(assistant): a question opens in the AI assistant chosen, Proton's Lumo by default
Questions asked from the app went to Claude alone. Settings › Assistant
now chooses where they open (decided 2026-10-14): Lumo, the default, by
its guest chat with the question in the address's fragment (Lumo's
`prefill`, which needs no account and reaches no server; the address for
one's own account loses the question when nobody is signed in), ChatGPT,
Claude, Mistral, Perplexity, Duck.ai, Grok, Kagi Assistant, HuggingChat,
or an address of one's own with %s. Each link fills the question in for
the person to send (Perplexity answers at once, Grok asks first); Gemini,
which has no such link, and Copilot, whose stopped working, are left out.
The field names the assistant ("Ask Lumo…"). A device that sent questions
to Claude goes on asking an assistant, now Lumo. Connecting Claude to read
studysets stays as it was.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-14 13:19:51 +02:00
/// No description provided for @settingsAskAssistant.
feat(assistant): ask Claude from Home, the Library, a studyset and a session
A device setting, off unless the person turns it on (Settings ›
Assistant), shows a field at the foot of Home and a button that unfolds
into one: above Create studyset in the Library (smaller), in the free lane
on the right of a studyset's bottom bar (floating alone from expanded
windows on), and in the card's lower right corner while studying.
A question is handed to Claude, not answered here: the app opens
claude.ai/new with the question written in, followed by what it is about
(the person's learning on the instance, a studyset by name, or the studied
card's front and back as text), cut at 12,000 characters, for the person
to read and send in Claude. A Claude plan can't drive a chat inside
another app, as Anthropic allows no third party to offer sign-in with it;
Claude is to read the studysets through a connector on the instance
instead (next). Nothing leaves the app until the person sends it there.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MMm7usAo6q9ZZRoPkWjoxi
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-10 11:51:58 +02:00
///
/// In en, this message translates to:
feat(assistant): a question opens in the AI assistant chosen, Proton's Lumo by default
Questions asked from the app went to Claude alone. Settings › Assistant
now chooses where they open (decided 2026-10-14): Lumo, the default, by
its guest chat with the question in the address's fragment (Lumo's
`prefill`, which needs no account and reaches no server; the address for
one's own account loses the question when nobody is signed in), ChatGPT,
Claude, Mistral, Perplexity, Duck.ai, Grok, Kagi Assistant, HuggingChat,
or an address of one's own with %s. Each link fills the question in for
the person to send (Perplexity answers at once, Grok asks first); Gemini,
which has no such link, and Copilot, whose stopped working, are left out.
The field names the assistant ("Ask Lumo…"). A device that sent questions
to Claude goes on asking an assistant, now Lumo. Connecting Claude to read
studysets stays as it was.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-14 13:19:51 +02:00
/// **'Ask an AI assistant or search the web'**
String get settingsAskAssistant ;
feat(assistant): ask Claude from Home, the Library, a studyset and a session
A device setting, off unless the person turns it on (Settings ›
Assistant), shows a field at the foot of Home and a button that unfolds
into one: above Create studyset in the Library (smaller), in the free lane
on the right of a studyset's bottom bar (floating alone from expanded
windows on), and in the card's lower right corner while studying.
A question is handed to Claude, not answered here: the app opens
claude.ai/new with the question written in, followed by what it is about
(the person's learning on the instance, a studyset by name, or the studied
card's front and back as text), cut at 12,000 characters, for the person
to read and send in Claude. A Claude plan can't drive a chat inside
another app, as Anthropic allows no third party to offer sign-in with it;
Claude is to read the studysets through a connector on the instance
instead (next). Nothing leaves the app until the person sends it there.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MMm7usAo6q9ZZRoPkWjoxi
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-10 11:51:58 +02:00
feat(assistant): a question opens in the AI assistant chosen, Proton's Lumo by default
Questions asked from the app went to Claude alone. Settings › Assistant
now chooses where they open (decided 2026-10-14): Lumo, the default, by
its guest chat with the question in the address's fragment (Lumo's
`prefill`, which needs no account and reaches no server; the address for
one's own account loses the question when nobody is signed in), ChatGPT,
Claude, Mistral, Perplexity, Duck.ai, Grok, Kagi Assistant, HuggingChat,
or an address of one's own with %s. Each link fills the question in for
the person to send (Perplexity answers at once, Grok asks first); Gemini,
which has no such link, and Copilot, whose stopped working, are left out.
The field names the assistant ("Ask Lumo…"). A device that sent questions
to Claude goes on asking an assistant, now Lumo. Connecting Claude to read
studysets stays as it was.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-14 13:19:51 +02:00
/// No description provided for @settingsAskAssistantSubtitle.
feat(assistant): ask Claude from Home, the Library, a studyset and a session
A device setting, off unless the person turns it on (Settings ›
Assistant), shows a field at the foot of Home and a button that unfolds
into one: above Create studyset in the Library (smaller), in the free lane
on the right of a studyset's bottom bar (floating alone from expanded
windows on), and in the card's lower right corner while studying.
A question is handed to Claude, not answered here: the app opens
claude.ai/new with the question written in, followed by what it is about
(the person's learning on the instance, a studyset by name, or the studied
card's front and back as text), cut at 12,000 characters, for the person
to read and send in Claude. A Claude plan can't drive a chat inside
another app, as Anthropic allows no third party to offer sign-in with it;
Claude is to read the studysets through a connector on the instance
instead (next). Nothing leaves the app until the person sends it there.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MMm7usAo6q9ZZRoPkWjoxi
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-10 11:51:58 +02:00
///
/// In en, this message translates to:
feat(assistant): a question opens in the AI assistant chosen, Proton's Lumo by default
Questions asked from the app went to Claude alone. Settings › Assistant
now chooses where they open (decided 2026-10-14): Lumo, the default, by
its guest chat with the question in the address's fragment (Lumo's
`prefill`, which needs no account and reaches no server; the address for
one's own account loses the question when nobody is signed in), ChatGPT,
Claude, Mistral, Perplexity, Duck.ai, Grok, Kagi Assistant, HuggingChat,
or an address of one's own with %s. Each link fills the question in for
the person to send (Perplexity answers at once, Grok asks first); Gemini,
which has no such link, and Copilot, whose stopped working, are left out.
The field names the assistant ("Ask Lumo…"). A device that sent questions
to Claude goes on asking an assistant, now Lumo. Connecting Claude to read
studysets stays as it was.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-14 13:19:51 +02:00
/// **'Off unless you turn it on. Adds a field on Home and a button in the Library, on a studyset and while studying. A question opens in the AI assistant you choose or as a web search, with only the context you choose; nothing is sent from here.'**
String get settingsAskAssistantSubtitle ;
feat(assistant): ask Claude from Home, the Library, a studyset and a session
A device setting, off unless the person turns it on (Settings ›
Assistant), shows a field at the foot of Home and a button that unfolds
into one: above Create studyset in the Library (smaller), in the free lane
on the right of a studyset's bottom bar (floating alone from expanded
windows on), and in the card's lower right corner while studying.
A question is handed to Claude, not answered here: the app opens
claude.ai/new with the question written in, followed by what it is about
(the person's learning on the instance, a studyset by name, or the studied
card's front and back as text), cut at 12,000 characters, for the person
to read and send in Claude. A Claude plan can't drive a chat inside
another app, as Anthropic allows no third party to offer sign-in with it;
Claude is to read the studysets through a connector on the instance
instead (next). Nothing leaves the app until the person sends it there.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MMm7usAo6q9ZZRoPkWjoxi
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-10 11:51:58 +02:00
/// No description provided for @assistantHint.
///
/// In en, this message translates to:
feat(assistant): a question opens in the AI assistant chosen, Proton's Lumo by default
Questions asked from the app went to Claude alone. Settings › Assistant
now chooses where they open (decided 2026-10-14): Lumo, the default, by
its guest chat with the question in the address's fragment (Lumo's
`prefill`, which needs no account and reaches no server; the address for
one's own account loses the question when nobody is signed in), ChatGPT,
Claude, Mistral, Perplexity, Duck.ai, Grok, Kagi Assistant, HuggingChat,
or an address of one's own with %s. Each link fills the question in for
the person to send (Perplexity answers at once, Grok asks first); Gemini,
which has no such link, and Copilot, whose stopped working, are left out.
The field names the assistant ("Ask Lumo…"). A device that sent questions
to Claude goes on asking an assistant, now Lumo. Connecting Claude to read
studysets stays as it was.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-14 13:19:51 +02:00
/// **'Ask {assistant}…'**
String assistantHint ( String assistant ) ;
feat(assistant): ask Claude from Home, the Library, a studyset and a session
A device setting, off unless the person turns it on (Settings ›
Assistant), shows a field at the foot of Home and a button that unfolds
into one: above Create studyset in the Library (smaller), in the free lane
on the right of a studyset's bottom bar (floating alone from expanded
windows on), and in the card's lower right corner while studying.
A question is handed to Claude, not answered here: the app opens
claude.ai/new with the question written in, followed by what it is about
(the person's learning on the instance, a studyset by name, or the studied
card's front and back as text), cut at 12,000 characters, for the person
to read and send in Claude. A Claude plan can't drive a chat inside
another app, as Anthropic allows no third party to offer sign-in with it;
Claude is to read the studysets through a connector on the instance
instead (next). Nothing leaves the app until the person sends it there.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MMm7usAo6q9ZZRoPkWjoxi
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-10 11:51:58 +02:00
/// No description provided for @assistantHintStudyset.
///
/// In en, this message translates to:
feat(assistant): a question opens in the AI assistant chosen, Proton's Lumo by default
Questions asked from the app went to Claude alone. Settings › Assistant
now chooses where they open (decided 2026-10-14): Lumo, the default, by
its guest chat with the question in the address's fragment (Lumo's
`prefill`, which needs no account and reaches no server; the address for
one's own account loses the question when nobody is signed in), ChatGPT,
Claude, Mistral, Perplexity, Duck.ai, Grok, Kagi Assistant, HuggingChat,
or an address of one's own with %s. Each link fills the question in for
the person to send (Perplexity answers at once, Grok asks first); Gemini,
which has no such link, and Copilot, whose stopped working, are left out.
The field names the assistant ("Ask Lumo…"). A device that sent questions
to Claude goes on asking an assistant, now Lumo. Connecting Claude to read
studysets stays as it was.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-14 13:19:51 +02:00
/// **'Ask {assistant} about {title}…'**
String assistantHintStudyset ( String assistant , String title ) ;
feat(assistant): ask Claude from Home, the Library, a studyset and a session
A device setting, off unless the person turns it on (Settings ›
Assistant), shows a field at the foot of Home and a button that unfolds
into one: above Create studyset in the Library (smaller), in the free lane
on the right of a studyset's bottom bar (floating alone from expanded
windows on), and in the card's lower right corner while studying.
A question is handed to Claude, not answered here: the app opens
claude.ai/new with the question written in, followed by what it is about
(the person's learning on the instance, a studyset by name, or the studied
card's front and back as text), cut at 12,000 characters, for the person
to read and send in Claude. A Claude plan can't drive a chat inside
another app, as Anthropic allows no third party to offer sign-in with it;
Claude is to read the studysets through a connector on the instance
instead (next). Nothing leaves the app until the person sends it there.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MMm7usAo6q9ZZRoPkWjoxi
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-10 11:51:58 +02:00
/// No description provided for @assistantHintCard.
///
/// In en, this message translates to:
feat(assistant): a question opens in the AI assistant chosen, Proton's Lumo by default
Questions asked from the app went to Claude alone. Settings › Assistant
now chooses where they open (decided 2026-10-14): Lumo, the default, by
its guest chat with the question in the address's fragment (Lumo's
`prefill`, which needs no account and reaches no server; the address for
one's own account loses the question when nobody is signed in), ChatGPT,
Claude, Mistral, Perplexity, Duck.ai, Grok, Kagi Assistant, HuggingChat,
or an address of one's own with %s. Each link fills the question in for
the person to send (Perplexity answers at once, Grok asks first); Gemini,
which has no such link, and Copilot, whose stopped working, are left out.
The field names the assistant ("Ask Lumo…"). A device that sent questions
to Claude goes on asking an assistant, now Lumo. Connecting Claude to read
studysets stays as it was.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-14 13:19:51 +02:00
/// **'Ask {assistant} about this card…'**
String assistantHintCard ( String assistant ) ;
feat(assistant): ask Claude from Home, the Library, a studyset and a session
A device setting, off unless the person turns it on (Settings ›
Assistant), shows a field at the foot of Home and a button that unfolds
into one: above Create studyset in the Library (smaller), in the free lane
on the right of a studyset's bottom bar (floating alone from expanded
windows on), and in the card's lower right corner while studying.
A question is handed to Claude, not answered here: the app opens
claude.ai/new with the question written in, followed by what it is about
(the person's learning on the instance, a studyset by name, or the studied
card's front and back as text), cut at 12,000 characters, for the person
to read and send in Claude. A Claude plan can't drive a chat inside
another app, as Anthropic allows no third party to offer sign-in with it;
Claude is to read the studysets through a connector on the instance
instead (next). Nothing leaves the app until the person sends it there.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MMm7usAo6q9ZZRoPkWjoxi
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-10 11:51:58 +02:00
/// No description provided for @assistantSend.
///
/// In en, this message translates to:
feat(assistant): a question opens in the AI assistant chosen, Proton's Lumo by default
Questions asked from the app went to Claude alone. Settings › Assistant
now chooses where they open (decided 2026-10-14): Lumo, the default, by
its guest chat with the question in the address's fragment (Lumo's
`prefill`, which needs no account and reaches no server; the address for
one's own account loses the question when nobody is signed in), ChatGPT,
Claude, Mistral, Perplexity, Duck.ai, Grok, Kagi Assistant, HuggingChat,
or an address of one's own with %s. Each link fills the question in for
the person to send (Perplexity answers at once, Grok asks first); Gemini,
which has no such link, and Copilot, whose stopped working, are left out.
The field names the assistant ("Ask Lumo…"). A device that sent questions
to Claude goes on asking an assistant, now Lumo. Connecting Claude to read
studysets stays as it was.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-14 13:19:51 +02:00
/// **'Ask in {assistant}'**
String assistantSend ( String assistant ) ;
feat(assistant): ask Claude from Home, the Library, a studyset and a session
A device setting, off unless the person turns it on (Settings ›
Assistant), shows a field at the foot of Home and a button that unfolds
into one: above Create studyset in the Library (smaller), in the free lane
on the right of a studyset's bottom bar (floating alone from expanded
windows on), and in the card's lower right corner while studying.
A question is handed to Claude, not answered here: the app opens
claude.ai/new with the question written in, followed by what it is about
(the person's learning on the instance, a studyset by name, or the studied
card's front and back as text), cut at 12,000 characters, for the person
to read and send in Claude. A Claude plan can't drive a chat inside
another app, as Anthropic allows no third party to offer sign-in with it;
Claude is to read the studysets through a connector on the instance
instead (next). Nothing leaves the app until the person sends it there.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MMm7usAo6q9ZZRoPkWjoxi
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-10 11:51:58 +02:00
/// No description provided for @assistantOpen.
///
/// In en, this message translates to:
feat(assistant): search the web too, and choose what goes along
The assistant's field now has a switch at its start: a question goes to
Claude or into a web search, and the field remembers which. A web search
opens the chosen engine's results: on Android the device's own web
search by default (ACTION_WEB_SEARCH, answered by its default browser or
search app; Brave Search where nothing answers), elsewhere Brave Search
by default; Settings › Assistant › Search engine offers Ecosia,
DuckDuckGo, Startpage, Qwant, Google, Bing, or one's own address with
%s where the question goes.
Above the field, chips offer what the question is about: a card's
front, back, each hint and its explanation, the studyset's name and
description. None is chosen, so only what the person picks goes along,
each part cut at 600 characters; Claude gets the question and a compact
line per chosen part, a web search the question and the chosen text,
never the studyset's name. A chosen chip alone is enough to send. The
old sentences around every question (where it was asked, the connector)
are gone: they cost tokens for nothing unless the studyset is chosen.
On a studyset's screen the button now stands above the bottom bar, as in
the Library: unfolded inside the bar, its chips lay outside the bar's
bounds, where taps don't land.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MMm7usAo6q9ZZRoPkWjoxi
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-10 14:41:36 +02:00
/// **'Ask or search'**
feat(assistant): ask Claude from Home, the Library, a studyset and a session
A device setting, off unless the person turns it on (Settings ›
Assistant), shows a field at the foot of Home and a button that unfolds
into one: above Create studyset in the Library (smaller), in the free lane
on the right of a studyset's bottom bar (floating alone from expanded
windows on), and in the card's lower right corner while studying.
A question is handed to Claude, not answered here: the app opens
claude.ai/new with the question written in, followed by what it is about
(the person's learning on the instance, a studyset by name, or the studied
card's front and back as text), cut at 12,000 characters, for the person
to read and send in Claude. A Claude plan can't drive a chat inside
another app, as Anthropic allows no third party to offer sign-in with it;
Claude is to read the studysets through a connector on the instance
instead (next). Nothing leaves the app until the person sends it there.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MMm7usAo6q9ZZRoPkWjoxi
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-10 11:51:58 +02:00
String get assistantOpen ;
/// No description provided for @assistantClose.
///
/// In en, this message translates to:
/// **'Close'**
String get assistantClose ;
/// No description provided for @assistantOpenFailed.
///
/// In en, this message translates to:
feat(assistant): a question opens in the AI assistant chosen, Proton's Lumo by default
Questions asked from the app went to Claude alone. Settings › Assistant
now chooses where they open (decided 2026-10-14): Lumo, the default, by
its guest chat with the question in the address's fragment (Lumo's
`prefill`, which needs no account and reaches no server; the address for
one's own account loses the question when nobody is signed in), ChatGPT,
Claude, Mistral, Perplexity, Duck.ai, Grok, Kagi Assistant, HuggingChat,
or an address of one's own with %s. Each link fills the question in for
the person to send (Perplexity answers at once, Grok asks first); Gemini,
which has no such link, and Copilot, whose stopped working, are left out.
The field names the assistant ("Ask Lumo…"). A device that sent questions
to Claude goes on asking an assistant, now Lumo. Connecting Claude to read
studysets stays as it was.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-14 13:19:51 +02:00
/// **'{assistant} couldn\'t be opened.'**
String assistantOpenFailed ( String assistant ) ;
feat(assistant): ask Claude from Home, the Library, a studyset and a session
A device setting, off unless the person turns it on (Settings ›
Assistant), shows a field at the foot of Home and a button that unfolds
into one: above Create studyset in the Library (smaller), in the free lane
on the right of a studyset's bottom bar (floating alone from expanded
windows on), and in the card's lower right corner while studying.
A question is handed to Claude, not answered here: the app opens
claude.ai/new with the question written in, followed by what it is about
(the person's learning on the instance, a studyset by name, or the studied
card's front and back as text), cut at 12,000 characters, for the person
to read and send in Claude. A Claude plan can't drive a chat inside
another app, as Anthropic allows no third party to offer sign-in with it;
Claude is to read the studysets through a connector on the instance
instead (next). Nothing leaves the app until the person sends it there.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MMm7usAo6q9ZZRoPkWjoxi
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-10 11:51:58 +02:00
feat(assistant): Claude reads studysets through a connector on the instance
A Claude plan can't drive a chat inside another app, but Claude's apps
(every plan, the phone apps too) can add a remote MCP server as a custom
connector. The instance now is one (ADR 0006): Serverpod's web server
serves the MCP endpoint (/mcp, Streamable HTTP, JSON answers, no
sessions) with five read-only tools over the person's own synced rows
(their studysets, a studyset's cards and notes a page at a time, a
search across cards, a note, their progress), and the OAuth the
specification asks for: protected resource and authorization server
metadata, dynamic client registration, authorization code with PKCE
(S256 only), tokens bound to the resource, iss in the response, refresh
tokens that rotate. Caddy forwards these paths alone to the web server,
which production now runs on 8082.
People connect with a code from the app (Settings › Assistant › Connect
Claude): eight Crockford characters, valid for ten minutes and once,
typed on the authorization page, which names where it returns to, loads
nothing from elsewhere and may not be framed. Anonymous people can
connect too. Codes and tokens are kept only as SHA-256 hashes; an access
token lives an hour, a refresh token 30 days. The app lists what is
connected and disconnects it, which ends its tokens at once; deleting
the account deletes it; a daily job forgets what expired and clients
nobody connected within a week. InstanceInfo says where the connector is
(additive); the migration only adds server-only tables, so devices and
older apps are untouched.
The handlers take plain requests, so the integration tests drive the
whole flow (registration, the code, tokens, refresh, the tools, another
person's content, disconnecting, expiry, pruning, account deletion)
without a web server.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MMm7usAo6q9ZZRoPkWjoxi
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-10 12:13:00 +02:00
/// No description provided for @assistantConnectTitle.
///
/// In en, this message translates to:
/// **'Connect Claude'**
String get assistantConnectTitle ;
/// No description provided for @assistantConnectSubtitle.
///
/// In en, this message translates to:
/// **'Let Claude read your studysets'**
String get assistantConnectSubtitle ;
/// No description provided for @assistantConnectIntro.
///
/// In en, this message translates to:
/// **'Claude can read your studysets, cards, notes and progress on {instance} through Halkyon Learn\'s connector, and answer about them. It can\'t change anything. What it reads goes to Anthropic, under their privacy policy.'**
String assistantConnectIntro ( String instance ) ;
/// No description provided for @assistantStepAdd.
///
/// In en, this message translates to:
/// **'In Claude, open Settings › Connectors and add a custom connector named Halkyon Learn, with this address. Added once, it works in Claude\'s apps too.'**
String get assistantStepAdd ;
/// No description provided for @assistantStepCode.
///
/// In en, this message translates to:
/// **'Claude then opens a page asking for a code. Get one here: it works once, for ten minutes.'**
String get assistantStepCode ;
/// No description provided for @assistantShowCode.
///
/// In en, this message translates to:
/// **'Show a code'**
String get assistantShowCode ;
/// No description provided for @assistantNewCode.
///
/// In en, this message translates to:
/// **'New code'**
String get assistantNewCode ;
/// No description provided for @assistantCodeValid.
///
/// In en, this message translates to:
/// **'Valid until {time}'**
String assistantCodeValid ( String time ) ;
/// No description provided for @assistantCopy.
///
/// In en, this message translates to:
/// **'Copy'**
String get assistantCopy ;
/// No description provided for @assistantCopied.
///
/// In en, this message translates to:
/// **'Copied'**
String get assistantCopied ;
/// No description provided for @assistantConnected.
///
/// In en, this message translates to:
/// **'Connected'**
String get assistantConnected ;
/// No description provided for @assistantNoneConnected.
///
/// In en, this message translates to:
/// **'Nothing connected yet.'**
String get assistantNoneConnected ;
/// No description provided for @assistantConnectedOn.
///
/// In en, this message translates to:
/// **'connected {date}'**
String assistantConnectedOn ( String date ) ;
/// No description provided for @assistantLastRead.
///
/// In en, this message translates to:
/// **'last read {date}'**
String assistantLastRead ( String date ) ;
/// No description provided for @assistantDisconnect.
///
/// In en, this message translates to:
/// **'Disconnect'**
String get assistantDisconnect ;
/// No description provided for @assistantDisconnectTitle.
///
/// In en, this message translates to:
/// **'Disconnect {name}?'**
String assistantDisconnectTitle ( String name ) ;
/// No description provided for @assistantDisconnectMessage.
///
/// In en, this message translates to:
/// **'It can\'t read your studysets any more. You can connect it again with a new code.'**
String get assistantDisconnectMessage ;
/// No description provided for @assistantNotOffered.
///
/// In en, this message translates to:
feat(assistant): a question opens in the AI assistant chosen, Proton's Lumo by default
Questions asked from the app went to Claude alone. Settings › Assistant
now chooses where they open (decided 2026-10-14): Lumo, the default, by
its guest chat with the question in the address's fragment (Lumo's
`prefill`, which needs no account and reaches no server; the address for
one's own account loses the question when nobody is signed in), ChatGPT,
Claude, Mistral, Perplexity, Duck.ai, Grok, Kagi Assistant, HuggingChat,
or an address of one's own with %s. Each link fills the question in for
the person to send (Perplexity answers at once, Grok asks first); Gemini,
which has no such link, and Copilot, whose stopped working, are left out.
The field names the assistant ("Ask Lumo…"). A device that sent questions
to Claude goes on asking an assistant, now Lumo. Connecting Claude to read
studysets stays as it was.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-14 13:19:51 +02:00
/// **'This instance doesn\'t offer a connector yet. Questions still open in the AI assistant you chose, with what they are about.'**
feat(assistant): Claude reads studysets through a connector on the instance
A Claude plan can't drive a chat inside another app, but Claude's apps
(every plan, the phone apps too) can add a remote MCP server as a custom
connector. The instance now is one (ADR 0006): Serverpod's web server
serves the MCP endpoint (/mcp, Streamable HTTP, JSON answers, no
sessions) with five read-only tools over the person's own synced rows
(their studysets, a studyset's cards and notes a page at a time, a
search across cards, a note, their progress), and the OAuth the
specification asks for: protected resource and authorization server
metadata, dynamic client registration, authorization code with PKCE
(S256 only), tokens bound to the resource, iss in the response, refresh
tokens that rotate. Caddy forwards these paths alone to the web server,
which production now runs on 8082.
People connect with a code from the app (Settings › Assistant › Connect
Claude): eight Crockford characters, valid for ten minutes and once,
typed on the authorization page, which names where it returns to, loads
nothing from elsewhere and may not be framed. Anonymous people can
connect too. Codes and tokens are kept only as SHA-256 hashes; an access
token lives an hour, a refresh token 30 days. The app lists what is
connected and disconnects it, which ends its tokens at once; deleting
the account deletes it; a daily job forgets what expired and clients
nobody connected within a week. InstanceInfo says where the connector is
(additive); the migration only adds server-only tables, so devices and
older apps are untouched.
The handlers take plain requests, so the integration tests drive the
whole flow (registration, the code, tokens, refresh, the tools, another
person's content, disconnecting, expiry, pruning, account deletion)
without a web server.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MMm7usAo6q9ZZRoPkWjoxi
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-10 12:13:00 +02:00
String get assistantNotOffered ;
/// No description provided for @assistantUnreachable.
///
/// In en, this message translates to:
/// **'The instance can\'t be reached. Try again once you are online.'**
String get assistantUnreachable ;
/// No description provided for @assistantRetry.
///
/// In en, this message translates to:
/// **'Try again'**
String get assistantRetry ;
feat(assistant): search the web too, and choose what goes along
The assistant's field now has a switch at its start: a question goes to
Claude or into a web search, and the field remembers which. A web search
opens the chosen engine's results: on Android the device's own web
search by default (ACTION_WEB_SEARCH, answered by its default browser or
search app; Brave Search where nothing answers), elsewhere Brave Search
by default; Settings › Assistant › Search engine offers Ecosia,
DuckDuckGo, Startpage, Qwant, Google, Bing, or one's own address with
%s where the question goes.
Above the field, chips offer what the question is about: a card's
front, back, each hint and its explanation, the studyset's name and
description. None is chosen, so only what the person picks goes along,
each part cut at 600 characters; Claude gets the question and a compact
line per chosen part, a web search the question and the chosen text,
never the studyset's name. A chosen chip alone is enough to send. The
old sentences around every question (where it was asked, the connector)
are gone: they cost tokens for nothing unless the studyset is chosen.
On a studyset's screen the button now stands above the bottom bar, as in
the Library: unfolded inside the bar, its chips lay outside the bar's
bounds, where taps don't land.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MMm7usAo6q9ZZRoPkWjoxi
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-10 14:41:36 +02:00
feat(assistant): a question opens in the AI assistant chosen, Proton's Lumo by default
Questions asked from the app went to Claude alone. Settings › Assistant
now chooses where they open (decided 2026-10-14): Lumo, the default, by
its guest chat with the question in the address's fragment (Lumo's
`prefill`, which needs no account and reaches no server; the address for
one's own account loses the question when nobody is signed in), ChatGPT,
Claude, Mistral, Perplexity, Duck.ai, Grok, Kagi Assistant, HuggingChat,
or an address of one's own with %s. Each link fills the question in for
the person to send (Perplexity answers at once, Grok asks first); Gemini,
which has no such link, and Copilot, whose stopped working, are left out.
The field names the assistant ("Ask Lumo…"). A device that sent questions
to Claude goes on asking an assistant, now Lumo. Connecting Claude to read
studysets stays as it was.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-14 13:19:51 +02:00
/// A line of context handed to the AI assistant after the question.
feat(assistant): search the web too, and choose what goes along
The assistant's field now has a switch at its start: a question goes to
Claude or into a web search, and the field remembers which. A web search
opens the chosen engine's results: on Android the device's own web
search by default (ACTION_WEB_SEARCH, answered by its default browser or
search app; Brave Search where nothing answers), elsewhere Brave Search
by default; Settings › Assistant › Search engine offers Ecosia,
DuckDuckGo, Startpage, Qwant, Google, Bing, or one's own address with
%s where the question goes.
Above the field, chips offer what the question is about: a card's
front, back, each hint and its explanation, the studyset's name and
description. None is chosen, so only what the person picks goes along,
each part cut at 600 characters; Claude gets the question and a compact
line per chosen part, a web search the question and the chosen text,
never the studyset's name. A chosen chip alone is enough to send. The
old sentences around every question (where it was asked, the connector)
are gone: they cost tokens for nothing unless the studyset is chosen.
On a studyset's screen the button now stands above the bottom bar, as in
the Library: unfolded inside the bar, its chips lay outside the bar's
bounds, where taps don't land.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MMm7usAo6q9ZZRoPkWjoxi
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-10 14:41:36 +02:00
///
/// In en, this message translates to:
/// **'Front: {text}'**
String assistantLineFront ( String text ) ;
/// No description provided for @assistantLineBack.
///
/// In en, this message translates to:
/// **'Back: {text}'**
String assistantLineBack ( String text ) ;
/// No description provided for @assistantLineHint.
///
/// In en, this message translates to:
/// **'Hint {number}: {text}'**
String assistantLineHint ( int number , String text ) ;
/// No description provided for @assistantLineExplanation.
///
/// In en, this message translates to:
/// **'Explanation: {text}'**
String assistantLineExplanation ( String text ) ;
/// No description provided for @assistantLineStudyset.
///
/// In en, this message translates to:
/// **'Studyset: “{title}” in Halkyon Learn on {instance}'**
String assistantLineStudyset ( String title , String instance ) ;
/// No description provided for @assistantLineDescription.
///
/// In en, this message translates to:
/// **'Description: {text}'**
String assistantLineDescription ( String text ) ;
/// No description provided for @assistantChipStudyset.
///
/// In en, this message translates to:
/// **'Studyset: {title}'**
String assistantChipStudyset ( String title ) ;
/// No description provided for @assistantContextLabel.
///
/// In en, this message translates to:
/// **'Send along'**
String get assistantContextLabel ;
/// No description provided for @assistantTargetWeb.
///
/// In en, this message translates to:
/// **'Web'**
String get assistantTargetWeb ;
/// No description provided for @assistantSwitchToWeb.
///
/// In en, this message translates to:
/// **'Search the web instead'**
String get assistantSwitchToWeb ;
feat(assistant): a question opens in the AI assistant chosen, Proton's Lumo by default
Questions asked from the app went to Claude alone. Settings › Assistant
now chooses where they open (decided 2026-10-14): Lumo, the default, by
its guest chat with the question in the address's fragment (Lumo's
`prefill`, which needs no account and reaches no server; the address for
one's own account loses the question when nobody is signed in), ChatGPT,
Claude, Mistral, Perplexity, Duck.ai, Grok, Kagi Assistant, HuggingChat,
or an address of one's own with %s. Each link fills the question in for
the person to send (Perplexity answers at once, Grok asks first); Gemini,
which has no such link, and Copilot, whose stopped working, are left out.
The field names the assistant ("Ask Lumo…"). A device that sent questions
to Claude goes on asking an assistant, now Lumo. Connecting Claude to read
studysets stays as it was.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-14 13:19:51 +02:00
/// No description provided for @assistantSwitchToAssistant.
feat(assistant): search the web too, and choose what goes along
The assistant's field now has a switch at its start: a question goes to
Claude or into a web search, and the field remembers which. A web search
opens the chosen engine's results: on Android the device's own web
search by default (ACTION_WEB_SEARCH, answered by its default browser or
search app; Brave Search where nothing answers), elsewhere Brave Search
by default; Settings › Assistant › Search engine offers Ecosia,
DuckDuckGo, Startpage, Qwant, Google, Bing, or one's own address with
%s where the question goes.
Above the field, chips offer what the question is about: a card's
front, back, each hint and its explanation, the studyset's name and
description. None is chosen, so only what the person picks goes along,
each part cut at 600 characters; Claude gets the question and a compact
line per chosen part, a web search the question and the chosen text,
never the studyset's name. A chosen chip alone is enough to send. The
old sentences around every question (where it was asked, the connector)
are gone: they cost tokens for nothing unless the studyset is chosen.
On a studyset's screen the button now stands above the bottom bar, as in
the Library: unfolded inside the bar, its chips lay outside the bar's
bounds, where taps don't land.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MMm7usAo6q9ZZRoPkWjoxi
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-10 14:41:36 +02:00
///
/// In en, this message translates to:
feat(assistant): a question opens in the AI assistant chosen, Proton's Lumo by default
Questions asked from the app went to Claude alone. Settings › Assistant
now chooses where they open (decided 2026-10-14): Lumo, the default, by
its guest chat with the question in the address's fragment (Lumo's
`prefill`, which needs no account and reaches no server; the address for
one's own account loses the question when nobody is signed in), ChatGPT,
Claude, Mistral, Perplexity, Duck.ai, Grok, Kagi Assistant, HuggingChat,
or an address of one's own with %s. Each link fills the question in for
the person to send (Perplexity answers at once, Grok asks first); Gemini,
which has no such link, and Copilot, whose stopped working, are left out.
The field names the assistant ("Ask Lumo…"). A device that sent questions
to Claude goes on asking an assistant, now Lumo. Connecting Claude to read
studysets stays as it was.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-14 13:19:51 +02:00
/// **'Ask {assistant} instead'**
String assistantSwitchToAssistant ( String assistant ) ;
feat(assistant): search the web too, and choose what goes along
The assistant's field now has a switch at its start: a question goes to
Claude or into a web search, and the field remembers which. A web search
opens the chosen engine's results: on Android the device's own web
search by default (ACTION_WEB_SEARCH, answered by its default browser or
search app; Brave Search where nothing answers), elsewhere Brave Search
by default; Settings › Assistant › Search engine offers Ecosia,
DuckDuckGo, Startpage, Qwant, Google, Bing, or one's own address with
%s where the question goes.
Above the field, chips offer what the question is about: a card's
front, back, each hint and its explanation, the studyset's name and
description. None is chosen, so only what the person picks goes along,
each part cut at 600 characters; Claude gets the question and a compact
line per chosen part, a web search the question and the chosen text,
never the studyset's name. A chosen chip alone is enough to send. The
old sentences around every question (where it was asked, the connector)
are gone: they cost tokens for nothing unless the studyset is chosen.
On a studyset's screen the button now stands above the bottom bar, as in
the Library: unfolded inside the bar, its chips lay outside the bar's
bounds, where taps don't land.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MMm7usAo6q9ZZRoPkWjoxi
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-10 14:41:36 +02:00
/// No description provided for @assistantHintWeb.
///
/// In en, this message translates to:
/// **'Search the web…'**
String get assistantHintWeb ;
/// No description provided for @assistantSendWeb.
///
/// In en, this message translates to:
/// **'Search'**
String get assistantSendWeb ;
/// No description provided for @assistantSearchFailed.
///
/// In en, this message translates to:
/// **'The search couldn\'t be opened.'**
String get assistantSearchFailed ;
/// No description provided for @settingsSearchEngine.
///
/// In en, this message translates to:
/// **'Search engine'**
String get settingsSearchEngine ;
feat(assistant): a question opens in the AI assistant chosen, Proton's Lumo by default
Questions asked from the app went to Claude alone. Settings › Assistant
now chooses where they open (decided 2026-10-14): Lumo, the default, by
its guest chat with the question in the address's fragment (Lumo's
`prefill`, which needs no account and reaches no server; the address for
one's own account loses the question when nobody is signed in), ChatGPT,
Claude, Mistral, Perplexity, Duck.ai, Grok, Kagi Assistant, HuggingChat,
or an address of one's own with %s. Each link fills the question in for
the person to send (Perplexity answers at once, Grok asks first); Gemini,
which has no such link, and Copilot, whose stopped working, are left out.
The field names the assistant ("Ask Lumo…"). A device that sent questions
to Claude goes on asking an assistant, now Lumo. Connecting Claude to read
studysets stays as it was.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-14 13:19:51 +02:00
/// No description provided for @settingsAiAssistant.
///
/// In en, this message translates to:
/// **'AI assistant'**
String get settingsAiAssistant ;
/// No description provided for @aiAssistantCustomHint.
///
/// In en, this message translates to:
/// **'https://example.org/chat?q=%s'**
String get aiAssistantCustomHint ;
feat(assistant): search the web too, and choose what goes along
The assistant's field now has a switch at its start: a question goes to
Claude or into a web search, and the field remembers which. A web search
opens the chosen engine's results: on Android the device's own web
search by default (ACTION_WEB_SEARCH, answered by its default browser or
search app; Brave Search where nothing answers), elsewhere Brave Search
by default; Settings › Assistant › Search engine offers Ecosia,
DuckDuckGo, Startpage, Qwant, Google, Bing, or one's own address with
%s where the question goes.
Above the field, chips offer what the question is about: a card's
front, back, each hint and its explanation, the studyset's name and
description. None is chosen, so only what the person picks goes along,
each part cut at 600 characters; Claude gets the question and a compact
line per chosen part, a web search the question and the chosen text,
never the studyset's name. A chosen chip alone is enough to send. The
old sentences around every question (where it was asked, the connector)
are gone: they cost tokens for nothing unless the studyset is chosen.
On a studyset's screen the button now stands above the bottom bar, as in
the Library: unfolded inside the bar, its chips lay outside the bar's
bounds, where taps don't land.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MMm7usAo6q9ZZRoPkWjoxi
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-10 14:41:36 +02:00
/// No description provided for @searchEngineDevice.
///
/// In en, this message translates to:
/// **'The device\'s search'**
String get searchEngineDevice ;
/// No description provided for @searchEngineCustom.
///
/// In en, this message translates to:
/// **'Your own address'**
String get searchEngineCustom ;
/// No description provided for @searchEngineCustomHint.
///
/// In en, this message translates to:
/// **'https://example.org/search?q=%s'**
String get searchEngineCustomHint ;
/// No description provided for @searchEngineCustomInvalid.
///
/// In en, this message translates to:
/// **'An http or https address with %s where the question goes.'**
String get searchEngineCustomInvalid ;
/// No description provided for @searchEngineCustomSave.
///
/// In en, this message translates to:
/// **'Use'**
String get searchEngineCustomSave ;
feat(studyset): Material, Progress and Study plan in place of the tabs
The studyset's four tabs become three parts, as Vaia has them, chosen
with a switch in the header that ticks as the navigation bar does: below
expanded windows in a line of its own under the title, above the path
and Topics; from there on at the end of the path's line, which the
topics leave free there.
- Material: the flashcards, notes and files one below the other, the
first three of each with Show all; Browse, the tag filter (where all
cards show, or while they are narrowed) and Reorder in each section's
header; an outlined box where a section is empty.
- Progress, once anything in the studyset was studied (a card rated, a
path finished): how much is learnt (mature cards), how many studied
cards are due today, and how long it was studied this week on this
device, with a line of the seven days (StudysetActivityCubit, from the
reviews' durations).
- Study plan: a ring of the paths finished, Next up (study the cards due,
go on with the first path not finished), then the paths.
It opens on the part it was left on; a tab kept before counts as its
part. Opened for the first time, it opens on the study plan while a path
is unfinished, else on the material. Material added lands in its part.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MMm7usAo6q9ZZRoPkWjoxi
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-12 13:48:11 +02:00
/// No description provided for @studysetPartMaterial.
///
/// In en, this message translates to:
/// **'Material'**
String get studysetPartMaterial ;
/// No description provided for @studysetPartProgress.
///
/// In en, this message translates to:
/// **'Progress'**
String get studysetPartProgress ;
/// No description provided for @studysetPartPlan.
///
/// In en, this message translates to:
/// **'Study plan'**
String get studysetPartPlan ;
/// No description provided for @progressLearned.
///
/// In en, this message translates to:
/// **'Learned'**
String get progressLearned ;
/// No description provided for @progressLearnedInfo.
///
/// In en, this message translates to:
/// **'Cards you will remember for three weeks or more.'**
String get progressLearnedInfo ;
/// No description provided for @progressOfStudyset.
///
/// In en, this message translates to:
/// **'of this studyset'**
String get progressOfStudyset ;
/// No description provided for @progressCardsLearned.
///
/// In en, this message translates to:
/// **'{count, plural, =1{{learned} of 1 card} other{{learned} of {count} cards}}'**
String progressCardsLearned ( int learned , int count ) ;
/// No description provided for @progressToReview.
///
/// In en, this message translates to:
/// **'To review'**
String get progressToReview ;
/// No description provided for @progressToReviewInfo.
///
/// In en, this message translates to:
/// **'Cards studied before that are due again today.'**
String get progressToReviewInfo ;
/// No description provided for @progressDueToday.
///
/// In en, this message translates to:
/// **'{count, plural, =1{card due today} other{cards due today}}'**
String progressDueToday ( int count ) ;
/// No description provided for @progressStudyTime.
///
/// In en, this message translates to:
/// **'Study time'**
String get progressStudyTime ;
/// No description provided for @progressStudyTimeInfo.
///
/// In en, this message translates to:
feat(profile): statistics beside the profile, a review counting a minute at most
The Profile tab has two tabs, Profile and Statistics, the latter for
anonymous people too: the time studied in all, per day and the most in
one day, and by days, weeks or months the study time and the ratings as
bars, a tap on a bar saying what it holds. The app draws the charts
itself, as the Progress part's sparkline, rather than taking a chart
package.
A review's time was however long its card was shown: one left on the
screen over lunch counted as an hour of study. It now counts at most a
minute, as Anki does by default, here and in a studyset's Progress; the
stored durations stay as they were.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MMm7usAo6q9ZZRoPkWjoxi
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-12 18:56:40 +02:00
/// **'How long you studied this studyset\'s flashcards in the last seven days, each card counted for at most a minute.'**
feat(studyset): Material, Progress and Study plan in place of the tabs
The studyset's four tabs become three parts, as Vaia has them, chosen
with a switch in the header that ticks as the navigation bar does: below
expanded windows in a line of its own under the title, above the path
and Topics; from there on at the end of the path's line, which the
topics leave free there.
- Material: the flashcards, notes and files one below the other, the
first three of each with Show all; Browse, the tag filter (where all
cards show, or while they are narrowed) and Reorder in each section's
header; an outlined box where a section is empty.
- Progress, once anything in the studyset was studied (a card rated, a
path finished): how much is learnt (mature cards), how many studied
cards are due today, and how long it was studied this week on this
device, with a line of the seven days (StudysetActivityCubit, from the
reviews' durations).
- Study plan: a ring of the paths finished, Next up (study the cards due,
go on with the first path not finished), then the paths.
It opens on the part it was left on; a tab kept before counts as its
part. Opened for the first time, it opens on the study plan while a path
is unfinished, else on the material. Material added lands in its part.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MMm7usAo6q9ZZRoPkWjoxi
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-12 13:48:11 +02:00
String get progressStudyTimeInfo ;
/// No description provided for @progressThisWeek.
///
/// In en, this message translates to:
/// **'this week'**
String get progressThisWeek ;
/// No description provided for @progressMinutes.
///
/// In en, this message translates to:
/// **'{minutes} min'**
String progressMinutes ( int minutes ) ;
/// No description provided for @progressHoursMinutes.
///
/// In en, this message translates to:
/// **'{hours} h {minutes} min'**
String progressHoursMinutes ( int hours , int minutes ) ;
feat: a round in the order cards come, shared daily limits, and a start screen
Studying:
- A round is one list in the order its cards come: those done, the one
shown, those to come. A card due again within the day goes back in
after 4 more cards when rated Bad, at the end otherwise, so the pills
above the card follow it; the buttons say "in 4 cards", "at the end",
"now" (round_order.dart).
- The studied studyset's daily limits hold for everything inside it
together, as Anki's do for subdecks: twenty topics start 20 new cards
a day in all, not 20 each.
- The study header's studyset name shows whole on hover or long press.
- The assistant's button sits at the lower left: in the Library across
from Create studyset, in a studyset above its bar, and higher while
studying, above the buttons that answer the card.
A studyset:
- Progress also counts the learning paths' steps done and lists the
PDFs not read to their end, begun first, each opening the reader,
across its topics (MaterialProgressCubit).
- Next up starts with studying for the exam while one is ahead: its
reviews due and the new cards its pace leaves for the day.
- Learning paths tagged with a level count towards its automatic exam
plan and offer it, as cards do.
- A step's first heading equal to its title is dropped on import and
hidden in paths imported before.
Signing out, or a session the server ended, leads to a start screen
(GitHub, email, without an account, another instance) rather than a new
anonymous account; only the very first start makes one by itself.
Settings names the kind of account and its instance.
Also: the Watch screen reads the cards once for held words and their
progress; its ±10 s buttons tick as the navigation bar does; a map card
keeps its view as its caption changes and brings a picked region
towards the middle; an empty or failed library can be pulled to sync.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-17 00:39:34 +02:00
/// No description provided for @progressPathsInfo.
///
/// In en, this message translates to:
/// **'The steps of this studyset\'s learning paths, and its topics\', that you have done.'**
String get progressPathsInfo ;
/// No description provided for @progressStepsDone.
///
/// In en, this message translates to:
/// **'{total, plural, =1{of 1 step done} other{of {total} steps done}}'**
String progressStepsDone ( int total ) ;
/// No description provided for @progressReading.
///
/// In en, this message translates to:
/// **'Reading'**
String get progressReading ;
/// No description provided for @progressReadingInfo.
///
/// In en, this message translates to:
/// **'The PDFs of this studyset and its topics not read to their last page yet. Tap one to read on.'**
String get progressReadingInfo ;
/// No description provided for @progressPdfPage.
///
/// In en, this message translates to:
/// **'Page {page} of {count}'**
String progressPdfPage ( int page , int count ) ;
/// No description provided for @progressPdfNotBegun.
///
/// In en, this message translates to:
/// **'Not begun yet'**
String get progressPdfNotBegun ;
/// No description provided for @progressPdfsAllRead.
///
/// In en, this message translates to:
/// **'{count, plural, =1{The PDF is read to its end.} other{All {count} PDFs are read to their end.}}'**
String progressPdfsAllRead ( int count ) ;
/// No description provided for @progressShowAllPdfs.
///
/// In en, this message translates to:
/// **'Show all {count}'**
String progressShowAllPdfs ( int count ) ;
/// No description provided for @progressShowFewerPdfs.
///
/// In en, this message translates to:
/// **'Show fewer'**
String get progressShowFewerPdfs ;
feat(studyset): Material, Progress and Study plan in place of the tabs
The studyset's four tabs become three parts, as Vaia has them, chosen
with a switch in the header that ticks as the navigation bar does: below
expanded windows in a line of its own under the title, above the path
and Topics; from there on at the end of the path's line, which the
topics leave free there.
- Material: the flashcards, notes and files one below the other, the
first three of each with Show all; Browse, the tag filter (where all
cards show, or while they are narrowed) and Reorder in each section's
header; an outlined box where a section is empty.
- Progress, once anything in the studyset was studied (a card rated, a
path finished): how much is learnt (mature cards), how many studied
cards are due today, and how long it was studied this week on this
device, with a line of the seven days (StudysetActivityCubit, from the
reviews' durations).
- Study plan: a ring of the paths finished, Next up (study the cards due,
go on with the first path not finished), then the paths.
It opens on the part it was left on; a tab kept before counts as its
part. Opened for the first time, it opens on the study plan while a path
is unfinished, else on the material. Material added lands in its part.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MMm7usAo6q9ZZRoPkWjoxi
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-12 13:48:11 +02:00
/// No description provided for @planPathsDone.
///
/// In en, this message translates to:
/// **'{count, plural, =1{{done} of 1 path finished} other{{done} of {count} paths finished}}'**
String planPathsDone ( int done , int count ) ;
/// No description provided for @planNoPaths.
///
/// In en, this message translates to:
/// **'No learning paths yet'**
String get planNoPaths ;
/// No description provided for @planNextUp.
///
/// In en, this message translates to:
/// **'Next up'**
String get planNextUp ;
/// No description provided for @planReview.
///
/// In en, this message translates to:
/// **'{count, plural, =1{Study 1 flashcard} other{Study {count} flashcards}}'**
String planReview ( int count ) ;
feat: a round in the order cards come, shared daily limits, and a start screen
Studying:
- A round is one list in the order its cards come: those done, the one
shown, those to come. A card due again within the day goes back in
after 4 more cards when rated Bad, at the end otherwise, so the pills
above the card follow it; the buttons say "in 4 cards", "at the end",
"now" (round_order.dart).
- The studied studyset's daily limits hold for everything inside it
together, as Anki's do for subdecks: twenty topics start 20 new cards
a day in all, not 20 each.
- The study header's studyset name shows whole on hover or long press.
- The assistant's button sits at the lower left: in the Library across
from Create studyset, in a studyset above its bar, and higher while
studying, above the buttons that answer the card.
A studyset:
- Progress also counts the learning paths' steps done and lists the
PDFs not read to their end, begun first, each opening the reader,
across its topics (MaterialProgressCubit).
- Next up starts with studying for the exam while one is ahead: its
reviews due and the new cards its pace leaves for the day.
- Learning paths tagged with a level count towards its automatic exam
plan and offer it, as cards do.
- A step's first heading equal to its title is dropped on import and
hidden in paths imported before.
Signing out, or a session the server ended, leads to a start screen
(GitHub, email, without an account, another instance) rather than a new
anonymous account; only the very first start makes one by itself.
Settings names the kind of account and its instance.
Also: the Watch screen reads the cards once for held words and their
progress; its ±10 s buttons tick as the navigation bar does; a map card
keeps its view as its caption changes and brings a picked region
towards the middle; an empty or failed library can be pulled to sync.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-17 00:39:34 +02:00
/// No description provided for @planStudyForExam.
///
/// In en, this message translates to:
/// **'{count, plural, =1{Study 1 flashcard for {exam}} other{Study {count} flashcards for {exam}}}'**
String planStudyForExam ( int count , String exam ) ;
feat(studyset): Material, Progress and Study plan in place of the tabs
The studyset's four tabs become three parts, as Vaia has them, chosen
with a switch in the header that ticks as the navigation bar does: below
expanded windows in a line of its own under the title, above the path
and Topics; from there on at the end of the path's line, which the
topics leave free there.
- Material: the flashcards, notes and files one below the other, the
first three of each with Show all; Browse, the tag filter (where all
cards show, or while they are narrowed) and Reorder in each section's
header; an outlined box where a section is empty.
- Progress, once anything in the studyset was studied (a card rated, a
path finished): how much is learnt (mature cards), how many studied
cards are due today, and how long it was studied this week on this
device, with a line of the seven days (StudysetActivityCubit, from the
reviews' durations).
- Study plan: a ring of the paths finished, Next up (study the cards due,
go on with the first path not finished), then the paths.
It opens on the part it was left on; a tab kept before counts as its
part. Opened for the first time, it opens on the study plan while a path
is unfinished, else on the material. Material added lands in its part.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MMm7usAo6q9ZZRoPkWjoxi
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-12 13:48:11 +02:00
/// No description provided for @planContinue.
///
/// In en, this message translates to:
/// **'Continue “{title}”'**
String planContinue ( String title ) ;
feat(studyset): learning paths are material, the study plan refers to them
A learning path is material too, as its flashcards, notes and files are,
and the study plan only refers to it (decided 2026-10-13), as it will to
an exam's date. Material's tabs are now Paths, Flashcards, Notes and
Files, the paths first as the way through the rest, with Reorder beside
them; opened here for the first time, Material shows the first tab with
something in it. Study plan keeps how many paths are finished and Next
up, which names the next step of each path under way ("Step 3 of 6: が"),
or offers to start the first path not finished when none is, and lists
how far every path has come, its steps done in a bar, each opening the
path. The path list's state now knows each path's steps done and next
step.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-14 06:38:33 +02:00
/// No description provided for @planStart.
///
/// In en, this message translates to:
/// **'Start “{title}”'**
String planStart ( String title ) ;
/// No description provided for @planStepOf.
///
/// In en, this message translates to:
/// **'Step {number} of {total}'**
String planStepOf ( int number , int total ) ;
/// No description provided for @planStepOfTitled.
///
/// In en, this message translates to:
/// **'Step {number} of {total}: {title}'**
String planStepOfTitled ( int number , int total , String title ) ;
/// No description provided for @planStepsDone.
///
/// In en, this message translates to:
/// **'{total, plural, =1{{done} of 1 step} other{{done} of {total} steps}}'**
String planStepsDone ( int done , int total ) ;
feat(studyset): Material, Progress and Study plan in place of the tabs
The studyset's four tabs become three parts, as Vaia has them, chosen
with a switch in the header that ticks as the navigation bar does: below
expanded windows in a line of its own under the title, above the path
and Topics; from there on at the end of the path's line, which the
topics leave free there.
- Material: the flashcards, notes and files one below the other, the
first three of each with Show all; Browse, the tag filter (where all
cards show, or while they are narrowed) and Reorder in each section's
header; an outlined box where a section is empty.
- Progress, once anything in the studyset was studied (a card rated, a
path finished): how much is learnt (mature cards), how many studied
cards are due today, and how long it was studied this week on this
device, with a line of the seven days (StudysetActivityCubit, from the
reviews' durations).
- Study plan: a ring of the paths finished, Next up (study the cards due,
go on with the first path not finished), then the paths.
It opens on the part it was left on; a tab kept before counts as its
part. Opened for the first time, it opens on the study plan while a path
is unfinished, else on the material. Material added lands in its part.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MMm7usAo6q9ZZRoPkWjoxi
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-12 13:48:11 +02:00
/// No description provided for @planNothing.
///
/// In en, this message translates to:
/// **'Nothing due right now.'**
String get planNothing ;
/// No description provided for @planLearningPaths.
///
/// In en, this message translates to:
/// **'Learning paths'**
String get planLearningPaths ;
feat(jellyfin): watch from one's own Jellyfin server and learn the words of its subtitles
Animelon's way of learning from video, from the person's own Jellyfin
server: connected per device (Settings, or a studyset's Add material ›
Watch from Jellyfin) with a name and password or a Quick Connect code,
over HTTPS only (plain HTTP only to a server on the same machine); the
token stays in the system's keyring. Shows, films and videos tied to a
studyset follow the person in their settings as the server's and the
item's ids alone (settings schema 9), named by the server when shown.
The Watch screen plays the video (video_player on Android, media_kit's
libmpv on Linux and Windows) with the line in the studyset's front
language cut into the dictionary's words with furigana, and the back
language's line beneath; a word tapped pauses the video and becomes a
card with the line and its translation as its first hint, tagged with
the show. The video's words are listed, most frequent first. The sound is
the track in the studyset's language, whichever the file marks default,
and the menu switches it; the subtitles read are a whole track, never a
forced one (which only covers what the sound doesn't say), and captions
for the hard of hearing only without plain ones. The line and its
translation take the video's width in a box of a fixed height, so the
video keeps its size; in the dark theme the video's colours glow around
it, as YouTube's ambient mode, and the words' panel takes the colour of
the episode's picture under the pointer.
A line is cut into words with one request: DictionaryEndpoint.withKeys
looks up every stretch of it that may be a word, as it stands and as
every word it may be a conjugation of, in the new dictionary_key table
(one row per key, indexed; migration dictionary-keys adds the table and
its index, nothing else), which the loader fills beside the entries.
The instance gets the text of each line looked up and keeps none of it,
as the Watch screen says.
Linux builds need libmpv-dev, and the app links libmpv: the snap stages
Ubuntu's libmpv2, makes the update-alternatives links for BLAS and LAPACK
that ffmpeg's speech recognition needs (staging never runs install
scripts), and removes what the GNOME runtime and core24 already hold, as
older copies of cairo, pango and librsvg hid the runtime's from its GTK.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-13 11:31:37 +02:00
feat(studyset): prepare for an exam, with plans, a date and the pace to it
A studyset plans for exams: each plan names what an exam covers, the
topics it takes whole (with their flashcards, learning paths and PDFs)
and the tags whose flashcards it takes from anywhere in the studyset.
The author's plans are published with the studyset (payload schema 7,
`examPlans`); copying a release and taking a newer one point their
topics at the copy's own studysets. Plans are also offered from level
tags: JLPT N5 to N1, HSK 1 to 7-9 and KRDict's levels, each taking the
levels below it, as the exams do.
The person chooses a plan and the exam's day (`exam`), which stays
theirs: the server leaves it out of a release, as a PDF's deadline.
The study plan then shows the days left, how many of the plan's
flashcards were started, its paths' steps done and its PDFs' pages
read, and the pace that gets everything new learnt a week before the
exam (by the exam when two weeks or fewer are left): new flashcards a
day, steps a week, pages a day. Study for the exam studies only what
the plan covers, its new cards at that pace (counted as the day began)
and every review due.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-14 14:26:08 +02:00
/// No description provided for @examPrepare.
///
/// In en, this message translates to:
/// **'Prepare for an exam'**
String get examPrepare ;
/// No description provided for @examPrepareHint.
///
/// In en, this message translates to:
/// **'Choose what it covers and its date, and get a pace that gets you there.'**
String get examPrepareHint ;
/// No description provided for @examChange.
///
/// In en, this message translates to:
/// **'Change the exam'**
String get examChange ;
/// No description provided for @examDaysLeft.
///
/// In en, this message translates to:
/// **'{count, plural, =0{Today, {date}} =1{Tomorrow, {date}} other{In {count} days, {date}}}'**
String examDaysLeft ( int count , String date ) ;
/// No description provided for @examPast.
///
/// In en, this message translates to:
/// **'It was on {date}'**
String examPast ( String date ) ;
/// No description provided for @examPlanGone.
///
/// In en, this message translates to:
/// **'This exam plan is gone'**
String get examPlanGone ;
/// No description provided for @examCardsStarted.
///
/// In en, this message translates to:
/// **'{total, plural, =1{{started} of 1 flashcard started} other{{started} of {total} flashcards started}}'**
String examCardsStarted ( int started , int total ) ;
/// No description provided for @examPagesRead.
///
/// In en, this message translates to:
/// **'{total, plural, =1{{read} of 1 page read} other{{read} of {total} pages read}}'**
String examPagesRead ( int read , int total ) ;
/// No description provided for @examPaceTitle.
///
/// In en, this message translates to:
/// **'Your pace'**
String get examPaceTitle ;
/// No description provided for @examPaceNew.
///
/// In en, this message translates to:
/// **'{count, plural, =1{1 new flashcard a day} other{{count} new flashcards a day}}'**
String examPaceNew ( int count ) ;
/// No description provided for @examPaceSteps.
///
/// In en, this message translates to:
/// **'{count, plural, =1{1 step a week} other{{count} steps a week}}'**
String examPaceSteps ( int count ) ;
/// No description provided for @examPacePages.
///
/// In en, this message translates to:
/// **'{count, plural, =1{1 page a day} other{{count} pages a day}}'**
String examPacePages ( int count ) ;
/// No description provided for @examPaceDone.
///
/// In en, this message translates to:
/// **'Everything new is learnt: review until the exam.'**
String get examPaceDone ;
/// No description provided for @examStudy.
///
/// In en, this message translates to:
/// **'Study for the exam'**
String get examStudy ;
/// No description provided for @examSheetHelp.
///
/// In en, this message translates to:
fix(studyset): an exam's pace keeps its rate, and plans hold what they show
The pace kept a week for reviews only while more than two weeks were
left: followed from further off, it halved its rate at two weeks and
brought new cards until the exam. It now keeps a week, or half the days
left when the exam is closer, and the sheet says so.
An exam whose plan is gone said "Today", as the days left came from the
pace, which needs the plan; they are now counted from the date alone.
Once the exam's day was over, the study plan still asked for everything
new at once and Study for the exam queued every new card; now neither
shows, and a session for it studies as usual.
Cards tagged HSK 3 alone offered HSK 1 and 2, plans covering nothing;
a level offers a plan only when it covers some tag present. The plan
editor kept topics deleted or moved out since and tags spelled in
another case, chosen but not shown, and ticking such a tag added it
twice; once it has read what a plan may cover, only that stays chosen,
each tag as the flashcards spell it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-14 17:58:52 +02:00
/// **'Choose what the exam covers and when it is. Everything new is then spread so that it is learnt a week before the exam, or halfway there when it is closer, leaving the rest for reviews. Only you see the date.'**
feat(studyset): prepare for an exam, with plans, a date and the pace to it
A studyset plans for exams: each plan names what an exam covers, the
topics it takes whole (with their flashcards, learning paths and PDFs)
and the tags whose flashcards it takes from anywhere in the studyset.
The author's plans are published with the studyset (payload schema 7,
`examPlans`); copying a release and taking a newer one point their
topics at the copy's own studysets. Plans are also offered from level
tags: JLPT N5 to N1, HSK 1 to 7-9 and KRDict's levels, each taking the
levels below it, as the exams do.
The person chooses a plan and the exam's day (`exam`), which stays
theirs: the server leaves it out of a release, as a PDF's deadline.
The study plan then shows the days left, how many of the plan's
flashcards were started, its paths' steps done and its PDFs' pages
read, and the pace that gets everything new learnt a week before the
exam (by the exam when two weeks or fewer are left): new flashcards a
day, steps a week, pages a day. Study for the exam studies only what
the plan covers, its new cards at that pace (counted as the day began)
and every review due.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-14 14:26:08 +02:00
String get examSheetHelp ;
/// No description provided for @examDate.
///
/// In en, this message translates to:
/// **'Exam date'**
String get examDate ;
/// No description provided for @examSheetPlans.
///
/// In en, this message translates to:
/// **'This studyset’ s exam plans'**
String get examSheetPlans ;
/// No description provided for @examSheetFromTags.
///
/// In en, this message translates to:
/// **'From the level tags'**
String get examSheetFromTags ;
/// No description provided for @examSheetNoPlans.
///
/// In en, this message translates to:
/// **'No exam plans yet. Make one from this studyset’ s topics and tags.'**
String get examSheetNoPlans ;
/// No description provided for @examRemove.
///
/// In en, this message translates to:
/// **'Stop preparing'**
String get examRemove ;
/// No description provided for @examPlanTopicCount.
///
/// In en, this message translates to:
/// **'{count, plural, =1{1 topic} other{{count} topics}}'**
String examPlanTopicCount ( int count ) ;
/// No description provided for @examPlanNew.
///
/// In en, this message translates to:
/// **'New exam plan'**
String get examPlanNew ;
/// No description provided for @examPlanEdit.
///
/// In en, this message translates to:
/// **'Edit exam plan'**
String get examPlanEdit ;
/// No description provided for @examPlanHelp.
///
/// In en, this message translates to:
/// **'An exam plan names what an exam covers: whole topics, with their flashcards, learning paths and PDFs, and flashcards with certain tags. It is published with the studyset.'**
String get examPlanHelp ;
/// No description provided for @examPlanName.
///
/// In en, this message translates to:
/// **'Name'**
String get examPlanName ;
/// No description provided for @examPlanNameHint.
///
/// In en, this message translates to:
/// **'e.g. Final exam'**
String get examPlanNameHint ;
/// No description provided for @examPlanTopics.
///
/// In en, this message translates to:
/// **'Topics it covers whole'**
String get examPlanTopics ;
/// No description provided for @examPlanTags.
///
/// In en, this message translates to:
/// **'Flashcards with these tags'**
String get examPlanTags ;
/// No description provided for @examPlanEmpty.
///
/// In en, this message translates to:
/// **'This studyset has no topics or tagged flashcards to choose from yet.'**
String get examPlanEmpty ;
/// No description provided for @examPlanDelete.
///
/// In en, this message translates to:
/// **'Delete plan'**
String get examPlanDelete ;
feat(jellyfin): watch from one's own Jellyfin server and learn the words of its subtitles
Animelon's way of learning from video, from the person's own Jellyfin
server: connected per device (Settings, or a studyset's Add material ›
Watch from Jellyfin) with a name and password or a Quick Connect code,
over HTTPS only (plain HTTP only to a server on the same machine); the
token stays in the system's keyring. Shows, films and videos tied to a
studyset follow the person in their settings as the server's and the
item's ids alone (settings schema 9), named by the server when shown.
The Watch screen plays the video (video_player on Android, media_kit's
libmpv on Linux and Windows) with the line in the studyset's front
language cut into the dictionary's words with furigana, and the back
language's line beneath; a word tapped pauses the video and becomes a
card with the line and its translation as its first hint, tagged with
the show. The video's words are listed, most frequent first. The sound is
the track in the studyset's language, whichever the file marks default,
and the menu switches it; the subtitles read are a whole track, never a
forced one (which only covers what the sound doesn't say), and captions
for the hard of hearing only without plain ones. The line and its
translation take the video's width in a box of a fixed height, so the
video keeps its size; in the dark theme the video's colours glow around
it, as YouTube's ambient mode, and the words' panel takes the colour of
the episode's picture under the pointer.
A line is cut into words with one request: DictionaryEndpoint.withKeys
looks up every stretch of it that may be a word, as it stands and as
every word it may be a conjugation of, in the new dictionary_key table
(one row per key, indexed; migration dictionary-keys adds the table and
its index, nothing else), which the loader fills beside the entries.
The instance gets the text of each line looked up and keeps none of it,
as the Watch screen says.
Linux builds need libmpv-dev, and the app links libmpv: the snap stages
Ubuntu's libmpv2, makes the update-alternatives links for BLAS and LAPACK
that ffmpeg's speech recognition needs (staging never runs install
scripts), and removes what the GNOME runtime and core24 already hold, as
older copies of cairo, pango and librsvg hid the runtime's from its GTK.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-13 11:31:37 +02:00
/// No description provided for @jellyfinTitle.
///
/// In en, this message translates to:
/// **'Jellyfin'**
String get jellyfinTitle ;
/// No description provided for @jellyfinSubtitle.
///
/// In en, this message translates to:
feat(jellyfin): From Jellyfin once signed in, the panel in the glow's colour, adding the words listed
Add material's entry is now From Jellyfin (Aus Jellyfin), with a play
icon rather than a TV, as it plays music too, and shows only once the
device is signed in to a server in Settings, as the assistant shows only
once switched on; Settings says where it then is. The screen it opens is
called Jellyfin, and its search finds music too.
The words' panel takes the glow's colour as the scene changes: each copy
the glow takes (every 3 s) gives its average, no lighter than 35 % so the
text stays readable, as strong as the glow and fading into the next as
it does. In the light theme, which has no glow, the panel is tinted from
it while the pointer is over it. What lights up in the panel takes a
scheme made from the same colour; the episode's thumbnail is no longer
used.
While the studyset holds none of the video's words, the study button
adds the words listed (Add 11 words, as the filter shows them), tagged
as a word added alone is; afterwards it studies them, and its menu adds
the new ones listed.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-13 21:03:37 +02:00
/// **'Watch shows and films and listen to music from your own Jellyfin server, and learn the words of their subtitles and lyrics. Signed in, a studyset\'s Add material offers From Jellyfin.'**
feat(jellyfin): watch from one's own Jellyfin server and learn the words of its subtitles
Animelon's way of learning from video, from the person's own Jellyfin
server: connected per device (Settings, or a studyset's Add material ›
Watch from Jellyfin) with a name and password or a Quick Connect code,
over HTTPS only (plain HTTP only to a server on the same machine); the
token stays in the system's keyring. Shows, films and videos tied to a
studyset follow the person in their settings as the server's and the
item's ids alone (settings schema 9), named by the server when shown.
The Watch screen plays the video (video_player on Android, media_kit's
libmpv on Linux and Windows) with the line in the studyset's front
language cut into the dictionary's words with furigana, and the back
language's line beneath; a word tapped pauses the video and becomes a
card with the line and its translation as its first hint, tagged with
the show. The video's words are listed, most frequent first. The sound is
the track in the studyset's language, whichever the file marks default,
and the menu switches it; the subtitles read are a whole track, never a
forced one (which only covers what the sound doesn't say), and captions
for the hard of hearing only without plain ones. The line and its
translation take the video's width in a box of a fixed height, so the
video keeps its size; in the dark theme the video's colours glow around
it, as YouTube's ambient mode, and the words' panel takes the colour of
the episode's picture under the pointer.
A line is cut into words with one request: DictionaryEndpoint.withKeys
looks up every stretch of it that may be a word, as it stands and as
every word it may be a conjugation of, in the new dictionary_key table
(one row per key, indexed; migration dictionary-keys adds the table and
its index, nothing else), which the loader fills beside the entries.
The instance gets the text of each line looked up and keeps none of it,
as the Watch screen says.
Linux builds need libmpv-dev, and the app links libmpv: the snap stages
Ubuntu's libmpv2, makes the update-alternatives links for BLAS and LAPACK
that ffmpeg's speech recognition needs (staging never runs install
scripts), and removes what the GNOME runtime and core24 already hold, as
older copies of cairo, pango and librsvg hid the runtime's from its GTK.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-13 11:31:37 +02:00
String get jellyfinSubtitle ;
/// No description provided for @jellyfinPrivacy.
///
/// In en, this message translates to:
feat(dictionary): Japanese cut into words and looked up on the device
Once the person agrees, the device holds MeCab's IPADIC and the
instance's JMdict and cuts lines as Jisho.org does, so their text never
leaves it (ADR 0012). MeCab comes through mecab_for_dart, whose build
hook compiles it; its tokens are grouped by Ve's rules, written anew in
Dart, with に kept a word of its own. Each word is found as it stands or
as a form of a word, else by its dictionary form, else as the longest
words it holds from token to token; longer words over token boundaries
are offered as before.
The curator packs Ubuntu's compiled IPADIC beside the dictionary files
(curate ipadic, and every update run). The server exports its JMdict
from its tables, so the device finds the same keys and keeps the same
versions, and serves both at /device-dictionaries/, named in
dictionary.deviceFiles('ja') with their hashes; Caddy forwards the path.
The app checks each file against its hash, unpacks IPADIC, builds a
SQLite database from the export on an isolate, and asks the instance
for newer files every 30 days, downloading only what changed. The Watch
screen offers the download once per device with its size (34 MB, some
150 MB once set up); Settings downloads and removes them. Without them,
or should cutting on the device fail, lines go to the instance as
before. MeCab's, IPADIC's and Ve's licences join the licence list.
Every build now needs a C++ compiler; mecab_for_dart holds code_assets
at 1.x.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-15 21:55:00 +02:00
/// **'Your server\'s address and your sign-in stay on this device, and what you tie to a studyset follows you as ids, never titles. To find the words of a line, the app asks the instance about its text, which it answers and doesn\'t keep, unless Japanese\'s dictionaries are on this device (Settings): then it looks them up here. Words you add are tagged with where you met them, kept with your flashcards like any tag.'**
feat(jellyfin): watch from one's own Jellyfin server and learn the words of its subtitles
Animelon's way of learning from video, from the person's own Jellyfin
server: connected per device (Settings, or a studyset's Add material ›
Watch from Jellyfin) with a name and password or a Quick Connect code,
over HTTPS only (plain HTTP only to a server on the same machine); the
token stays in the system's keyring. Shows, films and videos tied to a
studyset follow the person in their settings as the server's and the
item's ids alone (settings schema 9), named by the server when shown.
The Watch screen plays the video (video_player on Android, media_kit's
libmpv on Linux and Windows) with the line in the studyset's front
language cut into the dictionary's words with furigana, and the back
language's line beneath; a word tapped pauses the video and becomes a
card with the line and its translation as its first hint, tagged with
the show. The video's words are listed, most frequent first. The sound is
the track in the studyset's language, whichever the file marks default,
and the menu switches it; the subtitles read are a whole track, never a
forced one (which only covers what the sound doesn't say), and captions
for the hard of hearing only without plain ones. The line and its
translation take the video's width in a box of a fixed height, so the
video keeps its size; in the dark theme the video's colours glow around
it, as YouTube's ambient mode, and the words' panel takes the colour of
the episode's picture under the pointer.
A line is cut into words with one request: DictionaryEndpoint.withKeys
looks up every stretch of it that may be a word, as it stands and as
every word it may be a conjugation of, in the new dictionary_key table
(one row per key, indexed; migration dictionary-keys adds the table and
its index, nothing else), which the loader fills beside the entries.
The instance gets the text of each line looked up and keeps none of it,
as the Watch screen says.
Linux builds need libmpv-dev, and the app links libmpv: the snap stages
Ubuntu's libmpv2, makes the update-alternatives links for BLAS and LAPACK
that ffmpeg's speech recognition needs (staging never runs install
scripts), and removes what the GNOME runtime and core24 already hold, as
older copies of cairo, pango and librsvg hid the runtime's from its GTK.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-13 11:31:37 +02:00
String get jellyfinPrivacy ;
feat(dictionary): Japanese cut into words and looked up on the device
Once the person agrees, the device holds MeCab's IPADIC and the
instance's JMdict and cuts lines as Jisho.org does, so their text never
leaves it (ADR 0012). MeCab comes through mecab_for_dart, whose build
hook compiles it; its tokens are grouped by Ve's rules, written anew in
Dart, with に kept a word of its own. Each word is found as it stands or
as a form of a word, else by its dictionary form, else as the longest
words it holds from token to token; longer words over token boundaries
are offered as before.
The curator packs Ubuntu's compiled IPADIC beside the dictionary files
(curate ipadic, and every update run). The server exports its JMdict
from its tables, so the device finds the same keys and keeps the same
versions, and serves both at /device-dictionaries/, named in
dictionary.deviceFiles('ja') with their hashes; Caddy forwards the path.
The app checks each file against its hash, unpacks IPADIC, builds a
SQLite database from the export on an isolate, and asks the instance
for newer files every 30 days, downloading only what changed. The Watch
screen offers the download once per device with its size (34 MB, some
150 MB once set up); Settings downloads and removes them. Without them,
or should cutting on the device fail, lines go to the instance as
before. MeCab's, IPADIC's and Ve's licences join the licence list.
Every build now needs a C++ compiler; mecab_for_dart holds code_assets
at 1.x.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-15 21:55:00 +02:00
/// No description provided for @deviceDictionaryOfferTitle.
///
/// In en, this message translates to:
/// **'Look words up on this device?'**
String get deviceDictionaryOfferTitle ;
/// No description provided for @deviceDictionaryOfferBody.
///
/// In en, this message translates to:
/// **'Download Japanese\'s dictionaries once ({size}, more once set up) to cut lines into words here, as Jisho.org does. Their text then never leaves this device.'**
String deviceDictionaryOfferBody ( String size ) ;
/// No description provided for @deviceDictionaryOfferDownload.
///
/// In en, this message translates to:
/// **'Download'**
String get deviceDictionaryOfferDownload ;
/// No description provided for @deviceDictionaryOfferLater.
///
/// In en, this message translates to:
/// **'Not now'**
String get deviceDictionaryOfferLater ;
/// No description provided for @deviceDictionaryDownloading.
///
/// In en, this message translates to:
/// **'Downloading: {received} of {total}'**
String deviceDictionaryDownloading ( String received , String total ) ;
/// No description provided for @deviceDictionaryPreparing.
///
/// In en, this message translates to:
/// **'Setting them up…'**
String get deviceDictionaryPreparing ;
/// No description provided for @deviceDictionaryReady.
///
/// In en, this message translates to:
/// **'Words are now looked up on this device.'**
String get deviceDictionaryReady ;
/// No description provided for @deviceDictionaryFailed.
///
/// In en, this message translates to:
/// **'Couldn\'t download the dictionaries. Try again in Settings.'**
String get deviceDictionaryFailed ;
/// No description provided for @settingsDeviceDictionaryTitle.
///
/// In en, this message translates to:
/// **'Japanese on this device'**
String get settingsDeviceDictionaryTitle ;
/// No description provided for @settingsDeviceDictionaryAbout.
///
/// In en, this message translates to:
/// **'With Japanese\'s dictionaries on this device, the lines of videos and songs are cut into words here, as Jisho.org does, and their text never leaves it. Without them, the app asks the instance, which answers and keeps nothing.'**
String get settingsDeviceDictionaryAbout ;
/// No description provided for @settingsDeviceDictionaryHeld.
///
/// In en, this message translates to:
/// **'On this device: {size}'**
String settingsDeviceDictionaryHeld ( String size ) ;
/// No description provided for @settingsDeviceDictionaryDownload.
///
/// In en, this message translates to:
/// **'Download ({size})'**
String settingsDeviceDictionaryDownload ( String size ) ;
/// No description provided for @settingsDeviceDictionaryRemove.
///
/// In en, this message translates to:
/// **'Remove'**
String get settingsDeviceDictionaryRemove ;
/// No description provided for @settingsDeviceDictionaryFailed.
///
/// In en, this message translates to:
/// **'The download failed.'**
String get settingsDeviceDictionaryFailed ;
feat(jellyfin): watch from one's own Jellyfin server and learn the words of its subtitles
Animelon's way of learning from video, from the person's own Jellyfin
server: connected per device (Settings, or a studyset's Add material ›
Watch from Jellyfin) with a name and password or a Quick Connect code,
over HTTPS only (plain HTTP only to a server on the same machine); the
token stays in the system's keyring. Shows, films and videos tied to a
studyset follow the person in their settings as the server's and the
item's ids alone (settings schema 9), named by the server when shown.
The Watch screen plays the video (video_player on Android, media_kit's
libmpv on Linux and Windows) with the line in the studyset's front
language cut into the dictionary's words with furigana, and the back
language's line beneath; a word tapped pauses the video and becomes a
card with the line and its translation as its first hint, tagged with
the show. The video's words are listed, most frequent first. The sound is
the track in the studyset's language, whichever the file marks default,
and the menu switches it; the subtitles read are a whole track, never a
forced one (which only covers what the sound doesn't say), and captions
for the hard of hearing only without plain ones. The line and its
translation take the video's width in a box of a fixed height, so the
video keeps its size; in the dark theme the video's colours glow around
it, as YouTube's ambient mode, and the words' panel takes the colour of
the episode's picture under the pointer.
A line is cut into words with one request: DictionaryEndpoint.withKeys
looks up every stretch of it that may be a word, as it stands and as
every word it may be a conjugation of, in the new dictionary_key table
(one row per key, indexed; migration dictionary-keys adds the table and
its index, nothing else), which the loader fills beside the entries.
The instance gets the text of each line looked up and keeps none of it,
as the Watch screen says.
Linux builds need libmpv-dev, and the app links libmpv: the snap stages
Ubuntu's libmpv2, makes the update-alternatives links for BLAS and LAPACK
that ffmpeg's speech recognition needs (staging never runs install
scripts), and removes what the GNOME runtime and core24 already hold, as
older copies of cairo, pango and librsvg hid the runtime's from its GTK.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-13 11:31:37 +02:00
/// No description provided for @jellyfinSignedInAs.
///
/// In en, this message translates to:
/// **'{user} on {server}'**
String jellyfinSignedInAs ( String user , String server ) ;
/// No description provided for @jellyfinSignOut.
///
/// In en, this message translates to:
/// **'Sign out of Jellyfin'**
String get jellyfinSignOut ;
/// No description provided for @jellyfinAddress.
///
/// In en, this message translates to:
/// **'Server address'**
String get jellyfinAddress ;
/// No description provided for @jellyfinAddressHint.
///
/// In en, this message translates to:
/// **'jellyfin.example.org'**
String get jellyfinAddressHint ;
/// No description provided for @jellyfinFind.
///
/// In en, this message translates to:
/// **'Continue'**
String get jellyfinFind ;
/// No description provided for @jellyfinUser.
///
/// In en, this message translates to:
/// **'User name'**
String get jellyfinUser ;
feat(jellyfin): the instance suggests a Jellyfin server, signed in to with a tap
An instance now names a Jellyfin server for its people to learn from
(instance.info().jellyfinUrl): its operator's JELLYFIN_URL (`none`: none),
else https://jellyfin. on the domain registered for its public host, as
learn.halkyon.eu suggests jellyfin.halkyon.eu, once that answers as a
Jellyfin server. The registered domain comes from Mozilla's Public Suffix
List, kept in the server's data folder, so an instance at example.co.uk
never names someone else's jellyfin.co.uk. The instance looks as it
starts and again hourly when asked; the app never reaches for a guessed
address.
In the app, Add material offers From Jellyfin as soon as the instance
suggests a server, before anyone signed in to one. The sign-in fills the
server in and finds it by itself, and lists the people the server shows
on its login screen: one without a password, as a demo's, signs in with
a tap; one with a password is named in the form, which asks for it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-13 23:25:33 +02:00
/// No description provided for @jellyfinPublicUsers.
///
/// In en, this message translates to:
/// **'On this server'**
String get jellyfinPublicUsers ;
feat(jellyfin): watch from one's own Jellyfin server and learn the words of its subtitles
Animelon's way of learning from video, from the person's own Jellyfin
server: connected per device (Settings, or a studyset's Add material ›
Watch from Jellyfin) with a name and password or a Quick Connect code,
over HTTPS only (plain HTTP only to a server on the same machine); the
token stays in the system's keyring. Shows, films and videos tied to a
studyset follow the person in their settings as the server's and the
item's ids alone (settings schema 9), named by the server when shown.
The Watch screen plays the video (video_player on Android, media_kit's
libmpv on Linux and Windows) with the line in the studyset's front
language cut into the dictionary's words with furigana, and the back
language's line beneath; a word tapped pauses the video and becomes a
card with the line and its translation as its first hint, tagged with
the show. The video's words are listed, most frequent first. The sound is
the track in the studyset's language, whichever the file marks default,
and the menu switches it; the subtitles read are a whole track, never a
forced one (which only covers what the sound doesn't say), and captions
for the hard of hearing only without plain ones. The line and its
translation take the video's width in a box of a fixed height, so the
video keeps its size; in the dark theme the video's colours glow around
it, as YouTube's ambient mode, and the words' panel takes the colour of
the episode's picture under the pointer.
A line is cut into words with one request: DictionaryEndpoint.withKeys
looks up every stretch of it that may be a word, as it stands and as
every word it may be a conjugation of, in the new dictionary_key table
(one row per key, indexed; migration dictionary-keys adds the table and
its index, nothing else), which the loader fills beside the entries.
The instance gets the text of each line looked up and keeps none of it,
as the Watch screen says.
Linux builds need libmpv-dev, and the app links libmpv: the snap stages
Ubuntu's libmpv2, makes the update-alternatives links for BLAS and LAPACK
that ffmpeg's speech recognition needs (staging never runs install
scripts), and removes what the GNOME runtime and core24 already hold, as
older copies of cairo, pango and librsvg hid the runtime's from its GTK.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-13 11:31:37 +02:00
/// No description provided for @jellyfinPassword.
///
/// In en, this message translates to:
/// **'Password'**
String get jellyfinPassword ;
2026-10-14 01:52:42 +02:00
/// No description provided for @jellyfinPasswordNone.
///
/// In en, this message translates to:
/// **'Leave empty if the account has none'**
String get jellyfinPasswordNone ;
feat(jellyfin): watch from one's own Jellyfin server and learn the words of its subtitles
Animelon's way of learning from video, from the person's own Jellyfin
server: connected per device (Settings, or a studyset's Add material ›
Watch from Jellyfin) with a name and password or a Quick Connect code,
over HTTPS only (plain HTTP only to a server on the same machine); the
token stays in the system's keyring. Shows, films and videos tied to a
studyset follow the person in their settings as the server's and the
item's ids alone (settings schema 9), named by the server when shown.
The Watch screen plays the video (video_player on Android, media_kit's
libmpv on Linux and Windows) with the line in the studyset's front
language cut into the dictionary's words with furigana, and the back
language's line beneath; a word tapped pauses the video and becomes a
card with the line and its translation as its first hint, tagged with
the show. The video's words are listed, most frequent first. The sound is
the track in the studyset's language, whichever the file marks default,
and the menu switches it; the subtitles read are a whole track, never a
forced one (which only covers what the sound doesn't say), and captions
for the hard of hearing only without plain ones. The line and its
translation take the video's width in a box of a fixed height, so the
video keeps its size; in the dark theme the video's colours glow around
it, as YouTube's ambient mode, and the words' panel takes the colour of
the episode's picture under the pointer.
A line is cut into words with one request: DictionaryEndpoint.withKeys
looks up every stretch of it that may be a word, as it stands and as
every word it may be a conjugation of, in the new dictionary_key table
(one row per key, indexed; migration dictionary-keys adds the table and
its index, nothing else), which the loader fills beside the entries.
The instance gets the text of each line looked up and keeps none of it,
as the Watch screen says.
Linux builds need libmpv-dev, and the app links libmpv: the snap stages
Ubuntu's libmpv2, makes the update-alternatives links for BLAS and LAPACK
that ffmpeg's speech recognition needs (staging never runs install
scripts), and removes what the GNOME runtime and core24 already hold, as
older copies of cairo, pango and librsvg hid the runtime's from its GTK.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-13 11:31:37 +02:00
/// No description provided for @jellyfinSignIn.
///
/// In en, this message translates to:
/// **'Sign in'**
String get jellyfinSignIn ;
/// No description provided for @jellyfinQuickConnect.
///
/// In en, this message translates to:
/// **'Sign in with a code'**
String get jellyfinQuickConnect ;
/// No description provided for @jellyfinQuickConnectCode.
///
/// In en, this message translates to:
/// **'Enter this code under Quick Connect in a Jellyfin app you\'re signed in to:'**
String get jellyfinQuickConnectCode ;
/// No description provided for @jellyfinQuickConnectWaiting.
///
/// In en, this message translates to:
/// **'Waiting for the code to be approved…'**
String get jellyfinQuickConnectWaiting ;
/// No description provided for @jellyfinOtherServer.
///
/// In en, this message translates to:
/// **'Another server'**
String get jellyfinOtherServer ;
/// No description provided for @jellyfinUnreachable.
///
/// In en, this message translates to:
/// **'No server answered at that address over HTTPS.'**
String get jellyfinUnreachable ;
/// No description provided for @jellyfinNotEncrypted.
///
/// In en, this message translates to:
/// **'That address isn\'t encrypted, and your password would travel in the clear. Use the server\'s HTTPS address.'**
String get jellyfinNotEncrypted ;
/// No description provided for @jellyfinNotJellyfin.
///
/// In en, this message translates to:
/// **'That address isn\'t a Jellyfin server.'**
String get jellyfinNotJellyfin ;
/// No description provided for @jellyfinWrongCredentials.
///
/// In en, this message translates to:
/// **'The user name or password is wrong.'**
String get jellyfinWrongCredentials ;
/// No description provided for @jellyfinSignedOut.
///
/// In en, this message translates to:
/// **'You were signed out of Jellyfin. Sign in again.'**
String get jellyfinSignedOut ;
/// No description provided for @jellyfinFailed.
///
/// In en, this message translates to:
/// **'That didn\'t work on the Jellyfin server. Try again.'**
String get jellyfinFailed ;
/// No description provided for @studysetWatch.
///
/// In en, this message translates to:
feat(jellyfin): From Jellyfin once signed in, the panel in the glow's colour, adding the words listed
Add material's entry is now From Jellyfin (Aus Jellyfin), with a play
icon rather than a TV, as it plays music too, and shows only once the
device is signed in to a server in Settings, as the assistant shows only
once switched on; Settings says where it then is. The screen it opens is
called Jellyfin, and its search finds music too.
The words' panel takes the glow's colour as the scene changes: each copy
the glow takes (every 3 s) gives its average, no lighter than 35 % so the
text stays readable, as strong as the glow and fading into the next as
it does. In the light theme, which has no glow, the panel is tinted from
it while the pointer is over it. What lights up in the panel takes a
scheme made from the same colour; the episode's thumbnail is no longer
used.
While the studyset holds none of the video's words, the study button
adds the words listed (Add 11 words, as the filter shows them), tagged
as a word added alone is; afterwards it studies them, and its menu adds
the new ones listed.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-13 21:03:37 +02:00
/// **'From Jellyfin'**
feat(jellyfin): watch from one's own Jellyfin server and learn the words of its subtitles
Animelon's way of learning from video, from the person's own Jellyfin
server: connected per device (Settings, or a studyset's Add material ›
Watch from Jellyfin) with a name and password or a Quick Connect code,
over HTTPS only (plain HTTP only to a server on the same machine); the
token stays in the system's keyring. Shows, films and videos tied to a
studyset follow the person in their settings as the server's and the
item's ids alone (settings schema 9), named by the server when shown.
The Watch screen plays the video (video_player on Android, media_kit's
libmpv on Linux and Windows) with the line in the studyset's front
language cut into the dictionary's words with furigana, and the back
language's line beneath; a word tapped pauses the video and becomes a
card with the line and its translation as its first hint, tagged with
the show. The video's words are listed, most frequent first. The sound is
the track in the studyset's language, whichever the file marks default,
and the menu switches it; the subtitles read are a whole track, never a
forced one (which only covers what the sound doesn't say), and captions
for the hard of hearing only without plain ones. The line and its
translation take the video's width in a box of a fixed height, so the
video keeps its size; in the dark theme the video's colours glow around
it, as YouTube's ambient mode, and the words' panel takes the colour of
the episode's picture under the pointer.
A line is cut into words with one request: DictionaryEndpoint.withKeys
looks up every stretch of it that may be a word, as it stands and as
every word it may be a conjugation of, in the new dictionary_key table
(one row per key, indexed; migration dictionary-keys adds the table and
its index, nothing else), which the loader fills beside the entries.
The instance gets the text of each line looked up and keeps none of it,
as the Watch screen says.
Linux builds need libmpv-dev, and the app links libmpv: the snap stages
Ubuntu's libmpv2, makes the update-alternatives links for BLAS and LAPACK
that ffmpeg's speech recognition needs (staging never runs install
scripts), and removes what the GNOME runtime and core24 already hold, as
older copies of cairo, pango and librsvg hid the runtime's from its GTK.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-13 11:31:37 +02:00
String get studysetWatch ;
/// No description provided for @jellyfinBrowseTitle.
///
/// In en, this message translates to:
feat(jellyfin): From Jellyfin once signed in, the panel in the glow's colour, adding the words listed
Add material's entry is now From Jellyfin (Aus Jellyfin), with a play
icon rather than a TV, as it plays music too, and shows only once the
device is signed in to a server in Settings, as the assistant shows only
once switched on; Settings says where it then is. The screen it opens is
called Jellyfin, and its search finds music too.
The words' panel takes the glow's colour as the scene changes: each copy
the glow takes (every 3 s) gives its average, no lighter than 35 % so the
text stays readable, as strong as the glow and fading into the next as
it does. In the light theme, which has no glow, the panel is tinted from
it while the pointer is over it. What lights up in the panel takes a
scheme made from the same colour; the episode's thumbnail is no longer
used.
While the studyset holds none of the video's words, the study button
adds the words listed (Add 11 words, as the filter shows them), tagged
as a word added alone is; afterwards it studies them, and its menu adds
the new ones listed.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-13 21:03:37 +02:00
/// **'Jellyfin'**
feat(jellyfin): watch from one's own Jellyfin server and learn the words of its subtitles
Animelon's way of learning from video, from the person's own Jellyfin
server: connected per device (Settings, or a studyset's Add material ›
Watch from Jellyfin) with a name and password or a Quick Connect code,
over HTTPS only (plain HTTP only to a server on the same machine); the
token stays in the system's keyring. Shows, films and videos tied to a
studyset follow the person in their settings as the server's and the
item's ids alone (settings schema 9), named by the server when shown.
The Watch screen plays the video (video_player on Android, media_kit's
libmpv on Linux and Windows) with the line in the studyset's front
language cut into the dictionary's words with furigana, and the back
language's line beneath; a word tapped pauses the video and becomes a
card with the line and its translation as its first hint, tagged with
the show. The video's words are listed, most frequent first. The sound is
the track in the studyset's language, whichever the file marks default,
and the menu switches it; the subtitles read are a whole track, never a
forced one (which only covers what the sound doesn't say), and captions
for the hard of hearing only without plain ones. The line and its
translation take the video's width in a box of a fixed height, so the
video keeps its size; in the dark theme the video's colours glow around
it, as YouTube's ambient mode, and the words' panel takes the colour of
the episode's picture under the pointer.
A line is cut into words with one request: DictionaryEndpoint.withKeys
looks up every stretch of it that may be a word, as it stands and as
every word it may be a conjugation of, in the new dictionary_key table
(one row per key, indexed; migration dictionary-keys adds the table and
its index, nothing else), which the loader fills beside the entries.
The instance gets the text of each line looked up and keeps none of it,
as the Watch screen says.
Linux builds need libmpv-dev, and the app links libmpv: the snap stages
Ubuntu's libmpv2, makes the update-alternatives links for BLAS and LAPACK
that ffmpeg's speech recognition needs (staging never runs install
scripts), and removes what the GNOME runtime and core24 already hold, as
older copies of cairo, pango and librsvg hid the runtime's from its GTK.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-13 11:31:37 +02:00
String get jellyfinBrowseTitle ;
/// No description provided for @jellyfinTied.
///
/// In en, this message translates to:
/// **'Tied to this studyset'**
String get jellyfinTied ;
/// No description provided for @jellyfinLibraries.
///
/// In en, this message translates to:
/// **'Libraries'**
String get jellyfinLibraries ;
/// No description provided for @jellyfinSearchHint.
///
/// In en, this message translates to:
feat(jellyfin): From Jellyfin once signed in, the panel in the glow's colour, adding the words listed
Add material's entry is now From Jellyfin (Aus Jellyfin), with a play
icon rather than a TV, as it plays music too, and shows only once the
device is signed in to a server in Settings, as the assistant shows only
once switched on; Settings says where it then is. The screen it opens is
called Jellyfin, and its search finds music too.
The words' panel takes the glow's colour as the scene changes: each copy
the glow takes (every 3 s) gives its average, no lighter than 35 % so the
text stays readable, as strong as the glow and fading into the next as
it does. In the light theme, which has no glow, the panel is tinted from
it while the pointer is over it. What lights up in the panel takes a
scheme made from the same colour; the episode's thumbnail is no longer
used.
While the studyset holds none of the video's words, the study button
adds the words listed (Add 11 words, as the filter shows them), tagged
as a word added alone is; afterwards it studies them, and its menu adds
the new ones listed.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-13 21:03:37 +02:00
/// **'Search shows, films and music'**
feat(jellyfin): watch from one's own Jellyfin server and learn the words of its subtitles
Animelon's way of learning from video, from the person's own Jellyfin
server: connected per device (Settings, or a studyset's Add material ›
Watch from Jellyfin) with a name and password or a Quick Connect code,
over HTTPS only (plain HTTP only to a server on the same machine); the
token stays in the system's keyring. Shows, films and videos tied to a
studyset follow the person in their settings as the server's and the
item's ids alone (settings schema 9), named by the server when shown.
The Watch screen plays the video (video_player on Android, media_kit's
libmpv on Linux and Windows) with the line in the studyset's front
language cut into the dictionary's words with furigana, and the back
language's line beneath; a word tapped pauses the video and becomes a
card with the line and its translation as its first hint, tagged with
the show. The video's words are listed, most frequent first. The sound is
the track in the studyset's language, whichever the file marks default,
and the menu switches it; the subtitles read are a whole track, never a
forced one (which only covers what the sound doesn't say), and captions
for the hard of hearing only without plain ones. The line and its
translation take the video's width in a box of a fixed height, so the
video keeps its size; in the dark theme the video's colours glow around
it, as YouTube's ambient mode, and the words' panel takes the colour of
the episode's picture under the pointer.
A line is cut into words with one request: DictionaryEndpoint.withKeys
looks up every stretch of it that may be a word, as it stands and as
every word it may be a conjugation of, in the new dictionary_key table
(one row per key, indexed; migration dictionary-keys adds the table and
its index, nothing else), which the loader fills beside the entries.
The instance gets the text of each line looked up and keeps none of it,
as the Watch screen says.
Linux builds need libmpv-dev, and the app links libmpv: the snap stages
Ubuntu's libmpv2, makes the update-alternatives links for BLAS and LAPACK
that ffmpeg's speech recognition needs (staging never runs install
scripts), and removes what the GNOME runtime and core24 already hold, as
older copies of cairo, pango and librsvg hid the runtime's from its GTK.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-13 11:31:37 +02:00
String get jellyfinSearchHint ;
/// No description provided for @jellyfinTie.
///
/// In en, this message translates to:
/// **'Tie to this studyset'**
String get jellyfinTie ;
/// No description provided for @jellyfinUntie.
///
/// In en, this message translates to:
/// **'Untie from this studyset'**
String get jellyfinUntie ;
/// No description provided for @jellyfinEmpty.
///
/// In en, this message translates to:
/// **'Nothing here'**
String get jellyfinEmpty ;
/// No description provided for @jellyfinEpisode.
///
/// In en, this message translates to:
/// **'S{season} E{episode}'**
String jellyfinEpisode ( int season , int episode ) ;
/// No description provided for @jellyfinNoPlayer.
///
/// In en, this message translates to:
/// **'Videos can\'t play on this device.'**
String get jellyfinNoPlayer ;
/// No description provided for @jellyfinNoPlayerLinux.
///
/// In en, this message translates to:
/// **'Videos can\'t play on this device: playing them needs libmpv (the package libmpv2).'**
String get jellyfinNoPlayerLinux ;
/// No description provided for @watchNoSubtitles.
///
/// In en, this message translates to:
/// **'This video has no subtitles in the studyset\'s language as text, so there is nothing to look up.'**
String get watchNoSubtitles ;
/// No description provided for @watchSubtitles.
///
/// In en, this message translates to:
/// **'Subtitles'**
String get watchSubtitles ;
/// No description provided for @watchTranslation.
///
/// In en, this message translates to:
/// **'Translation'**
String get watchTranslation ;
/// No description provided for @watchAudio.
///
/// In en, this message translates to:
/// **'Audio'**
String get watchAudio ;
feat(jellyfin): the player as players are, with the settings under a cog
The Watch screen's player takes the shape people know from players,
YouTube's first: the picture set in from every side with rounded
corners, so the glow of its colours shows all around it, and over it
play or pause in the middle with 10 seconds back and on beside it, and
along its foot where the video is (0:05 ──── 23:47) and a cog. The
controls fade out while it plays and the pointer rests, and come back as
it moves, on a tap with a finger, on a jump and while paused. The cog
opens a panel inside the picture, so nothing runs off the window: Pause
after each line, the subtitles, the translation and the sound, each
track named by its language in the app's words, and the keyboard
shortcuts. The buttons for the line before, again and the next sit
beside the subtitles they act on, no longer looking like a player's
skip to the next video. Keys are YouTube's (Space or K, J and L, the
arrows, 0 to 9, Home, End) with Shift and the arrows for lines, R, P and
Esc; ? lists them.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-13 16:51:53 +02:00
/// No description provided for @watchBack10.
feat(jellyfin): watch from one's own Jellyfin server and learn the words of its subtitles
Animelon's way of learning from video, from the person's own Jellyfin
server: connected per device (Settings, or a studyset's Add material ›
Watch from Jellyfin) with a name and password or a Quick Connect code,
over HTTPS only (plain HTTP only to a server on the same machine); the
token stays in the system's keyring. Shows, films and videos tied to a
studyset follow the person in their settings as the server's and the
item's ids alone (settings schema 9), named by the server when shown.
The Watch screen plays the video (video_player on Android, media_kit's
libmpv on Linux and Windows) with the line in the studyset's front
language cut into the dictionary's words with furigana, and the back
language's line beneath; a word tapped pauses the video and becomes a
card with the line and its translation as its first hint, tagged with
the show. The video's words are listed, most frequent first. The sound is
the track in the studyset's language, whichever the file marks default,
and the menu switches it; the subtitles read are a whole track, never a
forced one (which only covers what the sound doesn't say), and captions
for the hard of hearing only without plain ones. The line and its
translation take the video's width in a box of a fixed height, so the
video keeps its size; in the dark theme the video's colours glow around
it, as YouTube's ambient mode, and the words' panel takes the colour of
the episode's picture under the pointer.
A line is cut into words with one request: DictionaryEndpoint.withKeys
looks up every stretch of it that may be a word, as it stands and as
every word it may be a conjugation of, in the new dictionary_key table
(one row per key, indexed; migration dictionary-keys adds the table and
its index, nothing else), which the loader fills beside the entries.
The instance gets the text of each line looked up and keeps none of it,
as the Watch screen says.
Linux builds need libmpv-dev, and the app links libmpv: the snap stages
Ubuntu's libmpv2, makes the update-alternatives links for BLAS and LAPACK
that ffmpeg's speech recognition needs (staging never runs install
scripts), and removes what the GNOME runtime and core24 already hold, as
older copies of cairo, pango and librsvg hid the runtime's from its GTK.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-13 11:31:37 +02:00
///
/// In en, this message translates to:
feat(jellyfin): the player as players are, with the settings under a cog
The Watch screen's player takes the shape people know from players,
YouTube's first: the picture set in from every side with rounded
corners, so the glow of its colours shows all around it, and over it
play or pause in the middle with 10 seconds back and on beside it, and
along its foot where the video is (0:05 ──── 23:47) and a cog. The
controls fade out while it plays and the pointer rests, and come back as
it moves, on a tap with a finger, on a jump and while paused. The cog
opens a panel inside the picture, so nothing runs off the window: Pause
after each line, the subtitles, the translation and the sound, each
track named by its language in the app's words, and the keyboard
shortcuts. The buttons for the line before, again and the next sit
beside the subtitles they act on, no longer looking like a player's
skip to the next video. Keys are YouTube's (Space or K, J and L, the
arrows, 0 to 9, Home, End) with Shift and the arrows for lines, R, P and
Esc; ? lists them.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-13 16:51:53 +02:00
/// **'10 seconds back'**
String get watchBack10 ;
/// No description provided for @watchForward10.
///
/// In en, this message translates to:
/// **'10 seconds on'**
String get watchForward10 ;
/// No description provided for @watchSettings.
///
/// In en, this message translates to:
/// **'Settings'**
String get watchSettings ;
/// No description provided for @watchShortcuts.
///
/// In en, this message translates to:
/// **'Keyboard shortcuts'**
String get watchShortcuts ;
/// No description provided for @watchTrackForced.
///
/// In en, this message translates to:
/// **'forced'**
String get watchTrackForced ;
/// No description provided for @watchTrackCaptions.
///
/// In en, this message translates to:
/// **'for the hard of hearing'**
String get watchTrackCaptions ;
/// No description provided for @watchKeyPlayPause.
///
/// In en, this message translates to:
/// **'Play or pause'**
String get watchKeyPlayPause ;
/// No description provided for @watchKeyFive.
///
/// In en, this message translates to:
/// **'5 seconds back or on'**
String get watchKeyFive ;
/// No description provided for @watchKeyTen.
///
/// In en, this message translates to:
/// **'10 seconds back or on'**
String get watchKeyTen ;
/// No description provided for @watchKeyLines.
///
/// In en, this message translates to:
/// **'Previous or next line'**
String get watchKeyLines ;
/// No description provided for @watchKeyJump.
///
/// In en, this message translates to:
/// **'To 0 %, 10 %, … 90 % of the video'**
String get watchKeyJump ;
/// No description provided for @watchKeyStartEnd.
///
/// In en, this message translates to:
/// **'To the start or the end'**
String get watchKeyStartEnd ;
/// No description provided for @watchKeyClose.
///
/// In en, this message translates to:
/// **'Close the word or the settings'**
String get watchKeyClose ;
/// No description provided for @watchKeyShortcuts.
///
/// In en, this message translates to:
/// **'These shortcuts'**
String get watchKeyShortcuts ;
/// No description provided for @watchKeySpace.
///
/// In en, this message translates to:
/// **'Space'**
String get watchKeySpace ;
/// No description provided for @watchKeyShift.
///
/// In en, this message translates to:
/// **'Shift'**
String get watchKeyShift ;
/// No description provided for @watchKeyHome.
///
/// In en, this message translates to:
/// **'Home'**
String get watchKeyHome ;
/// No description provided for @watchKeyEnd.
///
/// In en, this message translates to:
/// **'End'**
String get watchKeyEnd ;
feat(jellyfin): watch from one's own Jellyfin server and learn the words of its subtitles
Animelon's way of learning from video, from the person's own Jellyfin
server: connected per device (Settings, or a studyset's Add material ›
Watch from Jellyfin) with a name and password or a Quick Connect code,
over HTTPS only (plain HTTP only to a server on the same machine); the
token stays in the system's keyring. Shows, films and videos tied to a
studyset follow the person in their settings as the server's and the
item's ids alone (settings schema 9), named by the server when shown.
The Watch screen plays the video (video_player on Android, media_kit's
libmpv on Linux and Windows) with the line in the studyset's front
language cut into the dictionary's words with furigana, and the back
language's line beneath; a word tapped pauses the video and becomes a
card with the line and its translation as its first hint, tagged with
the show. The video's words are listed, most frequent first. The sound is
the track in the studyset's language, whichever the file marks default,
and the menu switches it; the subtitles read are a whole track, never a
forced one (which only covers what the sound doesn't say), and captions
for the hard of hearing only without plain ones. The line and its
translation take the video's width in a box of a fixed height, so the
video keeps its size; in the dark theme the video's colours glow around
it, as YouTube's ambient mode, and the words' panel takes the colour of
the episode's picture under the pointer.
A line is cut into words with one request: DictionaryEndpoint.withKeys
looks up every stretch of it that may be a word, as it stands and as
every word it may be a conjugation of, in the new dictionary_key table
(one row per key, indexed; migration dictionary-keys adds the table and
its index, nothing else), which the loader fills beside the entries.
The instance gets the text of each line looked up and keeps none of it,
as the Watch screen says.
Linux builds need libmpv-dev, and the app links libmpv: the snap stages
Ubuntu's libmpv2, makes the update-alternatives links for BLAS and LAPACK
that ffmpeg's speech recognition needs (staging never runs install
scripts), and removes what the GNOME runtime and core24 already hold, as
older copies of cairo, pango and librsvg hid the runtime's from its GTK.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-13 11:31:37 +02:00
feat(jellyfin): the words' panel unfolds senses, shows progress, filters
The panel beside the video lists each entry's senses one to a line, as
Jisho does (and as every dictionary list in the app now does), folded
to two lines; an entry unfolds over the list, not in it, after a moment
under the pointer or on a tap, in the video's colour, so nothing moves
under the pointer and words of many senses (上げる has 25) crowd out
nothing. Each entry links to the word on Jisho.org, opened only when
tapped. A word the studyset holds shows how far its card is learnt:
New, Learning, Due, or Learned with when it comes again. The video's
words go through a filter in place of New only: new to the studyset,
held or all, JLPT levels, and word classes. The dictionary's sources
stay in the panel's lower right corner, and the player's controls hide
after 2 seconds rather than 3.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-13 18:43:06 +02:00
/// No description provided for @watchProgressNew.
///
/// In en, this message translates to:
/// **'New'**
String get watchProgressNew ;
/// No description provided for @watchProgressLearning.
///
/// In en, this message translates to:
/// **'Learning'**
String get watchProgressLearning ;
/// No description provided for @watchProgressDue.
///
/// In en, this message translates to:
/// **'Due'**
String get watchProgressDue ;
/// No description provided for @watchProgressLearned.
///
/// In en, this message translates to:
/// **'Learned · {days, plural, =0{again today} =1{again tomorrow} other{again in {days} days}}'**
String watchProgressLearned ( int days ) ;
feat(dictionary): German, Greek, English, Chinese and Korean words
A studyset in German, Greek, English, Chinese or Korean finds its
dictionary as one in Japanese does (ADR 0008), and its subtitles are cut
into words by its language (`WordCutting`): Chinese by its longest
words as they stand, the longer words over each cut offered as for
Japanese; Korean at its spaces, a word the dictionary lacks as it stands
taken without the particle it ends with (집에서 is 집, 아이 stays a
child); every other language at its spaces, a word written exactly so
first (the noun Essen before the verb essen). A studyset finds the
dictionary of its language tag, else of its language alone (zh for
zh-Hans).
A Wiktionary word shows its genders, principal forms, IPA, romanisation
and Hanja; a German or Greek noun of one gender goes onto its card with
its article (das Haus, το σπίτι); its tag is its word class, named in
full (Noun, Adjective, Preposition), as Wiktionary's adj and adv would
read as JMdict's codes. Articles, pronouns, prepositions and
conjunctions are left out of a video's words. Chinese words show their
pinyin over each character on the Watch screen, carry their HSK level
as a tag (HSK-1 … HSK-7-9), and the dictionary adds a level whole; the
Watch screen filters by HSK levels for Chinese, JLPT's for Japanese, and
by none for a language without. A reading is typed in a session only
when it is kana, so pinyin isn't. Words are looked up on the web on
Jisho in Japanese, on Wiktionary at the language's section otherwise,
and a search's hint speaks of kana and romaji only in Japanese.
`DICTIONARY_LANGUAGES` in deploy/.env names the languages an instance
keeps (unset: de,el,en,ko,zh; empty: none).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-14 07:45:15 +02:00
/// No description provided for @watchLookUpOn.
feat(jellyfin): the words' panel unfolds senses, shows progress, filters
The panel beside the video lists each entry's senses one to a line, as
Jisho does (and as every dictionary list in the app now does), folded
to two lines; an entry unfolds over the list, not in it, after a moment
under the pointer or on a tap, in the video's colour, so nothing moves
under the pointer and words of many senses (上げる has 25) crowd out
nothing. Each entry links to the word on Jisho.org, opened only when
tapped. A word the studyset holds shows how far its card is learnt:
New, Learning, Due, or Learned with when it comes again. The video's
words go through a filter in place of New only: new to the studyset,
held or all, JLPT levels, and word classes. The dictionary's sources
stay in the panel's lower right corner, and the player's controls hide
after 2 seconds rather than 3.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-13 18:43:06 +02:00
///
/// In en, this message translates to:
feat(dictionary): German, Greek, English, Chinese and Korean words
A studyset in German, Greek, English, Chinese or Korean finds its
dictionary as one in Japanese does (ADR 0008), and its subtitles are cut
into words by its language (`WordCutting`): Chinese by its longest
words as they stand, the longer words over each cut offered as for
Japanese; Korean at its spaces, a word the dictionary lacks as it stands
taken without the particle it ends with (집에서 is 집, 아이 stays a
child); every other language at its spaces, a word written exactly so
first (the noun Essen before the verb essen). A studyset finds the
dictionary of its language tag, else of its language alone (zh for
zh-Hans).
A Wiktionary word shows its genders, principal forms, IPA, romanisation
and Hanja; a German or Greek noun of one gender goes onto its card with
its article (das Haus, το σπίτι); its tag is its word class, named in
full (Noun, Adjective, Preposition), as Wiktionary's adj and adv would
read as JMdict's codes. Articles, pronouns, prepositions and
conjunctions are left out of a video's words. Chinese words show their
pinyin over each character on the Watch screen, carry their HSK level
as a tag (HSK-1 … HSK-7-9), and the dictionary adds a level whole; the
Watch screen filters by HSK levels for Chinese, JLPT's for Japanese, and
by none for a language without. A reading is typed in a session only
when it is kana, so pinyin isn't. Words are looked up on the web on
Jisho in Japanese, on Wiktionary at the language's section otherwise,
and a search's hint speaks of kana and romaji only in Japanese.
`DICTIONARY_LANGUAGES` in deploy/.env names the languages an instance
keeps (unset: de,el,en,ko,zh; empty: none).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-14 07:45:15 +02:00
/// **'Look up on {site}'**
String watchLookUpOn ( String site ) ;
feat(jellyfin): the words' panel unfolds senses, shows progress, filters
The panel beside the video lists each entry's senses one to a line, as
Jisho does (and as every dictionary list in the app now does), folded
to two lines; an entry unfolds over the list, not in it, after a moment
under the pointer or on a tap, in the video's colour, so nothing moves
under the pointer and words of many senses (上げる has 25) crowd out
nothing. Each entry links to the word on Jisho.org, opened only when
tapped. A word the studyset holds shows how far its card is learnt:
New, Learning, Due, or Learned with when it comes again. The video's
words go through a filter in place of New only: new to the studyset,
held or all, JLPT levels, and word classes. The dictionary's sources
stay in the panel's lower right corner, and the player's controls hide
after 2 seconds rather than 3.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-13 18:43:06 +02:00
fix(dictionary): a long kana word stands, and a word offers the longer ones
Cutting a line into words, kana that is no common word gave way to a
shorter common one whatever its length, so もうろく (耄碌, senility) was
taken as もう and ろく. The rule now holds only for kana of up to three
characters (が and まだ still win over がま, a cattail, and だ); a longer
kana word stands even when uncommon, as ichi.moe's segmenter weighs long
kana words by their length. And as no cut is always right, each word now
knows the longer words running over it into its neighbours, worked out
from the same lookup, which the Watch screen's panel offers beneath its
entries ("Longer words here"), each to add with the line.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-13 19:04:38 +02:00
/// No description provided for @watchWider.
///
/// In en, this message translates to:
/// **'Longer words here'**
String get watchWider ;
feat(jellyfin): songs and their lyrics, read as subtitles are
A music library's artists and albums open as shows and seasons do, and
can be tied to a studyset. A song plays as sound alone (`/Audio/{id}/
stream`, as stored) with its lyrics as Jellyfin keeps them (`/Audio/{id}/
Lyrics`: an LRC file beside it, or its tags), cut into the dictionary's
words as a video's subtitles are, with no translation. Timed lyrics
follow the song: the line sung large and bright among the others faded,
scrolled to as it comes, a line tapped played from its start; untimed
ones show whole. A song without lyrics shows greyed and doesn't open
(`HasLyrics`), as there is nothing in it to read. Its cover stands where
the picture would, glowing as a video does, and its words are tagged
with the artist, shown, and "artist · title", hidden.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-13 19:46:54 +02:00
/// No description provided for @watchWordsTitleSong.
///
/// In en, this message translates to:
/// **'Words in this song'**
String get watchWordsTitleSong ;
/// No description provided for @watchStudyWordsSong.
///
/// In en, this message translates to:
/// **'Study this song\'s words'**
String get watchStudyWordsSong ;
/// No description provided for @watchNoLyrics.
///
/// In en, this message translates to:
/// **'This song has no lyrics, so there is nothing to look up.'**
String get watchNoLyrics ;
/// No description provided for @jellyfinNoLyrics.
///
/// In en, this message translates to:
/// **'No lyrics'**
String get jellyfinNoLyrics ;
2026-10-13 19:32:37 +02:00
/// No description provided for @watchStudy.
///
/// In en, this message translates to:
/// **'Study'**
String get watchStudy ;
/// No description provided for @watchStudyWords.
///
/// In en, this message translates to:
/// **'Study this video\'s words'**
String get watchStudyWords ;
feat(jellyfin): From Jellyfin once signed in, the panel in the glow's colour, adding the words listed
Add material's entry is now From Jellyfin (Aus Jellyfin), with a play
icon rather than a TV, as it plays music too, and shows only once the
device is signed in to a server in Settings, as the assistant shows only
once switched on; Settings says where it then is. The screen it opens is
called Jellyfin, and its search finds music too.
The words' panel takes the glow's colour as the scene changes: each copy
the glow takes (every 3 s) gives its average, no lighter than 35 % so the
text stays readable, as strong as the glow and fading into the next as
it does. In the light theme, which has no glow, the panel is tinted from
it while the pointer is over it. What lights up in the panel takes a
scheme made from the same colour; the episode's thumbnail is no longer
used.
While the studyset holds none of the video's words, the study button
adds the words listed (Add 11 words, as the filter shows them), tagged
as a word added alone is; afterwards it studies them, and its menu adds
the new ones listed.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-13 21:03:37 +02:00
/// No description provided for @watchAddListed.
///
/// In en, this message translates to:
/// **'{count, plural, =1{Add 1 word} other{Add {count} words}}'**
String watchAddListed ( int count ) ;
/// No description provided for @watchAddListedHint.
///
/// In en, this message translates to:
/// **'Adds the words listed to the studyset, tagged with where they were met, to study them'**
String get watchAddListedHint ;
/// No description provided for @watchAddNothing.
///
/// In en, this message translates to:
/// **'No new words listed: change the filter'**
String get watchAddNothing ;
/// No description provided for @watchAddNew.
///
/// In en, this message translates to:
/// **'{count, plural, =1{Add the 1 new word listed} other{Add the {count} new words listed}}'**
String watchAddNew ( int count ) ;
/// No description provided for @watchAddedWords.
2026-10-13 19:32:37 +02:00
///
/// In en, this message translates to:
feat(jellyfin): From Jellyfin once signed in, the panel in the glow's colour, adding the words listed
Add material's entry is now From Jellyfin (Aus Jellyfin), with a play
icon rather than a TV, as it plays music too, and shows only once the
device is signed in to a server in Settings, as the assistant shows only
once switched on; Settings says where it then is. The screen it opens is
called Jellyfin, and its search finds music too.
The words' panel takes the glow's colour as the scene changes: each copy
the glow takes (every 3 s) gives its average, no lighter than 35 % so the
text stays readable, as strong as the glow and fading into the next as
it does. In the light theme, which has no glow, the panel is tinted from
it while the pointer is over it. What lights up in the panel takes a
scheme made from the same colour; the episode's thumbnail is no longer
used.
While the studyset holds none of the video's words, the study button
adds the words listed (Add 11 words, as the filter shows them), tagged
as a word added alone is; afterwards it studies them, and its menu adds
the new ones listed.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-13 21:03:37 +02:00
/// **'{count, plural, =0{No words added} =1{1 word added} other{{count} words added}}'**
String watchAddedWords ( int count ) ;
2026-10-13 19:32:37 +02:00
/// No description provided for @watchStudyScheduled.
///
/// In en, this message translates to:
/// **'Study, on their schedules'**
String get watchStudyScheduled ;
/// No description provided for @watchStudyCram.
///
/// In en, this message translates to:
/// **'Cram, each once'**
String get watchStudyCram ;
feat(jellyfin): the words' panel unfolds senses, shows progress, filters
The panel beside the video lists each entry's senses one to a line, as
Jisho does (and as every dictionary list in the app now does), folded
to two lines; an entry unfolds over the list, not in it, after a moment
under the pointer or on a tap, in the video's colour, so nothing moves
under the pointer and words of many senses (上げる has 25) crowd out
nothing. Each entry links to the word on Jisho.org, opened only when
tapped. A word the studyset holds shows how far its card is learnt:
New, Learning, Due, or Learned with when it comes again. The video's
words go through a filter in place of New only: new to the studyset,
held or all, JLPT levels, and word classes. The dictionary's sources
stay in the panel's lower right corner, and the player's controls hide
after 2 seconds rather than 3.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-13 18:43:06 +02:00
/// No description provided for @watchFilter.
///
/// In en, this message translates to:
/// **'Filter the words'**
String get watchFilter ;
/// No description provided for @watchFilterFresh.
///
/// In en, this message translates to:
/// **'New words'**
String get watchFilterFresh ;
/// No description provided for @watchFilterHeld.
///
/// In en, this message translates to:
/// **'Words in the studyset'**
String get watchFilterHeld ;
/// No description provided for @watchFilterAll.
///
/// In en, this message translates to:
/// **'All words'**
String get watchFilterAll ;
feat(dictionary): characters of Chinese, Korean and Greek, written by hand; pinyin typed
Parity with Japanese's kanji and kana:
- The characters' tab is named by its language (Hanzi, Hangul & Hanja,
Alphabet), its groups too: HSK levels and the characters in common use
(Hong Kong's grades for traditional ones); Hangul and the Hanja taught
in schools; Greek's letters. Radicals for Chinese and Korean in their
own forms (讠 for 言 in simplified Chinese), none for Greek.
- A character's readings are named as its language names them: On and
Kun, Pinyin, Eum and Hun (음, 훈); a letter means its sound and name.
- How characters are written comes by language: Chinese from Make Me a
Hanzi (its medians in KanjiVG's square, Arphic Public License, a data
file beside the app, never in cards), the Hangul letters drawn by the
curator; a Korean word is written letter by letter (닭: ㄷ ㅏ ㄺ). Words
of alphabets (German, Greek) aren't written by hand.
- A Chinese word's pinyin is typed in a session, tone numbers turning
into marks (ni3 → nǐ); KRDict's levels (초급, 중급, 고급) tag Korean
words; a Korean word's pronunciation shows in brackets ([가치]).
- Smaller: Look up in the composer keeps a noun's article; the Watch
filter names its level list; hints and placeholders by language.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-14 12:35:17 +02:00
/// Words a level list (JLPT, HSK, KRDict) leaves out.
feat(jellyfin): the words' panel unfolds senses, shows progress, filters
The panel beside the video lists each entry's senses one to a line, as
Jisho does (and as every dictionary list in the app now does), folded
to two lines; an entry unfolds over the list, not in it, after a moment
under the pointer or on a tap, in the video's colour, so nothing moves
under the pointer and words of many senses (上げる has 25) crowd out
nothing. Each entry links to the word on Jisho.org, opened only when
tapped. A word the studyset holds shows how far its card is learnt:
New, Learning, Due, or Learned with when it comes again. The video's
words go through a filter in place of New only: new to the studyset,
held or all, JLPT levels, and word classes. The dictionary's sources
stay in the panel's lower right corner, and the player's controls hide
after 2 seconds rather than 3.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-13 18:43:06 +02:00
///
/// In en, this message translates to:
feat(dictionary): characters of Chinese, Korean and Greek, written by hand; pinyin typed
Parity with Japanese's kanji and kana:
- The characters' tab is named by its language (Hanzi, Hangul & Hanja,
Alphabet), its groups too: HSK levels and the characters in common use
(Hong Kong's grades for traditional ones); Hangul and the Hanja taught
in schools; Greek's letters. Radicals for Chinese and Korean in their
own forms (讠 for 言 in simplified Chinese), none for Greek.
- A character's readings are named as its language names them: On and
Kun, Pinyin, Eum and Hun (음, 훈); a letter means its sound and name.
- How characters are written comes by language: Chinese from Make Me a
Hanzi (its medians in KanjiVG's square, Arphic Public License, a data
file beside the app, never in cards), the Hangul letters drawn by the
curator; a Korean word is written letter by letter (닭: ㄷ ㅏ ㄺ). Words
of alphabets (German, Greek) aren't written by hand.
- A Chinese word's pinyin is typed in a session, tone numbers turning
into marks (ni3 → nǐ); KRDict's levels (초급, 중급, 고급) tag Korean
words; a Korean word's pronunciation shows in brackets ([가치]).
- Smaller: Look up in the composer keeps a noun's article; the Watch
filter names its level list; hints and placeholders by language.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-14 12:35:17 +02:00
/// **'No {levels} level'**
String watchFilterNoLevel ( String levels ) ;
feat(jellyfin): the words' panel unfolds senses, shows progress, filters
The panel beside the video lists each entry's senses one to a line, as
Jisho does (and as every dictionary list in the app now does), folded
to two lines; an entry unfolds over the list, not in it, after a moment
under the pointer or on a tap, in the video's colour, so nothing moves
under the pointer and words of many senses (上げる has 25) crowd out
nothing. Each entry links to the word on Jisho.org, opened only when
tapped. A word the studyset holds shows how far its card is learnt:
New, Learning, Due, or Learned with when it comes again. The video's
words go through a filter in place of New only: new to the studyset,
held or all, JLPT levels, and word classes. The dictionary's sources
stay in the panel's lower right corner, and the player's controls hide
after 2 seconds rather than 3.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-13 18:43:06 +02:00
/// No description provided for @watchFilterClass.
///
/// In en, this message translates to:
/// **'Word class'**
String get watchFilterClass ;
/// No description provided for @watchFilterEmpty.
///
/// In en, this message translates to:
/// **'No words of this video match the filter.'**
String get watchFilterEmpty ;
/// No description provided for @watchClassNoun.
///
/// In en, this message translates to:
/// **'Nouns'**
String get watchClassNoun ;
/// No description provided for @watchClassVerb.
///
/// In en, this message translates to:
/// **'Verbs'**
String get watchClassVerb ;
/// No description provided for @watchClassAdjective.
///
/// In en, this message translates to:
/// **'Adjectives'**
String get watchClassAdjective ;
/// No description provided for @watchClassAdverb.
///
/// In en, this message translates to:
/// **'Adverbs'**
String get watchClassAdverb ;
/// No description provided for @watchClassExpression.
///
/// In en, this message translates to:
/// **'Expressions'**
String get watchClassExpression ;
/// No description provided for @watchClassOther.
///
/// In en, this message translates to:
/// **'Others'**
String get watchClassOther ;
feat(jellyfin): watch from one's own Jellyfin server and learn the words of its subtitles
Animelon's way of learning from video, from the person's own Jellyfin
server: connected per device (Settings, or a studyset's Add material ›
Watch from Jellyfin) with a name and password or a Quick Connect code,
over HTTPS only (plain HTTP only to a server on the same machine); the
token stays in the system's keyring. Shows, films and videos tied to a
studyset follow the person in their settings as the server's and the
item's ids alone (settings schema 9), named by the server when shown.
The Watch screen plays the video (video_player on Android, media_kit's
libmpv on Linux and Windows) with the line in the studyset's front
language cut into the dictionary's words with furigana, and the back
language's line beneath; a word tapped pauses the video and becomes a
card with the line and its translation as its first hint, tagged with
the show. The video's words are listed, most frequent first. The sound is
the track in the studyset's language, whichever the file marks default,
and the menu switches it; the subtitles read are a whole track, never a
forced one (which only covers what the sound doesn't say), and captions
for the hard of hearing only without plain ones. The line and its
translation take the video's width in a box of a fixed height, so the
video keeps its size; in the dark theme the video's colours glow around
it, as YouTube's ambient mode, and the words' panel takes the colour of
the episode's picture under the pointer.
A line is cut into words with one request: DictionaryEndpoint.withKeys
looks up every stretch of it that may be a word, as it stands and as
every word it may be a conjugation of, in the new dictionary_key table
(one row per key, indexed; migration dictionary-keys adds the table and
its index, nothing else), which the loader fills beside the entries.
The instance gets the text of each line looked up and keeps none of it,
as the Watch screen says.
Linux builds need libmpv-dev, and the app links libmpv: the snap stages
Ubuntu's libmpv2, makes the update-alternatives links for BLAS and LAPACK
that ffmpeg's speech recognition needs (staging never runs install
scripts), and removes what the GNOME runtime and core24 already hold, as
older copies of cairo, pango and librsvg hid the runtime's from its GTK.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-13 11:31:37 +02:00
/// No description provided for @watchNone.
///
/// In en, this message translates to:
/// **'None'**
String get watchNone ;
/// No description provided for @watchPlay.
///
/// In en, this message translates to:
/// **'Play'**
String get watchPlay ;
/// No description provided for @watchPause.
///
/// In en, this message translates to:
/// **'Pause'**
String get watchPause ;
/// No description provided for @watchPreviousLine.
///
/// In en, this message translates to:
/// **'Previous line'**
String get watchPreviousLine ;
/// No description provided for @watchNextLine.
///
/// In en, this message translates to:
/// **'Next line'**
String get watchNextLine ;
/// No description provided for @watchPauseAtLineEnd.
///
/// In en, this message translates to:
/// **'Pause after each line'**
String get watchPauseAtLineEnd ;
/// No description provided for @watchWordsTitle.
///
/// In en, this message translates to:
/// **'Words in this video'**
String get watchWordsTitle ;
/// No description provided for @watchWordsFinding.
///
/// In en, this message translates to:
/// **'Finding the words…'**
String get watchWordsFinding ;
/// No description provided for @watchWordCount.
///
/// In en, this message translates to:
/// **'{count}× '**
String watchWordCount ( int count ) ;
/// No description provided for @watchAdd.
///
/// In en, this message translates to:
/// **'Add'**
String get watchAdd ;
/// No description provided for @watchAdded.
///
/// In en, this message translates to:
feat(tag): one switch hides the tags from Jellyfin while studying
Every tag the app makes for where words were met on Jellyfin (the show
and the episode, the artist and the song) now says so (`origin` in tag
schema 2, written only for such a tag), in place of a tag hidden one by
one. Manage tags, where there are such tags, has one switch, Hide tags
from Jellyfin while studying (`hideJellyfinTags`, settings schema 9,
following the person): with it on, cards being studied leave them out of
their row at once, and Tags and Manage tags list them faint. They still
filter and narrow a session. The per-tag eye button is gone.
A word added while watching no longer takes the line it was met in as
its first hint: its hints are the dictionary's examples alone. Tatoeba's
sentences are checked, a subtitle line may be wrong or mistyped, a word
met again in a better line wouldn't get it, and every episode's line
would pile up on a common word.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-13 20:52:22 +02:00
/// **'{word} added'**
feat(jellyfin): watch from one's own Jellyfin server and learn the words of its subtitles
Animelon's way of learning from video, from the person's own Jellyfin
server: connected per device (Settings, or a studyset's Add material ›
Watch from Jellyfin) with a name and password or a Quick Connect code,
over HTTPS only (plain HTTP only to a server on the same machine); the
token stays in the system's keyring. Shows, films and videos tied to a
studyset follow the person in their settings as the server's and the
item's ids alone (settings schema 9), named by the server when shown.
The Watch screen plays the video (video_player on Android, media_kit's
libmpv on Linux and Windows) with the line in the studyset's front
language cut into the dictionary's words with furigana, and the back
language's line beneath; a word tapped pauses the video and becomes a
card with the line and its translation as its first hint, tagged with
the show. The video's words are listed, most frequent first. The sound is
the track in the studyset's language, whichever the file marks default,
and the menu switches it; the subtitles read are a whole track, never a
forced one (which only covers what the sound doesn't say), and captions
for the hard of hearing only without plain ones. The line and its
translation take the video's width in a box of a fixed height, so the
video keeps its size; in the dark theme the video's colours glow around
it, as YouTube's ambient mode, and the words' panel takes the colour of
the episode's picture under the pointer.
A line is cut into words with one request: DictionaryEndpoint.withKeys
looks up every stretch of it that may be a word, as it stands and as
every word it may be a conjugation of, in the new dictionary_key table
(one row per key, indexed; migration dictionary-keys adds the table and
its index, nothing else), which the loader fills beside the entries.
The instance gets the text of each line looked up and keeps none of it,
as the Watch screen says.
Linux builds need libmpv-dev, and the app links libmpv: the snap stages
Ubuntu's libmpv2, makes the update-alternatives links for BLAS and LAPACK
that ffmpeg's speech recognition needs (staging never runs install
scripts), and removes what the GNOME runtime and core24 already hold, as
older copies of cairo, pango and librsvg hid the runtime's from its GTK.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-13 11:31:37 +02:00
String watchAdded ( String word ) ;
fix(dictionary): words held in topics, a chosen destination, faster adds
A word counted as held only in the studyset itself, so adding from a
studyset whose topics held it (Vocabulary, its words in N3) made a
second card, and watching from a top-level studyset put every new word
into it. Now a word is held anywhere in the studyset and its topics,
archived ones too, and its progress shows from that card; new words go
where the person chose for the studyset ("Adds to N3" in the dictionary
and the Watch screen, the studyset and its topics but archived ones,
kept in their settings, schema 10), the studyset itself until then.
Adding a word took about two seconds on 17,000 card tags: each of its
four tags read every card's tags twice. They are now written with one
read, the Watch screen shows the word added at once and takes it back
should writing fail, and it no longer reads the studyset's progress
again after each word. On a copy of a real database an Add takes 0.3 to
0.4 s.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-16 20:41:32 +02:00
/// No description provided for @watchAddFailed.
///
/// In en, this message translates to:
/// **'Couldn\'t add {word}. Try again.'**
String watchAddFailed ( String word ) ;
2026-10-13 22:38:29 +02:00
/// No description provided for @watchStretchNone.
///
/// In en, this message translates to:
/// **'{text} isn\'t in the dictionary as one word.'**
String watchStretchNone ( String text ) ;
/// No description provided for @watchStretchFailed.
///
/// In en, this message translates to:
/// **'Couldn\'t look it up. Choose it again to try once more.'**
String get watchStretchFailed ;
feat(dictionary): German, Greek, English, Chinese and Korean words
A studyset in German, Greek, English, Chinese or Korean finds its
dictionary as one in Japanese does (ADR 0008), and its subtitles are cut
into words by its language (`WordCutting`): Chinese by its longest
words as they stand, the longer words over each cut offered as for
Japanese; Korean at its spaces, a word the dictionary lacks as it stands
taken without the particle it ends with (집에서 is 집, 아이 stays a
child); every other language at its spaces, a word written exactly so
first (the noun Essen before the verb essen). A studyset finds the
dictionary of its language tag, else of its language alone (zh for
zh-Hans).
A Wiktionary word shows its genders, principal forms, IPA, romanisation
and Hanja; a German or Greek noun of one gender goes onto its card with
its article (das Haus, το σπίτι); its tag is its word class, named in
full (Noun, Adjective, Preposition), as Wiktionary's adj and adv would
read as JMdict's codes. Articles, pronouns, prepositions and
conjunctions are left out of a video's words. Chinese words show their
pinyin over each character on the Watch screen, carry their HSK level
as a tag (HSK-1 … HSK-7-9), and the dictionary adds a level whole; the
Watch screen filters by HSK levels for Chinese, JLPT's for Japanese, and
by none for a language without. A reading is typed in a session only
when it is kana, so pinyin isn't. Words are looked up on the web on
Jisho in Japanese, on Wiktionary at the language's section otherwise,
and a search's hint speaks of kana and romaji only in Japanese.
`DICTIONARY_LANGUAGES` in deploy/.env names the languages an instance
keeps (unset: de,el,en,ko,zh; empty: none).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-14 07:45:15 +02:00
/// No description provided for @watchStretchSearch.
2026-10-13 22:38:29 +02:00
///
/// In en, this message translates to:
feat(dictionary): German, Greek, English, Chinese and Korean words
A studyset in German, Greek, English, Chinese or Korean finds its
dictionary as one in Japanese does (ADR 0008), and its subtitles are cut
into words by its language (`WordCutting`): Chinese by its longest
words as they stand, the longer words over each cut offered as for
Japanese; Korean at its spaces, a word the dictionary lacks as it stands
taken without the particle it ends with (집에서 is 집, 아이 stays a
child); every other language at its spaces, a word written exactly so
first (the noun Essen before the verb essen). A studyset finds the
dictionary of its language tag, else of its language alone (zh for
zh-Hans).
A Wiktionary word shows its genders, principal forms, IPA, romanisation
and Hanja; a German or Greek noun of one gender goes onto its card with
its article (das Haus, το σπίτι); its tag is its word class, named in
full (Noun, Adjective, Preposition), as Wiktionary's adj and adv would
read as JMdict's codes. Articles, pronouns, prepositions and
conjunctions are left out of a video's words. Chinese words show their
pinyin over each character on the Watch screen, carry their HSK level
as a tag (HSK-1 … HSK-7-9), and the dictionary adds a level whole; the
Watch screen filters by HSK levels for Chinese, JLPT's for Japanese, and
by none for a language without. A reading is typed in a session only
when it is kana, so pinyin isn't. Words are looked up on the web on
Jisho in Japanese, on Wiktionary at the language's section otherwise,
and a search's hint speaks of kana and romaji only in Japanese.
`DICTIONARY_LANGUAGES` in deploy/.env names the languages an instance
keeps (unset: de,el,en,ko,zh; empty: none).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-14 07:45:15 +02:00
/// **'Search {site} for it'**
String watchStretchSearch ( String site ) ;
2026-10-13 22:38:29 +02:00
feat(jellyfin): watch from one's own Jellyfin server and learn the words of its subtitles
Animelon's way of learning from video, from the person's own Jellyfin
server: connected per device (Settings, or a studyset's Add material ›
Watch from Jellyfin) with a name and password or a Quick Connect code,
over HTTPS only (plain HTTP only to a server on the same machine); the
token stays in the system's keyring. Shows, films and videos tied to a
studyset follow the person in their settings as the server's and the
item's ids alone (settings schema 9), named by the server when shown.
The Watch screen plays the video (video_player on Android, media_kit's
libmpv on Linux and Windows) with the line in the studyset's front
language cut into the dictionary's words with furigana, and the back
language's line beneath; a word tapped pauses the video and becomes a
card with the line and its translation as its first hint, tagged with
the show. The video's words are listed, most frequent first. The sound is
the track in the studyset's language, whichever the file marks default,
and the menu switches it; the subtitles read are a whole track, never a
forced one (which only covers what the sound doesn't say), and captions
for the hard of hearing only without plain ones. The line and its
translation take the video's width in a box of a fixed height, so the
video keeps its size; in the dark theme the video's colours glow around
it, as YouTube's ambient mode, and the words' panel takes the colour of
the episode's picture under the pointer.
A line is cut into words with one request: DictionaryEndpoint.withKeys
looks up every stretch of it that may be a word, as it stands and as
every word it may be a conjugation of, in the new dictionary_key table
(one row per key, indexed; migration dictionary-keys adds the table and
its index, nothing else), which the loader fills beside the entries.
The instance gets the text of each line looked up and keeps none of it,
as the Watch screen says.
Linux builds need libmpv-dev, and the app links libmpv: the snap stages
Ubuntu's libmpv2, makes the update-alternatives links for BLAS and LAPACK
that ffmpeg's speech recognition needs (staging never runs install
scripts), and removes what the GNOME runtime and core24 already hold, as
older copies of cairo, pango and librsvg hid the runtime's from its GTK.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-13 11:31:37 +02:00
/// No description provided for @watchTapAWord.
///
/// In en, this message translates to:
2026-10-13 22:38:29 +02:00
/// **'Tap a word of the subtitles to look it up, or drag across characters to look up any part of a line (on a touch screen, hold first).'**
feat(jellyfin): watch from one's own Jellyfin server and learn the words of its subtitles
Animelon's way of learning from video, from the person's own Jellyfin
server: connected per device (Settings, or a studyset's Add material ›
Watch from Jellyfin) with a name and password or a Quick Connect code,
over HTTPS only (plain HTTP only to a server on the same machine); the
token stays in the system's keyring. Shows, films and videos tied to a
studyset follow the person in their settings as the server's and the
item's ids alone (settings schema 9), named by the server when shown.
The Watch screen plays the video (video_player on Android, media_kit's
libmpv on Linux and Windows) with the line in the studyset's front
language cut into the dictionary's words with furigana, and the back
language's line beneath; a word tapped pauses the video and becomes a
card with the line and its translation as its first hint, tagged with
the show. The video's words are listed, most frequent first. The sound is
the track in the studyset's language, whichever the file marks default,
and the menu switches it; the subtitles read are a whole track, never a
forced one (which only covers what the sound doesn't say), and captions
for the hard of hearing only without plain ones. The line and its
translation take the video's width in a box of a fixed height, so the
video keeps its size; in the dark theme the video's colours glow around
it, as YouTube's ambient mode, and the words' panel takes the colour of
the episode's picture under the pointer.
A line is cut into words with one request: DictionaryEndpoint.withKeys
looks up every stretch of it that may be a word, as it stands and as
every word it may be a conjugation of, in the new dictionary_key table
(one row per key, indexed; migration dictionary-keys adds the table and
its index, nothing else), which the loader fills beside the entries.
The instance gets the text of each line looked up and keeps none of it,
as the Watch screen says.
Linux builds need libmpv-dev, and the app links libmpv: the snap stages
Ubuntu's libmpv2, makes the update-alternatives links for BLAS and LAPACK
that ffmpeg's speech recognition needs (staging never runs install
scripts), and removes what the GNOME runtime and core24 already hold, as
older copies of cairo, pango and librsvg hid the runtime's from its GTK.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-13 11:31:37 +02:00
String get watchTapAWord ;
/// No description provided for @watchFormOf.
///
/// In en, this message translates to:
/// **'A form of {word}'**
String watchFormOf ( String word ) ;
/// No description provided for @watchFailed.
///
/// In en, this message translates to:
/// **'The video couldn\'t be played.'**
String get watchFailed ;
2026-10-21 21:40:27 +02:00
/// No description provided for @publishPrivateRefused.
///
/// In en, this message translates to:
/// **'It\'s private: only your devices can read it. Make it non-private first, in its menu.'**
String get publishPrivateRefused ;
/// No description provided for @publishTagNames.
///
/// In en, this message translates to:
/// **'Its tags couldn\'t all be named. Sync, then try again.'**
String get publishTagNames ;
/// No description provided for @studysetMenuMakePrivate.
///
/// In en, this message translates to:
/// **'Make private'**
String get studysetMenuMakePrivate ;
/// No description provided for @studysetMenuMakeReadable.
///
/// In en, this message translates to:
/// **'Make non-private'**
String get studysetMenuMakeReadable ;
/// No description provided for @studysetPrivateTitle.
///
/// In en, this message translates to:
/// **'Make “{title}” private?'**
String studysetPrivateTitle ( String title ) ;
/// No description provided for @studysetPrivateBody.
///
/// In en, this message translates to:
/// **'Everything in it is end-to-end encrypted on your devices: the instance can\'t read it, publish it or show it to Claude. Copies in the instance\'s backups stay readable until those expire.'**
String get studysetPrivateBody ;
/// No description provided for @studysetReadableTitle.
///
/// In en, this message translates to:
/// **'Make “{title}” non-private?'**
String studysetReadableTitle ( String title ) ;
/// No description provided for @studysetReadableBody.
///
/// In en, this message translates to:
/// **'Everything in it is decrypted and synced again, so the instance can read it: you can publish it, and Claude can read it through the connector.'**
String get studysetReadableBody ;
/// No description provided for @studysetPrivateNoKeys.
///
/// In en, this message translates to:
/// **'Sign in with your password on this device to make studysets private.'**
String get studysetPrivateNoKeys ;
/// No description provided for @studysetPrivateSomeLocked.
///
/// In en, this message translates to:
/// **'Some of it couldn\'t be opened on this device and stayed as it was.'**
String get studysetPrivateSomeLocked ;
/// No description provided for @studysetPrivateLabel.
///
/// In en, this message translates to:
/// **'Private'**
String get studysetPrivateLabel ;
/// No description provided for @studysetLockedTitle.
///
/// In en, this message translates to:
/// **'Locked'**
String get studysetLockedTitle ;
/// No description provided for @studysetLockedBody.
///
/// In en, this message translates to:
/// **'This studyset is private, and its key doesn\'t open on this device: it was encrypted with keys your account no longer has. It can only be deleted.'**
String get studysetLockedBody ;
/// No description provided for @libraryOfferPrivateTitle.
///
/// In en, this message translates to:
/// **'Make your studysets private?'**
String get libraryOfferPrivateTitle ;
/// No description provided for @libraryOfferPrivateBody.
///
/// In en, this message translates to:
/// **'New studysets are private: end-to-end encrypted, so only your devices can read them. Those you made before stay readable by the instance until you make them private too. Copies in the instance\'s backups stay readable until those expire.'**
String get libraryOfferPrivateBody ;
/// No description provided for @libraryOfferPrivateAction.
///
/// In en, this message translates to:
/// **'Make all private'**
String get libraryOfferPrivateAction ;
/// No description provided for @libraryOfferPrivateLater.
///
/// In en, this message translates to:
/// **'Not now'**
String get libraryOfferPrivateLater ;
/// No description provided for @exportReadableNote.
///
/// In en, this message translates to:
/// **'The archive isn\'t encrypted: whoever has the file can read it.'**
String get exportReadableNote ;
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
}
class _AppLocalizationsDelegate
extends LocalizationsDelegate < AppLocalizations > {
const _AppLocalizationsDelegate ( ) ;
@ override
Future < AppLocalizations > load ( Locale locale ) {
return SynchronousFuture < AppLocalizations > ( lookupAppLocalizations ( locale ) ) ;
}
@ override
bool isSupported ( Locale locale ) = >
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 02:01:01 +02:00
< String > [ ' de ' , ' en ' ] . contains ( locale . languageCode ) ;
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
bool shouldReload ( _AppLocalizationsDelegate old ) = > false ;
}
AppLocalizations lookupAppLocalizations ( Locale locale ) {
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 02:01:01 +02:00
// Lookup logic when language+country codes are specified.
switch ( locale . languageCode ) {
case ' en ' :
{
switch ( locale . countryCode ) {
case ' GB ' :
return AppLocalizationsEnGb ( ) ;
case ' US ' :
return AppLocalizationsEnUs ( ) ;
}
break ;
}
}
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
// Lookup logic when only language code is specified.
switch ( locale . languageCode ) {
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 02:01:01 +02:00
case ' de ' :
return AppLocalizationsDe ( ) ;
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
case ' en ' :
return AppLocalizationsEn ( ) ;
}
throw FlutterError (
' AppLocalizations.delegate failed to load unsupported locale " $ locale ". This is likely '
' an issue with the localizations generation tool. Please file an issue '
' on GitHub with a reproducible sample app and the gen-l10n configuration '
' that was used. ' ,
) ;
}