feat(todo): the app on halkyon_app, with its own icon

Todo's app is halkyon_app's instance and session gates with Todo's
frame ("Todo" under "Halkyon", its icon, its own sentences) and an
instance check accepting only `todo`; TodoDependencies on an instance,
an empty TodoPerson per person, nothing kept or synced yet. A person
sees an empty home and their account: whose it is on which instance,
switching account, signing out and deleting it (each asking first where
it deletes), and About with the source of what the instance runs and
the licence list. Texts in British English and German. App ID
app.halkyon.todo on every platform, GitHub's callback scheme registered
on Android, no web build.

Its icon is Lucide's list-checks (lucide-static 1.50.0) in the brand's
blues on white: tool/app_icons.py draws its strokes with Pillow and
writes every platform's launcher icon; Lucide's ISC notice joins the
licence list.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
This commit is contained in:
Tizian 「ティツィアーン」 2026-10-19 21:56:12 +02:00
commit 6871e3763b
Signed by: raisondetredev
GPG key ID: 07F6FCF405488B64
7 changed files with 1718 additions and 693 deletions

View file

@ -0,0 +1,27 @@
{
"@@locale": "de",
"appTitle": "Halkyon Todo",
"launchProduct": "Todo",
"instanceFirstStartSubtitle": "Wähle die Instanz, auf der deine Aufgaben liegen sollen. Jede betreibt jemand; weitere kannst du später hinzufügen.",
"emailSignInSwitchMessage": "Diese Adresse hat bereits ein Konto. Die Aufgaben auf diesem Gerät lassen sich noch nicht damit zusammenführen: Wenn du wechselst, werden sie gelöscht.",
"startOtherWays": "Starte ohne Konto oder fahre mit deiner E-Mail-Adresse fort.",
"homeEmptyTitle": "Hier ist noch nichts",
"homeEmptyHint": "Aufgaben kommen mit einer späteren Version von Halkyon Todo.",
"accountTitle": "Konto",
"accountSignedIn": "Angemeldet auf {instance}",
"accountAnonymous": "Anonym auf {instance}",
"accountSwitch": "Konto wechseln",
"accountSignOut": "Abmelden",
"accountSignOutTitle": "Abmelden?",
"accountSignOutAnonymousMessage": "Bei diesem Konto kannst du dich nicht wieder anmelden, deshalb wird es beim Abmelden gelöscht.",
"accountDelete": "Konto löschen",
"accountDeleteTitle": "Dein Konto löschen?",
"accountDeleteMessage": "Dein Konto wird auf dem Server und auf all deinen Geräten gelöscht. Dies kann nicht rückgängig gemacht werden.",
"aboutTitle": "Über",
"aboutSource": "Der Quellcode dessen, was diese Instanz betreibt:"
}

View file

@ -0,0 +1,32 @@
{
"@@locale": "en",
"appTitle": "Halkyon Todo",
"@appTitle": {"description": "The app's name: the window's title, and where halkyon_app's texts name the app."},
"launchProduct": "Todo",
"@launchProduct": {"description": "The app's word in its wordmark, under \"Halkyon\"."},
"instanceFirstStartSubtitle": "Choose the instance to keep your tasks on. Someone runs each one; you can add others later.",
"emailSignInSwitchMessage": "This address already has an account. The tasks on this device can't be combined with it yet: if you switch, they are deleted.",
"startOtherWays": "Start without an account, or continue with your email address.",
"@startOtherWays": {"description": "Said after a GitHub account nobody on the instance signs in with: the ways in the start screen offers instead."},
"homeEmptyTitle": "Nothing here yet",
"homeEmptyHint": "Tasks come with a later version of Halkyon Todo.",
"accountTitle": "Account",
"accountSignedIn": "Signed in on {instance}",
"@accountSignedIn": {"placeholders": {"instance": {"type": "String"}}},
"accountAnonymous": "Anonymous on {instance}",
"@accountAnonymous": {"placeholders": {"instance": {"type": "String"}}},
"accountSwitch": "Switch account",
"accountSignOut": "Sign out",
"accountSignOutTitle": "Sign out?",
"accountSignOutAnonymousMessage": "This account can't be signed into again, so signing out deletes it.",
"accountDelete": "Delete account",
"accountDeleteTitle": "Delete your account?",
"accountDeleteMessage": "Your account is deleted on the server and on all your devices. This can't be undone.",
"aboutTitle": "About",
"aboutSource": "The source of what this instance runs:"
}

View file

@ -0,0 +1,3 @@
{
"@@locale": "en_GB"
}

View file

@ -0,0 +1,3 @@
{
"@@locale": "en_US"
}

View file

@ -0,0 +1,264 @@
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 Todo'**
String get appTitle;
/// The app's word in its wordmark, under "Halkyon".
///
/// In en, this message translates to:
/// **'Todo'**
String get launchProduct;
/// No description provided for @instanceFirstStartSubtitle.
///
/// In en, this message translates to:
/// **'Choose the instance to keep your tasks on. Someone runs each one; you can add others later.'**
String get instanceFirstStartSubtitle;
/// No description provided for @emailSignInSwitchMessage.
///
/// In en, this message translates to:
/// **'This address already has an account. The tasks on this device can\'t be combined with it yet: if you switch, they are deleted.'**
String get emailSignInSwitchMessage;
/// Said after a GitHub account nobody on the instance signs in with: the ways in the start screen offers instead.
///
/// In en, this message translates to:
/// **'Start without an account, or continue with your email address.'**
String get startOtherWays;
/// No description provided for @homeEmptyTitle.
///
/// In en, this message translates to:
/// **'Nothing here yet'**
String get homeEmptyTitle;
/// No description provided for @homeEmptyHint.
///
/// In en, this message translates to:
/// **'Tasks come with a later version of Halkyon Todo.'**
String get homeEmptyHint;
/// No description provided for @accountTitle.
///
/// In en, this message translates to:
/// **'Account'**
String get accountTitle;
/// No description provided for @accountSignedIn.
///
/// In en, this message translates to:
/// **'Signed in on {instance}'**
String accountSignedIn(String instance);
/// No description provided for @accountAnonymous.
///
/// In en, this message translates to:
/// **'Anonymous on {instance}'**
String accountAnonymous(String instance);
/// No description provided for @accountSwitch.
///
/// In en, this message translates to:
/// **'Switch account'**
String get accountSwitch;
/// No description provided for @accountSignOut.
///
/// In en, this message translates to:
/// **'Sign out'**
String get accountSignOut;
/// No description provided for @accountSignOutTitle.
///
/// In en, this message translates to:
/// **'Sign out?'**
String get accountSignOutTitle;
/// No description provided for @accountSignOutAnonymousMessage.
///
/// In en, this message translates to:
/// **'This account can\'t be signed into again, so signing out deletes it.'**
String get accountSignOutAnonymousMessage;
/// No description provided for @accountDelete.
///
/// In en, this message translates to:
/// **'Delete account'**
String get accountDelete;
/// No description provided for @accountDeleteTitle.
///
/// In en, this message translates to:
/// **'Delete your account?'**
String get accountDeleteTitle;
/// No description provided for @accountDeleteMessage.
///
/// In en, this message translates to:
/// **'Your account is deleted on the server and on all your devices. This can\'t be undone.'**
String get accountDeleteMessage;
/// No description provided for @aboutTitle.
///
/// In en, this message translates to:
/// **'About'**
String get aboutTitle;
/// No description provided for @aboutSource.
///
/// In en, this message translates to:
/// **'The source of what this instance runs:'**
String get aboutSource;
}
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.',
);
}

View file

@ -0,0 +1,78 @@
// ignore: unused_import
import 'package:intl/intl.dart' as intl;
import 'app_localizations.dart';
// ignore_for_file: type=lint
/// The translations for German (`de`).
class AppLocalizationsDe extends AppLocalizations {
AppLocalizationsDe([String locale = 'de']) : super(locale);
@override
String get appTitle => 'Halkyon Todo';
@override
String get launchProduct => 'Todo';
@override
String get instanceFirstStartSubtitle =>
'Wähle die Instanz, auf der deine Aufgaben liegen sollen. Jede betreibt jemand; weitere kannst du später hinzufügen.';
@override
String get emailSignInSwitchMessage =>
'Diese Adresse hat bereits ein Konto. Die Aufgaben auf diesem Gerät lassen sich noch nicht damit zusammenführen: Wenn du wechselst, werden sie gelöscht.';
@override
String get startOtherWays =>
'Starte ohne Konto oder fahre mit deiner E-Mail-Adresse fort.';
@override
String get homeEmptyTitle => 'Hier ist noch nichts';
@override
String get homeEmptyHint =>
'Aufgaben kommen mit einer späteren Version von Halkyon Todo.';
@override
String get accountTitle => 'Konto';
@override
String accountSignedIn(String instance) {
return 'Angemeldet auf $instance';
}
@override
String accountAnonymous(String instance) {
return 'Anonym auf $instance';
}
@override
String get accountSwitch => 'Konto wechseln';
@override
String get accountSignOut => 'Abmelden';
@override
String get accountSignOutTitle => 'Abmelden?';
@override
String get accountSignOutAnonymousMessage =>
'Bei diesem Konto kannst du dich nicht wieder anmelden, deshalb wird es beim Abmelden gelöscht.';
@override
String get accountDelete => 'Konto löschen';
@override
String get accountDeleteTitle => 'Dein Konto löschen?';
@override
String get accountDeleteMessage =>
'Dein Konto wird auf dem Server und auf all deinen Geräten gelöscht. Dies kann nicht rückgängig gemacht werden.';
@override
String get aboutTitle => 'Über';
@override
String get aboutSource => 'Der Quellcode dessen, was diese Instanz betreibt:';
}

View file

@ -0,0 +1,88 @@
// ignore: unused_import
import 'package:intl/intl.dart' as intl;
import 'app_localizations.dart';
// ignore_for_file: type=lint
/// The translations for English (`en`).
class AppLocalizationsEn extends AppLocalizations {
AppLocalizationsEn([String locale = 'en']) : super(locale);
@override
String get appTitle => 'Halkyon Todo';
@override
String get launchProduct => 'Todo';
@override
String get instanceFirstStartSubtitle =>
'Choose the instance to keep your tasks on. Someone runs each one; you can add others later.';
@override
String get emailSignInSwitchMessage =>
'This address already has an account. The tasks on this device can\'t be combined with it yet: if you switch, they are deleted.';
@override
String get startOtherWays =>
'Start without an account, or continue with your email address.';
@override
String get homeEmptyTitle => 'Nothing here yet';
@override
String get homeEmptyHint =>
'Tasks come with a later version of Halkyon Todo.';
@override
String get accountTitle => 'Account';
@override
String accountSignedIn(String instance) {
return 'Signed in on $instance';
}
@override
String accountAnonymous(String instance) {
return 'Anonymous on $instance';
}
@override
String get accountSwitch => 'Switch account';
@override
String get accountSignOut => 'Sign out';
@override
String get accountSignOutTitle => 'Sign out?';
@override
String get accountSignOutAnonymousMessage =>
'This account can\'t be signed into again, so signing out deletes it.';
@override
String get accountDelete => 'Delete account';
@override
String get accountDeleteTitle => 'Delete your account?';
@override
String get accountDeleteMessage =>
'Your account is deleted on the server and on all your devices. This can\'t be undone.';
@override
String get aboutTitle => 'About';
@override
String get aboutSource => 'The source of what this instance runs:';
}
/// The translations for English, as used in the United Kingdom (`en_GB`).
class AppLocalizationsEnGb extends AppLocalizationsEn {
AppLocalizationsEnGb() : super('en_GB');
}
/// The translations for English, as used in the United States (`en_US`).
class AppLocalizationsEnUs extends AppLocalizationsEn {
AppLocalizationsEnUs() : super('en_US');
}