Architecture

Design choices

Moku is a thin client over Tsunagu, which is most of why Tauri makes sense here: the app itself doesn’t need to embed heavy logic, so a small native shell over a webview beats bundling Electron.

Backend supervision

On desktop, src-tauri/src/backend.rs spawns Tsunagu as a subprocess, waits for a ready signal on stdout, then polls its healthcheck endpoint before reporting the backend ready to the frontend. If the process exits unexpectedly, a crash event carries its exit code and a rolling log tail back to the UI. On Unix, the child is set to die with its parent; on stop, it gets a graceful SIGTERM before a hard kill.

Talking to Tsunagu

There’s no GraphQL codegen. src/lib/graphql/client.ts is a small hand-written function that posts queries over fetch, attaches auth headers, and throws typed errors on failure. Each domain under server-adapters/tsunagu/ hand-writes its own query and mutation strings rather than generating them from a schema.

Platform abstraction

A single platformService interface is implemented once for Tauri and once for plain web, letting the same SvelteKit codebase build as a static site or as a desktop app depending on the build target.

State

All global state uses Svelte 5 runes rather than the classic store API, one module per domain (library, settings, downloads, trackers, and so on), imported directly where it’s needed rather than passed through a central reducer.

Reader dispatch

There’s exactly one reader route. It resolves the active title’s content type and mounts the matching viewer: novel, anime, or manga by default. The manga viewer further branches into single, double, or longstrip components depending on the page style setting; reader chrome (settings panel, progress bar, chapter navigation) is shared across all three content types.