diff --git a/AGENTS.md b/AGENTS.md index 579a8acf..50bf18a7 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -11,6 +11,7 @@ Halkyon's apps and the packages they share, in one repository and one pub worksp - `halkyon_ui`: Halkyon's look, the theme around cobalt and copper, Noto Sans bundled, the window-size classes and the widgets every screen is built of (the floating bar and rail, `FoldingTabs`' tab row folding into the title), with texts of its own (English, German). - `halkyon_base_server`, `halkyon_base_client`: the Serverpod module every Halkyon server includes (ADR 0014): signing in (a password with TOTP and the resets of ADR 0017, SimpleLogin on `halkyon_oidc`, email codes proving an address; no anonymous accounts since ADR 0020, D25, nor GitHub since D36), an account's ways in and its deletion with a hook for each app's own data, the pruning, the instance's basics, people's handles and public profiles, who may sign up (`SIGN_UP`: open, approval, closed) with the accounts waiting for approval and the instance's operators who approve them (Halkyon Learn's ADR 0021, step 1), the operator's assistants forwarded on halkyon_llm with their grants, each person's budget, the efforts they allow and counted figures, and the instance's privacy page made from its settings (step 2), and the instance's register (ADR 0016): one account per person across an operator's services, held by one service (Learn's server on learn.halkyon.eu) and asked by the others, its members (`ACCOUNT_REGISTER_URL`), all sharing a secret (`INSTANCE_SECRET`); its README says how. - `halkyon_app`: every app's shell on the module's client (ADR 0014): the instances a device keeps accounts on and the first start, signing in and switching person, the gates building an app's dependencies per instance and per person, the sync service and where it stands, the account section of an app's settings (`AccountTiles`), every app's Profile (`HalkyonProfile`, decided 2026-11-02: the person's own tab, the same in every app, with their account, the ways they sign in and their public profile, one for all apps, then the app's own tabs) and people's public profiles (`PublicProfileRepository`, `PublicProfileSection`, `showProfileOf`), the wait screen of an account waiting for approval and the operator's card, badge and screen (ADR 0021), the assistants of an app's settings (`AssistantsCard`: granted, by provider, agreed to, what is left of the person's budget, used, and the person's own keys) and the operator's grants from that screen (`AssistantGrantsScreen`: several assistants to several people at once, each with one budget, D57 to D63), with the repositories asking them through the module, at an effort the operator allows, or a person's own key directly (step 2) and what an app's warning before a private studyset goes names (`providerTermsOf`, `OwnAssistant.onThisMachine`, step 3), About with the instance's source link (`showAppAbout`), the device's own speech recognition (`SpeechInput`, `DeviceSpeechInput` on `speech_to_text`, `FakeSpeechInput` in `testing.dart`; none on Linux) and dictation into a text at its cursor, listening again after each pause (`Dictation`, Meditate's ADR 0038, D15; an app that listens declares the microphone and its usage texts itself), what's new after an update (`WhatsNew`, `WhatsNewOnUpdate`, `WhatsNewTile`, from the app's `assets/whats_new/`, Train's ADR 0030, D61), the device's appearance and language, chosen in Settings and applied before anyone signs in (`Look`, `FileLookStore`, `LookCubit`, `DeviceLookCard`; Learn keeps them among its own device settings and shows `LookSettingsCard`; ADR 0014's amendment of 4 Nov 2026), the language every app picks (`resolveAppLocale`) and countries' and subdivisions' names (Unicode CLDR's, bundled), with texts that name the app. + - `halkyon_editor` (ADR 0033, D37, 6 Nov 2026): rich text, the general parts of Learn's editor on our fork of `appflowy_editor`: `RichContent` (the editor's document JSON, which Learn and Todo store), `RichContentEditor`, `RichContentView`, the format bar, links, the clipboard keeping formats, superscript and subscript, and `EditorBlocks`, through which an app adds blocks, marks within lines and their buttons (`basicBlocks` without any), with texts of its own (English, German). What is Learn's alone stays in Learn on top of it. - `halkyon_core` (low-level primitives in pure Dart: ids, a logger, `SerialQueue`, `CoalescingRunner`) and `halkyon_interop` (contracts between apps, pure Dart). - `halkyon_keys` (pure Dart on libsodium, ADR 0017): an account's keys on the device, the password's Argon2id and SRP's x, the key pairs and their wrapping, the recovery phrase, data keys, content sealed and signed, row IDs keyed by the account (`keyedRowId`). SRP-6a's two halves are the module's (`halkyon_base_server`'s `base.dart`, `halkyon_base_client`'s `srp.dart`), as are TOTP and the recovery codes. `sodium`'s build hook compiles libsodium, so every machine that builds or tests needs a C compiler and `make`. - `halkyon_llm` (pure Dart, Halkyon Learn's ADR 0021, D15 and D46): language models' APIs streamed over plain HTTP, no SDKs: Anthropic's Messages API, OpenAI's chat completions (each known host's fields, D45) and its Responses API, behind one interface (`LlmClient`), with `FakeLlmClient` for tests; tested on replies written from the providers' documentation and on those `tool/record_llm.dart` records with a key (D36). The module forwards the operator's assistants with it, and the apps call a person's own provider. BSD-3-Clause as `halkyon_oidc`, `publish_to: none`. diff --git a/LICENSE.md b/LICENSE.md index 7a4163c8..11b70bd9 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -11,7 +11,7 @@ The repository's packages carry different licences, each package's full text in | `apps/learn/halkyon_learn_curator`, turning dictionary downloads into dictionary files | GNU Affero General Public License, version 3 or any later version | `AGPL-3.0-or-later` | | `packages/halkyon_base_server`, the Serverpod module every Halkyon server includes | Server Side Public License, version 1 | `SSPL-1.0` | | `packages/halkyon_base_client`, generated from the module's models, with SRP's client half | GNU Affero General Public License, version 3 or any later version | `AGPL-3.0-or-later` | -| `packages/halkyon_app`, `packages/halkyon_ui` | GNU Affero General Public License, version 3 or any later version | `AGPL-3.0-or-later` | +| `packages/halkyon_app`, `packages/halkyon_editor`, `packages/halkyon_ui` | GNU Affero General Public License, version 3 or any later version | `AGPL-3.0-or-later` | | `packages/halkyon_keys`, an account's keys on the device | GNU Affero General Public License, version 3 or any later version | `AGPL-3.0-or-later` | | `packages/halkyon_core`, `packages/halkyon_interop`; BIP-39's English word list in halkyon_core under the MIT License (`BIP39-LICENSE`) | GNU Affero General Public License, version 3 or any later version, as halkyon_shared, where they began, licensed them | `AGPL-3.0-or-later` | | `apps/todo/halkyon_todo_flutter`, Todo's app | GNU Affero General Public License, version 3 or any later version | `AGPL-3.0-or-later` | diff --git a/README.md b/README.md index 35bc645c..c775934e 100644 --- a/README.md +++ b/README.md @@ -23,6 +23,8 @@ Halkyon Meditate is to follow. [`halkyon_base`](packages/halkyon_base_server/README.md) (what every server has, as a Serverpod module with its generated client `halkyon_base_client`: signing in, the account and its deletion, the pruning, the instance's basics), + [`halkyon_editor`](packages/halkyon_editor/README.md) (rich text: the editor, its view + and the document they keep), [`halkyon_core`](packages/halkyon_core/README.md) (low-level primitives) and [`halkyon_interop`](packages/halkyon_interop/README.md) (contracts between apps). - `deploy/`: what runs an instance (Docker Compose: Caddy, the server, PostgreSQL), diff --git a/apps/learn/AGENTS.md b/apps/learn/AGENTS.md index 0647149f..628eeda3 100644 --- a/apps/learn/AGENTS.md +++ b/apps/learn/AGENTS.md @@ -29,8 +29,8 @@ A spaced-repetition learning app: studysets of flashcards, reviewed on an FSRS s - **Material's `Card` widget clashes with the domain's `Card`**: files that need both import `material_ui` with `hide Card`. - **State: `flutter_bloc`, uniformly.** Blocs fold repository streams into immutable states with value equality, are provided as close to their consumer as possible, and log through one global `BlocObserver`. Why: the data layer is stream-shaped, event → state machines test without widgets, and one pattern everywhere is itself the point. - **Value equality comes from `equatable`**, mixed in (`with Equatable`), for models and states alike; `leancode_lint`'s `missing_equatable_props` catches a field left out of `props`. -- **Rich text is `RichContent`** (`lib/src/core/content/`): the editor's document JSON plus a `formatVersion`, pure Dart, so models and the server can use it. `lib/src/core/editor/` converts it to and from the editor's `Document` (giving tables the decimal sizes the editor casts to: JSON from imports and archives may hold whole numbers, or none; our fork of the editor fits a read-only table's rows to their cells, which upstream's leaves at their stored height) and holds the one editing widget (`RichContentEditor`) and the one read-only renderer (`RichContentView`). Copying and pasting keep every format between the app's editors (`EditorClipboard`, decided 2026-10-11): the system's clipboard holds plain text only, so the blocks copied last stay in memory beside it and a paste takes them while it still holds their text, plain text otherwise; the editor's own copy, cut and paste are replaced (Ctrl+C, X, V), and on phones a bar over selected text (the editor's `MobileFloatingToolbar`, over text only) copies, cuts, pastes and selects all. Links come from the format bar's link button and Ctrl+K (`link.dart`, in place of the editor's own link menu, English only); an address without its scheme leads to the web. Text above other text (ruby, `ruby.dart`, decided 2026-10-12) is named in the content's language, furigana over Japanese, pinyin over Chinese, else text above (`ContentLanguages`, in `core/widget/`, which every screen of a studyset sets through its language scope); read-only it shows only under the pointer or after a long press, its room kept so lines don't move, and while editing over the pointer; with the caret inside such text, the format bar's button edits all of it. - - **Blocks** beyond the editor's own (callout, spoiler, formula, image, sound, YouTube video, study button) keep their type and attributes as named in `core/content/content_blocks.dart`, which `RichContent` reads too (their text counts in `plainText`, and a video or study button isn't blank). An `EditorBlocks` lists how each type is built, which can be inserted (the format bar's Insert sheet and the desktop's `/` menu show the same list) and its typing rules; the core's are `coreBlocks`, and a feature's (the study button, `feature/study/`) reach the editors through `EditorBlocksScope`, which `PersonApp` fills, since the core can't know features. One builder serves editor and view: a block asks `editorState.editable`, and read-only shows what it is for (a spoiler hidden until revealed, a formula set, a video's play). A block taken whole mixes in `AtomicBlock` (selection, the remove button); unknown types show "needs a newer version" and stay as they are. New block types keep `formatVersion` 1: raising it would hide whole studysets and paths in older apps. +- **Rich text is `RichContent`** (`halkyon_editor`, ADR 0033, D37: the editor's general parts moved there on 6 Nov 2026, when Todo took editor documents too; Learn's own stay in `lib/src/core/editor/` on top of it: callout, spoiler, formula, file and YouTube blocks, ruby, Markdown, `core_blocks.dart`, `rich_text_screen.dart`, `save_status.dart`, and `learn_content.dart`, the `LearnContentEditor` and `LearnContentView` every screen uses, which are the package's widgets with Learn's blocks and the check of the spelling): the editor's document JSON plus a `formatVersion`, pure Dart, so models can use it. The package converts it to and from the editor's `Document` (giving tables the decimal sizes the editor casts to: JSON from imports and archives may hold whole numbers, or none; our fork of the editor fits a read-only table's rows to their cells, which upstream's leaves at their stored height) and holds the one editing widget (`RichContentEditor`) and the one read-only renderer (`RichContentView`). Copying and pasting keep every format between the app's editors (`EditorClipboard`, decided 2026-10-11): the system's clipboard holds plain text only, so the blocks copied last stay in memory beside it and a paste takes them while it still holds their text, plain text otherwise; the editor's own copy, cut and paste are replaced (Ctrl+C, X, V), and on phones a bar over selected text (the editor's `MobileFloatingToolbar`, over text only) copies, cuts, pastes and selects all. Links come from the format bar's link button and Ctrl+K (the package's `link.dart`, in place of the editor's own link menu, English only); an address without its scheme leads to the web. Text above other text (ruby, `ruby.dart`, decided 2026-10-12) is named in the content's language, furigana over Japanese, pinyin over Chinese, else text above (`ContentLanguages`, in `core/widget/`, which every screen of a studyset sets through its language scope); read-only it shows only under the pointer or after a long press, its room kept so lines don't move, and while editing over the pointer; with the caret inside such text, the format bar's button edits all of it. + - **Blocks** beyond the editor's own (callout, spoiler, formula, image, sound, YouTube video, study button) keep their type and attributes as named in the package's `content_blocks.dart`, which `RichContent` reads too (their text counts in `plainText`, and a video or study button isn't blank). An `EditorBlocks` lists how each type is built, which can be inserted (the format bar's Insert sheet and the desktop's `/` menu show the same list) and its typing rules, and since D37 the marks within lines the package can't know (ruby, formulas), their format bar buttons (`rubyFormatAction`, `inlineMathFormatAction`, after subscript) and what an editor going takes along; the core's are `coreBlocks`, on the package's `basicBlocks`, and a feature's (the study button, `feature/study/`) reach the editors through `EditorBlocksScope`, which `PersonApp` fills, since the core can't know features. One builder serves editor and view: a block asks `editorState.editable`, and read-only shows what it is for (a spoiler hidden until revealed, a formula set, a video's play). A block taken whole mixes in `AtomicBlock` (selection, the remove button); unknown types show "needs a newer version" and stay as they are. New block types keep `formatVersion` 1: raising it would hide whole studysets and paths in older apps. - **Time comes from `clock.now()`** (`package:clock`), never `DateTime.now()`, in server and app. Why: scheduling, streaks and expiry are deterministic under test (`withClock`, `fakeAsync`), and because Serverpod itself reads `clock.now()` (e.g. session expiry), `withClock` controls its behaviour too. - **UTC for storing and sending, local time for display and day boundaries.** Serverpod sends `DateTime` as UTC and stores it in `timestamp without time zone` columns. Compare and compute in UTC; call `.toLocal()` only to display. "Due today", streaks and daily goals use the learner's **local** calendar day, computed on the client; the server never needs day boundaries. - **Build configuration comes from `--dart-define`.** The client holds no secrets, ever. @@ -54,7 +54,7 @@ A spaced-repetition learning app: studysets of flashcards, reviewed on an FSRS s - Shared fakes live in `test/support/`, those of halkyon_app's boundaries in its `package:halkyon_app/testing.dart` (`FakeAuthGateway`, `FakeInstanceChecker`, `MemorySignInAddresses`, `MemoryInstanceStore`, `MemoryPendingChangesStore`), which `testDependencies` fills in; fakes pre-stub their streams so an incidental subscription doesn't fail the test. - **Editor in widget tests**: host `AppFlowyEditor` in a scrollable body with a fixed-height slot (an over-constrained `Expanded` hangs the test), and `pump(const Duration(seconds: 1))` after editor changes to flush its timers. Test the document ↔ JSON round-trip as plain unit tests. - **No `Stream.first` on a repository's stream in code that widget tests run**: `first` waits for the subscription's cancel future, which for a broadcast stream is a completed future of the root zone, so under the widget test's fake clock it never completes. Read once with the repository's `all()` instead. For the same reason a repository never keeps a completed future to chain later work on (a `Future.value()` made in `setUp` runs what waits for it in the real zone): a queue of writes starts empty and empties again when idle (`SerialQueue`, halkyon_core), and a cache keeps values, not futures (`GeoMapRepository`). Assets are read for real, which the fake clock doesn't advance: a widget test showing a map gets the maps read beforehand (`loadedMaps(tester)` into `testDependencies(maps: ...)`), and one showing how a character is written its strokes (`loadedStrokes(tester, characters)` into `testDependencies(strokes: ...)`). Nor is a subscription that has finished cancelled with `await` there (`await subscription.cancel()` on a stream done): its cancel future never completes under the fake clock either, which hung everything after it (found 2026-10-26 with the flashcards an assistant proposes); cancel it unawaited (`CardProposalsCubit`), or forget it in `onDone` and `onError`, so only one still running is awaited (`ConversationCubit`). -- **Text inside the editor's views** (`RichContentView`, `RichContentEditor`) is rich text: find it with `findRichText: true`. +- **Text inside the editor's views** (`LearnContentView`, `LearnContentEditor`) is rich text: find it with `findRichText: true`. - **Layout-sensitive widget tests** set `tester.view.physicalSize` explicitly, so a responsive branch isn't missed for the wrong reason. - **A menu item acts in the frame after its menu closes** (`MenuAnchor`): pump twice after tapping one before expecting what it does. - **Server tests of what writes synced rows** run on the sync engine's database, as the server does (the generated test tools build the pod without it): pass the interceptor to `withServerpod`, call `initializeOfflineSync` in `setUp`, and write as a person through `transactionForUser`. Its writes take transactions of their own, so these tests turn rollback off and delete what they made (`test/integration/asset/asset_test.dart`). diff --git a/apps/learn/README.md b/apps/learn/README.md index b2ca615d..0150568a 100644 --- a/apps/learn/README.md +++ b/apps/learn/README.md @@ -37,8 +37,8 @@ other apps and the packages they share ([the repository's README](../../README.m | `halkyon_learn_curator` | Turns dictionary sources into the files the server loads | AGPL-3.0-or-later | It builds on the shared packages in `packages/`: `halkyon_base` (sign-in, accounts, the -instance's basics; a Serverpod module), `halkyon_app` (instances, sign-in, sync) and -`halkyon_ui` (the look). +instance's basics; a Serverpod module), `halkyon_app` (instances, sign-in, sync), +`halkyon_editor` (rich text) and `halkyon_ui` (the look). ## Setting up diff --git a/apps/learn/halkyon_learn_flutter/lib/l10n/app_de.arb b/apps/learn/halkyon_learn_flutter/lib/l10n/app_de.arb index 99acb9f4..ec35993d 100644 --- a/apps/learn/halkyon_learn_flutter/lib/l10n/app_de.arb +++ b/apps/learn/halkyon_learn_flutter/lib/l10n/app_de.arb @@ -775,24 +775,9 @@ "composerDiscardConfirm": "Verwerfen", "composerConflict": "Diese Karteikarte wurde auf zwei Arten zugleich geändert: auf zwei deiner Geräte oder von dir und durch eine Korrektur im Wörterbuch oder eine neue Veröffentlichung dessen, wovon es kopiert ist. Wähle unten für jeden Teil die Version, die du behalten möchtest.", "composerKeepThis": "Diese behalten", - "formatAlignLeft": "Linksbündig", - "formatAlignCenter": "Zentriert", - "formatAlignRight": "Rechtsbündig", - "formatBold": "Fett", - "formatItalic": "Kursiv", - "formatUnderline": "Unterstrichen", - "formatSuperscript": "Hochgestellt", - "formatSubscript": "Tiefgestellt", "formatRuby": "Text darüber (Aussprache, Übersetzung)", "formatRubyFurigana": "Furigana (eine Lesung über dem Text)", "formatRubyPinyin": "Pinyin (über den Schriftzeichen)", - "formatLink": "Link", - "linkTitle": "Link", - "linkText": "Text", - "linkAddress": "Adresse", - "linkAddressHint": "https://…", - "linkSave": "Speichern", - "linkRemove": "Link entfernen", "rubyTitle": "Text darüber", "rubyTitleFurigana": "Furigana", "rubyTitlePinyin": "Pinyin", @@ -1100,22 +1085,6 @@ "fileNotKept": "Die Datei konnte nicht auf diesem Gerät gespeichert werden.", "fileRemoveImage": "Bild entfernen", "fileRemoveSound": "Audio entfernen", - "editorInsert": "Einfügen", - "blockGroupText": "Text", - "blockGroupBlocks": "Blöcke", - "blockGroupMedia": "Medien", - "blockHeading": "Überschrift {level}", - "blockBulletedList": "Aufzählung", - "blockNumberedList": "Nummerierte Liste", - "blockChecklist": "Checkliste", - "blockQuote": "Zitat", - "blockListItem": "Listenpunkt", - "blockChecklistItem": "Aufgabe", - "editorSlashHint": "Tippe / für einen Block oder schreib einfach los", - "blockDivider": "Trennlinie", - "blockTable": "Tabelle", - "blockRemove": "Entfernen", - "blockUnknown": "Dieser Teil braucht eine neuere Version der App.", "blockCallout": "Hinweisbox", "blockSpoiler": "Spoiler", "calloutNote": "Notiz", diff --git a/apps/learn/halkyon_learn_flutter/lib/l10n/app_en.arb b/apps/learn/halkyon_learn_flutter/lib/l10n/app_en.arb index ab9a39ab..e971714f 100644 --- a/apps/learn/halkyon_learn_flutter/lib/l10n/app_en.arb +++ b/apps/learn/halkyon_learn_flutter/lib/l10n/app_en.arb @@ -885,24 +885,9 @@ "composerDiscardConfirm": "Discard", "composerConflict": "This flashcard was changed in two ways at once: on two of your devices, or by you and by a correction from the dictionary or a new release of what it was copied from. Choose the version to keep of each part below.", "composerKeepThis": "Keep this", - "formatAlignLeft": "Align left", - "formatAlignCenter": "Centre", - "formatAlignRight": "Align right", - "formatBold": "Bold", - "formatItalic": "Italic", - "formatUnderline": "Underline", - "formatSuperscript": "Superscript", - "formatSubscript": "Subscript", "formatRuby": "Text above (a pronunciation, a translation)", "formatRubyFurigana": "Furigana (a reading above the text)", "formatRubyPinyin": "Pinyin (above the characters)", - "formatLink": "Link", - "linkTitle": "Link", - "linkText": "Text", - "linkAddress": "Address", - "linkAddressHint": "https://…", - "linkSave": "Save", - "linkRemove": "Remove link", "rubyTitle": "Text above", "rubyTitleFurigana": "Furigana", "rubyTitlePinyin": "Pinyin", @@ -1253,27 +1238,6 @@ "fileNotKept": "The file couldn't be kept on this device.", "fileRemoveImage": "Remove image", "fileRemoveSound": "Remove sound", - "editorInsert": "Insert", - "blockGroupText": "Text", - "blockGroupBlocks": "Blocks", - "blockGroupMedia": "Media", - "blockHeading": "Heading {level}", - "@blockHeading": { - "placeholders": { - "level": {"type": "int"} - } - }, - "blockBulletedList": "Bulleted list", - "blockNumberedList": "Numbered list", - "blockChecklist": "Checklist", - "blockQuote": "Quote", - "blockListItem": "List item", - "blockChecklistItem": "To-do", - "editorSlashHint": "Type / to insert a block, or just start typing", - "blockDivider": "Divider", - "blockTable": "Table", - "blockRemove": "Remove", - "blockUnknown": "This part needs a newer version of the app.", "blockCallout": "Callout", "blockSpoiler": "Spoiler", "calloutNote": "Note", diff --git a/apps/learn/halkyon_learn_flutter/lib/l10n/app_en_US.arb b/apps/learn/halkyon_learn_flutter/lib/l10n/app_en_US.arb index 51d4f673..4505a173 100644 --- a/apps/learn/halkyon_learn_flutter/lib/l10n/app_en_US.arb +++ b/apps/learn/halkyon_learn_flutter/lib/l10n/app_en_US.arb @@ -3,7 +3,6 @@ "studysetSheetColor": "Color", "tagSheetColor": "Color", - "formatAlignCenter": "Center", "homeCustomise": "Customize", "exploreLicense": "License", "exploreLicenseChosen": "License: {count}", diff --git a/apps/learn/halkyon_learn_flutter/lib/l10n/app_localizations.dart b/apps/learn/halkyon_learn_flutter/lib/l10n/app_localizations.dart index 00faa4cc..ab2f1a3e 100644 --- a/apps/learn/halkyon_learn_flutter/lib/l10n/app_localizations.dart +++ b/apps/learn/halkyon_learn_flutter/lib/l10n/app_localizations.dart @@ -4564,54 +4564,6 @@ abstract class AppLocalizations { /// **'Keep this'** String get composerKeepThis; - /// No description provided for @formatAlignLeft. - /// - /// In en, this message translates to: - /// **'Align left'** - String get formatAlignLeft; - - /// No description provided for @formatAlignCenter. - /// - /// In en, this message translates to: - /// **'Centre'** - String get formatAlignCenter; - - /// No description provided for @formatAlignRight. - /// - /// In en, this message translates to: - /// **'Align right'** - String get formatAlignRight; - - /// No description provided for @formatBold. - /// - /// In en, this message translates to: - /// **'Bold'** - String get formatBold; - - /// No description provided for @formatItalic. - /// - /// In en, this message translates to: - /// **'Italic'** - String get formatItalic; - - /// No description provided for @formatUnderline. - /// - /// In en, this message translates to: - /// **'Underline'** - String get formatUnderline; - - /// No description provided for @formatSuperscript. - /// - /// In en, this message translates to: - /// **'Superscript'** - String get formatSuperscript; - - /// No description provided for @formatSubscript. - /// - /// In en, this message translates to: - /// **'Subscript'** - String get formatSubscript; - /// No description provided for @formatRuby. /// /// In en, this message translates to: @@ -4630,48 +4582,6 @@ abstract class AppLocalizations { /// **'Pinyin (above the characters)'** String get formatRubyPinyin; - /// No description provided for @formatLink. - /// - /// In en, this message translates to: - /// **'Link'** - String get formatLink; - - /// No description provided for @linkTitle. - /// - /// In en, this message translates to: - /// **'Link'** - String get linkTitle; - - /// No description provided for @linkText. - /// - /// In en, this message translates to: - /// **'Text'** - String get linkText; - - /// No description provided for @linkAddress. - /// - /// In en, this message translates to: - /// **'Address'** - String get linkAddress; - - /// No description provided for @linkAddressHint. - /// - /// In en, this message translates to: - /// **'https://…'** - String get linkAddressHint; - - /// No description provided for @linkSave. - /// - /// In en, this message translates to: - /// **'Save'** - String get linkSave; - - /// No description provided for @linkRemove. - /// - /// In en, this message translates to: - /// **'Remove link'** - String get linkRemove; - /// No description provided for @rubyTitle. /// /// In en, this message translates to: @@ -6478,102 +6388,6 @@ abstract class AppLocalizations { /// **'Remove sound'** String get fileRemoveSound; - /// No description provided for @editorInsert. - /// - /// In en, this message translates to: - /// **'Insert'** - String get editorInsert; - - /// No description provided for @blockGroupText. - /// - /// In en, this message translates to: - /// **'Text'** - String get blockGroupText; - - /// No description provided for @blockGroupBlocks. - /// - /// In en, this message translates to: - /// **'Blocks'** - String get blockGroupBlocks; - - /// No description provided for @blockGroupMedia. - /// - /// In en, this message translates to: - /// **'Media'** - String get blockGroupMedia; - - /// No description provided for @blockHeading. - /// - /// In en, this message translates to: - /// **'Heading {level}'** - String blockHeading(int level); - - /// No description provided for @blockBulletedList. - /// - /// In en, this message translates to: - /// **'Bulleted list'** - String get blockBulletedList; - - /// No description provided for @blockNumberedList. - /// - /// In en, this message translates to: - /// **'Numbered list'** - String get blockNumberedList; - - /// No description provided for @blockChecklist. - /// - /// In en, this message translates to: - /// **'Checklist'** - String get blockChecklist; - - /// No description provided for @blockQuote. - /// - /// In en, this message translates to: - /// **'Quote'** - String get blockQuote; - - /// No description provided for @blockListItem. - /// - /// In en, this message translates to: - /// **'List item'** - String get blockListItem; - - /// No description provided for @blockChecklistItem. - /// - /// In en, this message translates to: - /// **'To-do'** - String get blockChecklistItem; - - /// No description provided for @editorSlashHint. - /// - /// In en, this message translates to: - /// **'Type / to insert a block, or just start typing'** - String get editorSlashHint; - - /// No description provided for @blockDivider. - /// - /// In en, this message translates to: - /// **'Divider'** - String get blockDivider; - - /// No description provided for @blockTable. - /// - /// In en, this message translates to: - /// **'Table'** - String get blockTable; - - /// No description provided for @blockRemove. - /// - /// In en, this message translates to: - /// **'Remove'** - String get blockRemove; - - /// No description provided for @blockUnknown. - /// - /// In en, this message translates to: - /// **'This part needs a newer version of the app.'** - String get blockUnknown; - /// No description provided for @blockCallout. /// /// In en, this message translates to: diff --git a/apps/learn/halkyon_learn_flutter/lib/l10n/app_localizations_de.dart b/apps/learn/halkyon_learn_flutter/lib/l10n/app_localizations_de.dart index 32463df9..2dfb4515 100644 --- a/apps/learn/halkyon_learn_flutter/lib/l10n/app_localizations_de.dart +++ b/apps/learn/halkyon_learn_flutter/lib/l10n/app_localizations_de.dart @@ -2837,30 +2837,6 @@ class AppLocalizationsDe extends AppLocalizations { @override String get composerKeepThis => 'Diese behalten'; - @override - String get formatAlignLeft => 'Linksbündig'; - - @override - String get formatAlignCenter => 'Zentriert'; - - @override - String get formatAlignRight => 'Rechtsbündig'; - - @override - String get formatBold => 'Fett'; - - @override - String get formatItalic => 'Kursiv'; - - @override - String get formatUnderline => 'Unterstrichen'; - - @override - String get formatSuperscript => 'Hochgestellt'; - - @override - String get formatSubscript => 'Tiefgestellt'; - @override String get formatRuby => 'Text darüber (Aussprache, Übersetzung)'; @@ -2870,27 +2846,6 @@ class AppLocalizationsDe extends AppLocalizations { @override String get formatRubyPinyin => 'Pinyin (über den Schriftzeichen)'; - @override - String get formatLink => 'Link'; - - @override - String get linkTitle => 'Link'; - - @override - String get linkText => 'Text'; - - @override - String get linkAddress => 'Adresse'; - - @override - String get linkAddressHint => 'https://…'; - - @override - String get linkSave => 'Speichern'; - - @override - String get linkRemove => 'Link entfernen'; - @override String get rubyTitle => 'Text darüber'; @@ -4045,57 +4000,6 @@ class AppLocalizationsDe extends AppLocalizations { @override String get fileRemoveSound => 'Audio entfernen'; - @override - String get editorInsert => 'Einfügen'; - - @override - String get blockGroupText => 'Text'; - - @override - String get blockGroupBlocks => 'Blöcke'; - - @override - String get blockGroupMedia => 'Medien'; - - @override - String blockHeading(int level) { - return 'Überschrift $level'; - } - - @override - String get blockBulletedList => 'Aufzählung'; - - @override - String get blockNumberedList => 'Nummerierte Liste'; - - @override - String get blockChecklist => 'Checkliste'; - - @override - String get blockQuote => 'Zitat'; - - @override - String get blockListItem => 'Listenpunkt'; - - @override - String get blockChecklistItem => 'Aufgabe'; - - @override - String get editorSlashHint => - 'Tippe / für einen Block oder schreib einfach los'; - - @override - String get blockDivider => 'Trennlinie'; - - @override - String get blockTable => 'Tabelle'; - - @override - String get blockRemove => 'Entfernen'; - - @override - String get blockUnknown => 'Dieser Teil braucht eine neuere Version der App.'; - @override String get blockCallout => 'Hinweisbox'; diff --git a/apps/learn/halkyon_learn_flutter/lib/l10n/app_localizations_en.dart b/apps/learn/halkyon_learn_flutter/lib/l10n/app_localizations_en.dart index 5b405b7a..5f5b0866 100644 --- a/apps/learn/halkyon_learn_flutter/lib/l10n/app_localizations_en.dart +++ b/apps/learn/halkyon_learn_flutter/lib/l10n/app_localizations_en.dart @@ -2813,30 +2813,6 @@ class AppLocalizationsEn extends AppLocalizations { @override String get composerKeepThis => 'Keep this'; - @override - String get formatAlignLeft => 'Align left'; - - @override - String get formatAlignCenter => 'Centre'; - - @override - String get formatAlignRight => 'Align right'; - - @override - String get formatBold => 'Bold'; - - @override - String get formatItalic => 'Italic'; - - @override - String get formatUnderline => 'Underline'; - - @override - String get formatSuperscript => 'Superscript'; - - @override - String get formatSubscript => 'Subscript'; - @override String get formatRuby => 'Text above (a pronunciation, a translation)'; @@ -2846,27 +2822,6 @@ class AppLocalizationsEn extends AppLocalizations { @override String get formatRubyPinyin => 'Pinyin (above the characters)'; - @override - String get formatLink => 'Link'; - - @override - String get linkTitle => 'Link'; - - @override - String get linkText => 'Text'; - - @override - String get linkAddress => 'Address'; - - @override - String get linkAddressHint => 'https://…'; - - @override - String get linkSave => 'Save'; - - @override - String get linkRemove => 'Remove link'; - @override String get rubyTitle => 'Text above'; @@ -4009,57 +3964,6 @@ class AppLocalizationsEn extends AppLocalizations { @override String get fileRemoveSound => 'Remove sound'; - @override - String get editorInsert => 'Insert'; - - @override - String get blockGroupText => 'Text'; - - @override - String get blockGroupBlocks => 'Blocks'; - - @override - String get blockGroupMedia => 'Media'; - - @override - String blockHeading(int level) { - return 'Heading $level'; - } - - @override - String get blockBulletedList => 'Bulleted list'; - - @override - String get blockNumberedList => 'Numbered list'; - - @override - String get blockChecklist => 'Checklist'; - - @override - String get blockQuote => 'Quote'; - - @override - String get blockListItem => 'List item'; - - @override - String get blockChecklistItem => 'To-do'; - - @override - String get editorSlashHint => - 'Type / to insert a block, or just start typing'; - - @override - String get blockDivider => 'Divider'; - - @override - String get blockTable => 'Table'; - - @override - String get blockRemove => 'Remove'; - - @override - String get blockUnknown => 'This part needs a newer version of the app.'; - @override String get blockCallout => 'Callout'; @@ -8046,9 +7950,6 @@ class AppLocalizationsEnUs extends AppLocalizationsEn { @override String get tagSheetColor => 'Color'; - @override - String get formatAlignCenter => 'Center'; - @override String get homeCustomise => 'Customize'; diff --git a/apps/learn/halkyon_learn_flutter/lib/src/app/person_app.dart b/apps/learn/halkyon_learn_flutter/lib/src/app/person_app.dart index 2ca3a711..16fb36f3 100644 --- a/apps/learn/halkyon_learn_flutter/lib/src/app/person_app.dart +++ b/apps/learn/halkyon_learn_flutter/lib/src/app/person_app.dart @@ -3,6 +3,7 @@ import 'dart:async'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:go_router/go_router.dart'; import 'package:halkyon_app/halkyon_app.dart'; +import 'package:halkyon_editor/halkyon_editor.dart'; import 'package:halkyon_learn_flutter/l10n/app_localizations.dart'; import 'package:halkyon_learn_flutter/src/app/app_router.dart'; import 'package:halkyon_learn_flutter/src/core/dependencies/dependencies.dart'; @@ -10,7 +11,6 @@ import 'package:halkyon_learn_flutter/src/core/dependencies/person_dependencies. import 'package:halkyon_learn_flutter/src/core/device_settings/bloc/device_settings_bloc.dart'; import 'package:halkyon_learn_flutter/src/core/device_settings/widget/device_settings_scope.dart'; import 'package:halkyon_learn_flutter/src/core/editor/core_blocks.dart'; -import 'package:halkyon_learn_flutter/src/core/editor/editor_blocks.dart'; import 'package:halkyon_learn_flutter/src/core/l10n/app_languages.dart'; import 'package:halkyon_learn_flutter/src/core/router/route_paths.dart'; import 'package:halkyon_learn_flutter/src/core/theme/learn_theme.dart'; diff --git a/apps/learn/halkyon_learn_flutter/lib/src/core/editor/callout_block.dart b/apps/learn/halkyon_learn_flutter/lib/src/core/editor/callout_block.dart index 37475519..35d3d191 100644 --- a/apps/learn/halkyon_learn_flutter/lib/src/core/editor/callout_block.dart +++ b/apps/learn/halkyon_learn_flutter/lib/src/core/editor/callout_block.dart @@ -2,9 +2,8 @@ import 'dart:async'; import 'package:appflowy_editor/appflowy_editor.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:halkyon_editor/halkyon_editor.dart'; import 'package:halkyon_learn_flutter/l10n/app_localizations.dart'; -import 'package:halkyon_learn_flutter/src/core/content/content_blocks.dart'; -import 'package:halkyon_learn_flutter/src/core/editor/editor_blocks.dart'; import 'package:halkyon_ui/halkyon_ui.dart'; import 'package:lucide_icons_flutter/lucide_icons.dart'; import 'package:material_ui/material_ui.dart'; @@ -17,7 +16,7 @@ import 'package:material_ui/material_ui.dart'; /// one opens and closes with the header's button, for that view alone. /// A callout can't go inside another. final calloutBlock = InsertableBlock( - name: (l10n) => l10n.blockCallout, + name: (context) => AppLocalizations.of(context).blockCallout, icon: LucideIcons.messageSquareWarning, group: InsertGroup.blocks, keywords: const ['callout', 'note', 'info', 'tip', 'warning', 'admonition'], diff --git a/apps/learn/halkyon_learn_flutter/lib/src/core/editor/core_blocks.dart b/apps/learn/halkyon_learn_flutter/lib/src/core/editor/core_blocks.dart index d82ba569..f3298329 100644 --- a/apps/learn/halkyon_learn_flutter/lib/src/core/editor/core_blocks.dart +++ b/apps/learn/halkyon_learn_flutter/lib/src/core/editor/core_blocks.dart @@ -1,18 +1,16 @@ -import 'package:appflowy_editor/appflowy_editor.dart'; -import 'package:halkyon_learn_flutter/l10n/app_localizations.dart'; +import 'package:halkyon_editor/halkyon_editor.dart'; import 'package:halkyon_learn_flutter/src/core/editor/callout_block.dart'; -import 'package:halkyon_learn_flutter/src/core/editor/editor_blocks.dart'; import 'package:halkyon_learn_flutter/src/core/editor/file_blocks.dart'; import 'package:halkyon_learn_flutter/src/core/editor/math_block.dart'; +import 'package:halkyon_learn_flutter/src/core/editor/ruby.dart'; import 'package:halkyon_learn_flutter/src/core/editor/spoiler_block.dart'; import 'package:halkyon_learn_flutter/src/core/editor/youtube_block.dart'; -import 'package:lucide_icons_flutter/lucide_icons.dart'; import 'package:material_ui/material_ui.dart'; -/// The blocks every editor knows: the editor's own, and the core's. +/// The blocks every editor of Learn knows: halkyon_editor's, and the core's. final coreBlocks = EditorBlocks( builders: { - ...standardBlockComponentBuilderMap, + ...basicBlocks.builders, ...fileBlockBuilders, ...calloutBlockBuilders, ...spoilerBlockBuilders, @@ -20,68 +18,7 @@ final coreBlocks = EditorBlocks( ...youtubeBlockBuilders, }, insertable: [ - for (final level in [1, 2, 3]) - _textBlock( - name: (l10n) => l10n.blockHeading(level), - icon: const [ - LucideIcons.heading1, - LucideIcons.heading2, - LucideIcons.heading3, - ][level - 1], - keywords: ['heading', 'h$level', 'title'], - node: () => headingNode(level: level), - ), - _textBlock( - name: (l10n) => l10n.blockBulletedList, - icon: LucideIcons.list, - keywords: const ['bulleted list', 'bullet', 'unordered'], - node: bulletedListNode, - ), - _textBlock( - name: (l10n) => l10n.blockNumberedList, - icon: LucideIcons.listOrdered, - keywords: const ['numbered list', 'ordered', 'number'], - node: numberedListNode, - ), - _textBlock( - name: (l10n) => l10n.blockChecklist, - icon: LucideIcons.listTodo, - keywords: const ['checklist', 'todo', 'checkbox', 'task'], - node: () => todoListNode(checked: false), - ), - _textBlock( - name: (l10n) => l10n.blockQuote, - icon: LucideIcons.textQuote, - keywords: const ['quote', 'citation'], - node: quoteNode, - ), - InsertableBlock( - name: (l10n) => l10n.blockDivider, - icon: LucideIcons.separatorHorizontal, - group: InsertGroup.blocks, - keywords: const ['divider', 'horizontal rule', 'line', 'separator'], - insert: (context, editorState, at) => - insertBlock(editorState, dividerNode(), at: at), - ), - InsertableBlock( - name: (l10n) => l10n.blockTable, - icon: LucideIcons.table, - group: InsertGroup.blocks, - keywords: const ['table', 'grid', 'rows', 'columns'], - insideCallouts: false, - // Two rows of two cells, the caret in the first. - insert: (context, editorState, at) => insertBlock( - editorState, - TableNode.fromList([ - ['', ''], - ['', ''], - ]).node, - at: at, - caretIn: const [0, 0], - lineAfter: true, - insideCallouts: false, - ), - ), + ...basicBlocks.insertable, calloutBlock, spoilerBlock, mathBlock, @@ -90,24 +27,12 @@ final coreBlocks = EditorBlocks( youtubeBlock, ], shortcuts: [leaveCalloutOnEnter], + textMarks: [withRuby, withInlineMath], + formatActions: [rubyFormatAction, inlineMathFormatAction], + onEditorClosed: [hideHoverReading], ); /// The blocks of the editors below [context]: the core's, and the /// features' too where the app gives them ([EditorBlocksScope]). EditorBlocks blocksOf(BuildContext context) => EditorBlocksScope.maybeOf(context) ?? coreBlocks; - -/// A line of text of a kind, which the caret goes into. -InsertableBlock _textBlock({ - required String Function(AppLocalizations l10n) name, - required IconData icon, - required List keywords, - required Node Function() node, -}) => InsertableBlock( - name: name, - icon: icon, - group: InsertGroup.text, - keywords: keywords, - insert: (context, editorState, at) => - insertBlock(editorState, node(), at: at, caretIn: const []), -); diff --git a/apps/learn/halkyon_learn_flutter/lib/src/core/editor/file_blocks.dart b/apps/learn/halkyon_learn_flutter/lib/src/core/editor/file_blocks.dart index 7d9889a0..6ac221aa 100644 --- a/apps/learn/halkyon_learn_flutter/lib/src/core/editor/file_blocks.dart +++ b/apps/learn/halkyon_learn_flutter/lib/src/core/editor/file_blocks.dart @@ -3,18 +3,15 @@ import 'dart:developer'; import 'package:appflowy_editor/appflowy_editor.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:go_router/go_router.dart'; +import 'package:halkyon_editor/halkyon_editor.dart'; import 'package:halkyon_learn_flutter/l10n/app_localizations.dart'; import 'package:halkyon_learn_flutter/src/core/asset/bloc/sound_bloc.dart'; import 'package:halkyon_learn_flutter/src/core/asset/data/asset_repository.dart'; import 'package:halkyon_learn_flutter/src/core/asset/data/media_type.dart'; import 'package:halkyon_learn_flutter/src/core/asset/widget/stored_file_image.dart'; import 'package:halkyon_learn_flutter/src/core/asset/widget/studyset_files.dart'; -import 'package:halkyon_learn_flutter/src/core/content/content_blocks.dart'; -import 'package:halkyon_learn_flutter/src/core/content/rich_content.dart'; import 'package:halkyon_learn_flutter/src/core/dependencies/dependencies.dart'; import 'package:halkyon_learn_flutter/src/core/dependencies/person_dependencies.dart'; -import 'package:halkyon_learn_flutter/src/core/editor/atomic_block.dart'; -import 'package:halkyon_learn_flutter/src/core/editor/editor_blocks.dart'; import 'package:halkyon_learn_flutter/src/core/router/route_paths.dart'; import 'package:halkyon_ui/halkyon_ui.dart'; import 'package:lucide_icons_flutter/lucide_icons.dart'; @@ -60,7 +57,7 @@ final Map fileBlockBuilders = { /// An image from the person's device, kept as a file of theirs. final imageBlock = InsertableBlock( - name: (l10n) => l10n.fileImage, + name: (context) => AppLocalizations.of(context).fileImage, icon: LucideIcons.image, group: InsertGroup.media, keywords: const ['image', 'picture', 'photo'], @@ -70,7 +67,7 @@ final imageBlock = InsertableBlock( /// A sound from the person's device, kept as a file of theirs. final soundBlock = InsertableBlock( - name: (l10n) => l10n.fileSound, + name: (context) => AppLocalizations.of(context).fileSound, icon: LucideIcons.volume2, group: InsertGroup.media, keywords: const ['sound', 'audio', 'pronunciation'], diff --git a/apps/learn/halkyon_learn_flutter/lib/src/core/editor/learn_content.dart b/apps/learn/halkyon_learn_flutter/lib/src/core/editor/learn_content.dart new file mode 100644 index 00000000..22367924 --- /dev/null +++ b/apps/learn/halkyon_learn_flutter/lib/src/core/editor/learn_content.dart @@ -0,0 +1,84 @@ +import 'package:appflowy_editor/appflowy_editor.dart'; +import 'package:halkyon_editor/halkyon_editor.dart'; +import 'package:halkyon_learn_flutter/src/core/dependencies/person_dependencies.dart'; +import 'package:halkyon_learn_flutter/src/core/editor/core_blocks.dart'; +import 'package:halkyon_learn_flutter/src/core/grammar/grammar_host.dart'; +import 'package:material_ui/material_ui.dart'; + +/// halkyon_editor's [RichContentEditor] with Learn's blocks ([blocksOf]) and, +/// with [grammar], spelling and grammar checked as the text is written, in +/// that language ([GrammarHost], ADR 0024). +class const LearnContentEditor({ + /// The content the editor opens with; later changes to it are ignored. + required final RichContent initialContent, + + /// Called with the whole content after every change to the document. + required final ValueChanged onChanged, + + /// Whether the editor takes the focus as it opens, its cursor at the end. + final bool autofocus = false, + + /// The editor's focus, to follow or move it; one of its own otherwise. + final FocusNode? focusNode, + + /// Whether a [FormatBar] shows below the editor while it has the focus. + final bool formatBar = false, + + /// The room around the text, within the editor's scrolling. + final EdgeInsets padding = EdgeInsets.zero, + + /// The language the text is checked in as it is written; null: it isn't. + final GrammarLanguage? grammar, + super.key, +}) extends StatelessWidget { + @override + Widget build(BuildContext context) => RichContentEditor( + initialContent: initialContent, + onChanged: onChanged, + autofocus: autofocus, + focusNode: focusNode, + formatBar: formatBar, + padding: padding, + blocks: blocksOf(context), + wrapEditor: grammar == null ? null : _checked, + ); + + Widget _checked( + BuildContext context, + EditorState editorState, + FocusNode focusNode, + Widget editor, + ) => switch (grammar) { + // Checked for the person signed in; nobody is outside their app. + final language? when PersonDependencies.maybeOf(context) != null => + GrammarHost( + editorState: editorState, + focusNode: focusNode, + language: language, + child: editor, + ), + _ => editor, + }; +} + +/// halkyon_editor's [RichContentView] with Learn's blocks ([blocksOf]). +class const LearnContentView({ + /// The content to show. + required final RichContent content, + + /// The text's style; the theme's `bodyLarge` without. + final TextStyle? textStyle, + + /// How paragraphs and headings align where the editor gave them no + /// alignment of their own; at the start without. + final TextAlign? textAlign, + super.key, +}) extends StatelessWidget { + @override + Widget build(BuildContext context) => RichContentView( + content: content, + textStyle: textStyle, + textAlign: textAlign, + blocks: blocksOf(context), + ); +} diff --git a/apps/learn/halkyon_learn_flutter/lib/src/core/editor/math_block.dart b/apps/learn/halkyon_learn_flutter/lib/src/core/editor/math_block.dart index 0f5bb766..9192a51c 100644 --- a/apps/learn/halkyon_learn_flutter/lib/src/core/editor/math_block.dart +++ b/apps/learn/halkyon_learn_flutter/lib/src/core/editor/math_block.dart @@ -1,8 +1,6 @@ import 'package:appflowy_editor/appflowy_editor.dart'; +import 'package:halkyon_editor/halkyon_editor.dart'; import 'package:halkyon_learn_flutter/l10n/app_localizations.dart'; -import 'package:halkyon_learn_flutter/src/core/content/content_blocks.dart'; -import 'package:halkyon_learn_flutter/src/core/editor/atomic_block.dart'; -import 'package:halkyon_learn_flutter/src/core/editor/editor_blocks.dart'; import 'package:halkyon_learn_flutter/src/core/editor/formula_screen.dart'; import 'package:halkyon_learn_flutter/src/core/editor/math_view.dart'; import 'package:lucide_icons_flutter/lucide_icons.dart'; @@ -12,7 +10,7 @@ import 'package:material_ui/material_ui.dart'; /// ([editFormula]) and inserted once saved; a tap on it in the editor opens /// it again. final mathBlock = InsertableBlock( - name: (l10n) => l10n.blockFormula, + name: (context) => AppLocalizations.of(context).blockFormula, icon: LucideIcons.sigma, group: InsertGroup.blocks, keywords: const ['formula', 'math', 'equation', 'latex', 'tex'], @@ -38,6 +36,18 @@ final Map mathBlockBuilders = { mathBlockType: _MathBlockBuilder(), }; +/// The format bar's button for formulas within lines (Σ). +final inlineMathFormatAction = FormatAction( + icon: LucideIcons.sigma, + tooltip: (context) => AppLocalizations.of(context).formatInlineMath, + active: (editorState) => + editorState.selection != null && + (editorState.getDeltaAttributeValueInSelection(inlineMathKey) ?? + false), + run: toggleInlineMath, + refocus: true, +); + /// Makes the selection of [editorState] a formula within its line, or /// plain text again if it is one; at a caret, writes a new formula on its /// own screen and puts it there. A formula within a line is text marked diff --git a/apps/learn/halkyon_learn_flutter/lib/src/core/editor/rich_content_markdown.dart b/apps/learn/halkyon_learn_flutter/lib/src/core/editor/rich_content_markdown.dart index 69701567..29278879 100644 --- a/apps/learn/halkyon_learn_flutter/lib/src/core/editor/rich_content_markdown.dart +++ b/apps/learn/halkyon_learn_flutter/lib/src/core/editor/rich_content_markdown.dart @@ -1,7 +1,5 @@ import 'package:appflowy_editor/appflowy_editor.dart'; -import 'package:halkyon_learn_flutter/src/core/content/content_blocks.dart'; -import 'package:halkyon_learn_flutter/src/core/content/rich_content.dart'; -import 'package:halkyon_learn_flutter/src/core/editor/rich_content_document.dart'; +import 'package:halkyon_editor/halkyon_editor.dart'; /// What Markdown has no words of its own for, in the person's language: a /// sound's link, a video's link without a caption, a spoiler's summary, a diff --git a/apps/learn/halkyon_learn_flutter/lib/src/core/editor/rich_text_screen.dart b/apps/learn/halkyon_learn_flutter/lib/src/core/editor/rich_text_screen.dart index bc983a39..e6114d92 100644 --- a/apps/learn/halkyon_learn_flutter/lib/src/core/editor/rich_text_screen.dart +++ b/apps/learn/halkyon_learn_flutter/lib/src/core/editor/rich_text_screen.dart @@ -1,9 +1,9 @@ import 'dart:math' as math; +import 'package:halkyon_editor/halkyon_editor.dart'; import 'package:halkyon_learn_flutter/l10n/app_localizations.dart'; import 'package:halkyon_learn_flutter/src/core/asset/widget/studyset_files.dart'; -import 'package:halkyon_learn_flutter/src/core/content/rich_content.dart'; -import 'package:halkyon_learn_flutter/src/core/editor/rich_content_editor.dart'; +import 'package:halkyon_learn_flutter/src/core/editor/learn_content.dart'; import 'package:halkyon_learn_flutter/src/core/grammar/grammar_host.dart'; import 'package:halkyon_ui/halkyon_ui.dart'; import 'package:material_ui/material_ui.dart'; @@ -84,7 +84,7 @@ class _RichTextEditorState() extends State<_RichTextEditor> { children: [ SizedBox( height: math.max(320, constraints.maxHeight - 32), - child: RichContentEditor( + child: LearnContentEditor( initialContent: widget.initial, onChanged: (text) => _text = text, formatBar: true, diff --git a/apps/learn/halkyon_learn_flutter/lib/src/core/editor/ruby.dart b/apps/learn/halkyon_learn_flutter/lib/src/core/editor/ruby.dart index 651f979d..810e2fa1 100644 --- a/apps/learn/halkyon_learn_flutter/lib/src/core/editor/ruby.dart +++ b/apps/learn/halkyon_learn_flutter/lib/src/core/editor/ruby.dart @@ -1,9 +1,10 @@ import 'package:appflowy_editor/appflowy_editor.dart'; import 'package:flutter/gestures.dart'; +import 'package:halkyon_editor/halkyon_editor.dart'; import 'package:halkyon_learn_flutter/l10n/app_localizations.dart'; -import 'package:halkyon_learn_flutter/src/core/content/content_blocks.dart'; import 'package:halkyon_learn_flutter/src/core/widget/content_languages.dart'; import 'package:halkyon_ui/halkyon_ui.dart'; +import 'package:lucide_icons_flutter/lucide_icons.dart'; import 'package:material_ui/material_ui.dart'; // Text above (ruby): text set small above text within a line, kept on the @@ -49,6 +50,16 @@ String rubyButtonName(BuildContext context) { }; } +/// The format bar's button for text above. +final rubyFormatAction = FormatAction( + icon: LucideIcons.languages, + tooltip: rubyButtonName, + active: (editorState) => + editorState.selection != null && + editorState.getDeltaAttributeValueInSelection(rubyKey) != null, + run: editRuby, +); + /// Gives the text selected in [editorState] text above, asked for starting /// from what it has; empty takes it away. With the caret in text that has /// text above (or right after it), that whole text is selected and edited, diff --git a/apps/learn/halkyon_learn_flutter/lib/src/core/editor/spoiler_block.dart b/apps/learn/halkyon_learn_flutter/lib/src/core/editor/spoiler_block.dart index ead962ad..4e17e266 100644 --- a/apps/learn/halkyon_learn_flutter/lib/src/core/editor/spoiler_block.dart +++ b/apps/learn/halkyon_learn_flutter/lib/src/core/editor/spoiler_block.dart @@ -1,8 +1,7 @@ import 'package:appflowy_editor/appflowy_editor.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:halkyon_editor/halkyon_editor.dart'; import 'package:halkyon_learn_flutter/l10n/app_localizations.dart'; -import 'package:halkyon_learn_flutter/src/core/content/content_blocks.dart'; -import 'package:halkyon_learn_flutter/src/core/editor/editor_blocks.dart'; import 'package:lucide_icons_flutter/lucide_icons.dart'; import 'package:material_ui/material_ui.dart'; @@ -11,7 +10,7 @@ import 'package:material_ui/material_ui.dart'; /// read-only, a Reveal button stands in for it until tapped (or hovered with /// a mouse), for that view alone. final spoilerBlock = InsertableBlock( - name: (l10n) => l10n.blockSpoiler, + name: (context) => AppLocalizations.of(context).blockSpoiler, icon: LucideIcons.eyeOff, group: InsertGroup.blocks, keywords: const ['spoiler', 'hidden', 'reveal', 'answer'], diff --git a/apps/learn/halkyon_learn_flutter/lib/src/core/editor/youtube_block.dart b/apps/learn/halkyon_learn_flutter/lib/src/core/editor/youtube_block.dart index fc51a630..0c127803 100644 --- a/apps/learn/halkyon_learn_flutter/lib/src/core/editor/youtube_block.dart +++ b/apps/learn/halkyon_learn_flutter/lib/src/core/editor/youtube_block.dart @@ -1,11 +1,9 @@ import 'dart:async'; import 'package:appflowy_editor/appflowy_editor.dart'; +import 'package:halkyon_editor/halkyon_editor.dart'; import 'package:halkyon_learn_flutter/l10n/app_localizations.dart'; -import 'package:halkyon_learn_flutter/src/core/content/content_blocks.dart'; import 'package:halkyon_learn_flutter/src/core/dependencies/dependencies.dart'; -import 'package:halkyon_learn_flutter/src/core/editor/atomic_block.dart'; -import 'package:halkyon_learn_flutter/src/core/editor/editor_blocks.dart'; import 'package:halkyon_learn_flutter/src/core/video/youtube_videos.dart'; import 'package:halkyon_ui/halkyon_ui.dart'; import 'package:lucide_icons_flutter/lucide_icons.dart'; @@ -16,7 +14,7 @@ import 'package:material_ui/material_ui.dart'; /// (`YoutubeVideos`). Read-only, a tap plays it in place where the app can, /// else opens it outside; in the editor, a tap opens its link and caption. final youtubeBlock = InsertableBlock( - name: (l10n) => l10n.blockYoutube, + name: (context) => AppLocalizations.of(context).blockYoutube, icon: LucideIcons.squarePlay, group: InsertGroup.media, keywords: const ['youtube', 'video'], diff --git a/apps/learn/halkyon_learn_flutter/lib/src/core/grammar/grammar_segments.dart b/apps/learn/halkyon_learn_flutter/lib/src/core/grammar/grammar_segments.dart index 51d78105..a163ef3b 100644 --- a/apps/learn/halkyon_learn_flutter/lib/src/core/grammar/grammar_segments.dart +++ b/apps/learn/halkyon_learn_flutter/lib/src/core/grammar/grammar_segments.dart @@ -1,6 +1,6 @@ import 'package:appflowy_editor/appflowy_editor.dart'; import 'package:equatable/equatable.dart'; -import 'package:halkyon_learn_flutter/src/core/content/content_blocks.dart'; +import 'package:halkyon_editor/halkyon_editor.dart'; /// A paragraph to check (ADR 0024): the text of the node at [path], so what /// is found at an offset of [text] is at that offset of the node. The diff --git a/apps/learn/halkyon_learn_flutter/lib/src/core/l10n/app_languages.dart b/apps/learn/halkyon_learn_flutter/lib/src/core/l10n/app_languages.dart index c711b948..fbd9e8cb 100644 --- a/apps/learn/halkyon_learn_flutter/lib/src/core/l10n/app_languages.dart +++ b/apps/learn/halkyon_learn_flutter/lib/src/core/l10n/app_languages.dart @@ -1,11 +1,12 @@ import 'package:halkyon_app/halkyon_app.dart'; +import 'package:halkyon_editor/halkyon_editor.dart'; import 'package:halkyon_learn_flutter/l10n/app_localizations.dart'; import 'package:halkyon_ui/halkyon_ui.dart'; import 'package:material_ui/material_ui.dart'; -/// The app's own texts, halkyon_app's and halkyon_ui's, and those of -/// Material, Cupertino and the widgets library, in every language the app -/// speaks. +/// The app's own texts, halkyon_app's, halkyon_editor's and halkyon_ui's, +/// and those of Material, Cupertino and the widgets library, in every +/// language the app speaks. /// /// Not gen-l10n's `AppLocalizations.localizationsDelegates`: those bring /// flutter_localizations' Material texts, which serve only @@ -15,6 +16,7 @@ import 'package:material_ui/material_ui.dart'; const appLocalizationsDelegates = >[ AppLocalizations.delegate, HalkyonAppLocalizations.delegate, + HalkyonEditorLocalizations.delegate, HalkyonUiLocalizations.delegate, ...GlobalMaterialLocalizations.delegates, ]; diff --git a/apps/learn/halkyon_learn_flutter/lib/src/feature/anki/data/anki_field.dart b/apps/learn/halkyon_learn_flutter/lib/src/feature/anki/data/anki_field.dart index 1b60f93a..f89f0641 100644 --- a/apps/learn/halkyon_learn_flutter/lib/src/feature/anki/data/anki_field.dart +++ b/apps/learn/halkyon_learn_flutter/lib/src/feature/anki/data/anki_field.dart @@ -1,5 +1,4 @@ -import 'package:halkyon_learn_flutter/src/core/content/content_blocks.dart'; -import 'package:halkyon_learn_flutter/src/core/content/rich_content.dart'; +import 'package:halkyon_editor/halkyon_editor.dart'; import 'package:html/dom.dart' as dom; import 'package:html/parser.dart' show parseFragment; diff --git a/apps/learn/halkyon_learn_flutter/lib/src/feature/anki/data/anki_importer.dart b/apps/learn/halkyon_learn_flutter/lib/src/feature/anki/data/anki_importer.dart index 4eb24293..204fae3e 100644 --- a/apps/learn/halkyon_learn_flutter/lib/src/feature/anki/data/anki_importer.dart +++ b/apps/learn/halkyon_learn_flutter/lib/src/feature/anki/data/anki_importer.dart @@ -1,8 +1,8 @@ import 'dart:developer'; +import 'package:halkyon_editor/halkyon_editor.dart'; import 'package:halkyon_learn_flutter/src/core/asset/data/asset_repository.dart'; import 'package:halkyon_learn_flutter/src/core/asset/data/media_type.dart'; -import 'package:halkyon_learn_flutter/src/core/content/rich_content.dart'; import 'package:halkyon_learn_flutter/src/feature/anki/data/anki_field.dart'; import 'package:halkyon_learn_flutter/src/feature/anki/data/anki_package.dart'; import 'package:halkyon_learn_flutter/src/feature/anki/model/anki_collection.dart'; diff --git a/apps/learn/halkyon_learn_flutter/lib/src/feature/archive/data/archive_exporter.dart b/apps/learn/halkyon_learn_flutter/lib/src/feature/archive/data/archive_exporter.dart index 591c442f..5c40c94c 100644 --- a/apps/learn/halkyon_learn_flutter/lib/src/feature/archive/data/archive_exporter.dart +++ b/apps/learn/halkyon_learn_flutter/lib/src/feature/archive/data/archive_exporter.dart @@ -2,9 +2,9 @@ import 'dart:convert'; import 'dart:typed_data'; import 'package:crypto/crypto.dart'; +import 'package:halkyon_editor/halkyon_editor.dart'; import 'package:halkyon_learn_flutter/src/core/asset/data/asset_payload.dart'; import 'package:halkyon_learn_flutter/src/core/asset/model/stored_file.dart'; -import 'package:halkyon_learn_flutter/src/core/content/rich_content.dart'; import 'package:halkyon_learn_flutter/src/feature/archive/model/import_archive.dart'; import 'package:halkyon_learn_flutter/src/feature/card/data/card_payload.dart'; import 'package:halkyon_learn_flutter/src/feature/card/data/card_repository.dart'; diff --git a/apps/learn/halkyon_learn_flutter/lib/src/feature/assistant/bloc/card_proposals_cubit.dart b/apps/learn/halkyon_learn_flutter/lib/src/feature/assistant/bloc/card_proposals_cubit.dart index 0ae85378..1cd776f6 100644 --- a/apps/learn/halkyon_learn_flutter/lib/src/feature/assistant/bloc/card_proposals_cubit.dart +++ b/apps/learn/halkyon_learn_flutter/lib/src/feature/assistant/bloc/card_proposals_cubit.dart @@ -3,8 +3,8 @@ import 'dart:async'; import 'package:equatable/equatable.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:halkyon_app/halkyon_app.dart'; +import 'package:halkyon_editor/halkyon_editor.dart'; import 'package:halkyon_learn_flutter/src/core/content/pdf_page_ref.dart'; -import 'package:halkyon_learn_flutter/src/core/content/rich_content.dart'; import 'package:halkyon_learn_flutter/src/feature/assistant/model/assistant_prompt.dart'; import 'package:halkyon_learn_flutter/src/feature/assistant/model/card_proposals.dart'; import 'package:halkyon_learn_flutter/src/feature/card/data/card_repository.dart'; diff --git a/apps/learn/halkyon_learn_flutter/lib/src/feature/assistant/model/assistant_topic.dart b/apps/learn/halkyon_learn_flutter/lib/src/feature/assistant/model/assistant_topic.dart index 1d5e86a0..c760b2c9 100644 --- a/apps/learn/halkyon_learn_flutter/lib/src/feature/assistant/model/assistant_topic.dart +++ b/apps/learn/halkyon_learn_flutter/lib/src/feature/assistant/model/assistant_topic.dart @@ -1,5 +1,5 @@ import 'package:equatable/equatable.dart'; -import 'package:halkyon_learn_flutter/src/core/content/rich_content.dart'; +import 'package:halkyon_editor/halkyon_editor.dart'; import 'package:halkyon_learn_flutter/src/feature/card/model/card_content.dart'; import 'package:halkyon_llm/halkyon_llm.dart' show LlmImage; diff --git a/apps/learn/halkyon_learn_flutter/lib/src/feature/assistant/widget/conversation_sheet.dart b/apps/learn/halkyon_learn_flutter/lib/src/feature/assistant/widget/conversation_sheet.dart index 1b96951c..068ccd7f 100644 --- a/apps/learn/halkyon_learn_flutter/lib/src/feature/assistant/widget/conversation_sheet.dart +++ b/apps/learn/halkyon_learn_flutter/lib/src/feature/assistant/widget/conversation_sheet.dart @@ -4,13 +4,12 @@ import 'package:appflowy_editor/appflowy_editor.dart' show markdownToDocument; import 'package:flutter/services.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:halkyon_app/halkyon_app.dart'; +import 'package:halkyon_editor/halkyon_editor.dart'; import 'package:halkyon_learn_flutter/l10n/app_localizations.dart'; import 'package:halkyon_learn_flutter/src/core/asset/widget/stored_file_image.dart'; -import 'package:halkyon_learn_flutter/src/core/content/rich_content.dart'; import 'package:halkyon_learn_flutter/src/core/dependencies/dependencies.dart'; import 'package:halkyon_learn_flutter/src/core/dependencies/person_dependencies.dart'; -import 'package:halkyon_learn_flutter/src/core/editor/rich_content_document.dart'; -import 'package:halkyon_learn_flutter/src/core/editor/rich_content_view.dart'; +import 'package:halkyon_learn_flutter/src/core/editor/learn_content.dart'; import 'package:halkyon_learn_flutter/src/feature/assistant/bloc/conversation_cubit.dart'; import 'package:halkyon_learn_flutter/src/feature/assistant/bloc/conversation_dock_cubit.dart'; import 'package:halkyon_learn_flutter/src/feature/assistant/model/assistant_conversation.dart'; @@ -688,7 +687,7 @@ class _AnswerState() extends State<_Answer> { return Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ - RichContentView(content: _content), + LearnContentView(content: _content), Align( alignment: AlignmentDirectional.centerStart, // Moved out by the room around its icon (a compact button is 40 diff --git a/apps/learn/halkyon_learn_flutter/lib/src/feature/card/data/card_payload.dart b/apps/learn/halkyon_learn_flutter/lib/src/feature/card/data/card_payload.dart index 869d6bde..0ba7a20b 100644 --- a/apps/learn/halkyon_learn_flutter/lib/src/feature/card/data/card_payload.dart +++ b/apps/learn/halkyon_learn_flutter/lib/src/feature/card/data/card_payload.dart @@ -1,9 +1,9 @@ import 'dart:typed_data'; +import 'package:halkyon_editor/halkyon_editor.dart'; import 'package:halkyon_learn_flutter/src/core/content/payload.dart'; import 'package:halkyon_learn_flutter/src/core/content/pdf_page_ref.dart'; import 'package:halkyon_learn_flutter/src/core/content/published_ref.dart'; -import 'package:halkyon_learn_flutter/src/core/content/rich_content.dart'; import 'package:halkyon_learn_flutter/src/feature/card/model/card_content.dart'; // A card revision's content as its synced row stores it (see diff --git a/apps/learn/halkyon_learn_flutter/lib/src/feature/card/model/card_content.dart b/apps/learn/halkyon_learn_flutter/lib/src/feature/card/model/card_content.dart index 3e7c7d25..6c0b6b0e 100644 --- a/apps/learn/halkyon_learn_flutter/lib/src/feature/card/model/card_content.dart +++ b/apps/learn/halkyon_learn_flutter/lib/src/feature/card/model/card_content.dart @@ -1,8 +1,8 @@ import 'package:equatable/equatable.dart'; +import 'package:halkyon_editor/halkyon_editor.dart'; import 'package:halkyon_learn_flutter/src/core/content/pdf_page_ref.dart'; import 'package:halkyon_learn_flutter/src/core/content/published_ref.dart'; import 'package:halkyon_learn_flutter/src/core/content/revision_merge.dart'; -import 'package:halkyon_learn_flutter/src/core/content/rich_content.dart'; /// What kind of card it is: how it is edited and studied. A closed set the /// app knows; what a card is about goes into its tags. diff --git a/apps/learn/halkyon_learn_flutter/lib/src/feature/card/widget/card_browser_screen.dart b/apps/learn/halkyon_learn_flutter/lib/src/feature/card/widget/card_browser_screen.dart index e50a7f4c..e4b2b202 100644 --- a/apps/learn/halkyon_learn_flutter/lib/src/feature/card/widget/card_browser_screen.dart +++ b/apps/learn/halkyon_learn_flutter/lib/src/feature/card/widget/card_browser_screen.dart @@ -9,7 +9,7 @@ import 'package:halkyon_learn_flutter/l10n/app_localizations.dart'; import 'package:halkyon_learn_flutter/src/core/dependencies/person_dependencies.dart'; import 'package:halkyon_learn_flutter/src/core/device_settings/bloc/device_settings_bloc.dart'; import 'package:halkyon_learn_flutter/src/core/device_settings/model/device_settings.dart'; -import 'package:halkyon_learn_flutter/src/core/editor/rich_content_view.dart'; +import 'package:halkyon_learn_flutter/src/core/editor/learn_content.dart'; import 'package:halkyon_learn_flutter/src/core/editor/ruby.dart'; import 'package:halkyon_learn_flutter/src/core/router/route_paths.dart'; import 'package:halkyon_learn_flutter/src/core/widget/content_languages.dart'; @@ -968,7 +968,7 @@ class const _Front({ ), ); } - return RichContentView( + return LearnContentView( content: content.front, textStyle: style, textAlign: centred ? TextAlign.center : null, @@ -1093,7 +1093,7 @@ class const _Back({ ], ); case CardType.frontBack: - return RichContentView( + return LearnContentView( content: content.back, textStyle: style, textAlign: centred ? TextAlign.center : null, diff --git a/apps/learn/halkyon_learn_flutter/lib/src/feature/card/widget/card_extras.dart b/apps/learn/halkyon_learn_flutter/lib/src/feature/card/widget/card_extras.dart index a5436e58..b0ba381f 100644 --- a/apps/learn/halkyon_learn_flutter/lib/src/feature/card/widget/card_extras.dart +++ b/apps/learn/halkyon_learn_flutter/lib/src/feature/card/widget/card_extras.dart @@ -1,6 +1,6 @@ +import 'package:halkyon_editor/halkyon_editor.dart'; import 'package:halkyon_learn_flutter/l10n/app_localizations.dart'; -import 'package:halkyon_learn_flutter/src/core/content/rich_content.dart'; -import 'package:halkyon_learn_flutter/src/core/editor/rich_content_view.dart'; +import 'package:halkyon_learn_flutter/src/core/editor/learn_content.dart'; import 'package:halkyon_ui/halkyon_ui.dart'; import 'package:lucide_icons_flutter/lucide_icons.dart'; import 'package:material_ui/material_ui.dart'; @@ -39,7 +39,7 @@ class const ExplanationButton({ context, builder: (context) => SheetScaffold( title: l10n.cardExplanation, - body: RichContentView(content: explanation), + body: LearnContentView(content: explanation), ), ), ), @@ -156,7 +156,7 @@ class _HintsSheetState() extends State<_HintsSheet> { ], ) : null, - body: RichContentView( + body: LearnContentView( key: ValueKey(_index), content: widget.hints[_index], ), diff --git a/apps/learn/halkyon_learn_flutter/lib/src/feature/card/widget/card_text_import_sheet.dart b/apps/learn/halkyon_learn_flutter/lib/src/feature/card/widget/card_text_import_sheet.dart index fb009f6c..b1722d85 100644 --- a/apps/learn/halkyon_learn_flutter/lib/src/feature/card/widget/card_text_import_sheet.dart +++ b/apps/learn/halkyon_learn_flutter/lib/src/feature/card/widget/card_text_import_sheet.dart @@ -1,7 +1,7 @@ import 'dart:async'; +import 'package:halkyon_editor/halkyon_editor.dart'; import 'package:halkyon_learn_flutter/l10n/app_localizations.dart'; -import 'package:halkyon_learn_flutter/src/core/content/rich_content.dart'; import 'package:halkyon_learn_flutter/src/core/dependencies/dependencies.dart'; import 'package:halkyon_learn_flutter/src/core/dependencies/person_dependencies.dart'; import 'package:halkyon_learn_flutter/src/feature/card/model/card_content.dart'; diff --git a/apps/learn/halkyon_learn_flutter/lib/src/feature/card/widget/composer/answers_editor.dart b/apps/learn/halkyon_learn_flutter/lib/src/feature/card/widget/composer/answers_editor.dart index a08cb7fc..ea1c5af0 100644 --- a/apps/learn/halkyon_learn_flutter/lib/src/feature/card/widget/composer/answers_editor.dart +++ b/apps/learn/halkyon_learn_flutter/lib/src/feature/card/widget/composer/answers_editor.dart @@ -1,5 +1,5 @@ +import 'package:halkyon_editor/halkyon_editor.dart'; import 'package:halkyon_learn_flutter/l10n/app_localizations.dart'; -import 'package:halkyon_learn_flutter/src/core/content/rich_content.dart'; import 'package:halkyon_learn_flutter/src/core/grammar/grammar_host.dart'; import 'package:halkyon_learn_flutter/src/feature/card/model/card_content.dart'; import 'package:halkyon_learn_flutter/src/feature/card/widget/composer/card_face.dart'; diff --git a/apps/learn/halkyon_learn_flutter/lib/src/feature/card/widget/composer/card_draft_editor.dart b/apps/learn/halkyon_learn_flutter/lib/src/feature/card/widget/composer/card_draft_editor.dart index 370031fe..93812e21 100644 --- a/apps/learn/halkyon_learn_flutter/lib/src/feature/card/widget/composer/card_draft_editor.dart +++ b/apps/learn/halkyon_learn_flutter/lib/src/feature/card/widget/composer/card_draft_editor.dart @@ -1,9 +1,9 @@ import 'package:flutter/services.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:halkyon_editor/halkyon_editor.dart'; import 'package:halkyon_learn_flutter/l10n/app_localizations.dart'; import 'package:halkyon_learn_flutter/src/core/content/published_ref.dart'; -import 'package:halkyon_learn_flutter/src/core/content/rich_content.dart'; -import 'package:halkyon_learn_flutter/src/core/editor/rich_content_view.dart'; +import 'package:halkyon_learn_flutter/src/core/editor/learn_content.dart'; import 'package:halkyon_learn_flutter/src/core/grammar/grammar_host.dart'; import 'package:halkyon_learn_flutter/src/core/l10n/language_tags.dart'; import 'package:halkyon_learn_flutter/src/core/widget/conflict_versions.dart'; @@ -758,7 +758,7 @@ class const _Conflicts({required final CardDraft draft}) class const _Version({required final Object? version}) extends StatelessWidget { @override Widget build(BuildContext context) => switch (version) { - final RichContent content => RichContentView(content: content), + final RichContent content => LearnContentView(content: content), final List answers => Text( [ for (final answer in answers) @@ -768,7 +768,7 @@ class const _Version({required final Object? version}) extends StatelessWidget { final List meanings => Text(meanings.join('; ')), final List hints => Column( crossAxisAlignment: CrossAxisAlignment.stretch, - children: [for (final hint in hints) RichContentView(content: hint)], + children: [for (final hint in hints) LearnContentView(content: hint)], ), final List senses => Text( [ diff --git a/apps/learn/halkyon_learn_flutter/lib/src/feature/card/widget/composer/card_face.dart b/apps/learn/halkyon_learn_flutter/lib/src/feature/card/widget/composer/card_face.dart index 40e51f1b..27ba2246 100644 --- a/apps/learn/halkyon_learn_flutter/lib/src/feature/card/widget/composer/card_face.dart +++ b/apps/learn/halkyon_learn_flutter/lib/src/feature/card/widget/composer/card_face.dart @@ -1,8 +1,6 @@ import 'package:flutter/gestures.dart'; -import 'package:halkyon_learn_flutter/src/core/content/rich_content.dart'; -import 'package:halkyon_learn_flutter/src/core/editor/format_bar.dart'; -import 'package:halkyon_learn_flutter/src/core/editor/rich_content_editor.dart'; -import 'package:halkyon_learn_flutter/src/core/editor/rich_content_view.dart'; +import 'package:halkyon_editor/halkyon_editor.dart'; +import 'package:halkyon_learn_flutter/src/core/editor/learn_content.dart'; import 'package:halkyon_learn_flutter/src/core/grammar/grammar_host.dart'; import 'package:lucide_icons_flutter/lucide_icons.dart'; import 'package:material_ui/material_ui.dart'; @@ -212,7 +210,7 @@ class _CardFaceState() extends State with WidgetsBindingObserver { final accent = widget.accent; final editor = SizedBox( height: _editorHeight, - child: RichContentEditor( + child: LearnContentEditor( initialContent: widget.content, onChanged: widget.onChanged, focusNode: _focus, @@ -272,7 +270,7 @@ class _CardFaceState() extends State with WidgetsBindingObserver { key: _preview, child: SingleChildScrollView( physics: const NeverScrollableScrollPhysics(), - child: RichContentView(content: widget.content), + child: LearnContentView(content: widget.content), ), ); final content = empty diff --git a/apps/learn/halkyon_learn_flutter/lib/src/feature/dictionary/model/dictionary.dart b/apps/learn/halkyon_learn_flutter/lib/src/feature/dictionary/model/dictionary.dart index b8def9ae..98620cf8 100644 --- a/apps/learn/halkyon_learn_flutter/lib/src/feature/dictionary/model/dictionary.dart +++ b/apps/learn/halkyon_learn_flutter/lib/src/feature/dictionary/model/dictionary.dart @@ -1,7 +1,7 @@ import 'dart:convert'; import 'package:equatable/equatable.dart'; -import 'package:halkyon_learn_flutter/src/core/content/rich_content.dart'; +import 'package:halkyon_editor/halkyon_editor.dart'; import 'package:halkyon_learn_flutter/src/feature/card/model/card_content.dart'; import 'package:halkyon_learn_flutter/src/feature/tag/model/tag.dart'; diff --git a/apps/learn/halkyon_learn_flutter/lib/src/feature/discussion/bloc/discussion_bloc.dart b/apps/learn/halkyon_learn_flutter/lib/src/feature/discussion/bloc/discussion_bloc.dart index d973fa44..736c618f 100644 --- a/apps/learn/halkyon_learn_flutter/lib/src/feature/discussion/bloc/discussion_bloc.dart +++ b/apps/learn/halkyon_learn_flutter/lib/src/feature/discussion/bloc/discussion_bloc.dart @@ -2,7 +2,7 @@ import 'dart:developer'; import 'package:equatable/equatable.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:halkyon_learn_flutter/src/core/content/rich_content.dart'; +import 'package:halkyon_editor/halkyon_editor.dart'; import 'package:halkyon_learn_flutter/src/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/data/discussion_repository.dart'; diff --git a/apps/learn/halkyon_learn_flutter/lib/src/feature/discussion/bloc/discussion_list_bloc.dart b/apps/learn/halkyon_learn_flutter/lib/src/feature/discussion/bloc/discussion_list_bloc.dart index d9b3f685..1c3e749c 100644 --- a/apps/learn/halkyon_learn_flutter/lib/src/feature/discussion/bloc/discussion_list_bloc.dart +++ b/apps/learn/halkyon_learn_flutter/lib/src/feature/discussion/bloc/discussion_list_bloc.dart @@ -3,7 +3,7 @@ import 'dart:developer'; import 'package:equatable/equatable.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:halkyon_learn_flutter/src/core/content/rich_content.dart'; +import 'package:halkyon_editor/halkyon_editor.dart'; import 'package:halkyon_learn_flutter/src/feature/discussion/data/discussion_repository.dart'; import 'package:halkyon_learn_flutter/src/feature/discussion/model/discussion.dart'; diff --git a/apps/learn/halkyon_learn_flutter/lib/src/feature/discussion/data/discussion_gateway.dart b/apps/learn/halkyon_learn_flutter/lib/src/feature/discussion/data/discussion_gateway.dart index b622f2be..20a75de6 100644 --- a/apps/learn/halkyon_learn_flutter/lib/src/feature/discussion/data/discussion_gateway.dart +++ b/apps/learn/halkyon_learn_flutter/lib/src/feature/discussion/data/discussion_gateway.dart @@ -1,4 +1,4 @@ -import 'package:halkyon_learn_flutter/src/core/content/rich_content.dart'; +import 'package:halkyon_editor/halkyon_editor.dart'; import 'package:halkyon_learn_flutter/src/feature/discussion/model/discussion.dart'; /// The boundary to the instance's discussions (ADR 0013). Writing throws diff --git a/apps/learn/halkyon_learn_flutter/lib/src/feature/discussion/data/discussion_repository.dart b/apps/learn/halkyon_learn_flutter/lib/src/feature/discussion/data/discussion_repository.dart index c5d4442f..61624feb 100644 --- a/apps/learn/halkyon_learn_flutter/lib/src/feature/discussion/data/discussion_repository.dart +++ b/apps/learn/halkyon_learn_flutter/lib/src/feature/discussion/data/discussion_repository.dart @@ -1,6 +1,6 @@ import 'dart:async'; -import 'package:halkyon_learn_flutter/src/core/content/rich_content.dart'; +import 'package:halkyon_editor/halkyon_editor.dart'; import 'package:halkyon_learn_flutter/src/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'; diff --git a/apps/learn/halkyon_learn_flutter/lib/src/feature/discussion/data/serverpod_discussion_gateway.dart b/apps/learn/halkyon_learn_flutter/lib/src/feature/discussion/data/serverpod_discussion_gateway.dart index 087f4e90..3c922082 100644 --- a/apps/learn/halkyon_learn_flutter/lib/src/feature/discussion/data/serverpod_discussion_gateway.dart +++ b/apps/learn/halkyon_learn_flutter/lib/src/feature/discussion/data/serverpod_discussion_gateway.dart @@ -1,8 +1,8 @@ import 'dart:convert'; import 'dart:developer'; +import 'package:halkyon_editor/halkyon_editor.dart'; import 'package:halkyon_learn_client/halkyon_learn_client.dart' as protocol; -import 'package:halkyon_learn_flutter/src/core/content/rich_content.dart'; import 'package:halkyon_learn_flutter/src/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'; diff --git a/apps/learn/halkyon_learn_flutter/lib/src/feature/discussion/model/discussion.dart b/apps/learn/halkyon_learn_flutter/lib/src/feature/discussion/model/discussion.dart index 603689f9..c65be284 100644 --- a/apps/learn/halkyon_learn_flutter/lib/src/feature/discussion/model/discussion.dart +++ b/apps/learn/halkyon_learn_flutter/lib/src/feature/discussion/model/discussion.dart @@ -1,5 +1,5 @@ import 'package:equatable/equatable.dart'; -import 'package:halkyon_learn_flutter/src/core/content/rich_content.dart'; +import 'package:halkyon_editor/halkyon_editor.dart'; import 'package:halkyon_learn_flutter/src/core/profile/public_person.dart'; // A published studyset's discussions (ADR 0013, decided 2026-10-15): questions diff --git a/apps/learn/halkyon_learn_flutter/lib/src/feature/discussion/widget/discussion_composer.dart b/apps/learn/halkyon_learn_flutter/lib/src/feature/discussion/widget/discussion_composer.dart index 1f5f7043..0ab24ae8 100644 --- a/apps/learn/halkyon_learn_flutter/lib/src/feature/discussion/widget/discussion_composer.dart +++ b/apps/learn/halkyon_learn_flutter/lib/src/feature/discussion/widget/discussion_composer.dart @@ -1,10 +1,9 @@ import 'dart:math' as math; +import 'package:halkyon_editor/halkyon_editor.dart'; import 'package:halkyon_learn_flutter/l10n/app_localizations.dart'; -import 'package:halkyon_learn_flutter/src/core/content/rich_content.dart'; import 'package:halkyon_learn_flutter/src/core/editor/core_blocks.dart'; -import 'package:halkyon_learn_flutter/src/core/editor/editor_blocks.dart'; -import 'package:halkyon_learn_flutter/src/core/editor/rich_content_editor.dart'; +import 'package:halkyon_learn_flutter/src/core/editor/learn_content.dart'; import 'package:halkyon_learn_flutter/src/core/grammar/grammar_host.dart'; import 'package:halkyon_learn_flutter/src/feature/discussion/model/discussion.dart'; import 'package:halkyon_learn_flutter/src/feature/discussion/widget/discussion_texts.dart'; @@ -78,14 +77,10 @@ class _ComposerState() extends State<_Composer> { // The core's blocks but files and videos, and none of the features': // a post is read by others, who have neither the person's files nor // their studysets. - final blocks = EditorBlocks( - builders: all.builders, - insertable: [ - for (final block in coreBlocks.insertable) - if (block.group != InsertGroup.media) block, - ], - shortcuts: all.shortcuts, - ); + final blocks = all.offering([ + for (final block in coreBlocks.insertable) + if (block.group != InsertGroup.media) block, + ]); return Scaffold( appBar: AppBar( title: Text(widget.heading), @@ -145,7 +140,7 @@ class _ComposerState() extends State<_Composer> { height: math.max(320, constraints.maxHeight - 160), child: EditorBlocksScope( blocks: blocks, - child: RichContentEditor( + child: LearnContentEditor( initialContent: widget.initial, autofocus: !widget.withTitle, onChanged: (content) => diff --git a/apps/learn/halkyon_learn_flutter/lib/src/feature/discussion/widget/discussion_screen.dart b/apps/learn/halkyon_learn_flutter/lib/src/feature/discussion/widget/discussion_screen.dart index a36cd876..8073456f 100644 --- a/apps/learn/halkyon_learn_flutter/lib/src/feature/discussion/widget/discussion_screen.dart +++ b/apps/learn/halkyon_learn_flutter/lib/src/feature/discussion/widget/discussion_screen.dart @@ -3,7 +3,7 @@ import 'dart:async'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:halkyon_learn_flutter/l10n/app_localizations.dart'; import 'package:halkyon_learn_flutter/src/core/dependencies/person_dependencies.dart'; -import 'package:halkyon_learn_flutter/src/core/editor/rich_content_view.dart'; +import 'package:halkyon_learn_flutter/src/core/editor/learn_content.dart'; import 'package:halkyon_learn_flutter/src/feature/discussion/bloc/discussion_bloc.dart'; import 'package:halkyon_learn_flutter/src/feature/discussion/model/discussion.dart'; import 'package:halkyon_learn_flutter/src/feature/discussion/widget/comment_sheet.dart'; @@ -261,7 +261,7 @@ class const _Post({ style: const TextStyle(fontStyle: FontStyle.italic), ) else - RichContentView(content: content), + LearnContentView(content: content), if (mayAccept || mayComment) Wrap( spacing: 8, diff --git a/apps/learn/halkyon_learn_flutter/lib/src/feature/explore/data/release_copier.dart b/apps/learn/halkyon_learn_flutter/lib/src/feature/explore/data/release_copier.dart index bc4af26e..16a59358 100644 --- a/apps/learn/halkyon_learn_flutter/lib/src/feature/explore/data/release_copier.dart +++ b/apps/learn/halkyon_learn_flutter/lib/src/feature/explore/data/release_copier.dart @@ -1,10 +1,9 @@ import 'dart:convert'; import 'dart:typed_data'; +import 'package:halkyon_editor/halkyon_editor.dart'; import 'package:halkyon_learn_flutter/src/core/asset/data/asset_repository.dart'; -import 'package:halkyon_learn_flutter/src/core/content/content_blocks.dart'; import 'package:halkyon_learn_flutter/src/core/content/published_ref.dart'; -import 'package:halkyon_learn_flutter/src/core/content/rich_content.dart'; import 'package:halkyon_learn_flutter/src/feature/card/data/card_payload.dart'; import 'package:halkyon_learn_flutter/src/feature/card/data/card_repository.dart'; import 'package:halkyon_learn_flutter/src/feature/card/model/card_content.dart'; diff --git a/apps/learn/halkyon_learn_flutter/lib/src/feature/explore/data/serverpod_explore_gateway.dart b/apps/learn/halkyon_learn_flutter/lib/src/feature/explore/data/serverpod_explore_gateway.dart index e6c9182c..a02d052a 100644 --- a/apps/learn/halkyon_learn_flutter/lib/src/feature/explore/data/serverpod_explore_gateway.dart +++ b/apps/learn/halkyon_learn_flutter/lib/src/feature/explore/data/serverpod_explore_gateway.dart @@ -1,8 +1,8 @@ import 'dart:convert'; import 'dart:typed_data'; +import 'package:halkyon_editor/halkyon_editor.dart'; import 'package:halkyon_learn_client/halkyon_learn_client.dart' as protocol; -import 'package:halkyon_learn_flutter/src/core/content/rich_content.dart'; import 'package:halkyon_learn_flutter/src/feature/explore/data/explore_gateway.dart'; import 'package:halkyon_learn_flutter/src/feature/explore/model/explore_studyset.dart'; diff --git a/apps/learn/halkyon_learn_flutter/lib/src/feature/explore/model/explore_studyset.dart b/apps/learn/halkyon_learn_flutter/lib/src/feature/explore/model/explore_studyset.dart index 77e01468..c26f47c0 100644 --- a/apps/learn/halkyon_learn_flutter/lib/src/feature/explore/model/explore_studyset.dart +++ b/apps/learn/halkyon_learn_flutter/lib/src/feature/explore/model/explore_studyset.dart @@ -1,5 +1,5 @@ import 'package:equatable/equatable.dart'; -import 'package:halkyon_learn_flutter/src/core/content/rich_content.dart'; +import 'package:halkyon_editor/halkyon_editor.dart'; /// A published studyset as Explore shows it (ADR 0005, rule 4): where it /// is, who made it, and its latest release with what it holds. diff --git a/apps/learn/halkyon_learn_flutter/lib/src/feature/explore/widget/explore_summary_sheet.dart b/apps/learn/halkyon_learn_flutter/lib/src/feature/explore/widget/explore_summary_sheet.dart index 80aacdcd..f91b27e0 100644 --- a/apps/learn/halkyon_learn_flutter/lib/src/feature/explore/widget/explore_summary_sheet.dart +++ b/apps/learn/halkyon_learn_flutter/lib/src/feature/explore/widget/explore_summary_sheet.dart @@ -2,7 +2,7 @@ import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:go_router/go_router.dart'; import 'package:halkyon_app/halkyon_app.dart'; import 'package:halkyon_learn_flutter/l10n/app_localizations.dart'; -import 'package:halkyon_learn_flutter/src/core/editor/rich_content_view.dart'; +import 'package:halkyon_learn_flutter/src/core/editor/learn_content.dart'; import 'package:halkyon_learn_flutter/src/core/l10n/content_languages.dart'; import 'package:halkyon_learn_flutter/src/core/router/route_paths.dart'; import 'package:halkyon_learn_flutter/src/feature/explore/bloc/explore_bloc.dart'; @@ -98,7 +98,7 @@ class const _Summary({required final ExploreStudyset studyset}) ), ) else - RichContentView(content: studyset.description), + LearnContentView(content: studyset.description), ], ), ), diff --git a/apps/learn/halkyon_learn_flutter/lib/src/feature/export/data/studyset_markdown.dart b/apps/learn/halkyon_learn_flutter/lib/src/feature/export/data/studyset_markdown.dart index 1731bcec..77b5ed84 100644 --- a/apps/learn/halkyon_learn_flutter/lib/src/feature/export/data/studyset_markdown.dart +++ b/apps/learn/halkyon_learn_flutter/lib/src/feature/export/data/studyset_markdown.dart @@ -1,9 +1,9 @@ import 'dart:typed_data'; +import 'package:halkyon_editor/halkyon_editor.dart'; import 'package:halkyon_learn_flutter/src/core/asset/model/stored_file.dart'; import 'package:halkyon_learn_flutter/src/core/citation/model/citation.dart'; import 'package:halkyon_learn_flutter/src/core/citation/model/source.dart'; -import 'package:halkyon_learn_flutter/src/core/content/rich_content.dart'; import 'package:halkyon_learn_flutter/src/core/editor/rich_content_markdown.dart'; import 'package:halkyon_learn_flutter/src/feature/card/data/card_repository.dart'; import 'package:halkyon_learn_flutter/src/feature/card/model/card.dart'; diff --git a/apps/learn/halkyon_learn_flutter/lib/src/feature/feedback/data/feedback_repository.dart b/apps/learn/halkyon_learn_flutter/lib/src/feature/feedback/data/feedback_repository.dart index 0301f763..3794f352 100644 --- a/apps/learn/halkyon_learn_flutter/lib/src/feature/feedback/data/feedback_repository.dart +++ b/apps/learn/halkyon_learn_flutter/lib/src/feature/feedback/data/feedback_repository.dart @@ -2,10 +2,10 @@ import 'dart:convert'; import 'dart:typed_data'; import 'package:crypto/crypto.dart'; +import 'package:halkyon_editor/halkyon_editor.dart'; import 'package:halkyon_learn_flutter/src/core/asset/data/asset_repository.dart'; import 'package:halkyon_learn_flutter/src/core/asset/data/asset_sealing.dart'; import 'package:halkyon_learn_flutter/src/core/content/published_ref.dart'; -import 'package:halkyon_learn_flutter/src/core/content/rich_content.dart'; import 'package:halkyon_learn_flutter/src/core/privacy/data/privacy.dart'; import 'package:halkyon_learn_flutter/src/core/profile/public_person.dart'; import 'package:halkyon_learn_flutter/src/feature/card/data/card_payload.dart'; diff --git a/apps/learn/halkyon_learn_flutter/lib/src/feature/feedback/widget/feedback_threads.dart b/apps/learn/halkyon_learn_flutter/lib/src/feature/feedback/widget/feedback_threads.dart index 95d8eef9..4e2198c9 100644 --- a/apps/learn/halkyon_learn_flutter/lib/src/feature/feedback/widget/feedback_threads.dart +++ b/apps/learn/halkyon_learn_flutter/lib/src/feature/feedback/widget/feedback_threads.dart @@ -1,6 +1,6 @@ import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:halkyon_editor/halkyon_editor.dart'; import 'package:halkyon_learn_flutter/l10n/app_localizations.dart'; -import 'package:halkyon_learn_flutter/src/core/content/rich_content.dart'; import 'package:halkyon_learn_flutter/src/core/dependencies/person_dependencies.dart'; import 'package:halkyon_learn_flutter/src/feature/card/widget/report_sheet.dart'; import 'package:halkyon_learn_flutter/src/feature/feedback/bloc/feedback_list_bloc.dart'; diff --git a/apps/learn/halkyon_learn_flutter/lib/src/feature/learning_path/bloc/path_editor_bloc.dart b/apps/learn/halkyon_learn_flutter/lib/src/feature/learning_path/bloc/path_editor_bloc.dart index 7a123c28..eb719830 100644 --- a/apps/learn/halkyon_learn_flutter/lib/src/feature/learning_path/bloc/path_editor_bloc.dart +++ b/apps/learn/halkyon_learn_flutter/lib/src/feature/learning_path/bloc/path_editor_bloc.dart @@ -3,7 +3,7 @@ import 'dart:developer'; import 'package:equatable/equatable.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:halkyon_learn_flutter/src/core/content/rich_content.dart'; +import 'package:halkyon_editor/halkyon_editor.dart'; import 'package:halkyon_learn_flutter/src/core/sync/fractional_position.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_repository.dart'; diff --git a/apps/learn/halkyon_learn_flutter/lib/src/feature/learning_path/data/learning_path_payload.dart b/apps/learn/halkyon_learn_flutter/lib/src/feature/learning_path/data/learning_path_payload.dart index 61f66016..3dfa5848 100644 --- a/apps/learn/halkyon_learn_flutter/lib/src/feature/learning_path/data/learning_path_payload.dart +++ b/apps/learn/halkyon_learn_flutter/lib/src/feature/learning_path/data/learning_path_payload.dart @@ -1,8 +1,8 @@ import 'dart:typed_data'; +import 'package:halkyon_editor/halkyon_editor.dart'; import 'package:halkyon_learn_flutter/src/core/content/payload.dart'; import 'package:halkyon_learn_flutter/src/core/content/published_ref.dart'; -import 'package:halkyon_learn_flutter/src/core/content/rich_content.dart'; import 'package:halkyon_learn_flutter/src/feature/learning_path/model/learning_path.dart'; // A learning path's content as its synced row stores it (see diff --git a/apps/learn/halkyon_learn_flutter/lib/src/feature/learning_path/data/learning_path_step_payload.dart b/apps/learn/halkyon_learn_flutter/lib/src/feature/learning_path/data/learning_path_step_payload.dart index 320b4ef8..4fb63a67 100644 --- a/apps/learn/halkyon_learn_flutter/lib/src/feature/learning_path/data/learning_path_step_payload.dart +++ b/apps/learn/halkyon_learn_flutter/lib/src/feature/learning_path/data/learning_path_step_payload.dart @@ -1,10 +1,10 @@ import 'dart:convert'; import 'dart:typed_data'; +import 'package:halkyon_editor/halkyon_editor.dart'; import 'package:halkyon_learn_flutter/src/core/citation/model/source.dart'; import 'package:halkyon_learn_flutter/src/core/content/payload.dart'; import 'package:halkyon_learn_flutter/src/core/content/published_ref.dart'; -import 'package:halkyon_learn_flutter/src/core/content/rich_content.dart'; import 'package:halkyon_learn_flutter/src/feature/learning_path/model/answer.dart'; import 'package:halkyon_learn_flutter/src/feature/learning_path/model/exercise.dart'; import 'package:halkyon_learn_flutter/src/feature/learning_path/model/step_content.dart'; diff --git a/apps/learn/halkyon_learn_flutter/lib/src/feature/learning_path/data/path_import.dart b/apps/learn/halkyon_learn_flutter/lib/src/feature/learning_path/data/path_import.dart index 72719006..50bdf15e 100644 --- a/apps/learn/halkyon_learn_flutter/lib/src/feature/learning_path/data/path_import.dart +++ b/apps/learn/halkyon_learn_flutter/lib/src/feature/learning_path/data/path_import.dart @@ -1,9 +1,8 @@ import 'dart:convert'; import 'dart:math'; +import 'package:halkyon_editor/halkyon_editor.dart'; import 'package:halkyon_learn_flutter/src/core/citation/model/source.dart'; -import 'package:halkyon_learn_flutter/src/core/content/content_blocks.dart'; -import 'package:halkyon_learn_flutter/src/core/content/rich_content.dart'; import 'package:halkyon_learn_flutter/src/feature/learning_path/data/learning_path_repository.dart'; import 'package:halkyon_learn_flutter/src/feature/learning_path/model/exercise.dart'; import 'package:halkyon_learn_flutter/src/feature/learning_path/model/learning_path.dart'; diff --git a/apps/learn/halkyon_learn_flutter/lib/src/feature/learning_path/model/learning_path.dart b/apps/learn/halkyon_learn_flutter/lib/src/feature/learning_path/model/learning_path.dart index df24fd96..5cd60b8d 100644 --- a/apps/learn/halkyon_learn_flutter/lib/src/feature/learning_path/model/learning_path.dart +++ b/apps/learn/halkyon_learn_flutter/lib/src/feature/learning_path/model/learning_path.dart @@ -1,6 +1,6 @@ import 'package:equatable/equatable.dart'; +import 'package:halkyon_editor/halkyon_editor.dart'; import 'package:halkyon_learn_flutter/src/core/content/published_ref.dart'; -import 'package:halkyon_learn_flutter/src/core/content/rich_content.dart'; /// What a learning path says about itself: its title, a secondary title (a /// translation, say) and a description. diff --git a/apps/learn/halkyon_learn_flutter/lib/src/feature/learning_path/model/step_content.dart b/apps/learn/halkyon_learn_flutter/lib/src/feature/learning_path/model/step_content.dart index 0a9f29c0..612e0d2c 100644 --- a/apps/learn/halkyon_learn_flutter/lib/src/feature/learning_path/model/step_content.dart +++ b/apps/learn/halkyon_learn_flutter/lib/src/feature/learning_path/model/step_content.dart @@ -1,8 +1,8 @@ import 'package:equatable/equatable.dart'; +import 'package:halkyon_editor/halkyon_editor.dart'; import 'package:halkyon_learn_flutter/src/core/citation/model/source.dart'; import 'package:halkyon_learn_flutter/src/core/content/published_ref.dart'; import 'package:halkyon_learn_flutter/src/core/content/revision_merge.dart'; -import 'package:halkyon_learn_flutter/src/core/content/rich_content.dart'; import 'package:halkyon_learn_flutter/src/feature/learning_path/model/exercise.dart'; /// The parts of a step that merge separately when devices edited it while diff --git a/apps/learn/halkyon_learn_flutter/lib/src/feature/learning_path/widget/path_editor_screen.dart b/apps/learn/halkyon_learn_flutter/lib/src/feature/learning_path/widget/path_editor_screen.dart index 860e329e..3469f24f 100644 --- a/apps/learn/halkyon_learn_flutter/lib/src/feature/learning_path/widget/path_editor_screen.dart +++ b/apps/learn/halkyon_learn_flutter/lib/src/feature/learning_path/widget/path_editor_screen.dart @@ -1,7 +1,7 @@ import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:go_router/go_router.dart'; +import 'package:halkyon_editor/halkyon_editor.dart'; import 'package:halkyon_learn_flutter/l10n/app_localizations.dart'; -import 'package:halkyon_learn_flutter/src/core/content/rich_content.dart'; import 'package:halkyon_learn_flutter/src/core/dependencies/person_dependencies.dart'; import 'package:halkyon_learn_flutter/src/core/editor/rich_text_screen.dart'; import 'package:halkyon_learn_flutter/src/core/router/route_paths.dart'; diff --git a/apps/learn/halkyon_learn_flutter/lib/src/feature/learning_path/widget/path_runner_screen.dart b/apps/learn/halkyon_learn_flutter/lib/src/feature/learning_path/widget/path_runner_screen.dart index a2102e42..b2c72787 100644 --- a/apps/learn/halkyon_learn_flutter/lib/src/feature/learning_path/widget/path_runner_screen.dart +++ b/apps/learn/halkyon_learn_flutter/lib/src/feature/learning_path/widget/path_runner_screen.dart @@ -2,11 +2,11 @@ import 'dart:async'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:go_router/go_router.dart'; +import 'package:halkyon_editor/halkyon_editor.dart'; import 'package:halkyon_learn_flutter/l10n/app_localizations.dart'; import 'package:halkyon_learn_flutter/src/core/citation/widget/source_list.dart'; -import 'package:halkyon_learn_flutter/src/core/content/rich_content.dart'; import 'package:halkyon_learn_flutter/src/core/dependencies/person_dependencies.dart'; -import 'package:halkyon_learn_flutter/src/core/editor/rich_content_view.dart'; +import 'package:halkyon_learn_flutter/src/core/editor/learn_content.dart'; import 'package:halkyon_learn_flutter/src/core/router/route_paths.dart'; import 'package:halkyon_learn_flutter/src/feature/learning_path/bloc/path_runner_bloc.dart'; import 'package:halkyon_learn_flutter/src/feature/learning_path/model/learning_path_step.dart'; @@ -231,7 +231,7 @@ class const _Step({ Text(content.title, style: theme.textTheme.titleLarge), if (!body.isBlank) ...[ const SizedBox(height: 8), - RichContentView(content: body), + LearnContentView(content: body), ], if (content.sources.isNotEmpty) Padding( @@ -332,7 +332,7 @@ class const _Feedback({ ), if (!feedback.isBlank) ...[ const SizedBox(height: 8), - RichContentView(content: feedback), + LearnContentView(content: feedback), ], ], ), diff --git a/apps/learn/halkyon_learn_flutter/lib/src/feature/learning_path/widget/step_editor_screen.dart b/apps/learn/halkyon_learn_flutter/lib/src/feature/learning_path/widget/step_editor_screen.dart index 4465c054..ca4bdfa8 100644 --- a/apps/learn/halkyon_learn_flutter/lib/src/feature/learning_path/widget/step_editor_screen.dart +++ b/apps/learn/halkyon_learn_flutter/lib/src/feature/learning_path/widget/step_editor_screen.dart @@ -1,13 +1,12 @@ import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:go_router/go_router.dart'; +import 'package:halkyon_editor/halkyon_editor.dart'; import 'package:halkyon_learn_flutter/l10n/app_localizations.dart'; import 'package:halkyon_learn_flutter/src/core/citation/model/source.dart'; import 'package:halkyon_learn_flutter/src/core/citation/widget/source_list.dart'; import 'package:halkyon_learn_flutter/src/core/citation/widget/source_view.dart'; import 'package:halkyon_learn_flutter/src/core/content/published_ref.dart'; -import 'package:halkyon_learn_flutter/src/core/content/rich_content.dart'; -import 'package:halkyon_learn_flutter/src/core/editor/rich_content_editor.dart'; -import 'package:halkyon_learn_flutter/src/core/editor/rich_content_view.dart'; +import 'package:halkyon_learn_flutter/src/core/editor/learn_content.dart'; import 'package:halkyon_learn_flutter/src/core/editor/rich_text_screen.dart'; import 'package:halkyon_learn_flutter/src/core/editor/save_status.dart'; import 'package:halkyon_learn_flutter/src/core/grammar/grammar_host.dart'; @@ -145,9 +144,9 @@ class const _Form({required final StepEditorState state}) height: 260, child: readOnly ? SingleChildScrollView( - child: RichContentView(content: content.body), + child: LearnContentView(content: content.body), ) - : RichContentEditor( + : LearnContentEditor( key: ValueKey('body-${state.revision}'), initialContent: content.body, onChanged: (body) => change(StepField.body, body), @@ -614,7 +613,7 @@ class const _Conflicts({required final StepEditorState state}) class const _Version({required final Object? version}) extends StatelessWidget { @override Widget build(BuildContext context) => switch (version) { - final RichContent content => RichContentView(content: content), + final RichContent content => LearnContentView(content: content), SingleChoice(:final options, :final correct) => Text( [ for (final (i, option) in options.indexed) diff --git a/apps/learn/halkyon_learn_flutter/lib/src/feature/note/bloc/note_editor_bloc.dart b/apps/learn/halkyon_learn_flutter/lib/src/feature/note/bloc/note_editor_bloc.dart index 79e84f23..b7b5d331 100644 --- a/apps/learn/halkyon_learn_flutter/lib/src/feature/note/bloc/note_editor_bloc.dart +++ b/apps/learn/halkyon_learn_flutter/lib/src/feature/note/bloc/note_editor_bloc.dart @@ -4,7 +4,7 @@ import 'dart:developer'; import 'package:equatable/equatable.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:halkyon_core/halkyon_core.dart' show SerialQueue; -import 'package:halkyon_learn_flutter/src/core/content/rich_content.dart'; +import 'package:halkyon_editor/halkyon_editor.dart'; import 'package:halkyon_learn_flutter/src/core/editor/save_status.dart'; import 'package:halkyon_learn_flutter/src/feature/note/data/note_repository.dart'; import 'package:halkyon_learn_flutter/src/feature/note/model/note.dart'; diff --git a/apps/learn/halkyon_learn_flutter/lib/src/feature/note/data/note_payload.dart b/apps/learn/halkyon_learn_flutter/lib/src/feature/note/data/note_payload.dart index c68ab71d..b9dc4fe7 100644 --- a/apps/learn/halkyon_learn_flutter/lib/src/feature/note/data/note_payload.dart +++ b/apps/learn/halkyon_learn_flutter/lib/src/feature/note/data/note_payload.dart @@ -1,9 +1,9 @@ import 'dart:typed_data'; +import 'package:halkyon_editor/halkyon_editor.dart'; import 'package:halkyon_learn_flutter/src/core/content/payload.dart'; import 'package:halkyon_learn_flutter/src/core/content/pdf_page_ref.dart'; import 'package:halkyon_learn_flutter/src/core/content/published_ref.dart'; -import 'package:halkyon_learn_flutter/src/core/content/rich_content.dart'; import 'package:halkyon_learn_flutter/src/feature/note/model/note_content.dart'; // A note revision's content as its synced row stores it (see diff --git a/apps/learn/halkyon_learn_flutter/lib/src/feature/note/model/note_content.dart b/apps/learn/halkyon_learn_flutter/lib/src/feature/note/model/note_content.dart index 493c810c..f8b9f115 100644 --- a/apps/learn/halkyon_learn_flutter/lib/src/feature/note/model/note_content.dart +++ b/apps/learn/halkyon_learn_flutter/lib/src/feature/note/model/note_content.dart @@ -1,8 +1,8 @@ import 'package:equatable/equatable.dart'; +import 'package:halkyon_editor/halkyon_editor.dart'; import 'package:halkyon_learn_flutter/src/core/content/pdf_page_ref.dart'; import 'package:halkyon_learn_flutter/src/core/content/published_ref.dart'; import 'package:halkyon_learn_flutter/src/core/content/revision_merge.dart'; -import 'package:halkyon_learn_flutter/src/core/content/rich_content.dart'; /// The parts of a note that merge separately when devices edited it while /// offline (ADR 0003): a title changed on one device and the body on another diff --git a/apps/learn/halkyon_learn_flutter/lib/src/feature/note/widget/note_editor_screen.dart b/apps/learn/halkyon_learn_flutter/lib/src/feature/note/widget/note_editor_screen.dart index 832ba37d..22dfb00e 100644 --- a/apps/learn/halkyon_learn_flutter/lib/src/feature/note/widget/note_editor_screen.dart +++ b/apps/learn/halkyon_learn_flutter/lib/src/feature/note/widget/note_editor_screen.dart @@ -1,10 +1,9 @@ import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:go_router/go_router.dart'; +import 'package:halkyon_editor/halkyon_editor.dart'; import 'package:halkyon_learn_flutter/l10n/app_localizations.dart'; import 'package:halkyon_learn_flutter/src/core/content/published_ref.dart'; -import 'package:halkyon_learn_flutter/src/core/content/rich_content.dart'; -import 'package:halkyon_learn_flutter/src/core/editor/rich_content_editor.dart'; -import 'package:halkyon_learn_flutter/src/core/editor/rich_content_view.dart'; +import 'package:halkyon_learn_flutter/src/core/editor/learn_content.dart'; import 'package:halkyon_learn_flutter/src/core/editor/save_status.dart'; import 'package:halkyon_learn_flutter/src/core/grammar/grammar_host.dart'; import 'package:halkyon_learn_flutter/src/core/widget/conflict_versions.dart'; @@ -183,11 +182,11 @@ class const _Note({ padding: const EdgeInsets.fromLTRB(16, 8, 16, 0), child: readOnly ? SingleChildScrollView( - child: RichContentView( + child: LearnContentView( content: state.content.body, ), ) - : RichContentEditor( + : LearnContentEditor( // Shown anew whenever the text is replaced // from outside: the editor reads its // content once. @@ -285,7 +284,7 @@ class const _Conflicts({required final NoteEditorState state}) NoteField.published => l10n.publishedField, }, show: (version) => switch (version) { - final RichContent body => RichContentView(content: body), + final RichContent body => LearnContentView(content: body), (pdfId: _, page: final int page) => Text( l10n.pdfPageShort(page), ), diff --git a/apps/learn/halkyon_learn_flutter/lib/src/feature/pdf_source/widget/pdf_capture.dart b/apps/learn/halkyon_learn_flutter/lib/src/feature/pdf_source/widget/pdf_capture.dart index a314cbd2..7814994f 100644 --- a/apps/learn/halkyon_learn_flutter/lib/src/feature/pdf_source/widget/pdf_capture.dart +++ b/apps/learn/halkyon_learn_flutter/lib/src/feature/pdf_source/widget/pdf_capture.dart @@ -3,12 +3,10 @@ import 'dart:ui' as ui; import 'package:appflowy_editor/appflowy_editor.dart'; import 'package:flutter/services.dart'; +import 'package:halkyon_editor/halkyon_editor.dart'; import 'package:halkyon_learn_flutter/l10n/app_localizations.dart'; import 'package:halkyon_learn_flutter/src/core/asset/data/asset_repository.dart'; import 'package:halkyon_learn_flutter/src/core/asset/widget/studyset_files.dart'; -import 'package:halkyon_learn_flutter/src/core/content/content_blocks.dart'; -import 'package:halkyon_learn_flutter/src/core/content/rich_content.dart'; -import 'package:halkyon_learn_flutter/src/core/editor/editor_blocks.dart'; import 'package:halkyon_learn_flutter/src/feature/pdf_source/data/pdf_engine.dart'; import 'package:halkyon_ui/halkyon_ui.dart'; import 'package:lucide_icons_flutter/lucide_icons.dart'; @@ -46,7 +44,7 @@ EditorBlocks screenshotBlocks(PdfCapture capture, String pdfId) => EditorBlocks( builders: const {}, insertable: [ InsertableBlock( - name: (l10n) => l10n.blockScreenshot, + name: (context) => AppLocalizations.of(context).blockScreenshot, icon: LucideIcons.crop, group: InsertGroup.media, keywords: const ['screenshot', 'capture', 'clip', 'pdf', 'image'], diff --git a/apps/learn/halkyon_learn_flutter/lib/src/feature/pdf_source/widget/pdf_reader_panels.dart b/apps/learn/halkyon_learn_flutter/lib/src/feature/pdf_source/widget/pdf_reader_panels.dart index b425ad72..19aa7ea5 100644 --- a/apps/learn/halkyon_learn_flutter/lib/src/feature/pdf_source/widget/pdf_reader_panels.dart +++ b/apps/learn/halkyon_learn_flutter/lib/src/feature/pdf_source/widget/pdf_reader_panels.dart @@ -2,11 +2,11 @@ import 'dart:async'; import 'dart:developer'; import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:halkyon_editor/halkyon_editor.dart'; import 'package:halkyon_learn_flutter/l10n/app_localizations.dart'; -import 'package:halkyon_learn_flutter/src/core/content/rich_content.dart'; import 'package:halkyon_learn_flutter/src/core/dependencies/person_dependencies.dart'; import 'package:halkyon_learn_flutter/src/core/editor/file_blocks.dart'; -import 'package:halkyon_learn_flutter/src/core/editor/rich_content_editor.dart'; +import 'package:halkyon_learn_flutter/src/core/editor/learn_content.dart'; import 'package:halkyon_learn_flutter/src/core/grammar/grammar_host.dart'; import 'package:halkyon_learn_flutter/src/feature/assistant/widget/card_proposals_sheet.dart'; import 'package:halkyon_learn_flutter/src/feature/card/bloc/card_composer_bloc.dart'; @@ -844,7 +844,7 @@ class _NotePanelState() ? const SizedBox.shrink() : Padding( padding: const EdgeInsets.fromLTRB(16, 0, 16, 8), - child: RichContentEditor( + child: LearnContentEditor( key: ValueKey((_page, _version)), initialContent: _body ?? RichContent.empty(), onChanged: _changed, diff --git a/apps/learn/halkyon_learn_flutter/lib/src/feature/pdf_source/widget/pdf_reader_screen.dart b/apps/learn/halkyon_learn_flutter/lib/src/feature/pdf_source/widget/pdf_reader_screen.dart index 5c4dc6bf..93b81f11 100644 --- a/apps/learn/halkyon_learn_flutter/lib/src/feature/pdf_source/widget/pdf_reader_screen.dart +++ b/apps/learn/halkyon_learn_flutter/lib/src/feature/pdf_source/widget/pdf_reader_screen.dart @@ -5,13 +5,13 @@ import 'package:flutter/gestures.dart'; import 'package:flutter/services.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:go_router/go_router.dart'; +import 'package:halkyon_editor/halkyon_editor.dart'; import 'package:halkyon_learn_flutter/l10n/app_localizations.dart'; import 'package:halkyon_learn_flutter/src/core/asset/widget/studyset_files.dart'; import 'package:halkyon_learn_flutter/src/core/dependencies/dependencies.dart'; import 'package:halkyon_learn_flutter/src/core/dependencies/person_dependencies.dart'; import 'package:halkyon_learn_flutter/src/core/device_settings/bloc/device_settings_bloc.dart'; import 'package:halkyon_learn_flutter/src/core/editor/core_blocks.dart'; -import 'package:halkyon_learn_flutter/src/core/editor/editor_blocks.dart'; import 'package:halkyon_learn_flutter/src/core/router/route_paths.dart'; import 'package:halkyon_learn_flutter/src/feature/pdf_source/bloc/pdf_reader_bloc.dart'; import 'package:halkyon_learn_flutter/src/feature/pdf_source/data/pdf_engine.dart'; diff --git a/apps/learn/halkyon_learn_flutter/lib/src/feature/publish/bloc/publish_bloc.dart b/apps/learn/halkyon_learn_flutter/lib/src/feature/publish/bloc/publish_bloc.dart index 9a995394..afb6a361 100644 --- a/apps/learn/halkyon_learn_flutter/lib/src/feature/publish/bloc/publish_bloc.dart +++ b/apps/learn/halkyon_learn_flutter/lib/src/feature/publish/bloc/publish_bloc.dart @@ -3,7 +3,7 @@ import 'dart:developer'; import 'package:equatable/equatable.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:halkyon_app/halkyon_app.dart'; -import 'package:halkyon_learn_flutter/src/core/content/rich_content.dart'; +import 'package:halkyon_editor/halkyon_editor.dart'; import 'package:halkyon_learn_flutter/src/feature/card/data/card_repository.dart'; import 'package:halkyon_learn_flutter/src/feature/learning_path/data/learning_path_repository.dart'; import 'package:halkyon_learn_flutter/src/feature/note/data/note_repository.dart'; diff --git a/apps/learn/halkyon_learn_flutter/lib/src/feature/publish/data/publish_gateway.dart b/apps/learn/halkyon_learn_flutter/lib/src/feature/publish/data/publish_gateway.dart index df887774..c6b983bf 100644 --- a/apps/learn/halkyon_learn_flutter/lib/src/feature/publish/data/publish_gateway.dart +++ b/apps/learn/halkyon_learn_flutter/lib/src/feature/publish/data/publish_gateway.dart @@ -1,4 +1,4 @@ -import 'package:halkyon_learn_flutter/src/core/content/rich_content.dart'; +import 'package:halkyon_editor/halkyon_editor.dart'; import 'package:halkyon_learn_flutter/src/feature/publish/model/publication.dart'; /// The boundary to publishing on the instance. Tests replace it with a diff --git a/apps/learn/halkyon_learn_flutter/lib/src/feature/publish/data/publish_repository.dart b/apps/learn/halkyon_learn_flutter/lib/src/feature/publish/data/publish_repository.dart index 9c1a1f88..2e91930e 100644 --- a/apps/learn/halkyon_learn_flutter/lib/src/feature/publish/data/publish_repository.dart +++ b/apps/learn/halkyon_learn_flutter/lib/src/feature/publish/data/publish_repository.dart @@ -1,7 +1,7 @@ import 'dart:async'; import 'package:flutter/foundation.dart'; -import 'package:halkyon_learn_flutter/src/core/content/rich_content.dart'; +import 'package:halkyon_editor/halkyon_editor.dart'; import 'package:halkyon_learn_flutter/src/feature/publish/data/publish_gateway.dart'; import 'package:halkyon_learn_flutter/src/feature/publish/data/published_store.dart'; import 'package:halkyon_learn_flutter/src/feature/publish/model/publication.dart'; diff --git a/apps/learn/halkyon_learn_flutter/lib/src/feature/publish/data/serverpod_publish_gateway.dart b/apps/learn/halkyon_learn_flutter/lib/src/feature/publish/data/serverpod_publish_gateway.dart index d88eb143..c7e869d5 100644 --- a/apps/learn/halkyon_learn_flutter/lib/src/feature/publish/data/serverpod_publish_gateway.dart +++ b/apps/learn/halkyon_learn_flutter/lib/src/feature/publish/data/serverpod_publish_gateway.dart @@ -1,7 +1,7 @@ import 'dart:convert'; +import 'package:halkyon_editor/halkyon_editor.dart'; import 'package:halkyon_learn_client/halkyon_learn_client.dart' as protocol; -import 'package:halkyon_learn_flutter/src/core/content/rich_content.dart'; import 'package:halkyon_learn_flutter/src/feature/publish/data/publish_gateway.dart'; import 'package:halkyon_learn_flutter/src/feature/publish/model/publication.dart'; diff --git a/apps/learn/halkyon_learn_flutter/lib/src/feature/publish/model/publication.dart b/apps/learn/halkyon_learn_flutter/lib/src/feature/publish/model/publication.dart index 0a83a9fd..56390934 100644 --- a/apps/learn/halkyon_learn_flutter/lib/src/feature/publish/model/publication.dart +++ b/apps/learn/halkyon_learn_flutter/lib/src/feature/publish/model/publication.dart @@ -1,5 +1,5 @@ import 'package:equatable/equatable.dart'; -import 'package:halkyon_learn_flutter/src/core/content/rich_content.dart'; +import 'package:halkyon_editor/halkyon_editor.dart'; /// A studyset its person published (ADR 0005, rule 3): where others find it /// ([location], `halkyon.eu/@raisondetredev/japanese`), under the name diff --git a/apps/learn/halkyon_learn_flutter/lib/src/feature/publish/widget/publish_sheet.dart b/apps/learn/halkyon_learn_flutter/lib/src/feature/publish/widget/publish_sheet.dart index 59960759..929a1739 100644 --- a/apps/learn/halkyon_learn_flutter/lib/src/feature/publish/widget/publish_sheet.dart +++ b/apps/learn/halkyon_learn_flutter/lib/src/feature/publish/widget/publish_sheet.dart @@ -4,8 +4,8 @@ import 'dart:math' as math; import 'package:flutter/services.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:go_router/go_router.dart'; +import 'package:halkyon_editor/halkyon_editor.dart'; import 'package:halkyon_learn_flutter/l10n/app_localizations.dart'; -import 'package:halkyon_learn_flutter/src/core/content/rich_content.dart'; import 'package:halkyon_learn_flutter/src/core/dependencies/person_dependencies.dart'; import 'package:halkyon_learn_flutter/src/core/editor/rich_text_screen.dart'; import 'package:halkyon_learn_flutter/src/core/router/route_paths.dart'; diff --git a/apps/learn/halkyon_learn_flutter/lib/src/feature/study/model/vocabulary_quiz.dart b/apps/learn/halkyon_learn_flutter/lib/src/feature/study/model/vocabulary_quiz.dart index d0726e48..0ce642ae 100644 --- a/apps/learn/halkyon_learn_flutter/lib/src/feature/study/model/vocabulary_quiz.dart +++ b/apps/learn/halkyon_learn_flutter/lib/src/feature/study/model/vocabulary_quiz.dart @@ -1,7 +1,7 @@ import 'dart:math' as math; import 'package:equatable/equatable.dart'; -import 'package:halkyon_learn_flutter/src/core/content/rich_content.dart'; +import 'package:halkyon_editor/halkyon_editor.dart'; import 'package:halkyon_learn_flutter/src/feature/card/model/card_content.dart'; import 'package:halkyon_learn_flutter/src/feature/study/model/study_options.dart'; diff --git a/apps/learn/halkyon_learn_flutter/lib/src/feature/study/widget/study_button_block.dart b/apps/learn/halkyon_learn_flutter/lib/src/feature/study/widget/study_button_block.dart index 9074cb73..ca14ed0b 100644 --- a/apps/learn/halkyon_learn_flutter/lib/src/feature/study/widget/study_button_block.dart +++ b/apps/learn/halkyon_learn_flutter/lib/src/feature/study/widget/study_button_block.dart @@ -2,11 +2,9 @@ import 'dart:async'; import 'package:appflowy_editor/appflowy_editor.dart'; import 'package:go_router/go_router.dart'; +import 'package:halkyon_editor/halkyon_editor.dart'; import 'package:halkyon_learn_flutter/l10n/app_localizations.dart'; -import 'package:halkyon_learn_flutter/src/core/content/content_blocks.dart'; import 'package:halkyon_learn_flutter/src/core/dependencies/person_dependencies.dart'; -import 'package:halkyon_learn_flutter/src/core/editor/atomic_block.dart'; -import 'package:halkyon_learn_flutter/src/core/editor/editor_blocks.dart'; import 'package:halkyon_learn_flutter/src/core/router/route_paths.dart'; import 'package:halkyon_learn_flutter/src/feature/study/model/study_focus.dart'; import 'package:halkyon_learn_flutter/src/feature/studyset/model/studyset.dart'; @@ -27,7 +25,7 @@ final studyEditorBlocks = EditorBlocks( /// say, to practise what it just taught. What it asks of the session leaves /// the person's settings as they are (`StudyFocus`). final _studyButtonBlock = InsertableBlock( - name: (l10n) => l10n.blockStudyButton, + name: (context) => AppLocalizations.of(context).blockStudyButton, icon: LucideIcons.graduationCap, group: InsertGroup.blocks, keywords: const ['study', 'button', 'session', 'review', 'practice'], diff --git a/apps/learn/halkyon_learn_flutter/lib/src/feature/study/widget/study_card_body.dart b/apps/learn/halkyon_learn_flutter/lib/src/feature/study/widget/study_card_body.dart index 3f15553e..8af2c88b 100644 --- a/apps/learn/halkyon_learn_flutter/lib/src/feature/study/widget/study_card_body.dart +++ b/apps/learn/halkyon_learn_flutter/lib/src/feature/study/widget/study_card_body.dart @@ -2,7 +2,7 @@ import 'dart:math' as math; import 'package:halkyon_learn_flutter/l10n/app_localizations.dart'; import 'package:halkyon_learn_flutter/src/core/chinese/pinyin_input.dart'; -import 'package:halkyon_learn_flutter/src/core/editor/rich_content_view.dart'; +import 'package:halkyon_learn_flutter/src/core/editor/learn_content.dart'; import 'package:halkyon_learn_flutter/src/core/japanese/kana_input.dart'; import 'package:halkyon_learn_flutter/src/core/l10n/language_tags.dart'; import 'package:halkyon_learn_flutter/src/core/widget/content_languages.dart'; @@ -261,7 +261,7 @@ class const FrontBackBody({ header: header, onTap: onToggle, children: [ - RichContentView( + LearnContentView( content: prompt, textStyle: theme.textTheme.headlineMedium, textAlign: TextAlign.center, @@ -272,7 +272,7 @@ class const FrontBackBody({ card: card, shown: answerShown, onShow: onShow, - answer: RichContentView( + answer: LearnContentView( content: answer, textStyle: theme.textTheme.headlineSmall, textAlign: TextAlign.center, @@ -749,7 +749,7 @@ class const _Expression({ ), const SizedBox(height: 2), ], - RichContentView( + LearnContentView( content: card.content.front, textStyle: style, textAlign: TextAlign.center, @@ -894,7 +894,7 @@ class _MultipleChoiceBodyState() extends State { child: StudyCardSurface( header: widget.header, children: [ - RichContentView( + LearnContentView( content: _content.front, textStyle: theme.textTheme.headlineMedium, textAlign: TextAlign.center, @@ -1038,7 +1038,7 @@ class const _Option({ // showing it would take them for selecting text. Expanded( child: IgnorePointer( - child: RichContentView( + child: LearnContentView( content: answer.content, textStyle: theme.textTheme.bodyLarge, ), @@ -1121,7 +1121,7 @@ class _MatchingBodyState() extends State { crossAxisAlignment: CrossAxisAlignment.stretch, children: [ if (!widget.card.content.front.isBlank) ...[ - RichContentView( + LearnContentView( content: widget.card.content.front, textStyle: theme.textTheme.headlineSmall, ), @@ -1363,7 +1363,7 @@ class _MapBodyState() extends State { children: [ widget.header, const SizedBox(height: 16), - RichContentView( + LearnContentView( content: content.front, textStyle: theme.textTheme.headlineSmall, textAlign: TextAlign.center, diff --git a/apps/learn/halkyon_learn_flutter/lib/src/feature/studyset/data/studyset_payload.dart b/apps/learn/halkyon_learn_flutter/lib/src/feature/studyset/data/studyset_payload.dart index 32cc0879..116d18b2 100644 --- a/apps/learn/halkyon_learn_flutter/lib/src/feature/studyset/data/studyset_payload.dart +++ b/apps/learn/halkyon_learn_flutter/lib/src/feature/studyset/data/studyset_payload.dart @@ -1,9 +1,9 @@ import 'dart:typed_data'; +import 'package:halkyon_editor/halkyon_editor.dart'; import 'package:halkyon_learn_flutter/src/core/citation/model/source.dart'; import 'package:halkyon_learn_flutter/src/core/content/payload.dart'; import 'package:halkyon_learn_flutter/src/core/content/published_ref.dart'; -import 'package:halkyon_learn_flutter/src/core/content/rich_content.dart'; import 'package:halkyon_learn_flutter/src/feature/study/model/studyset_study_options.dart'; import 'package:halkyon_learn_flutter/src/feature/studyset/model/exam_plan.dart'; import 'package:halkyon_learn_flutter/src/feature/studyset/model/studyset_content.dart'; diff --git a/apps/learn/halkyon_learn_flutter/lib/src/feature/studyset/model/studyset_content.dart b/apps/learn/halkyon_learn_flutter/lib/src/feature/studyset/model/studyset_content.dart index 983a4713..4554290d 100644 --- a/apps/learn/halkyon_learn_flutter/lib/src/feature/studyset/model/studyset_content.dart +++ b/apps/learn/halkyon_learn_flutter/lib/src/feature/studyset/model/studyset_content.dart @@ -1,7 +1,7 @@ import 'package:equatable/equatable.dart'; +import 'package:halkyon_editor/halkyon_editor.dart'; import 'package:halkyon_learn_flutter/src/core/citation/model/source.dart'; import 'package:halkyon_learn_flutter/src/core/content/published_ref.dart'; -import 'package:halkyon_learn_flutter/src/core/content/rich_content.dart'; import 'package:halkyon_learn_flutter/src/feature/study/model/studyset_study_options.dart'; import 'package:halkyon_learn_flutter/src/feature/studyset/model/exam_plan.dart'; diff --git a/apps/learn/halkyon_learn_flutter/lib/src/feature/studyset/widget/studyset_screen.dart b/apps/learn/halkyon_learn_flutter/lib/src/feature/studyset/widget/studyset_screen.dart index 3277f985..9b737965 100644 --- a/apps/learn/halkyon_learn_flutter/lib/src/feature/studyset/widget/studyset_screen.dart +++ b/apps/learn/halkyon_learn_flutter/lib/src/feature/studyset/widget/studyset_screen.dart @@ -8,7 +8,7 @@ import 'package:go_router/go_router.dart'; import 'package:halkyon_learn_flutter/l10n/app_localizations.dart'; import 'package:halkyon_learn_flutter/src/core/dependencies/dependencies.dart'; import 'package:halkyon_learn_flutter/src/core/dependencies/person_dependencies.dart'; -import 'package:halkyon_learn_flutter/src/core/editor/rich_content_view.dart'; +import 'package:halkyon_learn_flutter/src/core/editor/learn_content.dart'; import 'package:halkyon_learn_flutter/src/core/router/route_paths.dart'; import 'package:halkyon_learn_flutter/src/feature/anki/data/anki_importer.dart'; import 'package:halkyon_learn_flutter/src/feature/anki/widget/anki_import_sheet.dart'; @@ -978,7 +978,7 @@ class _DescriptionState() extends State<_Description> { heightFactor: 0.7, body: Padding( padding: const EdgeInsets.only(top: 12, bottom: 24), - child: RichContentView( + child: LearnContentView( content: content.description, textStyle: Theme.of(context).textTheme.bodyMedium, ), @@ -992,7 +992,7 @@ class _DescriptionState() extends State<_Description> { Widget build(BuildContext context) { final view = ConstrainedBox( constraints: const BoxConstraints(maxWidth: readableContentMaxWidth), - child: RichContentView( + child: LearnContentView( content: widget.studyset.content.description, textStyle: Theme.of(context).textTheme.bodyMedium, ), diff --git a/apps/learn/halkyon_learn_flutter/lib/src/feature/studyset/widget/studyset_sheet.dart b/apps/learn/halkyon_learn_flutter/lib/src/feature/studyset/widget/studyset_sheet.dart index d1e5b847..c9f6bf72 100644 --- a/apps/learn/halkyon_learn_flutter/lib/src/feature/studyset/widget/studyset_sheet.dart +++ b/apps/learn/halkyon_learn_flutter/lib/src/feature/studyset/widget/studyset_sheet.dart @@ -1,7 +1,7 @@ import 'package:clock/clock.dart'; import 'package:flutter/services.dart'; +import 'package:halkyon_editor/halkyon_editor.dart'; import 'package:halkyon_learn_flutter/l10n/app_localizations.dart'; -import 'package:halkyon_learn_flutter/src/core/content/rich_content.dart'; import 'package:halkyon_learn_flutter/src/core/editor/rich_text_screen.dart'; import 'package:halkyon_learn_flutter/src/core/l10n/content_languages.dart'; import 'package:halkyon_learn_flutter/src/feature/studyset/model/license.dart'; diff --git a/apps/learn/halkyon_learn_flutter/pubspec.yaml b/apps/learn/halkyon_learn_flutter/pubspec.yaml index 16999dc1..654e73c7 100644 --- a/apps/learn/halkyon_learn_flutter/pubspec.yaml +++ b/apps/learn/halkyon_learn_flutter/pubspec.yaml @@ -79,6 +79,10 @@ dependencies: # run their work in (SerialQueue, CoalescingRunner). halkyon_core: path: ../../../packages/halkyon_core + # Rich text's general parts (a shared package, ADR 0033, D37); Learn's own + # blocks build on it in lib/src/core/editor/. + halkyon_editor: + path: ../../../packages/halkyon_editor halkyon_learn_client: path: ../halkyon_learn_client # Language models' APIs (a shared package, ADR 0021, D15): the protocols a diff --git a/apps/learn/halkyon_learn_flutter/test/app/long_names_test.dart b/apps/learn/halkyon_learn_flutter/test/app/long_names_test.dart index 6f57abe5..280abed4 100644 --- a/apps/learn/halkyon_learn_flutter/test/app/long_names_test.dart +++ b/apps/learn/halkyon_learn_flutter/test/app/long_names_test.dart @@ -1,7 +1,7 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:halkyon_app/halkyon_app.dart'; import 'package:halkyon_app/testing.dart'; -import 'package:halkyon_learn_flutter/src/core/content/rich_content.dart'; +import 'package:halkyon_editor/halkyon_editor.dart'; import 'package:halkyon_learn_flutter/src/core/profile/public_person.dart'; import 'package:halkyon_learn_flutter/src/core/router/route_paths.dart'; import 'package:halkyon_learn_flutter/src/feature/card/data/card_repository.dart'; diff --git a/apps/learn/halkyon_learn_flutter/test/core/content/published_ref_test.dart b/apps/learn/halkyon_learn_flutter/test/core/content/published_ref_test.dart index f9c693a7..15a928a4 100644 --- a/apps/learn/halkyon_learn_flutter/test/core/content/published_ref_test.dart +++ b/apps/learn/halkyon_learn_flutter/test/core/content/published_ref_test.dart @@ -1,6 +1,6 @@ import 'package:flutter_test/flutter_test.dart'; +import 'package:halkyon_editor/halkyon_editor.dart'; import 'package:halkyon_learn_flutter/src/core/content/published_ref.dart'; -import 'package:halkyon_learn_flutter/src/core/content/rich_content.dart'; import 'package:halkyon_learn_flutter/src/feature/card/data/card_payload.dart'; import 'package:halkyon_learn_flutter/src/feature/card/model/card_content.dart'; import 'package:halkyon_learn_flutter/src/feature/learning_path/data/learning_path_payload.dart'; diff --git a/apps/learn/halkyon_learn_flutter/test/core/editor/editor_blocks_test.dart b/apps/learn/halkyon_learn_flutter/test/core/editor/editor_blocks_test.dart index 3ad12f76..f9f32e86 100644 --- a/apps/learn/halkyon_learn_flutter/test/core/editor/editor_blocks_test.dart +++ b/apps/learn/halkyon_learn_flutter/test/core/editor/editor_blocks_test.dart @@ -4,10 +4,8 @@ import 'package:appflowy_editor/appflowy_editor.dart'; import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:halkyon_app/testing.dart'; -import 'package:halkyon_learn_flutter/src/core/content/rich_content.dart'; -import 'package:halkyon_learn_flutter/src/core/editor/rich_content_document.dart'; -import 'package:halkyon_learn_flutter/src/core/editor/rich_content_editor.dart'; -import 'package:halkyon_learn_flutter/src/core/editor/rich_content_view.dart'; +import 'package:halkyon_editor/halkyon_editor.dart'; +import 'package:halkyon_learn_flutter/src/core/editor/learn_content.dart'; import 'package:halkyon_learn_flutter/src/core/l10n/app_languages.dart'; import 'package:halkyon_learn_flutter/src/core/router/route_paths.dart'; import 'package:halkyon_learn_flutter/src/feature/studyset/data/studyset_payload.dart'; @@ -248,7 +246,7 @@ void main() { MaterialApp( localizationsDelegates: appLocalizationsDelegates, home: Scaffold( - body: RichContentView( + body: LearnContentView( content: Document( root: pageNode(children: [table]), ).toRichContent(), @@ -292,7 +290,7 @@ void main() { ); testWidgets('says it needs a newer app where it is shown', (tester) async { - await pump(tester, RichContentView(content: hologram)); + await pump(tester, LearnContentView(content: hologram)); await tester.pump(const Duration(seconds: 1)); expect( @@ -307,7 +305,7 @@ void main() { tester, SizedBox( height: 400, - child: RichContentEditor( + child: LearnContentEditor( initialContent: hologram, onChanged: (content) => latest = content, ), diff --git a/apps/learn/halkyon_learn_flutter/test/core/editor/file_blocks_test.dart b/apps/learn/halkyon_learn_flutter/test/core/editor/file_blocks_test.dart index 7fa2f3f6..7ae12f4b 100644 --- a/apps/learn/halkyon_learn_flutter/test/core/editor/file_blocks_test.dart +++ b/apps/learn/halkyon_learn_flutter/test/core/editor/file_blocks_test.dart @@ -3,12 +3,11 @@ import 'dart:typed_data'; import 'package:appflowy_editor/appflowy_editor.dart'; import 'package:flutter_test/flutter_test.dart'; +import 'package:halkyon_editor/halkyon_editor.dart'; import 'package:halkyon_learn_flutter/src/core/asset/data/asset_payload.dart'; import 'package:halkyon_learn_flutter/src/core/asset/data/asset_repository.dart'; import 'package:halkyon_learn_flutter/src/core/asset/widget/stored_file_image.dart'; -import 'package:halkyon_learn_flutter/src/core/content/rich_content.dart'; import 'package:halkyon_learn_flutter/src/core/editor/file_blocks.dart'; -import 'package:halkyon_learn_flutter/src/core/editor/rich_content_document.dart'; import 'package:halkyon_learn_flutter/src/core/router/route_paths.dart'; import 'package:halkyon_learn_flutter/src/feature/card/data/card_repository.dart'; import 'package:halkyon_learn_flutter/src/feature/card/model/card_content.dart'; diff --git a/apps/learn/halkyon_learn_flutter/test/core/editor/format_bar_test.dart b/apps/learn/halkyon_learn_flutter/test/core/editor/format_bar_test.dart index dde6e038..e404b7ca 100644 --- a/apps/learn/halkyon_learn_flutter/test/core/editor/format_bar_test.dart +++ b/apps/learn/halkyon_learn_flutter/test/core/editor/format_bar_test.dart @@ -1,11 +1,7 @@ import 'package:appflowy_editor/appflowy_editor.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'package:halkyon_learn_flutter/src/core/content/content_blocks.dart'; -import 'package:halkyon_learn_flutter/src/core/content/rich_content.dart'; -import 'package:halkyon_learn_flutter/src/core/editor/format_bar.dart'; -import 'package:halkyon_learn_flutter/src/core/editor/rich_content_editor.dart'; -import 'package:halkyon_learn_flutter/src/core/editor/rich_content_view.dart'; -import 'package:halkyon_learn_flutter/src/core/editor/scripts.dart'; +import 'package:halkyon_editor/halkyon_editor.dart'; +import 'package:halkyon_learn_flutter/src/core/editor/learn_content.dart'; import 'package:halkyon_learn_flutter/src/core/l10n/app_languages.dart'; import 'package:halkyon_learn_flutter/src/core/widget/content_languages.dart'; import 'package:material_ui/material_ui.dart'; @@ -31,7 +27,7 @@ void main() { children: [ SizedBox( height: 400, - child: RichContentEditor( + child: LearnContentEditor( initialContent: latest, onChanged: (content) => latest = content, formatBar: true, @@ -81,56 +77,6 @@ void main() { ]; } - testWidgets('shows while the editor has the focus, and only then', ( - tester, - ) async { - await edit(tester, 'Water is H2O'); - expect(find.byType(FormatBar), findsNothing); - - await tester.tap(find.byType(AppFlowyEditor)); - await tester.pump(const Duration(seconds: 1)); - expect(find.byType(FormatBar), findsOneWidget); - - FocusManager.instance.primaryFocus?.unfocus(); - await tester.pump(const Duration(seconds: 1)); - expect(find.byType(FormatBar), findsNothing); - }); - - testWidgets('bold marks the selection, shows it, and keeps the editor ' - 'focused', (tester) async { - final editorState = await edit(tester, 'Water is H2O'); - await select(tester, editorState, 0, 5); - - await press(tester, 'Bold'); - - expect(runs().first, {AppFlowyRichTextKeys.bold: true}); - expect(find.byType(FormatBar), findsOneWidget); - expect( - tester - .widget( - find.ancestor( - of: find.byTooltip('Bold'), - matching: find.byType(IconButton), - ), - ) - .isSelected, - isTrue, - ); - }); - - testWidgets('a subscript replaces a superscript on the same text', ( - tester, - ) async { - final editorState = await edit(tester, 'H2O'); - await select(tester, editorState, 1, 2); - - await press(tester, 'Superscript'); - expect(runs()[1], {superscriptKey: true}); - - await press(tester, 'Subscript'); - expect(runs()[1], {subscriptKey: true}); - }); - testWidgets('text above is furigana in Japanese: it gives the selected ' 'text a reading, asked for, and takes it away again', (tester) async { final editorState = await edit(tester, '漢字を読む', language: 'ja'); @@ -210,68 +156,4 @@ void main() { await tester.pumpAndSettle(); expect(find.byType(AlertDialog), findsNothing); }); - - testWidgets('centres the selected line', (tester) async { - final editorState = await edit(tester, 'Water is H2O'); - await select(tester, editorState, 0, 5); - - await press(tester, 'Center'); - - final document = latest.editorJson['document']! as Map; - final first = (document['children']! as List).first as Map; - expect((first['data']! as Map)['align'], 'center'); - }); - - testWidgets('read-only text shows a subscript raised by the font, ' - 'smaller', (tester) async { - await tester.pumpWidget( - MaterialApp( - localizationsDelegates: appLocalizationsDelegates, - home: Scaffold( - body: ListView( - children: [ - RichContentView( - content: RichContent({ - 'document': { - 'type': 'page', - 'children': [ - { - 'type': 'paragraph', - 'data': { - 'delta': [ - {'insert': 'H'}, - { - 'insert': '2', - 'attributes': {subscriptKey: true}, - }, - {'insert': 'O'}, - ], - }, - }, - ], - }, - }), - ), - ], - ), - ), - ), - ); - await tester.pump(const Duration(seconds: 1)); - - final spans = []; - for (final text in tester.widgetList(find.byType(RichText))) { - text.text.visitChildren((span) { - if (span is TextSpan && span.text == '2') { - spans.add(span); - } - return true; - }); - } - expect(spans, isNotEmpty); - expect( - spans.first.style?.fontFeatures, - contains(const FontFeature.subscripts()), - ); - }); } diff --git a/apps/learn/halkyon_learn_flutter/test/core/editor/math_test.dart b/apps/learn/halkyon_learn_flutter/test/core/editor/math_test.dart index aaa8c09f..3606d69c 100644 --- a/apps/learn/halkyon_learn_flutter/test/core/editor/math_test.dart +++ b/apps/learn/halkyon_learn_flutter/test/core/editor/math_test.dart @@ -1,12 +1,9 @@ import 'package:appflowy_editor/appflowy_editor.dart'; import 'package:flutter_math_fork/flutter_math.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'package:halkyon_learn_flutter/src/core/content/content_blocks.dart'; -import 'package:halkyon_learn_flutter/src/core/content/rich_content.dart'; +import 'package:halkyon_editor/halkyon_editor.dart'; +import 'package:halkyon_learn_flutter/src/core/editor/learn_content.dart'; import 'package:halkyon_learn_flutter/src/core/editor/math_view.dart'; -import 'package:halkyon_learn_flutter/src/core/editor/rich_content_document.dart'; -import 'package:halkyon_learn_flutter/src/core/editor/rich_content_editor.dart'; -import 'package:halkyon_learn_flutter/src/core/editor/rich_content_view.dart'; import 'package:halkyon_learn_flutter/src/core/l10n/app_languages.dart'; import 'package:halkyon_learn_flutter/src/core/theme/learn_theme.dart'; import 'package:material_ui/material_ui.dart'; @@ -52,7 +49,7 @@ void main() { home: Scaffold( body: SizedBox( height: 600, - child: RichContentEditor( + child: LearnContentEditor( initialContent: content, onChanged: (content) => latest = content, formatBar: true, @@ -210,7 +207,7 @@ void main() { MaterialApp( theme: learnTheme(Brightness.light), localizationsDelegates: appLocalizationsDelegates, - home: Scaffold(body: RichContentView(content: content)), + home: Scaffold(body: LearnContentView(content: content)), ), ); await tester.pump(const Duration(seconds: 1)); diff --git a/apps/learn/halkyon_learn_flutter/test/core/editor/rich_content_markdown_test.dart b/apps/learn/halkyon_learn_flutter/test/core/editor/rich_content_markdown_test.dart index 6f6c5f78..36af2d71 100644 --- a/apps/learn/halkyon_learn_flutter/test/core/editor/rich_content_markdown_test.dart +++ b/apps/learn/halkyon_learn_flutter/test/core/editor/rich_content_markdown_test.dart @@ -1,5 +1,5 @@ import 'package:flutter_test/flutter_test.dart'; -import 'package:halkyon_learn_flutter/src/core/content/rich_content.dart'; +import 'package:halkyon_editor/halkyon_editor.dart'; import 'package:halkyon_learn_flutter/src/core/editor/rich_content_markdown.dart'; void main() { diff --git a/apps/learn/halkyon_learn_flutter/test/core/editor/rich_content_view_test.dart b/apps/learn/halkyon_learn_flutter/test/core/editor/rich_content_view_test.dart index 9d2c113a..b4760751 100644 --- a/apps/learn/halkyon_learn_flutter/test/core/editor/rich_content_view_test.dart +++ b/apps/learn/halkyon_learn_flutter/test/core/editor/rich_content_view_test.dart @@ -1,36 +1,19 @@ import 'package:appflowy_editor/appflowy_editor.dart'; import 'package:flutter/gestures.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'package:halkyon_learn_flutter/src/core/content/content_blocks.dart'; -import 'package:halkyon_learn_flutter/src/core/content/rich_content.dart'; -import 'package:halkyon_learn_flutter/src/core/editor/rich_content_view.dart'; +import 'package:halkyon_editor/halkyon_editor.dart'; +import 'package:halkyon_learn_flutter/src/core/editor/learn_content.dart'; import 'package:halkyon_learn_flutter/src/core/editor/ruby.dart'; import 'package:halkyon_learn_flutter/src/core/l10n/app_languages.dart'; import 'package:material_ui/material_ui.dart'; void main() { - RichContent paragraph(String text) => RichContent({ - 'document': { - 'type': 'page', - 'children': [ - { - 'type': 'paragraph', - 'data': { - 'delta': [ - {'insert': text}, - ], - }, - }, - ], - }, - }); - Future show(WidgetTester tester, RichContent content) async { await tester.pumpWidget( MaterialApp( localizationsDelegates: appLocalizationsDelegates, home: Scaffold( - body: ListView(children: [RichContentView(content: content)]), + body: ListView(children: [LearnContentView(content: content)]), ), ), ); @@ -38,221 +21,6 @@ void main() { await tester.pump(const Duration(seconds: 1)); } - testWidgets('shows a table whose sizes are whole numbers, or missing, as ' - 'JSON written elsewhere may hold them', (tester) async { - Map cell(int column, int row, Map data) => - { - 'type': 'table/cell', - 'data': {'colPosition': column, 'rowPosition': row, ...data}, - 'children': [ - { - 'type': 'paragraph', - 'data': { - 'delta': [ - {'insert': '$column$row'}, - ], - }, - }, - ], - }; - for (final (table, cells) in [ - // As an archive from elsewhere brought it: whole numbers. - ( - { - 'colsLen': 2, - 'rowsLen': 1, - 'colsHeight': 82, - 'colDefaultWidth': 160, - 'rowDefaultHeight': 40, - 'colMinimumWidth': 40, - }, - {'width': 80, 'height': 40}, - ), - // Without the editor's sizes. - ({'colsLen': 2, 'rowsLen': 1}, {}), - ]) { - await show( - tester, - RichContent({ - 'document': { - 'type': 'page', - 'children': [ - { - 'type': 'table', - 'data': table, - 'children': [cell(0, 0, cells), cell(1, 0, cells)], - }, - ], - }, - }), - ); - - expect(tester.takeException(), isNull); - expect(find.textContaining('00', findRichText: true), findsOneWidget); - expect(find.textContaining('10', findRichText: true), findsOneWidget); - } - }); - - testWidgets('a row holding a cell of several lines grows to fit it, read ' - 'only as in the editor', (tester) async { - Map cell(int row, String text) => { - 'type': 'table/cell', - 'data': {'colPosition': 0, 'rowPosition': row}, - 'children': [ - { - 'type': 'paragraph', - 'data': { - 'delta': [ - {'insert': text}, - ], - }, - }, - ], - }; - await show( - tester, - RichContent({ - 'document': { - 'type': 'page', - 'children': [ - { - 'type': 'table', - 'data': {'colsLen': 1, 'rowsLen': 2}, - 'children': [cell(0, 'る\nう\nつ'), cell(1, 'す')], - }, - ], - }, - }), - ); - await tester.pump(); - - final cells = find.byType(TableCelBlockWidget); - expect( - tester.getSize(cells.at(0)).height, - greaterThan(2 * tester.getSize(cells.at(1)).height), - ); - }); - - testWidgets('shows the content', (tester) async { - await show(tester, paragraph('猫は動物です。')); - - expect(find.textContaining('猫は動物です。', findRichText: true), findsOneWidget); - }); - - testWidgets('shows new content when it changes', (tester) async { - await show(tester, paragraph('猫')); - await show(tester, paragraph('犬')); - - expect(find.textContaining('犬', findRichText: true), findsOneWidget); - expect(find.textContaining('猫', findRichText: true), findsNothing); - }); - - testWidgets('is display only: a tap on its text goes to what surrounds it, ' - 'and no keyboard opens', (tester) async { - var tapped = 0; - await tester.pumpWidget( - MaterialApp( - localizationsDelegates: appLocalizationsDelegates, - home: Scaffold( - body: ListView( - children: [ - GestureDetector( - onTap: () => tapped++, - child: RichContentView(content: paragraph('猫は動物です。')), - ), - ], - ), - ), - ), - ); - await tester.pump(const Duration(seconds: 1)); - - await tester.tap(find.textContaining('猫', findRichText: true)); - await tester.pump(const Duration(seconds: 1)); - - expect(tapped, 1); - expect(tester.testTextInput.hasAnyClients, isFalse); - }, variant: TargetPlatformVariant.all()); - - group('asked to centre', () { - Map block( - String type, - String text, [ - Map data = const {}, - ]) => { - 'type': type, - 'data': { - ...data, - 'delta': [ - {'insert': text}, - ], - }, - }; - - Future showCentred( - WidgetTester tester, - List> blocks, - ) async { - tester.view.physicalSize = const Size(400, 800); - tester.view.devicePixelRatio = 1; - addTearDown(tester.view.reset); - await tester.pumpWidget( - MaterialApp( - localizationsDelegates: appLocalizationsDelegates, - home: Scaffold( - body: ListView( - children: [ - RichContentView( - content: RichContent({ - 'document': {'type': 'page', 'children': blocks}, - }), - textAlign: TextAlign.center, - ), - ], - ), - ), - ), - ); - await tester.pump(const Duration(seconds: 1)); - } - - double centreOf(WidgetTester tester, String text) => - tester.getCenter(find.text(text, findRichText: true)).dx; - double leftOf(WidgetTester tester, String text) => - tester.getTopLeft(find.text(text, findRichText: true)).dx; - - testWidgets('centres paragraphs and headings', (tester) async { - await showCentred(tester, [ - block('heading', 'Greetings', {'level': 2}), - block('paragraph', 'hola'), - ]); - - expect(centreOf(tester, 'Greetings'), moreOrLessEquals(200, epsilon: 1)); - expect(centreOf(tester, 'hola'), moreOrLessEquals(200, epsilon: 1)); - }); - - testWidgets('leaves a list at the start: centred, its markers would ' - 'drift apart', (tester) async { - await showCentred(tester, [ - block('bulleted_list', 'hola'), - block('bulleted_list', 'buenos días'), - ]); - - expect(leftOf(tester, 'hola'), lessThan(60)); - expect(leftOf(tester, 'buenos días'), leftOf(tester, 'hola')); - }); - - testWidgets('keeps the alignment a paragraph was given in the editor', ( - tester, - ) async { - await showCentred(tester, [ - block('paragraph', 'hola', {'align': 'left'}), - ]); - - expect(leftOf(tester, 'hola'), lessThan(40)); - }); - }); - testWidgets('sets furigana above the text it reads, the text on the line', ( tester, ) async { diff --git a/apps/learn/halkyon_learn_flutter/test/core/editor/text_blocks_test.dart b/apps/learn/halkyon_learn_flutter/test/core/editor/text_blocks_test.dart index b5596214..f138ae93 100644 --- a/apps/learn/halkyon_learn_flutter/test/core/editor/text_blocks_test.dart +++ b/apps/learn/halkyon_learn_flutter/test/core/editor/text_blocks_test.dart @@ -1,10 +1,7 @@ import 'package:appflowy_editor/appflowy_editor.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'package:halkyon_learn_flutter/src/core/content/content_blocks.dart'; -import 'package:halkyon_learn_flutter/src/core/content/rich_content.dart'; -import 'package:halkyon_learn_flutter/src/core/editor/rich_content_document.dart'; -import 'package:halkyon_learn_flutter/src/core/editor/rich_content_editor.dart'; -import 'package:halkyon_learn_flutter/src/core/editor/rich_content_view.dart'; +import 'package:halkyon_editor/halkyon_editor.dart'; +import 'package:halkyon_learn_flutter/src/core/editor/learn_content.dart'; import 'package:halkyon_learn_flutter/src/core/l10n/app_languages.dart'; import 'package:halkyon_learn_flutter/src/core/theme/learn_theme.dart'; import 'package:material_ui/material_ui.dart'; @@ -34,7 +31,7 @@ void main() { tester, SizedBox( height: 600, - child: RichContentEditor( + child: LearnContentEditor( initialContent: content, onChanged: (content) => latest = content, formatBar: true, @@ -184,7 +181,7 @@ void main() { ], ), ).toRichContent(); - await pump(tester, RichContentView(content: content)); + await pump(tester, LearnContentView(content: content)); expect(find.text('Remember'), findsOneWidget); expect( @@ -213,7 +210,7 @@ void main() { ], ), ).toRichContent(); - await pump(tester, RichContentView(content: content)); + await pump(tester, LearnContentView(content: content)); expect(find.text('Note'), findsOneWidget); }); @@ -248,7 +245,7 @@ void main() { ], ), ).toRichContent(); - await pump(tester, RichContentView(content: content)); + await pump(tester, LearnContentView(content: content)); expect(find.text('ねこ', findRichText: true), findsNothing); diff --git a/apps/learn/halkyon_learn_flutter/test/core/editor/youtube_block_test.dart b/apps/learn/halkyon_learn_flutter/test/core/editor/youtube_block_test.dart index 5ebb09d1..34bbcb87 100644 --- a/apps/learn/halkyon_learn_flutter/test/core/editor/youtube_block_test.dart +++ b/apps/learn/halkyon_learn_flutter/test/core/editor/youtube_block_test.dart @@ -1,11 +1,8 @@ import 'package:appflowy_editor/appflowy_editor.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:halkyon_app/halkyon_app.dart'; -import 'package:halkyon_learn_flutter/src/core/content/content_blocks.dart'; -import 'package:halkyon_learn_flutter/src/core/content/rich_content.dart'; -import 'package:halkyon_learn_flutter/src/core/editor/rich_content_document.dart'; -import 'package:halkyon_learn_flutter/src/core/editor/rich_content_editor.dart'; -import 'package:halkyon_learn_flutter/src/core/editor/rich_content_view.dart'; +import 'package:halkyon_editor/halkyon_editor.dart'; +import 'package:halkyon_learn_flutter/src/core/editor/learn_content.dart'; import 'package:halkyon_learn_flutter/src/core/l10n/app_languages.dart'; import 'package:halkyon_learn_flutter/src/core/theme/learn_theme.dart'; import 'package:material_ui/material_ui.dart'; @@ -38,7 +35,7 @@ void main() { tester, SizedBox( height: 600, - child: RichContentEditor( + child: LearnContentEditor( initialContent: content, onChanged: (_) {}, formatBar: true, @@ -132,7 +129,7 @@ void main() { testWidgets('shown read-only, nothing loads until a tap, which opens it ' "outside the app where it can't play inside", (tester) async { - await pump(tester, RichContentView(content: video(start: 90))); + await pump(tester, LearnContentView(content: video(start: 90))); expect(find.text('YouTube video'), findsOneWidget); expect(find.text('YouTube · from 1:30'), findsOneWidget); @@ -149,7 +146,7 @@ void main() { tester, ) async { videos = FakeYoutubeVideos(playInApp: true); - await pump(tester, RichContentView(content: video(caption: 'Listen'))); + await pump(tester, LearnContentView(content: video(caption: 'Listen'))); expect(find.text('Listen'), findsOneWidget); await tester.tap(find.byTooltip('Play video')); @@ -162,7 +159,7 @@ void main() { testWidgets("a video whose id can't be read says so", (tester) async { await pump( tester, - RichContentView( + LearnContentView( content: Document( root: pageNode( children: [ diff --git a/apps/learn/halkyon_learn_flutter/test/core/grammar/grammar_host_test.dart b/apps/learn/halkyon_learn_flutter/test/core/grammar/grammar_host_test.dart index b333f371..2ef7e24a 100644 --- a/apps/learn/halkyon_learn_flutter/test/core/grammar/grammar_host_test.dart +++ b/apps/learn/halkyon_learn_flutter/test/core/grammar/grammar_host_test.dart @@ -1,5 +1,5 @@ import 'package:flutter_test/flutter_test.dart'; -import 'package:halkyon_learn_flutter/src/core/content/rich_content.dart'; +import 'package:halkyon_editor/halkyon_editor.dart'; import 'package:halkyon_learn_flutter/src/core/device_settings/model/device_settings.dart'; import 'package:halkyon_learn_flutter/src/core/grammar/grammar_match.dart'; import 'package:halkyon_learn_flutter/src/core/grammar/grammar_overlay.dart'; diff --git a/apps/learn/halkyon_learn_flutter/test/core/grammar/grammar_segments_test.dart b/apps/learn/halkyon_learn_flutter/test/core/grammar/grammar_segments_test.dart index e8d2e904..6c0e23c7 100644 --- a/apps/learn/halkyon_learn_flutter/test/core/grammar/grammar_segments_test.dart +++ b/apps/learn/halkyon_learn_flutter/test/core/grammar/grammar_segments_test.dart @@ -1,6 +1,6 @@ import 'package:appflowy_editor/appflowy_editor.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'package:halkyon_learn_flutter/src/core/content/content_blocks.dart'; +import 'package:halkyon_editor/halkyon_editor.dart'; import 'package:halkyon_learn_flutter/src/core/grammar/grammar_segments.dart'; void main() { diff --git a/apps/learn/halkyon_learn_flutter/test/core/privacy/privacy_markers_test.dart b/apps/learn/halkyon_learn_flutter/test/core/privacy/privacy_markers_test.dart index 0f943a07..6363c6c5 100644 --- a/apps/learn/halkyon_learn_flutter/test/core/privacy/privacy_markers_test.dart +++ b/apps/learn/halkyon_learn_flutter/test/core/privacy/privacy_markers_test.dart @@ -1,9 +1,9 @@ import 'dart:typed_data'; import 'package:flutter_test/flutter_test.dart'; +import 'package:halkyon_editor/halkyon_editor.dart'; import 'package:halkyon_learn_flutter/l10n/app_localizations_en.dart'; import 'package:halkyon_learn_flutter/src/core/asset/widget/studyset_files.dart'; -import 'package:halkyon_learn_flutter/src/core/content/rich_content.dart'; import 'package:halkyon_learn_flutter/src/core/editor/rich_text_screen.dart'; import 'package:halkyon_learn_flutter/src/core/l10n/app_languages.dart'; import 'package:halkyon_learn_flutter/src/core/privacy/model/unsigned_edit.dart'; diff --git a/apps/learn/halkyon_learn_flutter/test/core/privacy/sealed_codecs_test.dart b/apps/learn/halkyon_learn_flutter/test/core/privacy/sealed_codecs_test.dart index 8506d58f..9ec4e1fd 100644 --- a/apps/learn/halkyon_learn_flutter/test/core/privacy/sealed_codecs_test.dart +++ b/apps/learn/halkyon_learn_flutter/test/core/privacy/sealed_codecs_test.dart @@ -3,8 +3,8 @@ import 'dart:typed_data'; import 'package:flutter_test/flutter_test.dart'; import 'package:halkyon_app/halkyon_app.dart'; import 'package:halkyon_app/testing.dart'; +import 'package:halkyon_editor/halkyon_editor.dart'; import 'package:halkyon_learn_flutter/src/core/asset/data/asset_payload.dart'; -import 'package:halkyon_learn_flutter/src/core/content/rich_content.dart'; import 'package:halkyon_learn_flutter/src/core/privacy/data/content_keys.dart'; import 'package:halkyon_learn_flutter/src/feature/card/data/card_payload.dart'; import 'package:halkyon_learn_flutter/src/feature/card/model/card_content.dart'; diff --git a/apps/learn/halkyon_learn_flutter/test/core/router/app_shell_test.dart b/apps/learn/halkyon_learn_flutter/test/core/router/app_shell_test.dart index 1d0ee81e..9a44251b 100644 --- a/apps/learn/halkyon_learn_flutter/test/core/router/app_shell_test.dart +++ b/apps/learn/halkyon_learn_flutter/test/core/router/app_shell_test.dart @@ -1,6 +1,6 @@ import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'package:halkyon_learn_flutter/src/core/content/rich_content.dart'; +import 'package:halkyon_editor/halkyon_editor.dart'; import 'package:halkyon_learn_flutter/src/core/router/route_paths.dart'; import 'package:halkyon_learn_flutter/src/feature/card/data/card_repository.dart'; import 'package:halkyon_learn_flutter/src/feature/card/model/card_content.dart'; diff --git a/apps/learn/halkyon_learn_flutter/test/core/widget/studyset_screens_colour_test.dart b/apps/learn/halkyon_learn_flutter/test/core/widget/studyset_screens_colour_test.dart index ac52550a..eb947503 100644 --- a/apps/learn/halkyon_learn_flutter/test/core/widget/studyset_screens_colour_test.dart +++ b/apps/learn/halkyon_learn_flutter/test/core/widget/studyset_screens_colour_test.dart @@ -1,5 +1,5 @@ import 'package:flutter_test/flutter_test.dart'; -import 'package:halkyon_learn_flutter/src/core/content/rich_content.dart'; +import 'package:halkyon_editor/halkyon_editor.dart'; import 'package:halkyon_learn_flutter/src/core/router/route_paths.dart'; import 'package:halkyon_learn_flutter/src/feature/card/data/card_repository.dart'; import 'package:halkyon_learn_flutter/src/feature/card/model/card_content.dart'; diff --git a/apps/learn/halkyon_learn_flutter/test/feature/anki/data/anki_field_test.dart b/apps/learn/halkyon_learn_flutter/test/feature/anki/data/anki_field_test.dart index 9ef845db..5420b068 100644 --- a/apps/learn/halkyon_learn_flutter/test/feature/anki/data/anki_field_test.dart +++ b/apps/learn/halkyon_learn_flutter/test/feature/anki/data/anki_field_test.dart @@ -1,5 +1,5 @@ import 'package:flutter_test/flutter_test.dart'; -import 'package:halkyon_learn_flutter/src/core/content/rich_content.dart'; +import 'package:halkyon_editor/halkyon_editor.dart'; import 'package:halkyon_learn_flutter/src/feature/anki/data/anki_field.dart'; void main() { diff --git a/apps/learn/halkyon_learn_flutter/test/feature/anki/widget/anki_import_sheet_test.dart b/apps/learn/halkyon_learn_flutter/test/feature/anki/widget/anki_import_sheet_test.dart index 11f3009e..e8032599 100644 --- a/apps/learn/halkyon_learn_flutter/test/feature/anki/widget/anki_import_sheet_test.dart +++ b/apps/learn/halkyon_learn_flutter/test/feature/anki/widget/anki_import_sheet_test.dart @@ -2,7 +2,7 @@ import 'dart:typed_data'; import 'package:flutter_test/flutter_test.dart'; import 'package:halkyon_app/testing.dart'; -import 'package:halkyon_learn_flutter/src/core/content/rich_content.dart'; +import 'package:halkyon_editor/halkyon_editor.dart'; import 'package:halkyon_learn_flutter/src/core/router/route_paths.dart'; import 'package:halkyon_learn_flutter/src/feature/card/data/card_repository.dart'; import 'package:halkyon_learn_flutter/src/feature/note/data/note_repository.dart'; diff --git a/apps/learn/halkyon_learn_flutter/test/feature/archive/archive_export_test.dart b/apps/learn/halkyon_learn_flutter/test/feature/archive/archive_export_test.dart index b5fc635c..043a96af 100644 --- a/apps/learn/halkyon_learn_flutter/test/feature/archive/archive_export_test.dart +++ b/apps/learn/halkyon_learn_flutter/test/feature/archive/archive_export_test.dart @@ -3,11 +3,10 @@ import 'dart:typed_data'; import 'package:clock/clock.dart'; import 'package:flutter_test/flutter_test.dart'; +import 'package:halkyon_editor/halkyon_editor.dart'; import 'package:halkyon_learn_flutter/src/core/asset/data/asset_repository.dart'; import 'package:halkyon_learn_flutter/src/core/asset/model/stored_file.dart'; -import 'package:halkyon_learn_flutter/src/core/content/content_blocks.dart'; import 'package:halkyon_learn_flutter/src/core/content/published_ref.dart'; -import 'package:halkyon_learn_flutter/src/core/content/rich_content.dart'; import 'package:halkyon_learn_flutter/src/feature/archive/data/archive_exporter.dart'; import 'package:halkyon_learn_flutter/src/feature/archive/data/archive_importer.dart'; import 'package:halkyon_learn_flutter/src/feature/archive/data/archive_reader.dart'; diff --git a/apps/learn/halkyon_learn_flutter/test/feature/archive/archive_test.dart b/apps/learn/halkyon_learn_flutter/test/feature/archive/archive_test.dart index a55955a6..88d87a19 100644 --- a/apps/learn/halkyon_learn_flutter/test/feature/archive/archive_test.dart +++ b/apps/learn/halkyon_learn_flutter/test/feature/archive/archive_test.dart @@ -3,9 +3,8 @@ import 'dart:typed_data'; import 'package:archive/archive.dart'; import 'package:flutter_test/flutter_test.dart'; +import 'package:halkyon_editor/halkyon_editor.dart'; import 'package:halkyon_learn_flutter/src/core/asset/data/asset_repository.dart'; -import 'package:halkyon_learn_flutter/src/core/content/content_blocks.dart'; -import 'package:halkyon_learn_flutter/src/core/content/rich_content.dart'; import 'package:halkyon_learn_flutter/src/feature/archive/data/archive_importer.dart'; import 'package:halkyon_learn_flutter/src/feature/archive/data/archive_reader.dart'; import 'package:halkyon_learn_flutter/src/feature/card/data/card_payload.dart'; diff --git a/apps/learn/halkyon_learn_flutter/test/feature/assistant/assistant_field_test.dart b/apps/learn/halkyon_learn_flutter/test/feature/assistant/assistant_field_test.dart index bbed5c3b..bd22c1a6 100644 --- a/apps/learn/halkyon_learn_flutter/test/feature/assistant/assistant_field_test.dart +++ b/apps/learn/halkyon_learn_flutter/test/feature/assistant/assistant_field_test.dart @@ -1,7 +1,7 @@ import 'package:flutter/foundation.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:halkyon_app/testing.dart' show FakeSpeechInput; -import 'package:halkyon_learn_flutter/src/core/content/rich_content.dart'; +import 'package:halkyon_editor/halkyon_editor.dart'; import 'package:halkyon_learn_flutter/src/core/device_settings/model/device_settings.dart'; import 'package:halkyon_learn_flutter/src/core/router/route_paths.dart'; import 'package:halkyon_learn_flutter/src/feature/assistant/data/web_search.dart'; diff --git a/apps/learn/halkyon_learn_flutter/test/feature/assistant/bloc/conversation_cubit_test.dart b/apps/learn/halkyon_learn_flutter/test/feature/assistant/bloc/conversation_cubit_test.dart index 075c9317..9aca0a65 100644 --- a/apps/learn/halkyon_learn_flutter/test/feature/assistant/bloc/conversation_cubit_test.dart +++ b/apps/learn/halkyon_learn_flutter/test/feature/assistant/bloc/conversation_cubit_test.dart @@ -4,7 +4,7 @@ import 'dart:typed_data'; import 'package:flutter_test/flutter_test.dart'; import 'package:halkyon_app/halkyon_app.dart'; import 'package:halkyon_app/testing.dart'; -import 'package:halkyon_learn_flutter/src/core/content/rich_content.dart'; +import 'package:halkyon_editor/halkyon_editor.dart'; import 'package:halkyon_learn_flutter/src/feature/assistant/bloc/conversation_cubit.dart'; import 'package:halkyon_learn_flutter/src/feature/assistant/data/assistant_lookups.dart'; import 'package:halkyon_learn_flutter/src/feature/assistant/data/conversation_repository.dart'; diff --git a/apps/learn/halkyon_learn_flutter/test/feature/assistant/card_proposals_sheet_test.dart b/apps/learn/halkyon_learn_flutter/test/feature/assistant/card_proposals_sheet_test.dart index f81f2351..10ffca4b 100644 --- a/apps/learn/halkyon_learn_flutter/test/feature/assistant/card_proposals_sheet_test.dart +++ b/apps/learn/halkyon_learn_flutter/test/feature/assistant/card_proposals_sheet_test.dart @@ -4,8 +4,8 @@ import 'dart:typed_data'; import 'package:flutter_test/flutter_test.dart'; import 'package:halkyon_app/halkyon_app.dart'; import 'package:halkyon_app/testing.dart'; +import 'package:halkyon_editor/halkyon_editor.dart'; import 'package:halkyon_learn_flutter/src/core/asset/data/asset_repository.dart'; -import 'package:halkyon_learn_flutter/src/core/content/rich_content.dart'; import 'package:halkyon_learn_flutter/src/core/router/route_paths.dart'; import 'package:halkyon_learn_flutter/src/feature/assistant/bloc/card_proposals_cubit.dart'; import 'package:halkyon_learn_flutter/src/feature/card/data/card_payload.dart'; diff --git a/apps/learn/halkyon_learn_flutter/test/feature/assistant/conversation_test.dart b/apps/learn/halkyon_learn_flutter/test/feature/assistant/conversation_test.dart index 024e08ef..2c6b5e55 100644 --- a/apps/learn/halkyon_learn_flutter/test/feature/assistant/conversation_test.dart +++ b/apps/learn/halkyon_learn_flutter/test/feature/assistant/conversation_test.dart @@ -4,8 +4,8 @@ import 'package:flutter/foundation.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:halkyon_app/halkyon_app.dart'; import 'package:halkyon_app/testing.dart'; +import 'package:halkyon_editor/halkyon_editor.dart'; import 'package:halkyon_learn_flutter/src/core/asset/data/asset_repository.dart'; -import 'package:halkyon_learn_flutter/src/core/content/rich_content.dart'; import 'package:halkyon_learn_flutter/src/core/device_settings/model/device_settings.dart'; import 'package:halkyon_learn_flutter/src/core/grammar/grammar_match.dart'; import 'package:halkyon_learn_flutter/src/core/router/route_paths.dart'; diff --git a/apps/learn/halkyon_learn_flutter/test/feature/assistant/data/assistant_lookups_test.dart b/apps/learn/halkyon_learn_flutter/test/feature/assistant/data/assistant_lookups_test.dart index 29f336e9..b288fb47 100644 --- a/apps/learn/halkyon_learn_flutter/test/feature/assistant/data/assistant_lookups_test.dart +++ b/apps/learn/halkyon_learn_flutter/test/feature/assistant/data/assistant_lookups_test.dart @@ -2,7 +2,7 @@ import 'dart:convert'; import 'dart:typed_data'; import 'package:flutter_test/flutter_test.dart'; -import 'package:halkyon_learn_flutter/src/core/content/rich_content.dart'; +import 'package:halkyon_editor/halkyon_editor.dart'; import 'package:halkyon_learn_flutter/src/feature/assistant/data/assistant_lookups.dart'; import 'package:halkyon_learn_flutter/src/feature/assistant/model/assistant_conversation.dart'; import 'package:halkyon_learn_flutter/src/feature/assistant/model/assistant_lookup.dart'; diff --git a/apps/learn/halkyon_learn_flutter/test/feature/card/bloc/card_blocs_test.dart b/apps/learn/halkyon_learn_flutter/test/feature/card/bloc/card_blocs_test.dart index 7083219b..5fabe0fd 100644 --- a/apps/learn/halkyon_learn_flutter/test/feature/card/bloc/card_blocs_test.dart +++ b/apps/learn/halkyon_learn_flutter/test/feature/card/bloc/card_blocs_test.dart @@ -1,5 +1,5 @@ import 'package:flutter_test/flutter_test.dart'; -import 'package:halkyon_learn_flutter/src/core/content/rich_content.dart'; +import 'package:halkyon_editor/halkyon_editor.dart'; import 'package:halkyon_learn_flutter/src/feature/card/bloc/card_list_bloc.dart'; import 'package:halkyon_learn_flutter/src/feature/card/data/card_deletions.dart'; import 'package:halkyon_learn_flutter/src/feature/card/data/card_repository.dart'; diff --git a/apps/learn/halkyon_learn_flutter/test/feature/card/bloc/card_browser_bloc_test.dart b/apps/learn/halkyon_learn_flutter/test/feature/card/bloc/card_browser_bloc_test.dart index ca6f46d7..f8ed53ce 100644 --- a/apps/learn/halkyon_learn_flutter/test/feature/card/bloc/card_browser_bloc_test.dart +++ b/apps/learn/halkyon_learn_flutter/test/feature/card/bloc/card_browser_bloc_test.dart @@ -1,5 +1,5 @@ import 'package:flutter_test/flutter_test.dart'; -import 'package:halkyon_learn_flutter/src/core/content/rich_content.dart'; +import 'package:halkyon_editor/halkyon_editor.dart'; import 'package:halkyon_learn_flutter/src/feature/card/bloc/card_browser_bloc.dart'; import 'package:halkyon_learn_flutter/src/feature/card/data/card_deletions.dart'; import 'package:halkyon_learn_flutter/src/feature/card/data/card_repository.dart'; diff --git a/apps/learn/halkyon_learn_flutter/test/feature/card/bloc/card_composer_bloc_test.dart b/apps/learn/halkyon_learn_flutter/test/feature/card/bloc/card_composer_bloc_test.dart index e99ada3b..7e9ff7a8 100644 --- a/apps/learn/halkyon_learn_flutter/test/feature/card/bloc/card_composer_bloc_test.dart +++ b/apps/learn/halkyon_learn_flutter/test/feature/card/bloc/card_composer_bloc_test.dart @@ -1,5 +1,5 @@ import 'package:flutter_test/flutter_test.dart'; -import 'package:halkyon_learn_flutter/src/core/content/rich_content.dart'; +import 'package:halkyon_editor/halkyon_editor.dart'; import 'package:halkyon_learn_flutter/src/feature/card/bloc/card_composer_bloc.dart'; import 'package:halkyon_learn_flutter/src/feature/card/data/card_payload.dart'; import 'package:halkyon_learn_flutter/src/feature/card/data/card_repository.dart'; diff --git a/apps/learn/halkyon_learn_flutter/test/feature/card/data/card_payload_test.dart b/apps/learn/halkyon_learn_flutter/test/feature/card/data/card_payload_test.dart index 0b104f5e..e5544fa6 100644 --- a/apps/learn/halkyon_learn_flutter/test/feature/card/data/card_payload_test.dart +++ b/apps/learn/halkyon_learn_flutter/test/feature/card/data/card_payload_test.dart @@ -2,8 +2,8 @@ import 'dart:convert'; import 'dart:typed_data'; import 'package:flutter_test/flutter_test.dart'; +import 'package:halkyon_editor/halkyon_editor.dart'; import 'package:halkyon_learn_flutter/src/core/content/payload.dart'; -import 'package:halkyon_learn_flutter/src/core/content/rich_content.dart'; import 'package:halkyon_learn_flutter/src/feature/card/data/card_payload.dart'; import 'package:halkyon_learn_flutter/src/feature/card/model/card_content.dart'; diff --git a/apps/learn/halkyon_learn_flutter/test/feature/card/data/card_repository_test.dart b/apps/learn/halkyon_learn_flutter/test/feature/card/data/card_repository_test.dart index 81232afb..8a4f4fd1 100644 --- a/apps/learn/halkyon_learn_flutter/test/feature/card/data/card_repository_test.dart +++ b/apps/learn/halkyon_learn_flutter/test/feature/card/data/card_repository_test.dart @@ -2,9 +2,9 @@ import 'dart:convert'; import 'dart:typed_data'; import 'package:flutter_test/flutter_test.dart'; +import 'package:halkyon_editor/halkyon_editor.dart'; import 'package:halkyon_learn_flutter/src/core/content/payload.dart'; import 'package:halkyon_learn_flutter/src/core/content/revision_merge.dart'; -import 'package:halkyon_learn_flutter/src/core/content/rich_content.dart'; import 'package:halkyon_learn_flutter/src/feature/card/data/card_gateway.dart'; import 'package:halkyon_learn_flutter/src/feature/card/data/card_payload.dart'; import 'package:halkyon_learn_flutter/src/feature/card/data/card_repository.dart'; diff --git a/apps/learn/halkyon_learn_flutter/test/feature/card/model/card_browser_test.dart b/apps/learn/halkyon_learn_flutter/test/feature/card/model/card_browser_test.dart index ca4c68a7..0152a703 100644 --- a/apps/learn/halkyon_learn_flutter/test/feature/card/model/card_browser_test.dart +++ b/apps/learn/halkyon_learn_flutter/test/feature/card/model/card_browser_test.dart @@ -1,5 +1,5 @@ import 'package:flutter_test/flutter_test.dart'; -import 'package:halkyon_learn_flutter/src/core/content/rich_content.dart'; +import 'package:halkyon_editor/halkyon_editor.dart'; import 'package:halkyon_learn_flutter/src/feature/card/model/card.dart'; import 'package:halkyon_learn_flutter/src/feature/card/model/card_browser.dart'; import 'package:halkyon_learn_flutter/src/feature/card/model/card_content.dart'; diff --git a/apps/learn/halkyon_learn_flutter/test/feature/card/model/card_content_test.dart b/apps/learn/halkyon_learn_flutter/test/feature/card/model/card_content_test.dart index 4cc3e248..5d29229b 100644 --- a/apps/learn/halkyon_learn_flutter/test/feature/card/model/card_content_test.dart +++ b/apps/learn/halkyon_learn_flutter/test/feature/card/model/card_content_test.dart @@ -1,5 +1,5 @@ import 'package:flutter_test/flutter_test.dart'; -import 'package:halkyon_learn_flutter/src/core/content/rich_content.dart'; +import 'package:halkyon_editor/halkyon_editor.dart'; import 'package:halkyon_learn_flutter/src/feature/card/model/card_content.dart'; void main() { diff --git a/apps/learn/halkyon_learn_flutter/test/feature/card/model/card_merge_test.dart b/apps/learn/halkyon_learn_flutter/test/feature/card/model/card_merge_test.dart index 3fe1a467..7a23af97 100644 --- a/apps/learn/halkyon_learn_flutter/test/feature/card/model/card_merge_test.dart +++ b/apps/learn/halkyon_learn_flutter/test/feature/card/model/card_merge_test.dart @@ -1,5 +1,5 @@ import 'package:flutter_test/flutter_test.dart'; -import 'package:halkyon_learn_flutter/src/core/content/rich_content.dart'; +import 'package:halkyon_editor/halkyon_editor.dart'; import 'package:halkyon_learn_flutter/src/feature/card/model/card_content.dart'; import 'package:halkyon_learn_flutter/src/feature/card/model/card_merge.dart'; import 'package:halkyon_learn_flutter/src/feature/card/model/card_revision.dart'; diff --git a/apps/learn/halkyon_learn_flutter/test/feature/card/widget/card_browser_screen_test.dart b/apps/learn/halkyon_learn_flutter/test/feature/card/widget/card_browser_screen_test.dart index 5a9e57b3..c084398b 100644 --- a/apps/learn/halkyon_learn_flutter/test/feature/card/widget/card_browser_screen_test.dart +++ b/apps/learn/halkyon_learn_flutter/test/feature/card/widget/card_browser_screen_test.dart @@ -1,5 +1,5 @@ import 'package:flutter_test/flutter_test.dart'; -import 'package:halkyon_learn_flutter/src/core/content/rich_content.dart'; +import 'package:halkyon_editor/halkyon_editor.dart'; import 'package:halkyon_learn_flutter/src/core/router/route_paths.dart'; import 'package:halkyon_learn_flutter/src/feature/card/data/card_repository.dart'; import 'package:halkyon_learn_flutter/src/feature/card/model/card_content.dart'; diff --git a/apps/learn/halkyon_learn_flutter/test/feature/card/widget/card_composer_test.dart b/apps/learn/halkyon_learn_flutter/test/feature/card/widget/card_composer_test.dart index 52997656..ebd1f8c0 100644 --- a/apps/learn/halkyon_learn_flutter/test/feature/card/widget/card_composer_test.dart +++ b/apps/learn/halkyon_learn_flutter/test/feature/card/widget/card_composer_test.dart @@ -2,9 +2,8 @@ import 'package:appflowy_editor/appflowy_editor.dart'; import 'package:flutter/gestures.dart'; import 'package:flutter/rendering.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'package:halkyon_learn_flutter/src/core/content/content_blocks.dart'; -import 'package:halkyon_learn_flutter/src/core/content/rich_content.dart'; -import 'package:halkyon_learn_flutter/src/core/editor/rich_content_editor.dart'; +import 'package:halkyon_editor/halkyon_editor.dart'; +import 'package:halkyon_learn_flutter/src/core/editor/learn_content.dart'; import 'package:halkyon_learn_flutter/src/core/router/route_paths.dart'; import 'package:halkyon_learn_flutter/src/feature/card/data/card_payload.dart'; import 'package:halkyon_learn_flutter/src/feature/card/data/card_repository.dart'; @@ -351,12 +350,12 @@ void main() { await tester.tap(find.text('cat', findRichText: true)); await settle(tester); - expect(find.byType(RichContentEditor), findsOneWidget); + expect(find.byType(LearnContentEditor), findsOneWidget); tester.view.viewInsets = const FakeViewPadding(bottom: 400); await settle(tester); expect( - tester.getBottomLeft(find.byType(RichContentEditor)).dy, + tester.getBottomLeft(find.byType(LearnContentEditor)).dy, lessThanOrEqualTo(915 - 400), ); }); @@ -372,7 +371,7 @@ void main() { expect(find.byTooltip('Bold'), findsOneWidget); // A short field opens as low as a field gets, the bar within. expect( - tester.getSize(find.byType(RichContentEditor)).height, + tester.getSize(find.byType(LearnContentEditor)).height, CardFace.defaultMinEditorHeight, ); @@ -405,7 +404,7 @@ void main() { await settle(tester); final editor = tester.widget( find.descendant( - of: find.byType(RichContentEditor), + of: find.byType(LearnContentEditor), matching: find.byType(AppFlowyEditor), ), ); @@ -414,7 +413,7 @@ void main() { ); await slash.handler(editor.editorState); await tester.pumpAndSettle(); - expect(find.byType(RichContentEditor), findsOneWidget); + expect(find.byType(LearnContentEditor), findsOneWidget); // The menu builds only the items near what it shows. final menu = find.descendant( @@ -504,10 +503,10 @@ void main() { expect(find.text('Correct answer'), findsOneWidget); expect(find.text('Wrong answer'), findsOneWidget); // The new answer is the one open, framed by its card alone. - expect(find.byType(RichContentEditor), findsOneWidget); + expect(find.byType(LearnContentEditor), findsOneWidget); expect( find.ancestor( - of: find.byType(RichContentEditor), + of: find.byType(LearnContentEditor), matching: find.byWidgetPredicate( (widget) => widget is CardFace && widget.accent != null, ), @@ -530,19 +529,19 @@ void main() { await tester.tap(inField('cat')); await settle(tester); - expect(find.byType(RichContentEditor), findsOneWidget); + expect(find.byType(LearnContentEditor), findsOneWidget); await tester.tapAt(outside, kind: PointerDeviceKind.mouse); await settle(tester); - expect(find.byType(RichContentEditor), findsNothing); + expect(find.byType(LearnContentEditor), findsNothing); await tester.tap(inField('cat')); await settle(tester); await tester.dragFrom(outside, const Offset(0, -40)); await settle(tester); - expect(find.byType(RichContentEditor), findsOneWidget); + expect(find.byType(LearnContentEditor), findsOneWidget); await tester.tapAt(tester.getCenter(find.text('Back'))); await settle(tester); - expect(find.byType(RichContentEditor), findsNothing); + expect(find.byType(LearnContentEditor), findsNothing); }); testWidgets('a field closed after writing in it is not left lit as if ' @@ -559,7 +558,7 @@ void main() { await mouse.down(tester.getCenter(inField('cat'))); await mouse.up(); await settle(tester); - expect(find.byType(RichContentEditor), findsOneWidget); + expect(find.byType(LearnContentEditor), findsOneWidget); // Into the question, the pointer leaving the answer on its way. final front = tester.getCenter(inField('猫')); diff --git a/apps/learn/halkyon_learn_flutter/test/feature/card/widget/card_widgets_test.dart b/apps/learn/halkyon_learn_flutter/test/feature/card/widget/card_widgets_test.dart index fa8611f2..af31afb4 100644 --- a/apps/learn/halkyon_learn_flutter/test/feature/card/widget/card_widgets_test.dart +++ b/apps/learn/halkyon_learn_flutter/test/feature/card/widget/card_widgets_test.dart @@ -1,5 +1,5 @@ import 'package:flutter_test/flutter_test.dart'; -import 'package:halkyon_learn_flutter/src/core/content/rich_content.dart'; +import 'package:halkyon_editor/halkyon_editor.dart'; import 'package:halkyon_learn_flutter/src/core/router/route_paths.dart'; import 'package:halkyon_learn_flutter/src/feature/card/data/card_payload.dart'; import 'package:halkyon_learn_flutter/src/feature/card/data/card_repository.dart'; diff --git a/apps/learn/halkyon_learn_flutter/test/feature/dictionary/dictionary_data_test.dart b/apps/learn/halkyon_learn_flutter/test/feature/dictionary/dictionary_data_test.dart index 2e4a9d35..b093f16a 100644 --- a/apps/learn/halkyon_learn_flutter/test/feature/dictionary/dictionary_data_test.dart +++ b/apps/learn/halkyon_learn_flutter/test/feature/dictionary/dictionary_data_test.dart @@ -1,7 +1,7 @@ import 'dart:typed_data'; import 'package:flutter_test/flutter_test.dart'; -import 'package:halkyon_learn_flutter/src/core/content/rich_content.dart'; +import 'package:halkyon_editor/halkyon_editor.dart'; import 'package:halkyon_learn_flutter/src/feature/card/data/card_repository.dart'; import 'package:halkyon_learn_flutter/src/feature/card/model/card.dart'; import 'package:halkyon_learn_flutter/src/feature/card/model/card_content.dart'; diff --git a/apps/learn/halkyon_learn_flutter/test/feature/dictionary/dictionary_screen_test.dart b/apps/learn/halkyon_learn_flutter/test/feature/dictionary/dictionary_screen_test.dart index 05d6bc25..0d26efb7 100644 --- a/apps/learn/halkyon_learn_flutter/test/feature/dictionary/dictionary_screen_test.dart +++ b/apps/learn/halkyon_learn_flutter/test/feature/dictionary/dictionary_screen_test.dart @@ -1,7 +1,7 @@ import 'dart:typed_data'; import 'package:flutter_test/flutter_test.dart'; -import 'package:halkyon_learn_flutter/src/core/content/rich_content.dart'; +import 'package:halkyon_editor/halkyon_editor.dart'; import 'package:halkyon_learn_flutter/src/core/router/route_paths.dart'; import 'package:halkyon_learn_flutter/src/feature/card/data/card_repository.dart'; import 'package:halkyon_learn_flutter/src/feature/card/model/card_content.dart'; diff --git a/apps/learn/halkyon_learn_flutter/test/feature/discussion/discussion_data_test.dart b/apps/learn/halkyon_learn_flutter/test/feature/discussion/discussion_data_test.dart index 18669b2f..30691b15 100644 --- a/apps/learn/halkyon_learn_flutter/test/feature/discussion/discussion_data_test.dart +++ b/apps/learn/halkyon_learn_flutter/test/feature/discussion/discussion_data_test.dart @@ -1,5 +1,5 @@ import 'package:flutter_test/flutter_test.dart'; -import 'package:halkyon_learn_flutter/src/core/content/rich_content.dart'; +import 'package:halkyon_editor/halkyon_editor.dart'; import 'package:halkyon_learn_flutter/src/core/profile/public_person.dart'; import 'package:halkyon_learn_flutter/src/feature/discussion/bloc/discussion_bloc.dart'; import 'package:halkyon_learn_flutter/src/feature/discussion/bloc/discussion_list_bloc.dart'; diff --git a/apps/learn/halkyon_learn_flutter/test/feature/discussion/discussion_widgets_test.dart b/apps/learn/halkyon_learn_flutter/test/feature/discussion/discussion_widgets_test.dart index 7065a6b3..b61776fd 100644 --- a/apps/learn/halkyon_learn_flutter/test/feature/discussion/discussion_widgets_test.dart +++ b/apps/learn/halkyon_learn_flutter/test/feature/discussion/discussion_widgets_test.dart @@ -2,8 +2,8 @@ import 'package:appflowy_editor/appflowy_editor.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:halkyon_app/halkyon_app.dart'; import 'package:halkyon_app/testing.dart'; +import 'package:halkyon_editor/halkyon_editor.dart'; import 'package:halkyon_learn_flutter/src/core/content/published_ref.dart'; -import 'package:halkyon_learn_flutter/src/core/content/rich_content.dart'; import 'package:halkyon_learn_flutter/src/core/profile/public_person.dart'; import 'package:halkyon_learn_flutter/src/core/router/route_paths.dart'; import 'package:halkyon_learn_flutter/src/feature/discussion/model/discussion.dart'; diff --git a/apps/learn/halkyon_learn_flutter/test/feature/explore/copy_update_banner_test.dart b/apps/learn/halkyon_learn_flutter/test/feature/explore/copy_update_banner_test.dart index 134814b0..e312aa2f 100644 --- a/apps/learn/halkyon_learn_flutter/test/feature/explore/copy_update_banner_test.dart +++ b/apps/learn/halkyon_learn_flutter/test/feature/explore/copy_update_banner_test.dart @@ -2,9 +2,9 @@ import 'dart:convert'; import 'dart:typed_data'; import 'package:flutter_test/flutter_test.dart'; +import 'package:halkyon_editor/halkyon_editor.dart'; import 'package:halkyon_learn_flutter/src/core/asset/data/asset_repository.dart'; import 'package:halkyon_learn_flutter/src/core/content/published_ref.dart'; -import 'package:halkyon_learn_flutter/src/core/content/rich_content.dart'; import 'package:halkyon_learn_flutter/src/core/router/route_paths.dart'; import 'package:halkyon_learn_flutter/src/feature/card/data/card_payload.dart'; import 'package:halkyon_learn_flutter/src/feature/card/data/card_repository.dart'; diff --git a/apps/learn/halkyon_learn_flutter/test/feature/explore/copy_updater_test.dart b/apps/learn/halkyon_learn_flutter/test/feature/explore/copy_updater_test.dart index 0af557fd..71a54e83 100644 --- a/apps/learn/halkyon_learn_flutter/test/feature/explore/copy_updater_test.dart +++ b/apps/learn/halkyon_learn_flutter/test/feature/explore/copy_updater_test.dart @@ -3,8 +3,8 @@ import 'dart:typed_data'; import 'package:crypto/crypto.dart'; import 'package:flutter_test/flutter_test.dart'; +import 'package:halkyon_editor/halkyon_editor.dart'; import 'package:halkyon_learn_flutter/src/core/asset/data/asset_repository.dart'; -import 'package:halkyon_learn_flutter/src/core/content/rich_content.dart'; import 'package:halkyon_learn_flutter/src/feature/card/data/card_payload.dart'; import 'package:halkyon_learn_flutter/src/feature/card/data/card_repository.dart'; import 'package:halkyon_learn_flutter/src/feature/card/model/card.dart'; diff --git a/apps/learn/halkyon_learn_flutter/test/feature/explore/data/learning_counting_test.dart b/apps/learn/halkyon_learn_flutter/test/feature/explore/data/learning_counting_test.dart index 2a0b6f25..8ec28723 100644 --- a/apps/learn/halkyon_learn_flutter/test/feature/explore/data/learning_counting_test.dart +++ b/apps/learn/halkyon_learn_flutter/test/feature/explore/data/learning_counting_test.dart @@ -1,6 +1,6 @@ import 'package:flutter_test/flutter_test.dart'; +import 'package:halkyon_editor/halkyon_editor.dart'; import 'package:halkyon_learn_flutter/src/core/content/published_ref.dart'; -import 'package:halkyon_learn_flutter/src/core/content/rich_content.dart'; import 'package:halkyon_learn_flutter/src/feature/explore/data/learning_counting.dart'; import 'package:halkyon_learn_flutter/src/feature/settings/data/settings_repository.dart'; import 'package:halkyon_learn_flutter/src/feature/studyset/data/studyset_payload.dart'; diff --git a/apps/learn/halkyon_learn_flutter/test/feature/explore/explore_screen_test.dart b/apps/learn/halkyon_learn_flutter/test/feature/explore/explore_screen_test.dart index 071b0f9e..b866dfe6 100644 --- a/apps/learn/halkyon_learn_flutter/test/feature/explore/explore_screen_test.dart +++ b/apps/learn/halkyon_learn_flutter/test/feature/explore/explore_screen_test.dart @@ -4,8 +4,8 @@ import 'dart:typed_data'; import 'package:flutter_test/flutter_test.dart'; import 'package:halkyon_app/halkyon_app.dart'; import 'package:halkyon_app/testing.dart'; +import 'package:halkyon_editor/halkyon_editor.dart'; import 'package:halkyon_learn_flutter/src/core/content/published_ref.dart'; -import 'package:halkyon_learn_flutter/src/core/content/rich_content.dart'; import 'package:halkyon_learn_flutter/src/core/router/route_paths.dart'; import 'package:halkyon_learn_flutter/src/feature/card/data/card_payload.dart'; import 'package:halkyon_learn_flutter/src/feature/card/model/card_content.dart'; diff --git a/apps/learn/halkyon_learn_flutter/test/feature/explore/release_copier_test.dart b/apps/learn/halkyon_learn_flutter/test/feature/explore/release_copier_test.dart index 189cbdcc..0fccdace 100644 --- a/apps/learn/halkyon_learn_flutter/test/feature/explore/release_copier_test.dart +++ b/apps/learn/halkyon_learn_flutter/test/feature/explore/release_copier_test.dart @@ -2,9 +2,9 @@ import 'dart:convert'; import 'dart:typed_data'; import 'package:flutter_test/flutter_test.dart'; +import 'package:halkyon_editor/halkyon_editor.dart'; import 'package:halkyon_learn_flutter/src/core/asset/data/asset_repository.dart'; import 'package:halkyon_learn_flutter/src/core/content/published_ref.dart'; -import 'package:halkyon_learn_flutter/src/core/content/rich_content.dart'; import 'package:halkyon_learn_flutter/src/feature/card/data/card_payload.dart'; import 'package:halkyon_learn_flutter/src/feature/card/data/card_repository.dart'; import 'package:halkyon_learn_flutter/src/feature/card/model/card_content.dart'; diff --git a/apps/learn/halkyon_learn_flutter/test/feature/export/data/studyset_markdown_test.dart b/apps/learn/halkyon_learn_flutter/test/feature/export/data/studyset_markdown_test.dart index 42925e2b..3cb09531 100644 --- a/apps/learn/halkyon_learn_flutter/test/feature/export/data/studyset_markdown_test.dart +++ b/apps/learn/halkyon_learn_flutter/test/feature/export/data/studyset_markdown_test.dart @@ -2,10 +2,10 @@ import 'dart:convert'; import 'dart:typed_data'; import 'package:flutter_test/flutter_test.dart'; +import 'package:halkyon_editor/halkyon_editor.dart'; import 'package:halkyon_learn_flutter/src/core/asset/data/asset_repository.dart'; import 'package:halkyon_learn_flutter/src/core/citation/model/citation.dart'; import 'package:halkyon_learn_flutter/src/core/citation/model/source.dart'; -import 'package:halkyon_learn_flutter/src/core/content/rich_content.dart'; import 'package:halkyon_learn_flutter/src/feature/card/data/card_repository.dart'; import 'package:halkyon_learn_flutter/src/feature/card/model/card_content.dart'; import 'package:halkyon_learn_flutter/src/feature/export/data/studyset_markdown.dart'; diff --git a/apps/learn/halkyon_learn_flutter/test/feature/export/widget/export_studyset_test.dart b/apps/learn/halkyon_learn_flutter/test/feature/export/widget/export_studyset_test.dart index 444be0f9..5cc4d0cc 100644 --- a/apps/learn/halkyon_learn_flutter/test/feature/export/widget/export_studyset_test.dart +++ b/apps/learn/halkyon_learn_flutter/test/feature/export/widget/export_studyset_test.dart @@ -3,9 +3,9 @@ import 'dart:typed_data'; import 'package:archive/archive.dart'; import 'package:flutter_test/flutter_test.dart'; +import 'package:halkyon_editor/halkyon_editor.dart'; import 'package:halkyon_learn_flutter/src/core/asset/data/asset_payload.dart'; import 'package:halkyon_learn_flutter/src/core/asset/data/asset_repository.dart'; -import 'package:halkyon_learn_flutter/src/core/content/rich_content.dart'; import 'package:halkyon_learn_flutter/src/core/router/route_paths.dart'; import 'package:halkyon_learn_flutter/src/feature/card/data/card_repository.dart'; import 'package:halkyon_learn_flutter/src/feature/card/model/card_content.dart'; diff --git a/apps/learn/halkyon_learn_flutter/test/feature/feedback/feedback_repository_test.dart b/apps/learn/halkyon_learn_flutter/test/feature/feedback/feedback_repository_test.dart index 2e50360e..a553b64f 100644 --- a/apps/learn/halkyon_learn_flutter/test/feature/feedback/feedback_repository_test.dart +++ b/apps/learn/halkyon_learn_flutter/test/feature/feedback/feedback_repository_test.dart @@ -3,9 +3,9 @@ import 'dart:typed_data'; import 'package:crypto/crypto.dart'; import 'package:flutter_test/flutter_test.dart'; +import 'package:halkyon_editor/halkyon_editor.dart'; import 'package:halkyon_learn_flutter/src/core/asset/data/asset_repository.dart'; import 'package:halkyon_learn_flutter/src/core/content/published_ref.dart'; -import 'package:halkyon_learn_flutter/src/core/content/rich_content.dart'; import 'package:halkyon_learn_flutter/src/core/profile/public_person.dart'; import 'package:halkyon_learn_flutter/src/feature/card/data/card_repository.dart'; import 'package:halkyon_learn_flutter/src/feature/card/model/card_content.dart'; diff --git a/apps/learn/halkyon_learn_flutter/test/feature/feedback/feedback_widgets_test.dart b/apps/learn/halkyon_learn_flutter/test/feature/feedback/feedback_widgets_test.dart index 68b940e1..5bd46c95 100644 --- a/apps/learn/halkyon_learn_flutter/test/feature/feedback/feedback_widgets_test.dart +++ b/apps/learn/halkyon_learn_flutter/test/feature/feedback/feedback_widgets_test.dart @@ -1,8 +1,8 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:halkyon_app/halkyon_app.dart'; import 'package:halkyon_app/testing.dart'; +import 'package:halkyon_editor/halkyon_editor.dart'; import 'package:halkyon_learn_flutter/src/core/content/published_ref.dart'; -import 'package:halkyon_learn_flutter/src/core/content/rich_content.dart'; import 'package:halkyon_learn_flutter/src/core/profile/public_person.dart'; import 'package:halkyon_learn_flutter/src/core/router/route_paths.dart'; import 'package:halkyon_learn_flutter/src/feature/card/data/card_repository.dart'; diff --git a/apps/learn/halkyon_learn_flutter/test/feature/home/bloc/home_bloc_test.dart b/apps/learn/halkyon_learn_flutter/test/feature/home/bloc/home_bloc_test.dart index 9e88c665..1728fc90 100644 --- a/apps/learn/halkyon_learn_flutter/test/feature/home/bloc/home_bloc_test.dart +++ b/apps/learn/halkyon_learn_flutter/test/feature/home/bloc/home_bloc_test.dart @@ -1,6 +1,6 @@ import 'package:clock/clock.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'package:halkyon_learn_flutter/src/core/content/rich_content.dart'; +import 'package:halkyon_editor/halkyon_editor.dart'; import 'package:halkyon_learn_flutter/src/feature/card/data/card_repository.dart'; import 'package:halkyon_learn_flutter/src/feature/card/model/card_content.dart'; import 'package:halkyon_learn_flutter/src/feature/home/bloc/home_bloc.dart'; diff --git a/apps/learn/halkyon_learn_flutter/test/feature/home/widget/home_screen_test.dart b/apps/learn/halkyon_learn_flutter/test/feature/home/widget/home_screen_test.dart index 4cf49dc0..6e1b8a83 100644 --- a/apps/learn/halkyon_learn_flutter/test/feature/home/widget/home_screen_test.dart +++ b/apps/learn/halkyon_learn_flutter/test/feature/home/widget/home_screen_test.dart @@ -1,5 +1,5 @@ import 'package:flutter_test/flutter_test.dart'; -import 'package:halkyon_learn_flutter/src/core/content/rich_content.dart'; +import 'package:halkyon_editor/halkyon_editor.dart'; import 'package:halkyon_learn_flutter/src/feature/card/data/card_repository.dart'; import 'package:halkyon_learn_flutter/src/feature/card/model/card_content.dart'; import 'package:halkyon_learn_flutter/src/feature/home/widget/goal_card.dart'; diff --git a/apps/learn/halkyon_learn_flutter/test/feature/jellyfin/jellyfin_browser_test.dart b/apps/learn/halkyon_learn_flutter/test/feature/jellyfin/jellyfin_browser_test.dart index bf6be0c5..511d28c5 100644 --- a/apps/learn/halkyon_learn_flutter/test/feature/jellyfin/jellyfin_browser_test.dart +++ b/apps/learn/halkyon_learn_flutter/test/feature/jellyfin/jellyfin_browser_test.dart @@ -1,5 +1,5 @@ import 'package:flutter_test/flutter_test.dart'; -import 'package:halkyon_learn_flutter/src/core/content/rich_content.dart'; +import 'package:halkyon_editor/halkyon_editor.dart'; import 'package:halkyon_learn_flutter/src/core/router/route_paths.dart'; import 'package:halkyon_learn_flutter/src/feature/jellyfin/data/jellyfin_suggestion.dart'; import 'package:halkyon_learn_flutter/src/feature/jellyfin/model/jellyfin.dart'; diff --git a/apps/learn/halkyon_learn_flutter/test/feature/jellyfin/watch_screen_test.dart b/apps/learn/halkyon_learn_flutter/test/feature/jellyfin/watch_screen_test.dart index c1ac5084..a3ff69b9 100644 --- a/apps/learn/halkyon_learn_flutter/test/feature/jellyfin/watch_screen_test.dart +++ b/apps/learn/halkyon_learn_flutter/test/feature/jellyfin/watch_screen_test.dart @@ -2,7 +2,7 @@ import 'package:flutter/gestures.dart'; import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:go_router/go_router.dart'; -import 'package:halkyon_learn_flutter/src/core/content/rich_content.dart'; +import 'package:halkyon_editor/halkyon_editor.dart'; import 'package:halkyon_learn_flutter/src/core/router/route_paths.dart'; import 'package:halkyon_learn_flutter/src/core/video/media_players.dart'; import 'package:halkyon_learn_flutter/src/feature/card/data/card_repository.dart'; diff --git a/apps/learn/halkyon_learn_flutter/test/feature/learning_path/bloc/path_editor_bloc_test.dart b/apps/learn/halkyon_learn_flutter/test/feature/learning_path/bloc/path_editor_bloc_test.dart index da712598..113fa387 100644 --- a/apps/learn/halkyon_learn_flutter/test/feature/learning_path/bloc/path_editor_bloc_test.dart +++ b/apps/learn/halkyon_learn_flutter/test/feature/learning_path/bloc/path_editor_bloc_test.dart @@ -1,5 +1,5 @@ import 'package:flutter_test/flutter_test.dart'; -import 'package:halkyon_learn_flutter/src/core/content/rich_content.dart'; +import 'package:halkyon_editor/halkyon_editor.dart'; import 'package:halkyon_learn_flutter/src/feature/learning_path/bloc/path_editor_bloc.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_repository.dart'; diff --git a/apps/learn/halkyon_learn_flutter/test/feature/learning_path/data/learning_path_repository_test.dart b/apps/learn/halkyon_learn_flutter/test/feature/learning_path/data/learning_path_repository_test.dart index bcc93140..44adb8b3 100644 --- a/apps/learn/halkyon_learn_flutter/test/feature/learning_path/data/learning_path_repository_test.dart +++ b/apps/learn/halkyon_learn_flutter/test/feature/learning_path/data/learning_path_repository_test.dart @@ -1,6 +1,6 @@ import 'package:flutter_test/flutter_test.dart'; +import 'package:halkyon_editor/halkyon_editor.dart'; import 'package:halkyon_learn_flutter/src/core/content/payload.dart'; -import 'package:halkyon_learn_flutter/src/core/content/rich_content.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/learning_path_step_payload.dart'; import 'package:halkyon_learn_flutter/src/feature/learning_path/model/exercise.dart'; diff --git a/apps/learn/halkyon_learn_flutter/test/feature/learning_path/data/path_import_test.dart b/apps/learn/halkyon_learn_flutter/test/feature/learning_path/data/path_import_test.dart index 1b604ada..ad77834d 100644 --- a/apps/learn/halkyon_learn_flutter/test/feature/learning_path/data/path_import_test.dart +++ b/apps/learn/halkyon_learn_flutter/test/feature/learning_path/data/path_import_test.dart @@ -1,10 +1,9 @@ import 'dart:math'; import 'package:flutter_test/flutter_test.dart'; +import 'package:halkyon_editor/halkyon_editor.dart'; import 'package:halkyon_learn_flutter/l10n/app_localizations.dart'; import 'package:halkyon_learn_flutter/src/core/citation/model/source.dart'; -import 'package:halkyon_learn_flutter/src/core/content/content_blocks.dart'; -import 'package:halkyon_learn_flutter/src/core/content/rich_content.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_import.dart'; import 'package:halkyon_learn_flutter/src/feature/learning_path/model/exercise.dart'; diff --git a/apps/learn/halkyon_learn_flutter/test/feature/learning_path/data/payload_test.dart b/apps/learn/halkyon_learn_flutter/test/feature/learning_path/data/payload_test.dart index 19a6985f..33dafe97 100644 --- a/apps/learn/halkyon_learn_flutter/test/feature/learning_path/data/payload_test.dart +++ b/apps/learn/halkyon_learn_flutter/test/feature/learning_path/data/payload_test.dart @@ -2,9 +2,9 @@ import 'dart:convert'; import 'dart:typed_data'; import 'package:flutter_test/flutter_test.dart'; +import 'package:halkyon_editor/halkyon_editor.dart'; import 'package:halkyon_learn_flutter/src/core/citation/model/source.dart'; import 'package:halkyon_learn_flutter/src/core/content/payload.dart'; -import 'package:halkyon_learn_flutter/src/core/content/rich_content.dart'; import 'package:halkyon_learn_flutter/src/feature/learning_path/data/learning_path_payload.dart'; import 'package:halkyon_learn_flutter/src/feature/learning_path/data/learning_path_step_payload.dart'; import 'package:halkyon_learn_flutter/src/feature/learning_path/model/answer.dart'; diff --git a/apps/learn/halkyon_learn_flutter/test/feature/learning_path/model/step_merge_test.dart b/apps/learn/halkyon_learn_flutter/test/feature/learning_path/model/step_merge_test.dart index ef04d9da..5ff32cae 100644 --- a/apps/learn/halkyon_learn_flutter/test/feature/learning_path/model/step_merge_test.dart +++ b/apps/learn/halkyon_learn_flutter/test/feature/learning_path/model/step_merge_test.dart @@ -1,5 +1,5 @@ import 'package:flutter_test/flutter_test.dart'; -import 'package:halkyon_learn_flutter/src/core/content/rich_content.dart'; +import 'package:halkyon_editor/halkyon_editor.dart'; import 'package:halkyon_learn_flutter/src/feature/learning_path/model/exercise.dart'; import 'package:halkyon_learn_flutter/src/feature/learning_path/model/learning_path_step.dart'; import 'package:halkyon_learn_flutter/src/feature/learning_path/model/step_content.dart'; diff --git a/apps/learn/halkyon_learn_flutter/test/feature/learning_path/widget/path_screens_test.dart b/apps/learn/halkyon_learn_flutter/test/feature/learning_path/widget/path_screens_test.dart index 40422f68..874cd2e8 100644 --- a/apps/learn/halkyon_learn_flutter/test/feature/learning_path/widget/path_screens_test.dart +++ b/apps/learn/halkyon_learn_flutter/test/feature/learning_path/widget/path_screens_test.dart @@ -4,9 +4,9 @@ import 'dart:typed_data'; import 'package:flutter_test/flutter_test.dart'; import 'package:halkyon_app/testing.dart'; +import 'package:halkyon_editor/halkyon_editor.dart'; import 'package:halkyon_learn_flutter/src/core/citation/model/citation.dart'; import 'package:halkyon_learn_flutter/src/core/citation/model/source.dart'; -import 'package:halkyon_learn_flutter/src/core/content/rich_content.dart'; import 'package:halkyon_learn_flutter/src/core/device_settings/model/device_settings.dart'; import 'package:halkyon_learn_flutter/src/core/router/route_paths.dart'; import 'package:halkyon_learn_flutter/src/feature/learning_path/data/learning_path_repository.dart'; diff --git a/apps/learn/halkyon_learn_flutter/test/feature/note/bloc/note_editor_bloc_test.dart b/apps/learn/halkyon_learn_flutter/test/feature/note/bloc/note_editor_bloc_test.dart index ccff14b5..e4c16647 100644 --- a/apps/learn/halkyon_learn_flutter/test/feature/note/bloc/note_editor_bloc_test.dart +++ b/apps/learn/halkyon_learn_flutter/test/feature/note/bloc/note_editor_bloc_test.dart @@ -1,5 +1,5 @@ import 'package:flutter_test/flutter_test.dart'; -import 'package:halkyon_learn_flutter/src/core/content/rich_content.dart'; +import 'package:halkyon_editor/halkyon_editor.dart'; import 'package:halkyon_learn_flutter/src/core/editor/save_status.dart'; import 'package:halkyon_learn_flutter/src/feature/note/bloc/note_editor_bloc.dart'; import 'package:halkyon_learn_flutter/src/feature/note/data/note_payload.dart'; diff --git a/apps/learn/halkyon_learn_flutter/test/feature/note/bloc/note_list_bloc_test.dart b/apps/learn/halkyon_learn_flutter/test/feature/note/bloc/note_list_bloc_test.dart index cff3c80b..12b9e533 100644 --- a/apps/learn/halkyon_learn_flutter/test/feature/note/bloc/note_list_bloc_test.dart +++ b/apps/learn/halkyon_learn_flutter/test/feature/note/bloc/note_list_bloc_test.dart @@ -1,5 +1,5 @@ import 'package:flutter_test/flutter_test.dart'; -import 'package:halkyon_learn_flutter/src/core/content/rich_content.dart'; +import 'package:halkyon_editor/halkyon_editor.dart'; import 'package:halkyon_learn_flutter/src/feature/note/bloc/note_list_bloc.dart'; import 'package:halkyon_learn_flutter/src/feature/note/data/note_deletions.dart'; import 'package:halkyon_learn_flutter/src/feature/note/data/note_repository.dart'; diff --git a/apps/learn/halkyon_learn_flutter/test/feature/note/data/note_deletions_test.dart b/apps/learn/halkyon_learn_flutter/test/feature/note/data/note_deletions_test.dart index 4bf4307a..bae64c77 100644 --- a/apps/learn/halkyon_learn_flutter/test/feature/note/data/note_deletions_test.dart +++ b/apps/learn/halkyon_learn_flutter/test/feature/note/data/note_deletions_test.dart @@ -1,5 +1,5 @@ import 'package:flutter_test/flutter_test.dart'; -import 'package:halkyon_learn_flutter/src/core/content/rich_content.dart'; +import 'package:halkyon_editor/halkyon_editor.dart'; import 'package:halkyon_learn_flutter/src/feature/note/data/note_deletions.dart'; import 'package:halkyon_learn_flutter/src/feature/note/data/note_repository.dart'; import 'package:halkyon_learn_flutter/src/feature/note/model/note_content.dart'; diff --git a/apps/learn/halkyon_learn_flutter/test/feature/note/data/note_payload_test.dart b/apps/learn/halkyon_learn_flutter/test/feature/note/data/note_payload_test.dart index 06c754dc..23c89503 100644 --- a/apps/learn/halkyon_learn_flutter/test/feature/note/data/note_payload_test.dart +++ b/apps/learn/halkyon_learn_flutter/test/feature/note/data/note_payload_test.dart @@ -2,8 +2,8 @@ import 'dart:convert'; import 'dart:typed_data'; import 'package:flutter_test/flutter_test.dart'; +import 'package:halkyon_editor/halkyon_editor.dart'; import 'package:halkyon_learn_flutter/src/core/content/payload.dart'; -import 'package:halkyon_learn_flutter/src/core/content/rich_content.dart'; import 'package:halkyon_learn_flutter/src/feature/note/data/note_payload.dart'; import 'package:halkyon_learn_flutter/src/feature/note/model/note_content.dart'; diff --git a/apps/learn/halkyon_learn_flutter/test/feature/note/data/note_repository_test.dart b/apps/learn/halkyon_learn_flutter/test/feature/note/data/note_repository_test.dart index 204f0e7d..d2fef121 100644 --- a/apps/learn/halkyon_learn_flutter/test/feature/note/data/note_repository_test.dart +++ b/apps/learn/halkyon_learn_flutter/test/feature/note/data/note_repository_test.dart @@ -1,7 +1,7 @@ import 'package:clock/clock.dart'; import 'package:flutter_test/flutter_test.dart'; +import 'package:halkyon_editor/halkyon_editor.dart'; import 'package:halkyon_learn_flutter/src/core/content/payload.dart'; -import 'package:halkyon_learn_flutter/src/core/content/rich_content.dart'; import 'package:halkyon_learn_flutter/src/feature/note/data/note_payload.dart'; import 'package:halkyon_learn_flutter/src/feature/note/data/note_repository.dart'; import 'package:halkyon_learn_flutter/src/feature/note/model/note.dart'; diff --git a/apps/learn/halkyon_learn_flutter/test/feature/note/model/note_merge_test.dart b/apps/learn/halkyon_learn_flutter/test/feature/note/model/note_merge_test.dart index 551291fd..eb45ab4a 100644 --- a/apps/learn/halkyon_learn_flutter/test/feature/note/model/note_merge_test.dart +++ b/apps/learn/halkyon_learn_flutter/test/feature/note/model/note_merge_test.dart @@ -1,5 +1,5 @@ import 'package:flutter_test/flutter_test.dart'; -import 'package:halkyon_learn_flutter/src/core/content/rich_content.dart'; +import 'package:halkyon_editor/halkyon_editor.dart'; import 'package:halkyon_learn_flutter/src/feature/note/model/note_content.dart'; import 'package:halkyon_learn_flutter/src/feature/note/model/note_merge.dart'; import 'package:halkyon_learn_flutter/src/feature/note/model/note_revision.dart'; diff --git a/apps/learn/halkyon_learn_flutter/test/feature/note/widget/note_screens_test.dart b/apps/learn/halkyon_learn_flutter/test/feature/note/widget/note_screens_test.dart index f3324b4e..1b6a2a29 100644 --- a/apps/learn/halkyon_learn_flutter/test/feature/note/widget/note_screens_test.dart +++ b/apps/learn/halkyon_learn_flutter/test/feature/note/widget/note_screens_test.dart @@ -1,6 +1,6 @@ import 'package:flutter_test/flutter_test.dart'; -import 'package:halkyon_learn_flutter/src/core/content/rich_content.dart'; -import 'package:halkyon_learn_flutter/src/core/editor/rich_content_editor.dart'; +import 'package:halkyon_editor/halkyon_editor.dart'; +import 'package:halkyon_learn_flutter/src/core/editor/learn_content.dart'; import 'package:halkyon_learn_flutter/src/core/router/route_paths.dart'; import 'package:halkyon_learn_flutter/src/feature/note/data/note_payload.dart'; import 'package:halkyon_learn_flutter/src/feature/note/data/note_repository.dart'; @@ -111,7 +111,7 @@ void main() { await tester.pump(); await tester.pump(const Duration(seconds: 1)); await tester.pumpAndSettle(); - expect(find.byType(RichContentEditor), findsOneWidget); + expect(find.byType(LearnContentEditor), findsOneWidget); await tester.enterText(find.byType(TextField), 'Particles'); await tester.pump(); @@ -135,14 +135,14 @@ void main() { find.text('Particles'), find.widgetWithText(ActionChip, 'Add tags'), find.byType(Divider), - find.byType(RichContentEditor), + find.byType(LearnContentEditor), ]) { expect(find.descendant(of: card, matching: part), findsOneWidget); } final title = tester.getRect(find.text('Particles')); final tags = tester.getRect(find.text('Add tags')); final line = tester.getRect(find.byType(Divider)); - final text = tester.getRect(find.byType(RichContentEditor)); + final text = tester.getRect(find.byType(LearnContentEditor)); expect(title.top, lessThan(tags.top)); expect(tags.bottom, lessThanOrEqualTo(line.top)); expect(line.bottom, lessThanOrEqualTo(text.top)); diff --git a/apps/learn/halkyon_learn_flutter/test/feature/pdf_source/bloc/pdf_list_bloc_test.dart b/apps/learn/halkyon_learn_flutter/test/feature/pdf_source/bloc/pdf_list_bloc_test.dart index 56472e25..6661e01d 100644 --- a/apps/learn/halkyon_learn_flutter/test/feature/pdf_source/bloc/pdf_list_bloc_test.dart +++ b/apps/learn/halkyon_learn_flutter/test/feature/pdf_source/bloc/pdf_list_bloc_test.dart @@ -2,8 +2,8 @@ import 'dart:convert'; import 'dart:typed_data'; import 'package:flutter_test/flutter_test.dart'; +import 'package:halkyon_editor/halkyon_editor.dart'; import 'package:halkyon_learn_flutter/src/core/asset/data/asset_repository.dart'; -import 'package:halkyon_learn_flutter/src/core/content/rich_content.dart'; import 'package:halkyon_learn_flutter/src/feature/card/data/card_repository.dart'; import 'package:halkyon_learn_flutter/src/feature/card/model/card_content.dart'; import 'package:halkyon_learn_flutter/src/feature/note/data/note_repository.dart'; diff --git a/apps/learn/halkyon_learn_flutter/test/feature/pdf_source/widget/pdf_reader_test.dart b/apps/learn/halkyon_learn_flutter/test/feature/pdf_source/widget/pdf_reader_test.dart index 8cfeb946..f1af7299 100644 --- a/apps/learn/halkyon_learn_flutter/test/feature/pdf_source/widget/pdf_reader_test.dart +++ b/apps/learn/halkyon_learn_flutter/test/feature/pdf_source/widget/pdf_reader_test.dart @@ -4,11 +4,10 @@ import 'dart:typed_data'; import 'package:appflowy_editor/appflowy_editor.dart'; import 'package:flutter/gestures.dart'; import 'package:flutter_test/flutter_test.dart'; +import 'package:halkyon_editor/halkyon_editor.dart'; import 'package:halkyon_learn_flutter/src/core/asset/data/asset_repository.dart'; -import 'package:halkyon_learn_flutter/src/core/content/content_blocks.dart'; -import 'package:halkyon_learn_flutter/src/core/content/rich_content.dart'; import 'package:halkyon_learn_flutter/src/core/device_settings/model/device_settings.dart'; -import 'package:halkyon_learn_flutter/src/core/editor/rich_content_editor.dart'; +import 'package:halkyon_learn_flutter/src/core/editor/learn_content.dart'; import 'package:halkyon_learn_flutter/src/core/router/route_paths.dart'; import 'package:halkyon_learn_flutter/src/feature/card/data/card_repository.dart'; import 'package:halkyon_learn_flutter/src/feature/card/model/card_content.dart'; @@ -269,7 +268,7 @@ void main() { final editorState = tester .widget( find.descendant( - of: find.byType(RichContentEditor), + of: find.byType(LearnContentEditor), matching: find.byType(AppFlowyEditor), ), ) @@ -313,7 +312,7 @@ void main() { await tester.tap(find.text('Create flashcard')); await tester.pumpAndSettle(); final question = find.descendant( - of: find.byType(RichContentEditor).first, + of: find.byType(LearnContentEditor).first, matching: find.byType(AppFlowyEditor), ); await tester.tap(question); @@ -351,7 +350,7 @@ void main() { // made the editor scroll up and down for ever. await tester.tap( find.descendant( - of: find.byType(RichContentEditor).first, + of: find.byType(LearnContentEditor).first, matching: find.byType(Image), ), ); diff --git a/apps/learn/halkyon_learn_flutter/test/feature/publish/publish_bloc_test.dart b/apps/learn/halkyon_learn_flutter/test/feature/publish/publish_bloc_test.dart index 97108f50..b41975db 100644 --- a/apps/learn/halkyon_learn_flutter/test/feature/publish/publish_bloc_test.dart +++ b/apps/learn/halkyon_learn_flutter/test/feature/publish/publish_bloc_test.dart @@ -1,7 +1,7 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:halkyon_app/halkyon_app.dart'; import 'package:halkyon_app/testing.dart'; -import 'package:halkyon_learn_flutter/src/core/content/rich_content.dart'; +import 'package:halkyon_editor/halkyon_editor.dart'; import 'package:halkyon_learn_flutter/src/feature/card/data/card_payload.dart'; import 'package:halkyon_learn_flutter/src/feature/card/data/card_repository.dart'; import 'package:halkyon_learn_flutter/src/feature/card/model/card_content.dart'; diff --git a/apps/learn/halkyon_learn_flutter/test/feature/publish/publish_sheet_test.dart b/apps/learn/halkyon_learn_flutter/test/feature/publish/publish_sheet_test.dart index 157844aa..ca940e57 100644 --- a/apps/learn/halkyon_learn_flutter/test/feature/publish/publish_sheet_test.dart +++ b/apps/learn/halkyon_learn_flutter/test/feature/publish/publish_sheet_test.dart @@ -1,7 +1,7 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:halkyon_app/halkyon_app.dart'; import 'package:halkyon_app/testing.dart'; -import 'package:halkyon_learn_flutter/src/core/content/rich_content.dart'; +import 'package:halkyon_editor/halkyon_editor.dart'; import 'package:halkyon_learn_flutter/src/core/router/route_paths.dart'; import 'package:halkyon_learn_flutter/src/feature/card/data/card_payload.dart'; import 'package:halkyon_learn_flutter/src/feature/card/data/card_repository.dart'; diff --git a/apps/learn/halkyon_learn_flutter/test/feature/statistics/widget/statistics_view_test.dart b/apps/learn/halkyon_learn_flutter/test/feature/statistics/widget/statistics_view_test.dart index faad4802..951cf0ac 100644 --- a/apps/learn/halkyon_learn_flutter/test/feature/statistics/widget/statistics_view_test.dart +++ b/apps/learn/halkyon_learn_flutter/test/feature/statistics/widget/statistics_view_test.dart @@ -1,6 +1,6 @@ import 'package:clock/clock.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'package:halkyon_learn_flutter/src/core/content/rich_content.dart'; +import 'package:halkyon_editor/halkyon_editor.dart'; import 'package:halkyon_learn_flutter/src/feature/card/data/card_repository.dart'; import 'package:halkyon_learn_flutter/src/feature/card/model/card_content.dart'; import 'package:halkyon_learn_flutter/src/feature/statistics/widget/bar_chart.dart'; diff --git a/apps/learn/halkyon_learn_flutter/test/feature/study/bloc/study_session_bloc_test.dart b/apps/learn/halkyon_learn_flutter/test/feature/study/bloc/study_session_bloc_test.dart index 812b95bb..de8a0c90 100644 --- a/apps/learn/halkyon_learn_flutter/test/feature/study/bloc/study_session_bloc_test.dart +++ b/apps/learn/halkyon_learn_flutter/test/feature/study/bloc/study_session_bloc_test.dart @@ -2,7 +2,7 @@ import 'dart:math' as math; import 'package:clock/clock.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'package:halkyon_learn_flutter/src/core/content/rich_content.dart'; +import 'package:halkyon_editor/halkyon_editor.dart'; import 'package:halkyon_learn_flutter/src/feature/card/data/card_deletions.dart'; import 'package:halkyon_learn_flutter/src/feature/card/data/card_repository.dart'; import 'package:halkyon_learn_flutter/src/feature/card/model/card_content.dart'; diff --git a/apps/learn/halkyon_learn_flutter/test/feature/study/bloc/study_settings_bloc_test.dart b/apps/learn/halkyon_learn_flutter/test/feature/study/bloc/study_settings_bloc_test.dart index b62241e1..62c80640 100644 --- a/apps/learn/halkyon_learn_flutter/test/feature/study/bloc/study_settings_bloc_test.dart +++ b/apps/learn/halkyon_learn_flutter/test/feature/study/bloc/study_settings_bloc_test.dart @@ -1,5 +1,5 @@ import 'package:flutter_test/flutter_test.dart'; -import 'package:halkyon_learn_flutter/src/core/content/rich_content.dart'; +import 'package:halkyon_editor/halkyon_editor.dart'; import 'package:halkyon_learn_flutter/src/feature/card/data/card_repository.dart'; import 'package:halkyon_learn_flutter/src/feature/card/model/card_content.dart'; import 'package:halkyon_learn_flutter/src/feature/settings/data/settings_repository.dart'; diff --git a/apps/learn/halkyon_learn_flutter/test/feature/study/model/card_study_mode_test.dart b/apps/learn/halkyon_learn_flutter/test/feature/study/model/card_study_mode_test.dart index 2526bb97..7955795d 100644 --- a/apps/learn/halkyon_learn_flutter/test/feature/study/model/card_study_mode_test.dart +++ b/apps/learn/halkyon_learn_flutter/test/feature/study/model/card_study_mode_test.dart @@ -1,5 +1,5 @@ import 'package:flutter_test/flutter_test.dart'; -import 'package:halkyon_learn_flutter/src/core/content/rich_content.dart'; +import 'package:halkyon_editor/halkyon_editor.dart'; import 'package:halkyon_learn_flutter/src/feature/card/model/card_content.dart'; import 'package:halkyon_learn_flutter/src/feature/study/model/card_study_mode.dart'; import 'package:halkyon_learn_flutter/src/feature/study/model/study_options.dart'; diff --git a/apps/learn/halkyon_learn_flutter/test/feature/study/model/example_word_test.dart b/apps/learn/halkyon_learn_flutter/test/feature/study/model/example_word_test.dart index c506cebd..13692799 100644 --- a/apps/learn/halkyon_learn_flutter/test/feature/study/model/example_word_test.dart +++ b/apps/learn/halkyon_learn_flutter/test/feature/study/model/example_word_test.dart @@ -1,5 +1,5 @@ import 'package:flutter_test/flutter_test.dart'; -import 'package:halkyon_learn_flutter/src/core/content/rich_content.dart'; +import 'package:halkyon_editor/halkyon_editor.dart'; import 'package:halkyon_learn_flutter/src/feature/card/model/card_content.dart'; import 'package:halkyon_learn_flutter/src/feature/study/model/example_word.dart'; diff --git a/apps/learn/halkyon_learn_flutter/test/feature/study/model/vocabulary_quiz_test.dart b/apps/learn/halkyon_learn_flutter/test/feature/study/model/vocabulary_quiz_test.dart index 5b7037ee..0ba932fc 100644 --- a/apps/learn/halkyon_learn_flutter/test/feature/study/model/vocabulary_quiz_test.dart +++ b/apps/learn/halkyon_learn_flutter/test/feature/study/model/vocabulary_quiz_test.dart @@ -1,7 +1,7 @@ import 'dart:math' as math; import 'package:flutter_test/flutter_test.dart'; -import 'package:halkyon_learn_flutter/src/core/content/rich_content.dart'; +import 'package:halkyon_editor/halkyon_editor.dart'; import 'package:halkyon_learn_flutter/src/feature/card/model/card_content.dart'; import 'package:halkyon_learn_flutter/src/feature/study/model/study_options.dart'; import 'package:halkyon_learn_flutter/src/feature/study/model/vocabulary_quiz.dart'; diff --git a/apps/learn/halkyon_learn_flutter/test/feature/study/widget/study_button_block_test.dart b/apps/learn/halkyon_learn_flutter/test/feature/study/widget/study_button_block_test.dart index 491f7148..63c96cae 100644 --- a/apps/learn/halkyon_learn_flutter/test/feature/study/widget/study_button_block_test.dart +++ b/apps/learn/halkyon_learn_flutter/test/feature/study/widget/study_button_block_test.dart @@ -1,8 +1,6 @@ import 'package:appflowy_editor/appflowy_editor.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'package:halkyon_learn_flutter/src/core/content/content_blocks.dart'; -import 'package:halkyon_learn_flutter/src/core/content/rich_content.dart'; -import 'package:halkyon_learn_flutter/src/core/editor/rich_content_document.dart'; +import 'package:halkyon_editor/halkyon_editor.dart'; import 'package:halkyon_learn_flutter/src/core/router/route_paths.dart'; import 'package:halkyon_learn_flutter/src/feature/card/data/card_repository.dart'; import 'package:halkyon_learn_flutter/src/feature/card/model/card_content.dart'; diff --git a/apps/learn/halkyon_learn_flutter/test/feature/study/widget/study_screen_test.dart b/apps/learn/halkyon_learn_flutter/test/feature/study/widget/study_screen_test.dart index 64be7594..6ebb5625 100644 --- a/apps/learn/halkyon_learn_flutter/test/feature/study/widget/study_screen_test.dart +++ b/apps/learn/halkyon_learn_flutter/test/feature/study/widget/study_screen_test.dart @@ -1,5 +1,5 @@ import 'package:flutter_test/flutter_test.dart'; -import 'package:halkyon_learn_flutter/src/core/content/rich_content.dart'; +import 'package:halkyon_editor/halkyon_editor.dart'; import 'package:halkyon_learn_flutter/src/core/router/route_paths.dart'; import 'package:halkyon_learn_flutter/src/feature/card/data/card_repository.dart'; import 'package:halkyon_learn_flutter/src/feature/card/model/card_content.dart'; diff --git a/apps/learn/halkyon_learn_flutter/test/feature/studyset/bloc/exam_cubit_test.dart b/apps/learn/halkyon_learn_flutter/test/feature/studyset/bloc/exam_cubit_test.dart index 6c47897e..141f3778 100644 --- a/apps/learn/halkyon_learn_flutter/test/feature/studyset/bloc/exam_cubit_test.dart +++ b/apps/learn/halkyon_learn_flutter/test/feature/studyset/bloc/exam_cubit_test.dart @@ -3,8 +3,8 @@ import 'dart:async'; import 'package:clock/clock.dart'; import 'package:fake_async/fake_async.dart'; import 'package:flutter_test/flutter_test.dart'; +import 'package:halkyon_editor/halkyon_editor.dart'; import 'package:halkyon_learn_flutter/src/core/asset/data/asset_repository.dart'; -import 'package:halkyon_learn_flutter/src/core/content/rich_content.dart'; import 'package:halkyon_learn_flutter/src/feature/card/data/card_repository.dart'; import 'package:halkyon_learn_flutter/src/feature/card/model/card_content.dart'; import 'package:halkyon_learn_flutter/src/feature/learning_path/data/learning_path_repository.dart'; diff --git a/apps/learn/halkyon_learn_flutter/test/feature/studyset/bloc/studyset_activity_cubit_test.dart b/apps/learn/halkyon_learn_flutter/test/feature/studyset/bloc/studyset_activity_cubit_test.dart index 5fcc4993..33e10c9a 100644 --- a/apps/learn/halkyon_learn_flutter/test/feature/studyset/bloc/studyset_activity_cubit_test.dart +++ b/apps/learn/halkyon_learn_flutter/test/feature/studyset/bloc/studyset_activity_cubit_test.dart @@ -1,6 +1,6 @@ import 'package:clock/clock.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'package:halkyon_learn_flutter/src/core/content/rich_content.dart'; +import 'package:halkyon_editor/halkyon_editor.dart'; import 'package:halkyon_learn_flutter/src/feature/card/data/card_repository.dart'; import 'package:halkyon_learn_flutter/src/feature/card/model/card_content.dart'; import 'package:halkyon_learn_flutter/src/feature/study/data/review_repository.dart'; diff --git a/apps/learn/halkyon_learn_flutter/test/feature/studyset/bloc/studyset_list_bloc_test.dart b/apps/learn/halkyon_learn_flutter/test/feature/studyset/bloc/studyset_list_bloc_test.dart index d355cf20..8009dcea 100644 --- a/apps/learn/halkyon_learn_flutter/test/feature/studyset/bloc/studyset_list_bloc_test.dart +++ b/apps/learn/halkyon_learn_flutter/test/feature/studyset/bloc/studyset_list_bloc_test.dart @@ -2,8 +2,8 @@ import 'dart:typed_data'; import 'package:clock/clock.dart'; import 'package:flutter_test/flutter_test.dart'; +import 'package:halkyon_editor/halkyon_editor.dart'; import 'package:halkyon_learn_flutter/src/core/asset/data/asset_repository.dart'; -import 'package:halkyon_learn_flutter/src/core/content/rich_content.dart'; import 'package:halkyon_learn_flutter/src/feature/card/data/card_repository.dart'; import 'package:halkyon_learn_flutter/src/feature/card/model/card_content.dart'; import 'package:halkyon_learn_flutter/src/feature/learning_path/data/learning_path_repository.dart'; diff --git a/apps/learn/halkyon_learn_flutter/test/feature/studyset/data/studyset_deletions_test.dart b/apps/learn/halkyon_learn_flutter/test/feature/studyset/data/studyset_deletions_test.dart index b767f4e3..eadc676b 100644 --- a/apps/learn/halkyon_learn_flutter/test/feature/studyset/data/studyset_deletions_test.dart +++ b/apps/learn/halkyon_learn_flutter/test/feature/studyset/data/studyset_deletions_test.dart @@ -4,8 +4,8 @@ import 'dart:typed_data'; import 'package:clock/clock.dart'; import 'package:flutter_test/flutter_test.dart'; +import 'package:halkyon_editor/halkyon_editor.dart'; import 'package:halkyon_learn_flutter/src/core/asset/data/asset_repository.dart'; -import 'package:halkyon_learn_flutter/src/core/content/rich_content.dart'; import 'package:halkyon_learn_flutter/src/feature/card/data/card_repository.dart'; import 'package:halkyon_learn_flutter/src/feature/card/model/card_content.dart'; import 'package:halkyon_learn_flutter/src/feature/learning_path/data/learning_path_repository.dart'; diff --git a/apps/learn/halkyon_learn_flutter/test/feature/studyset/data/studyset_payload_test.dart b/apps/learn/halkyon_learn_flutter/test/feature/studyset/data/studyset_payload_test.dart index 7296f84e..309a3e53 100644 --- a/apps/learn/halkyon_learn_flutter/test/feature/studyset/data/studyset_payload_test.dart +++ b/apps/learn/halkyon_learn_flutter/test/feature/studyset/data/studyset_payload_test.dart @@ -2,9 +2,9 @@ import 'dart:convert'; import 'dart:typed_data'; import 'package:flutter_test/flutter_test.dart'; +import 'package:halkyon_editor/halkyon_editor.dart'; import 'package:halkyon_learn_flutter/src/core/citation/model/source.dart'; import 'package:halkyon_learn_flutter/src/core/content/payload.dart'; -import 'package:halkyon_learn_flutter/src/core/content/rich_content.dart'; import 'package:halkyon_learn_flutter/src/feature/study/model/studyset_study_options.dart'; import 'package:halkyon_learn_flutter/src/feature/studyset/data/studyset_payload.dart'; import 'package:halkyon_learn_flutter/src/feature/studyset/model/exam_plan.dart'; diff --git a/apps/learn/halkyon_learn_flutter/test/feature/studyset/data/studyset_progress_repository_test.dart b/apps/learn/halkyon_learn_flutter/test/feature/studyset/data/studyset_progress_repository_test.dart index 4754ce32..12c16c7b 100644 --- a/apps/learn/halkyon_learn_flutter/test/feature/studyset/data/studyset_progress_repository_test.dart +++ b/apps/learn/halkyon_learn_flutter/test/feature/studyset/data/studyset_progress_repository_test.dart @@ -1,7 +1,7 @@ import 'package:clock/clock.dart'; import 'package:flutter_test/flutter_test.dart'; +import 'package:halkyon_editor/halkyon_editor.dart'; import 'package:halkyon_learn_flutter/src/core/asset/data/asset_repository.dart'; -import 'package:halkyon_learn_flutter/src/core/content/rich_content.dart'; import 'package:halkyon_learn_flutter/src/feature/card/data/card_repository.dart'; import 'package:halkyon_learn_flutter/src/feature/card/model/card_content.dart'; import 'package:halkyon_learn_flutter/src/feature/learning_path/data/learning_path_repository.dart'; diff --git a/apps/learn/halkyon_learn_flutter/test/feature/studyset/data/studyset_repository_test.dart b/apps/learn/halkyon_learn_flutter/test/feature/studyset/data/studyset_repository_test.dart index cdaf2c6b..c4461fdd 100644 --- a/apps/learn/halkyon_learn_flutter/test/feature/studyset/data/studyset_repository_test.dart +++ b/apps/learn/halkyon_learn_flutter/test/feature/studyset/data/studyset_repository_test.dart @@ -3,8 +3,8 @@ import 'dart:typed_data'; import 'package:clock/clock.dart'; import 'package:flutter_test/flutter_test.dart'; +import 'package:halkyon_editor/halkyon_editor.dart'; import 'package:halkyon_learn_flutter/src/core/content/payload.dart'; -import 'package:halkyon_learn_flutter/src/core/content/rich_content.dart'; import 'package:halkyon_learn_flutter/src/feature/studyset/data/studyset_payload.dart'; import 'package:halkyon_learn_flutter/src/feature/studyset/data/studyset_repository.dart'; import 'package:halkyon_learn_flutter/src/feature/studyset/model/studyset.dart'; diff --git a/apps/learn/halkyon_learn_flutter/test/feature/studyset/model/studyset_tree_test.dart b/apps/learn/halkyon_learn_flutter/test/feature/studyset/model/studyset_tree_test.dart index ee683ab4..9d8dc3de 100644 --- a/apps/learn/halkyon_learn_flutter/test/feature/studyset/model/studyset_tree_test.dart +++ b/apps/learn/halkyon_learn_flutter/test/feature/studyset/model/studyset_tree_test.dart @@ -1,5 +1,5 @@ import 'package:flutter_test/flutter_test.dart'; -import 'package:halkyon_learn_flutter/src/core/content/rich_content.dart'; +import 'package:halkyon_editor/halkyon_editor.dart'; import 'package:halkyon_learn_flutter/src/feature/studyset/model/studyset.dart'; import 'package:halkyon_learn_flutter/src/feature/studyset/model/studyset_content.dart'; import 'package:halkyon_learn_flutter/src/feature/studyset/model/studyset_tree.dart'; diff --git a/apps/learn/halkyon_learn_flutter/test/feature/studyset/widget/library_screen_test.dart b/apps/learn/halkyon_learn_flutter/test/feature/studyset/widget/library_screen_test.dart index 2d18333e..50e583ad 100644 --- a/apps/learn/halkyon_learn_flutter/test/feature/studyset/widget/library_screen_test.dart +++ b/apps/learn/halkyon_learn_flutter/test/feature/studyset/widget/library_screen_test.dart @@ -3,8 +3,8 @@ import 'dart:async'; import 'package:clock/clock.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:halkyon_app/testing.dart'; +import 'package:halkyon_editor/halkyon_editor.dart'; import 'package:halkyon_learn_flutter/src/core/content/published_ref.dart'; -import 'package:halkyon_learn_flutter/src/core/content/rich_content.dart'; import 'package:halkyon_learn_flutter/src/core/router/route_paths.dart'; import 'package:halkyon_learn_flutter/src/feature/card/data/card_repository.dart'; import 'package:halkyon_learn_flutter/src/feature/card/model/card_content.dart'; diff --git a/apps/learn/halkyon_learn_flutter/test/feature/studyset/widget/studyset_card_test.dart b/apps/learn/halkyon_learn_flutter/test/feature/studyset/widget/studyset_card_test.dart index ff36021c..d4cf155c 100644 --- a/apps/learn/halkyon_learn_flutter/test/feature/studyset/widget/studyset_card_test.dart +++ b/apps/learn/halkyon_learn_flutter/test/feature/studyset/widget/studyset_card_test.dart @@ -1,7 +1,7 @@ import 'package:flutter/gestures.dart'; import 'package:flutter_test/flutter_test.dart'; +import 'package:halkyon_editor/halkyon_editor.dart'; import 'package:halkyon_learn_flutter/l10n/app_localizations.dart'; -import 'package:halkyon_learn_flutter/src/core/content/rich_content.dart'; import 'package:halkyon_learn_flutter/src/core/l10n/app_languages.dart'; import 'package:halkyon_learn_flutter/src/core/theme/learn_theme.dart'; import 'package:halkyon_learn_flutter/src/feature/study/model/study_progress.dart'; diff --git a/apps/learn/halkyon_learn_flutter/test/feature/studyset/widget/studyset_screen_test.dart b/apps/learn/halkyon_learn_flutter/test/feature/studyset/widget/studyset_screen_test.dart index 1a12b873..97fdd4ed 100644 --- a/apps/learn/halkyon_learn_flutter/test/feature/studyset/widget/studyset_screen_test.dart +++ b/apps/learn/halkyon_learn_flutter/test/feature/studyset/widget/studyset_screen_test.dart @@ -4,9 +4,9 @@ import 'package:flutter/rendering.dart'; import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:halkyon_app/testing.dart'; +import 'package:halkyon_editor/halkyon_editor.dart'; import 'package:halkyon_learn_flutter/l10n/app_localizations.dart'; import 'package:halkyon_learn_flutter/src/core/citation/model/source.dart'; -import 'package:halkyon_learn_flutter/src/core/content/rich_content.dart'; import 'package:halkyon_learn_flutter/src/core/dependencies/person_dependencies.dart'; import 'package:halkyon_learn_flutter/src/core/router/route_paths.dart'; import 'package:halkyon_learn_flutter/src/feature/card/data/card_repository.dart'; diff --git a/apps/learn/halkyon_learn_flutter/test/feature/tag/widget/tag_widgets_test.dart b/apps/learn/halkyon_learn_flutter/test/feature/tag/widget/tag_widgets_test.dart index ab59aca9..07a1d1ed 100644 --- a/apps/learn/halkyon_learn_flutter/test/feature/tag/widget/tag_widgets_test.dart +++ b/apps/learn/halkyon_learn_flutter/test/feature/tag/widget/tag_widgets_test.dart @@ -1,5 +1,5 @@ import 'package:flutter_test/flutter_test.dart'; -import 'package:halkyon_learn_flutter/src/core/content/rich_content.dart'; +import 'package:halkyon_editor/halkyon_editor.dart'; import 'package:halkyon_learn_flutter/src/core/router/route_paths.dart'; import 'package:halkyon_learn_flutter/src/feature/card/data/card_repository.dart'; import 'package:halkyon_learn_flutter/src/feature/card/model/card_content.dart'; diff --git a/apps/learn/halkyon_learn_flutter/test/support/fake_discussion_gateway.dart b/apps/learn/halkyon_learn_flutter/test/support/fake_discussion_gateway.dart index 3bccc2f2..12495fa3 100644 --- a/apps/learn/halkyon_learn_flutter/test/support/fake_discussion_gateway.dart +++ b/apps/learn/halkyon_learn_flutter/test/support/fake_discussion_gateway.dart @@ -1,4 +1,4 @@ -import 'package:halkyon_learn_flutter/src/core/content/rich_content.dart'; +import 'package:halkyon_editor/halkyon_editor.dart'; import 'package:halkyon_learn_flutter/src/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'; diff --git a/apps/learn/halkyon_learn_flutter/test/support/fake_publish_gateway.dart b/apps/learn/halkyon_learn_flutter/test/support/fake_publish_gateway.dart index f4777761..57b6dbb2 100644 --- a/apps/learn/halkyon_learn_flutter/test/support/fake_publish_gateway.dart +++ b/apps/learn/halkyon_learn_flutter/test/support/fake_publish_gateway.dart @@ -1,4 +1,4 @@ -import 'package:halkyon_learn_flutter/src/core/content/rich_content.dart'; +import 'package:halkyon_editor/halkyon_editor.dart'; import 'package:halkyon_learn_flutter/src/feature/publish/data/publish_gateway.dart'; import 'package:halkyon_learn_flutter/src/feature/publish/model/publication.dart'; diff --git a/apps/learn/halkyon_learn_flutter/test/sync/card_sync_test.dart b/apps/learn/halkyon_learn_flutter/test/sync/card_sync_test.dart index 0443a3aa..dd2516da 100644 --- a/apps/learn/halkyon_learn_flutter/test/sync/card_sync_test.dart +++ b/apps/learn/halkyon_learn_flutter/test/sync/card_sync_test.dart @@ -10,7 +10,7 @@ library; import 'dart:io'; import 'package:flutter_test/flutter_test.dart'; -import 'package:halkyon_learn_flutter/src/core/content/rich_content.dart'; +import 'package:halkyon_editor/halkyon_editor.dart'; import 'package:halkyon_learn_flutter/src/feature/card/model/card_content.dart'; import 'package:halkyon_learn_flutter/src/feature/study/model/card_schedule.dart'; import 'package:halkyon_learn_flutter/src/feature/study/model/rating.dart'; diff --git a/apps/learn/halkyon_learn_flutter/test/sync/note_sync_test.dart b/apps/learn/halkyon_learn_flutter/test/sync/note_sync_test.dart index 26ac1bf3..6547ca37 100644 --- a/apps/learn/halkyon_learn_flutter/test/sync/note_sync_test.dart +++ b/apps/learn/halkyon_learn_flutter/test/sync/note_sync_test.dart @@ -10,7 +10,7 @@ library; import 'dart:io'; import 'package:flutter_test/flutter_test.dart'; -import 'package:halkyon_learn_flutter/src/core/content/rich_content.dart'; +import 'package:halkyon_editor/halkyon_editor.dart'; import 'package:halkyon_learn_flutter/src/feature/note/model/note_content.dart'; import 'package:halkyon_learn_flutter/src/feature/studyset/model/studyset_content.dart'; import 'package:serverpod_auth_core_client/serverpod_auth_core_client.dart'; diff --git a/apps/learn/halkyon_learn_flutter/test/sync/publish_sync_test.dart b/apps/learn/halkyon_learn_flutter/test/sync/publish_sync_test.dart index 29d1d5df..648121bc 100644 --- a/apps/learn/halkyon_learn_flutter/test/sync/publish_sync_test.dart +++ b/apps/learn/halkyon_learn_flutter/test/sync/publish_sync_test.dart @@ -22,7 +22,7 @@ import 'dart:math'; import 'package:flutter_test/flutter_test.dart'; import 'package:halkyon_app/halkyon_app.dart'; -import 'package:halkyon_learn_flutter/src/core/content/rich_content.dart'; +import 'package:halkyon_editor/halkyon_editor.dart'; import 'package:halkyon_learn_flutter/src/feature/card/model/card.dart'; import 'package:halkyon_learn_flutter/src/feature/card/model/card_content.dart'; import 'package:halkyon_learn_flutter/src/feature/explore/data/copy_updater.dart'; diff --git a/apps/learn/halkyon_learn_flutter/test/sync/tag_sync_test.dart b/apps/learn/halkyon_learn_flutter/test/sync/tag_sync_test.dart index 30de541b..e2ba4912 100644 --- a/apps/learn/halkyon_learn_flutter/test/sync/tag_sync_test.dart +++ b/apps/learn/halkyon_learn_flutter/test/sync/tag_sync_test.dart @@ -12,7 +12,7 @@ library; import 'dart:io'; import 'package:flutter_test/flutter_test.dart'; -import 'package:halkyon_learn_flutter/src/core/content/rich_content.dart'; +import 'package:halkyon_editor/halkyon_editor.dart'; import 'package:halkyon_learn_flutter/src/feature/card/model/card_content.dart'; import 'package:halkyon_learn_flutter/src/feature/home/model/study_goal.dart'; import 'package:halkyon_learn_flutter/src/feature/settings/model/person_settings.dart'; diff --git a/docs/adr/0033-halkyon-todo.md b/docs/adr/0033-halkyon-todo.md index 7a39c286..851ba813 100644 --- a/docs/adr/0033-halkyon-todo.md +++ b/docs/adr/0033-halkyon-todo.md @@ -731,6 +731,10 @@ Comments are kept in their own sealed table, `task_comment`, and which task one Reporting is worked out on the device from what tasks keep (`completedAt`, `completions`), so no event log exists and a task deleted leaves the history and the counts, which can shorten a streak already shown. Goals start at 5 a day and 30 a week with Saturday and Sunday off, as Todoist's, and live in the sealed settings row, synced. Days off and a vacation break no streak, and a goal reached on one still counts; vacations stay kept once over, or a streak across one would break the day it ended. Days off changed apply to the past as well, since only one set is kept. A day counts by the device's time zone at reading. The card on Today says the goal was reached once a day, marked in the settings so a second device doesn't repeat it; it plays nothing and shares nothing. +**D37. The editor's general parts become a shared package** (the developer's, 6 Nov 2026). Comments and descriptions in Todo take rich text, stored as the editor's document JSON, not as Markdown (task names stay plain). Two apps storing the same documents need the same editor, so `packages/halkyon_editor` is made now rather than Todo writing a second one or depending on Learn. It holds, moved with their history and their tests: `RichContent` and `content_blocks.dart`, the document's conversion, `RichContentEditor`, `RichContentView`, the format bar, `EditorBlocks`, the editor's look, the clipboard, `AtomicBlock`, the Insert menu, links, superscript and subscript, with their strings in the same words (its own gen-l10n, `HalkyonEditorLocalizations`; only `commonCancel`, used elsewhere, stayed in Learn's too). `content_blocks.dart` names Learn's blocks too, since `RichContent.plainText` reads them and a block's type is one format for good, whichever app wrote it. + +What is Learn's alone stayed in Learn, on top of the package: formula, callout, spoiler, file and YouTube blocks, ruby, Markdown, `rich_text_screen`, `save_status`, `core_blocks`, the study button, grammar. Nothing in the package may know them, so what the editor took from them directly is inverted: `EditorBlocks` gains `textMarks` (ruby and formulas drawn within lines), `formatActions` (their buttons, in the same place, after subscript) and `onEditorClosed` (a reading shown over the pointer taken away), and `RichContentEditor` a `wrapEditor` (the grammar host). An `InsertableBlock`'s name now takes the `BuildContext`, not Learn's `AppLocalizations`, and `offering` narrows what an editor offers keeping the rest (the discussion composer). Learn's screens use `LearnContentEditor` and `LearnContentView`, thin wrappers in `core/editor/learn_content.dart` passing Learn's blocks and turning `grammar` into the wrapper; a global default the app sets instead would have left every widget test without Learn's blocks, and the package's own default is `basicBlocks`, so that Todo gets a working editor with no setup. `RichContent` stays pure Dart in a Flutter package: nothing outside the apps reads it today; a server that had to would move it to a pure Dart package then. + ## Steps 1. **The frame**: go_router and `NavigationFrame` with the four tabs (empty screens that diff --git a/packages/halkyon_editor/.gitignore b/packages/halkyon_editor/.gitignore new file mode 100644 index 00000000..dd5eb989 --- /dev/null +++ b/packages/halkyon_editor/.gitignore @@ -0,0 +1,31 @@ +# Miscellaneous +*.class +*.log +*.pyc +*.swp +.DS_Store +.atom/ +.buildlog/ +.history +.svn/ +migrate_working_dir/ + +# IntelliJ related +*.iml +*.ipr +*.iws +.idea/ + +# The .vscode folder contains launch configuration and tasks you configure in +# VS Code which you may wish to be included in version control, so this line +# is commented out by default. +#.vscode/ + +# Flutter/Dart/Pub related +# Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock. +/pubspec.lock +**/doc/api/ +.dart_tool/ +.flutter-plugins-dependencies +/build/ +/coverage/ diff --git a/packages/halkyon_editor/CHANGELOG.md b/packages/halkyon_editor/CHANGELOG.md new file mode 100644 index 00000000..70af2f61 --- /dev/null +++ b/packages/halkyon_editor/CHANGELOG.md @@ -0,0 +1,7 @@ +## 0.1.0 + +* The general parts of Halkyon Learn's editor, for every Halkyon app: + `RichContent`, `RichContentEditor`, `RichContentView`, `FormatBar`, + `EditorBlocks` with `basicBlocks` and what an app adds to them, `AtomicBlock`, + `EditorClipboard`, links, superscript and subscript, with their texts + (English, German). diff --git a/packages/halkyon_editor/README.md b/packages/halkyon_editor/README.md new file mode 100644 index 00000000..54c49324 --- /dev/null +++ b/packages/halkyon_editor/README.md @@ -0,0 +1,65 @@ +# halkyon_editor + +Halkyon's rich text, shared by its apps: the document as an app keeps it, the +one editor and the one read-only view of it, built on +[`appflowy_editor`](https://github.com/AppFlowy-IO/appflowy-editor) (our fork, +pinned, as every app's), with their texts in British English, American English +and German. Moved out of Halkyon Learn on 6 Nov 2026, when Halkyon Todo took +editor documents too (ADR 0033, D37). + +## Using it + +An app installs `HalkyonEditorLocalizations.delegate` beside its other +delegates, and halkyon_ui's, whose sheets the Insert menu opens in. + +```dart +// Stored: RichContent is the editor's document JSON and its format's version. +final content = RichContent.plain('Buy milk'); // or RichContent.empty() +content.plainText; // 'Buy milk' +content.isBlank; // false +final stored = content.toJson(); +RichContent.fromJson(stored); + +RichContentEditor( + initialContent: content, + onChanged: (content) => save(content), + formatBar: true, +); +RichContentView(content: content); +``` + +## What it holds + +- **`RichContent`** (`lib/src/content/`): pure Dart, the document in JSON. + `content_blocks.dart` names every block's type and attributes across the + apps, for `plainText` and for what a block's type is called for good. +- **`RichContentEditor`, `RichContentView`** (`lib/src/editor/`): the editor, + with the `FormatBar` docked below it while it has the focus, copying and + pasting that keep the formats (`EditorClipboard`), links (`editLink`, Ctrl+K) + and superscript and subscript; the view, display only, taking the height of + its content. +- **`EditorBlocks`**: what an editor knows. `basicBlocks` is what it knows + without an app (the editor's own builders; headings, lists, a checklist, a + quote, a divider and a table to insert). An app adds its own blocks, and + passes them as `blocks:` or through `EditorBlocksScope`, with: + - `builders`, `insertable` (the Insert sheet and the desktop's `/` menu show + the same list) and `shortcuts`; + - `textMarks`, how marks of its own are drawn, each wrapping the one before + it, and `formatActions`, their buttons, after subscript; + - `onEditorClosed`, for what its text left outside the editor. + `plus` joins two sets, `offering` narrows what is offered and nothing else. +- **`AtomicBlock`, `BlockNotice`**: for an app's block taken whole, selected + and removed as one. A type nothing knows shows as needing a newer app, and + stays as it is. + +`RichContentEditor`'s `wrapEditor` puts what an app adds around the editor +itself, knowing its state and focus (Learn's check of the spelling). + +Editors are tested as in Learn: host one in a scrollable body with a slot of +fixed height, `pump(const Duration(seconds: 1))` after changes, and find its +text with `findRichText: true`. + +## Licence + +GNU Affero General Public License, version 3 or any later version +(`AGPL-3.0-or-later`), as the other packages of the apps; see [LICENSE](LICENSE). diff --git a/packages/halkyon_editor/lib/halkyon_editor.dart b/packages/halkyon_editor/lib/halkyon_editor.dart index 2175fe41..ec30a202 100644 --- a/packages/halkyon_editor/lib/halkyon_editor.dart +++ b/packages/halkyon_editor/lib/halkyon_editor.dart @@ -1,3 +1,19 @@ /// Halkyon's rich text: the editor, the read-only view and the document /// they keep. library; + +export 'l10n/halkyon_editor_localizations.dart'; +export 'src/content/content_blocks.dart'; +export 'src/content/rich_content.dart'; +export 'src/editor/atomic_block.dart'; +export 'src/editor/basic_blocks.dart'; +export 'src/editor/block_menu.dart'; +export 'src/editor/editor_blocks.dart'; +export 'src/editor/editor_clipboard.dart'; +export 'src/editor/editor_style.dart'; +export 'src/editor/format_bar.dart'; +export 'src/editor/link.dart'; +export 'src/editor/rich_content_document.dart'; +export 'src/editor/rich_content_editor.dart'; +export 'src/editor/rich_content_view.dart'; +export 'src/editor/scripts.dart'; diff --git a/apps/learn/halkyon_learn_flutter/lib/src/core/content/content_blocks.dart b/packages/halkyon_editor/lib/src/content/content_blocks.dart similarity index 100% rename from apps/learn/halkyon_learn_flutter/lib/src/core/content/content_blocks.dart rename to packages/halkyon_editor/lib/src/content/content_blocks.dart diff --git a/apps/learn/halkyon_learn_flutter/lib/src/core/content/rich_content.dart b/packages/halkyon_editor/lib/src/content/rich_content.dart similarity index 98% rename from apps/learn/halkyon_learn_flutter/lib/src/core/content/rich_content.dart rename to packages/halkyon_editor/lib/src/content/rich_content.dart index 7f7ddfec..7bcd07a7 100644 --- a/apps/learn/halkyon_learn_flutter/lib/src/core/content/rich_content.dart +++ b/packages/halkyon_editor/lib/src/content/rich_content.dart @@ -1,12 +1,12 @@ import 'package:equatable/equatable.dart'; -import 'package:halkyon_learn_flutter/src/core/content/content_blocks.dart'; +import 'package:halkyon_editor/src/content/content_blocks.dart'; /// Rich text as the editor stores it: the editor's document JSON plus the /// version of that format, so stored content can be migrated when it changes. /// /// Pure Dart on purpose: models hold it, the data layer encodes it into the /// content payload, and the server can read it without Flutter. The editor -/// converts it to and from its own document type in `core/editor`. +/// converts it to and from its own document type in `rich_content_document.dart`. /// /// Made from the JSON the editor writes for a document, which it keeps as a /// deeply unmodifiable copy. diff --git a/apps/learn/halkyon_learn_flutter/lib/src/core/editor/atomic_block.dart b/packages/halkyon_editor/lib/src/editor/atomic_block.dart similarity index 100% rename from apps/learn/halkyon_learn_flutter/lib/src/core/editor/atomic_block.dart rename to packages/halkyon_editor/lib/src/editor/atomic_block.dart diff --git a/packages/halkyon_editor/lib/src/editor/basic_blocks.dart b/packages/halkyon_editor/lib/src/editor/basic_blocks.dart new file mode 100644 index 00000000..181701d8 --- /dev/null +++ b/packages/halkyon_editor/lib/src/editor/basic_blocks.dart @@ -0,0 +1,95 @@ +import 'package:appflowy_editor/appflowy_editor.dart'; +import 'package:halkyon_editor/l10n/halkyon_editor_localizations.dart'; +import 'package:halkyon_editor/src/editor/editor_blocks.dart'; +import 'package:lucide_icons_flutter/lucide_icons.dart'; +import 'package:material_ui/material_ui.dart'; + +/// The blocks every editor knows, an app's own or none beside them: the +/// editor's own, and those of them people can insert. +final basicBlocks = EditorBlocks( + builders: standardBlockComponentBuilderMap, + insertable: [ + for (final level in [1, 2, 3]) + _textBlock( + name: (l10n) => l10n.blockHeading(level), + icon: const [ + LucideIcons.heading1, + LucideIcons.heading2, + LucideIcons.heading3, + ][level - 1], + keywords: ['heading', 'h$level', 'title'], + node: () => headingNode(level: level), + ), + _textBlock( + name: (l10n) => l10n.blockBulletedList, + icon: LucideIcons.list, + keywords: const ['bulleted list', 'bullet', 'unordered'], + node: bulletedListNode, + ), + _textBlock( + name: (l10n) => l10n.blockNumberedList, + icon: LucideIcons.listOrdered, + keywords: const ['numbered list', 'ordered', 'number'], + node: numberedListNode, + ), + _textBlock( + name: (l10n) => l10n.blockChecklist, + icon: LucideIcons.listTodo, + keywords: const ['checklist', 'todo', 'checkbox', 'task'], + node: () => todoListNode(checked: false), + ), + _textBlock( + name: (l10n) => l10n.blockQuote, + icon: LucideIcons.textQuote, + keywords: const ['quote', 'citation'], + node: quoteNode, + ), + InsertableBlock( + name: (context) => HalkyonEditorLocalizations.of(context).blockDivider, + icon: LucideIcons.separatorHorizontal, + group: InsertGroup.blocks, + keywords: const ['divider', 'horizontal rule', 'line', 'separator'], + insert: (context, editorState, at) => + insertBlock(editorState, dividerNode(), at: at), + ), + InsertableBlock( + name: (context) => HalkyonEditorLocalizations.of(context).blockTable, + icon: LucideIcons.table, + group: InsertGroup.blocks, + keywords: const ['table', 'grid', 'rows', 'columns'], + insideCallouts: false, + // Two rows of two cells, the caret in the first. + insert: (context, editorState, at) => insertBlock( + editorState, + TableNode.fromList([ + ['', ''], + ['', ''], + ]).node, + at: at, + caretIn: const [0, 0], + lineAfter: true, + insideCallouts: false, + ), + ), + ], +); + +/// The blocks of the editors and views below [context]: an app's +/// ([EditorBlocksScope]), else [basicBlocks]. +EditorBlocks editorBlocksOf(BuildContext context) => + EditorBlocksScope.maybeOf(context) ?? basicBlocks; + +/// A line of text of a kind, which the caret goes into. +InsertableBlock _textBlock({ + required String Function(HalkyonEditorLocalizations l10n) name, + required IconData icon, + required List keywords, + required Node Function() node, +}) => InsertableBlock( + name: (context) => name(HalkyonEditorLocalizations.of(context)), + icon: icon, + group: InsertGroup.text, + keywords: keywords, + insert: (context, editorState, at) => + insertBlock(editorState, node(), at: at, caretIn: const []), +); diff --git a/apps/learn/halkyon_learn_flutter/lib/src/core/editor/block_menu.dart b/packages/halkyon_editor/lib/src/editor/block_menu.dart similarity index 87% rename from apps/learn/halkyon_learn_flutter/lib/src/core/editor/block_menu.dart rename to packages/halkyon_editor/lib/src/editor/block_menu.dart index 74107403..adca3733 100644 --- a/apps/learn/halkyon_learn_flutter/lib/src/core/editor/block_menu.dart +++ b/packages/halkyon_editor/lib/src/editor/block_menu.dart @@ -1,5 +1,5 @@ -import 'package:halkyon_learn_flutter/l10n/app_localizations.dart'; -import 'package:halkyon_learn_flutter/src/core/editor/editor_blocks.dart'; +import 'package:halkyon_editor/l10n/halkyon_editor_localizations.dart'; +import 'package:halkyon_editor/src/editor/editor_blocks.dart'; import 'package:halkyon_ui/halkyon_ui.dart'; import 'package:material_ui/material_ui.dart'; @@ -17,7 +17,7 @@ class const _BlockMenu({required final List blocks}) extends StatelessWidget { @override Widget build(BuildContext context) { - final l10n = AppLocalizations.of(context); + final l10n = HalkyonEditorLocalizations.of(context); final theme = Theme.of(context); return SheetScaffold( title: l10n.editorInsert, @@ -43,7 +43,7 @@ class const _BlockMenu({required final List blocks}) for (final block in inGroup) ListTile( leading: Icon(block.icon), - title: Text(block.name(l10n)), + title: Text(block.name(context)), onTap: () => Navigator.of(context).pop(block), ), ], diff --git a/apps/learn/halkyon_learn_flutter/lib/src/core/editor/editor_blocks.dart b/packages/halkyon_editor/lib/src/editor/editor_blocks.dart similarity index 80% rename from apps/learn/halkyon_learn_flutter/lib/src/core/editor/editor_blocks.dart rename to packages/halkyon_editor/lib/src/editor/editor_blocks.dart index 30881c7e..21e96f74 100644 --- a/apps/learn/halkyon_learn_flutter/lib/src/core/editor/editor_blocks.dart +++ b/packages/halkyon_editor/lib/src/editor/editor_blocks.dart @@ -1,8 +1,8 @@ import 'package:appflowy_editor/appflowy_editor.dart'; import 'package:flutter/foundation.dart'; -import 'package:halkyon_learn_flutter/l10n/app_localizations.dart'; -import 'package:halkyon_learn_flutter/src/core/content/content_blocks.dart'; -import 'package:halkyon_learn_flutter/src/core/editor/atomic_block.dart'; +import 'package:halkyon_editor/l10n/halkyon_editor_localizations.dart'; +import 'package:halkyon_editor/src/content/content_blocks.dart'; +import 'package:halkyon_editor/src/editor/atomic_block.dart'; import 'package:lucide_icons_flutter/lucide_icons.dart'; import 'package:material_ui/material_ui.dart'; @@ -21,7 +21,7 @@ enum InsertGroup() { /// A block people can insert, as the Insert menu and the `/` menu offer it. final class const InsertableBlock({ /// Its name in the menus. - required final String Function(AppLocalizations l10n) name, + required final String Function(BuildContext context) name, /// Its icon in the menus. required final IconData icon, @@ -49,26 +49,81 @@ final class const InsertableBlock({ final bool inFormatBar = false, }); +/// A button an app adds to the format bar, after subscript: a mark of its +/// own on the selection. +final class const FormatAction({ + /// Its icon. + required final IconData icon, + + /// Its name, under the context of the bar. + required final String Function(BuildContext context) tooltip, + + /// Whether the selection has what it does already. + required final bool Function(EditorState editorState) active, + + /// Does it, with the editor's context, for what it asks first. + required final Future Function( + BuildContext context, + EditorState editorState, + ) + run, + + /// Whether the editor takes the focus back once it is done: for what asks + /// on a screen of its own. + final bool refocus = false, +}); + +/// A way of drawing marked text, wrapping [base], the way so far; [editing] +/// says whether the text is being written or only shown. +typedef TextMark = TextSpanDecoratorForAttribute Function( + TextSpanDecoratorForAttribute base, { + required bool editing, +}); + /// The blocks an editor knows: how each type is built, which of them /// people can insert, in the menus' order, and what typing does in them -/// besides what the editor does anyway. +/// besides what the editor does anyway; with them what an app adds around +/// its blocks, marks within lines above all, which the package can't know. final class const EditorBlocks({ required final Map builders, required final List insertable, /// Tried before the editor's own, so they may take a key from it. final List shortcuts = const [], + + /// How marks of the app's own are drawn, over superscript and subscript, + /// each wrapping the one before it. + final List textMarks = const [], + + /// The format bar's buttons for those marks, in their order. + final List formatActions = const [], + + /// Called as an editor goes, for what its text left outside it. + final List onEditorClosed = const [], }) { /// These blocks and [more]'s, [more]'s after these. EditorBlocks plus(EditorBlocks more) => EditorBlocks( builders: {...builders, ...more.builders}, insertable: [...insertable, ...more.insertable], shortcuts: [...shortcuts, ...more.shortcuts], + textMarks: [...textMarks, ...more.textMarks], + formatActions: [...formatActions, ...more.formatActions], + onEditorClosed: [...onEditorClosed, ...more.onEditorClosed], + ); + + /// These blocks, but offering only [offered] to insert; what is in a + /// document already is drawn all the same. + EditorBlocks offering(List offered) => EditorBlocks( + builders: builders, + insertable: offered, + shortcuts: shortcuts, + textMarks: textMarks, + formatActions: formatActions, + onEditorClosed: onEditorClosed, ); } -/// Gives the editors below the blocks of the features too (a study button, -/// say), which the core can't know: the core's blocks plus theirs. +/// Gives the editors and views below the blocks of an app. class const EditorBlocksScope({ required final EditorBlocks blocks, required super.child, @@ -92,7 +147,7 @@ class const EditorBlocksScope({ /// English. Map buildersWithUnknown( EditorBlocks blocks, - AppLocalizations l10n, + HalkyonEditorLocalizations l10n, ) => { for (final MapEntry(key: type, value: builder) in blocks.builders.entries) type: _withPlaceholder(builder, l10n), @@ -103,7 +158,7 @@ Map buildersWithUnknown( /// empty, for the editor's own text blocks; others as they are. BlockComponentBuilder _withPlaceholder( BlockComponentBuilder builder, - AppLocalizations l10n, + HalkyonEditorLocalizations l10n, ) { BlockComponentConfiguration saying(String Function(Node node) text) => builder.configuration.copyWith(placeholderText: text); @@ -156,12 +211,14 @@ BlockComponentBuilder _withPlaceholder( /// be translated and whose Image item makes images that never show. Desktop /// only, as the editor's (phones use the Insert button). CharacterShortcutEvent slashMenu(BuildContext context, EditorBlocks blocks) { - final l10n = AppLocalizations.of(context); return customSlashCommand( [ - for (final block in blocks.insertable) + // Named here, in the build: the menu asks later, outside any. + for (final (block, name) in blocks.insertable.map( + (block) => (block, block.name(context)), + )) SelectionMenuItem( - getName: () => block.name(l10n), + getName: () => name, icon: (editorState, selected, style) => Icon( block.icon, size: 18, @@ -326,7 +383,7 @@ class _UnknownBlockState() @override Widget build(BuildContext context) { - final l10n = AppLocalizations.of(context); + final l10n = HalkyonEditorLocalizations.of(context); return buildAtomic( removeLabel: l10n.blockRemove, child: BlockNotice( diff --git a/apps/learn/halkyon_learn_flutter/lib/src/core/editor/editor_clipboard.dart b/packages/halkyon_editor/lib/src/editor/editor_clipboard.dart similarity index 100% rename from apps/learn/halkyon_learn_flutter/lib/src/core/editor/editor_clipboard.dart rename to packages/halkyon_editor/lib/src/editor/editor_clipboard.dart diff --git a/apps/learn/halkyon_learn_flutter/lib/src/core/editor/editor_style.dart b/packages/halkyon_editor/lib/src/editor/editor_style.dart similarity index 68% rename from apps/learn/halkyon_learn_flutter/lib/src/core/editor/editor_style.dart rename to packages/halkyon_editor/lib/src/editor/editor_style.dart index ebe9fd0c..9d246503 100644 --- a/apps/learn/halkyon_learn_flutter/lib/src/core/editor/editor_style.dart +++ b/packages/halkyon_editor/lib/src/editor/editor_style.dart @@ -1,8 +1,8 @@ import 'package:appflowy_editor/appflowy_editor.dart'; import 'package:flutter/foundation.dart'; -import 'package:halkyon_learn_flutter/src/core/editor/math_block.dart'; -import 'package:halkyon_learn_flutter/src/core/editor/ruby.dart'; -import 'package:halkyon_learn_flutter/src/core/editor/scripts.dart'; +import 'package:halkyon_editor/src/editor/basic_blocks.dart'; +import 'package:halkyon_editor/src/editor/editor_blocks.dart'; +import 'package:halkyon_editor/src/editor/scripts.dart'; import 'package:material_ui/material_ui.dart'; /// The rich text's look at [context], editing or showing it: the theme's @@ -11,15 +11,22 @@ import 'package:material_ui/material_ui.dart'; /// and 100 dp beside it on desktops. /// /// Phones get the touch variant, with handles to drag the selection. Both -/// draw superscript and subscript text (`withScripts`), furigana -/// (`withRuby`) and formulas within lines (`withInlineMath`): marked or as -/// their source while [editing], else set. +/// draw superscript and subscript text (`withScripts`) and the marks of +/// [blocks] (of those above [context] without): marked or as their source +/// while [editing], else set. /// The text is the theme's `bodyLarge` unless [textStyle] says otherwise. EditorStyle editorStyleOf( BuildContext context, { bool editing = true, TextStyle? textStyle, + EditorBlocks? blocks, }) { + final marks = (blocks ?? editorBlocksOf(context)).textMarks; + TextSpanDecoratorForAttribute marked(TextSpanDecoratorForAttribute base) => + marks.fold( + withScripts(base), + (decorator, mark) => mark(decorator, editing: editing), + ); final theme = Theme.of(context); final colors = theme.colorScheme; final text = TextStyleConfiguration( @@ -44,26 +51,14 @@ EditorStyle editorStyleOf( selectionColor: selection, dragHandleColor: colors.primary, textStyleConfiguration: text, - textSpanDecorator: withInlineMath( - withRuby( - withScripts(mobileTextSpanDecoratorForAttribute), - editing: editing, - ), - editing: editing, - ), + textSpanDecorator: marked(mobileTextSpanDecoratorForAttribute), ), _ => EditorStyle.desktop( padding: EdgeInsets.zero, cursorColor: colors.primary, selectionColor: selection, textStyleConfiguration: text, - textSpanDecorator: withInlineMath( - withRuby( - withScripts(defaultTextSpanDecoratorForAttribute), - editing: editing, - ), - editing: editing, - ), + textSpanDecorator: marked(defaultTextSpanDecoratorForAttribute), ), }; } diff --git a/apps/learn/halkyon_learn_flutter/lib/src/core/editor/format_bar.dart b/packages/halkyon_editor/lib/src/editor/format_bar.dart similarity index 84% rename from apps/learn/halkyon_learn_flutter/lib/src/core/editor/format_bar.dart rename to packages/halkyon_editor/lib/src/editor/format_bar.dart index 4944be0c..8e87d127 100644 --- a/apps/learn/halkyon_learn_flutter/lib/src/core/editor/format_bar.dart +++ b/packages/halkyon_editor/lib/src/editor/format_bar.dart @@ -1,19 +1,16 @@ import 'dart:async'; import 'package:appflowy_editor/appflowy_editor.dart'; -import 'package:halkyon_learn_flutter/l10n/app_localizations.dart'; -import 'package:halkyon_learn_flutter/src/core/content/content_blocks.dart'; -import 'package:halkyon_learn_flutter/src/core/editor/editor_blocks.dart'; -import 'package:halkyon_learn_flutter/src/core/editor/link.dart'; -import 'package:halkyon_learn_flutter/src/core/editor/ruby.dart'; -import 'package:halkyon_learn_flutter/src/core/editor/scripts.dart'; +import 'package:halkyon_editor/l10n/halkyon_editor_localizations.dart'; +import 'package:halkyon_editor/src/editor/editor_blocks.dart'; +import 'package:halkyon_editor/src/editor/link.dart'; +import 'package:halkyon_editor/src/editor/scripts.dart'; import 'package:lucide_icons_flutter/lucide_icons.dart'; import 'package:material_ui/material_ui.dart'; /// Formats the selection of [editorState]: alignment, bold, italic, -/// underline, a link, superscript and subscript, text above (furigana), -/// and with [onFormula] formulas -/// within the line. Each button shows whether the selection has its format +/// underline, a link, superscript and subscript, and the app's own +/// [actions]. Each button shows whether the selection has its format /// already. With [onInsert], an Insert button comes first, for blocks; the /// [pinned] blocks have buttons at the end, outside the scrolling, so they /// are always at hand. @@ -27,8 +24,11 @@ class const FormatBar({ /// Called by the Insert button, which shows only with it. final VoidCallback? onInsert, - /// Called by the formula button (Σ), which shows only with it. - final VoidCallback? onFormula, + /// The app's own buttons, after subscript. + final List actions = const [], + + /// Called with the action whose button was pressed. + final ValueChanged? onAction, /// Blocks with a button of their own at the end. final List pinned = const [], @@ -128,7 +128,7 @@ class _FormatBarState() extends State { @override Widget build(BuildContext context) { - final l10n = AppLocalizations.of(context); + final l10n = HalkyonEditorLocalizations.of(context); final editorState = widget.editorState; final formats = SingleChildScrollView( scrollDirection: Axis.horizontal, @@ -203,23 +203,12 @@ class _FormatBarState() extends State { active: _has(subscriptKey), onPressed: () => toggleScript(editorState, subscriptKey), ), - _FormatButton( - icon: LucideIcons.languages, - tooltip: rubyButtonName(context), - active: - editorState.selection != null && - editorState.getDeltaAttributeValueInSelection( - rubyKey, - ) != - null, - onPressed: () => editRuby(context, editorState), - ), - if (widget.onFormula case final onFormula?) + for (final action in widget.actions) _FormatButton( - icon: LucideIcons.sigma, - tooltip: l10n.formatInlineMath, - active: _has(inlineMathKey), - onPressed: onFormula, + icon: action.icon, + tooltip: action.tooltip(context), + active: action.active(editorState), + onPressed: () => widget.onAction?.call(action), ), ], ), @@ -235,7 +224,7 @@ class _FormatBarState() extends State { for (final block in widget.pinned) _FormatButton( icon: block.icon, - tooltip: block.name(l10n), + tooltip: block.name(context), active: false, onPressed: () => widget.onPinned?.call(block), ), diff --git a/apps/learn/halkyon_learn_flutter/lib/src/core/editor/link.dart b/packages/halkyon_editor/lib/src/editor/link.dart similarity index 96% rename from apps/learn/halkyon_learn_flutter/lib/src/core/editor/link.dart rename to packages/halkyon_editor/lib/src/editor/link.dart index 8fd461e3..751951c4 100644 --- a/apps/learn/halkyon_learn_flutter/lib/src/core/editor/link.dart +++ b/packages/halkyon_editor/lib/src/editor/link.dart @@ -1,7 +1,7 @@ import 'dart:async'; import 'package:appflowy_editor/appflowy_editor.dart'; -import 'package:halkyon_learn_flutter/l10n/app_localizations.dart'; +import 'package:halkyon_editor/l10n/halkyon_editor_localizations.dart'; import 'package:halkyon_ui/halkyon_ui.dart'; import 'package:material_ui/material_ui.dart'; @@ -14,7 +14,7 @@ import 'package:material_ui/material_ui.dart'; /// caret ([editLink]), asking in [context]. CommandShortcutEvent linkShortcut(BuildContext context) => CommandShortcutEvent( key: 'link', - getDescription: () => AppLocalizations.of(context).formatLink, + getDescription: () => HalkyonEditorLocalizations.of(context).formatLink, command: 'ctrl+k', macOSCommand: 'cmd+k', handler: (editorState) { @@ -121,7 +121,7 @@ class _LinkDialogState() extends State<_LinkDialog> { @override Widget build(BuildContext context) { - final l10n = AppLocalizations.of(context); + final l10n = HalkyonEditorLocalizations.of(context); return AlertDialog( title: Text(l10n.linkTitle), content: Column( diff --git a/apps/learn/halkyon_learn_flutter/lib/src/core/editor/rich_content_document.dart b/packages/halkyon_editor/lib/src/editor/rich_content_document.dart similarity index 97% rename from apps/learn/halkyon_learn_flutter/lib/src/core/editor/rich_content_document.dart rename to packages/halkyon_editor/lib/src/editor/rich_content_document.dart index 7b636872..df802831 100644 --- a/apps/learn/halkyon_learn_flutter/lib/src/core/editor/rich_content_document.dart +++ b/packages/halkyon_editor/lib/src/editor/rich_content_document.dart @@ -1,7 +1,7 @@ import 'dart:convert'; import 'package:appflowy_editor/appflowy_editor.dart'; -import 'package:halkyon_learn_flutter/src/core/content/rich_content.dart'; +import 'package:halkyon_editor/src/content/rich_content.dart'; /// Opens [RichContent] in the editor. extension RichContentToDocument on RichContent { diff --git a/apps/learn/halkyon_learn_flutter/lib/src/core/editor/rich_content_editor.dart b/packages/halkyon_editor/lib/src/editor/rich_content_editor.dart similarity index 77% rename from apps/learn/halkyon_learn_flutter/lib/src/core/editor/rich_content_editor.dart rename to packages/halkyon_editor/lib/src/editor/rich_content_editor.dart index 743e4af3..4676bdb2 100644 --- a/apps/learn/halkyon_learn_flutter/lib/src/core/editor/rich_content_editor.dart +++ b/packages/halkyon_editor/lib/src/editor/rich_content_editor.dart @@ -3,33 +3,36 @@ import 'dart:math' as math; import 'package:appflowy_editor/appflowy_editor.dart'; import 'package:flutter/foundation.dart'; -import 'package:halkyon_learn_flutter/l10n/app_localizations.dart'; -import 'package:halkyon_learn_flutter/src/core/content/rich_content.dart'; -import 'package:halkyon_learn_flutter/src/core/dependencies/person_dependencies.dart'; -import 'package:halkyon_learn_flutter/src/core/editor/block_menu.dart'; -import 'package:halkyon_learn_flutter/src/core/editor/core_blocks.dart'; -import 'package:halkyon_learn_flutter/src/core/editor/editor_blocks.dart'; -import 'package:halkyon_learn_flutter/src/core/editor/editor_clipboard.dart'; -import 'package:halkyon_learn_flutter/src/core/editor/editor_style.dart'; -import 'package:halkyon_learn_flutter/src/core/editor/format_bar.dart'; -import 'package:halkyon_learn_flutter/src/core/editor/link.dart'; -import 'package:halkyon_learn_flutter/src/core/editor/math_block.dart'; -import 'package:halkyon_learn_flutter/src/core/editor/rich_content_document.dart'; -import 'package:halkyon_learn_flutter/src/core/editor/ruby.dart'; -import 'package:halkyon_learn_flutter/src/core/grammar/grammar_host.dart'; +import 'package:halkyon_editor/l10n/halkyon_editor_localizations.dart'; +import 'package:halkyon_editor/src/content/rich_content.dart'; +import 'package:halkyon_editor/src/editor/basic_blocks.dart'; +import 'package:halkyon_editor/src/editor/block_menu.dart'; +import 'package:halkyon_editor/src/editor/editor_blocks.dart'; +import 'package:halkyon_editor/src/editor/editor_clipboard.dart'; +import 'package:halkyon_editor/src/editor/editor_style.dart'; +import 'package:halkyon_editor/src/editor/format_bar.dart'; +import 'package:halkyon_editor/src/editor/link.dart'; +import 'package:halkyon_editor/src/editor/rich_content_document.dart'; import 'package:material_ui/material_ui.dart'; +/// What an app puts around an editor: [editor] within whatever it adds, +/// knowing the editor's state and focus (Learn's check of the spelling, say). +typedef EditorWrapper = Widget Function( + BuildContext context, + EditorState editorState, + FocusNode focusNode, + Widget editor, +); + /// Edits [initialContent] and reports each change as new [RichContent]. /// /// Empty content opens with an empty paragraph, so there is somewhere to type. /// With [formatBar], a [FormatBar] is docked below the editor while it has /// the focus, its Insert button first; the editor then takes what height is -/// left. On desktops, `/` opens the same blocks as Insert ([blocksOf]). +/// left. On desktops, `/` opens the same blocks as Insert ([blocks]). /// Copying and pasting keep the formats between the app's editors /// ([EditorClipboard]): with the keys on desktops, and on phones from the -/// bar that shows over selected text (Copy, Cut, Paste, Select all). With -/// [grammar], spelling and grammar are checked as the text is written, in -/// that language ([GrammarHost], ADR 0024). +/// bar that shows over selected text (Copy, Cut, Paste, Select all). class const RichContentEditor({ /// The content the editor opens with; later changes to it are ignored. required final RichContent initialContent, @@ -49,8 +52,11 @@ class const RichContentEditor({ /// The room around the text, within the editor's scrolling. final EdgeInsets padding = EdgeInsets.zero, - /// The language the text is checked in as it is written; null: it isn't. - final GrammarLanguage? grammar, + /// The blocks it knows; those above it ([editorBlocksOf]) without. + final EditorBlocks? blocks, + + /// Puts what an app adds around the editor itself, inside its bars. + final EditorWrapper? wrapEditor, super.key, }) extends StatefulWidget { /// Whether an editor's popup (its `/` menu) has the focus for now: it @@ -71,6 +77,8 @@ class _RichContentEditorState() extends State { late final _scroll = EditorScrollController(editorState: _editorState); FocusNode? _ownFocus; var _focused = false; + // As of the last build, for what happens outside one. + late EditorBlocks _blocks; FocusNode get _focus => widget.focusNode ?? (_ownFocus ??= FocusNode()); @@ -93,8 +101,10 @@ class _RichContentEditorState() extends State { @override void dispose() { - // Its text with text above may be under the pointer. - hideHoverReading(); + // What its text showed outside it may be under the pointer. + for (final closed in _blocks.onEditorClosed) { + closed(); + } _focus.removeListener(_onFocus); _ownFocus?.dispose(); unawaited(_changes.cancel()); @@ -132,7 +142,7 @@ class _RichContentEditorState() extends State { /// inserted: the menu took the focus, and the editor's selection with it. Future _insert() async { final at = _editorState.selection; - final block = await chooseBlock(context, blocksOf(context).insertable); + final block = await chooseBlock(context, _blocks.insertable); if (!mounted) { return; } @@ -154,27 +164,29 @@ class _RichContentEditorState() extends State { _focus.requestFocus(); } - /// Makes the selection a formula within its line, or writes one at the - /// caret; the editor's focus comes back after its screen. - Future _formula() async { - await toggleInlineMath(context, _editorState); - if (mounted) { + /// Does [action]; the editor's focus comes back after any screen of its. + Future _run(FormatAction action) async { + await action.run(context, _editorState); + if (action.refocus && mounted) { _focus.requestFocus(); } } @override Widget build(BuildContext context) { - final blocks = blocksOf(context); + final blocks = _blocks = widget.blocks ?? editorBlocksOf(context); final appFlowyEditor = LayoutBuilder( builder: (context, constraints) => AppFlowyEditor( editorState: _editorState, editorScrollController: _scroll, - editorStyle: editorStyleOf(context).copyWith(padding: widget.padding), + editorStyle: editorStyleOf( + context, + blocks: blocks, + ).copyWith(padding: widget.padding), commandShortcutEvents: clipboardShortcuts(link: linkShortcut(context)), blockComponentBuilders: buildersWithUnknown( blocks, - AppLocalizations.of(context), + HalkyonEditorLocalizations.of(context), ), characterShortcutEvents: characterShortcutsOf( blocks, @@ -194,17 +206,14 @@ class _RichContentEditorState() extends State { : appFlowyEditorAutoScrollEdgeOffset, ), ); - final checked = switch (widget.grammar) { - // Checked for the person signed in; nobody is outside their app. - final language? when PersonDependencies.maybeOf(context) != null => - GrammarHost( - editorState: _editorState, - focusNode: _focus, - language: language, - child: appFlowyEditor, - ), - _ => appFlowyEditor, - }; + final wrapped = + widget.wrapEditor?.call( + context, + _editorState, + _focus, + appFlowyEditor, + ) ?? + appFlowyEditor; final editor = switch (defaultTargetPlatform) { TargetPlatform.android || TargetPlatform.iOS => MobileFloatingToolbar( editorState: _editorState, @@ -235,9 +244,9 @@ class _RichContentEditorState() extends State { onShare: null, anchors: TextSelectionToolbarAnchors(primaryAnchor: anchor), ), - child: checked, + child: wrapped, ), - _ => checked, + _ => wrapped, }; if (!widget.formatBar) { return editor; @@ -254,9 +263,10 @@ class _RichContentEditorState() extends State { FormatBar( editorState: _editorState, onInsert: _insert, - onFormula: _formula, + actions: blocks.formatActions, + onAction: (action) => unawaited(_run(action)), pinned: [ - for (final block in blocksOf(context).insertable) + for (final block in blocks.insertable) if (block.inFormatBar) block, ], onPinned: (block) => diff --git a/apps/learn/halkyon_learn_flutter/lib/src/core/editor/rich_content_view.dart b/packages/halkyon_editor/lib/src/editor/rich_content_view.dart similarity index 72% rename from apps/learn/halkyon_learn_flutter/lib/src/core/editor/rich_content_view.dart rename to packages/halkyon_editor/lib/src/editor/rich_content_view.dart index c6ee8003..beba932a 100644 --- a/apps/learn/halkyon_learn_flutter/lib/src/core/editor/rich_content_view.dart +++ b/packages/halkyon_editor/lib/src/editor/rich_content_view.dart @@ -1,11 +1,11 @@ import 'package:appflowy_editor/appflowy_editor.dart'; import 'package:flutter/widgets.dart'; -import 'package:halkyon_learn_flutter/l10n/app_localizations.dart'; -import 'package:halkyon_learn_flutter/src/core/content/rich_content.dart'; -import 'package:halkyon_learn_flutter/src/core/editor/core_blocks.dart'; -import 'package:halkyon_learn_flutter/src/core/editor/editor_blocks.dart'; -import 'package:halkyon_learn_flutter/src/core/editor/editor_style.dart'; -import 'package:halkyon_learn_flutter/src/core/editor/rich_content_document.dart'; +import 'package:halkyon_editor/l10n/halkyon_editor_localizations.dart'; +import 'package:halkyon_editor/src/content/rich_content.dart'; +import 'package:halkyon_editor/src/editor/basic_blocks.dart'; +import 'package:halkyon_editor/src/editor/editor_blocks.dart'; +import 'package:halkyon_editor/src/editor/editor_style.dart'; +import 'package:halkyon_editor/src/editor/rich_content_document.dart'; /// Shows [content] read-only: the one renderer for rich text wherever it is /// shown rather than edited, so every block looks the same everywhere. @@ -28,6 +28,9 @@ class const RichContentView({ /// alignment of their own; at the start without. Lists, quotes and the /// other blocks keep the start: centred, a list's markers drift apart. final TextAlign? textAlign, + + /// The blocks it knows; those above it ([editorBlocksOf]) without. + final EditorBlocks? blocks, super.key, }) extends StatefulWidget { @override @@ -59,25 +62,29 @@ class _RichContentViewState() extends State { // IntrinsicHeight gives it its content's height, as the editor's own example // does for editors in a list. @override - Widget build(BuildContext context) => IntrinsicHeight( - child: AppFlowyEditor( - key: ObjectKey(_editorState), - editorState: _editorState, - editorStyle: editorStyleOf( - context, - editing: false, - textStyle: widget.textStyle, + Widget build(BuildContext context) { + final blocks = widget.blocks ?? editorBlocksOf(context); + return IntrinsicHeight( + child: AppFlowyEditor( + key: ObjectKey(_editorState), + editorState: _editorState, + editorStyle: editorStyleOf( + context, + editing: false, + textStyle: widget.textStyle, + blocks: blocks, + ), + blockComponentBuilders: buildersWithUnknown( + blocks, + HalkyonEditorLocalizations.of(context), + ), + editable: false, + disableSelectionService: true, + disableKeyboardService: true, + shrinkWrap: true, ), - blockComponentBuilders: buildersWithUnknown( - blocksOf(context), - AppLocalizations.of(context), - ), - editable: false, - disableSelectionService: true, - disableKeyboardService: true, - shrinkWrap: true, - ), - ); + ); + } } /// [content] to show, its top-level paragraphs and headings aligned as diff --git a/apps/learn/halkyon_learn_flutter/lib/src/core/editor/scripts.dart b/packages/halkyon_editor/lib/src/editor/scripts.dart similarity index 100% rename from apps/learn/halkyon_learn_flutter/lib/src/core/editor/scripts.dart rename to packages/halkyon_editor/lib/src/editor/scripts.dart diff --git a/apps/learn/halkyon_learn_flutter/test/core/content/rich_content_test.dart b/packages/halkyon_editor/test/content/rich_content_test.dart similarity index 98% rename from apps/learn/halkyon_learn_flutter/test/core/content/rich_content_test.dart rename to packages/halkyon_editor/test/content/rich_content_test.dart index 715a4143..3e0c6a61 100644 --- a/apps/learn/halkyon_learn_flutter/test/core/content/rich_content_test.dart +++ b/packages/halkyon_editor/test/content/rich_content_test.dart @@ -1,8 +1,7 @@ import 'dart:convert'; import 'package:flutter_test/flutter_test.dart'; -import 'package:halkyon_learn_flutter/src/core/content/content_blocks.dart'; -import 'package:halkyon_learn_flutter/src/core/content/rich_content.dart'; +import 'package:halkyon_editor/halkyon_editor.dart'; void main() { /// The editor's JSON for a page with one paragraph per text. diff --git a/packages/halkyon_editor/test/editor/editor_blocks_test.dart b/packages/halkyon_editor/test/editor/editor_blocks_test.dart new file mode 100644 index 00000000..13219cca --- /dev/null +++ b/packages/halkyon_editor/test/editor/editor_blocks_test.dart @@ -0,0 +1,371 @@ +import 'package:appflowy_editor/appflowy_editor.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:halkyon_editor/halkyon_editor.dart'; +import 'package:lucide_icons_flutter/lucide_icons.dart'; +import 'package:material_ui/material_ui.dart'; + +import '../support/test_app.dart'; + +/// Wraps text marked `shout` in capitals, or lower case while [editing]. +TextSpanDecoratorForAttribute _shouting( + TextSpanDecoratorForAttribute base, { + required bool editing, +}) => (context, node, index, text, before, after) { + final span = base(context, node, index, text, before, after); + return text.attributes?['shout'] == true + ? TextSpan( + text: editing ? text.text.toLowerCase() : text.text.toUpperCase(), + style: span.style, + ) + : span; +}; + +RichContent _shouted(String text) => RichContent({ + 'document': { + 'type': 'page', + 'children': [ + { + 'type': 'paragraph', + 'data': { + 'delta': [ + { + 'insert': text, + 'attributes': {'shout': true}, + }, + ], + }, + }, + ], + }, +}); + +/// A block of a type nothing here knows, as a newer app may have written. +RichContent _unknownBlock() => RichContent({ + 'document': { + 'type': 'page', + 'children': [ + {'type': 'from_the_future'}, + { + 'type': 'paragraph', + 'data': { + 'delta': [ + {'insert': 'around it'}, + ], + }, + }, + ], + }, +}); + +Widget _app(Widget child) => MaterialApp( + localizationsDelegates: editorTestDelegates, + home: Scaffold(body: child), +); + +Widget _editor({ + RichContent? content, + EditorBlocks? blocks, + EditorWrapper? wrapEditor, + ValueChanged? onChanged, +}) => SizedBox( + height: 400, + child: RichContentEditor( + initialContent: content ?? RichContent.plain('Water'), + onChanged: onChanged ?? (_) {}, + formatBar: true, + blocks: blocks, + wrapEditor: wrapEditor, + ), +); + +InsertableBlock _block(String name) => InsertableBlock( + name: (context) => name, + icon: LucideIcons.star, + group: InsertGroup.blocks, + keywords: const [], + insert: (context, editorState, at) async {}, +); + +FormatAction _action({ + bool refocus = false, + Future Function(BuildContext, EditorState)? run, +}) => FormatAction( + icon: LucideIcons.star, + tooltip: (context) => 'Star', + active: (_) => false, + run: run ?? (context, editorState) async {}, + refocus: refocus, +); + +Future _focusEditor(WidgetTester tester) async { + await tester.tap(find.byType(AppFlowyEditor)); + await tester.pump(const Duration(seconds: 1)); +} + +void main() { + group('without any blocks of an app', () { + testWidgets('the editor offers the basic blocks, listed by kind', ( + tester, + ) async { + await tester.pumpWidget(_app(_editor())); + await _focusEditor(tester); + + await tester.tap(find.byTooltip('Insert')); + await tester.pumpAndSettle(); + + expect(find.text('Heading 1'), findsOneWidget); + expect(find.text('Checklist'), findsOneWidget); + expect(find.text('Table'), findsOneWidget); + expect(find.text('Media'), findsNothing); + }); + + testWidgets('a block it does not know says so in the view', (tester) async { + await tester.pumpWidget( + _app(ListView(children: [RichContentView(content: _unknownBlock())])), + ); + await tester.pump(const Duration(seconds: 1)); + + final l10n = lookupHalkyonEditorLocalizations(const Locale('en')); + expect(find.text(l10n.blockUnknown), findsOneWidget); + expect(find.text('around it', findRichText: true), findsOneWidget); + }); + }); + + group('what an app adds', () { + testWidgets('marks of its own are drawn, set in the view and as written ' + 'in the editor', (tester) async { + final blocks = EditorBlocks( + builders: basicBlocks.builders, + insertable: basicBlocks.insertable, + textMarks: const [_shouting], + ); + await tester.pumpWidget( + _app( + ListView( + children: [ + RichContentView(content: _shouted('Water'), blocks: blocks), + _editor(content: _shouted('Water'), blocks: blocks), + ], + ), + ), + ); + await tester.pump(const Duration(seconds: 1)); + + expect(find.text('WATER', findRichText: true), findsOneWidget); + expect(find.text('water', findRichText: true), findsOneWidget); + }); + + testWidgets('its format button comes last of the formats and its run gets ' + "the editor's state", (tester) async { + EditorState? ran; + await tester.pumpWidget( + _app( + _editor( + blocks: EditorBlocks( + builders: basicBlocks.builders, + insertable: basicBlocks.insertable, + formatActions: [ + _action(run: (context, editorState) async => ran = editorState), + ], + ), + ), + ), + ); + await _focusEditor(tester); + + await tester.ensureVisible(find.byTooltip('Star')); + await tester.pump(); + expect( + tester.getTopLeft(find.byTooltip('Star')).dx, + greaterThan(tester.getTopLeft(find.byTooltip('Subscript')).dx), + ); + await tester.tap(find.byTooltip('Star')); + await tester.pump(const Duration(seconds: 1)); + + expect( + ran, + same( + tester + .widget(find.byType(AppFlowyEditor)) + .editorState, + ), + ); + }); + + testWidgets('a format button asked to takes the focus back to the editor', ( + tester, + ) async { + final focusNode = FocusNode(); + addTearDown(focusNode.dispose); + await tester.pumpWidget( + _app( + SizedBox( + height: 400, + child: RichContentEditor( + initialContent: RichContent.plain('Water'), + onChanged: (_) {}, + formatBar: true, + focusNode: focusNode, + blocks: EditorBlocks( + builders: basicBlocks.builders, + insertable: basicBlocks.insertable, + formatActions: [ + _action( + refocus: true, + run: (context, editorState) async => focusNode.unfocus(), + ), + ], + ), + ), + ), + ), + ); + await _focusEditor(tester); + + await tester.ensureVisible(find.byTooltip('Star')); + await tester.pump(); + await tester.tap(find.byTooltip('Star')); + await tester.pump(const Duration(seconds: 1)); + + expect(focusNode.hasFocus, isTrue); + }); + + testWidgets('it hears of an editor going', (tester) async { + var closed = 0; + await tester.pumpWidget( + _app( + _editor( + blocks: EditorBlocks( + builders: basicBlocks.builders, + insertable: basicBlocks.insertable, + onEditorClosed: [() => closed++], + ), + ), + ), + ); + await tester.pump(const Duration(seconds: 1)); + expect(closed, 0); + + await tester.pumpWidget(_app(const SizedBox())); + await tester.pump(const Duration(seconds: 1)); + + expect(closed, 1); + }); + + testWidgets('what it wraps the editor in knows its state and focus', ( + tester, + ) async { + const wrapper = Key('wrapper'); + EditorState? seen; + FocusNode? seenFocus; + await tester.pumpWidget( + _app( + _editor( + wrapEditor: (context, editorState, focusNode, editor) { + seen = editorState; + seenFocus = focusNode; + return KeyedSubtree(key: wrapper, child: editor); + }, + ), + ), + ); + await _focusEditor(tester); + + final editor = find.byType(AppFlowyEditor); + expect( + find.descendant(of: find.byKey(wrapper), matching: editor), + findsOneWidget, + ); + expect(seen, same(tester.widget(editor).editorState)); + expect(seenFocus?.hasFocus, isTrue); + }); + + testWidgets('blocks from a scope count, and those given beat them', ( + tester, + ) async { + Future insertMenu(Widget editor) async { + await tester.pumpWidget( + _app( + EditorBlocksScope( + blocks: EditorBlocks( + builders: basicBlocks.builders, + insertable: [_block('From the scope')], + ), + child: editor, + ), + ), + ); + await _focusEditor(tester); + await tester.tap(find.byTooltip('Insert')); + await tester.pumpAndSettle(); + } + + await insertMenu(_editor()); + expect(find.text('From the scope'), findsOneWidget); + + await tester.pumpWidget(const SizedBox()); + await insertMenu( + _editor( + blocks: EditorBlocks( + builders: basicBlocks.builders, + insertable: [_block('Given')], + ), + ), + ); + expect(find.text('Given'), findsOneWidget); + expect(find.text('From the scope'), findsNothing); + }); + }); + + test('plus keeps what either adds, in their order', () { + void first() {} + void second() {} + final star = _action(); + final moon = _action(); + final both = + EditorBlocks( + builders: const {}, + insertable: const [], + textMarks: const [_shouting], + formatActions: [star], + onEditorClosed: [first], + ).plus( + EditorBlocks( + builders: const {}, + insertable: const [], + formatActions: [moon], + onEditorClosed: [second], + ), + ); + + expect(both.textMarks, [_shouting]); + expect(both.formatActions, [star, moon]); + expect(both.onEditorClosed, [first, second]); + }); + + test('offering changes what is offered, nothing else', () { + void closed() {} + final star = _action(); + final shortcut = CharacterShortcutEvent( + key: 'nothing', + character: '~', + handler: (_) async => false, + ); + final block = _block('One'); + final blocks = EditorBlocks( + builders: basicBlocks.builders, + insertable: basicBlocks.insertable, + shortcuts: [shortcut], + textMarks: const [_shouting], + formatActions: [star], + onEditorClosed: [closed], + ).offering([block]); + + expect(blocks.insertable, [block]); + expect(blocks.builders, basicBlocks.builders); + expect(blocks.shortcuts, [shortcut]); + expect(blocks.textMarks, [_shouting]); + expect(blocks.formatActions, [star]); + expect(blocks.onEditorClosed, [closed]); + }); +} diff --git a/apps/learn/halkyon_learn_flutter/test/core/editor/editor_placeholders_test.dart b/packages/halkyon_editor/test/editor/editor_placeholders_test.dart similarity index 73% rename from apps/learn/halkyon_learn_flutter/test/core/editor/editor_placeholders_test.dart rename to packages/halkyon_editor/test/editor/editor_placeholders_test.dart index 32d44961..7d51c5ce 100644 --- a/apps/learn/halkyon_learn_flutter/test/core/editor/editor_placeholders_test.dart +++ b/packages/halkyon_editor/test/editor/editor_placeholders_test.dart @@ -1,19 +1,18 @@ import 'package:appflowy_editor/appflowy_editor.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'package:halkyon_learn_flutter/l10n/app_localizations.dart'; -import 'package:halkyon_learn_flutter/src/core/editor/core_blocks.dart'; -import 'package:halkyon_learn_flutter/src/core/editor/editor_blocks.dart'; +import 'package:halkyon_editor/halkyon_editor.dart'; import 'package:material_ui/material_ui.dart'; void main() { - final german = lookupAppLocalizations(const Locale('de')); + final german = lookupHalkyonEditorLocalizations(const Locale('de')); /// The placeholder an empty [node] shows, among the builders for [l10n]. - String placeholderOf(Node node, AppLocalizations l10n) => buildersWithUnknown( - coreBlocks, - l10n, - )[node.type]!.configuration.placeholderText(node); + String placeholderOf(Node node, HalkyonEditorLocalizations l10n) => + buildersWithUnknown( + basicBlocks, + l10n, + )[node.type]!.configuration.placeholderText(node); test("the editor's own text blocks say what they are in the app's " 'language', () { @@ -42,7 +41,7 @@ void main() { test('the blocks keep what else the editor set them up with', () { final todo = - buildersWithUnknown(coreBlocks, german)[TodoListBlockKeys.type]! + buildersWithUnknown(basicBlocks, german)[TodoListBlockKeys.type]! as TodoListBlockComponentBuilder; expect(todo.toggleChildrenTriggers, isNotEmpty); diff --git a/packages/halkyon_editor/test/editor/format_bar_test.dart b/packages/halkyon_editor/test/editor/format_bar_test.dart new file mode 100644 index 00000000..fa08c39c --- /dev/null +++ b/packages/halkyon_editor/test/editor/format_bar_test.dart @@ -0,0 +1,188 @@ +import 'package:appflowy_editor/appflowy_editor.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:halkyon_editor/halkyon_editor.dart'; +import 'package:material_ui/material_ui.dart'; + +import '../support/test_app.dart'; + +void main() { + late RichContent latest; + + /// Edits [text], or [content]. + Future edit( + WidgetTester tester, + String text, { + RichContent? content, + }) async { + latest = content ?? RichContent.plain(text); + await tester.pumpWidget( + MaterialApp( + localizationsDelegates: editorTestDelegates, + home: Scaffold( + body: ListView( + children: [ + SizedBox( + height: 400, + child: RichContentEditor( + initialContent: latest, + onChanged: (content) => latest = content, + formatBar: true, + ), + ), + ], + ), + ), + ), + ); + await tester.pump(const Duration(seconds: 1)); + return tester + .widget(find.byType(AppFlowyEditor)) + .editorState; + } + + /// Focuses the editor and selects [start] to [end] of its first line. + Future select( + WidgetTester tester, + EditorState editorState, + int start, + int end, + ) async { + await tester.tap(find.byType(AppFlowyEditor)); + await tester.pump(const Duration(seconds: 1)); + editorState.selection = Selection.single( + path: [0], + startOffset: start, + endOffset: end, + ); + await tester.pump(const Duration(seconds: 1)); + } + + Future press(WidgetTester tester, String tooltip) async { + await tester.tap(find.byTooltip(tooltip)); + await tester.pump(const Duration(seconds: 1)); + } + + /// The attributes of the first line's runs, in order. + List?> runs() { + final document = latest.editorJson['document']! as Map; + final first = (document['children']! as List).first as Map; + return [ + for (final run in (first['data']! as Map)['delta']! as List) + (run as Map)['attributes'] as Map?, + ]; + } + + testWidgets('shows while the editor has the focus, and only then', ( + tester, + ) async { + await edit(tester, 'Water is H2O'); + expect(find.byType(FormatBar), findsNothing); + + await tester.tap(find.byType(AppFlowyEditor)); + await tester.pump(const Duration(seconds: 1)); + expect(find.byType(FormatBar), findsOneWidget); + + FocusManager.instance.primaryFocus?.unfocus(); + await tester.pump(const Duration(seconds: 1)); + expect(find.byType(FormatBar), findsNothing); + }); + + testWidgets('bold marks the selection, shows it, and keeps the editor ' + 'focused', (tester) async { + final editorState = await edit(tester, 'Water is H2O'); + await select(tester, editorState, 0, 5); + + await press(tester, 'Bold'); + + expect(runs().first, {AppFlowyRichTextKeys.bold: true}); + expect(find.byType(FormatBar), findsOneWidget); + expect( + tester + .widget( + find.ancestor( + of: find.byTooltip('Bold'), + matching: find.byType(IconButton), + ), + ) + .isSelected, + isTrue, + ); + }); + + testWidgets('a subscript replaces a superscript on the same text', ( + tester, + ) async { + final editorState = await edit(tester, 'H2O'); + await select(tester, editorState, 1, 2); + + await press(tester, 'Superscript'); + expect(runs()[1], {superscriptKey: true}); + + await press(tester, 'Subscript'); + expect(runs()[1], {subscriptKey: true}); + }); + + testWidgets('centres the selected line', (tester) async { + final editorState = await edit(tester, 'Water is H2O'); + await select(tester, editorState, 0, 5); + + await press(tester, 'Center'); + + final document = latest.editorJson['document']! as Map; + final first = (document['children']! as List).first as Map; + expect((first['data']! as Map)['align'], 'center'); + }); + + testWidgets('read-only text shows a subscript raised by the font, ' + 'smaller', (tester) async { + await tester.pumpWidget( + MaterialApp( + localizationsDelegates: editorTestDelegates, + home: Scaffold( + body: ListView( + children: [ + RichContentView( + content: RichContent({ + 'document': { + 'type': 'page', + 'children': [ + { + 'type': 'paragraph', + 'data': { + 'delta': [ + {'insert': 'H'}, + { + 'insert': '2', + 'attributes': {subscriptKey: true}, + }, + {'insert': 'O'}, + ], + }, + }, + ], + }, + }), + ), + ], + ), + ), + ), + ); + await tester.pump(const Duration(seconds: 1)); + + final spans = []; + for (final text in tester.widgetList(find.byType(RichText))) { + text.text.visitChildren((span) { + if (span is TextSpan && span.text == '2') { + spans.add(span); + } + return true; + }); + } + expect(spans, isNotEmpty); + expect( + spans.first.style?.fontFeatures, + contains(const FontFeature.subscripts()), + ); + }); +} diff --git a/apps/learn/halkyon_learn_flutter/test/core/editor/link_and_clipboard_test.dart b/packages/halkyon_editor/test/editor/link_and_clipboard_test.dart similarity index 94% rename from apps/learn/halkyon_learn_flutter/test/core/editor/link_and_clipboard_test.dart rename to packages/halkyon_editor/test/editor/link_and_clipboard_test.dart index ff75258f..06dd62dc 100644 --- a/apps/learn/halkyon_learn_flutter/test/core/editor/link_and_clipboard_test.dart +++ b/packages/halkyon_editor/test/editor/link_and_clipboard_test.dart @@ -1,14 +1,11 @@ import 'package:appflowy_editor/appflowy_editor.dart'; import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'package:halkyon_learn_flutter/src/core/content/content_blocks.dart'; -import 'package:halkyon_learn_flutter/src/core/content/rich_content.dart'; -import 'package:halkyon_learn_flutter/src/core/editor/editor_clipboard.dart'; -import 'package:halkyon_learn_flutter/src/core/editor/link.dart'; -import 'package:halkyon_learn_flutter/src/core/editor/rich_content_editor.dart'; -import 'package:halkyon_learn_flutter/src/core/l10n/app_languages.dart'; +import 'package:halkyon_editor/halkyon_editor.dart'; import 'package:material_ui/material_ui.dart'; +import '../support/test_app.dart'; + /// Content of one line of [runs], each a text and its marks. RichContent line(List<(String, Map)> runs) => RichContent({ 'document': { @@ -80,7 +77,7 @@ void main() { final latest = [...contents]; await tester.pumpWidget( MaterialApp( - localizationsDelegates: appLocalizationsDelegates, + localizationsDelegates: editorTestDelegates, home: Scaffold( body: Column( children: [ diff --git a/apps/learn/halkyon_learn_flutter/test/core/editor/rich_content_document_test.dart b/packages/halkyon_editor/test/editor/rich_content_document_test.dart similarity index 93% rename from apps/learn/halkyon_learn_flutter/test/core/editor/rich_content_document_test.dart rename to packages/halkyon_editor/test/editor/rich_content_document_test.dart index 6a494a18..06f8aa29 100644 --- a/apps/learn/halkyon_learn_flutter/test/core/editor/rich_content_document_test.dart +++ b/packages/halkyon_editor/test/editor/rich_content_document_test.dart @@ -1,7 +1,6 @@ import 'package:appflowy_editor/appflowy_editor.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'package:halkyon_learn_flutter/src/core/content/rich_content.dart'; -import 'package:halkyon_learn_flutter/src/core/editor/rich_content_document.dart'; +import 'package:halkyon_editor/halkyon_editor.dart'; void main() { TestWidgetsFlutterBinding.ensureInitialized(); diff --git a/apps/learn/halkyon_learn_flutter/test/core/editor/rich_content_editor_test.dart b/packages/halkyon_editor/test/editor/rich_content_editor_test.dart similarity index 84% rename from apps/learn/halkyon_learn_flutter/test/core/editor/rich_content_editor_test.dart rename to packages/halkyon_editor/test/editor/rich_content_editor_test.dart index 3c2780ee..54c4d6d2 100644 --- a/apps/learn/halkyon_learn_flutter/test/core/editor/rich_content_editor_test.dart +++ b/packages/halkyon_editor/test/editor/rich_content_editor_test.dart @@ -1,16 +1,16 @@ import 'package:appflowy_editor/appflowy_editor.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'package:halkyon_learn_flutter/src/core/content/rich_content.dart'; -import 'package:halkyon_learn_flutter/src/core/editor/rich_content_editor.dart'; -import 'package:halkyon_learn_flutter/src/core/l10n/app_languages.dart'; +import 'package:halkyon_editor/halkyon_editor.dart'; import 'package:material_ui/material_ui.dart'; +import '../support/test_app.dart'; + void main() { testWidgets('a click in an editor shorter than its content selects there, ' 'and bringing the selection into view comes to an end', (tester) async { await tester.pumpWidget( MaterialApp( - localizationsDelegates: appLocalizationsDelegates, + localizationsDelegates: editorTestDelegates, home: Scaffold( body: ListView( children: [ diff --git a/packages/halkyon_editor/test/editor/rich_content_view_test.dart b/packages/halkyon_editor/test/editor/rich_content_view_test.dart new file mode 100644 index 00000000..2b75c0e6 --- /dev/null +++ b/packages/halkyon_editor/test/editor/rich_content_view_test.dart @@ -0,0 +1,252 @@ +import 'package:appflowy_editor/appflowy_editor.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:halkyon_editor/halkyon_editor.dart'; +import 'package:material_ui/material_ui.dart'; + +import '../support/test_app.dart'; + +void main() { + RichContent paragraph(String text) => RichContent({ + 'document': { + 'type': 'page', + 'children': [ + { + 'type': 'paragraph', + 'data': { + 'delta': [ + {'insert': text}, + ], + }, + }, + ], + }, + }); + + Future show(WidgetTester tester, RichContent content) async { + await tester.pumpWidget( + MaterialApp( + localizationsDelegates: editorTestDelegates, + home: Scaffold( + body: ListView(children: [RichContentView(content: content)]), + ), + ), + ); + // Flushes the editor's timers. + await tester.pump(const Duration(seconds: 1)); + } + + testWidgets('shows a table whose sizes are whole numbers, or missing, as ' + 'JSON written elsewhere may hold them', (tester) async { + Map cell(int column, int row, Map data) => + { + 'type': 'table/cell', + 'data': {'colPosition': column, 'rowPosition': row, ...data}, + 'children': [ + { + 'type': 'paragraph', + 'data': { + 'delta': [ + {'insert': '$column$row'}, + ], + }, + }, + ], + }; + for (final (table, cells) in [ + // As an archive from elsewhere brought it: whole numbers. + ( + { + 'colsLen': 2, + 'rowsLen': 1, + 'colsHeight': 82, + 'colDefaultWidth': 160, + 'rowDefaultHeight': 40, + 'colMinimumWidth': 40, + }, + {'width': 80, 'height': 40}, + ), + // Without the editor's sizes. + ({'colsLen': 2, 'rowsLen': 1}, {}), + ]) { + await show( + tester, + RichContent({ + 'document': { + 'type': 'page', + 'children': [ + { + 'type': 'table', + 'data': table, + 'children': [cell(0, 0, cells), cell(1, 0, cells)], + }, + ], + }, + }), + ); + + expect(tester.takeException(), isNull); + expect(find.textContaining('00', findRichText: true), findsOneWidget); + expect(find.textContaining('10', findRichText: true), findsOneWidget); + } + }); + + testWidgets('a row holding a cell of several lines grows to fit it, read ' + 'only as in the editor', (tester) async { + Map cell(int row, String text) => { + 'type': 'table/cell', + 'data': {'colPosition': 0, 'rowPosition': row}, + 'children': [ + { + 'type': 'paragraph', + 'data': { + 'delta': [ + {'insert': text}, + ], + }, + }, + ], + }; + await show( + tester, + RichContent({ + 'document': { + 'type': 'page', + 'children': [ + { + 'type': 'table', + 'data': {'colsLen': 1, 'rowsLen': 2}, + 'children': [cell(0, 'る\nう\nつ'), cell(1, 'す')], + }, + ], + }, + }), + ); + await tester.pump(); + + final cells = find.byType(TableCelBlockWidget); + expect( + tester.getSize(cells.at(0)).height, + greaterThan(2 * tester.getSize(cells.at(1)).height), + ); + }); + + testWidgets('shows the content', (tester) async { + await show(tester, paragraph('猫は動物です。')); + + expect(find.textContaining('猫は動物です。', findRichText: true), findsOneWidget); + }); + + testWidgets('shows new content when it changes', (tester) async { + await show(tester, paragraph('猫')); + await show(tester, paragraph('犬')); + + expect(find.textContaining('犬', findRichText: true), findsOneWidget); + expect(find.textContaining('猫', findRichText: true), findsNothing); + }); + + testWidgets('is display only: a tap on its text goes to what surrounds it, ' + 'and no keyboard opens', (tester) async { + var tapped = 0; + await tester.pumpWidget( + MaterialApp( + localizationsDelegates: editorTestDelegates, + home: Scaffold( + body: ListView( + children: [ + GestureDetector( + onTap: () => tapped++, + child: RichContentView(content: paragraph('猫は動物です。')), + ), + ], + ), + ), + ), + ); + await tester.pump(const Duration(seconds: 1)); + + await tester.tap(find.textContaining('猫', findRichText: true)); + await tester.pump(const Duration(seconds: 1)); + + expect(tapped, 1); + expect(tester.testTextInput.hasAnyClients, isFalse); + }, variant: TargetPlatformVariant.all()); + + group('asked to centre', () { + Map block( + String type, + String text, [ + Map data = const {}, + ]) => { + 'type': type, + 'data': { + ...data, + 'delta': [ + {'insert': text}, + ], + }, + }; + + Future showCentred( + WidgetTester tester, + List> blocks, + ) async { + tester.view.physicalSize = const Size(400, 800); + tester.view.devicePixelRatio = 1; + addTearDown(tester.view.reset); + await tester.pumpWidget( + MaterialApp( + localizationsDelegates: editorTestDelegates, + home: Scaffold( + body: ListView( + children: [ + RichContentView( + content: RichContent({ + 'document': {'type': 'page', 'children': blocks}, + }), + textAlign: TextAlign.center, + ), + ], + ), + ), + ), + ); + await tester.pump(const Duration(seconds: 1)); + } + + double centreOf(WidgetTester tester, String text) => + tester.getCenter(find.text(text, findRichText: true)).dx; + double leftOf(WidgetTester tester, String text) => + tester.getTopLeft(find.text(text, findRichText: true)).dx; + + testWidgets('centres paragraphs and headings', (tester) async { + await showCentred(tester, [ + block('heading', 'Greetings', {'level': 2}), + block('paragraph', 'hola'), + ]); + + expect(centreOf(tester, 'Greetings'), moreOrLessEquals(200, epsilon: 1)); + expect(centreOf(tester, 'hola'), moreOrLessEquals(200, epsilon: 1)); + }); + + testWidgets('leaves a list at the start: centred, its markers would ' + 'drift apart', (tester) async { + await showCentred(tester, [ + block('bulleted_list', 'hola'), + block('bulleted_list', 'buenos días'), + ]); + + expect(leftOf(tester, 'hola'), lessThan(60)); + expect(leftOf(tester, 'buenos días'), leftOf(tester, 'hola')); + }); + + testWidgets('keeps the alignment a paragraph was given in the editor', ( + tester, + ) async { + await showCentred(tester, [ + block('paragraph', 'hola', {'align': 'left'}), + ]); + + expect(leftOf(tester, 'hola'), lessThan(40)); + }); + }); +} diff --git a/packages/halkyon_editor/test/l10n/arb_files_test.dart b/packages/halkyon_editor/test/l10n/arb_files_test.dart index 485a601e..700708e2 100644 --- a/packages/halkyon_editor/test/l10n/arb_files_test.dart +++ b/packages/halkyon_editor/test/l10n/arb_files_test.dart @@ -56,7 +56,10 @@ void main() { test("the English variants override the template's messages only, with " 'the same placeholders', () { - for (final variant in ['halkyon_editor_en_GB.arb', 'halkyon_editor_en_US.arb']) { + for (final variant in [ + 'halkyon_editor_en_GB.arb', + 'halkyon_editor_en_US.arb', + ]) { for (final MapEntry(:key, :value) in _messages(variant).entries) { expect(template, contains(key), reason: '$variant: $key'); expect( diff --git a/packages/halkyon_editor/test/l10n/halkyon_editor_localizations_test.dart b/packages/halkyon_editor/test/l10n/halkyon_editor_localizations_test.dart new file mode 100644 index 00000000..e1669fc4 --- /dev/null +++ b/packages/halkyon_editor/test/l10n/halkyon_editor_localizations_test.dart @@ -0,0 +1,21 @@ +import 'package:flutter_test/flutter_test.dart'; +import 'package:halkyon_editor/halkyon_editor.dart'; +import 'package:material_ui/material_ui.dart'; + +void main() { + test('each language speaks its own, American English only where it ' + 'differs', () { + final british = lookupHalkyonEditorLocalizations( + const Locale('en', 'GB'), + ); + final american = lookupHalkyonEditorLocalizations( + const Locale('en', 'US'), + ); + final german = lookupHalkyonEditorLocalizations(const Locale('de')); + + expect(british.formatAlignCenter, 'Centre'); + expect(american.formatAlignCenter, 'Center'); + expect(american.formatBold, british.formatBold); + expect(german.formatBold, isNot(british.formatBold)); + }); +} diff --git a/packages/halkyon_editor/test/support/test_app.dart b/packages/halkyon_editor/test/support/test_app.dart new file mode 100644 index 00000000..d775a262 --- /dev/null +++ b/packages/halkyon_editor/test/support/test_app.dart @@ -0,0 +1,10 @@ +import 'package:halkyon_editor/halkyon_editor.dart'; +import 'package:halkyon_ui/halkyon_ui.dart'; +import 'package:material_ui/material_ui.dart'; + +/// The texts an app installs for the editor. +const editorTestDelegates = >[ + HalkyonEditorLocalizations.delegate, + HalkyonUiLocalizations.delegate, + ...GlobalMaterialLocalizations.delegates, +]; diff --git a/third_party/README.md b/third_party/README.md index 8100e840..9c5fb803 100644 --- a/third_party/README.md +++ b/third_party/README.md @@ -15,7 +15,7 @@ compilerOptions { jvmTarget } }` block added with the same JVM target. | Plugin | Release | Licence | Used by | |---|---|---|---| | `flutter_web_auth_2` | 5.1.0 | MIT | `halkyon_oidc_flutter` (signing in with SimpleLogin) | -| `keyboard_height_plugin` | 0.3.0 | BSD-3-Clause | `appflowy_editor` (Learn's notes) | +| `keyboard_height_plugin` | 0.3.0 | BSD-3-Clause | `appflowy_editor` (`halkyon_editor`, Learn's notes) | When a plugin's own release has migrated, take it from pub.dev again: drop its override and its folder here. Before then, a newer release is brought in by copying