# The Linux app as a Snap, built in LXD on a developer's machine, in the repository # root: # # snapcraft pack # # It builds what is committed (the source is this repository's HEAD), never the # working tree with its build caches, and always for learn.halkyon.eu. Flutter is # pinned like the local and CI one (FLUTTER_VERSION in .forgejo/workflows/ci.yaml): # snapcraft's own Flutter plugin can only take a channel and can't pass # --dart-define, so the build is spelled out below. name: halkyon-learn title: Halkyon Learn base: core24 adopt-info: halkyon-learn # the version, from the app's pubspec.yaml summary: Learn with flashcards, studied offline and synced between your devices description: | Halkyon Learn is a spaced-repetition app for your own material: studysets of flashcards, notes, learning paths and PDFs, reviewed on an FSRS schedule. It works offline and syncs between your devices through an instance anyone can run. Learn from what you watch: play films and shows from your Jellyfin server, and every subtitle line is split into dictionary words, with readings above and the translation below. Tap a word, and it is a flashcard. Dictionaries for Japanese, Chinese, Korean, German, Greek and English; kanji, hanzi and Hangul written by hand, each stroke checked; exam plans for the JLPT, HSK and Korean levels, with a daily pace to your exam's date; and studysets published to Explore and copied, later releases coming in as merges that keep your own edits. It starts with an anonymous account; signing in with GitHub later keeps everything. It connects to learn.halkyon.eu, the project's instance. Questions and reports: support@halkyon.app license: AGPL-3.0-or-later source-code: https://code.halkyon.app/project-halkyon/halkyon_learn # The project's, not an instance's: the app works with any instance. website: https://halkyon.app/learn # No contact or issues: the Store refuses an upload whose links aren't http(s) URLs # ("Links must be http(s):// URLs", checked with snapcraft v9.1.4 on 2026-10-14), # though snapcraft's reference allows mail addresses there (canonical/snapcraft#6062 # asks for them). Reports come by mail until people can open an account on the issue # tracker, so the address is in the description. # The app's icon with rounded corners, made by tool/app_icons.py; also the desktop file's. icon: snap/gui/halkyon-learn.png grade: stable confinement: strict platforms: amd64: lint: ignore: # PDFium is loaded by name while the app runs, which the linter can't see. - library: - bin/lib/libpdfium.so apps: halkyon-learn: command: bin/halkyon_learn_flutter # How the session is kept: see snap/local/secret-backend. command-chain: [bin/secret-backend] # GTK, WebKitGTK, GStreamer and libsecret come from the GNOME runtime; files # and the browser go through the desktop's portals. extensions: [gnome] plugs: - network # GitHub sign-in answers on a loopback address the app listens on. - network-bind - audio-playback # Never connected by itself; whoever connects it by hand gets the keyring. - password-manager-service # Lets Serverpod hear from NetworkManager that the network went, and end # the sync's connection at once rather than after 40 s of silence. Not # connected by itself until the Store grants it on a forum request; # without it the app logs the refusal once and works on. - network-manager-observe parts: flutter: plugin: nil source: https://github.com/flutter/flutter.git source-tag: '3.47.5' source-depth: 1 build-packages: - clang - cmake - curl - git - libgtk-3-dev - lld - llvm-18 - ninja-build - pkg-config - unzip - xz-utils - zip override-build: | mkdir -p "$CRAFT_PART_INSTALL/usr/libexec" "$CRAFT_PART_INSTALL/usr/bin" cp -r "$CRAFT_PART_SRC" "$CRAFT_PART_INSTALL/usr/libexec/flutter" ln -s ../libexec/flutter/bin/flutter "$CRAFT_PART_INSTALL/usr/bin/flutter" ln -s ../libexec/flutter/bin/dart "$CRAFT_PART_INSTALL/usr/bin/dart" "$CRAFT_PART_INSTALL/usr/bin/flutter" --disable-analytics "$CRAFT_PART_INSTALL/usr/bin/flutter" precache --linux # Needed to build, not in the Snap. override-prime: '' halkyon-learn: after: [flutter] plugin: nil source: . source-type: git # The sound player links GStreamer, which the GNOME SDK lacks; the GNOME # runtime brings the same GStreamer (1.24) to run. The video player # (media_kit, for Jellyfin) links libmpv, which neither has: the Snap # brings Ubuntu's libmpv2 with what it needs (ffmpeg's libraries), or the # app wouldn't start. build-packages: - libgstreamer1.0-dev - libgstreamer-plugins-base1.0-dev - libmpv-dev stage-packages: - libmpv2 override-build: | set -eu export PATH="$CRAFT_STAGE/usr/bin:$PATH" cd halkyon_learn_flutter craftctl set version="$(sed -n 's/^version: *\([^+]*\).*/\1/p' pubspec.yaml)" flutter pub get --enforce-lockfile flutter build linux --release \ --dart-define=SERVER_URL=https://learn.halkyon.eu/ mkdir -p "$CRAFT_PART_INSTALL/bin" cp -r build/linux/x64/release/bundle/. "$CRAFT_PART_INSTALL/bin/" # ffmpeg's speech recognition (libsphinxbase) links libblas.so.3 and # liblapack.so.3, which Ubuntu keeps in blas/ and lapack/ and links into # the library folder from its packages' install scripts # (update-alternatives), which staging never runs: without these links # the app doesn't start. The linter doesn't notice, as the build # machine has them. lib="$CRAFT_PART_INSTALL/usr/lib/$CRAFT_ARCH_TRIPLET_BUILD_FOR" mkdir -p "$lib" ln -sf blas/libblas.so.3 "$lib/libblas.so.3" ln -sf lapack/liblapack.so.3 "$lib/liblapack.so.3" secret-backend: plugin: dump source: snap/local organize: secret-backend: bin/secret-backend # Staged packages bring their whole dependency tree, also what the GNOME # runtime and the base hold already: libmpv's ffmpeg brought Ubuntu's older # librsvg, cairo, pango, harfbuzz and gdk-pixbuf, which came first on the # library path and hid the runtime's newer ones from its GTK (its SVG # loader failed for a function only the runtime's librsvg has). So every # file the runtime or the base has at the same path goes, as the GNOME # extension's reference shows, but only under usr/ and never a directory: # its example removes every path the base has, and core24's bin and lib are # links, so it would take the app's bin/ with them. blas/ and lapack/ stay: # the runtime has them too, but the links into them are the app's own. cleanup: after: [halkyon-learn, secret-backend] plugin: nil build-snaps: [core24, gnome-46-2404] override-prime: | set -eu for snap in core24 gnome-46-2404; do cd "/snap/$snap/current/usr" find . \( -type f -o -type l \) -print0 | while IFS= read -r -d '' path; do case "$path" in */blas/* | */lapack/*) continue ;; esac target="$CRAFT_PRIME/usr/$path" if [ -L "$target" ] || [ -f "$target" ]; then rm -f "$target" fi done done