Commit graph halkyon/apps/learn
Author SHA1 Message Date
d6b2bec113
perf(learn): flashcards moved in the card browser go at once, in one write
Moving the chosen cards to another studyset wrote them one at a time,
each looking up its privacy keys and writing its row, while the
browser still showed them. CardBrowserBloc now shows the move at once
(gone from here, or after the cards of the studyset they go into where
the browser shows it, _withMoves) and puts them back where the move
fails, counting the failure for the screen's snack bar
(moveFailures, cardBrowserMoveFailed).

CardRepository.moveTo hands the gateway every card's new position at
once (CardGateway.updateStudysets): SyncCardGateway reads them in one
query, checks every card's key before changing any (one private card
among them refuses the whole move, as before for each), rekeys those
that need it, and updates every row in one write and one sync request.
updateStudyset delegates to it.

Whats-New: Flashcards moved to another studyset in the card browser move at once.
Whats-New-De: Im Karteikarten-Browser in ein anderes Lernset verschobene Karteikarten sind sofort dort.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-11-08 11:48:41 +01:00
107e6f9913
perf(learn): discussions and feedback show what the person does at once
In a discussion, accepting an answer (or none), hiding or showing a post
and deleting one waited for the instance with every action disabled
(busy). DiscussionBloc now shows the discussion as the action leaves it
at once (Discussion.withAccepted, withPost, DiscussionPost.copyWith), the
actions staying available, and puts it back as it was where the instance
refuses (the refusal's snack bar as before) or can't be reached, then
reads it anew. Answering, commenting and editing still wait, as what
the instance makes of them is theirs to say.

In the feedback inbox, a thread opened shows read at once, a failure to
record it said to nobody (the next read shows the truth), and a decision
(closing, reopening, hiding, showing, declining) shows at once and is
put back with the failure's snack bar where it isn't written
(FeedbackThread.seen, decided). Accepting a suggestion still waits, as
it changes the author's content and can fail with SuggestionNotTaken.

Whats-New: Accepting, hiding and deleting in discussions, and closing or declining feedback, show at once.
Whats-New-De: Annehmen, Ausblenden und Löschen in Diskussionen sowie Schließen oder Ablehnen von Rückmeldungen zeigen sich sofort.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-11-08 11:48:41 +01:00
891e9dfd75
perf(learn): a conversation deleted goes at once, with an Undo
Deleting a kept conversation asked first and then waited while its
turns were blanked and deleted. It now goes as a card or a note does:
ConversationDeletions, a DelayedDeletions opened with the person's
data, takes it off the history at once (the list leaves out what is
pending), offers Undo for a moment on the sheet (hasSnackBars), and
blanks and deletes it once that is over; a deletion that fails shows
the conversation again, as every DelayedDeletions does. The question
before deleting goes, as Undo replaces it.

Whats-New: A conversation with an assistant is deleted at once, and Undo brings it back.
Whats-New-De: Eine Unterhaltung mit einem Assistenten wird sofort gelöscht, und „Rückgängig“ holt sie zurück.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-11-08 11:48:40 +01:00
920e1c6c13
perf(learn): Delete now leaves Recently deleted at once
Deleting a studyset for good blanks and deletes every card, note, path
and PDF of its subtree before the studyset's row goes, which took about
3 s while the studyset stayed in Recently deleted. Now the repository's
Recently deleted leaves it out at once (StudysetRepository.leaving and
stay, applied to the value last read without reading again), the
deletion runs in the background, still one at a time in
StudysetDeletions' queue, and where it fails the studyset shows there
again, read anew with what is left of it, and a snack bar says so.

Whats-New: Delete now in Recently deleted takes a studyset away at once.
Whats-New-De: „Jetzt löschen“ unter „Zuletzt gelöscht“ entfernt ein Lernset sofort.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-11-08 11:48:40 +01:00
56f40d4d65
refactor(ui,learn,todo,train,meditate): the navigation bar's one look, the debug section gone
The developer decided on 8 Nov 2026, after trying the variants on the
phone: the wave in the colour of the screen in view, without Material's
ripple, the indicator kept and there at once, a 1.2 s wave, top corners
of 20 dp, the bar's own neutral surface. Learn's bar stays cobalt, the
colour its tabs wash in, rather than its icon's purple.

So the bar has one look: FlushNavigationBar.waveDuration and
cornerRadius are constants, and the debug section goes from the four
Settings screens with what it switched (NavigationBarDebugSection,
debugNavigationBarStyle, effectiveNavigationBarStyle, the style record
and the variants A, C and F, the speeds, the corners and the tinted
surface). Their tests go too; those stating the look kept stay. Learn's
sign-out helper no longer has to scroll past the section.

ADR 0030's amendment of D60, the halkyon_ui README and Learn's
AGENTS.md record the decision and the alternatives set aside.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-11-08 11:48:39 +01:00
60d53ed262
feat(ui,learn): the navigation bar in the colour of the screen in view
The developer's review of 8 Nov 2026: the bar follows the screen in view
rather than one colour per app, so a Todo project of its own colour gives
the bar that colour while it shows. The wave, the chosen destination's
icon and label and its indicator take ScreenTint.color, the colour the
screen in view claims through its TopGradient; the frame's accent, the
app's colour, stays only for the moment before any screen claims one,
since ScreenTint itself starts at cobalt, which would be wrong for Todo,
Train and Meditate. Learn's tabs claim cobalt, their wash, so its bar is
cobalt there now, not purple.

A colour changing on the same tab (a project opening) passes into the
next over 300 ms, at once with animations off. A wave paints in that
colour as it goes: it starts in the colour of the screen being left, as
the arriving one claims its own only once it shows, a frame later, and
passes into it with the rest of the bar.

A person's colours may be light, so the chosen destination's colour is
made legible (readableTone, halkyon_ui): a palette colour starts from its
own text tone as Todo's dates take it (its outline in light mode, its
fill in dark), any other colour from itself, then darkens towards black
in light mode and lightens towards white in dark, only until it reads at
4.5:1 on the bar and on its indicator. Every palette colour is tested in
both themes, on the neutral and the tinted surface. This also lifts
Learn's label, which read at about 3:1.

The debug section gains Bar surface, neutral or tinted: tinted mixes 6 %
of the screen's colour into the bar's own surface in light mode and 8 %
in dark, changing with it. Neutral by default, and in the shipping style.

Whats-New: The navigation bar takes the colour of the screen you're on, such as a project's own.
Whats-New-De: Die Navigationsleiste nimmt die Farbe des Bildschirms an, auf dem du bist, etwa die eines Projekts.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-11-08 11:48:38 +01:00
45fd6595e1
feat(ui,learn,todo,train,meditate): a wave in the app's colour over the navigation bar
The developer's choice of 7 Nov 2026 (the mockup's variant E): a tap on
another destination sends a radial glow in the app's colour over the bar
from that destination's centre, solid to 35 % of its radius and gone at
70 %, growing to cover the bar (cubic(.2, .7, .2, 1)) while it fades from
half its strength in dark mode and a third in light, over 1.2 s, behind
the destinations and clipped to the bar. None for the destination already
chosen, none with animations off, and none for a tab changed without a
tap (a link, back): the wave answers the finger. The destination chosen
takes the app's colour, its indicator tinted 22 % with it, without
Material's ripple or the indicator growing; the bar's top corners are
rounded, 20 dp, the screen showing through them.

Each app gives the frame its colour (NavigationFrame's accent, from
PaletteColor): Learn's purple, its icon's last colour, Todo's strawberry,
Train's carrot and Meditate's fern, their screens' wash.

For trying it on a phone, a debug build's Settings has a section
Navigation bar (debug), halkyon_ui's NavigationBarDebugSection, switching
live, in memory alone: the variant (A Material only, C the wave with
Material's fill, E without it, F without the indicator too), the wave's
time (0.7 to 2 s) and the corners (0 to 28 dp). Release builds never read
it and show shippingNavigationBarStyle, the debug section's start: to
remove it, delete the section from the four Settings screens and
debugNavigationBarStyle, and let effectiveNavigationBarStyle return the
shipping style.

Whats-New: Tapping a tab sends a soft wave in the app's colour across the navigation bar, now with rounded corners.
Whats-New-De: Ein Tipp auf einen Tab schickt eine sanfte Welle in der Farbe der App über die Navigationsleiste, jetzt mit abgerundeten Ecken.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-11-08 11:48:37 +01:00
05db5e2f45
feat(learn,todo,train,meditate,ui): the navigation bar flush with the foot, a fade above it
Below medium windows the frame's bar is Material 3 Expressive's again,
flush with the window's foot (decided 7 Nov 2026 by the developer): the
theme's NavigationBar across the whole width, 64 dp in its own colour,
running on beneath the system's bar as Material's does. Right above it a
fade 32 dp high, from the background of the screen in view (the
ScreenTint's, the theme's surface before a screen claims one) made
see-through to that background, so what scrolls on beneath the bar fades
out rather than being cut off; taps pass through it. The rail of wider
windows stays as it was.

halkyon_ui's FloatingNavigationBar becomes FlushNavigationBar, its side
margin, lift and rounded card gone. Its coverOf is now the whole room
content leaves at its end, the system's bar included (the body's bottom
padding: bar, fade and system's bar inside the frame, the system's bar
alone outside it), and floating buttons rise by buttonLiftOf, the same
without the system's bar, which a Scaffold keeps them clear of itself.
Lists, Todo's hour grid and the Plan's sheet keep using coverOf; the
Profile's list no longer adds the system's bar a second time.

Whats-New: The navigation bar sits at the bottom edge again, with a soft fade above it.
Whats-New-De: Die Navigationsleiste sitzt wieder am unteren Rand, mit einem sanften Übergang darüber.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-11-08 11:48:37 +01:00
648dfe4051
refactor(learn): the editor's general parts move to halkyon_editor (ADR 0033, D37)
Todo stores editor documents too, so the document, the editor, its view,
the format bar, links, the clipboard, scripts and EditorBlocks move, with
their strings and tests, to a shared package on the same appflowy_editor
fork. What is Learn's alone stays in Learn on top of it, and what the
editor took from it directly is inverted: EditorBlocks gains textMarks,
formatActions and onEditorClosed, RichContentEditor a wrapEditor, and an
InsertableBlock's name takes the BuildContext. Learn's screens use
LearnContentEditor and LearnContentView, which add Learn's blocks and the
grammar check. Learn behaves as before.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-11-07 21:49:48 +01:00
34a693f365
fix(learn): a chosen chip keeps its icon
The check Material draws on a chosen chip covered the icon of the rest
day's chip and of the card browser's status chips, as in Meditate. Their
colour says they are chosen, as in Explore's.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-11-05 22:55:30 +01:00
53313978f1
chore(release): learn-v0.25.0
All checks were successful
CI / check (push) Successful in 47m20s
CI / deploy (push) Successful in 1m2s
Build 28.

Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-11-05 11:06:33 +01:00
23d3bcb4ca
feat(app,learn): the device's appearance and language, halkyon_app's, as Learn's Settings show them
The developer asked for Todo, Train and Meditate to offer Learn's
Appearance and Language (ADR 0014's amendment of 4 Nov 2026). Their
model, store, cubit and card move into halkyon_app: Appearance and Look
(a BCP 47 tag, null for the device's language), FileLookStore keeping it
in the app's support folder as JSON written by rename (per device, not
synced, read before the first frame), LookCubit for the apps to provide
above InstanceGate, and LookSettingsCard / DeviceLookCard with the texts
moved from Learn's arbs (look*, the same words). Each app passes the
languages it speaks, named in their own language.

Learn keeps its stored choice where it was, among its DeviceSettings,
and feeds the shared card from them: no migration, so nobody's language
or appearance moves, and nothing changes on its screen.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-11-05 08:58:40 +01:00
a7638e5e54
feat(learn): Settings' dictionaries on this device, one row a language
The developer's review of 4 Nov 2026. "Japanese on this device" becomes
"Dictionaries on this device", with one row a language: its name at the
start, what the device holds or that the download failed beneath it, and
its action at the end (Download with its size, the progress, Remove),
built as a list of rows so Korean's and Chinese's line up beneath
later. Japanese's is the only one now. The help speaks of a language's
dictionaries in general, then of what Japanese's hold (MeCab's, JMdict).

Whats-New: Settings lists the dictionaries on this device by language, each with its own Download or Remove.
Whats-New-De: Die Einstellungen listen die Wörterbücher auf diesem Gerät nach Sprache, jede mit eigenem Herunterladen oder Entfernen.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-11-05 08:58:39 +01:00
12bc31b982
fix(learn): a widget dropped while arranging Home stays where it was dropped, and its handle drags at once
Arranging Home kept no order of its own: a drop showed the old order
until the layout saved came back through the settings, once the
dashboard's changes settled 300 ms later, so the widget snapped back and
then jumped. _Arranging now holds the layout as arranged, shows each
change at once and saves it behind, taking a layout from the settings
only once its own changes are back (or, with none pending, as changed
on another device). The handle was an IconButton with an empty
onPressed inside the drag listener, whose tap and tooltip competed with
the drag; it is a plain icon now, named for screen readers.

Whats-New: Arranging Home's widgets, a widget stays where you drop it, and dragging starts at once.
Whats-New-De: Beim Anordnen der Widgets auf der Startseite bleibt ein Widget, wo du es ablegst, und das Ziehen beginnt sofort.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-11-05 08:58:39 +01:00
bda09a2959
feat(learn): Add material no longer offers flashcards from a text (ADR 0021, D56 amended)
The developer's review of 4 Nov 2026. The entry goes from a studyset's
Add material, and with it the sheet for pasting a text
(proposeCardsFromText, _TextSheet), its three texts and proposeCards'
fromStudyset, which only it set to false. A note's and a PDF page's
flashcards stay; their tests now drive the proposals sheet from a note.

Whats-New: Add material no longer offers Flashcards from a text; a note's or a PDF page's flashcards stay.
Whats-New-De: Material hinzufügen bietet keine Karteikarten aus einem Text mehr an; die aus einer Notiz oder einer PDF-Seite bleiben.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-11-05 08:58:38 +01:00
bc4953aaf8
fix(learn,ui): the open assistant field holds still: no flicker, no sliding away, no resizing under the keyboard (ADR 0021, D69)
Inside a studyset the field flickered as it opened and closed: the
floating location switched between a raised centre and the centre,
which Scaffold animates by scaling the button out and in. It keeps one
location now, raised above the licence line by padding. While open it
no longer slides away as the studyset or the Library scrolls
(ScrollHidingFab.pinned, new), and the keyboard covers Home, the
Library, a studyset and a session rather than shrinking them; the field
lifts itself by as much of the keyboard as reaches above it.
NavigationFrame no longer resizes for the keyboard itself, leaving it
to each tab's own scaffold, which still does by default (Todo's,
Train's and Meditate's tests pass unchanged).

Whats-New: Inside a studyset, the question field opens without flickering, stays while you scroll, and the screen no longer jumps when the keyboard comes.
Whats-New-De: In einem Lernset öffnet sich das Fragefeld ohne Flackern, bleibt beim Scrollen stehen, und der Bildschirm springt nicht mehr, wenn die Tastatur kommt.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-11-05 08:58:38 +01:00
f45ed98d52
feat(learn): one assistant field across the app, kept from tab to tab (ADR 0021, D69)
Each screen's button kept its own open state and its own draft, so the
question half written on the Library was gone on Home. ComposerCubit,
provided beside ConversationDockCubit, now holds whether the field is
open and the draft (text, files attached, parts chosen) for every
button. Switching tabs keeps it as it is (AppShell says so before
goBranch); going to another screen within a tab, or over the tabs, keeps
it open only while something is written or attached. Only the button of
the screen at the router's location unfolds, so no tab kept alive
behind holds a second field.

Whats-New: The question you are writing stays open as you switch tabs.
Whats-New-De: Die Frage, die du schreibst, bleibt offen, wenn du den Tab wechselst.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-11-05 08:58:38 +01:00
b4b27a1d0c
feat(learn,app): the field's row as one, its model by name in its provider's colour, the microphone dictating (ADR 0021, D69)
The developer's review of 4 Nov 2026. Below the question, × and + stand
at the start, who answers is centred and the conversations and the
microphone stand at the end, every button the same rounded square, the
microphone listening and Send too. The row above the text ("Only what I
choose · 2 parts") goes, as +'s sheet offers the same choice; a web
search gets + with that row alone. Who answers shows its model's name
alone ('Haiku 4.5', AssistantProvider.modelName: Anthropic's without
"Claude", others as they are), outlined in its provider's colour (D60).
The microphone is halkyon_app's Dictation: what is said goes where the
cursor stands, and it listens again after each pause until stopped. It
gives way to Send once anything could be sent, as Claude's field does.

Whats-New: The question's buttons are in one row: close and + on the left, who answers in the middle, Conversations and the microphone on the right.
Whats-New: What goes along is chosen under +, for the web search too.
Whats-New: Who answers shows the model's name, in its provider's colour.
Whats-New: The microphone keeps listening after a pause, writing where the cursor is, until you stop it.
Whats-New-De: Die Knöpfe der Frage stehen in einer Reihe: Schließen und + links, wer antwortet in der Mitte, Unterhaltungen und Mikrofon rechts.
Whats-New-De: Was mitgeht, wählst du unter +, auch für die Websuche.
Whats-New-De: Wer antwortet, zeigt den Namen des Modells, in der Farbe seines Anbieters.
Whats-New-De: Das Mikrofon hört nach einer Pause weiter zu und schreibt, wo der Cursor steht, bis du es stoppst.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-11-05 08:58:37 +01:00
2d27451cb8
fix(app,learn,meditate): a device without speech recognition says so, and how to get one
On GrapheneOS, which installs no recogniser, the microphone did nothing
and stayed listening: speech_to_text's initialize throws a
PlatformException (recognizerNotAvailable) there rather than answering
false, and DeviceSpeechInput let it through. A listen now answers how it
went (SpeechStart: listening, noRecognizer, unavailable) and never
throws; Learn's field and Meditate's dream say that no speech
recognition is installed and name two to install (FUTO Voice Input,
Google's speech services), in halkyon_app's texts. It also passes
androidNoBluetooth, as the apps declare no Bluetooth permission the
plugin would otherwise ask for.

Whats-New: Where the device has no speech recognition, the microphone says so and how to get one.
Whats-New-De: Wo das Gerät keine Spracherkennung hat, sagt das Mikrofon das und wie du eine bekommst.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-11-05 08:58:37 +01:00
00212bf49c
feat(learn): voice mode goes, as a spoken conversation isn't wanted (ADR 0021, D68 withdrawn)
The developer's review of 4 Nov 2026: "an actual conversation with the AI
chatbot is not desirable and necessary". The field's voice-mode button,
the screen it opened, reading answers aloud and its texts go, and with
them flutter_tts, its patched copy in third_party/ (one override fewer),
the TTS_SERVICE query in the manifest and the plugin's registrants. The
microphone stays and dictates into the question. ImageApproval is the
conversation sheet's own again, as only voice mode needed it.

Whats-New: Voice mode is gone; the microphone dictates into your question.
Whats-New-De: Der Sprachmodus ist weg; das Mikrofon diktiert in deine Frage.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-11-05 08:58:36 +01:00
3310ffa1cd
chore(release): learn-v0.24.0
All checks were successful
CI / check (push) Successful in 45m15s
CI / deploy (push) Successful in 1m0s
Build 27.

Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-11-04 15:29:48 +01:00
2ec58bfaee
fix(deploy): the images drop third_party/'s overrides instead of copying it
Two deploys failed building the images (4 Nov 2026): first the
dictionaries' image lacked third_party/ (pub refuses an override whose
path is missing, exit 66), then, with it copied, pub refused the
overrides themselves, as the three plugins depend on the Flutter SDK,
which the dart image doesn't have (exit 69); every image would have.
No image's package uses them, so tool/workspace_only.sh, which already
narrows the root pubspec for an image, drops the overrides naming
third_party/, and no Dockerfile copies it.

Each image's build stage replayed from this commit with a standalone
Dart 3.13.4, a fresh pub cache, dash and mawk: all four build, and the
commit deployed before fails as the deploy did.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-11-04 13:43:46 +01:00
70a334077a
fix(learn): the dictionaries' image copies third_party/, as the servers' do
Some checks failed
CI / check (push) Successful in 43m57s
CI / deploy (push) Failing after 16s
The deploy of 55c73a7e failed building it: the root's dependency
overrides name the three patched plugins by path, and pub refuses a
path that isn't there (exit 66), though the curator uses none of them.
The servers' Dockerfiles got the copy with the overrides; the
curator's was missed. Checked by running each image's steps up to
`dart pub get` in an empty folder: the curator's failed as on the
server before, and all four images resolve now.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-11-04 12:05:36 +01:00
438ce20dbf
build: Android's built-in Kotlin, the three plugins applying KGP themselves patched in third_party/
AGP 9 compiles Kotlin itself (android.builtInKotlin=true in every app),
and warns that applying the Kotlin Gradle Plugin will soon fail.
flutter_web_auth_2 5.1.0, keyboard_height_plugin 0.3.0 and flutter_tts
4.2.5 still apply it unconditionally, so their pub releases are copied
into third_party/ with only their android/build.gradle changed, and
the root overrides them by path (third_party/README.md says when to
drop each). The servers' images copy third_party/, as pub refuses an
override whose path is missing. third_party/ isn't our own code: left
out of the analysis and the format check.

All four apps' APKs build without the warning. A Kotlin daemon's
session file that slipped into Train's android/ goes, and every app's
android/.gitignore keeps .kotlin/ out.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-11-04 09:32:13 +01:00
86f221d02f
style(learn): the photo picker formatted
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-11-04 09:32:11 +01:00
918080ce46
Merge branch 'learn-fb' into integration 2026-11-04 09:32:06 +01:00
df713431dd
feat(learn): the lock beside a studyset no longer changes its privacy, which Edit does
The developer's review of 3 Nov 2026: a studyset is made private, or
non-private, in Edit alone. The lock's sheet still says what its privacy
means, offers publishing one that isn't private, and finishes one only
partly private.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-11-04 09:32:05 +01:00
830aea10fe
feat(learn,app): explanations wait behind a question mark beside their title
What explains a setting or a section, rather than what is the person's
own, moves behind halkyon_ui's HelpButton, beside its title through the
new ExplainedTitle (the developer's review of 3 Nov 2026): in Learn's
settings (citations, the Pomodoro timer, grammar, the web search,
Jellyfin, the device's dictionaries, counting one's learning), the study
settings, export's progress, the Jellyfin tags and the study button's
back first; in halkyon_app, setting up a password, adding SimpleLogin,
own keys not allowed, the profile page's link back and the operator's
grant sheet.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-11-04 09:32:05 +01:00
c984678c5b
feat(learn): voice mode, a conversation spoken as Claude's "Let's talk" (ADR 0021, D68)
Beside the field's microphone, a button opens voice mode over the whole
screen: a glow rising from below, a big microphone, and +, who answers,
the speaker and End in one row. It listens with the device's recogniser,
asks what was heard in one kept conversation, reads the answer aloud
with the device's speech (flutter_tts, its Markdown left out) and
listens again. On Linux there is none, as there is no microphone; where
nothing reads aloud, answers are shown as text.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-11-04 09:32:04 +01:00
7be31dcbfc
feat(learn): + opens a sheet as Claude's, Camera, Photos and Files, and what goes along in rounded rows
Following the developer's review of 3 Nov 2026 (ADR 0021, D67):

- + below the assistant's field, and below a conversation's follow-up,
  opens "Add to your question" as Claude's "Add to session": three big
  tiles side by side, Camera (where the device takes photos) and Photos
  for an assistant reading images, and Files; below them, in the field, a
  row leading to what goes along, showing how far the assistant looks and
  how many parts are chosen.
- Camera and Photos through image_picker (flutter.dev's own): the camera's
  own app and the system's photo picker, no permission asked on Android, a
  file dialog on the desktop, which has no Camera; photos made at most
  2048 pixels a side, read on the device like any file attached.
- What goes along is chosen in rounded rows, as Claude's Permission, in
  place of radio buttons and chips: how far it looks with a tick, private
  studysets with a switch, the parts each with a tick, a card's image with
  its thumbnail.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-11-04 09:32:04 +01:00
a7904bac05
feat(learn): a studyset is made private in Edit, no longer from its options
The developer's review of 3 Nov 2026: Make private and Make non-private
leave a studyset's options (its card's menu, its screen's); a top-level
studyset's Edit has a Visibility section instead, its switch making it
private, or non-private, at once once the person agreed in the dialog as
before (ADR 0019), not on Save, and what that means behind the question
mark. A topic's Edit offers none, its privacy being its tree's. The lock on
a studyset's card still explains its privacy and keeps its own button.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-11-04 09:32:04 +01:00
96b1a0fc64
feat(learn): the assistant's field spans the screen, its buttons boxed and still as who answers changes
Following the developer's review of 3 Nov 2026 (ADR 0021, D67):

- Unfolded in the Library, the field spans the screen between its margins,
  as on Home and a studyset: it fell short on the right, by the padding
  around the two folded buttons, which now stands only around those.
- Who answers stands right before the buttons at the right, which no longer
  shift as its name changes (Claude Haiku, Claude Opus): it fills the space
  left, aligned to its end, rather than a share of it.
- Every button of the field and of a conversation's follow-up is boxed, a
  tile a shade above the field with rounded corners, as Claude's apps have
  them; who answers too.
- On a studyset with a licence, the unfolded field takes its usual place
  over the licence's line rather than squeezing in above it; folded, its
  row still keeps clear of the line.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-11-04 09:32:03 +01:00
8c70ad98a1
feat(app): an authenticator's key copied, marked sensitive, and its code six digits
A phone without a camera whose authenticator doesn't open otpauth: had no
way but typing the key. It is copied again, the key alone (ADR 0017
amended, the developer's decision of 3 Nov 2026): on Android through each
app's SensitiveClipboard.kt, marked EXTRA_IS_SENSITIVE, so Gboard and the
system show no preview and keep it out of their histories; elsewhere through
Flutter's clipboard. Emptied 60 seconds later if the clipboard still holds
it. The recovery phrase and codes stay never copied.

The code's field counted "/7", taking an optional space for a seventh
character: it now takes six digits alone, a pasted space dropped, and counts
nothing.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-11-04 09:32:01 +01:00
603218ef96
refactor(app): the device's speech recognition moves from Learn into halkyon_app, with dictation into a text
Learn's `SpeechInput` and `DeviceSpeechInput` (ADR 0021, D67) are
halkyon_app's now, and its `FakeSpeechInput` is in `testing.dart`, so
Meditate's dream journal (ADR 0038, D15) listens through the same code.
Learn imports them from there and behaves as before: its composer and
its tests are unchanged but for the imports, and `DeviceSpeechInput()`
asks for what it asked before.

`DeviceSpeechInput` takes three options, off by default:
- `preferOnDevice`: on Android, recognition on the device where it can
  (Android 12 on), else offline; should that fail before a word is
  heard (not a pause), it listens as Android otherwise does from then
  on.
- `pauseFor`: how long a pause may last before the recogniser stops.
- `listenFor`: how long one listen may last at most.

`Dictation` writes what is heard where the cursor stands
(`DictationInsertion`: in place of a selection, set apart by a space),
shows the words so far, and listens again after each pause while it is
on, stopping by itself after three listens running heard nothing.

speech_to_text moves from Learn's pubspec to halkyon_app's, the lock
unchanged. Todo, Train and Meditate link the plugin now; their
generated plugin registrants (macOS, Windows) say so. An app declares
the microphone itself, and Todo and Train declare none, so nothing
there can listen.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-11-04 09:31:58 +01:00
996e584338
chore(release): learn-v0.23.0
All checks were successful
CI / check (push) Successful in 40m57s
CI / deploy (push) Successful in 1m1s
Build 26.

Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-11-03 17:47:36 +01:00
ac387b5250
feat(icons): every icon fades from cobalt into its app's backdrop; Learn shows its own inside the app; Train's dumbbell (ADR 0031, D5 to D7)
All checks were successful
CI / check (push) Successful in 41m0s
CI / deploy (push) Successful in 4m37s
Each icon now ends in the colour its app's screens wash down from: Learn cobalt into
purple, Todo through violet into strawberry (Learn's former gradient), Train unchanged
into carrot, Meditate's to come through pacific into fern (Todo's former). Train's glyph
is Lucide's dumbbell, Meditate's will be the sprout.

Learn shows its graduation cap atop the rail, on the launch screen and on halkyon_app's
screens, as Todo and Train show theirs; the kingfisher leaves the app and stays the
F-Droid repository's icon.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-11-02 23:12:20 +01:00
3ac1880ac8
build(deploy)!: the instance's compose file is compose.learn.yaml, beside compose.train.yaml and compose.todo.yaml
No file has Compose's default name any more, so .env's COMPOSE_FILE is always set,
naming compose.learn.yaml first (.env.example sets it). compose.yaml stays as a link to
compose.learn.yaml for the one deploy that switches learn.halkyon.eu's .env over: the
deploy's merge runs with the COMPOSE_FILE from before. A later commit removes it.

BREAKING CHANGE: an instance's deploy/.env must set COMPOSE_FILE, starting with
compose.learn.yaml, before the link goes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-11-02 23:12:15 +01:00
5469ce791f
build(deploy): the server's checkout is ~deploy/halkyon, the repository's name
All checks were successful
CI / check (push) Successful in 40m10s
CI / deploy (push) Successful in 58s
The folder kept its name from before the monorepo (ADR 0015), though
Train and Todo deploy from it too. redeploy.sh, the backup's units and
the operator's steps name ~deploy/halkyon; the Compose project keeps the
name halkyon-learn, as Docker names its volumes after it.

The server moves after this is deployed: the running redeploy.sh is read
whole before it runs, so the deploy of this commit still works in the
old folder; then the folder is moved, the pinned commands in deploy's
authorized_keys and the backup's units changed, and the containers
recreated on the new paths.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-11-02 18:56:39 +01:00
b7b0b937fe
feat(app): one Profile for every app, the person's own tab first, then each app's (ADR 0014)
The public profile was already one per person for every service of an
instance (the register, ADR 0016); its app side was Learn's alone. It
moves into halkyon_app: the model, the repository on the module's
endpoints, the form, the avatar drawn on the device and the sheet with
someone's profile. They take the Public prefix (PublicProfileRepository,
BasePublicProfileGateway), as Train's own ProfileRepository holds the
body's figures. AppDependencies gains profiles and files, the picker the
avatar is chosen with, which moves out of Learn too.

HalkyonProfile is every app's Profile screen: the person's own tab first,
the same in every app (the account and the ways they sign in, the app's
own cards, then the public profile), then the app's own tabs, folding into
the title. Learn adds its inbox and Statistics. Train adds Progress, Days
and, where nothing records, Plans, and so opens on the person's tab, no
longer on Progress. Todo, which has no Profile tab, opens it from a row at
the top of Browse, where Todoist keeps the account.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-11-02 11:11:11 +01:00
a0bb75f1ad
feat(learn): pick up learning on Home, the studysets studied latest first, and Progress closer to Vaia's
Home gets a widget of its own, Pick up learning, below the goal: the
studysets at the top of the library that were studied, the one rated last
first, so one studied long ago sinks. Three show, the others behind Show
more. Each shows its name, which opens it, how much is learnt, when it was
last studied, and today's task (cards to review, new cards to learn), which
starts studying it, outlined while reviews wait. The progress is the
library's, worked out once for every screen; when each was last studied
comes from the reviews Home reads anyway. With nothing studied it takes no
room.

A dashboard arranged before it gets the widget below the goal, wherever
the goal is: a widget a layout misses now goes after the one before it in
a new dashboard, no longer at the end.

On a studyset's Progress, the tiles are filled without an outline, To
review has a history icon (amber while cards are due), the learnt bar is
thicker, and the exam follows the figures, as Vaia asks for its date
there: an invitation to choose it, opening the study plan's sheet, or,
once chosen, how ready the person is.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-11-02 09:17:07 +01:00
447e00c52a
feat(learn): the assistant's field laid out as Claude's, with files attached and questions spoken, and no Check (ADR 0021, D67)
The question's text stands above its buttons: + attaching files, who
answers as a chip (Effort now a submenu of it), the conversations kept,
and the microphone while nothing is written, which gives way to the
button sending. What goes along is one row above the text, opening a
sheet with how far the assistant looks and the parts to send. A
conversation's follow-up has the same + and microphone.

Files are read on the device: an image goes as itself to an assistant
that reads images, a PDF as its pages' text, a text file as its text,
each cut at 20,000 characters and labelled by its name after the
question.

Voice is the device's own speech recognition (speech_to_text: Android,
Apple, Windows; none on Linux), in the app's language, the microphone
asked for once tapped. The privacy page says that Android's recogniser
may send the sound to Google or the phone's maker.

The field's Check goes, with its LanguageTool sheet (amending ADR 0024);
the editors' underlining and Why? stay.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-11-02 08:46:37 +01:00
5641285d31
feat(learn): Explore by what a licence allows, a skeleton while the library loads, and an assistant's agreement per provider (ADR 0005, rule 12; ADR 0021, D66)
- Explore filters by what a studyset's licence lets a learner do: adapt
  it (no NoDerivatives) and use it commercially (no NonCommercial),
  which the instance reads from the release's licence
  (ExploreQuery.adaptable, commercial) and the app the same way
  (LicenseUse). An instance from before ignores both.
- While the studysets are read, the Library shows shimmering skeletons
  of their cards rather than a spinner: halkyon_ui's new Shimmer and
  SkeletonBox, still where the system asks for no animations.
- The scope's "Include private studysets" is offered only while the
  person has a private studyset.
- An agreement to what goes where is per provider (protocol and host):
  agreeing to one assistant agrees to every assistant of that provider,
  those granted later too, and the agreement sheet says so. No
  migration: it is written on every grant of the provider, so taking
  one back keeps it for the others, and read per provider from any of
  them.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-11-02 05:19:18 +01:00
138c4dd46e
feat(learn): the assistant's button folded on Home too, the conversation at full height folding into it, and smaller fixes from the developer's list (ADR 0021, D64, D65)
- Home's assistant starts folded into its button at the lower left, as
  in the Library, where a field stood at its foot.
- The button has Material 3's floating action button shape (a rounded
  square) rather than a circle, its gradient ring kept.
- On a studyset below expanded windows it stands in one row with Add
  material and Study rather than above them.
- While studying, the ratings below the card make way while its field
  is open rather than standing under it.
- The conversation opens at the window's full height, its header above
  the list, so a drag on the header moves the sheet and a drag on the
  list scrolls it. Close ends the conversation; any other way out folds
  it into the button (ConversationDockCubit, above the router), its
  answer still coming, until any button opens it again or another
  conversation starts.
- An answer's Copy icon lines up with the answer's text.
- A sense's grammar notes run on after its glosses as one flowing text,
  so a short note such as "n" no longer takes a line of its own.
- A card's hints show their count in a grey badge beside the title, so
  it no longer reads as the hint's number.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-11-02 05:19:18 +01:00
4898d0a04a
feat(learn): the assistant's conversation a dialog on the desktop and in expanded windows (Todo's ADR 0033, D11)
- halkyon_ui's sheetShowsAsDialog says whether showAdaptiveSheet shows
  a dialog here, for a sheet of its own that can't go through it.
- Learn's conversation, a half-height sheet dragged up, asks it: where
  sheets are dialogs it is a dialog up to 720 wide and as tall as the
  window, with no handle to drag.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-11-01 18:20:58 +01:00
b908019969
feat(todo): dialogs on the desktop, the tabs reordered, a task's path and menu (ADR 0033, D11 to D14)
- halkyon_ui: showAdaptiveSheet shows a dialog on Linux, macOS and Windows
  at every width (bottom sheets stay on phones and tablets below expanded
  windows), so Learn and Train follow; SheetScaffold takes a heading in
  the title's place and may leave out its close button.
- Todo: the tabs are Inbox, Today, Upcoming, Browse, Today still opening;
  `serverpod start` launches the app on Linux, as it has no web build.
- A task's sheet shows where the task is (its project, an icon once tasks
  are above it, and the tasks above it, each opening what it names) and a
  ⋮ menu in place of its close button: when it was added (its UUID v7),
  Duplicate with its sub-tasks, hiding its done sub-tasks (synced in its
  payload), Close in the pane, Delete after asking. The description's icon
  stays by its first line.
- A row names its project and section at the end of its date's line; every
  menu entry has its icon.
- Learn: a studyset's path starts at the Library tab's icon, named by its
  tooltip.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-11-01 16:53:38 +01:00
7727317c68
feat(app): what's new after an update, drafted by the release tool (ADR 0030, D61)
halkyon_app's WhatsNew reads what each release changed from the app's
assets/whats_new/ (<version>.<language>.txt, English and German, a change
a line): after an update a sheet lists every release since the one the
device showed last, newest first, once; Settings shows the running
version's again, above About. Nothing on a fresh install.

tool/release.dart drafts the missing texts from the commits that count
after "Go on?" (German marked TRANSLATE:), opens them in the editor until
they fit (no mark left, F-Droid's 500 characters) and commits them with
the version. The English is the Forgejo notes' What's new, each language
F-Droid's changelog of the build, written at publishing and copied in by
tool/fdroid_repo.sh. Learn and Train bundle their texts, Learn's 0.22.0
and Train's 0.1.0 written to start.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-31 19:36:20 +01:00
581cf1f1cd
feat(ui): tabs folding into the title, in Learn's and Train's Profile (ADR 0030, D60)
halkyon_ui's FoldingTabs: the tab row under the app bar's title at the
top, scrolling away with the content, the chosen tab's name then small
under the title, which, tapped, scrolls back up to the row, as Strava's.
Learn's Profile (Profile, Statistics) is built on it, and Train's
Profile in tabs: Progress (your body, the last 12 weeks, this year),
Days, and Plans where the device doesn't record.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-31 19:12:11 +01:00
b45db6ea7c
feat(ui): every app's frame around its tabs, Learn's moved into halkyon_ui
NavigationFrame is Learn's AppShell without its router and its tabs: a
navigation rail from medium windows on with the app's icon on top,
leading to the first tab, a bar floating above the foot below, the
background of the screen in view around it, and a tick on every choice.
TabsStack is Learn's container of the tabs' navigators, every tab kept
alive and told whether it shows. Learn's shell is built on both, its
tests unchanged; Train's is next.

The rail's icon is drawn from 120 px at most in memory, as Learn's was.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-30 23:52:39 +01:00
46b2b9bd09
build: CI and the snap build with Flutter v3.47.8, as the laptop does
Some checks failed
CI / check (push) Has been cancelled
CI / deploy (push) Has been skipped
A patch release; the release tool refuses to build with another Flutter
than CI tests with.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-30 17:43:24 +01:00
0ec15d2fb2
fix(learn): nothing of Learn's goes to Android's backup or to another phone (ADR 0019, D9)
Some checks failed
CI / check (push) Has been cancelled
CI / deploy (push) Has been cancelled
The device keeps private studysets open in its database, which Android's
backup (Google's, or Seedvault where turned on) and a transfer to a new
phone took open. allowBackup and fullBackupContent are off, and from
Android 12 on the data extraction rules exclude every folder from the
transfer too, which allowBackup alone no longer stops.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-30 16:46:23 +01:00