refactor(content,core,app,editor,learn): Learn's content in pure Dart, for its agent

ADR 0044's D49 (amended in building it): what reads and seals Learn's
content moves out of the Flutter packages, so Learn's agent, which runs
without Flutter, shares it rather than repeating the privacy rules.

packages/halkyon_content (new, pure Dart) takes from halkyon_app the
envelope and its cipher, SealedFile, UnlockedAccountKeys, shared
spaces' gateway with SharedKeys and KeyPins, FileCache and
FileServerGateway, with their fakes and tests; halkyon_app exports it,
so no app changes. Its rich_text.dart takes RichContent from
halkyon_editor, which exports it. newAccountKeys makes an agent's key
pair. halkyon_core takes DelayedDeletions and gains SyncRequests, what a
synced gateway asks of the sync, which SyncService implements.

apps/learn/halkyon_learn_core (new, pure Dart) takes Learn's privacy,
sync, payloads and the models, codecs, gateways and repositories of
studysets, cards, notes, tags and feedback, with what they reach of
files, learning paths, citations and study options; the app imports them
from there. A subject's icon, colour and name stay in the app
(SubjectLook); what only the app shows (progress, exam coverage, the
card browser, deletions' waits) stays too. Nothing changes for people
using the apps.

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-11 21:24:30 +01:00
commit d09625d43e
Signed by: raisondetredev
GPG key ID: 07F6FCF405488B64
14 changed files with 3517 additions and 2007 deletions

View file

@ -1,6 +1,6 @@
import 'dart:typed_data';
import 'package:halkyon_learn_flutter/src/core/asset/data/asset_gateway.dart';
import 'package:halkyon_learn_core/core/asset/data/asset_gateway.dart';
/// An asset row as tests write one: readable, unless
/// `FakeAssetGateway.receiveRemote` is told its key.

View file

@ -4,7 +4,7 @@ import 'dart:typed_data';
import 'package:halkyon_app/halkyon_app.dart';
import 'package:halkyon_app/testing.dart';
import 'package:halkyon_learn_flutter/src/core/asset/data/asset_sealing.dart';
import 'package:halkyon_learn_core/core/asset/data/asset_sealing.dart';
/// [AssetSealing] for tests: the studysets in [keys] are private, sealed
/// with the key named; the others readable. Files are sealed with

View file

@ -2,8 +2,8 @@ import 'dart:async';
import 'dart:typed_data';
import 'package:clock/clock.dart';
import 'package:halkyon_learn_flutter/src/core/content/payload.dart';
import 'package:halkyon_learn_flutter/src/feature/card/data/card_gateway.dart';
import 'package:halkyon_learn_core/core/content/payload.dart';
import 'package:halkyon_learn_core/feature/card/data/card_gateway.dart';
/// A [CardGateway] in memory. Ids sort by creation, as UUID v7 ids do: cards
/// are `card-0001`, `card-0002`, …, revisions `rev-0001`, …;

View file

@ -1,6 +1,6 @@
import 'dart:typed_data';
import 'package:halkyon_learn_flutter/src/core/privacy/data/content_key_gateway.dart';
import 'package:halkyon_learn_core/core/privacy/data/content_key_gateway.dart';
/// [ContentKeyGateway] in memory; several devices of one person share one,
/// as their sync would.

View file

@ -1,4 +1,4 @@
import 'package:halkyon_learn_flutter/src/feature/card/model/card_content.dart';
import 'package:halkyon_learn_core/feature/card/model/card_content.dart';
import 'package:halkyon_learn_flutter/src/feature/dictionary/data/dictionary_gateway.dart';
import 'package:halkyon_learn_flutter/src/feature/dictionary/model/dictionary.dart';

View file

@ -1,7 +1,7 @@
import 'dart:async';
import 'package:halkyon_editor/halkyon_editor.dart';
import 'package:halkyon_learn_flutter/src/core/profile/public_person.dart';
import 'package:halkyon_learn_core/core/profile/public_person.dart';
import 'package:halkyon_learn_flutter/src/feature/discussion/data/discussion_gateway.dart';
import 'package:halkyon_learn_flutter/src/feature/discussion/model/discussion.dart';

View file

@ -3,9 +3,9 @@ import 'dart:convert';
import 'dart:typed_data';
import 'package:halkyon_app/halkyon_app.dart';
import 'package:halkyon_learn_flutter/src/feature/feedback/data/feedback_envelopes.dart';
import 'package:halkyon_learn_flutter/src/feature/feedback/data/feedback_gateway.dart';
import 'package:halkyon_learn_flutter/src/feature/feedback/model/feedback.dart';
import 'package:halkyon_learn_core/feature/feedback/data/feedback_envelopes.dart';
import 'package:halkyon_learn_core/feature/feedback/data/feedback_gateway.dart';
import 'package:halkyon_learn_core/feature/feedback/model/feedback.dart';
/// A [FeedbackWriteGateway] in memory: what was written, in order.
class FakeFeedbackWriteGateway() implements FeedbackWriteGateway {

View file

@ -1,8 +1,8 @@
import 'dart:async';
import 'dart:typed_data';
import 'package:halkyon_learn_flutter/src/core/content/payload.dart';
import 'package:halkyon_learn_flutter/src/feature/learning_path/data/learning_path_gateway.dart';
import 'package:halkyon_learn_core/core/content/payload.dart';
import 'package:halkyon_learn_core/feature/learning_path/data/learning_path_gateway.dart';
/// A [LearningPathGateway] in memory. Ids sort by creation, as UUID v7 ids
/// do: paths are `path-0001`, …, steps `step-0002`, …, revisions

View file

@ -2,8 +2,8 @@ import 'dart:async';
import 'dart:typed_data';
import 'package:clock/clock.dart';
import 'package:halkyon_learn_flutter/src/core/content/payload.dart';
import 'package:halkyon_learn_flutter/src/feature/note/data/note_gateway.dart';
import 'package:halkyon_learn_core/core/content/payload.dart';
import 'package:halkyon_learn_core/feature/note/data/note_gateway.dart';
/// A [NoteGateway] in memory. Ids sort by creation, as UUID v7 ids do: notes
/// are `note-0001`, `note-0002`, …, revisions `nrev-0001`, …, each written

View file

@ -1,8 +1,8 @@
import 'dart:async';
import 'dart:typed_data';
import 'package:halkyon_learn_flutter/src/core/privacy/model/unsigned_edit.dart';
import 'package:halkyon_learn_flutter/src/feature/studyset/data/studyset_gateway.dart';
import 'package:halkyon_learn_core/core/privacy/model/unsigned_edit.dart';
import 'package:halkyon_learn_core/feature/studyset/data/studyset_gateway.dart';
/// A [StudysetGateway] in memory: ids are `studyset-1`, `studyset-2`, …, and
/// [receiveRemoteChange] and [receiveRemoteDeletion] play a sync that brought

View file

@ -1,7 +1,7 @@
import 'dart:async';
import 'dart:typed_data';
import 'package:halkyon_learn_flutter/src/feature/tag/data/tag_gateway.dart';
import 'package:halkyon_learn_core/feature/tag/data/tag_gateway.dart';
/// A [TagGateway] in memory: ids are `tag-001`, `tag-002`, … and
/// `card-tag-001`, … and `note-tag-001`, …, in the order of creation as UUID

View file

@ -1,4 +1,4 @@
import 'package:halkyon_learn_flutter/src/feature/studyset/data/studyset_tabs.dart';
import 'package:halkyon_learn_core/feature/studyset/data/studyset_tabs.dart';
/// Keeps the studysets' tabs in memory.
class MemoryStudysetTabStore([Map<String, String> initial = const {}])

View file

@ -1,4 +1,4 @@
import 'package:halkyon_learn_flutter/src/feature/card/model/card_content.dart';
import 'package:halkyon_learn_core/feature/card/model/card_content.dart';
import 'package:halkyon_learn_flutter/src/feature/dictionary/model/dictionary.dart';
import 'package:halkyon_learn_flutter/src/feature/jellyfin/model/jellyfin.dart';

View file

@ -3,8 +3,24 @@ import 'dart:typed_data';
import 'package:halkyon_app/halkyon_app.dart';
import 'package:halkyon_app/testing.dart';
import 'package:halkyon_learn_flutter/src/core/asset/data/asset_gateway.dart';
import 'package:halkyon_learn_flutter/src/core/asset/data/asset_repository.dart';
import 'package:halkyon_learn_core/core/asset/data/asset_gateway.dart';
import 'package:halkyon_learn_core/core/asset/data/asset_repository.dart';
import 'package:halkyon_learn_core/feature/card/data/card_deletions.dart';
import 'package:halkyon_learn_core/feature/card/data/card_gateway.dart';
import 'package:halkyon_learn_core/feature/card/data/card_repository.dart';
import 'package:halkyon_learn_core/feature/feedback/data/feedback_envelopes.dart';
import 'package:halkyon_learn_core/feature/feedback/data/feedback_gateway.dart';
import 'package:halkyon_learn_core/feature/feedback/data/feedback_repository.dart';
import 'package:halkyon_learn_core/feature/learning_path/data/learning_path_gateway.dart';
import 'package:halkyon_learn_core/feature/learning_path/data/learning_path_repository.dart';
import 'package:halkyon_learn_core/feature/note/data/note_deletions.dart';
import 'package:halkyon_learn_core/feature/note/data/note_gateway.dart';
import 'package:halkyon_learn_core/feature/note/data/note_repository.dart';
import 'package:halkyon_learn_core/feature/studyset/data/studyset_gateway.dart';
import 'package:halkyon_learn_core/feature/studyset/data/studyset_repository.dart';
import 'package:halkyon_learn_core/feature/studyset/data/studyset_tabs.dart';
import 'package:halkyon_learn_core/feature/tag/data/tag_gateway.dart';
import 'package:halkyon_learn_core/feature/tag/data/tag_repository.dart';
import 'package:halkyon_learn_flutter/src/core/asset/data/sound_player.dart';
import 'package:halkyon_learn_flutter/src/core/dependencies/dependencies.dart';
import 'package:halkyon_learn_flutter/src/core/dependencies/person_dependencies.dart';
@ -25,9 +41,6 @@ import 'package:halkyon_learn_flutter/src/feature/assistant/data/conversation_de
import 'package:halkyon_learn_flutter/src/feature/assistant/data/conversation_repository.dart';
import 'package:halkyon_learn_flutter/src/feature/assistant/data/photo_picker.dart';
import 'package:halkyon_learn_flutter/src/feature/assistant/data/web_search.dart';
import 'package:halkyon_learn_flutter/src/feature/card/data/card_deletions.dart';
import 'package:halkyon_learn_flutter/src/feature/card/data/card_gateway.dart';
import 'package:halkyon_learn_flutter/src/feature/card/data/card_repository.dart';
import 'package:halkyon_learn_flutter/src/feature/dictionary/data/device_dictionaries.dart';
import 'package:halkyon_learn_flutter/src/feature/dictionary/data/dictionary_gateway.dart';
import 'package:halkyon_learn_flutter/src/feature/dictionary/data/dictionary_repository.dart';
@ -39,9 +52,6 @@ import 'package:halkyon_learn_flutter/src/feature/explore/data/explore_repositor
import 'package:halkyon_learn_flutter/src/feature/explore/data/release_copier.dart';
import 'package:halkyon_learn_flutter/src/feature/export/data/export_archive.dart';
import 'package:halkyon_learn_flutter/src/feature/export/data/studyset_markdown.dart';
import 'package:halkyon_learn_flutter/src/feature/feedback/data/feedback_envelopes.dart';
import 'package:halkyon_learn_flutter/src/feature/feedback/data/feedback_gateway.dart';
import 'package:halkyon_learn_flutter/src/feature/feedback/data/feedback_repository.dart';
import 'package:halkyon_learn_flutter/src/feature/geo_map/data/geo_map_repository.dart';
import 'package:halkyon_learn_flutter/src/feature/jellyfin/data/jellyfin_gateway.dart';
import 'package:halkyon_learn_flutter/src/feature/jellyfin/data/jellyfin_repository.dart';
@ -49,13 +59,8 @@ import 'package:halkyon_learn_flutter/src/feature/jellyfin/data/jellyfin_store.d
import 'package:halkyon_learn_flutter/src/feature/jellyfin/data/jellyfin_suggestion.dart';
import 'package:halkyon_learn_flutter/src/feature/kanji/data/stroke_repository.dart';
import 'package:halkyon_learn_flutter/src/feature/learning_path/data/learning_path_deletions.dart';
import 'package:halkyon_learn_flutter/src/feature/learning_path/data/learning_path_gateway.dart';
import 'package:halkyon_learn_flutter/src/feature/learning_path/data/learning_path_repository.dart';
import 'package:halkyon_learn_flutter/src/feature/learning_path/data/path_answer_gateway.dart';
import 'package:halkyon_learn_flutter/src/feature/learning_path/data/path_progress_repository.dart';
import 'package:halkyon_learn_flutter/src/feature/note/data/note_deletions.dart';
import 'package:halkyon_learn_flutter/src/feature/note/data/note_gateway.dart';
import 'package:halkyon_learn_flutter/src/feature/note/data/note_repository.dart';
import 'package:halkyon_learn_flutter/src/feature/pdf_source/data/pdf_deletions.dart';
import 'package:halkyon_learn_flutter/src/feature/pdf_source/data/pdf_engine.dart';
import 'package:halkyon_learn_flutter/src/feature/pdf_source/data/pdf_source_gateway.dart';
@ -71,12 +76,7 @@ import 'package:halkyon_learn_flutter/src/feature/sharing/data/studyset_sharing.
import 'package:halkyon_learn_flutter/src/feature/study/data/review_gateway.dart';
import 'package:halkyon_learn_flutter/src/feature/study/data/review_repository.dart';
import 'package:halkyon_learn_flutter/src/feature/studyset/data/studyset_deletions.dart';
import 'package:halkyon_learn_flutter/src/feature/studyset/data/studyset_gateway.dart';
import 'package:halkyon_learn_flutter/src/feature/studyset/data/studyset_progress_repository.dart';
import 'package:halkyon_learn_flutter/src/feature/studyset/data/studyset_repository.dart';
import 'package:halkyon_learn_flutter/src/feature/studyset/data/studyset_tabs.dart';
import 'package:halkyon_learn_flutter/src/feature/tag/data/tag_gateway.dart';
import 'package:halkyon_learn_flutter/src/feature/tag/data/tag_repository.dart';
import 'fake_asset_gateway.dart';
import 'fake_asset_sealing.dart';