docs(meditate): ADR 0038, D16, the operator's library, as the developer asked
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
This commit is contained in:
parent
811182b376
commit
c44cf55fd4
2 changed files with 104 additions and 7 deletions
|
|
@ -3,9 +3,10 @@
|
||||||
What holds for every app and package of this repository is in the [root AGENTS.md](../../AGENTS.md); it applies here in full, and Learn's patterns (`apps/learn/AGENTS.md`) wherever Meditate needs the same thing. What Meditate is and why: [ADR 0038](../../docs/adr/0038-halkyon-meditate.md) (proposed 3 Nov 2026, built by an agent without the developer's interjection; its decisions await their reading).
|
What holds for every app and package of this repository is in the [root AGENTS.md](../../AGENTS.md); it applies here in full, and Learn's patterns (`apps/learn/AGENTS.md`) wherever Meditate needs the same thing. What Meditate is and why: [ADR 0038](../../docs/adr/0038-halkyon-meditate.md) (proposed 3 Nov 2026, built by an agent without the developer's interjection; its decisions await their reading).
|
||||||
|
|
||||||
- **Packages**: `halkyon_meditate_server`, `halkyon_meditate_client` (generated, never edited), `halkyon_meditate_flutter`; app ID `app.halkyon.meditate` on every platform; no web build. Licences as Learn's (server SSPL, client and app AGPL; `LICENSE.md`). Copied from Todo's tree on 3 Nov 2026 and stripped to the shell, so its frame, keys and sealed rows are Todo's and Train's.
|
- **Packages**: `halkyon_meditate_server`, `halkyon_meditate_client` (generated, never edited), `halkyon_meditate_flutter`; app ID `app.halkyon.meditate` on every platform; no web build. Licences as Learn's (server SSPL, client and app AGPL; `LICENSE.md`). Copied from Todo's tree on 3 Nov 2026 and stripped to the shell, so its frame, keys and sealed rows are Todo's and Train's.
|
||||||
- **The server** includes `halkyon_base` (ADR 0014) and configures it in `server.dart` (`meditateBase()`, `lib/src/meditate_base.dart`): its basics say it serves `meditate` (never change it once apps use it), sign-in mails name Halkyon Meditate, the source is this repository, `minimumAppBuild` 1, a person's space deleted with them (`deletePersonData: [deleteMeditateData]`, `lib/src/meditate_data.dart`). **Accounts** (ADR 0016): Meditate holds its own register when it runs alone, and is a member of the instance's register where the operator sets `ACCOUNT_REGISTER_URL` (`AccountsMode.fromEnvironment()`) with the instance's shared `INSTANCE_SECRET`; `INSTANCE_MEMBER_MEDITATE` names it to the register. `schedulePruning` (`lib/src/pruning.dart`) keeps the module's pruning scheduled once each. Synced tables (ADR 0038, D7; `database: sync`, every row sealed): `content_key`, `meditate_settings` (one row a person, under an ID keyed by the account), `sitting` and `dream` (D14), each indexing `spaceId`. Migrations: `initial` (Serverpod's, the auth modules', the module's and the sync engine's tables), `sittings` (the three tables) and `dreams` (`dream`). Tests: `test/integration/meditate_server_test.dart` (the module's endpoints through the server's own dispatch, `halkyon_base.<endpoint>`, and the sync engine's connector refusing calls without a session), `test/integration/sitting/person_deletion_test.dart`.
|
- **The server** includes `halkyon_base` (ADR 0014) and configures it in `server.dart` (`meditateBase()`, `lib/src/meditate_base.dart`): its basics say it serves `meditate` (never change it once apps use it), sign-in mails name Halkyon Meditate, the source is this repository, `minimumAppBuild` 1, a person's space deleted with them (`deletePersonData: [deleteMeditateData]`, `lib/src/meditate_data.dart`). **Accounts** (ADR 0016): Meditate holds its own register when it runs alone, and is a member of the instance's register where the operator sets `ACCOUNT_REGISTER_URL` (`AccountsMode.fromEnvironment()`) with the instance's shared `INSTANCE_SECRET`; `INSTANCE_MEMBER_MEDITATE` names it to the register. `schedulePruning` (`lib/src/pruning.dart`) keeps the module's pruning scheduled once each. Synced tables (ADR 0038, D7; `database: sync`, every row sealed): `content_key`, `meditate_settings` (one row a person, under an ID keyed by the account), `sitting`, `dream` (D14) and `lesson_done` (D16), each indexing `spaceId`. Migrations: `initial` (Serverpod's, the auth modules', the module's and the sync engine's tables), `sittings` (the three tables), `dreams` (`dream`) and `library` (`lesson_done`). Tests: `test/integration/meditate_server_test.dart` (the module's endpoints through the server's own dispatch, `halkyon_base.<endpoint>`, and the sync engine's connector refusing calls without a session), `test/integration/sitting/person_deletion_test.dart`, the library's in `test/unit/library/` (the manifest, the files over HTTP) and `test/integration/library/` (the endpoint).
|
||||||
- **The app** is halkyon_app's `InstanceGate` and `SessionGate` with Meditate's frame (`meditateFrame`) and `ServerpodInstanceChecker(app: 'meditate')`; `MeditateDependencies` per instance, `MeditatePerson` per person (their sync, `SettingsRepository`, `SittingRepository`, `DreamRepository`, and the device's `BellPlayer` and `ScreenAwake`); SimpleLogin's way back on `app.halkyon.meditate://` and the loopback port 43826. The person's app (`MeditatePersonApp`) washes in fern (`ScreenColor`, D3) and holds `PracticeCubit` (settings and sittings, read again on every change) and `JournalCubit` (the dreams) for every screen. go_router (`buildMeditateRouter`, `MeditatePaths`): halkyon_ui's `NavigationFrame` (`MeditateShell`) with Today, Explore, Journal and Profile (D2, D14; a bar below medium windows, a rail with the icon from medium on), a practice's page in Explore's branch (`PracticeScreen`), and over the tabs the sitting (`SittingScreen`, handed its `SittingPlan` as `extra`), a dream (`DreamScreen`, `/dream` new, `/dream/:id` kept) and Settings. **Today** (`TodayTab`, D9): greeting by the hour, from 4 to 12 o'clock a card to write down last night's dream while it has none (`dreamPromptDue`, D14), Sit now (5, 10, 20 and the timer's own length), the practice of the day (`practiceOfDay`), the week and the days in a row (`WeekCard`), favourites, recently sat. **Explore** (`ExploreTab`, D10): search folded without case or accents (`matchesSearch`), four goal tiles, kind chips, practice cards. **The Journal** (`JournalTab`, D14; `lib/src/feature/journal/`): dreams by month, the newest first (`dreamsByMonth`), searched over title, text and labels and filtered by marks (`findDreams`), *+* to write one; a dream's night is the date of the morning after it (`defaultNight`: today's date at any hour); its screen writes with the text first and focused, the rest folded, and reads a kept one with Edit and Delete; a microphone under the text dictates where the device recognises speech (D15: halkyon_app's `Dictation` on `MeditatePerson.speech`, `DeviceSpeechInput` preferring Android's on-device recogniser, listening again after each pause until stopped or three silent listens; none on Linux, `NoSpeechInput` in tests that don't say), and Today's card offers *Speak it* (`/dream?speak=1`). The platforms declare the microphone as Learn's do (Android's `RECORD_AUDIO` and recognition query, iOS's and macOS's usage texts, macOS's `audio-input`), and the privacy page's points are `meditate_base.dart`'s. **Profile** (D2, D8): halkyon_app's `HalkyonProfile` with a *Practice* tab (minutes, sittings, days, the week, the journal's dreams this month and lucid ones once it holds one, every sitting with Delete). **The practices** are `practices` in `lib/src/feature/practice/model/practice.dart` (IDs never change once released), their words in the ARBs by ID (`practiceTexts`), their cues spread evenly over the length (`Practice.cueAt`). **A sitting** (`SittingCubit`, D6): its time from `clock.now()`, the ticks only showing it; the bell at the start (where on), between every interval and at the end; the screen kept on while it runs (where on); kept whole at its end, and ended early kept from a minute on where the person says so. **The figures** (`practiceFigures`, D8) on the device: a sitting's day by the wall clock it was sat by (`Sitting.offsetMinutes`), the streak up to today or yesterday, the week from the language's first day. **Settings** (D11): Sync, the operator's card, *Sitting* (bell, starting bell, bells between, screen on, the timer's length), *Practice* (the streak shown), *Journal* (Today's morning card, `dreamPrompt`), the account, What's new, About, the version. Texts in British English (the template) and German (du), `en_US` and `en_GB` empty. Widget tests start the whole app with halkyon_app's fakes (`test/app/meditate_app_test.dart`), the bells and screen recorded (`test/support/recording_device.dart`); a sitting on screen ticks every second, so tests pump seconds (`sit`) rather than `pumpAndSettle`; a test needing the hour (Today's morning card) starts the app inside `withClock` (`startAt`).
|
- **The app** is halkyon_app's `InstanceGate` and `SessionGate` with Meditate's frame (`meditateFrame`) and `ServerpodInstanceChecker(app: 'meditate')`; `MeditateDependencies` per instance, `MeditatePerson` per person (their sync, `SettingsRepository`, `SittingRepository`, `DreamRepository`, `LibraryRepository`, `LessonRepository`, and the device's `BellPlayer`, `GuidePlayer` and `ScreenAwake`); SimpleLogin's way back on `app.halkyon.meditate://` and the loopback port 43826. The person's app (`MeditatePersonApp`) washes in fern (`ScreenColor`, D3) and holds `PracticeCubit` (settings and sittings, read again on every change), `JournalCubit` (the dreams) and `LibraryCubit` (the instance's library, lessons done, recordings kept) for every screen. go_router (`buildMeditateRouter`, `MeditatePaths`): halkyon_ui's `NavigationFrame` (`MeditateShell`) with Today, Explore, Journal and Profile (D2, D14; a bar below medium windows, a rail with the icon from medium on), a practice's page in Explore's branch (`PracticeScreen`) with a library item's and course's (`/explore/library/:id`, `?course=` for a lesson, `/explore/courses/:id`), and over the tabs the sitting (`SittingScreen`, handed its `SittingPlan` as `extra`), a recording playing (`ListeningScreen`, its `ListeningPlan` as `extra`), the library's credits (`/library-credits`), a dream (`DreamScreen`, `/dream` new, `/dream/:id` kept) and Settings. **Today** (`TodayTab`, D9): greeting by the hour, from 4 to 12 o'clock a card to write down last night's dream while it has none (`dreamPromptDue`, D14), Sit now (5, 10, 20 and the timer's own length), the practice of the day (`practiceOfDay`), the week and the days in a row (`WeekCard`), favourites, recently sat. **Explore** (`ExploreTab`, D10): search folded without case or accents (`matchesSearch`), four goal tiles, kind chips, practice cards. **The Journal** (`JournalTab`, D14; `lib/src/feature/journal/`): dreams by month, the newest first (`dreamsByMonth`), searched over title, text and labels and filtered by marks (`findDreams`), *+* to write one; a dream's night is the date of the morning after it (`defaultNight`: today's date at any hour); its screen writes with the text first and focused, the rest folded, and reads a kept one with Edit and Delete; a microphone under the text dictates where the device recognises speech (D15: halkyon_app's `Dictation` on `MeditatePerson.speech`, `DeviceSpeechInput` preferring Android's on-device recogniser, listening again after each pause until stopped or three silent listens; none on Linux, `NoSpeechInput` in tests that don't say), and Today's card offers *Speak it* (`/dream?speak=1`). The platforms declare the microphone as Learn's do (Android's `RECORD_AUDIO` and recognition query, iOS's and macOS's usage texts, macOS's `audio-input`), and the privacy page's points are `meditate_base.dart`'s. **Profile** (D2, D8): halkyon_app's `HalkyonProfile` with a *Practice* tab (minutes, sittings, days, the week, the journal's dreams this month and lucid ones once it holds one, every sitting with Delete). **The practices** are `practices` in `lib/src/feature/practice/model/practice.dart` (IDs never change once released), their words in the ARBs by ID (`practiceTexts`), their cues spread evenly over the length (`Practice.cueAt`). **A sitting** (`SittingCubit`, D6): its time from `clock.now()`, the ticks only showing it; the bell at the start (where on), between every interval and at the end; the screen kept on while it runs (where on); kept whole at its end, and ended early kept from a minute on where the person says so. **The figures** (`practiceFigures`, D8) on the device: a sitting's day by the wall clock it was sat by (`Sitting.offsetMinutes`), the streak up to today or yesterday, the week from the language's first day. **Settings** (D11): Sync, the operator's card, *Sitting* (bell, starting bell, bells between, screen on, the timer's length), *Practice* (the streak shown), *Journal* (Today's morning card, `dreamPrompt`), the account, What's new, About, the version. Texts in British English (the template) and German (du), `en_US` and `en_GB` empty. Widget tests start the whole app with halkyon_app's fakes (`test/app/meditate_app_test.dart`), the bells and screen recorded (`test/support/recording_device.dart`); a sitting on screen ticks every second, so tests pump seconds (`sit`) rather than `pumpAndSettle`; a test needing the hour (Today's morning card) starts the app inside `withClock` (`startAt`).
|
||||||
|
- **The operator's library** (D16; `lib/src/library/` on the server, `lib/src/feature/library/` in the app): a folder named by `MEDITATE_LIBRARY_DIR` with recordings, pictures and `library.yaml` (commented sample: the server's `library.example.yaml`), read by `readLibrary` (every check and refusal, with the reason), held in memory by `MeditateLibrary` and reread every ten minutes (`LibraryReloadFutureCall`, `startLibrary`); checked before deploying with `dart run bin/check_library.dart <folder>` in the server package. `LibraryEndpoint.catalogue` for signed-in people alone, with an access (`LibraryFileAccess`: the hour begun plus 12 hours, signed with a key made at start); the web server's `/library/<served name>?access=…` serves the files by ranges (`serveLibraryFile`), never by a path. In the app: Explore's section (none without a library), the item's page (recordings, Play, favourite as `library/<id>` in the settings, keep on the device, a sound under a silent sitting, its credit), the course's (lessons ticked), the player (a finished item is a sitting `library/<id>` and its lesson done), Settings' *Library credits*; the last catalogue in the person's folder (`library.json`), recordings kept in `library/` beside it. Tests: `test/feature/library/library_test.dart` and the app test's group, on `test/support/fake_library.dart` (made-up entries only: nothing of 7Mind's, whose structure alone D16 follows).
|
||||||
- **The bells** (D5) are `assets/bells/*.wav`, synthesised by `apps/meditate/tool/bells.py` (Python's standard library, run from `apps/meditate`; the same bytes every run) and played with `audioplayers` (`AudioplayersBellPlayer`). Only while the app runs: bells with the screen off are for later.
|
- **The bells** (D5) are `assets/bells/*.wav`, synthesised by `apps/meditate/tool/bells.py` (Python's standard library, run from `apps/meditate`; the same bytes every run) and played with `audioplayers` (`AudioplayersBellPlayer`). Only while the app runs: bells with the screen off are for later.
|
||||||
- **The icon** is Lucide's `sprout` from cobalt through pacific into fern (ADR 0031, D5, D7), drawn by the repository's `tool/app_icons.py` (`python3 -I tool/app_icons.py meditate` from the root): `assets/icon/halkyon_meditate_icon.png`, Lucide's licence beside it, every platform's launcher icon.
|
- **The icon** is Lucide's `sprout` from cobalt through pacific into fern (ADR 0031, D5, D7), drawn by the repository's `tool/app_icons.py` (`python3 -I tool/app_icons.py meditate` from the root): `assets/icon/halkyon_meditate_icon.png`, Lucide's licence beside it, every platform's launcher icon.
|
||||||
- **`serverpod start`** runs in `apps/meditate/halkyon_meditate_server` on ports of its own (8280 to 8282, PostgreSQL 8290), so it can run beside Learn's (8080…) and Train's (8180…); its tests need throwaway passwords in its git-ignored `config/passwords.yaml` (`database`, `serviceSecret`, `serverSideSessionKeyHashPepper`, `emailSignInPepper`) or from the environment, as CI gives them.
|
- **`serverpod start`** runs in `apps/meditate/halkyon_meditate_server` on ports of its own (8280 to 8282, PostgreSQL 8290), so it can run beside Learn's (8080…) and Train's (8180…); its tests need throwaway passwords in its git-ignored `config/passwords.yaml` (`database`, `serviceSecret`, `serverSideSessionKeyHashPepper`, `emailSignInPepper`) or from the environment, as CI gives them.
|
||||||
- **Not yet**: releases (`apps/meditate/release.dart`, its key, F-Droid listing, snap), the deploy beside learn.halkyon.eu (`compose.meditate.yaml`, `config/production.yaml`, the `Dockerfile`), and what ADR 0038 lists under *Later*.
|
- **Not yet**: releases (`apps/meditate/release.dart`, its key, F-Droid listing, snap), the deploy beside learn.halkyon.eu (`compose.meditate.yaml` with the library's folder mounted read-only, Caddy forwarding `/library/*`, `config/production.yaml`, the `Dockerfile`), and what ADR 0038 lists under *Later*.
|
||||||
|
|
|
||||||
|
|
@ -204,6 +204,89 @@ names and pictures are its own). Everything below is Halkyon's own.
|
||||||
query on Android, the microphone's and speech recognition's usage texts on iOS and
|
query on Android, the microphone's and speech recognition's usage texts on iOS and
|
||||||
macOS, and macOS's `audio-input` entitlement.
|
macOS, and macOS's `audio-input` entitlement.
|
||||||
|
|
||||||
|
## The operator's library (3 Nov 2026, the developer's request)
|
||||||
|
|
||||||
|
- **D16 Guided content the operator provides**, asked for by the developer on 3 Nov
|
||||||
|
2026, structured as 7Mind's catalogue is (items with a title, a category, a
|
||||||
|
description, a speaker and recordings; categories; courses of ordered lessons). Only
|
||||||
|
the structure is taken: nothing of 7Mind's (texts, recordings, names, ids) is in the
|
||||||
|
repository, its tests or its fixtures, whose entries are made up. The agent's
|
||||||
|
recommendations, to be confirmed:
|
||||||
|
- **A folder and a manifest.** The operator mounts a folder into Meditate's server and
|
||||||
|
names it in `MEDITATE_LIBRARY_DIR`: recordings and pictures, and `library.yaml`, YAML
|
||||||
|
rather than JSON because operators write it by hand and want comments (a commented,
|
||||||
|
made-up example: `halkyon_meditate_server/library.example.yaml`). An item: `id` (the
|
||||||
|
operator's slug, a-z, 0-9 and `-`, which sittings, favourites and lessons done refer
|
||||||
|
to, so never changed), `title`, `category`, `description`, `speaker`, `language`
|
||||||
|
(BCP 47), `image`, one or more recordings under `audio` (each a `file`, a `label`
|
||||||
|
telling them apart by length or voice, and a `duration`), and **`licence` and
|
||||||
|
`credit`, required**, with an optional `licence-url`. A course: `id`, `title`,
|
||||||
|
`description`, `category`, `language`, `image` and `lessons` in order, each an
|
||||||
|
`item` with an optional `title` of its own. The duration is the manifest's, in
|
||||||
|
seconds or `m:ss`, not read from the file: reading MP3's, AAC's and Opus's lengths
|
||||||
|
without a decoder is unreliable, and a wrong length would count wrong sittings; a
|
||||||
|
check that reads it can come with the deploy's image.
|
||||||
|
- **Categories**: Meditate's eleven keys (`meditation`, `sleep-story`, `sound`,
|
||||||
|
`movement`, `autogenic-training`, `progressive-muscle-relaxation`, `routine`,
|
||||||
|
`dream-journey`, `knowledge`, `morning-story`, `other`), named by the app in English
|
||||||
|
and German; and **the operator's own** under `categories:` with a key and an English
|
||||||
|
and a German name, never one of Meditate's keys: an instance's library is its own,
|
||||||
|
and "other" alone would hide what it offers.
|
||||||
|
- **Checked as read, refusing what fails, with the reason**: an item without a licence
|
||||||
|
or credit, a recording or picture outside the folder (a `..`, an absolute path or a
|
||||||
|
link leading out, each resolved), missing, or of another type (recordings: MP3, M4A,
|
||||||
|
AAC, Ogg, Opus, FLAC; pictures: JPEG, PNG, WebP), a wrong id, category or language,
|
||||||
|
no duration, an id taken twice, a course pointing at an item not served; the rest is
|
||||||
|
served. A manifest that doesn't read at all leaves the library as it was served (an
|
||||||
|
edit half done), one removed ends it. `bin/check_library.dart <folder>` reads a folder
|
||||||
|
as the server would, before it is deployed, and exits 1 on any problem.
|
||||||
|
- **In memory, not in the database**: the manifest is the library's whole truth and
|
||||||
|
small, read as the server starts and again every ten minutes by a future call
|
||||||
|
(`LibraryReloadFutureCall`, as Learn's dictionaries are reread), an unchanged folder
|
||||||
|
costing a hash of the manifest and a stat of each file. No table, no migration, and
|
||||||
|
nothing to keep in step.
|
||||||
|
- **For signed-in people alone** (`LibraryEndpoint.catalogue`, `requireLogin`): the
|
||||||
|
operator's licences may not allow offering it to the public.
|
||||||
|
- **The files on Serverpod's web server**, `/library/<served name>`, by ranges (relic's
|
||||||
|
`StaticHandler`: `Range`, `If-Range`, `ETag`), as the platforms' players fetch a
|
||||||
|
recording. A file is asked for by a served name the reading gave it (a hash of its
|
||||||
|
place, size and time with its extension); no request's path reaches the file system.
|
||||||
|
Since players can't all send the session's header, the catalogue brings an
|
||||||
|
**access**: an hour's start plus 12 hours, signed with a key the server makes as it
|
||||||
|
starts (a restart ends every access; the app asks again), in the URL's query.
|
||||||
|
Without one: 401; one not signed here or ended: 403; any other name: 404. Starting
|
||||||
|
from the hour, the access is the same for everyone given it within that hour, so a
|
||||||
|
fetch names no account (the privacy page says so). Rejected: an endpoint streaming
|
||||||
|
the bytes (Serverpod's endpoints answer whole, no ranges), Caddy serving the folder
|
||||||
|
(it can't check a session or an access without a plugin).
|
||||||
|
- **In the app**: Explore shows the library below Meditate's six practices, with chips
|
||||||
|
of the categories it uses and the same search, its courses (lessons done counted)
|
||||||
|
then its items; an item's page has its recordings, Play, the heart, keeping it on the
|
||||||
|
device, for a sound a silent sitting with it looped (the timer's length), and its
|
||||||
|
credit and licence; a course's page lists its lessons, ticked once done. The player
|
||||||
|
(`GuidePlayer` on audioplayers, beside the bells') plays, pauses and seeks; played to
|
||||||
|
its end, an item is kept as a sitting of its whole length under `library/<id>`, so
|
||||||
|
the figures count it, and as its course's lesson done; stopped before, nothing.
|
||||||
|
**Playing on with the screen off** (Android's foreground service and media
|
||||||
|
notification, iOS's `audio` background mode, as `audio_service` does) is for later:
|
||||||
|
not cheap to do well.
|
||||||
|
- **The person's, sealed and synced**: favourites of the library in the settings' list
|
||||||
|
beside the practices', as `library/<id>`; lessons done in a new table, `lesson_done`,
|
||||||
|
a row each under an ID keyed by the account, the course and the item, so two devices
|
||||||
|
doing it make one row; both name the operator's ids, so a lesson whose course went
|
||||||
|
counts again should it return.
|
||||||
|
- **Offline**: the last catalogue is kept in the person's folder (`library.json`), and a
|
||||||
|
recording is kept on the device only on the person's asking, removed again on it (and
|
||||||
|
with their folder); a kept one plays from there.
|
||||||
|
- **No library, nothing**: an instance without one shows no section in Explore and no
|
||||||
|
credits in Settings.
|
||||||
|
- **Credits**: Settings lists every item's credit and licence beside About (*Library
|
||||||
|
credits*), and each item's page shows its own.
|
||||||
|
- **The deploy, later** (step 6): the folder mounted read-only into Meditate's
|
||||||
|
container (`/srv/meditate-library:/library:ro`, `MEDITATE_LIBRARY_DIR=/library`),
|
||||||
|
Caddy forwarding `/library/*` to the web server with the rest, `check_library`
|
||||||
|
compiled into the image so an operator checks a folder on the host.
|
||||||
|
|
||||||
## Steps
|
## Steps
|
||||||
|
|
||||||
1. **The shell** (3 Nov): Todo's copied and renamed, its features left out, the three tabs
|
1. **The shell** (3 Nov): Todo's copied and renamed, its features left out, the three tabs
|
||||||
|
|
@ -216,7 +299,10 @@ names and pictures are its own). Everything below is Halkyon's own.
|
||||||
5. **The dream journal** (3 Nov): D14, the `dream` table and its migration, the Journal
|
5. **The dream journal** (3 Nov): D14, the `dream` table and its migration, the Journal
|
||||||
tab, a dream's screen, Today's card, Settings' *Journal*, Profile's figures; and
|
tab, a dream's screen, Today's card, Settings' *Journal*, Profile's figures; and
|
||||||
dreams spoken (D15).
|
dreams spoken (D15).
|
||||||
6. **Released and deployed** (later, as Todo's step 5): `apps/meditate/release.dart`, a key
|
6. **The operator's library** (3 Nov): D16, the server's reading, endpoint, files and
|
||||||
|
check, the `lesson_done` table and its migration, Explore's section, an item's and a
|
||||||
|
course's pages, the player, keeping on the device, the credits.
|
||||||
|
7. **Released and deployed** (later, as Todo's step 5): `apps/meditate/release.dart`, a key
|
||||||
of its own, the F-Droid listing, a snap, `deploy/compose.meditate.yaml` at
|
of its own, the F-Droid listing, a snap, `deploy/compose.meditate.yaml` at
|
||||||
meditate.halkyon.eu (its `.env` values the developer has set already, 2 Nov), its
|
meditate.halkyon.eu (its `.env` values the developer has set already, 2 Nov), its
|
||||||
database and role, `redeploy.sh` and `backup.sh` taking it in.
|
database and role, `redeploy.sh` and `backup.sh` taking it in.
|
||||||
|
|
@ -242,13 +328,20 @@ names and pictures are its own). Everything below is Halkyon's own.
|
||||||
`dream_space_idx` on `spaceId`, its foreign key to `offline_sync_spaces("id")` with
|
`dream_space_idx` on `spaceId`, its foreign key to `offline_sync_spaces("id")` with
|
||||||
`ON DELETE CASCADE`, so a person deleted takes their dreams with their space; then the
|
`ON DELETE CASCADE`, so a person deleted takes their dreams with their space; then the
|
||||||
version rows. A new, empty table: nothing existing changes.
|
version rows. A new, empty table: nothing existing changes.
|
||||||
|
- `library` (D16): the table `lesson_done` as `dream` is (`CREATE TABLE "lesson_done"`
|
||||||
|
with the same columns, `id` a UUID v7 by default, the function again `CREATE OR
|
||||||
|
REPLACE`), the index `lesson_done_space_idx` on `spaceId` (btree), its foreign key to
|
||||||
|
`offline_sync_spaces("id")` `ON DELETE CASCADE`, so a person deleted takes their
|
||||||
|
lessons done with their space; then the version rows. The library itself has no
|
||||||
|
table. A new, empty table: nothing existing changes.
|
||||||
|
|
||||||
## Later
|
## Later
|
||||||
|
|
||||||
- Recorded guidance (D4), the cues read out by text-to-speech, bells in the background
|
- Recorded guidance (D4), the cues read out by text-to-speech, bells in the background
|
||||||
(D6), a daily reminder (D12), sittings written to Health Connect as mindfulness
|
(D6), a daily reminder (D12), sittings written to Health Connect as mindfulness
|
||||||
sessions (Android 14's `MindfulnessSessionRecord`), courses of several practices in
|
sessions (Android 14's `MindfulnessSessionRecord`), courses of Meditate's own
|
||||||
order (7Mind's courses), ambient sounds.
|
practices in order; for the library (D16), playing on with the screen off, durations
|
||||||
|
read from the files, the pictures shown.
|
||||||
|
|
||||||
## Consequences
|
## Consequences
|
||||||
|
|
||||||
|
|
@ -256,4 +349,7 @@ names and pictures are its own). Everything below is Halkyon's own.
|
||||||
`practice.dart` and its words in the two ARBs; a practice once used keeps its ID, as
|
`practice.dart` and its words in the two ARBs; a practice once used keeps its ID, as
|
||||||
sittings name it.
|
sittings name it.
|
||||||
- Without recorded voices, Meditate is a timer with guidance more than a library;
|
- Without recorded voices, Meditate is a timer with guidance more than a library;
|
||||||
recorded guidance (D4) is what would make it more.
|
recorded guidance (D4) is what would make it more, and an instance's library (D16)
|
||||||
|
brings it where its operator offers one.
|
||||||
|
- An operator's library is theirs to license: Meditate refuses an item without a licence
|
||||||
|
and credit, but can't check that they hold.
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue