Commit graph halkyon/halkyon_learn_flutter/macos
Author SHA1 Message Date
da51ef73cb
feat(jellyfin): watch from one's own Jellyfin server and learn the words of its subtitles
Animelon's way of learning from video, from the person's own Jellyfin
server: connected per device (Settings, or a studyset's Add material ›
Watch from Jellyfin) with a name and password or a Quick Connect code,
over HTTPS only (plain HTTP only to a server on the same machine); the
token stays in the system's keyring. Shows, films and videos tied to a
studyset follow the person in their settings as the server's and the
item's ids alone (settings schema 9), named by the server when shown.

The Watch screen plays the video (video_player on Android, media_kit's
libmpv on Linux and Windows) with the line in the studyset's front
language cut into the dictionary's words with furigana, and the back
language's line beneath; a word tapped pauses the video and becomes a
card with the line and its translation as its first hint, tagged with
the show. The video's words are listed, most frequent first. The sound is
the track in the studyset's language, whichever the file marks default,
and the menu switches it; the subtitles read are a whole track, never a
forced one (which only covers what the sound doesn't say), and captions
for the hard of hearing only without plain ones. The line and its
translation take the video's width in a box of a fixed height, so the
video keeps its size; in the dark theme the video's colours glow around
it, as YouTube's ambient mode, and the words' panel takes the colour of
the episode's picture under the pointer.

A line is cut into words with one request: DictionaryEndpoint.withKeys
looks up every stretch of it that may be a word, as it stands and as
every word it may be a conjugation of, in the new dictionary_key table
(one row per key, indexed; migration dictionary-keys adds the table and
its index, nothing else), which the loader fills beside the entries.
The instance gets the text of each line looked up and keeps none of it,
as the Watch screen says.

Linux builds need libmpv-dev, and the app links libmpv: the snap stages
Ubuntu's libmpv2, makes the update-alternatives links for BLAS and LAPACK
that ffmpeg's speech recognition needs (staging never runs install
scripts), and removes what the GNOME runtime and core24 already hold, as
older copies of cairo, pango and librsvg hid the runtime's from its GTK.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-13 21:21:48 +02:00
e291d3af7a
fix(app): the app has its own icon instead of Flutter's
All checks were successful
CI / check (push) Successful in 5s
CI / deploy (push) Successful in 1m3s
The launcher showed Flutter's logo: the project template's icons were
never replaced. tool/app_icons.py now makes every icon from the
kingfisher (assets/icon/halkyon_icon.png), through flutter_launcher_icons
v0.14.4 configured in the app's pubspec: Android's adaptive icon (the
kingfisher inset 16 % on white, for each launcher to cut to its shape)
and the square one before Android 8; iOS's, without transparency;
macOS's, the rounded tile on Apple's grid (824 of 1024 pixels); and
Windows', the rounded tile, its .ico with every size from 16 to 256
pixels (the tool writes only 256). It keeps the iOS project file as it
was, which the tool would break (flutter_launcher_icons issue 634), and
writes the snap's icon as tool/snap_icon.py did, which it replaces.

The Linux runner gives its window the bundled icon too, for desktops
that don't take it from the desktop file.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-06 22:49:25 +02:00
e35ce5c81d
feat: the app tells when sync fails, and when it is too old
Published apps can't be made to update, so the app now notices when it
no longer matches the server: the server names the oldest app build it
works with (instance.info's minimumAppBuild), asked before the first
sync of each launch, and the sync engine's refusal of differing synced
tables counts the same. Either way the app says "update the app; if it
is up to date, the server needs an update" in a banner on Home and in
the new Sync card in Settings, and what the person does meanwhile stays
on the device.

The Sync card shows where sync stands since the app started (syncing,
everything synced, sync failing, with the last completion and failure)
and offers Sync now. While the last sync failed, a dot sits on the
Profile tab and the Settings cog. One sync that goes through clears it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-05 13:00:38 +02:00
b96db731c1
feat: PDF sources: read, take notes and cards from pages, deadlines
Some checks failed
CI / check (push) Failing after 26s
CI / deploy (push) Has been skipped
PDFs live in a studyset's new Files tab: uploaded from Add material
(a file the studyset holds already asks first), each row with its
pages, cards, notes and size, how far it is read and its reading
deadline; renamed, given a deadline (a page or a chapter by a day),
marked as read, started afresh, deleted with a moment to undo.

The reader (/pdfs/:id) opens where reading left off: on wide windows
a rail of tools (search, fit, zoom, fullscreen), a page counter with
the chapter, and a sidebar; on phones a top bar, a page scrubber and
a sheet. The sidebar holds the PDF's flashcards, by page or chapter,
made from selected text or written there, each tied to its page and
tagged with its chapter; and the page's note, an ordinary note tied
to the page. Screenshots of a region become images that know their
page. Cards and screenshots lead back to their page; reading on past
a chapter with cards suggests studying it. Home shows the deadlines
running and to come, and a timeline of them.

Server: pdf_source rows (content in the payload, last writer wins)
and an append-only pdf_reading log, the furthest page derived from
it so devices never conflict. Files up to 50 MB for PDFs (20 MB for
others; a larger file that isn't a PDF is deleted once stored). A
deleted PDF takes its file's asset row along unless another PDF has
the same file. Card payload 4 and note payload 2 keep the page.

The reader draws with pdfrx (PDFium) behind PdfEngine in
Dependencies; widget tests use a fake, and
integration_test/pdf_engine_test.dart checks the real engine on a
device or the desktop.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-03 14:40:08 +02:00
ec34bb2334
feat(app): editor blocks: callout, spoiler, formulas, files, video, study button
All checks were successful
CI / check (push) Successful in 8m34s
CI / deploy (push) Successful in 1m1s
Blocks beyond the editor's own, each built once for editor and view:
- callout: eight kinds in theme colours, kind, title and "folded at
  first" from its header; written in place, left with Enter on its
  empty last line; never inside another
- spoiler: written in plain sight, hidden behind Reveal read-only
- formula on its own line, written on its own screen with a preview
  and snippet buttons, and formulas within a line (Σ in the format
  bar); set by flutter_math_fork behind MathView, LaTeX's numbered
  environments read as their aligned forms
- image and sound picked from the device, kept as synced files
- YouTube video from its link, with its start and a caption: a card
  until played, nothing fetched before; played in place where a web
  view exists (youtube_player_iframe, privacy-enhanced), else opened
  in the browser, behind YoutubeVideos in Dependencies
- study button: a studyset, a tag or all cards, with or without its
  topics, back first; its StudyFocus rides on the study route and
  narrows that session only
- tables, already in the editor, now insertable everywhere

An Insert button opens on the format bar, so blocks can be inserted on
phones too; the desktop's / menu lists the same blocks, translated.
Card faces now dock the format bar while written, so cards get blocks
and formatting on phones as well. The blocks are listed in
EditorBlocks: the core's, plus the features' through
EditorBlocksScope. Blocks of a type the app doesn't know say they need
a newer version and stay as they are, so formatVersion stays 1.

Their types and keys are in content_blocks.dart, which RichContent
reads: a block's text counts as text, and a video or study button
isn't blank. The path import now makes formulas real ones.

integration_test/youtube_player_test.dart plays a real video through
the app's player on a device, which no widget test can: YouTube
refuses embeds that don't say where they are embedded.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-03 12:20:41 +02:00
a6d5769a2e
Import Anki decks with their files and history
Files in content: the person's images and sounds as asset rows synced like
all content, their files kept by hash on each device, uploaded after each
sync, downloaded on demand and checked against their hash
(core/asset/AssetRepository). Rich text names them as asset:<id> URLs, in
the editor's image block and an audio block of ours; any other URL is never
fetched. Sounds play through audioplayers, one at a time. Content holding
only an image or a sound isn't blank, and lists show it as such.

The Anki import ("Import from Anki" under Add material): .apkg and .colpkg
packages in all three formats Anki has written, read on an isolate of their
own. Modern packages compress everything with zstd in frames that don't
record their size, which pure_zstd v1.0.0 refuses; our fork decodes them.
Fields become rich text by a converter of our own (lines, lists, formatting,
images, sounds, cloze deletions hidden on the front). One card per note,
the files first, studysets as chosen (flatten, sub-studysets, or tags by
subdeck), the notes' tags, and, if wanted, the review history, which FSRS
replays: the first template's reviews to the card's schedule, a reversed
note type's second to the reverse one.

Imported cards say where they came from in their first revision (card
schema 3); the dashboard doesn't count them as written. The test fixtures
are exported by Anki itself (tool/anki/make_fixtures.py).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-02 05:11:50 +02:00
09a3904d4c
build(app): register the editor's plugins on macOS
Flutter regenerated the macOS plugin registrant for the two native plugins the editor brings, device_info_plus and file_picker_darwin.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-09-30 20:38:13 +02:00
a4fcd98e4a
chore: set app ID to app.halkyon.learn and titles to 'Halkyon Learn'
Furthermore, sets the copyright holder on Windows and macOS.

Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-09-24 15:32:30 +02:00
e17d99fa51
chore: scaffold the Serverpod 4 build
Deviations from a stock `serverpod create`:
- Dropped Flutter web support, for two reasons:
  - Appflowy editor had issues in the past, potentially related to the
    Flutter-Web IME bridge. Upstream has also open bugs in relation to
that:
    - AppFlowy-IO/appflowy-editor#1029 [Bug][Web] Unwanted space on click
       and overall space bugs (open)
    - AppFlowy-IO/appflowy-editor#782 [Bug] Editor auto-scrolling when
       pressing space too quickly if placed inside a scrollable (open)
  - Offline-first is a required feature. serverpod_offline_sync's SQLite
    support in browsers, maybe via sqlite3.wasm + OPFS, is not
documented.
- SDK constraints raised to ^3.13.0, instead of ^3.12.2, so enjoy the
  newest language features.
- Pinned to Flutter 3.47.4 via FVM.

Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-09-24 15:32:29 +02:00