feat(meditate): the dream journal, a fourth tab, Today's morning card

ADR 0038, D14. A dream (`Dream`) holds the night it belongs to (the
morning's date, today's by default), an optional title, its text, the
marks lucid, nightmare and recurring, a clarity of 1 to 5, the mood on
waking and free labels, and when it was written; a row each in `dream`,
sealed for its row with the person's key as the sittings
(`DreamRepository` on `SealedRows`, `SyncDreamGateway`), followed by
`JournalCubit` for every screen.

- The Journal, between Explore and Profile: dreams by month, the
  newest first, searched over title, text and labels as Explore
  searches, filtered by marks, `+` above the bar; empty, it says that
  entries are sealed on the device before they sync.
- A dream's screen over the tabs: a new one with its text first and
  focused, the rest folded below; a kept one to read, with Edit and
  Delete (asked first); leaving one written but not saved asks first.
- Today, from 4 to 12 o'clock while last night has no dream, a gentle
  card leading to a new one; Settings' Journal card switches it off
  (`dreamPrompt` in the sealed settings, schema 1 kept).
- Profile's Practice: this month's dreams and the lucid ones, once the
  journal holds one.
- English and German texts; What's new 0.1.0 names the journal; the
  account's deletion says it takes the dreams too.

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 14:19:10 +01:00
commit 86c9f4522a
Signed by: raisondetredev
GPG key ID: 07F6FCF405488B64
5 changed files with 2762 additions and 41 deletions

View file

@ -4,7 +4,7 @@
"launchProduct": "Meditate",
"instanceFirstStartSubtitle": "Wähle die Instanz, auf der deine Praxis liegen soll. Jede betreibt jemand; weitere kannst du später hinzufügen.",
"accountTitle": "Konto",
"accountDeleteMessage": "Dein Konto wird auf dem Server und auf all deinen Geräten gelöscht, mit jeder gespeicherten Sitzung. Dies kann nicht rückgängig gemacht werden.",
"accountDeleteMessage": "Dein Konto wird auf dem Server und auf all deinen Geräten gelöscht, mit jeder gespeicherten Sitzung und jedem Traum. Dies kann nicht rückgängig gemacht werden.",
"aboutTitle": "Über Halkyon Meditate",
"settingsTitle": "Einstellungen",
"settingsVersion": "Version {version} (Build {build})",
@ -120,5 +120,51 @@
"timerLengthTitle": "Dauer des stillen Timers",
"settingsPractice": "Praxis",
"showStreakTitle": "Tage in Folge zeigen",
"showStreakHint": "Unter Heute und in deinem Profil"
"showStreakHint": "Unter Heute und in deinem Profil",
"shellTabJournal": "Journal",
"journalSearch": "Träume durchsuchen",
"journalEmptyTitle": "Dein Traumtagebuch",
"journalEmpty": "Schreib einen Traum gleich nach dem Aufwachen auf, bevor er verblasst. Jeder Eintrag wird auf diesem Gerät versiegelt, bevor er synchronisiert wird: Niemand sonst kann ihn lesen, auch die Instanz nicht.",
"journalNoMatch": "Kein Traum passt.",
"journalAdd": "Traum aufschreiben",
"journalFiguresTitle": "Traumtagebuch",
"figuresDreamsThisMonth": "Diesen Monat",
"figuresLucid": "Luzid",
"markLucid": "Luzid",
"markNightmare": "Albtraum",
"markRecurring": "Wiederkehrend",
"moodGlad": "Froh",
"moodCalm": "Ruhig",
"moodPuzzled": "Verwundert",
"moodSad": "Traurig",
"moodUneasy": "Unruhig",
"dreamNew": "Neuer Traum",
"dreamLastNight": "Letzte Nacht",
"dreamNight": "Nacht vor dem {date}",
"dreamTextHint": "Was hast du geträumt?",
"dreamTitleLabel": "Titel (optional)",
"dreamMore": "Nacht, Titel, Merkmale und mehr",
"dreamNightLabel": "Nacht",
"dreamMarksLabel": "Merkmale",
"dreamClarityLabel": "Klarheit",
"dreamClarityHint": "Wie klar du dich erinnerst, 1 bis 5",
"dreamClarityValue": "Klarheit {clarity}/5",
"dreamMoodLabel": "Stimmung beim Aufwachen",
"dreamLabelsLabel": "Schlagwörter",
"dreamLabelsHint": "Menschen, Orte, Themen",
"dreamLabelAdd": "Schlagwort hinzufügen",
"dreamSave": "Speichern",
"dreamEdit": "Bearbeiten",
"dreamDelete": "Löschen",
"dreamDeleteTitle": "Diesen Traum löschen?",
"dreamDeleteMessage": "Er verschwindet auf all deinen Geräten aus deinem Journal.",
"dreamDiscardTitle": "Diesen Traum verwerfen?",
"dreamDiscardMessage": "Was du geschrieben hast, wird nicht gespeichert.",
"dreamDiscard": "Verwerfen",
"dreamWritten": "Geschrieben am {date}",
"todayDreamPrompt": "Schreib den Traum der letzten Nacht auf",
"todayDreamPromptHint": "Bevor er verblasst. Versiegelt auf diesem Gerät.",
"settingsJournal": "Journal",
"dreamPromptTitle": "Nach dem Traum der letzten Nacht fragen",
"dreamPromptHint": "Unter Heute, von 4 bis 12 Uhr, bis du einen aufschreibst"
}

View file

@ -10,7 +10,7 @@
},
"instanceFirstStartSubtitle": "Choose the instance to keep your practice on. Someone runs each one; you can add others later.",
"accountTitle": "Account",
"accountDeleteMessage": "Your account is deleted on the server and on all your devices, with every sitting kept. This can't be undone.",
"accountDeleteMessage": "Your account is deleted on the server and on all your devices, with every sitting and dream kept. This can't be undone.",
"aboutTitle": "About Halkyon Meditate",
"settingsTitle": "Settings",
"settingsVersion": "Version {version} (build {build})",
@ -186,5 +186,73 @@
"timerLengthTitle": "Silent timer's length",
"settingsPractice": "Practice",
"showStreakTitle": "Show the days in a row",
"showStreakHint": "On Today and in your Profile"
"showStreakHint": "On Today and in your Profile",
"shellTabJournal": "Journal",
"journalSearch": "Search dreams",
"journalEmptyTitle": "Your dream journal",
"journalEmpty": "Write down a dream as soon as you wake, before it fades. Every entry is sealed on this device before it syncs: nobody else can read it, not even the instance.",
"journalNoMatch": "No dream matches.",
"journalAdd": "Write down a dream",
"journalFiguresTitle": "Dream journal",
"figuresDreamsThisMonth": "This month",
"figuresLucid": "Lucid",
"markLucid": "Lucid",
"markNightmare": "Nightmare",
"markRecurring": "Recurring",
"moodGlad": "Glad",
"moodCalm": "Calm",
"moodPuzzled": "Puzzled",
"moodSad": "Sad",
"moodUneasy": "Uneasy",
"dreamNew": "New dream",
"dreamLastNight": "Last night",
"dreamNight": "Night before {date}",
"@dreamNight": {
"description": "The night a dream belongs to, named by the morning after it.",
"placeholders": {
"date": {
"type": "String"
}
}
},
"dreamTextHint": "What did you dream?",
"dreamTitleLabel": "Title (optional)",
"dreamMore": "Night, title, marks and more",
"dreamNightLabel": "Night",
"dreamMarksLabel": "Marks",
"dreamClarityLabel": "Clarity",
"dreamClarityHint": "How clearly you remember it, 1 to 5",
"dreamClarityValue": "Clarity {clarity}/5",
"@dreamClarityValue": {
"placeholders": {
"clarity": {
"type": "int"
}
}
},
"dreamMoodLabel": "Mood on waking",
"dreamLabelsLabel": "Labels",
"dreamLabelsHint": "People, places, themes",
"dreamLabelAdd": "Add label",
"dreamSave": "Save",
"dreamEdit": "Edit",
"dreamDelete": "Delete",
"dreamDeleteTitle": "Delete this dream?",
"dreamDeleteMessage": "It goes from your journal on all your devices.",
"dreamDiscardTitle": "Discard this dream?",
"dreamDiscardMessage": "What you wrote isn't saved.",
"dreamDiscard": "Discard",
"dreamWritten": "Written {date}",
"@dreamWritten": {
"placeholders": {
"date": {
"type": "String"
}
}
},
"todayDreamPrompt": "Write down last night's dream",
"todayDreamPromptHint": "Before it fades. Sealed on this device.",
"settingsJournal": "Journal",
"dreamPromptTitle": "Ask for last night's dream",
"dreamPromptHint": "On Today, from 4 to 12 o'clock, until you write one"
}

View file

@ -127,7 +127,7 @@ abstract class AppLocalizations {
/// 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.'**
/// **'Your account is deleted on the server and on all your devices, with every sitting and dream kept. This can\'t be undone.'**
String get accountDeleteMessage;
/// No description provided for @aboutTitle.
@ -825,6 +825,282 @@ abstract class AppLocalizations {
/// In en, this message translates to:
/// **'On Today and in your Profile'**
String get showStreakHint;
/// No description provided for @shellTabJournal.
///
/// In en, this message translates to:
/// **'Journal'**
String get shellTabJournal;
/// No description provided for @journalSearch.
///
/// In en, this message translates to:
/// **'Search dreams'**
String get journalSearch;
/// No description provided for @journalEmptyTitle.
///
/// In en, this message translates to:
/// **'Your dream journal'**
String get journalEmptyTitle;
/// No description provided for @journalEmpty.
///
/// In en, this message translates to:
/// **'Write down a dream as soon as you wake, before it fades. Every entry is sealed on this device before it syncs: nobody else can read it, not even the instance.'**
String get journalEmpty;
/// No description provided for @journalNoMatch.
///
/// In en, this message translates to:
/// **'No dream matches.'**
String get journalNoMatch;
/// No description provided for @journalAdd.
///
/// In en, this message translates to:
/// **'Write down a dream'**
String get journalAdd;
/// No description provided for @journalFiguresTitle.
///
/// In en, this message translates to:
/// **'Dream journal'**
String get journalFiguresTitle;
/// No description provided for @figuresDreamsThisMonth.
///
/// In en, this message translates to:
/// **'This month'**
String get figuresDreamsThisMonth;
/// No description provided for @figuresLucid.
///
/// In en, this message translates to:
/// **'Lucid'**
String get figuresLucid;
/// No description provided for @markLucid.
///
/// In en, this message translates to:
/// **'Lucid'**
String get markLucid;
/// No description provided for @markNightmare.
///
/// In en, this message translates to:
/// **'Nightmare'**
String get markNightmare;
/// No description provided for @markRecurring.
///
/// In en, this message translates to:
/// **'Recurring'**
String get markRecurring;
/// No description provided for @moodGlad.
///
/// In en, this message translates to:
/// **'Glad'**
String get moodGlad;
/// No description provided for @moodCalm.
///
/// In en, this message translates to:
/// **'Calm'**
String get moodCalm;
/// No description provided for @moodPuzzled.
///
/// In en, this message translates to:
/// **'Puzzled'**
String get moodPuzzled;
/// No description provided for @moodSad.
///
/// In en, this message translates to:
/// **'Sad'**
String get moodSad;
/// No description provided for @moodUneasy.
///
/// In en, this message translates to:
/// **'Uneasy'**
String get moodUneasy;
/// No description provided for @dreamNew.
///
/// In en, this message translates to:
/// **'New dream'**
String get dreamNew;
/// No description provided for @dreamLastNight.
///
/// In en, this message translates to:
/// **'Last night'**
String get dreamLastNight;
/// The night a dream belongs to, named by the morning after it.
///
/// In en, this message translates to:
/// **'Night before {date}'**
String dreamNight(String date);
/// No description provided for @dreamTextHint.
///
/// In en, this message translates to:
/// **'What did you dream?'**
String get dreamTextHint;
/// No description provided for @dreamTitleLabel.
///
/// In en, this message translates to:
/// **'Title (optional)'**
String get dreamTitleLabel;
/// No description provided for @dreamMore.
///
/// In en, this message translates to:
/// **'Night, title, marks and more'**
String get dreamMore;
/// No description provided for @dreamNightLabel.
///
/// In en, this message translates to:
/// **'Night'**
String get dreamNightLabel;
/// No description provided for @dreamMarksLabel.
///
/// In en, this message translates to:
/// **'Marks'**
String get dreamMarksLabel;
/// No description provided for @dreamClarityLabel.
///
/// In en, this message translates to:
/// **'Clarity'**
String get dreamClarityLabel;
/// No description provided for @dreamClarityHint.
///
/// In en, this message translates to:
/// **'How clearly you remember it, 1 to 5'**
String get dreamClarityHint;
/// No description provided for @dreamClarityValue.
///
/// In en, this message translates to:
/// **'Clarity {clarity}/5'**
String dreamClarityValue(int clarity);
/// No description provided for @dreamMoodLabel.
///
/// In en, this message translates to:
/// **'Mood on waking'**
String get dreamMoodLabel;
/// No description provided for @dreamLabelsLabel.
///
/// In en, this message translates to:
/// **'Labels'**
String get dreamLabelsLabel;
/// No description provided for @dreamLabelsHint.
///
/// In en, this message translates to:
/// **'People, places, themes'**
String get dreamLabelsHint;
/// No description provided for @dreamLabelAdd.
///
/// In en, this message translates to:
/// **'Add label'**
String get dreamLabelAdd;
/// No description provided for @dreamSave.
///
/// In en, this message translates to:
/// **'Save'**
String get dreamSave;
/// No description provided for @dreamEdit.
///
/// In en, this message translates to:
/// **'Edit'**
String get dreamEdit;
/// No description provided for @dreamDelete.
///
/// In en, this message translates to:
/// **'Delete'**
String get dreamDelete;
/// No description provided for @dreamDeleteTitle.
///
/// In en, this message translates to:
/// **'Delete this dream?'**
String get dreamDeleteTitle;
/// No description provided for @dreamDeleteMessage.
///
/// In en, this message translates to:
/// **'It goes from your journal on all your devices.'**
String get dreamDeleteMessage;
/// No description provided for @dreamDiscardTitle.
///
/// In en, this message translates to:
/// **'Discard this dream?'**
String get dreamDiscardTitle;
/// No description provided for @dreamDiscardMessage.
///
/// In en, this message translates to:
/// **'What you wrote isn\'t saved.'**
String get dreamDiscardMessage;
/// No description provided for @dreamDiscard.
///
/// In en, this message translates to:
/// **'Discard'**
String get dreamDiscard;
/// No description provided for @dreamWritten.
///
/// In en, this message translates to:
/// **'Written {date}'**
String dreamWritten(String date);
/// No description provided for @todayDreamPrompt.
///
/// In en, this message translates to:
/// **'Write down last night\'s dream'**
String get todayDreamPrompt;
/// No description provided for @todayDreamPromptHint.
///
/// In en, this message translates to:
/// **'Before it fades. Sealed on this device.'**
String get todayDreamPromptHint;
/// No description provided for @settingsJournal.
///
/// In en, this message translates to:
/// **'Journal'**
String get settingsJournal;
/// No description provided for @dreamPromptTitle.
///
/// In en, this message translates to:
/// **'Ask for last night\'s dream'**
String get dreamPromptTitle;
/// No description provided for @dreamPromptHint.
///
/// In en, this message translates to:
/// **'On Today, from 4 to 12 o\'clock, until you write one'**
String get dreamPromptHint;
}
class _AppLocalizationsDelegate

View file

@ -24,7 +24,7 @@ class AppLocalizationsDe extends AppLocalizations {
@override
String get accountDeleteMessage =>
'Dein Konto wird auf dem Server und auf all deinen Geräten gelöscht, mit jeder gespeicherten Sitzung. Dies kann nicht rückgängig gemacht werden.';
'Dein Konto wird auf dem Server und auf all deinen Geräten gelöscht, mit jeder gespeicherten Sitzung und jedem Traum. Dies kann nicht rückgängig gemacht werden.';
@override
String get aboutTitle => 'Über Halkyon Meditate';
@ -442,4 +442,153 @@ class AppLocalizationsDe extends AppLocalizations {
@override
String get showStreakHint => 'Unter Heute und in deinem Profil';
@override
String get shellTabJournal => 'Journal';
@override
String get journalSearch => 'Träume durchsuchen';
@override
String get journalEmptyTitle => 'Dein Traumtagebuch';
@override
String get journalEmpty =>
'Schreib einen Traum gleich nach dem Aufwachen auf, bevor er verblasst. Jeder Eintrag wird auf diesem Gerät versiegelt, bevor er synchronisiert wird: Niemand sonst kann ihn lesen, auch die Instanz nicht.';
@override
String get journalNoMatch => 'Kein Traum passt.';
@override
String get journalAdd => 'Traum aufschreiben';
@override
String get journalFiguresTitle => 'Traumtagebuch';
@override
String get figuresDreamsThisMonth => 'Diesen Monat';
@override
String get figuresLucid => 'Luzid';
@override
String get markLucid => 'Luzid';
@override
String get markNightmare => 'Albtraum';
@override
String get markRecurring => 'Wiederkehrend';
@override
String get moodGlad => 'Froh';
@override
String get moodCalm => 'Ruhig';
@override
String get moodPuzzled => 'Verwundert';
@override
String get moodSad => 'Traurig';
@override
String get moodUneasy => 'Unruhig';
@override
String get dreamNew => 'Neuer Traum';
@override
String get dreamLastNight => 'Letzte Nacht';
@override
String dreamNight(String date) {
return 'Nacht vor dem $date';
}
@override
String get dreamTextHint => 'Was hast du geträumt?';
@override
String get dreamTitleLabel => 'Titel (optional)';
@override
String get dreamMore => 'Nacht, Titel, Merkmale und mehr';
@override
String get dreamNightLabel => 'Nacht';
@override
String get dreamMarksLabel => 'Merkmale';
@override
String get dreamClarityLabel => 'Klarheit';
@override
String get dreamClarityHint => 'Wie klar du dich erinnerst, 1 bis 5';
@override
String dreamClarityValue(int clarity) {
return 'Klarheit $clarity/5';
}
@override
String get dreamMoodLabel => 'Stimmung beim Aufwachen';
@override
String get dreamLabelsLabel => 'Schlagwörter';
@override
String get dreamLabelsHint => 'Menschen, Orte, Themen';
@override
String get dreamLabelAdd => 'Schlagwort hinzufügen';
@override
String get dreamSave => 'Speichern';
@override
String get dreamEdit => 'Bearbeiten';
@override
String get dreamDelete => 'Löschen';
@override
String get dreamDeleteTitle => 'Diesen Traum löschen?';
@override
String get dreamDeleteMessage =>
'Er verschwindet auf all deinen Geräten aus deinem Journal.';
@override
String get dreamDiscardTitle => 'Diesen Traum verwerfen?';
@override
String get dreamDiscardMessage =>
'Was du geschrieben hast, wird nicht gespeichert.';
@override
String get dreamDiscard => 'Verwerfen';
@override
String dreamWritten(String date) {
return 'Geschrieben am $date';
}
@override
String get todayDreamPrompt => 'Schreib den Traum der letzten Nacht auf';
@override
String get todayDreamPromptHint =>
'Bevor er verblasst. Versiegelt auf diesem Gerät.';
@override
String get settingsJournal => 'Journal';
@override
String get dreamPromptTitle => 'Nach dem Traum der letzten Nacht fragen';
@override
String get dreamPromptHint =>
'Unter Heute, von 4 bis 12 Uhr, bis du einen aufschreibst';
}

View file

@ -24,7 +24,7 @@ class AppLocalizationsEn extends AppLocalizations {
@override
String get accountDeleteMessage =>
'Your account is deleted on the server and on all your devices, with every sitting kept. This can\'t be undone.';
'Your account is deleted on the server and on all your devices, with every sitting and dream kept. This can\'t be undone.';
@override
String get aboutTitle => 'About Halkyon Meditate';
@ -440,6 +440,153 @@ class AppLocalizationsEn extends AppLocalizations {
@override
String get showStreakHint => 'On Today and in your Profile';
@override
String get shellTabJournal => 'Journal';
@override
String get journalSearch => 'Search dreams';
@override
String get journalEmptyTitle => 'Your dream journal';
@override
String get journalEmpty =>
'Write down a dream as soon as you wake, before it fades. Every entry is sealed on this device before it syncs: nobody else can read it, not even the instance.';
@override
String get journalNoMatch => 'No dream matches.';
@override
String get journalAdd => 'Write down a dream';
@override
String get journalFiguresTitle => 'Dream journal';
@override
String get figuresDreamsThisMonth => 'This month';
@override
String get figuresLucid => 'Lucid';
@override
String get markLucid => 'Lucid';
@override
String get markNightmare => 'Nightmare';
@override
String get markRecurring => 'Recurring';
@override
String get moodGlad => 'Glad';
@override
String get moodCalm => 'Calm';
@override
String get moodPuzzled => 'Puzzled';
@override
String get moodSad => 'Sad';
@override
String get moodUneasy => 'Uneasy';
@override
String get dreamNew => 'New dream';
@override
String get dreamLastNight => 'Last night';
@override
String dreamNight(String date) {
return 'Night before $date';
}
@override
String get dreamTextHint => 'What did you dream?';
@override
String get dreamTitleLabel => 'Title (optional)';
@override
String get dreamMore => 'Night, title, marks and more';
@override
String get dreamNightLabel => 'Night';
@override
String get dreamMarksLabel => 'Marks';
@override
String get dreamClarityLabel => 'Clarity';
@override
String get dreamClarityHint => 'How clearly you remember it, 1 to 5';
@override
String dreamClarityValue(int clarity) {
return 'Clarity $clarity/5';
}
@override
String get dreamMoodLabel => 'Mood on waking';
@override
String get dreamLabelsLabel => 'Labels';
@override
String get dreamLabelsHint => 'People, places, themes';
@override
String get dreamLabelAdd => 'Add label';
@override
String get dreamSave => 'Save';
@override
String get dreamEdit => 'Edit';
@override
String get dreamDelete => 'Delete';
@override
String get dreamDeleteTitle => 'Delete this dream?';
@override
String get dreamDeleteMessage =>
'It goes from your journal on all your devices.';
@override
String get dreamDiscardTitle => 'Discard this dream?';
@override
String get dreamDiscardMessage => 'What you wrote isn\'t saved.';
@override
String get dreamDiscard => 'Discard';
@override
String dreamWritten(String date) {
return 'Written $date';
}
@override
String get todayDreamPrompt => 'Write down last night\'s dream';
@override
String get todayDreamPromptHint => 'Before it fades. Sealed on this device.';
@override
String get settingsJournal => 'Journal';
@override
String get dreamPromptTitle => 'Ask for last night\'s dream';
@override
String get dreamPromptHint =>
'On Today, from 4 to 12 o\'clock, until you write one';
}
/// The translations for English, as used in the United Kingdom (`en_GB`).