A sync test of the public tier as a whole: the author's device syncs a
studyset (a topic, tagged cards, a note, an exam plan, an exam of their
own), the instance builds the first release from what it holds, another
person finds it in Explore and adds it to their library (the topic, the
tags, the note and the plan's topic come along, the exam stays the
author's), a second release is offered and taken while the learner's own
edit stays, and a third is offered no more once the author withdraws.
Until now each half had tests of its own only: the server's publish from
rows written into its database, the app's copy and update from releases
through fakes.
A test can't sign in to GitHub, so it gives the author the identified
scope by SQL, on the person and their sessions, as linking GitHub does.
The devices' clients are exposed for what lives on the instance rather
than in the synced data.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
A studyset plans for exams: each plan names what an exam covers, the
topics it takes whole (with their flashcards, learning paths and PDFs)
and the tags whose flashcards it takes from anywhere in the studyset.
The author's plans are published with the studyset (payload schema 7,
`examPlans`); copying a release and taking a newer one point their
topics at the copy's own studysets. Plans are also offered from level
tags: JLPT N5 to N1, HSK 1 to 7-9 and KRDict's levels, each taking the
levels below it, as the exams do.
The person chooses a plan and the exam's day (`exam`), which stays
theirs: the server leaves it out of a release, as a PDF's deadline.
The study plan then shows the days left, how many of the plan's
flashcards were started, its paths' steps done and its PDFs' pages
read, and the pace that gets everything new learnt a week before the
exam (by the exam when two weeks or fewer are left): new flashcards a
day, steps a week, pages a day. Study for the exam studies only what
the plan covers, its new cards at that pace (counted as the day began)
and every review due.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
People testing the app looked for the material of a studyset that groups
topics in its own empty tabs, which said "No flashcards yet" and how to
add some. A tab with none of its own while the topics hold some now says
so: "The flashcards are in its topics", each topic holding some with how
much (everything inside it counted), opening it; for flashcards Browse
all of them in one list; and, quieter, how to add some here after all.
Opened for the first time, such a studyset shows the first tab its topics
fill. The tabs' counts stay the studyset's own.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
Questions asked from the app went to Claude alone. Settings › Assistant
now chooses where they open (decided 2026-10-14): Lumo, the default, by
its guest chat with the question in the address's fragment (Lumo's
`prefill`, which needs no account and reaches no server; the address for
one's own account loses the question when nobody is signed in), ChatGPT,
Claude, Mistral, Perplexity, Duck.ai, Grok, Kagi Assistant, HuggingChat,
or an address of one's own with %s. Each link fills the question in for
the person to send (Perplexity answers at once, Grok asks first); Gemini,
which has no such link, and Copilot, whose stopped working, are left out.
The field names the assistant ("Ask Lumo…"). A device that sent questions
to Claude goes on asking an assistant, now Lumo. Connecting Claude to read
studysets stays as it was.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
What applies of Japanese's features to Chinese, Korean, Greek, German and
English, with the developer's choices and each decision's reason: Unihan's
characters, KRDict's words and levels, Make Me a Hanzi's and the curator's
own strokes, typed pinyin, radicals and readings by language; what doesn't
apply or has no open data. AGENTS.md, the server's README and the
instance's example environment follow.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
Korean names a Hanja by its meaning, then its sound; simplified Chinese
shares some radicals' forms with Japanese (麦), which showed twice, and
writes 黑 as itself. A test can read a language's strokes beforehand.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
"Ich rufe dich morgen an": where a clause ends in a separable particle,
each word before it is also asked for with the particle joined on, as a
subordinate clause writes the verb (anrufe), which Wiktionary lists among
anrufen's forms. The verb is offered beside its stem and its particle,
as the longer words running over a Japanese word are.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
Search folded case and katakana only, for Japanese. It now also leaves out
accents, tone marks and umlauts (strasse finds Straße, maedchen and
madchen Mädchen, σπιτι σπίτι, xue xi xué xí), and A–Z sorts by the same
bare letters, so Ärger stands among the a's.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
The library's cards and Explore's drew Chinese and Korean titles with
Japanese shapes first (骨, 直): each card now sets its studyset's
languages, as every screen inside a studyset does.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
Parity with Japanese's kanji and kana:
- The characters' tab is named by its language (Hanzi, Hangul & Hanja,
Alphabet), its groups too: HSK levels and the characters in common use
(Hong Kong's grades for traditional ones); Hangul and the Hanja taught
in schools; Greek's letters. Radicals for Chinese and Korean in their
own forms (讠 for 言 in simplified Chinese), none for Greek.
- A character's readings are named as its language names them: On and
Kun, Pinyin, Eum and Hun (음, 훈); a letter means its sound and name.
- How characters are written comes by language: Chinese from Make Me a
Hanzi (its medians in KanjiVG's square, Arphic Public License, a data
file beside the app, never in cards), the Hangul letters drawn by the
curator; a Korean word is written letter by letter (닭: ㄷ ㅏ ㄺ). Words
of alphabets (German, Greek) aren't written by hand.
- A Chinese word's pinyin is typed in a session, tone numbers turning
into marks (ni3 → nǐ); KRDict's levels (초급, 중급, 고급) tag Korean
words; a Korean word's pronunciation shows in brackets ([가치]).
- Smaller: Look up in the composer keeps a noun's article; the Watch
filter names its level list; hints and placeholders by language.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
Korean's words move from Wiktionary to KRDict, and an operator may keep
fewer languages: the curator removes those files, and the loader now
unloads every source no file holds, so a language never has two word
dictionaries. Only once every file in the folder could be read, as an
unreadable one may be the dictionary that seems gone.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
For parity with Japanese's KANJIDIC2 and kana:
- Unihan (Unicode License v3) makes characters' dictionaries: the 8,105
standard simplified characters (unihan-hans) and the traditional ones in
use (unihan-hant), told apart and ranked by CC-CEDICT's words (Unihan
calls 这 its own traditional variant; 後 ranks as the 后 of 以后), with
pinyin, meanings, strokes, Kangxi radical, HSK 2025 character levels,
the 3,500 common ones and Hong Kong's grades; and Korean Hanja
(unihan-ko) with their sounds, the 1,800 of education, and their native
readings from Wiktionary (물 수), with the 40 Hangul letters as kana are.
- The Greek alphabet, the curator's own (CC0): letters with names,
transliteration and sound.
- KRDict, the Basic Korean Dictionary for learners (CC BY-SA 2.0 KR,
downloaded without an account), replaces Wiktionary for Korean's words:
learners' levels, pronunciation where it differs from the spelling
(같이 [가치]), Hanja, English senses; Wiktionary's update keeps the
words' forms (가다's 99) and the Hanja's readings beside it for both.
- The update converts KRDict when its page offers another export and the
characters weekly, and removes the files of languages no longer kept
or of sources replaced.
Measured on the real data: Unihan 3 s; KRDict 54,870 entries in 24 s
and under 600 MB.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
Korean's 이 has thirteen entries in Wiktionary (tooth, this, two, a name
…), each of which took the syllable's frequency, so all thirteen were
common and adding Korean's common words would add thirteen cards of
이. Only a word's first three entries, in Wiktionary's order, which
puts the usual meanings first, take its frequency now; the rest rank
after the common words.
The quick look before reading a line of the dump as JSON accepts it
with or without a space after the colon, so it still finds the
languages should kaikki.org write its JSON otherwise.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
Wiktionary tags a contraction as an abbreviation and an alternative of
its first word (im: in + dem), so German's im, zum and ins, which every
subtitle has, were neither entries nor found as forms. A contraction is
now an entry of its own, as text writes it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
As a Japanese word's says its JLPT level: HSK 1 … HSK 6, HSK 7–9. The
levels' scheme (`WordLevels`, `levelOf`) moves from the Watch screen's
filter into the dictionary's model, which both name levels with.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
JMdict brings Tatoeba's sentences for Japanese; the other languages'
dictionaries had none (CC-CEDICT) or Wiktionary's own, often
quotations. Each file now takes Tatoeba's sentences of the week (its
per-language exports, CC BY 2.0 FR) as its entries' first examples,
which become a card's hints: each sentence with a translation into the
dictionary's meaning language goes to the entries its words find, cut
as the app cuts a line of subtitles (at spaces, Korean's particles off,
Chinese by its longest words), up to three for each, those using the
word as its headword writes it first (Haus rather than zu Hause; 学习
rather than 學習 in the simplified dictionary), then those nearest seven
words long. German's common words nearly all get some (8,849 of
9,292), in ten seconds and under 300 MB; a dictionary whose sentences
can't be had keeps its words. `curate tatoeba` does the same for a file
made by hand.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
A studyset in German, Greek, English, Chinese or Korean finds its
dictionary as one in Japanese does (ADR 0008), and its subtitles are cut
into words by its language (`WordCutting`): Chinese by its longest
words as they stand, the longer words over each cut offered as for
Japanese; Korean at its spaces, a word the dictionary lacks as it stands
taken without the particle it ends with (집에서 is 집, 아이 stays a
child); every other language at its spaces, a word written exactly so
first (the noun Essen before the verb essen). A studyset finds the
dictionary of its language tag, else of its language alone (zh for
zh-Hans).
A Wiktionary word shows its genders, principal forms, IPA, romanisation
and Hanja; a German or Greek noun of one gender goes onto its card with
its article (das Haus, το σπίτι); its tag is its word class, named in
full (Noun, Adjective, Preposition), as Wiktionary's adj and adv would
read as JMdict's codes. Articles, pronouns, prepositions and
conjunctions are left out of a video's words. Chinese words show their
pinyin over each character on the Watch screen, carry their HSK level
as a tag (HSK-1 … HSK-7-9), and the dictionary adds a level whole; the
Watch screen filters by HSK levels for Chinese, JLPT's for Japanese, and
by none for a language without. A reading is typed in a session only
when it is kana, so pinyin isn't. Words are looked up on the web on
Jisho in Japanese, on Wiktionary at the language's section otherwise,
and a search's hint speaks of kana and romaji only in Japanese.
`DICTIONARY_LANGUAGES` in deploy/.env names the languages an instance
keeps (unset: de,el,en,ko,zh; empty: none).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
A dictionary file may name an entry's `textKeys`, its own forms: those
alone fill `dictionary_key`, so a German word is found in a subtitle by
its forms, never by its English meanings, which would match English
words in German text; a search still finds it by them. Files without
them (JMdict's) fill it with every key without a space, as before.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
Korean comes from the same dump as German and Greek, with English
meanings (decided 2026-10-14: Korean last, from Wiktionary for now): a
verb's conjugations are among its forms, so text finds them; a word's
Hanja and romanisation find it in a search and show beside it. The
counter words Wiktionary's tables name and its notes on what it couldn't
read are no forms of a word.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
The syllabus's reading is matched with its tones first, so 和 hé gets
HSK 1 and 和 hè doesn't; only where no entry of a word reads it so (the
syllabus marks the tone changes of 一 and 不, 一下 yíxià, CC-CEDICT
doesn't) are the letters alone compared. CC-CEDICT's 2,885 entries that
are only "variant of …" (咊, an old variant of 和) are no entries of
their own any more, which repeated their word in the simplified file,
but written forms of that word, finding it in text and searches.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
Chinese comes from CC-CEDICT (CC BY-SA 4.0), made for learners of
Chinese: `curate cedict` writes two dictionaries of its entries,
`cc-cedict-hans-en` (zh-Hans) and `cc-cedict-hant-en` (zh-Hant), so a
studyset finds its words as it writes them, each finding a word by
either script, by pinyin with or without tones and by its meanings.
Readings are pinyin with tone marks, a syllable to a character; measure
words are a remark beside the meanings.
The HSK levels are those of the 2025 examination syllabus, which HSK
3.0's examination follows since July 2026 (decided 2026-10-14: level
tags, credited), as hsk30 extracted them (MIT): `hsk:1` … `hsk:6`, and
`hsk:7` for 7 to 9, each given to the entry read as the syllabus reads
the word (的 de, not 的 dì; 书 shū, not the Book of Documents, Shū),
ranking the words, HSK 1 first.
`curate update --languages de,el,en,zh` takes over from `--wiktionary`:
Chinese from MDBG's file of the day as often as EDRDG's files, the rest
from Wiktionary's dump.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
The languages besides Japanese come from the English Wiktionary as
Wiktextract reads it (decided 2026-10-14: German, Greek and English
first, then Chinese, then Korean): kaikki.org's raw dump, every language
in one file, as its per-language files are being withdrawn.
`curate wiktionary --languages de,el,en` reads it once, streamed, and
writes German and Greek with Wiktionary's English meanings and English
with what its translation tables give in German, the app's other
language (`wiktionary-de-en`, `-el-en`, `-en-de`; sources
`wiktionary-<language>`, as the server holds one source per meaning
language). Inflected forms, and Wiktionary's entries for forms alone
("Häusern: dative plural of Haus"), find their word instead of being
entries; names and affixes are left out, as are articles that tables
give cells of their own, abbreviations and rare spellings, which would
match common words. Entries keep their part of speech, genders, IPA,
principal forms and examples keyed by language as JMdict's.
wordfreq's small lists (CC BY-SA 4.0, crediting SUBTLEX) in data/ rank
the words by how often they are used and label the 15,000 most used
`common`. Typing without accents or umlauts finds a word (`strasse`,
`haeuser`, `spiti`).
The format gains `textKeys`: what finds an entry in running text, its
own forms, never its meanings, which would match words of the meaning
language in a subtitle. `curate update --wiktionary de,el,en` keeps the
languages current: it asks kaikki.org when the dump last changed and
converts it again only when it is newer, or the languages or the
conversion changed. The whole dump converts in about ten minutes in
under 300 MB.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
The test font draws every letter as a full square, so at 1,200 wide the
four tabs left the flashcards' filter folded into the menu.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
Since paths moved out of the Study plan, every empty section the test
looks at is a tab of Material, so the check for one without a tab is
always true, which the analyser refuses.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
With four tabs, Paths 5, Flashcards 12, Notes 0 and Files 0 don't fit a
phone, and Files scrolls out of sight. An empty tab says so itself once
opened, so its count goes (decided 2026-10-14, after the trial's
screenshots); the four tabs then fit as long as a few are empty.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
A learning path is material too, as its flashcards, notes and files are,
and the study plan only refers to it (decided 2026-10-13), as it will to
an exam's date. Material's tabs are now Paths, Flashcards, Notes and
Files, the paths first as the way through the rest, with Reorder beside
them; opened here for the first time, Material shows the first tab with
something in it. Study plan keeps how many paths are finished and Next
up, which names the next step of each path under way ("Step 3 of 6: が"),
or offers to start the first path not finished when none is, and lists
how far every path has come, its steps done in a bar, each opening the
path. The path list's state now knows each path's steps done and next
step.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
With Jellyfin's default of three failed tries, anyone could switch the
demo off with three wrong passwords; an account without a password has
nothing for the lockout to guard. The runbook also says how people sign
in to it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
Jellyfin v10.11 and later no longer say which accounts have a password
(HasPassword is always true, jellyfin/jellyfin#14950), so the demo's
account was listed as "With a password" and only named in the form.
Every listed account is now named in the form with the password field
focused, which says to leave it empty for an account without one, as
Jellyfin's own apps do; the line under each account goes.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
rsync has to be installed on both ends, and learn.halkyon.eu's server
has none; tar through SSH needs nothing the server lacks.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
The sidecar's steps made Jellyfin's user with its own group, to which
useradd gave a system group ID (987 on learn.halkyon.eu's server), while
compose.jellyfin.yaml runs it as 61096:61096. The group is now made
first with the same ID, and the steps say why useradd's warning about an
ID above the system range is meant.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
A failed deploy put the checkout back at the deployed commit but left
the containers as the new one had made them: when Caddy couldn't start
on the new network (2026-10-14), learn.halkyon.eu stayed down until they
were brought back by hand. Failing after the switch, the deploy now also
starts the containers as the deployed commit has them, on the new
images, which fit the database the migrations have changed. Before the
switch, the running containers are never touched, as before.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
The deploy that brought the edge network failed and left learn.halkyon.eu
down: the server, started before Caddy, got the first free address on
the network, 172.30.80.2, which is Caddy's fixed one, so Caddy couldn't
start ("Address already in use"). Containers without an address of their
own now get one from 172.30.80.128/25, below which the fixed ones (Caddy
.2, sidecars from .10) are never taken.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
The servers' names are no secret, so the settings that carry them stay
as they were, and nothing on a server or a laptop needs changing: CI
runs on `sahaquiel` again, the release uploads to `ramiel-fdroid`, and
deploys by hand go through `sahaquiel-deploy`. Prose still names a
server by the domain it serves, which says what a reader needs.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
The cut takes the longest word and knows no grammar, so 今日は reads as
こんにちは where it is 今日 and は. Any stretch of a line's characters can
now be chosen by hand: dragged across with a mouse, Shift+clicked from
the word or stretch chosen, or on a touch screen held on a character and
dragged on. It is looked up as one word, as it stands or as a form of
one, in one small request (the instance has the line's text already);
while it is chosen the line reads it as that word (きょう over 今日), and
what the dictionary lacks as one word is offered as a search on Jisho.
The line's words move into line_words.dart, out of the Watch screen.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
After v0.13.0's deploy every request to learn.halkyon.eu took a minute or
two and syncs failed, until the server was restarted by hand. The
migration changed a synced table, so the new server, as it first used
the database, found the sync engine's registry of synced tables changed,
and then rebuilt every space's projections in every session until it
stopped (serverpod_offline_sync issue #150). The maintenance run that
applies the migrations doesn't spare it, as AGENTS.md wrongly said.
redeploy.sh now restarts the server once it answers whenever the deploy
brought a migration, and waits for it to answer again: started a second
time, it finds the registry as the first start left it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
An instance may now run services beside itself, each a compose file of
its own switched on in deploy/.env (COMPOSE_FILE=compose.yaml:compose.
jellyfin.yaml), so the deploy runs them with the instance. The first is
Jellyfin, which people watch and listen to learn from: the instance then
suggests it (JELLYFIN_URL from JELLYFIN_DOMAIN), and Caddy serves its
site, refusing every change to a user from outside, so a passwordless
demo account stays everyone's; its admin signs in through an SSH tunnel.
The instance's compose gets a second network, `edge`, for Caddy (at a
fixed address, which sidecars trust as their proxy) and the server;
Postgres stays on the internal one. Caddy loses its needless way to
Postgres, and a sidecar never gains one. A sidecar publishes no port,
as the CI runner may reach 127.0.0.1, and runs as a user ID of its own.
Caddy imports the sidecars' sites from sidecars/, mounted by their
compose files (none without), and redeploy.sh recreates it when one
changes. sidecars/README.md has the steps.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
An instance now names a Jellyfin server for its people to learn from
(instance.info().jellyfinUrl): its operator's JELLYFIN_URL (`none`: none),
else https://jellyfin. on the domain registered for its public host, as
learn.halkyon.eu suggests jellyfin.halkyon.eu, once that answers as a
Jellyfin server. The registered domain comes from Mozilla's Public Suffix
List, kept in the server's data folder, so an instance at example.co.uk
never names someone else's jellyfin.co.uk. The instance looks as it
starts and again hourly when asked; the app never reaches for a guessed
address.
In the app, Add material offers From Jellyfin as soon as the instance
suggests a server, before anyone signed in to one. The sign-in fills the
server in and finds it by itself, and lists the people the server shows
on its login screen: one without a password, as a demo's, signs in with
a tap; one with a password is named in the form, which asks for it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
Files called the servers by their own host names, which are the
operator's and say nothing a reader needs. They now say which domain a
server serves: learn.halkyon.eu's server (the instance and its CI
runner), the halkyon.app server (Forgejo and the F-Droid repository).
Two names are settings rather than words. CI's runner label becomes
`instance` (`runs-on: instance`, `labels: ["instance:host"]`): the
runner reads its installed copy of deploy/runner/config.yml, so jobs
wait in the queue until that copy is updated and the runner restarted.
The SSH aliases the scripts and docs use become `learn-deploy` and
`fdroid-upload`. AGENTS.md says to keep it so.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
Add material's entry is now From Jellyfin (Aus Jellyfin), with a play
icon rather than a TV, as it plays music too, and shows only once the
device is signed in to a server in Settings, as the assistant shows only
once switched on; Settings says where it then is. The screen it opens is
called Jellyfin, and its search finds music too.
The words' panel takes the glow's colour as the scene changes: each copy
the glow takes (every 3 s) gives its average, no lighter than 35 % so the
text stays readable, as strong as the glow and fading into the next as
it does. In the light theme, which has no glow, the panel is tinted from
it while the pointer is over it. What lights up in the panel takes a
scheme made from the same colour; the episode's thumbnail is no longer
used.
While the studyset holds none of the video's words, the study button
adds the words listed (Add 11 words, as the filter shows them), tagged
as a word added alone is; afterwards it studies them, and its menu adds
the new ones listed.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
Every tag the app makes for where words were met on Jellyfin (the show
and the episode, the artist and the song) now says so (`origin` in tag
schema 2, written only for such a tag), in place of a tag hidden one by
one. Manage tags, where there are such tags, has one switch, Hide tags
from Jellyfin while studying (`hideJellyfinTags`, settings schema 9,
following the person): with it on, cards being studied leave them out of
their row at once, and Tags and Manage tags list them faint. They still
filter and narrow a session. The per-tag eye button is gone.
A word added while watching no longer takes the line it was met in as
its first hint: its hints are the dictionary's examples alone. Tatoeba's
sentences are checked, a subtitle line may be wrong or mistyped, a word
met again in a better line wouldn't get it, and every episode's line
would pile up on a common word.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
A session begun anew (turned to the other side, its settings changed, or
tried again) kept the cards it had crammed before, so a cram of a few
cards turned round said at once there was nothing to study. It now
crams every card again.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
A music library's artists and albums open as shows and seasons do, and
can be tied to a studyset. A song plays as sound alone (`/Audio/{id}/
stream`, as stored) with its lyrics as Jellyfin keeps them (`/Audio/{id}/
Lyrics`: an LRC file beside it, or its tags), cut into the dictionary's
words as a video's subtitles are, with no translation. Timed lyrics
follow the song: the line sung large and bright among the others faded,
scrolled to as it comes, a line tapped played from its start; untimed
ones show whole. A song without lyrics shows greyed and doesn't open
(`HasLyrics`), as there is nothing in it to read. Its cover stands where
the picture would, glowing as a video does, and its words are tagged
with the artist, shown, and "artist · title", hidden.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
A word added from an episode gets the show's tag and the episode's own ("あかね噺 S1 E1"), hidden on cards. The words panel's Study button studies the studyset's cards of the video's words, on their schedules or crammed (each once): it pauses the video and first gives the episode's tag to the cards of its words the studyset held before, then opens the session on that tag. A study focus can now ask for cramming.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
A tag's eye in Manage tags hides it on cards being studied, or shows it again: for the many tags naming where a word was met (an episode, a song), which would crowd a common word's card. A hidden tag still filters, narrows a study session and shows where tags are edited. Tag schema 2 adds 'hidden', written only for a hidden tag, so older apps go on reading every other.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>