This commit is contained in:
Kelorastus
2026-09-23 16:05:19 +02:00
parent 4a92cc0b63
commit 4d620fc5ad
27 changed files with 3571 additions and 104 deletions
+51 -3
View File
@@ -35,6 +35,8 @@ interface Status {
phase: Phase;
error: string | null; // set with phase "error"; cleared by the next command or `refresh`
install_dir: string; // e.g. "D:\\ODIAN"
portable: boolean; // this launcher ships on an ODIAN drive: it runs that drive's
// install (any letter), the folder is fixed, no self-update
sync: SyncStatus | null; // counts from the last sync this session; null before one
live: boolean | null; // while ODIAN runs: true = its data comes from the DHP,
// false = from this PC. null when it is not running, or
@@ -84,6 +86,25 @@ interface StackEvent {
}
interface LogEvent { level: "info" | "warn" | "error"; message: string; at: number }
interface MarketItem {
key: string; // stable across versions: "tiles:<style>/<dataset>" | "src:<path>"
kind: "tiles" | "src";
title: string;
description: string;
bytes: number; // what installing it downloads, in full
required: boolean; // part of every install; never offered for removal
presence: "installed" | "partial" | "absent"; // partial: a download that stopped
selected: boolean; // chosen: installed with the version, kept by updates
}
interface Market {
version: string; // the installed version, else the DHP's current one
installed: boolean; // false: a choice waits for Install
core_bytes: number; // the app and database: what every install downloads besides the items
items: MarketItem[]; // required first
free_bytes: number | null; // free space where ODIAN is (or will be) installed
}
```
## Commands (`invoke`)
@@ -98,7 +119,7 @@ interface LogEvent { level: "info" | "warn" | "error"; message: string; at: numb
| `get_notes` | `{ version?: string }` | `string` | Markdown. From the site when online; the cached copy of the installed version offline. **Untrusted: the UI must sanitize.** |
| `open_external` | `{ url: string }` | `void` | opens an `http`/`https` link in the system browser (links in patch notes); anything else is refused |
| `start_install` | — | `void` | installs or updates to `available`; needs `signed_in` (the site checks the token); progress arrives as events |
| `start_install_from_drive` | `{ path: string }` | `void` | same, reading an ODIAN drive (SSD) instead of the site; no sign-in needed |
| `start_install_from_drive` | `{ path: string }` | `void` | same, copying from an ODIAN drive (SSD) instead of the site; no sign-in needed. `path` is the drive's root (a pack store) or a folder holding an install, and every file copied is checked against the manifest. A tile set the PC already records as complete for the same index (`.blocks-done.json`) is left as it stands instead of being re-read: that record is written last, so a copy stopped halfway still reads as partial and resumes. The copy lands in the install folder of Settings, so a drive can install onto the PC it is plugged into; an install folder that is the drive's own ODIAN (or inside it) is refused, since there is nothing to copy, and so is a drive carrying an older ODIAN than the one installed there |
| `cancel_install` | — | `void` | stops at the next file boundary; everything already verified is kept |
| `launch` | — | `void` | starts the local stack; `stack` events follow |
| `stop` | — | `void` | stops Odian.exe's stack cleanly |
@@ -106,14 +127,41 @@ interface LogEvent { level: "info" | "warn" | "error"; message: string; at: numb
| `get_settings` | — | `Settings` | |
| `set_settings` | `{ settings: Settings }` | `Status` | |
| `pick_folder` | — | `string \| null` | native folder picker |
| `get_market` | `{ drive?: string }` | `Market` | the marketplace: every tile set and source file of the installed version (read from disk), or of the DHP's current one before the first install. With `drive`, what that drive carries instead (what it cannot install is left out), which is how the UI asks before installing from a drive |
| `apply_market` | `{ selected: string[] }` | `void` | saves the choice of optional items (keys), then applies it to the installed version: unchosen items are removed from the PC, chosen missing ones download (phase `installing`, `install-progress` events). Refused while busy or ODIAN runs, and while an update is required; downloading needs the DHP. Before the first install it only saves the choice |
| `set_market` | `{ selected: string[], drive?: string }` | `void` | the choice of optional items (keys) for an install about to run; changes nothing installed. Without `drive` it is this PC's standing choice and is saved. With `drive` it filters that one copy and is NOT saved: leaving out a tile set the PC already holds must not read as dropping it, or the next `apply_market` would delete it |
| `sync_now` | — | `void` | pushes and pulls operator data now; only while ODIAN is stopped (phase `syncing` meanwhile) |
| `get_sync_conflicts` | — | `SyncConflict[]` | what waits for a decision, oldest first; starts the local Postgres just for the read |
| `resolve_sync_conflict` | `{ tbl, key, choice: "theirs" \| "mine" \| "value" \| "dismiss", value?: unknown }` | `SyncConflict[]` | records one decision and returns what is left. `theirs` takes the DHP's version; `mine` keeps this PC's (the next sync sends it); `value` keeps a field-by-field pick; `dismiss` keeps a refused edit on this PC only. Links and folder placements held with a record follow its decision |
Errors reject the promise with a string message.
Planned, not yet available: the size of `available` for the Install button, and
inspecting a drive (its version and size) before installing from it.
Planned, not yet available: the size of `available` for the Install button.
## Where a drive's launcher installs
A launcher that ships on an ODIAN drive (`site.json` with `"portable": true`) keeps its
settings beside the exe, so the site and the install folder travel with the drive. The
folder may be on the drive itself, and ODIAN then runs from the drive on any PC: it is
stored without its letter, because the next PC hands the drive a different one. It may
also be a folder on a disk of the PC in front of the operator, and the drive then installs
ODIAN onto that PC and leaves; that folder is stored whole. A drive's root and a network
share are refused: Postgres and the tiles need a real disk.
## Marketplace
The DHP decides what is optional: a manifest tile set or source file with
`"optional": true` (plus `title` and `description`, from `catalog.json` at assemble) is
left out of Install and listed by `get_market`. Everything else is part of every install.
A manifest from before the marketplace flags nothing, so the launcher applies the same rule
to it: the app, the database and the `dark` tile set come with every install and the rest is
offered, which is what makes copying an older version from a drive a choice rather than all
228 GB or nothing. A manifest that flags anything at all is the DHP's to decide and is taken
exactly as it stands. The choice is kept in the
install's state (`extras`) and survives updates: an update refreshes the chosen items and
nothing else. Installing from a drive asks the same way: the marketplace opens on what the
drive carries, `set_market` records the answer, and `start_install_from_drive` copies it. A PC installed before the marketplace counts what it has on disk as chosen,
so nothing is removed or downloaded again behind the operator's back.
## Live mode