feat(meditate): the app: Today, Explore, Profile, the sitting and its bells

halkyon_meditate_flutter on halkyon_app and halkyon_ui (ADR 0038): the
first start, sign-in and account as every Halkyon app's, a bar below
medium windows and a rail with the icon from medium on, fern washes,
Settings in cards (Sync, the operator's, Sitting, Practice, the
account, What's new, About, the version), English and German.

- Today: a greeting by the hour, Sit now at 5, 10, 20 minutes and the
  timer's own length, the practice of the day, the week with the days
  in a row, favourites, recently sat (D9).
- Explore: six practices of Halkyon's own, written for the app, found
  by a search without case or accents, by goal and by kind; each page
  with how it goes, its lengths, its heart and Start (D4, D10).
- The sitting, over the tabs: its time from the clock, a ring counting
  down, the cue of the moment, Pause and End, the bells at the start,
  between and at the end, the screen kept on; ended early, kept from a
  minute on where the person says so (D5, D6).
- Profile: every Halkyon app's, then the person's practice: minutes,
  sittings, days, the week and every sitting kept, to delete (D8).
- Settings and sittings sealed with the person's key and synced (D7).

The bells are synthesised by apps/meditate/tool/bells.py, the icon is
Lucide's sprout from cobalt through pacific into fern
(tool/app_icons.py). Releases and the deploy are a later step.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
This commit is contained in:
Tizian 「ティツィアーン」 2026-11-03 09:25:30 +01:00
commit cb026dce8d
Signed by: raisondetredev
GPG key ID: 07F6FCF405488B64

View file

@ -0,0 +1,876 @@
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;
import 'app_localizations_de.dart';
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.
static const List<Locale> supportedLocales = <Locale>[
Locale('de'),
Locale('en'),
Locale('en', 'GB'),
Locale('en', 'US'),
];
/// The app's name: the window's title, and where halkyon_app's texts name the app.
///
/// In en, this message translates to:
/// **'Halkyon Meditate'**
String get appTitle;
/// The app's word in its wordmark, under "Halkyon".
///
/// In en, this message translates to:
/// **'Meditate'**
String get launchProduct;
/// No description provided for @instanceFirstStartSubtitle.
///
/// In en, this message translates to:
/// **'Choose the instance to keep your practice on. Someone runs each one; you can add others later.'**
String get instanceFirstStartSubtitle;
/// No description provided for @accountTitle.
///
/// In en, this message translates to:
/// **'Account'**
String get accountTitle;
/// No description provided for @accountDeleteMessage.
///
/// In en, this message translates to:
/// **'Your account is deleted on the server and on all your devices, with every sitting kept. This can\'t be undone.'**
String get accountDeleteMessage;
/// No description provided for @aboutTitle.
///
/// In en, this message translates to:
/// **'About Halkyon Meditate'**
String get aboutTitle;
/// No description provided for @settingsTitle.
///
/// In en, this message translates to:
/// **'Settings'**
String get settingsTitle;
/// No description provided for @settingsVersion.
///
/// In en, this message translates to:
/// **'Version {version} (build {build})'**
String settingsVersion(String version, String build);
/// No description provided for @keysMissing.
///
/// In en, this message translates to:
/// **'This device can\'t save: it doesn\'t hold your account\'s keys. Sign in with your password or recovery phrase to add them.'**
String get keysMissing;
/// No description provided for @shellTabToday.
///
/// In en, this message translates to:
/// **'Today'**
String get shellTabToday;
/// 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 @greetingMorning.
///
/// In en, this message translates to:
/// **'Good morning'**
String get greetingMorning;
/// No description provided for @greetingAfternoon.
///
/// In en, this message translates to:
/// **'Good afternoon'**
String get greetingAfternoon;
/// No description provided for @greetingEvening.
///
/// In en, this message translates to:
/// **'Good evening'**
String get greetingEvening;
/// No description provided for @greetingNight.
///
/// In en, this message translates to:
/// **'A quiet night'**
String get greetingNight;
/// Minutes, after a number typed or stepped.
///
/// In en, this message translates to:
/// **'min'**
String get minutesUnit;
/// A length in minutes, short, on chips and rows.
///
/// In en, this message translates to:
/// **'{minutes} min'**
String minutes(int minutes);
/// No description provided for @todaySitNow.
///
/// In en, this message translates to:
/// **'Sit now'**
String get todaySitNow;
/// No description provided for @todaySitNowHint.
///
/// In en, this message translates to:
/// **'A silent sitting with bells, as long as you choose.'**
String get todaySitNowHint;
/// No description provided for @todayPracticeOfDay.
///
/// In en, this message translates to:
/// **'Practice of the day'**
String get todayPracticeOfDay;
/// No description provided for @todayThisWeek.
///
/// In en, this message translates to:
/// **'This week'**
String get todayThisWeek;
/// No description provided for @streakDays.
///
/// In en, this message translates to:
/// **'{count, plural, =0{No days in a row yet} =1{1 day in a row} other{{count} days in a row}}'**
String streakDays(int count);
/// No description provided for @favouritesTitle.
///
/// In en, this message translates to:
/// **'Favourites'**
String get favouritesTitle;
/// No description provided for @favouritesNone.
///
/// In en, this message translates to:
/// **'No favourites yet. Mark a practice with its heart in Explore.'**
String get favouritesNone;
/// No description provided for @recentTitle.
///
/// In en, this message translates to:
/// **'Recently sat'**
String get recentTitle;
/// No description provided for @recentNone.
///
/// In en, this message translates to:
/// **'Nothing sat yet.'**
String get recentNone;
/// No description provided for @silentTimer.
///
/// In en, this message translates to:
/// **'Silent timer'**
String get silentTimer;
/// A sitting kept: when it began and how long it was sat.
///
/// In en, this message translates to:
/// **'{date} · {minutes} min'**
String sittingRow(String date, int minutes);
/// No description provided for @exploreSearch.
///
/// In en, this message translates to:
/// **'Search practices'**
String get exploreSearch;
/// No description provided for @exploreGoals.
///
/// In en, this message translates to:
/// **'What brings you here?'**
String get exploreGoals;
/// No description provided for @exploreAll.
///
/// In en, this message translates to:
/// **'All'**
String get exploreAll;
/// No description provided for @exploreNoMatch.
///
/// In en, this message translates to:
/// **'No practice matches.'**
String get exploreNoMatch;
/// No description provided for @goalCalm.
///
/// In en, this message translates to:
/// **'Calm down'**
String get goalCalm;
/// No description provided for @goalSleep.
///
/// In en, this message translates to:
/// **'Sleep'**
String get goalSleep;
/// No description provided for @goalFocus.
///
/// In en, this message translates to:
/// **'Focus'**
String get goalFocus;
/// No description provided for @goalBegin.
///
/// In en, this message translates to:
/// **'Begin'**
String get goalBegin;
/// No description provided for @kindBreathing.
///
/// In en, this message translates to:
/// **'Breathing'**
String get kindBreathing;
/// No description provided for @kindBody.
///
/// In en, this message translates to:
/// **'Body'**
String get kindBody;
/// No description provided for @kindAwareness.
///
/// In en, this message translates to:
/// **'Awareness'**
String get kindAwareness;
/// No description provided for @kindKindness.
///
/// In en, this message translates to:
/// **'Kindness'**
String get kindKindness;
/// No description provided for @kindSleep.
///
/// In en, this message translates to:
/// **'Sleep'**
String get kindSleep;
/// No description provided for @kindPause.
///
/// In en, this message translates to:
/// **'Pause'**
String get kindPause;
/// No description provided for @practiceStart.
///
/// In en, this message translates to:
/// **'Start'**
String get practiceStart;
/// No description provided for @practiceLength.
///
/// In en, this message translates to:
/// **'Length'**
String get practiceLength;
/// No description provided for @practiceHowItGoes.
///
/// In en, this message translates to:
/// **'How it goes'**
String get practiceHowItGoes;
/// No description provided for @practiceFavourite.
///
/// In en, this message translates to:
/// **'Add to favourites'**
String get practiceFavourite;
/// No description provided for @practiceUnfavourite.
///
/// In en, this message translates to:
/// **'Remove from favourites'**
String get practiceUnfavourite;
/// No description provided for @practiceGone.
///
/// In en, this message translates to:
/// **'This practice isn\'t in this version of the app.'**
String get practiceGone;
/// No description provided for @practiceBreathCountingTitle.
///
/// In en, this message translates to:
/// **'Counting breaths'**
String get practiceBreathCountingTitle;
/// No description provided for @practiceBreathCountingSummary.
///
/// In en, this message translates to:
/// **'Count each out-breath from one to ten, then begin again.'**
String get practiceBreathCountingSummary;
/// No description provided for @practiceBreathCountingCue1.
///
/// In en, this message translates to:
/// **'Sit comfortably. Let the breath come and go by itself.'**
String get practiceBreathCountingCue1;
/// No description provided for @practiceBreathCountingCue2.
///
/// In en, this message translates to:
/// **'With each out-breath, count: one, two, and on up to ten.'**
String get practiceBreathCountingCue2;
/// No description provided for @practiceBreathCountingCue3.
///
/// In en, this message translates to:
/// **'Lost count? Notice it kindly and begin again at one.'**
String get practiceBreathCountingCue3;
/// No description provided for @practiceBreathCountingCue4.
///
/// In en, this message translates to:
/// **'Let the counting go. Rest with the breath as it is.'**
String get practiceBreathCountingCue4;
/// No description provided for @practiceThreeMinutePauseTitle.
///
/// In en, this message translates to:
/// **'Three-minute pause'**
String get practiceThreeMinutePauseTitle;
/// No description provided for @practiceThreeMinutePauseSummary.
///
/// In en, this message translates to:
/// **'A short stop in the day: notice, gather, widen.'**
String get practiceThreeMinutePauseSummary;
/// No description provided for @practiceThreeMinutePauseCue1.
///
/// In en, this message translates to:
/// **'What is here right now? Thoughts, feelings, sensations.'**
String get practiceThreeMinutePauseCue1;
/// No description provided for @practiceThreeMinutePauseCue2.
///
/// In en, this message translates to:
/// **'Gather your attention on the breath, low in the belly.'**
String get practiceThreeMinutePauseCue2;
/// No description provided for @practiceThreeMinutePauseCue3.
///
/// In en, this message translates to:
/// **'Widen it to the whole body, and to the room around you.'**
String get practiceThreeMinutePauseCue3;
/// No description provided for @practiceBodyScanTitle.
///
/// In en, this message translates to:
/// **'Body scan'**
String get practiceBodyScanTitle;
/// No description provided for @practiceBodyScanSummary.
///
/// In en, this message translates to:
/// **'Move your attention slowly through the body, from the feet up.'**
String get practiceBodyScanSummary;
/// No description provided for @practiceBodyScanCue1.
///
/// In en, this message translates to:
/// **'Lie or sit comfortably. Feel where the body meets the ground.'**
String get practiceBodyScanCue1;
/// No description provided for @practiceBodyScanCue2.
///
/// In en, this message translates to:
/// **'Bring your attention to your feet and legs. Just notice.'**
String get practiceBodyScanCue2;
/// No description provided for @practiceBodyScanCue3.
///
/// In en, this message translates to:
/// **'Move up through the belly, the back and the chest.'**
String get practiceBodyScanCue3;
/// No description provided for @practiceBodyScanCue4.
///
/// In en, this message translates to:
/// **'Notice the hands, the arms, the shoulders and the neck.'**
String get practiceBodyScanCue4;
/// No description provided for @practiceBodyScanCue5.
///
/// In en, this message translates to:
/// **'The face and the head. Then feel the whole body at once.'**
String get practiceBodyScanCue5;
/// No description provided for @practiceOpenAwarenessTitle.
///
/// In en, this message translates to:
/// **'Open awareness'**
String get practiceOpenAwarenessTitle;
/// No description provided for @practiceOpenAwarenessSummary.
///
/// In en, this message translates to:
/// **'Rest your attention on nothing in particular, and let whatever comes pass.'**
String get practiceOpenAwarenessSummary;
/// No description provided for @practiceOpenAwarenessCue1.
///
/// In en, this message translates to:
/// **'Begin with a few breaths, settling in.'**
String get practiceOpenAwarenessCue1;
/// No description provided for @practiceOpenAwarenessCue2.
///
/// In en, this message translates to:
/// **'Let the breath go. Notice sounds as they come and go.'**
String get practiceOpenAwarenessCue2;
/// No description provided for @practiceOpenAwarenessCue3.
///
/// In en, this message translates to:
/// **'Thoughts may come too. See them arrive, and leave.'**
String get practiceOpenAwarenessCue3;
/// No description provided for @practiceOpenAwarenessCue4.
///
/// In en, this message translates to:
/// **'Simply be aware, with nothing to do.'**
String get practiceOpenAwarenessCue4;
/// No description provided for @practiceLovingKindnessTitle.
///
/// In en, this message translates to:
/// **'Kind wishes'**
String get practiceLovingKindnessTitle;
/// No description provided for @practiceLovingKindnessSummary.
///
/// In en, this message translates to:
/// **'Wish yourself and others well, one circle after the other.'**
String get practiceLovingKindnessSummary;
/// No description provided for @practiceLovingKindnessCue1.
///
/// In en, this message translates to:
/// **'Think of yourself: may I be safe, may I be at ease.'**
String get practiceLovingKindnessCue1;
/// No description provided for @practiceLovingKindnessCue2.
///
/// In en, this message translates to:
/// **'Think of someone dear to you: may you be safe, may you be at ease.'**
String get practiceLovingKindnessCue2;
/// No description provided for @practiceLovingKindnessCue3.
///
/// In en, this message translates to:
/// **'Think of someone you hardly know, and wish them the same.'**
String get practiceLovingKindnessCue3;
/// No description provided for @practiceLovingKindnessCue4.
///
/// In en, this message translates to:
/// **'Let the wishes spread to everyone, everywhere.'**
String get practiceLovingKindnessCue4;
/// No description provided for @practiceSettlingForSleepTitle.
///
/// In en, this message translates to:
/// **'Settling for sleep'**
String get practiceSettlingForSleepTitle;
/// No description provided for @practiceSettlingForSleepSummary.
///
/// In en, this message translates to:
/// **'Slow the breath and let the day go before you sleep.'**
String get practiceSettlingForSleepSummary;
/// No description provided for @practiceSettlingForSleepCue1.
///
/// In en, this message translates to:
/// **'Lie down. Let the bed carry your weight.'**
String get practiceSettlingForSleepCue1;
/// No description provided for @practiceSettlingForSleepCue2.
///
/// In en, this message translates to:
/// **'Breathe out a little longer than you breathe in.'**
String get practiceSettlingForSleepCue2;
/// No description provided for @practiceSettlingForSleepCue3.
///
/// In en, this message translates to:
/// **'Let the day\'s thoughts drift past like clouds.'**
String get practiceSettlingForSleepCue3;
/// No description provided for @practiceSettlingForSleepCue4.
///
/// In en, this message translates to:
/// **'Nothing more to do. Let sleep come when it comes.'**
String get practiceSettlingForSleepCue4;
/// No description provided for @timerCue.
///
/// In en, this message translates to:
/// **'Sit in silence. The bell tells you when it is over.'**
String get timerCue;
/// No description provided for @sittingPause.
///
/// In en, this message translates to:
/// **'Pause'**
String get sittingPause;
/// No description provided for @sittingResume.
///
/// In en, this message translates to:
/// **'Resume'**
String get sittingResume;
/// No description provided for @sittingEnd.
///
/// In en, this message translates to:
/// **'End'**
String get sittingEnd;
/// No description provided for @sittingPaused.
///
/// In en, this message translates to:
/// **'Paused'**
String get sittingPaused;
/// No description provided for @sittingDoneTitle.
///
/// In en, this message translates to:
/// **'Well sat'**
String get sittingDoneTitle;
/// No description provided for @sittingDoneMessage.
///
/// In en, this message translates to:
/// **'{minutes, plural, =1{1 minute sat.} other{{minutes} minutes sat.}}'**
String sittingDoneMessage(int minutes);
/// No description provided for @sittingDone.
///
/// In en, this message translates to:
/// **'Done'**
String get sittingDone;
/// No description provided for @sittingEndTitle.
///
/// In en, this message translates to:
/// **'End the sitting?'**
String get sittingEndTitle;
/// No description provided for @sittingEndMessage.
///
/// In en, this message translates to:
/// **'{minutes, plural, =1{1 minute sat so far. Keep it among your sittings?} other{{minutes} minutes sat so far. Keep them among your sittings?}}'**
String sittingEndMessage(int minutes);
/// No description provided for @sittingKeep.
///
/// In en, this message translates to:
/// **'Keep'**
String get sittingKeep;
/// No description provided for @sittingDiscard.
///
/// In en, this message translates to:
/// **'Discard'**
String get sittingDiscard;
/// No description provided for @sittingSitOn.
///
/// In en, this message translates to:
/// **'Sit on'**
String get sittingSitOn;
/// No description provided for @profileTabPractice.
///
/// In en, this message translates to:
/// **'Practice'**
String get profileTabPractice;
/// No description provided for @figuresMinutes.
///
/// In en, this message translates to:
/// **'Minutes'**
String get figuresMinutes;
/// No description provided for @figuresSittings.
///
/// In en, this message translates to:
/// **'Sittings'**
String get figuresSittings;
/// No description provided for @figuresDays.
///
/// In en, this message translates to:
/// **'Days'**
String get figuresDays;
/// No description provided for @historyTitle.
///
/// In en, this message translates to:
/// **'Your sittings'**
String get historyTitle;
/// No description provided for @sittingDelete.
///
/// In en, this message translates to:
/// **'Delete'**
String get sittingDelete;
/// No description provided for @sittingDeleteTitle.
///
/// In en, this message translates to:
/// **'Delete this sitting?'**
String get sittingDeleteTitle;
/// No description provided for @sittingDeleteMessage.
///
/// In en, this message translates to:
/// **'It goes from your figures on all your devices.'**
String get sittingDeleteMessage;
/// No description provided for @settingsSitting.
///
/// In en, this message translates to:
/// **'Sitting'**
String get settingsSitting;
/// No description provided for @bellTitle.
///
/// In en, this message translates to:
/// **'Bell'**
String get bellTitle;
/// No description provided for @bellBowl.
///
/// In en, this message translates to:
/// **'Singing bowl'**
String get bellBowl;
/// No description provided for @bellSmallBell.
///
/// In en, this message translates to:
/// **'Small bell'**
String get bellSmallBell;
/// No description provided for @bellWoodBlock.
///
/// In en, this message translates to:
/// **'Wooden block'**
String get bellWoodBlock;
/// No description provided for @startBellTitle.
///
/// In en, this message translates to:
/// **'Bell at the start'**
String get startBellTitle;
/// No description provided for @intervalTitle.
///
/// In en, this message translates to:
/// **'Bells between'**
String get intervalTitle;
/// No description provided for @intervalNone.
///
/// In en, this message translates to:
/// **'None'**
String get intervalNone;
/// No description provided for @intervalEvery.
///
/// In en, this message translates to:
/// **'Every {minutes} min'**
String intervalEvery(int minutes);
/// No description provided for @keepScreenOnTitle.
///
/// In en, this message translates to:
/// **'Keep the screen on'**
String get keepScreenOnTitle;
/// No description provided for @keepScreenOnHint.
///
/// In en, this message translates to:
/// **'While a sitting runs'**
String get keepScreenOnHint;
/// No description provided for @timerLengthTitle.
///
/// In en, this message translates to:
/// **'Silent timer\'s length'**
String get timerLengthTitle;
/// No description provided for @settingsPractice.
///
/// In en, this message translates to:
/// **'Practice'**
String get settingsPractice;
/// No description provided for @showStreakTitle.
///
/// In en, this message translates to:
/// **'Show the days in a row'**
String get showStreakTitle;
/// No description provided for @showStreakHint.
///
/// In en, this message translates to:
/// **'On Today and in your Profile'**
String get showStreakHint;
}
class _AppLocalizationsDelegate
extends LocalizationsDelegate<AppLocalizations> {
const _AppLocalizationsDelegate();
@override
Future<AppLocalizations> load(Locale locale) {
return SynchronousFuture<AppLocalizations>(lookupAppLocalizations(locale));
}
@override
bool isSupported(Locale locale) =>
<String>['de', 'en'].contains(locale.languageCode);
@override
bool shouldReload(_AppLocalizationsDelegate old) => false;
}
AppLocalizations lookupAppLocalizations(Locale locale) {
// 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;
}
}
// Lookup logic when only language code is specified.
switch (locale.languageCode) {
case 'de':
return AppLocalizationsDe();
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.',
);
}