diff --git a/.agents/skills/build-cadmus-native/SKILL.md b/.agents/skills/build-cadmus-native/SKILL.md index 58a1e477..eb4bf864 100644 --- a/.agents/skills/build-cadmus-native/SKILL.md +++ b/.agents/skills/build-cadmus-native/SKILL.md @@ -29,16 +29,7 @@ it when documentation sources change. ## One-time setup -### 1. Native dependencies (MuPDF + C wrapper) - -```bash -cargo xtask setup-native -``` - -This downloads MuPDF sources, applies Cadmus-specific patches, and compiles the -C wrapper library. It is **required** before any compilation or test run. - -### 2. (Optional) Download runtime assets +### Download runtime assets (optional) ```bash cargo xtask download-assets @@ -47,6 +38,10 @@ cargo xtask download-assets Pulls static assets (fonts, icons, etc.) from the latest GitHub release. Not strictly required for compilation, but the emulator and some tests expect them. +Native dependencies (MuPDF, libwebp, and the C wrapper) are now built +automatically by `build.rs` when you run any Cargo command that compiles +`cadmus-core`. + ## Daily workflow commands | Goal | Command | @@ -84,21 +79,20 @@ need to verify a specific one. - **`fmt`** — runs `cargo fmt --check` (or `--apply` in CI) across the workspace - **`clippy`** — iterates the full feature matrix; use `--features` to narrow it -- **`test`** — iterates the test feature matrix; native deps are assumed built -- **`run-emulator`** — ensures `setup-native` has run, then `cargo run -p emulator` -- **`install-importer`** — ensures `setup-native` has run, then `cargo install --path crates/importer` +- **`test`** — iterates the test feature matrix +- **`run-emulator`** — runs `cargo run -p emulator` +- **`install-importer`** — runs `cargo install --path crates/importer` ## Common mistakes | Mistake | Result | Fix | | ------------------------------------------------------------- | ------------------------------------------------------------- | -------------------------------------------------------- | | Running `cargo check` before `cargo xtask docs --mdbook-only` | `RustEmbed` folder-not-found error | Generate the EPUB first | -| Running `cargo test` before `cargo xtask setup-native` | Linker errors for missing MuPDF / wrapper | Run `setup-native` first | | Running bare `cargo clippy` / `cargo test` directly | May miss feature-gated code or use wrong feature combinations | Prefer `cargo xtask clippy` and `cargo xtask test` | | Running `cargo xtask test` without `--features` | Runs the full (slow) CI matrix locally | Pass `--features default` or the specific combo you need | ## Platform notes -- **Linux**: `setup-native` builds MuPDF from source; ensure `gcc`, `make`, `cmake`, and standard build tools are installed. -- **macOS**: `setup-native` builds MuPDF from source; Xcode Command Line Tools must be installed. -- The `build-kobo` command is **Linux-only** (cross-compiles for ARM); it is not covered here because it runs inside a containerised CI action. +- **Linux**: Native dependencies are built from source automatically via `build.rs`; ensure `gcc`, `make`, `cmake`, and standard build tools are installed. +- **macOS**: Native dependencies are built from source automatically via `build.rs`; Xcode Command Line Tools must be installed. Full support including Kobo cross-compilation. +- The `build-kobo` command cross-compiles for ARM and is available on both Linux and macOS; it runs inside a containerised CI action in the main workflow. diff --git a/.agents/skills/build-kobo/SKILL.md b/.agents/skills/build-kobo/SKILL.md index 83d69808..7941326e 100644 --- a/.agents/skills/build-kobo/SKILL.md +++ b/.agents/skills/build-kobo/SKILL.md @@ -4,43 +4,24 @@ description: Cross-compile Cadmus for Kobo e-reader devices (ARM Linux). Use thi --- Use `cargo xtask build-kobo` to cross-compile Cadmus for Kobo devices. -This task is **Linux only** — the Linaro ARM toolchain consists of x86_64 Linux -ELF binaries that cannot run on macOS. Use Docker or a Linux VM on macOS. +The Linaro ARM toolchain is supported on both Linux and macOS hosts. ## Basic usage ```sh -# Fast mode — downloads pre-built .so files (default) +# Cross-compile for Kobo cargo xtask build-kobo -# Slow mode — builds all thirdparty libraries from source (required for CI) -cargo xtask build-kobo --slow - -# Skip library download entirely (when libs/ already exists) -cargo xtask build-kobo --skip - -# Download thirdparty sources only, without building -cargo xtask build-kobo --download-only - # Build with specific Cargo feature flags cargo xtask build-kobo --features test ``` -## Build modes - -| Mode | Flag | Description | -| -------------------- | ------------------------ | ------------------------------------------------------- | -| Fast (default) | _(none)_ | Downloads pre-built `.so` files + MuPDF sources | -| Slow | `--slow` | Builds all thirdparty libraries from source | -| Slow + download only | `--slow --download-only` | Downloads all thirdparty sources without building | -| Skip | `--skip` | Assumes `libs/` already exists; skips download entirely | - ## What it does 1. Verifies the Linaro ARM toolchain is available on `PATH` -2. Downloads or builds thirdparty `.so` libraries into `libs/` -3. Builds the `mupdf_wrapper` C library for the ARM target -4. Runs `cargo build --release --target arm-unknown-linux-gnueabihf -p cadmus` +2. Runs `cargo build --release --target arm-unknown-linux-gnueabihf -p cadmus` + +Thirdparty dependencies (zlib, bzip2, libpng, libjpeg, openjpeg, jbig2dec, libwebp, freetype2, harfbuzz, gumbo, djvulibre, mupdf) are tracked as git submodules and built automatically by the `build-deps` crate's build.rs when needed. On a warm cache, no submodule initialisation or C/C++ compilation happens. ## Output @@ -49,7 +30,6 @@ The compiled binary is written to: ## Prerequisites -- **Linux only** — exits with a clear error on macOS +- **Linux or macOS** — cross-compilation is supported on both platforms - Linaro ARM toolchain on `PATH`: `arm-linux-gnueabihf-gcc`, `arm-linux-gnueabihf-ar` - (provided by the devenv shell on Linux) -- Run `cargo xtask setup-native` first if MuPDF sources are not yet present + (provided by the devenv shell) diff --git a/build-scripts/mupdf/README.md b/build-scripts/mupdf/README.md index c31d9df8..cd49d135 100644 --- a/build-scripts/mupdf/README.md +++ b/build-scripts/mupdf/README.md @@ -1,8 +1,8 @@ # MuPDF Patches for Cadmus This directory contains the upstream MuPDF source tree (currently 1.27.0) plus -Cadmus-specific patches. The patches are applied by `cargo xtask setup-native` -and by the Kobo build scripts. +Cadmus-specific patches. The patches are applied automatically by the +`build-deps` crate's `build.rs` during compilation. ## Patch overview diff --git a/devenv.nix b/devenv.nix index e898de22..41636ffa 100644 --- a/devenv.nix +++ b/devenv.nix @@ -676,14 +676,9 @@ in ]; }; - # Build mupdf and wrapper for native development - "deps:native" = { - exec = "cargo xtask setup-native"; - }; - # Build for Kobo with cross-compilation "build:kobo" = { - exec = "cargo xtask build-kobo --slow"; + exec = "cargo xtask build-kobo"; after = [ "docs:build" ]; }; @@ -704,14 +699,9 @@ in zola serve --base-url http://localhost ''; - # Build mupdf for native development - cadmus-setup-native.exec = '' - cargo xtask setup-native - ''; - # Build for Kobo device cadmus-build-kobo.exec = '' - cargo xtask build-kobo --slow + cargo xtask build-kobo cargo xtask dist ''; @@ -766,7 +756,6 @@ in echo "Cadmus development environment" echo "" echo "Available commands:" - echo " cadmus-setup-native - Build mupdf for native development (run once)" echo " cadmus-docs-build - Build complete documentation portal" echo " cadmus-docs-serve - Serve documentation locally (http://localhost:1111)" echo " cargo test - Run tests (after setup)" @@ -783,7 +772,6 @@ in echo " cadmus-clippy - Lint lines changed vs master (reviewdog)" echo " cargo xtask clippy - Lint across feature matrix" echo " cargo xtask test - Test across feature matrix" - echo " cargo xtask setup-native - Build MuPDF for native development" echo " cargo xtask docs - Build documentation portal" echo " cargo xtask dist - Assemble Kobo distribution" echo " cargo xtask bundle - Package KoboRoot.tgz" diff --git a/docs/po/messages.pot b/docs/po/messages.pot index 1f94cea9..04805af7 100644 --- a/docs/po/messages.pot +++ b/docs/po/messages.pot @@ -2,7 +2,7 @@ msgid "" msgstr "" "Project-Id-Version: Cadmus Documentation\n" -"POT-Creation-Date: 2026-06-05T16:12:12+02:00\n" +"POT-Creation-Date: 2026-06-07T18:53:20Z\n" "PO-Revision-Date: \n" "Last-Translator: \n" "Language-Team: \n" @@ -75,7 +75,7 @@ msgid "Importing" msgstr "" #: src/SUMMARY.md:19 src/installation/ota.md:86 src/troubleshooting/index.md:1 -#: src/contributing/devenv-setup.md:225 +#: src/contributing/devenv-setup.md:221 msgid "Troubleshooting" msgstr "" @@ -95,7 +95,7 @@ msgstr "" msgid "Event System" msgstr "" -#: src/SUMMARY.md:28 src/settings/index.md:279 +#: src/SUMMARY.md:28 src/settings/index.md:283 #: src/contributing/telemetry/index.md:1 msgid "Telemetry" msgstr "" @@ -639,8 +639,8 @@ msgstr "" msgid "Source" msgstr "" -#: src/installation/ota.md:34 src/contributing/devenv-setup.md:49 -#: src/contributing/devenv-setup.md:76 +#: src/installation/ota.md:34 src/contributing/devenv-setup.md:47 +#: src/contributing/devenv-setup.md:73 msgid "Description" msgstr "" @@ -1178,9 +1178,9 @@ msgstr "" #: src/settings/index.md:153 src/settings/index.md:159 #: src/settings/index.md:167 src/settings/index.md:188 #: src/settings/index.md:201 src/settings/index.md:213 -#: src/settings/index.md:221 src/settings/index.md:234 -#: src/settings/index.md:254 src/settings/index.md:303 -#: src/settings/index.md:314 +#: src/settings/index.md:221 src/settings/index.md:238 +#: src/settings/index.md:258 src/settings/index.md:307 +#: src/settings/index.md:318 msgid "✏️" msgstr "" @@ -1488,22 +1488,35 @@ msgid "" msgstr "" #: src/settings/index.md:232 -msgid "`import.sync-metadata`" +msgid "" +"Import scanning happens automatically on startup using incremental file " +"checking — files are only re-scanned if their modification time or size has " +"changed since the last import." +msgstr "" + +#: src/settings/index.md:234 +msgid "" +"To trigger a full re-scan of all files regardless of cached values, use the " +"**Force Full Import** action button in the Import settings category." msgstr "" #: src/settings/index.md:236 +msgid "`import.sync-metadata`" +msgstr "" + +#: src/settings/index.md:240 msgid "Re-extract metadata (title, author, etc.) whenever a document changes." msgstr "" -#: src/settings/index.md:243 +#: src/settings/index.md:247 msgid "`import.metadata-kinds`" msgstr "" -#: src/settings/index.md:245 +#: src/settings/index.md:249 msgid "File extensions of documents whose metadata is extracted during import." msgstr "" -#: src/settings/index.md:247 +#: src/settings/index.md:251 msgid "" "```toml\n" "[import]\n" @@ -1511,15 +1524,15 @@ msgid "" "```" msgstr "" -#: src/settings/index.md:252 +#: src/settings/index.md:256 msgid "`import.allowed-kinds`" msgstr "" -#: src/settings/index.md:256 +#: src/settings/index.md:260 msgid "File extensions of documents considered during the import process." msgstr "" -#: src/settings/index.md:258 +#: src/settings/index.md:262 msgid "" "```toml\n" "[import]\n" @@ -1528,15 +1541,15 @@ msgid "" "```" msgstr "" -#: src/settings/index.md:263 +#: src/settings/index.md:267 msgid "OTA" msgstr "" -#: src/settings/index.md:265 +#: src/settings/index.md:269 msgid "The OTA feature downloads builds from GitHub." msgstr "" -#: src/settings/index.md:267 +#: src/settings/index.md:271 msgid "" "Authentication for main branch and PR builds uses **GitHub device auth " "flow**. When you select a build that requires authentication, Cadmus will " @@ -1545,39 +1558,39 @@ msgid "" "once you authorize." msgstr "" -#: src/settings/index.md:273 +#: src/settings/index.md:277 msgid "" "The token is saved to disk after the first authorization so you will not be " "prompted again on subsequent downloads." msgstr "" -#: src/settings/index.md:276 +#: src/settings/index.md:280 msgid "" "For step-by-step instructions with screenshots, see the [OTA " "updates](../installation/ota.md) guide." msgstr "" -#: src/settings/index.md:281 +#: src/settings/index.md:285 msgid "" "Cadmus writes JSON logs to disk. When the build enables the `tracing` " "feature, it can also export logs to an OpenTelemetry endpoint." msgstr "" -#: src/settings/index.md:284 +#: src/settings/index.md:288 msgid "These settings are available in the **Settings → Telemetry** menu." msgstr "" -#: src/settings/index.md:286 +#: src/settings/index.md:290 msgid "" "\\[!IMPORTANT\\] Changes to these settings only take effect after restarting " "Cadmus. The application initializes telemetry on startup." msgstr "" -#: src/settings/index.md:290 +#: src/settings/index.md:294 msgid "`logging`" msgstr "" -#: src/settings/index.md:292 +#: src/settings/index.md:296 msgid "" "```toml\n" "[logging]\n" @@ -1589,29 +1602,29 @@ msgid "" "```" msgstr "" -#: src/settings/index.md:301 src/contributing/telemetry/logging.md:50 +#: src/settings/index.md:305 src/contributing/telemetry/logging.md:50 msgid "`logging.enabled`" msgstr "" -#: src/settings/index.md:305 +#: src/settings/index.md:309 msgid "Enable or disable structured JSON logging." msgstr "" -#: src/settings/index.md:312 src/contributing/telemetry/logging.md:51 +#: src/settings/index.md:316 src/contributing/telemetry/logging.md:51 msgid "`logging.level`" msgstr "" -#: src/settings/index.md:316 +#: src/settings/index.md:320 msgid "Minimum log level to record." msgstr "" -#: src/settings/index.md:318 +#: src/settings/index.md:322 msgid "" "Possible values: `\"trace\"`, `\"debug\"`, `\"info\"`, `\"warn\"`, " "`\"error\"`." msgstr "" -#: src/settings/index.md:320 +#: src/settings/index.md:324 msgid "" "```toml\n" "[logging]\n" @@ -1619,37 +1632,37 @@ msgid "" "```" msgstr "" -#: src/settings/index.md:325 src/contributing/telemetry/logging.md:52 +#: src/settings/index.md:329 src/contributing/telemetry/logging.md:52 msgid "`logging.max-files`" msgstr "" -#: src/settings/index.md:327 +#: src/settings/index.md:331 msgid "" "Number of log files to keep. Only the most recent N files are kept — older " "ones are deleted automatically when Cadmus starts." msgstr "" -#: src/settings/index.md:330 src/settings/index.md:403 +#: src/settings/index.md:334 src/settings/index.md:407 msgid "Default: `3`" msgstr "" -#: src/settings/index.md:331 +#: src/settings/index.md:335 msgid "Set to `0` to keep all log files." msgstr "" -#: src/settings/index.md:338 src/contributing/telemetry/logging.md:54 +#: src/settings/index.md:342 src/contributing/telemetry/logging.md:54 msgid "`logging.otlp-endpoint`" msgstr "" -#: src/settings/index.md:340 +#: src/settings/index.md:344 msgid "✏️ (only when the `tracing` feature is enabled)" msgstr "" -#: src/settings/index.md:342 +#: src/settings/index.md:346 msgid "Optional OTLP endpoint for exporting logs to an OpenTelemetry collector." msgstr "" -#: src/settings/index.md:344 +#: src/settings/index.md:348 msgid "" "```toml\n" "[logging]\n" @@ -1657,31 +1670,31 @@ msgid "" "```" msgstr "" -#: src/settings/index.md:349 src/settings/index.md:366 +#: src/settings/index.md:353 src/settings/index.md:370 msgid "Environment override:" msgstr "" -#: src/settings/index.md:351 +#: src/settings/index.md:355 msgid "" "`OTEL_EXPORTER_OTLP_ENDPOINT` takes precedence over `logging.otlp-endpoint`." msgstr "" -#: src/settings/index.md:353 src/contributing/telemetry/profiling.md:32 +#: src/settings/index.md:357 src/contributing/telemetry/profiling.md:32 msgid "`logging.pyroscope-endpoint`" msgstr "" -#: src/settings/index.md:355 +#: src/settings/index.md:359 msgid "✏️ (only when the `profiling` feature is enabled)" msgstr "" -#: src/settings/index.md:357 +#: src/settings/index.md:361 msgid "" "Optional Pyroscope server URL for continuous profiling. When set, Cadmus " "starts both a heap profiling agent (via jemalloc) and a CPU profiling agent " "(via pprof) that push profiles to this endpoint." msgstr "" -#: src/settings/index.md:361 +#: src/settings/index.md:365 msgid "" "```toml\n" "[logging]\n" @@ -1689,58 +1702,58 @@ msgid "" "```" msgstr "" -#: src/settings/index.md:368 +#: src/settings/index.md:372 msgid "" "`PYROSCOPE_SERVER_URL` takes precedence over `logging.pyroscope-endpoint`." msgstr "" -#: src/settings/index.md:370 +#: src/settings/index.md:374 msgid "`logging.enable-kern-log`" msgstr "" -#: src/settings/index.md:372 src/settings/index.md:384 +#: src/settings/index.md:376 src/settings/index.md:388 msgid "🧪 📱 ✏️" msgstr "" -#: src/settings/index.md:374 +#: src/settings/index.md:378 msgid "" "Captures kernel logs via `logread -F` and forwards them to structured " "logging with the target `cadmus_core::logging:kern`." msgstr "" -#: src/settings/index.md:382 +#: src/settings/index.md:386 msgid "`logging.enable-dbus-log`" msgstr "" -#: src/settings/index.md:386 +#: src/settings/index.md:390 msgid "" "Captures D-Bus signals via the built-in zbus-based DbusMonitorTask and " "forwards them to structured logging." msgstr "" -#: src/settings/index.md:394 +#: src/settings/index.md:398 msgid "Settings Retention" msgstr "" -#: src/settings/index.md:396 +#: src/settings/index.md:400 msgid "" "Cadmus stores each version's settings in a separate file in the `Settings/` " "directory (for example, `Settings-v1.2.3.toml`). This ensures backward and " "forward compatibility when you upgrade." msgstr "" -#: src/settings/index.md:399 +#: src/settings/index.md:403 msgid "`settings-retention`" msgstr "" -#: src/settings/index.md:401 +#: src/settings/index.md:405 msgid "" "Number of recent version settings files to keep. Only the most recent N " "version files are kept. When a new version is saved, older versions beyond " "this limit are deleted automatically." msgstr "" -#: src/settings/index.md:404 +#: src/settings/index.md:408 msgid "Set to `0` to keep all version files" msgstr "" @@ -3410,26 +3423,55 @@ msgid "" msgstr "" #: src/library/importing.md:6 -msgid "Automatic import" +msgid "Automatic import on startup" msgstr "" #: src/library/importing.md:8 msgid "" -"Cadmus imports any new books it finds automatically. Copy files to your " -"device, and they'll appear in your library after the next import pass." +"Cadmus automatically imports books every time it starts. The import is " +"**incremental**: files whose modification time and file size haven't changed " +"since the last import are skipped, avoiding unnecessary re-fingerprinting. " +"Only new or modified files are processed, significantly improving startup " +"performance for large libraries." +msgstr "" + +#: src/library/importing.md:14 +msgid "" +"Copy files to your device, restart the app, and they'll appear in your " +"library right away." +msgstr "" + +#: src/library/importing.md:17 +msgid "Force full import" +msgstr "" + +#: src/library/importing.md:19 +msgid "" +"If you suspect the import cache is stale or corrupted, you can force a full " +"re-import:" msgstr "" -#: src/library/importing.md:11 -msgid "Manual import" +#: src/library/importing.md:22 +msgid "Open **Settings**." msgstr "" -#: src/library/importing.md:13 -msgid "You can trigger an import at any time from the settings editor." +#: src/library/importing.md:23 +msgid "Navigate to the **Import** section." msgstr "" -#: src/library/importing.md:15 +#: src/library/importing.md:24 +msgid "Tap **Force Full Import**." +msgstr "" + +#: src/library/importing.md:25 +msgid "Confirm when prompted." +msgstr "" + +#: src/library/importing.md:27 msgid "" -"Cadmus will scan your library folder and add any books it hasn't seen before." +"This bypasses the incremental import cache and re-fingerprints all files in " +"your library directories. Be aware that this can take time and drain the " +"battery for large libraries, so keep your device plugged in while it runs." msgstr "" #: src/troubleshooting/index.md:5 @@ -3619,14 +3661,10 @@ msgid "Clone the repository and enter the devenv shell:" msgstr "" #: src/contributing/devenv-setup.md:21 -msgid "Run the one-time setup to build native dependencies:" -msgstr "" - -#: src/contributing/devenv-setup.md:27 msgid "Download the packaged runtime assets used by Kobo builds:" msgstr "" -#: src/contributing/devenv-setup.md:33 +#: src/contributing/devenv-setup.md:27 msgid "" "\\[!NOTE\\] `cadmus-core` generates some compile-time metadata from the " "bundled asset directories. For Kobo builds, make sure `bin/`, `resources/`, " @@ -3634,537 +3672,528 @@ msgid "" "the generated asset list is complete." msgstr "" -#: src/contributing/devenv-setup.md:39 +#: src/contributing/devenv-setup.md:33 +msgid "" +"Thirdparty C/C++ dependencies (MuPDF, libwebp, zlib, etc.) are tracked as " +"git submodules and built automatically by `build.rs` when you run `cargo " +"build` or `cargo xtask run-emulator`. No separate setup step is required." +msgstr "" + +#: src/contributing/devenv-setup.md:37 msgid "Run the emulator:" msgstr "" -#: src/contributing/devenv-setup.md:45 +#: src/contributing/devenv-setup.md:43 msgid "Available Commands" msgstr "" -#: src/contributing/devenv-setup.md:47 +#: src/contributing/devenv-setup.md:45 msgid "Once inside the devenv shell, these commands are available:" msgstr "" -#: src/contributing/devenv-setup.md:49 +#: src/contributing/devenv-setup.md:47 msgid "Command" msgstr "" -#: src/contributing/devenv-setup.md:51 -msgid "`cargo xtask setup-native`" -msgstr "" - -#: src/contributing/devenv-setup.md:51 -msgid "Build MuPDF for native development (run once)" -msgstr "" - -#: src/contributing/devenv-setup.md:52 +#: src/contributing/devenv-setup.md:49 msgid "`cargo xtask download-assets`" msgstr "" -#: src/contributing/devenv-setup.md:52 +#: src/contributing/devenv-setup.md:49 msgid "Download packaged Plato runtime assets" msgstr "" -#: src/contributing/devenv-setup.md:53 +#: src/contributing/devenv-setup.md:50 msgid "`cargo xtask test`" msgstr "" -#: src/contributing/devenv-setup.md:53 +#: src/contributing/devenv-setup.md:50 msgid "Run the test suite across the feature matrix" msgstr "" -#: src/contributing/devenv-setup.md:54 +#: src/contributing/devenv-setup.md:51 msgid "`cargo xtask run-emulator`" msgstr "" -#: src/contributing/devenv-setup.md:54 +#: src/contributing/devenv-setup.md:51 msgid "Run the emulator" msgstr "" -#: src/contributing/devenv-setup.md:55 +#: src/contributing/devenv-setup.md:52 msgid "`cargo xtask build-kobo`" msgstr "" -#: src/contributing/devenv-setup.md:55 +#: src/contributing/devenv-setup.md:52 msgid "Cross-compile for Kobo device" msgstr "" -#: src/contributing/devenv-setup.md:56 +#: src/contributing/devenv-setup.md:53 msgid "`cargo xtask dist`" msgstr "" -#: src/contributing/devenv-setup.md:56 +#: src/contributing/devenv-setup.md:53 msgid "Assemble the Kobo distribution directory" msgstr "" -#: src/contributing/devenv-setup.md:57 +#: src/contributing/devenv-setup.md:54 msgid "`cargo xtask bundle`" msgstr "" -#: src/contributing/devenv-setup.md:57 +#: src/contributing/devenv-setup.md:54 msgid "Package KoboRoot.tgz for installation" msgstr "" -#: src/contributing/devenv-setup.md:58 +#: src/contributing/devenv-setup.md:55 msgid "`cadmus-dev-otel`" msgstr "" -#: src/contributing/devenv-setup.md:58 +#: src/contributing/devenv-setup.md:55 msgid "Run emulator with tracing and profiling enabled" msgstr "" -#: src/contributing/devenv-setup.md:59 +#: src/contributing/devenv-setup.md:56 msgid "`devenv up`" msgstr "" -#: src/contributing/devenv-setup.md:59 +#: src/contributing/devenv-setup.md:56 msgid "Start observability stack (Grafana, Tempo, Loki)" msgstr "" -#: src/contributing/devenv-setup.md:60 +#: src/contributing/devenv-setup.md:57 msgid "`cargo xtask docs`" msgstr "" -#: src/contributing/devenv-setup.md:60 +#: src/contributing/devenv-setup.md:57 msgid "Build documentation portal (mdBook + Cargo docs)" msgstr "" -#: src/contributing/devenv-setup.md:61 +#: src/contributing/devenv-setup.md:58 msgid "`cadmus-docs-serve`" msgstr "" -#: src/contributing/devenv-setup.md:61 +#: src/contributing/devenv-setup.md:58 msgid "Serve documentation portal locally on port 1111" msgstr "" -#: src/contributing/devenv-setup.md:62 +#: src/contributing/devenv-setup.md:59 msgid "`cadmus-translate`" msgstr "" -#: src/contributing/devenv-setup.md:62 +#: src/contributing/devenv-setup.md:59 msgid "Generate the docs translation template (.pot)" msgstr "" -#: src/contributing/devenv-setup.md:64 +#: src/contributing/devenv-setup.md:61 msgid "" "Run `cargo xtask --help` to see all available subcommands, or `cargo xtask " " --help` for options on a specific command." msgstr "" -#: src/contributing/devenv-setup.md:67 +#: src/contributing/devenv-setup.md:64 msgid "" "Or have a look at the rustdocs for `xtask` here." msgstr "" -#: src/contributing/devenv-setup.md:69 +#: src/contributing/devenv-setup.md:66 msgid "Tasks" msgstr "" -#: src/contributing/devenv-setup.md:71 +#: src/contributing/devenv-setup.md:68 msgid "" "The devenv environment uses [tasks](https://devenv.sh/tasks/) to manage " "build dependencies. Tasks are defined in `devenv.nix` and can be run with " "`devenv tasks run `." msgstr "" -#: src/contributing/devenv-setup.md:74 +#: src/contributing/devenv-setup.md:71 msgid "Available Tasks" msgstr "" -#: src/contributing/devenv-setup.md:76 +#: src/contributing/devenv-setup.md:73 msgid "Task" msgstr "" -#: src/contributing/devenv-setup.md:76 +#: src/contributing/devenv-setup.md:73 msgid "Dependencies" msgstr "" -#: src/contributing/devenv-setup.md:78 src/contributing/devenv-setup.md:80 +#: src/contributing/devenv-setup.md:75 src/contributing/devenv-setup.md:76 msgid "`docs:build`" msgstr "" -#: src/contributing/devenv-setup.md:78 +#: src/contributing/devenv-setup.md:75 msgid "Build documentation EPUB (only rebuilds if files changed)" msgstr "" -#: src/contributing/devenv-setup.md:78 src/contributing/devenv-setup.md:79 +#: src/contributing/devenv-setup.md:75 msgid "None" msgstr "" -#: src/contributing/devenv-setup.md:79 -msgid "`deps:native`" -msgstr "" - -#: src/contributing/devenv-setup.md:79 -msgid "Build MuPDF and wrapper for native development" -msgstr "" - -#: src/contributing/devenv-setup.md:80 +#: src/contributing/devenv-setup.md:76 msgid "`build:kobo`" msgstr "" -#: src/contributing/devenv-setup.md:80 +#: src/contributing/devenv-setup.md:76 msgid "Build for Kobo device" msgstr "" -#: src/contributing/devenv-setup.md:82 +#: src/contributing/devenv-setup.md:78 msgid "All tasks delegate to `cargo xtask` under the hood." msgstr "" -#: src/contributing/devenv-setup.md:84 +#: src/contributing/devenv-setup.md:80 msgid "How Tasks Work" msgstr "" -#: src/contributing/devenv-setup.md:86 +#: src/contributing/devenv-setup.md:82 msgid "" "Tasks with dependencies automatically run their dependencies first. For " "example:" msgstr "" -#: src/contributing/devenv-setup.md:89 +#: src/contributing/devenv-setup.md:85 msgid "# This will first run docs:build (if needed), then build for Kobo\n" msgstr "" -#: src/contributing/devenv-setup.md:93 +#: src/contributing/devenv-setup.md:89 msgid "" "The `docs:build` task uses `execIfModified` to only rebuild when " "documentation files have actually changed." msgstr "" -#: src/contributing/devenv-setup.md:95 +#: src/contributing/devenv-setup.md:91 msgid "Kobo Build Notes" msgstr "" -#: src/contributing/devenv-setup.md:97 +#: src/contributing/devenv-setup.md:93 msgid "`cargo xtask download-assets` must run before `cargo xtask build-kobo`." msgstr "" -#: src/contributing/devenv-setup.md:98 +#: src/contributing/devenv-setup.md:94 msgid "" "OTA updates delete Cadmus-owned bundled files before reboot, then Kobo " "extracts the new `KoboRoot.tgz` over the install directory." msgstr "" -#: src/contributing/devenv-setup.md:100 +#: src/contributing/devenv-setup.md:96 msgid "" "User files outside the generated Cadmus-owned asset list must be preserved." msgstr "" -#: src/contributing/devenv-setup.md:102 +#: src/contributing/devenv-setup.md:98 msgid "Documentation Portal" msgstr "" -#: src/contributing/devenv-setup.md:104 +#: src/contributing/devenv-setup.md:100 msgid "" "Cadmus provides a unified documentation portal that combines user guides, " "API reference, and contribution guides in one place." msgstr "" -#: src/contributing/devenv-setup.md:107 +#: src/contributing/devenv-setup.md:103 msgid "Building and Serving Locally" msgstr "" -#: src/contributing/devenv-setup.md:109 +#: src/contributing/devenv-setup.md:105 msgid "To build the documentation portal:" msgstr "" -#: src/contributing/devenv-setup.md:115 +#: src/contributing/devenv-setup.md:111 msgid "This runs the full build pipeline:" msgstr "" -#: src/contributing/devenv-setup.md:117 +#: src/contributing/devenv-setup.md:113 msgid "Builds the mdBook user guide (`docs/book/html/`)" msgstr "" -#: src/contributing/devenv-setup.md:118 +#: src/contributing/devenv-setup.md:114 msgid "Generates Rust API documentation (`target/doc/`)" msgstr "" -#: src/contributing/devenv-setup.md:119 +#: src/contributing/devenv-setup.md:115 msgid "Builds the Zola landing page and integrates all documentation" msgstr "" -#: src/contributing/devenv-setup.md:121 +#: src/contributing/devenv-setup.md:117 msgid "To serve the portal locally with live reload:" msgstr "" -#: src/contributing/devenv-setup.md:127 +#: src/contributing/devenv-setup.md:123 msgid "" "The portal will be available at with automatic " "rebuilds when you change documentation files or Rust code." msgstr "" -#: src/contributing/devenv-setup.md:130 +#: src/contributing/devenv-setup.md:126 msgid "Documentation Structure" msgstr "" -#: src/contributing/devenv-setup.md:132 +#: src/contributing/devenv-setup.md:128 msgid "The portal provides three integrated sections:" msgstr "" -#: src/contributing/devenv-setup.md:134 +#: src/contributing/devenv-setup.md:130 msgid "**Landing Page** (`/`) - Overview and feature highlights" msgstr "" -#: src/contributing/devenv-setup.md:135 +#: src/contributing/devenv-setup.md:131 msgid "**User Guide** (`/guide/`) - User-facing documentation from mdBook" msgstr "" -#: src/contributing/devenv-setup.md:136 +#: src/contributing/devenv-setup.md:132 msgid "**API Reference** (`/api/`) - Auto-generated Rust API documentation" msgstr "" -#: src/contributing/devenv-setup.md:138 +#: src/contributing/devenv-setup.md:134 msgid "" "All three sections are deployed as a single artifact to GitHub Pages at " "." msgstr "" -#: src/contributing/devenv-setup.md:141 +#: src/contributing/devenv-setup.md:137 msgid "Continuous Integration" msgstr "" -#: src/contributing/devenv-setup.md:143 +#: src/contributing/devenv-setup.md:139 msgid "" "Documentation is automatically built and validated on every pull request and " "deployed on push to `main` or `master`. The CI pipeline checks:" msgstr "" -#: src/contributing/devenv-setup.md:146 +#: src/contributing/devenv-setup.md:142 msgid "mdBook documentation compiles" msgstr "" -#: src/contributing/devenv-setup.md:147 +#: src/contributing/devenv-setup.md:143 msgid "Rust code documentation is valid" msgstr "" -#: src/contributing/devenv-setup.md:148 +#: src/contributing/devenv-setup.md:144 msgid "Zola landing page builds successfully" msgstr "" -#: src/contributing/devenv-setup.md:150 +#: src/contributing/devenv-setup.md:146 msgid "Running Tests" msgstr "" -#: src/contributing/devenv-setup.md:152 +#: src/contributing/devenv-setup.md:148 msgid "" "Tests require the `TEST_ROOT_DIR` environment variable to be set. The " "easiest way to run the full test matrix is:" msgstr "" -#: src/contributing/devenv-setup.md:159 +#: src/contributing/devenv-setup.md:155 msgid "" "This sets `TEST_ROOT_DIR` automatically and runs tests across all feature " "combinations. To run a single feature combination:" msgstr "" -#: src/contributing/devenv-setup.md:163 +#: src/contributing/devenv-setup.md:159 msgid "\"emulator + test\"" msgstr "" -#: src/contributing/devenv-setup.md:166 +#: src/contributing/devenv-setup.md:162 msgid "Or to run tests manually without xtask:" msgstr "" -#: src/contributing/devenv-setup.md:169 src/contributing/devenv-setup.md:237 +#: src/contributing/devenv-setup.md:165 src/contributing/devenv-setup.md:233 msgid "$(pwd)" msgstr "" -#: src/contributing/devenv-setup.md:172 +#: src/contributing/devenv-setup.md:168 msgid "" "`TEST_ROOT_DIR` is automatically configured in CI but must be set manually " "when running `cargo test` directly." msgstr "" -#: src/contributing/devenv-setup.md:175 +#: src/contributing/devenv-setup.md:171 msgid "Platform Support" msgstr "" -#: src/contributing/devenv-setup.md:177 +#: src/contributing/devenv-setup.md:173 msgid "Linux (Full Support)" msgstr "" -#: src/contributing/devenv-setup.md:179 +#: src/contributing/devenv-setup.md:175 msgid "Linux provides full development capabilities including:" msgstr "" -#: src/contributing/devenv-setup.md:181 src/contributing/devenv-setup.md:191 +#: src/contributing/devenv-setup.md:177 src/contributing/devenv-setup.md:187 msgid "Native development (emulator, tests)" msgstr "" -#: src/contributing/devenv-setup.md:182 src/contributing/devenv-setup.md:192 +#: src/contributing/devenv-setup.md:178 src/contributing/devenv-setup.md:188 msgid "Cross-compilation for Kobo devices using the Linaro ARM toolchain" msgstr "" -#: src/contributing/devenv-setup.md:183 src/contributing/devenv-setup.md:193 +#: src/contributing/devenv-setup.md:179 src/contributing/devenv-setup.md:189 msgid "Git hooks (actionlint, shellcheck, shfmt, markdownlint, prettier)" msgstr "" -#: src/contributing/devenv-setup.md:185 +#: src/contributing/devenv-setup.md:181 msgid "" "The Linaro toolchain is automatically added to `PATH` and provides " "`arm-linux-gnueabihf-*` commands." msgstr "" -#: src/contributing/devenv-setup.md:187 +#: src/contributing/devenv-setup.md:183 msgid "macOS (Full Support)" msgstr "" -#: src/contributing/devenv-setup.md:189 +#: src/contributing/devenv-setup.md:185 msgid "macOS supports full development capabilities including:" msgstr "" -#: src/contributing/devenv-setup.md:195 +#: src/contributing/devenv-setup.md:191 msgid "macOS-Specific Notes" msgstr "" -#: src/contributing/devenv-setup.md:197 +#: src/contributing/devenv-setup.md:193 msgid "" -"**MuPDF build**: On macOS, the native setup script manually gathers " +"**MuPDF build**: On macOS, the native build script manually gathers " "pkg-config CFLAGS for system libraries because MuPDF's build system doesn't " "properly detect them on Darwin." msgstr "" -#: src/contributing/devenv-setup.md:200 +#: src/contributing/devenv-setup.md:196 msgid "Observability Stack" msgstr "" -#: src/contributing/devenv-setup.md:202 +#: src/contributing/devenv-setup.md:198 msgid "The devenv includes a full observability stack for development:" msgstr "" -#: src/contributing/devenv-setup.md:205 +#: src/contributing/devenv-setup.md:201 msgid "# Start all services\n" msgstr "" -#: src/contributing/devenv-setup.md:207 +#: src/contributing/devenv-setup.md:203 msgid "# In another terminal, run the instrumented emulator\n" msgstr "" -#: src/contributing/devenv-setup.md:212 +#: src/contributing/devenv-setup.md:208 msgid "Services available after `devenv up`:" msgstr "" -#: src/contributing/devenv-setup.md:214 +#: src/contributing/devenv-setup.md:210 msgid "Service" msgstr "" -#: src/contributing/devenv-setup.md:214 +#: src/contributing/devenv-setup.md:210 msgid "URL" msgstr "" -#: src/contributing/devenv-setup.md:214 -#: src/contributing/library-database.md:148 +#: src/contributing/devenv-setup.md:210 +#: src/contributing/library-database.md:159 msgid "Purpose" msgstr "" -#: src/contributing/devenv-setup.md:216 +#: src/contributing/devenv-setup.md:212 msgid "Grafana" msgstr "" -#: src/contributing/devenv-setup.md:216 +#: src/contributing/devenv-setup.md:212 msgid "" msgstr "" -#: src/contributing/devenv-setup.md:216 +#: src/contributing/devenv-setup.md:212 msgid "Dashboards and exploration" msgstr "" -#: src/contributing/devenv-setup.md:217 +#: src/contributing/devenv-setup.md:213 msgid "Tempo" msgstr "" -#: src/contributing/devenv-setup.md:217 +#: src/contributing/devenv-setup.md:213 msgid "" msgstr "" -#: src/contributing/devenv-setup.md:217 +#: src/contributing/devenv-setup.md:213 msgid "Distributed tracing" msgstr "" -#: src/contributing/devenv-setup.md:218 +#: src/contributing/devenv-setup.md:214 msgid "Loki" msgstr "" -#: src/contributing/devenv-setup.md:218 +#: src/contributing/devenv-setup.md:214 msgid "" msgstr "" -#: src/contributing/devenv-setup.md:218 +#: src/contributing/devenv-setup.md:214 msgid "Log aggregation" msgstr "" -#: src/contributing/devenv-setup.md:219 +#: src/contributing/devenv-setup.md:215 msgid "Prometheus" msgstr "" -#: src/contributing/devenv-setup.md:219 +#: src/contributing/devenv-setup.md:215 msgid "" msgstr "" -#: src/contributing/devenv-setup.md:219 +#: src/contributing/devenv-setup.md:215 msgid "Metrics" msgstr "" -#: src/contributing/devenv-setup.md:220 +#: src/contributing/devenv-setup.md:216 msgid "OTLP Collector" msgstr "" -#: src/contributing/devenv-setup.md:220 +#: src/contributing/devenv-setup.md:216 msgid "" msgstr "" -#: src/contributing/devenv-setup.md:220 +#: src/contributing/devenv-setup.md:216 msgid "Telemetry ingestion" msgstr "" -#: src/contributing/devenv-setup.md:221 +#: src/contributing/devenv-setup.md:217 msgid "Pyroscope" msgstr "" -#: src/contributing/devenv-setup.md:221 +#: src/contributing/devenv-setup.md:217 msgid "" msgstr "" -#: src/contributing/devenv-setup.md:221 +#: src/contributing/devenv-setup.md:217 msgid "Continuous profiling" msgstr "" -#: src/contributing/devenv-setup.md:223 +#: src/contributing/devenv-setup.md:219 msgid "For more details on telemetry, see [Telemetry](telemetry/index.md)." msgstr "" -#: src/contributing/devenv-setup.md:227 +#: src/contributing/devenv-setup.md:223 msgid "Shell takes a long time to start" msgstr "" -#: src/contributing/devenv-setup.md:229 +#: src/contributing/devenv-setup.md:225 msgid "" "The first `devenv shell` invocation downloads and builds dependencies, which " "can take several minutes. Subsequent invocations are cached and should be " "fast." msgstr "" -#: src/contributing/devenv-setup.md:232 +#: src/contributing/devenv-setup.md:228 msgid "Tests fail with \"TEST_ROOT_DIR must be set\"" msgstr "" -#: src/contributing/devenv-setup.md:234 +#: src/contributing/devenv-setup.md:230 msgid "Set the environment variable before running tests:" msgstr "" -#: src/contributing/devenv-setup.md:240 +#: src/contributing/devenv-setup.md:236 msgid "Local Configuration" msgstr "" -#: src/contributing/devenv-setup.md:242 +#: src/contributing/devenv-setup.md:238 msgid "" "Create `devenv.local.nix` to override settings without modifying the tracked " "configuration:" msgstr "" -#: src/contributing/devenv-setup.md:244 +#: src/contributing/devenv-setup.md:240 msgid "" "```nix\n" "{ pkgs, ... }:\n" @@ -4178,7 +4207,7 @@ msgid "" "```" msgstr "" -#: src/contributing/devenv-setup.md:255 +#: src/contributing/devenv-setup.md:251 msgid "This file is gitignored and won't affect other contributors." msgstr "" @@ -4940,7 +4969,7 @@ msgid "**Comparison:**" msgstr "" #: src/contributing/event-system.md:325 -#: src/contributing/library-database.md:148 +#: src/contributing/library-database.md:159 msgid "Method" msgstr "" @@ -6373,6 +6402,10 @@ msgid "" " INTEGER library_id FK\n" " TEXT book_fingerprint FK\n" " INTEGER added_to_library_at\n" +" TEXT file_path\n" +" TEXT absolute_path\n" +" INTEGER mtime\n" +" INTEGER file_size\n" " }\n" "\n" " _cadmus_migrations {\n" @@ -6394,37 +6427,37 @@ msgid "" "```" msgstr "" -#: src/contributing/library-database.md:98 +#: src/contributing/library-database.md:102 msgid "Key design choices" msgstr "" -#: src/contributing/library-database.md:100 +#: src/contributing/library-database.md:104 msgid "" "**`books` is the main table.** Every other per-book table references " "`books.fingerprint` with `ON DELETE CASCADE`, so deleting a book row removes " "all associated data automatically." msgstr "" -#: src/contributing/library-database.md:103 +#: src/contributing/library-database.md:107 msgid "" "**Authors are normalised.** `authors` holds unique author names; " "`book_authors` is the join table and carries a `position` column that " "preserves display order." msgstr "" -#: src/contributing/library-database.md:106 +#: src/contributing/library-database.md:110 msgid "" "**All tables use `STRICT` mode.** SQLite's `STRICT` pragma enforces column " "type constraints at the storage layer, catching type mismatches early." msgstr "" -#: src/contributing/library-database.md:108 +#: src/contributing/library-database.md:112 msgid "" "**Timestamps are Unix epoch integers.** `added_at`, `created_at`, and " "similar columns are `INTEGER NOT NULL`; never `TEXT`." msgstr "" -#: src/contributing/library-database.md:110 +#: src/contributing/library-database.md:114 msgid "" "**TOC tree via adjacency list.** `toc_entries.parent_id` is a " "self-reference; `position` preserves sibling order. The `id` is a UUID7 " @@ -6432,7 +6465,18 @@ msgid "" "without a growing rowid." msgstr "" -#: src/contributing/library-database.md:113 +#: src/contributing/library-database.md:117 +msgid "" +"**Incremental import via `mtime` and `file_size`.** The `library_books` " +"table stores the file's last modification time (ceiling-rounded to 2-second " +"FAT32 precision) and size in bytes. During import scans, files whose `mtime` " +"and `file_size` have not changed are skipped without re-fingerprinting, " +"improving startup performance. An index on `(library_id, absolute_path)` " +"enables efficient per-path lookups. Both columns are nullable to support " +"books imported before migration 011." +msgstr "" + +#: src/contributing/library-database.md:124 msgid "" "**`library_books_full_info` view.** An aggregating view joins `books`, " "`reading_states`, `book_authors`, `authors`, `book_categories`, and " @@ -6440,11 +6484,11 @@ msgid "" "exposed so callers can filter with a plain `WHERE library_id = ?`." msgstr "" -#: src/contributing/library-database.md:118 +#: src/contributing/library-database.md:129 msgid "Data access layer" msgstr "" -#: src/contributing/library-database.md:120 +#: src/contributing/library-database.md:131 msgid "" "The `cadmus_core::library::db::Db` " @@ -6453,7 +6497,7 @@ msgid "" "async SQLx call through the global Tokio runtime:" msgstr "" -#: src/contributing/library-database.md:126 +#: src/contributing/library-database.md:137 msgid "" "```mermaid\n" "flowchart LR\n" @@ -6472,7 +6516,7 @@ msgid "" "```" msgstr "" -#: src/contributing/library-database.md:142 +#: src/contributing/library-database.md:153 msgid "" "The sync bridge exists because Cadmus's UI event loop is single-threaded and " "synchronous. The global `RUNTIME` (a `tokio::runtime::Runtime` singleton) " @@ -6480,109 +6524,109 @@ msgid "" "async." msgstr "" -#: src/contributing/library-database.md:146 +#: src/contributing/library-database.md:157 msgid "Key methods on `Db`:" msgstr "" -#: src/contributing/library-database.md:150 +#: src/contributing/library-database.md:161 msgid "" "`register_library`" msgstr "" -#: src/contributing/library-database.md:150 +#: src/contributing/library-database.md:161 msgid "Insert a new library row and return its id" msgstr "" -#: src/contributing/library-database.md:151 +#: src/contributing/library-database.md:162 msgid "" "`get_library_by_path`" msgstr "" -#: src/contributing/library-database.md:151 +#: src/contributing/library-database.md:162 msgid "Look up a library id by filesystem path" msgstr "" -#: src/contributing/library-database.md:152 +#: src/contributing/library-database.md:163 msgid "" "`get_all_books`" msgstr "" -#: src/contributing/library-database.md:152 +#: src/contributing/library-database.md:163 msgid "Fetch every book in a library via the full-info view" msgstr "" -#: src/contributing/library-database.md:153 +#: src/contributing/library-database.md:164 msgid "" "`insert_book`" msgstr "" -#: src/contributing/library-database.md:153 +#: src/contributing/library-database.md:164 msgid "Write a new book and its authors/categories" msgstr "" -#: src/contributing/library-database.md:154 +#: src/contributing/library-database.md:165 msgid "" "`save_reading_state`" msgstr "" -#: src/contributing/library-database.md:154 +#: src/contributing/library-database.md:165 msgid "Save or update reading progress for a book" msgstr "" -#: src/contributing/library-database.md:155 +#: src/contributing/library-database.md:166 msgid "" "`save_toc`" msgstr "" -#: src/contributing/library-database.md:155 +#: src/contributing/library-database.md:166 msgid "Bulk-write a book's table of contents" msgstr "" -#: src/contributing/library-database.md:156 +#: src/contributing/library-database.md:167 msgid "" "`get_thumbnail`" msgstr "" -#: src/contributing/library-database.md:156 +#: src/contributing/library-database.md:167 msgid "Retrieve the stored cover thumbnail BLOB" msgstr "" -#: src/contributing/library-database.md:157 +#: src/contributing/library-database.md:168 msgid "" "`save_thumbnail`" msgstr "" -#: src/contributing/library-database.md:157 +#: src/contributing/library-database.md:168 msgid "Save or replace a cover thumbnail" msgstr "" -#: src/contributing/library-database.md:159 +#: src/contributing/library-database.md:170 msgid "How a book scan flows into the database" msgstr "" -#: src/contributing/library-database.md:161 +#: src/contributing/library-database.md:172 msgid "When a library directory is scanned, Cadmus follows this sequence:" msgstr "" -#: src/contributing/library-database.md:192 +#: src/contributing/library-database.md:203 msgid "Related pages" msgstr "" -#: src/contributing/library-database.md:194 +#: src/contributing/library-database.md:205 msgid "" "[SQLite & SQLx](sqlite-sqlx.md) — compile-time query verification, review " "rules" msgstr "" -#: src/contributing/library-database.md:195 +#: src/contributing/library-database.md:206 msgid "" "[Runtime Migrations](runtime-migrations.md) — one-time data migrations using " "the `migration!` macro" diff --git a/docs/po/xx.po b/docs/po/xx.po index 278960d1..04716680 100644 --- a/docs/po/xx.po +++ b/docs/po/xx.po @@ -1,17 +1,16 @@ msgid "" msgstr "" "Project-Id-Version: Cadmus Documentation\n" -"POT-Creation-Date: 2026-03-21T18:51:51+01:00\n" +"POT-Creation-Date: 2026-06-07T18:53:20Z\n" "PO-Revision-Date: 2026-03-21 18:52+0100\n" "Language-Team: Language xx\n" +"Language: xx\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"Language: xx\n" "Plural-Forms: nplurals=1; plural=0;\n" -#: src/SUMMARY.md:1 src/contributing/event-system.md:16 -#: src/contributing/telemetry.md:5 +#: src/SUMMARY.md:1 src/contributing/event-system.md:14 msgid "Overview" msgstr "" @@ -31,63 +30,126 @@ msgstr "" msgid "OTA updates" msgstr "" -#: src/SUMMARY.md:8 src/installation/migration.md:1 +#: src/SUMMARY.md:8 +msgid "Manually install PR builds" +msgstr "" + +#: src/SUMMARY.md:9 src/installation/migration.md:1 msgid "Migrating from Plato" msgstr "" -#: src/SUMMARY.md:9 src/settings/index.md:1 +#: src/SUMMARY.md:10 src/installation/uninstall.md:1 +msgid "Uninstalling Cadmus" +msgstr "" + +#: src/SUMMARY.md:11 src/SUMMARY.md:15 src/settings/index.md:1 +#: src/ui/settings/index.md:1 msgid "Settings" msgstr "" -#: src/SUMMARY.md:10 src/ui/index.md:1 +#: src/SUMMARY.md:12 src/ui/index.md:1 msgid "User Interface" msgstr "" -#: src/SUMMARY.md:11 src/ui/filechooser.md:1 +#: src/SUMMARY.md:13 src/ui/filechooser.md:1 msgid "File Chooser" msgstr "" -#: src/SUMMARY.md:12 src/troubleshooting/index.md:1 -#: src/contributing/devenv-setup.md:228 +#: src/SUMMARY.md:14 src/ui/keyboard.md:1 +msgid "Keyboard" +msgstr "" + +#: src/SUMMARY.md:16 src/installation/migration.md:100 +#: src/ui/settings/dictionaries.md:1 +msgid "Dictionaries" +msgstr "" + +#: src/SUMMARY.md:17 src/library/index.md:1 +msgid "Library" +msgstr "" + +#: src/SUMMARY.md:18 +msgid "Importing" +msgstr "" + +#: src/SUMMARY.md:19 src/installation/ota.md:86 src/troubleshooting/index.md:1 +#: src/contributing/devenv-setup.md:221 msgid "Troubleshooting" msgstr "" -#: src/SUMMARY.md:16 +#: src/SUMMARY.md:23 msgid "Contributor Guide" msgstr "" -#: src/SUMMARY.md:18 +#: src/SUMMARY.md:25 msgid "Development Environment" msgstr "" -#: src/SUMMARY.md:19 src/contributing/event-system.md:1 +#: src/SUMMARY.md:26 src/contributing/code-style.md:1 +msgid "Code Style and Linting" +msgstr "" + +#: src/SUMMARY.md:27 src/contributing/event-system.md:1 msgid "Event System" msgstr "" -#: src/SUMMARY.md:20 src/contributing/telemetry.md:1 -msgid "OpenTelemetry Integration" +#: src/SUMMARY.md:28 src/settings/index.md:283 +#: src/contributing/telemetry/index.md:1 +msgid "Telemetry" +msgstr "" + +#: src/SUMMARY.md:29 src/contributing/telemetry/logging.md:1 +msgid "Logging" msgstr "" -#: src/SUMMARY.md:21 src/index.md:9 +#: src/SUMMARY.md:30 src/contributing/telemetry/tracing.md:1 +msgid "Tracing" +msgstr "" + +#: src/SUMMARY.md:31 src/contributing/telemetry/profiling.md:1 +msgid "Profiling" +msgstr "" + +#: src/SUMMARY.md:32 src/index.md:9 msgid "Documentation" msgstr "" -#: src/SUMMARY.md:22 +#: src/SUMMARY.md:33 src/contributing/translations/index.md:1 msgid "Translations" msgstr "" -#: src/SUMMARY.md:23 +#: src/SUMMARY.md:34 +msgid "UI Strings" +msgstr "" + +#: src/SUMMARY.md:35 +msgid "Guide" +msgstr "" + +#: src/SUMMARY.md:36 src/contributing/sqlite-sqlx.md:1 msgid "SQLite & SQLx" msgstr "" -#: src/SUMMARY.md:24 src/contributing/library-database.md:1 +#: src/SUMMARY.md:37 src/contributing/library-database.md:1 msgid "Library Database" msgstr "" -#: src/SUMMARY.md:25 src/contributing/runtime-migrations.md:1 +#: src/SUMMARY.md:38 src/contributing/runtime-migrations.md:1 msgid "Runtime Migrations" msgstr "" +#: src/SUMMARY.md:42 src/investigations/index.md:1 +msgid "Investigations" +msgstr "" + +#: src/SUMMARY.md:44 +msgid "Intro" +msgstr "" + +#: src/SUMMARY.md:45 src/investigations/kobo/issue-51-dhcp-investigation.md:1 +msgid "DHCP IP Address Changes on WiFi Toggle" +msgstr "" + #: src/index.md:3 msgid "Cadmus" msgstr "" @@ -114,64 +176,155 @@ msgstr "" msgid "Supported devices" msgstr "" -#: src/index.md:63 +#: src/index.md:20 +msgid "_Libra Colour_." +msgstr "" + +#: src/index.md:21 +msgid "_Clara Colour_." +msgstr "" + +#: src/index.md:22 +msgid "_Clara BW_." +msgstr "" + +#: src/index.md:23 +msgid "_Elipsa 2E_." +msgstr "" + +#: src/index.md:24 +msgid "_Clara 2E_." +msgstr "" + +#: src/index.md:25 +msgid "_Libra 2_." +msgstr "" + +#: src/index.md:26 +msgid "_Sage_." +msgstr "" + +#: src/index.md:27 +msgid "_Elipsa_." +msgstr "" + +#: src/index.md:28 +msgid "_Nia_." +msgstr "" + +#: src/index.md:29 +msgid "_Libra H₂O_." +msgstr "" + +#: src/index.md:30 +msgid "_Forma_." +msgstr "" + +#: src/index.md:31 +msgid "_Clara HD_." +msgstr "" + +#: src/index.md:32 +msgid "_Aura H₂O Edition 2_." +msgstr "" + +#: src/index.md:33 +msgid "_Aura Edition 2_." +msgstr "" + +#: src/index.md:34 +msgid "_Aura ONE_." +msgstr "" + +#: src/index.md:35 +msgid "_Glo HD_." +msgstr "" + +#: src/index.md:36 +msgid "_Aura H₂O_." +msgstr "" + +#: src/index.md:37 +msgid "_Aura_." +msgstr "" + +#: src/index.md:38 +msgid "_Glo_." +msgstr "" + +#: src/index.md:39 +msgid "_Touch C_." +msgstr "" + +#: src/index.md:40 +msgid "_Touch B_." +msgstr "" + +#: src/index.md:42 msgid "Supported formats" msgstr "" -#: src/index.md:65 +#: src/index.md:44 msgid "" -"PDF, CBZ, FB2, MOBI, XPS and TXT via [MuPDF](https://mupdf.com/index.html)." +"PDF, CBZ, FB2, MOBI, XPS, TXT, JPEG, JPG, PNG, WEBP and SVG via [MuPDF]" +"(https://mupdf.com/index.html)." msgstr "" -#: src/index.md:66 +#: src/index.md:45 msgid "ePUB through a built-in renderer." msgstr "" -#: src/index.md:67 +#: src/index.md:46 msgid "DJVU via [DjVuLibre](http://djvu.sourceforge.net/index.html)." msgstr "" -#: src/index.md:69 +#: src/index.md:48 msgid "Features" msgstr "" -#: src/index.md:71 +#: src/index.md:50 msgid "Crop the margins." msgstr "" -#: src/index.md:72 +#: src/index.md:51 msgid "Continuous fit-to-width zoom mode with line preserving cuts." msgstr "" -#: src/index.md:73 +#: src/index.md:52 msgid "Rotate the screen (portrait ↔ landscape)." msgstr "" -#: src/index.md:74 +#: src/index.md:53 msgid "Adjust the contrast." msgstr "" -#: src/index.md:75 +#: src/index.md:54 msgid "Define words using _dictd_ dictionaries." msgstr "" -#: src/index.md:76 +#: src/index.md:55 msgid "Annotations, highlights and bookmarks." msgstr "" -#: src/index.md:77 +#: src/index.md:56 msgid "Retrieve articles from online sources through hooks." msgstr "" -#: src/index.md:79 +#: src/index.md:58 msgid "Screenshots" msgstr "" -#: src/index.md:90 +#: src/index.md:60 +msgid "" +"![Tn01](artworks/thumbnail01.png) ![Tn02](artworks/thumbnail02.png) ![Tn03]" +"(artworks/thumbnail03.png) ![Tn04](artworks/thumbnail04.png)" +msgstr "" + +#: src/index.md:65 msgid "Acknowledgments" msgstr "" -#: src/index.md:92 +#: src/index.md:67 msgid "" "Cadmus is a fork of [Plato](https://github.com/baskerville/plato), a " "document reader created by Bastien Dejean." @@ -207,6 +360,7 @@ msgid "Cadmus only" msgstr "" #: src/installation/index.md:9 src/installation/index.md:10 +#: src/installation/uninstall.md:10 msgid "`/mnt/onboard/.adds/cadmus`" msgstr "" @@ -227,6 +381,7 @@ msgid "Test build only" msgstr "" #: src/installation/index.md:11 src/installation/index.md:12 +#: src/installation/uninstall.md:11 msgid "`/mnt/onboard/.adds/cadmus-tst`" msgstr "" @@ -257,7 +412,7 @@ msgid "" "out changes that haven't been released yet" msgstr "" -#: src/installation/index.md:21 src/installation/ota.md:66 +#: src/installation/index.md:21 src/installation/ota.md:80 msgid "First-time setup" msgstr "" @@ -271,78 +426,98 @@ msgstr "" msgid "Download the package you want from the table above." msgstr "" -#: src/installation/index.md:25 src/installation/index.md:44 +#: src/installation/index.md:25 src/installation/index.md:50 +#: src/installation/uninstall.md:5 msgid "Connect your Kobo to your computer via USB." msgstr "" #: src/installation/index.md:26 +msgid "Rename the downloaded file to `KoboRoot.tgz`." +msgstr "" + +#: src/installation/index.md:27 msgid "" -"Copy the downloaded file to `/mnt/onboard/.kobo/KoboRoot.tgz` on the device." +"Copy that renamed file to `/mnt/onboard/.kobo/KoboRoot.tgz` on the device." msgstr "" -#: src/installation/index.md:27 src/installation/test-builds.md:12 +#: src/installation/index.md:28 src/installation/test-builds.md:13 msgid "Eject the device and reboot." msgstr "" -#: src/installation/index.md:29 +#: src/installation/index.md:30 +msgid "" +"\\[!NOTE\\] You must rename the file to `KoboRoot.tgz` before copying it to " +"your Kobo. For example, `KoboRoot-nm.tgz` and `KoboRoot-test.tgz` will not " +"install until you rename them." +msgstr "" + +#: src/installation/index.md:35 msgid "Updating" msgstr "" -#: src/installation/index.md:31 +#: src/installation/index.md:37 msgid "There are two ways to update Cadmus once it's installed." msgstr "" -#: src/installation/index.md:33 +#: src/installation/index.md:39 msgid "Wirelessly (OTA)" msgstr "" -#: src/installation/index.md:35 +#: src/installation/index.md:41 msgid "" "The easiest way — no computer needed, just WiFi. Open **Main Menu → Check " "for Updates** and follow the prompts. See [OTA updates](./ota.md) for " "details." msgstr "" -#: src/installation/index.md:39 +#: src/installation/index.md:45 msgid "Via USB" msgstr "" -#: src/installation/index.md:41 +#: src/installation/index.md:47 msgid "" "You can also update by copying a new package over USB, the same way you did " "the first-time install." msgstr "" -#: src/installation/index.md:45 +#: src/installation/index.md:51 msgid "When Cadmus asks \"Share storage via USB?\", tap **Share**." msgstr "" -#: src/installation/index.md:46 +#: src/installation/index.md:52 msgid "" "Download the package you want from the [latest release](https://github.com/" "OGKevin/cadmus/releases/latest)." msgstr "" -#: src/installation/index.md:47 +#: src/installation/index.md:53 msgid "Copy it to `/mnt/onboard/.kobo/KoboRoot.tgz` on your Kobo." msgstr "" -#: src/installation/index.md:48 +#: src/installation/index.md:54 msgid "Eject and disconnect the USB cable." msgstr "" -#: src/installation/index.md:50 +#: src/installation/index.md:56 msgid "" "\\[!NOTE\\] Always name the file `KoboRoot.tgz` on the device, regardless of " "which package you downloaded (e.g. `KoboRoot-nm.tgz` must be renamed)." msgstr "" -#: src/installation/index.md:54 +#: src/installation/index.md:60 msgid "" "Cadmus detects the file automatically and reboots your Kobo to install the " "update. You don't need to do anything else." msgstr "" +#: src/installation/index.md:63 +msgid "Uninstalling" +msgstr "" + +#: src/installation/index.md:65 +msgid "See [Uninstalling Cadmus](./uninstall.md)." +msgstr "" + #: src/installation/test-builds.md:3 msgid "First-time install" msgstr "" @@ -368,14 +543,24 @@ msgid "Extract it and pick the [package](./index.md) that matches your setup." msgstr "" #: src/installation/test-builds.md:10 -msgid "Copy the selected KoboRoot file to: `/mnt/onboard/.kobo/KoboRoot.tgz`" +msgid "Rename the selected file to `KoboRoot.tgz`." +msgstr "" + +#: src/installation/test-builds.md:11 +msgid "Copy that renamed file to: `/mnt/onboard/.kobo/KoboRoot.tgz`" +msgstr "" + +#: src/installation/test-builds.md:15 +msgid "" +"\\[!NOTE\\] Test packages such as `KoboRoot-test.tgz` and `KoboRoot-nm-test." +"tgz` must be renamed to `KoboRoot.tgz` before you copy them to your Kobo." msgstr "" -#: src/installation/test-builds.md:14 +#: src/installation/test-builds.md:19 msgid "Updating an existing test build" msgstr "" -#: src/installation/test-builds.md:16 +#: src/installation/test-builds.md:21 msgid "" "Use the OTA feature to download updates from a PR number directly on your " "device. This lets you test changes without connecting to a computer." @@ -397,145 +582,240 @@ msgid "A WiFi connection" msgstr "" #: src/installation/ota.md:10 +msgid "Authentication" +msgstr "" + +#: src/installation/ota.md:12 +msgid "" +"Main branch and PR builds require a GitHub account. Stable releases are " +"public and need no authentication." +msgstr "" + +#: src/installation/ota.md:15 +msgid "" +"The first time you request a main branch or PR build, Cadmus will show a " +"screen with a URL and a short code:" +msgstr "" + +#: src/installation/ota.md:18 +msgid "Go to the URL shown on screen" +msgstr "" + +#: src/installation/ota.md:19 +msgid "Enter the code shown on your device" +msgstr "" + +#: src/installation/ota.md:20 +msgid "Sign in to GitHub and approve the request" +msgstr "" + +#: src/installation/ota.md:22 msgid "" -"Stable releases are public and require no authentication. Main branch and PR " -"builds require a GitHub account — Cadmus will guide you through a one-time " -"sign-in the first time you request one." +"Cadmus detects the approval automatically and starts the download. The token " +"is saved to disk so you won't need to sign in again." +msgstr "" + +#: src/installation/ota.md:27 +msgid "![ota-pick](./screenshots/ota-pick.png)" +msgstr "" + +#: src/installation/ota.md:27 +msgid "![auth](./screenshots/device-auth.png)" msgstr "" -#: src/installation/ota.md:14 +#: src/installation/ota.md:29 msgid "How to update" msgstr "" -#: src/installation/ota.md:16 +#: src/installation/ota.md:31 msgid "" "Open **Main Menu → Check for Updates**. You'll see options for where to get " "the update from:" msgstr "" -#: src/installation/ota.md:19 src/installation/migration.md:35 +#: src/installation/ota.md:34 src/installation/migration.md:43 msgid "Source" msgstr "" -#: src/installation/ota.md:19 src/contributing/devenv-setup.md:43 -#: src/contributing/devenv-setup.md:68 +#: src/installation/ota.md:34 src/contributing/devenv-setup.md:47 +#: src/contributing/devenv-setup.md:73 msgid "Description" msgstr "" -#: src/installation/ota.md:21 +#: src/installation/ota.md:36 msgid "**Stable Release**" msgstr "" -#: src/installation/ota.md:21 +#: src/installation/ota.md:36 msgid "Latest official release from GitHub" msgstr "" -#: src/installation/ota.md:22 +#: src/installation/ota.md:37 msgid "**Main Branch**" msgstr "" -#: src/installation/ota.md:22 +#: src/installation/ota.md:37 msgid "Latest development build (most recent changes)" msgstr "" -#: src/installation/ota.md:23 +#: src/installation/ota.md:38 msgid "**PR Build**" msgstr "" -#: src/installation/ota.md:23 +#: src/installation/ota.md:38 msgid "Test a specific pull request" msgstr "" -#: src/installation/ota.md:25 +#: src/installation/ota.md:40 msgid "\\[!NOTE\\] The _Stable Release_ option is not shown in test builds." msgstr "" -#: src/installation/ota.md:28 +#: src/installation/ota.md:43 msgid "Updating from the main branch" msgstr "" -#: src/installation/ota.md:30 +#: src/installation/ota.md:45 msgid "" "Select **Main Branch** to get the most recent development build. This " "includes changes that have been merged but not yet released officially." msgstr "" -#: src/installation/ota.md:33 +#: src/installation/ota.md:48 msgid "" -"If this is your first time, Cadmus will show a screen with a URL and a short " -"code. On any device with a browser:" -msgstr "" - -#: src/installation/ota.md:36 -msgid "Go to the URL shown on screen" -msgstr "" - -#: src/installation/ota.md:37 -msgid "Enter the code shown on your Device" -msgstr "" - -#: src/installation/ota.md:38 -msgid "Sign in to GitHub and approve the request" +"If you haven't authenticated before, Cadmus will guide you through the " +"GitHub sign-in process. See [Authentication](#authentication) for details." msgstr "" -#: src/installation/ota.md:40 +#: src/installation/ota.md:51 msgid "" -"Cadmus will detect the approval automatically and start the download. The " -"token is saved to disk so you won't need to sign in again." +"The update downloads from GitHub, installs automatically, and reboots the " +"device to finish." msgstr "" -#: src/installation/ota.md:43 +#: src/installation/ota.md:54 msgid "" -"The update downloads from GitHub, installs automatically, and reboots the " -"device to finish." +"Before that reboot, Cadmus removes the files it previously installed so the " +"new package can replace them cleanly. Your custom fonts, icons, and other " +"user-added files will be preserved." msgstr "" -#: src/installation/ota.md:46 +#: src/installation/ota.md:58 msgid "Testing a pull request" msgstr "" -#: src/installation/ota.md:48 +#: src/installation/ota.md:60 msgid "" "Select **PR Build** to try out a specific change before it's released. Enter " -"the PR number when prompted. The same one-time GitHub sign-in applies if you " -"haven't authenticated before." +"the PR number when prompted. If you haven't authenticated before, Cadmus " +"will guide you through the GitHub sign-in process. See [Authentication]" +"(#authentication) for details." msgstr "" -#: src/installation/ota.md:52 +#: src/installation/ota.md:65 msgid "" "\\[!TIP\\] Find the PR number in the GitHub URL. For example, in `github.com/" "OGKevin/cadmus/pull/42` the PR number is **42**." msgstr "" -#: src/installation/ota.md:56 +#: src/installation/ota.md:69 msgid "Normal vs test builds" msgstr "" -#: src/installation/ota.md:58 +#: src/installation/ota.md:71 msgid "" "OTA works for both types of builds. The type you're currently using " "determines what gets downloaded:" msgstr "" -#: src/installation/ota.md:61 +#: src/installation/ota.md:74 msgid "" "**Normal builds** update to `KoboRoot.tgz` in `/mnt/onboard/.adds/cadmus`" msgstr "" -#: src/installation/ota.md:62 +#: src/installation/ota.md:75 msgid "" "**Test builds** update to `KoboRoot-test.tgz` in `/mnt/onboard/.adds/cadmus-" -"tst` See the [available packages](./index.md#available-packages) table for " -"all options." +"tst`" +msgstr "" + +#: src/installation/ota.md:77 +msgid "" +"See the [available packages](./index.md#available-packages) table for all " +"options." msgstr "" -#: src/installation/ota.md:68 +#: src/installation/ota.md:82 msgid "" "OTA only works for updating an existing installation. To install Cadmus for " "the first time, follow the [installation guide](./index.md) or the [test " "builds guide](./test-builds.md) to copy a KoboRoot file via USB." msgstr "" +#: src/installation/ota.md:88 +msgid "\"Insufficient disk space\" error" +msgstr "" + +#: src/installation/ota.md:90 +msgid "" +"If Cadmus shows an error like _\"Insufficient disk space: need 100MB, have " +"XMB\"_ while downloading an update:" +msgstr "" + +#: src/installation/ota.md:93 +msgid "Cadmus downloads update files into a `tmp` folder:" +msgstr "" + +#: src/installation/ota.md:94 +msgid "**With SD card**: `/mnt/sd/.cadmus/tmp` (uses SD card space)" +msgstr "" + +#: src/installation/ota.md:95 +msgid "" +"**Without SD card**: `/mnt/onboard/.adds/cadmus/tmp` (uses internal storage)" +msgstr "" + +#: src/installation/ota.md:96 +msgid "" +"If you see this error and have an SD card inserted, the card may be full" +msgstr "" + +#: src/installation/ota.md:97 +msgid "" +"If you do not have an SD card, free up space on internal storage by deleting " +"books or other files you do not need" +msgstr "" + +#: src/installation/manual-pr-install.md:1 +msgid "Manually install PR build" +msgstr "" + +#: src/installation/manual-pr-install.md:3 +msgid "To manually install a PR build, follow these steps:" +msgstr "" + +#: src/installation/manual-pr-install.md:5 +msgid "Open the PR you want to install" +msgstr "" + +#: src/installation/manual-pr-install.md:6 +msgid "Press the checks tab ![pr checks](screenshots/pr-checks.png)" +msgstr "" + +#: src/installation/manual-pr-install.md:8 +msgid "Press the cargo job ![cargo job](screenshots/pr-cargo-job.png)" +msgstr "" + +#: src/installation/manual-pr-install.md:10 +msgid "" +"Scroll down until you find the files ![Download from GitHub Actions]" +"(screenshots/artifacts.png)" +msgstr "" + +#: src/installation/manual-pr-install.md:13 +msgid "" +"Afterward, follow the [installation](index.md) instructions accordingly." +msgstr "" + #: src/installation/migration.md:3 msgid "" "Cadmus is a fork of Plato and uses the same `Settings.toml` format, so " @@ -546,7 +826,8 @@ msgstr "" msgid "Copy your settings" msgstr "" -#: src/installation/migration.md:8 src/installation/migration.md:69 +#: src/installation/migration.md:8 src/installation/migration.md:71 +#: src/installation/uninstall.md:8 src/installation/uninstall.md:16 msgid "Build" msgstr "" @@ -558,7 +839,8 @@ msgstr "" msgid "Cadmus settings" msgstr "" -#: src/installation/migration.md:10 src/installation/migration.md:71 +#: src/installation/migration.md:10 src/installation/migration.md:73 +#: src/installation/uninstall.md:10 src/installation/uninstall.md:18 msgid "Stable" msgstr "" @@ -566,11 +848,12 @@ msgstr "" msgid "`/mnt/onboard/.adds/plato/Settings.toml`" msgstr "" -#: src/installation/migration.md:10 +#: src/installation/migration.md:10 src/installation/migration.md:98 msgid "`/mnt/onboard/.adds/cadmus/Settings.toml`" msgstr "" -#: src/installation/migration.md:11 src/installation/migration.md:72 +#: src/installation/migration.md:11 src/installation/migration.md:74 +#: src/installation/uninstall.md:11 src/installation/uninstall.md:19 msgid "Test" msgstr "" @@ -581,264 +864,463 @@ msgstr "" #: src/installation/migration.md:13 msgid "" "Copy the file as-is into the Cadmus folder so it is named `Settings.toml` " -"(for example, `/mnt/onboard/.adds/cadmus/Settings.toml` or `/mnt/" -"onboard/.adds/cadmus-tst/Settings.toml`). The `[[libraries]]` section is the " -"most important part, it tells Cadmus where your books live and drives the " -"reading-progress import on first launch. On first launch, Cadmus will move " -"this file into its `Settings/` folder automatically." +"(for example, `/mnt/onboard/.adds/cadmus/Settings.toml` or `/mnt/onboard/." +"adds/cadmus-tst/Settings.toml`). The `[[libraries]]` section is the most " +"important part, it tells Cadmus where your books live and drives the reading-" +"progress import on first launch. On first launch, Cadmus will move this file " +"into its `Settings/` folder automatically." +msgstr "" + +#: src/installation/migration.md:17 +msgid "" +"\\[!NOTE\\] If your SD card is already inserted when you upgrade, Cadmus " +"automatically moves your settings, logs, and dictionaries to `/mnt/sd/." +"cadmus/` (or `/mnt/sd/.cadmus-tst/` for test builds) on the next boot." +msgstr "" + +#: src/installation/migration.md:22 +msgid "" +"\\[!NOTE\\] If you insert an SD card **after** Cadmus has already run, the " +"automatic migration will not run. You will need to copy your data manually — " +"see [Moving data to an SD card](#moving-data-to-an-sd-card) below." +msgstr "" + +#: src/installation/migration.md:27 src/settings/index.md:138 +msgid "" +"```toml\n" +"[[libraries]]\n" +"name = \"On Board\"\n" +"path = \"/mnt/onboard\"\n" +"mode = \"database\"\n" +"```" msgstr "" -#: src/installation/migration.md:26 +#: src/installation/migration.md:34 msgid "" "\\[!IMPORTANT\\] Make sure each `[[libraries]]` entry has the correct `path` " "and `name`. If a path doesn't match what's on disk, Cadmus skips that " "library's import." msgstr "" -#: src/installation/migration.md:30 +#: src/installation/migration.md:38 msgid "What happens on first launch" msgstr "" -#: src/installation/migration.md:32 +#: src/installation/migration.md:40 msgid "" "When Cadmus starts for the first time it automatically imports your data " "from each library listed in settings:" msgstr "" -#: src/installation/migration.md:35 +#: src/installation/migration.md:43 msgid "What's imported" msgstr "" -#: src/installation/migration.md:37 +#: src/installation/migration.md:45 msgid "`.metadata.json`" msgstr "" -#: src/installation/migration.md:37 +#: src/installation/migration.md:45 msgid "Book metadata (title, author, …) and reading progress" msgstr "" -#: src/installation/migration.md:38 +#: src/installation/migration.md:46 msgid "`.reading-states/.json`" msgstr "" -#: src/installation/migration.md:38 +#: src/installation/migration.md:46 msgid "Reading progress for books not already covered by the above" msgstr "" -#: src/installation/migration.md:40 +#: src/installation/migration.md:48 msgid "" -"Both database mode and filesystem mode libraries are handled. Cadmus reads " -"`.reading-states/` in all cases, so current page, bookmarks, and annotations " +"Both database mode and filesystem mode libraries are handled. Cadmus reads `." +"reading-states/` in all cases, so current page, bookmarks, and annotations " "carry over regardless of which mode you used in Plato." msgstr "" -#: src/installation/migration.md:44 -msgid "\\[!NOTE\\] After a successful import the original files are renamed:" -msgstr "" - -#: src/installation/migration.md:50 -msgid "`.reading-states/` → `.reading-states.imported`" -msgstr "" - #: src/installation/migration.md:52 msgid "" -"These renamed files are just a safety backup. Once you've confirmed " -"everything looks right you can delete them." +"\\[!NOTE\\] The original `.metadata.json` and `.reading-states/` files are " +"not modified or deleted during import. Once you have confirmed everything " +"looks right in Cadmus, you can safely delete them. Keeping these files means " +"your Plato progress remains intact. If you decide to go back to Plato, you " +"can do so without losing your original Plato reading states. Though keep in " +"mind that any progress you make in Cadmus will not sync back to Plato." msgstr "" -#: src/installation/migration.md:55 +#: src/installation/migration.md:60 msgid "" "\\[!NOTE\\] Cadmus also removes the `.thumbnail-previews/` folder and " "regenerates thumbnails itself." msgstr "" -#: src/installation/migration.md:59 +#: src/installation/migration.md:64 msgid "Re-running the import" msgstr "" -#: src/installation/migration.md:61 +#: src/installation/migration.md:66 msgid "" "If the import went wrong (for example, the library path was incorrect in " "settings), you can start it fresh:" msgstr "" -#: src/installation/migration.md:64 -msgid "" -"Rename `.metadata.json.imported` back to `.metadata.json` and `.reading-" -"states.imported` back to `.reading-states/` in each library directory." -msgstr "" - -#: src/installation/migration.md:67 +#: src/installation/migration.md:69 msgid "Delete the Cadmus SQLite database:" msgstr "" -#: src/installation/migration.md:69 -msgid "Database path" +#: src/installation/migration.md:71 +msgid "Database path (with SD card)" msgstr "" #: src/installation/migration.md:71 +msgid "Database path (without SD card)" +msgstr "" + +#: src/installation/migration.md:73 src/installation/migration.md:101 +msgid "`/mnt/sd/.cadmus/cadmus.sqlite`" +msgstr "" + +#: src/installation/migration.md:73 src/installation/migration.md:101 msgid "`/mnt/onboard/.adds/cadmus/cadmus.sqlite`" msgstr "" -#: src/installation/migration.md:72 -msgid "`/mnt/onboard/.adds/cadmus-tst/cadmus.sqlite`" +#: src/installation/migration.md:74 +msgid "`/mnt/sd/.cadmus-tst/cadmus.sqlite`" msgstr "" #: src/installation/migration.md:74 -msgid "Restart Cadmus — the import will run again from scratch." +msgid "`/mnt/onboard/.adds/cadmus-tst/cadmus.sqlite`" msgstr "" #: src/installation/migration.md:76 +msgid "Restart Cadmus — the import will run again from scratch." +msgstr "" + +#: src/installation/migration.md:78 msgid "" "If something still looks wrong after re-running, check the logs for details. " "See [Troubleshooting](../troubleshooting/index.md) for where to find them." msgstr "" -#: src/settings/index.md:3 +#: src/installation/migration.md:81 +msgid "Moving data to an SD card" +msgstr "" + +#: src/installation/migration.md:83 msgid "" -"Cadmus reads settings from `Settings/Settings-*.toml`. Settings can be " -"changed via **Main Menu → Settings**, which opens the built-in settings " -"editor." +"If you insert an SD card after Cadmus has already run, you need to move your " +"data manually. Cadmus will use the SD card for new data once it detects the " +"card, but your existing files stay in internal storage until you move them." msgstr "" -#: src/settings/index.md:6 -msgid "**Legend:**" +#: src/installation/migration.md:87 +msgid "\\[!CAUTION\\] Do these operations while Cadmus is not running!" msgstr "" -#: src/settings/index.md:11 -msgid "🔑 Required for feature to work" +#: src/installation/migration.md:90 +msgid "In other words, do this while the Nickel/KOReader/etc is running." msgstr "" -#: src/settings/index.md:12 -msgid "🧪 Only available in test builds" +#: src/installation/migration.md:92 +msgid "Connect your Kobo to your computer" msgstr "" -#: src/settings/index.md:13 +#: src/installation/migration.md:93 +msgid "Copy the following from internal storage to the SD card:" +msgstr "" + +#: src/installation/migration.md:95 +msgid "What to move" +msgstr "" + +#: src/installation/migration.md:95 +msgid "From (internal)" +msgstr "" + +#: src/installation/migration.md:95 +msgid "To (SD card)" +msgstr "" + +#: src/installation/migration.md:97 +msgid "Settings directory" +msgstr "" + +#: src/installation/migration.md:97 +msgid "`/mnt/onboard/.adds/cadmus/Settings/`" +msgstr "" + +#: src/installation/migration.md:97 +msgid "`/mnt/sd/.cadmus/Settings/`" +msgstr "" + +#: src/installation/migration.md:98 +msgid "Settings file" +msgstr "" + +#: src/installation/migration.md:98 +msgid "`/mnt/sd/.cadmus/Settings.toml`" +msgstr "" + +#: src/installation/migration.md:99 src/troubleshooting/index.md:3 +msgid "Logs" +msgstr "" + +#: src/installation/migration.md:99 +msgid "`/mnt/onboard/.adds/cadmus/logs/`" +msgstr "" + +#: src/installation/migration.md:99 +msgid "`/mnt/sd/.cadmus/logs/`" +msgstr "" + +#: src/installation/migration.md:100 +msgid "`/mnt/onboard/.adds/cadmus/dictionaries/`" +msgstr "" + +#: src/installation/migration.md:100 +msgid "`/mnt/sd/.cadmus/dictionaries/`" +msgstr "" + +#: src/installation/migration.md:101 +msgid "Database" +msgstr "" + +#: src/installation/migration.md:103 +msgid "" +"\\[!NOTE\\] Use the test paths (`cadmus-tst` / `.cadmus-tst`) if you are on " +"a test build." +msgstr "" + +#: src/installation/migration.md:106 +msgid "" +"Restart Cadmus. It will pick up the files from the SD card automatically." +msgstr "" + +#: src/installation/uninstall.md:3 +msgid "To remove Cadmus from your Kobo:" +msgstr "" + +#: src/installation/uninstall.md:6 +msgid "Delete the Cadmus folder from `.adds`:" +msgstr "" + +#: src/installation/uninstall.md:8 +msgid "Folder to delete" +msgstr "" + +#: src/installation/uninstall.md:13 +msgid "" +"If you installed a package that included NickelMenu, delete the Cadmus menu " +"entry too:" +msgstr "" + +#: src/installation/uninstall.md:16 +msgid "NickelMenu entry to delete" +msgstr "" + +#: src/installation/uninstall.md:18 +msgid "`/mnt/onboard/.adds/nm/cadmus`" +msgstr "" + +#: src/installation/uninstall.md:19 +msgid "`/mnt/onboard/.adds/nm/cadmus-tst`" +msgstr "" + +#: src/installation/uninstall.md:21 +msgid "Eject the device and disconnect the USB cable." +msgstr "" + +#: src/installation/uninstall.md:23 +msgid "" +"\\[!NOTE\\] If you no longer need NickelMenu at all, you can remove it " +"separately." +msgstr "" + +#: src/settings/index.md:3 +msgid "" +"Cadmus reads settings from `Settings/Settings-*.toml`. Settings can be " +"changed via **Main Menu → Settings**, which opens the built-in settings " +"editor." +msgstr "" + +#: src/settings/index.md:6 +msgid "**Legend:**" +msgstr "" + +#: src/settings/index.md:8 +msgid "✏️ Editable in the settings editor" +msgstr "" + +#: src/settings/index.md:9 +msgid "🔑 Required for feature to work" +msgstr "" + +#: src/settings/index.md:10 +msgid "🧪 Only available in test builds" +msgstr "" + +#: src/settings/index.md:11 msgid "📱 Kobo" msgstr "" -#: src/settings/index.md:15 +#: src/settings/index.md:13 msgid "General Settings" msgstr "" -#: src/settings/index.md:17 +#: src/settings/index.md:15 msgid "`keyboard-layout`" msgstr "" -#: src/settings/index.md:19 src/settings/index.md:34 src/settings/index.md:47 -#: src/settings/index.md:65 src/settings/index.md:80 src/settings/index.md:95 -#: src/settings/index.md:114 src/settings/index.md:134 -#: src/settings/index.md:150 src/settings/index.md:156 -#: src/settings/index.md:162 src/settings/index.md:172 -#: src/settings/index.md:196 src/settings/index.md:212 -#: src/settings/index.md:222 src/settings/index.md:232 -#: src/settings/index.md:280 src/settings/index.md:294 +#: src/settings/index.md:17 src/settings/index.md:29 src/settings/index.md:39 +#: src/settings/index.md:54 src/settings/index.md:66 src/settings/index.md:78 +#: src/settings/index.md:90 src/settings/index.md:106 src/settings/index.md:123 +#: src/settings/index.md:134 src/settings/index.md:147 +#: src/settings/index.md:153 src/settings/index.md:159 +#: src/settings/index.md:167 src/settings/index.md:188 +#: src/settings/index.md:201 src/settings/index.md:213 +#: src/settings/index.md:221 src/settings/index.md:238 +#: src/settings/index.md:258 src/settings/index.md:307 +#: src/settings/index.md:318 msgid "✏️" msgstr "" -#: src/settings/index.md:21 +#: src/settings/index.md:19 msgid "Keyboard layout to use for text input." msgstr "" -#: src/settings/index.md:27 +#: src/settings/index.md:21 +msgid "Possible values: `\"English\"`, `\"Russian\"`." +msgstr "" + +#: src/settings/index.md:23 msgid "" "```toml\n" "keyboard-layout = \"English\"\n" "```" msgstr "" -#: src/settings/index.md:32 +#: src/settings/index.md:27 msgid "`sleep-cover`" msgstr "" -#: src/settings/index.md:36 +#: src/settings/index.md:31 msgid "Handle the magnetic sleep cover event." msgstr "" -#: src/settings/index.md:45 +#: src/settings/index.md:37 msgid "`auto-share`" msgstr "" -#: src/settings/index.md:49 +#: src/settings/index.md:41 msgid "" "Automatically enter shared mode when connected to a computer, skipping the " "\"Share storage via USB?\" prompt." msgstr "" -#: src/settings/index.md:52 +#: src/settings/index.md:44 msgid "" "\\[!TIP\\] Turn this on if you update Cadmus via USB often — you won't have " "to confirm the sharing dialog each time you plug in." msgstr "" -#: src/settings/index.md:63 +#: src/settings/index.md:52 msgid "`auto-suspend`" msgstr "" -#: src/settings/index.md:67 +#: src/settings/index.md:56 msgid "" "Number of minutes of inactivity after which the device will automatically go " "to sleep." msgstr "" -#: src/settings/index.md:69 src/settings/index.md:84 +#: src/settings/index.md:58 src/settings/index.md:70 msgid "Zero means never." msgstr "" -#: src/settings/index.md:78 +#: src/settings/index.md:64 msgid "`auto-power-off`" msgstr "" -#: src/settings/index.md:82 +#: src/settings/index.md:68 msgid "Delay in days after which a suspended device will power off." msgstr "" -#: src/settings/index.md:93 +#: src/settings/index.md:76 msgid "`button-scheme`" msgstr "" -#: src/settings/index.md:97 +#: src/settings/index.md:80 msgid "" "Defines how the back and forward buttons are mapped to page forward and page " "backward actions." msgstr "" -#: src/settings/index.md:103 +#: src/settings/index.md:82 +msgid "Possible values: `\"natural\"`, `\"inverted\"`." +msgstr "" + +#: src/settings/index.md:84 msgid "" "```toml\n" "button-scheme = \"natural\"\n" "```" msgstr "" -#: src/settings/index.md:108 +#: src/settings/index.md:88 +msgid "`locale`" +msgstr "" + +#: src/settings/index.md:92 +msgid "" +"The preferred language for the user interface, using BCP 47 format (e.g., " +"`\"en-US\"`, `\"de-DE\"`)." +msgstr "" + +#: src/settings/index.md:94 +msgid "This setting is optional. When not set, `en-GB` is used." +msgstr "" + +#: src/settings/index.md:96 +msgid "" +"```toml\n" +"locale = \"en-GB\"\n" +"```" +msgstr "" + +#: src/settings/index.md:100 msgid "Reader" msgstr "" -#: src/settings/index.md:110 +#: src/settings/index.md:102 msgid "Settings that control the reading experience." msgstr "" -#: src/settings/index.md:112 +#: src/settings/index.md:104 msgid "`reader.finished`" msgstr "" -#: src/settings/index.md:116 +#: src/settings/index.md:108 msgid "What to do when you finish reading a book." msgstr "" -#: src/settings/index.md:118 src/settings/index.md:177 +#: src/settings/index.md:110 src/settings/index.md:172 msgid "Possible values:" msgstr "" -#: src/settings/index.md:123 +#: src/settings/index.md:112 +msgid "`\"notify\"` (show a notification)" +msgstr "" + +#: src/settings/index.md:113 msgid "`\"close\"` (close the book and go back)" msgstr "" -#: src/settings/index.md:124 +#: src/settings/index.md:114 msgid "`\"go-to-next\"` (open the next book in the library)." msgstr "" -#: src/settings/index.md:126 +#: src/settings/index.md:116 msgid "" "```toml\n" "[reader]\n" @@ -846,6 +1328,22 @@ msgid "" "```" msgstr "" +#: src/settings/index.md:121 +msgid "`reader.dithered-kinds`" +msgstr "" + +#: src/settings/index.md:125 +msgid "File extensions rendered with dithering by default." +msgstr "" + +#: src/settings/index.md:127 +msgid "" +"```toml\n" +"[reader]\n" +"dithered-kinds = [\"cbz\", \"png\", \"jpg\", \"jpeg\", \"webp\"]\n" +"```" +msgstr "" + #: src/settings/index.md:132 msgid "Libraries" msgstr "" @@ -855,53 +1353,61 @@ msgid "" "Document library configuration. Each library has a name, path, and mode." msgstr "" -#: src/settings/index.md:148 +#: src/settings/index.md:145 msgid "`libraries.name`" msgstr "" -#: src/settings/index.md:152 +#: src/settings/index.md:149 msgid "Display name for the library." msgstr "" -#: src/settings/index.md:154 +#: src/settings/index.md:151 msgid "`libraries.path`" msgstr "" -#: src/settings/index.md:158 +#: src/settings/index.md:155 msgid "Directory path containing documents." msgstr "" -#: src/settings/index.md:160 +#: src/settings/index.md:157 msgid "`libraries.mode`" msgstr "" -#: src/settings/index.md:164 +#: src/settings/index.md:161 msgid "Library indexing mode." msgstr "" -#: src/settings/index.md:170 +#: src/settings/index.md:163 +msgid "Possible values: `\"database\"`, `\"filesystem\"`." +msgstr "" + +#: src/settings/index.md:165 msgid "`libraries.finished`" msgstr "" -#: src/settings/index.md:174 +#: src/settings/index.md:169 msgid "" "Override the `reader.finished` setting for this specific library. When set, " "this takes precedence over the global reader setting." msgstr "" -#: src/settings/index.md:182 +#: src/settings/index.md:174 +msgid "`\"notify\"`" +msgstr "" + +#: src/settings/index.md:175 msgid "`\"close\"`" msgstr "" -#: src/settings/index.md:183 +#: src/settings/index.md:176 msgid "`\"go-to-next\"`." msgstr "" -#: src/settings/index.md:184 +#: src/settings/index.md:177 msgid "Leave unset to inherit the global `reader.finished` setting." msgstr "" -#: src/settings/index.md:186 +#: src/settings/index.md:179 msgid "" "```toml\n" "[[libraries]]\n" @@ -911,47 +1417,141 @@ msgid "" "```" msgstr "" -#: src/settings/index.md:194 +#: src/settings/index.md:186 msgid "Intermissions" msgstr "" -#: src/settings/index.md:198 +#: src/settings/index.md:190 msgid "Defines the images displayed when entering an intermission state." msgstr "" -#: src/settings/index.md:210 +#: src/settings/index.md:192 +msgid "" +"```toml\n" +"[intermissions]\n" +"suspend = \"logo:\"\n" +"power-off = \"logo:\"\n" +"share = \"logo:\"\n" +"```" +msgstr "" + +#: src/settings/index.md:199 msgid "`intermissions.suspend`" msgstr "" -#: src/settings/index.md:214 +#: src/settings/index.md:203 msgid "Image displayed when the device enters sleep mode." msgstr "" -#: src/settings/index.md:220 +#: src/settings/index.md:205 +msgid "" +"Setting this to `\"calendar:\"` also enables the calendar refresh: every 5 " +"minutes, the device wakes, shows the calendar, and then goes back to sleep " +"automatically." +msgstr "" + +#: src/settings/index.md:209 +msgid "" +"Possible values: `\"logo:\"` (built-in logo), `\"cover:\"` (current book " +"cover), `\"calendar:\"` (built-in calendar), or a path to a custom image " +"file." +msgstr "" + +#: src/settings/index.md:211 msgid "`intermissions.power-off`" msgstr "" -#: src/settings/index.md:224 +#: src/settings/index.md:215 msgid "Image displayed when the device powers off." msgstr "" -#: src/settings/index.md:230 +#: src/settings/index.md:217 src/settings/index.md:225 +msgid "" +"Possible values: `\"logo:\"` (built-in logo), `\"cover:\"` (current book " +"cover), or a path to a custom image file." +msgstr "" + +#: src/settings/index.md:219 msgid "`intermissions.share`" msgstr "" -#: src/settings/index.md:234 +#: src/settings/index.md:223 msgid "Image displayed when entering USB sharing mode." msgstr "" +#: src/settings/index.md:227 +msgid "Import" +msgstr "" + +#: src/settings/index.md:229 +msgid "" +"These settings control how Cadmus imports documents from your device. They " +"are available in the **Settings → Import** menu." +msgstr "" + +#: src/settings/index.md:232 +msgid "" +"Import scanning happens automatically on startup using incremental file " +"checking — files are only re-scanned if their modification time or size has " +"changed since the last import." +msgstr "" + +#: src/settings/index.md:234 +msgid "" +"To trigger a full re-scan of all files regardless of cached values, use the " +"**Force Full Import** action button in the Import settings category." +msgstr "" + +#: src/settings/index.md:236 +msgid "`import.sync-metadata`" +msgstr "" + #: src/settings/index.md:240 +msgid "Re-extract metadata (title, author, etc.) whenever a document changes." +msgstr "" + +#: src/settings/index.md:247 +msgid "`import.metadata-kinds`" +msgstr "" + +#: src/settings/index.md:249 +msgid "File extensions of documents whose metadata is extracted during import." +msgstr "" + +#: src/settings/index.md:251 +msgid "" +"```toml\n" +"[import]\n" +"metadata-kinds = [\"epub\", \"pdf\", \"djvu\"]\n" +"```" +msgstr "" + +#: src/settings/index.md:256 +msgid "`import.allowed-kinds`" +msgstr "" + +#: src/settings/index.md:260 +msgid "File extensions of documents considered during the import process." +msgstr "" + +#: src/settings/index.md:262 +msgid "" +"```toml\n" +"[import]\n" +"allowed-kinds = [\"djvu\", \"xps\", \"fb2\", \"txt\", \"pdf\", \"oxps\", " +"\"cbz\", \"epub\"]\n" +"```" +msgstr "" + +#: src/settings/index.md:267 msgid "OTA" msgstr "" -#: src/settings/index.md:242 +#: src/settings/index.md:269 msgid "The OTA feature downloads builds from GitHub." msgstr "" -#: src/settings/index.md:244 +#: src/settings/index.md:271 msgid "" "Authentication for main branch and PR builds uses **GitHub device auth " "flow**. When you select a build that requires authentication, Cadmus will " @@ -960,53 +1560,73 @@ msgid "" "once you authorize." msgstr "" -#: src/settings/index.md:250 +#: src/settings/index.md:277 msgid "" "The token is saved to disk after the first authorization so you will not be " "prompted again on subsequent downloads." msgstr "" -#: src/settings/index.md:253 -msgid "Telemetry" +#: src/settings/index.md:280 +msgid "" +"For step-by-step instructions with screenshots, see the [OTA updates](../" +"installation/ota.md) guide." msgstr "" -#: src/settings/index.md:255 +#: src/settings/index.md:285 msgid "" -"Cadmus writes JSON logs to disk. When the build enables the `otel` feature, " -"it can also export logs to an OpenTelemetry endpoint." +"Cadmus writes JSON logs to disk. When the build enables the `tracing` " +"feature, it can also export logs to an OpenTelemetry endpoint." msgstr "" -#: src/settings/index.md:258 +#: src/settings/index.md:288 msgid "These settings are available in the **Settings → Telemetry** menu." msgstr "" -#: src/settings/index.md:260 +#: src/settings/index.md:290 msgid "" "\\[!IMPORTANT\\] Changes to these settings only take effect after restarting " "Cadmus. The application initializes telemetry on startup." msgstr "" -#: src/settings/index.md:264 +#: src/settings/index.md:294 msgid "`logging`" msgstr "" -#: src/settings/index.md:278 +#: src/settings/index.md:296 +msgid "" +"```toml\n" +"[logging]\n" +"enabled = true\n" +"level = \"info\"\n" +"max-files = 3\n" +"directory = \"logs\"\n" +"# otlp-endpoint = \"https://otel.example.com:4318\"\n" +"```" +msgstr "" + +#: src/settings/index.md:305 src/contributing/telemetry/logging.md:50 msgid "`logging.enabled`" msgstr "" -#: src/settings/index.md:282 +#: src/settings/index.md:309 msgid "Enable or disable structured JSON logging." msgstr "" -#: src/settings/index.md:292 +#: src/settings/index.md:316 src/contributing/telemetry/logging.md:51 msgid "`logging.level`" msgstr "" -#: src/settings/index.md:296 +#: src/settings/index.md:320 msgid "Minimum log level to record." msgstr "" -#: src/settings/index.md:302 +#: src/settings/index.md:322 +msgid "" +"Possible values: `\"trace\"`, `\"debug\"`, `\"info\"`, `\"warn\"`, " +"`\"error\"`." +msgstr "" + +#: src/settings/index.md:324 msgid "" "```toml\n" "[logging]\n" @@ -1014,78 +1634,129 @@ msgid "" "```" msgstr "" -#: src/settings/index.md:308 +#: src/settings/index.md:329 src/contributing/telemetry/logging.md:52 msgid "`logging.max-files`" msgstr "" -#: src/settings/index.md:310 +#: src/settings/index.md:331 msgid "" "Number of log files to keep. Only the most recent N files are kept — older " "ones are deleted automatically when Cadmus starts." msgstr "" -#: src/settings/index.md:313 src/settings/index.md:368 +#: src/settings/index.md:334 src/settings/index.md:407 msgid "Default: `3`" msgstr "" -#: src/settings/index.md:314 +#: src/settings/index.md:335 msgid "Set to `0` to keep all log files." msgstr "" -#: src/settings/index.md:324 +#: src/settings/index.md:342 src/contributing/telemetry/logging.md:54 msgid "`logging.otlp-endpoint`" msgstr "" -#: src/settings/index.md:326 -msgid "✏️ (only when the `otel` feature is enabled)" +#: src/settings/index.md:344 +msgid "✏️ (only when the `tracing` feature is enabled)" msgstr "" -#: src/settings/index.md:328 +#: src/settings/index.md:346 msgid "" "Optional OTLP endpoint for exporting logs to an OpenTelemetry collector." msgstr "" -#: src/settings/index.md:338 +#: src/settings/index.md:348 +msgid "" +"```toml\n" +"[logging]\n" +"otlp-endpoint = \"https://otel.example.com:4318\"\n" +"```" +msgstr "" + +#: src/settings/index.md:353 src/settings/index.md:370 msgid "Environment override:" msgstr "" -#: src/settings/index.md:344 +#: src/settings/index.md:355 +msgid "" +"`OTEL_EXPORTER_OTLP_ENDPOINT` takes precedence over `logging.otlp-endpoint`." +msgstr "" + +#: src/settings/index.md:357 src/contributing/telemetry/profiling.md:32 +msgid "`logging.pyroscope-endpoint`" +msgstr "" + +#: src/settings/index.md:359 +msgid "✏️ (only when the `profiling` feature is enabled)" +msgstr "" + +#: src/settings/index.md:361 +msgid "" +"Optional Pyroscope server URL for continuous profiling. When set, Cadmus " +"starts both a heap profiling agent (via jemalloc) and a CPU profiling agent " +"(via pprof) that push profiles to this endpoint." +msgstr "" + +#: src/settings/index.md:365 +msgid "" +"```toml\n" +"[logging]\n" +"pyroscope-endpoint = \"http://localhost:4040\"\n" +"```" +msgstr "" + +#: src/settings/index.md:372 +msgid "" +"`PYROSCOPE_SERVER_URL` takes precedence over `logging.pyroscope-endpoint`." +msgstr "" + +#: src/settings/index.md:374 msgid "`logging.enable-kern-log`" msgstr "" -#: src/settings/index.md:346 +#: src/settings/index.md:376 src/settings/index.md:388 msgid "🧪 📱 ✏️" msgstr "" -#: src/settings/index.md:348 +#: src/settings/index.md:378 msgid "" "Captures kernel logs via `logread -F` and forwards them to structured " "logging with the target `cadmus_core::logging:kern`." msgstr "" -#: src/settings/index.md:359 +#: src/settings/index.md:386 +msgid "`logging.enable-dbus-log`" +msgstr "" + +#: src/settings/index.md:390 +msgid "" +"Captures D-Bus signals via the built-in zbus-based DbusMonitorTask and " +"forwards them to structured logging." +msgstr "" + +#: src/settings/index.md:398 msgid "Settings Retention" msgstr "" -#: src/settings/index.md:361 +#: src/settings/index.md:400 msgid "" "Cadmus stores each version's settings in a separate file in the `Settings/` " "directory (for example, `Settings-v1.2.3.toml`). This ensures backward and " "forward compatibility when you upgrade." msgstr "" -#: src/settings/index.md:364 +#: src/settings/index.md:403 msgid "`settings-retention`" msgstr "" -#: src/settings/index.md:366 +#: src/settings/index.md:405 msgid "" "Number of recent version settings files to keep. Only the most recent N " "version files are kept. When a new version is saved, older versions beyond " "this limit are deleted automatically." msgstr "" -#: src/settings/index.md:369 +#: src/settings/index.md:408 msgid "Set to `0` to keep all version files" msgstr "" @@ -1099,6 +1770,14 @@ msgstr "" msgid "[File Chooser](filechooser.md)" msgstr "" +#: src/ui/index.md:7 +msgid "[Keyboard](keyboard.md)" +msgstr "" + +#: src/ui/index.md:8 +msgid "[Settings](settings/index.md)" +msgstr "" + #: src/ui/filechooser.md:3 msgid "" "The file chooser helps you pick files or folders. It appears when you need " @@ -1168,1570 +1847,4271 @@ msgstr "" msgid "The title will indicate the mode." msgstr "" -#: src/troubleshooting/index.md:3 -msgid "Logs" +#: src/ui/keyboard.md:3 +msgid "" +"The on-screen keyboard lets you type text for searches, dictionary lookups, " +"and other input fields." msgstr "" -#: src/troubleshooting/index.md:5 -msgid "" -"When something isn't working right, logs will help with figuring out what " -"went wrong. If you're reporting an issue, sharing your logs makes it much " -"easier to debug." +#: src/ui/keyboard.md:6 +msgid "Basics" msgstr "" -#: src/troubleshooting/index.md:9 -msgid "Where to find Cadmus logs" +#: src/ui/keyboard.md:8 +msgid "**Lock modifier keys** — Tap _ALT_ or _SHIFT_ twice to lock them on." msgstr "" -#: src/troubleshooting/index.md:11 +#: src/ui/keyboard.md:9 msgid "" -"Cadmus saves logs in a `logs` folder. Here's where to find it on each " -"platform:" +"**Combine key (CMB)** — Press _CMB_ followed by two keys to type accented or " +"special characters (see [Combination Sequences](#combination-sequences) " +"below). For example, `CMB o e` produces _œ_." msgstr "" -#: src/troubleshooting/index.md:13 -msgid "Platform" +#: src/ui/keyboard.md:12 +msgid "" +"**Delete / motion keys** — Tap and hold to act on whole words instead of " +"individual characters." msgstr "" -#: src/troubleshooting/index.md:13 -msgid "Stable build" +#: src/ui/keyboard.md:14 +msgid "**Keyboard layouts** — Tap and hold the space bar to switch layouts." msgstr "" -#: src/troubleshooting/index.md:13 -msgid "Test build" +#: src/ui/keyboard.md:16 +msgid "Inputting Accented Characters" msgstr "" -#: src/troubleshooting/index.md:15 -msgid "Kobo" +#: src/ui/keyboard.md:18 +msgid "" +"Cadmus supports these through **combination sequences** using the _CMB_ key. " +"See [Combination Sequences](#combination-sequences)." msgstr "" -#: src/troubleshooting/index.md:15 -msgid "`/mnt/onboard/.adds/cadmus/logs`" +#: src/ui/keyboard.md:21 +msgid "How to type an accented character" msgstr "" -#: src/troubleshooting/index.md:15 -msgid "`/mnt/onboard/.adds/cadmus-tst/logs`" +#: src/ui/keyboard.md:23 +msgid "Press the **CMB** key." msgstr "" -#: src/troubleshooting/index.md:17 -msgid "" -"Each time you start Cadmus, it creates a new log file with a unique ID. By " -"default, only the 3 most recent log files are kept — older ones are deleted " -"automatically. You can change this with the [`logging.max-files`](../" -"settings/index.md#loggingmax-files) setting." +#: src/ui/keyboard.md:24 +msgid "Press the **base letter** (e.g. `e`)." msgstr "" -#: src/troubleshooting/index.md:22 -msgid "The log files look like this:" +#: src/ui/keyboard.md:25 +msgid "Press the **accent modifier** (e.g. `'` for acute)." msgstr "" -#: src/troubleshooting/index.md:30 -msgid "Finding your run ID" +#: src/ui/keyboard.md:27 +msgid "Example: `CMB e '` produces **é**." msgstr "" -#: src/troubleshooting/index.md:32 -msgid "" -"Every time Cadmus starts, it prints a run ID to help you identify which log " -"file belongs to that session. You can find this in:" +#: src/ui/keyboard.md:29 +msgid "Combination Sequences" msgstr "" -#: src/troubleshooting/index.md:36 +#: src/ui/keyboard.md:31 msgid "" -"**info.log** - The startup log in the Cadmus folder. Look for the line that " -"says `Cadmus run started with ID:` followed by a string of letters and " -"numbers." +"Press _CMB_ followed by the two keys listed below to produce the " +"corresponding character." msgstr "" -#: src/troubleshooting/index.md:39 -msgid "For example:" +#: src/ui/keyboard.md:34 +msgid "Keys" msgstr "" -#: src/troubleshooting/index.md:45 -msgid "" -"Copy only the UUID part — the string of letters and numbers between `ID:` " -"and the `(version` text." +#: src/ui/keyboard.md:34 +msgid "Result" msgstr "" -#: src/troubleshooting/index.md:48 -msgid "" -"**Console output** - If you're running Cadmus from a terminal, the same run " -"ID is printed when it starts." +#: src/ui/keyboard.md:36 +msgid "`a '`" msgstr "" -#: src/troubleshooting/index.md:51 -msgid "Kernel logs" +#: src/ui/keyboard.md:36 +msgid "á" msgstr "" -#: src/troubleshooting/index.md:53 -msgid "" -"Kernel logs can be useful to debug lower level system issues, for example a " -"kernel panic, which triggers a device reboot." +#: src/ui/keyboard.md:36 +msgid "`A '`" msgstr "" -#: src/troubleshooting/index.md:56 -msgid "" -"Kernel logs are only available in [test builds](../installation/test-" -"builds.md). If you're using a test build and want to include kernel logs:" +#: src/ui/keyboard.md:36 +msgid "Á" msgstr "" -#: src/troubleshooting/index.md:59 -msgid "Open **Main Menu → Settings**" +#: src/ui/keyboard.md:36 +msgid "```a `` ```" msgstr "" -#: src/troubleshooting/index.md:60 -msgid "Go to `Telemetry`" +#: src/ui/keyboard.md:36 +msgid "à" msgstr "" -#: src/troubleshooting/index.md:61 -msgid "Enable kernel logs" +#: src/ui/keyboard.md:36 +msgid "```A `` ```" msgstr "" -#: src/troubleshooting/index.md:62 -msgid "Restart Cadmus" +#: src/ui/keyboard.md:36 +msgid "À" msgstr "" -#: src/troubleshooting/index.md:64 -msgid "" -"Kernel logs will then be saved in the same log file as your Cadmus logs." +#: src/ui/keyboard.md:37 +msgid "`e '`" msgstr "" -#: src/troubleshooting/index.md:66 -msgid "" -"\\[!NOTE\\] Kernel logs will use more disk space, so don't forget to turn it " -"back off." +#: src/ui/keyboard.md:37 +msgid "é" msgstr "" -#: src/contributing/devenv-setup.md:1 -msgid "Development Environment Setup" +#: src/ui/keyboard.md:37 +msgid "`E '`" msgstr "" -#: src/contributing/devenv-setup.md:3 -msgid "" -"Cadmus uses [devenv](https://devenv.sh/) with Nix to provide a reproducible " -"development environment. This guide covers setup on both Linux and macOS." +#: src/ui/keyboard.md:37 +msgid "É" msgstr "" -#: src/contributing/devenv-setup.md:6 src/contributing/translations.md:6 -msgid "Prerequisites" +#: src/ui/keyboard.md:37 +msgid "```e `` ```" msgstr "" -#: src/contributing/devenv-setup.md:8 -msgid "" -"Install Nix with flakes enabled. The easiest way is using the [Determinate " -"Nix Installer](https://github.com/DeterminateSystems/nix-installer)." +#: src/ui/keyboard.md:37 +msgid "è" msgstr "" -#: src/contributing/devenv-setup.md:9 -msgid "Install [devenv](https://devenv.sh/getting-started)." +#: src/ui/keyboard.md:37 +msgid "```E `` ```" msgstr "" -#: src/contributing/devenv-setup.md:11 -msgid "Quick Start" +#: src/ui/keyboard.md:37 +msgid "È" msgstr "" -#: src/contributing/devenv-setup.md:13 -msgid "Clone the repository and enter the devenv shell:" +#: src/ui/keyboard.md:38 +msgid "`i '`" msgstr "" -#: src/contributing/devenv-setup.md:23 -msgid "Run the one-time setup to build native dependencies:" +#: src/ui/keyboard.md:38 +msgid "í" msgstr "" -#: src/contributing/devenv-setup.md:31 -msgid "Run the emulator:" +#: src/ui/keyboard.md:38 +msgid "`I '`" msgstr "" -#: src/contributing/devenv-setup.md:39 -msgid "Available Commands" +#: src/ui/keyboard.md:38 +msgid "Í" msgstr "" -#: src/contributing/devenv-setup.md:41 -msgid "Once inside the devenv shell, these commands are available:" +#: src/ui/keyboard.md:38 +msgid "```i `` ```" msgstr "" -#: src/contributing/devenv-setup.md:43 -msgid "Command" +#: src/ui/keyboard.md:38 +msgid "ì" msgstr "" -#: src/contributing/devenv-setup.md:45 -msgid "`cargo xtask setup-native`" +#: src/ui/keyboard.md:38 +msgid "```I `` ```" msgstr "" -#: src/contributing/devenv-setup.md:45 -msgid "Build MuPDF for native development (run once)" +#: src/ui/keyboard.md:38 +msgid "Ì" msgstr "" -#: src/contributing/devenv-setup.md:46 -msgid "`cargo xtask test`" +#: src/ui/keyboard.md:39 +msgid "`o '`" msgstr "" -#: src/contributing/devenv-setup.md:46 -msgid "Run the test suite across the feature matrix" +#: src/ui/keyboard.md:39 +msgid "ó" msgstr "" -#: src/contributing/devenv-setup.md:47 -msgid "`cargo xtask run-emulator`" +#: src/ui/keyboard.md:39 +msgid "`O '`" msgstr "" -#: src/contributing/devenv-setup.md:47 -msgid "Run the emulator" +#: src/ui/keyboard.md:39 +msgid "Ó" msgstr "" -#: src/contributing/devenv-setup.md:48 -msgid "`cargo xtask build-kobo`" +#: src/ui/keyboard.md:39 +msgid "```o `` ```" msgstr "" -#: src/contributing/devenv-setup.md:48 -msgid "Cross-compile for Kobo device (Linux only)" +#: src/ui/keyboard.md:39 +msgid "ò" msgstr "" -#: src/contributing/devenv-setup.md:49 -msgid "`cargo xtask dist`" +#: src/ui/keyboard.md:39 +msgid "```O `` ```" msgstr "" -#: src/contributing/devenv-setup.md:49 -msgid "Assemble the Kobo distribution directory" +#: src/ui/keyboard.md:39 +msgid "Ò" msgstr "" -#: src/contributing/devenv-setup.md:50 -msgid "`cargo xtask bundle`" +#: src/ui/keyboard.md:40 +msgid "`u '`" msgstr "" -#: src/contributing/devenv-setup.md:50 -msgid "Package KoboRoot.tgz for installation" +#: src/ui/keyboard.md:40 +msgid "ú" msgstr "" -#: src/contributing/devenv-setup.md:51 -msgid "`cadmus-dev-otel`" +#: src/ui/keyboard.md:40 +msgid "`U '`" msgstr "" -#: src/contributing/devenv-setup.md:51 -msgid "Run emulator with OpenTelemetry instrumentation" +#: src/ui/keyboard.md:40 +msgid "Ú" msgstr "" -#: src/contributing/devenv-setup.md:52 -msgid "`devenv up`" +#: src/ui/keyboard.md:40 +msgid "```u `` ```" msgstr "" -#: src/contributing/devenv-setup.md:52 -msgid "Start observability stack (Grafana, Tempo, Loki)" +#: src/ui/keyboard.md:40 +msgid "ù" msgstr "" -#: src/contributing/devenv-setup.md:53 -msgid "`cargo xtask docs`" +#: src/ui/keyboard.md:40 +msgid "```U `` ```" msgstr "" -#: src/contributing/devenv-setup.md:53 -msgid "Build documentation portal (mdBook + Cargo docs)" +#: src/ui/keyboard.md:40 +msgid "Ù" msgstr "" -#: src/contributing/devenv-setup.md:54 -msgid "`cadmus-docs-serve`" +#: src/ui/keyboard.md:41 +msgid "`y '`" msgstr "" -#: src/contributing/devenv-setup.md:54 -msgid "Serve documentation portal locally on port 1111" +#: src/ui/keyboard.md:41 +msgid "ý" msgstr "" -#: src/contributing/devenv-setup.md:56 -msgid "" -"Run `cargo xtask --help` to see all available subcommands, or `cargo xtask " -" --help` for options on a specific command." +#: src/ui/keyboard.md:41 +msgid "`Y '`" msgstr "" -#: src/contributing/devenv-setup.md:59 -msgid "" -"Or have a look at the rustdocs for `xtask` here." +#: src/ui/keyboard.md:41 +msgid "Ý" msgstr "" -#: src/contributing/devenv-setup.md:61 -msgid "Tasks" +#: src/ui/keyboard.md:41 +msgid "`u \"`" msgstr "" -#: src/contributing/devenv-setup.md:63 -msgid "" -"The devenv environment uses [tasks](https://devenv.sh/tasks/) to manage " -"build dependencies. Tasks are defined in `devenv.nix` and can be run with " -"`devenv tasks run `." +#: src/ui/keyboard.md:41 +msgid "ű" msgstr "" -#: src/contributing/devenv-setup.md:66 -msgid "Available Tasks" +#: src/ui/keyboard.md:41 +msgid "`U \"`" msgstr "" -#: src/contributing/devenv-setup.md:68 -msgid "Task" +#: src/ui/keyboard.md:41 +msgid "Ű" msgstr "" -#: src/contributing/devenv-setup.md:68 -msgid "Dependencies" +#: src/ui/keyboard.md:42 +msgid "`a ^`" msgstr "" -#: src/contributing/devenv-setup.md:70 src/contributing/devenv-setup.md:72 -msgid "`docs:build`" +#: src/ui/keyboard.md:42 +msgid "â" msgstr "" -#: src/contributing/devenv-setup.md:70 -msgid "Build documentation EPUB (only rebuilds if files changed)" +#: src/ui/keyboard.md:42 +msgid "`A ^`" msgstr "" -#: src/contributing/devenv-setup.md:70 src/contributing/devenv-setup.md:71 -msgid "None" +#: src/ui/keyboard.md:42 +msgid "Â" msgstr "" -#: src/contributing/devenv-setup.md:71 -msgid "`deps:native`" +#: src/ui/keyboard.md:42 +msgid "`o \"`" msgstr "" -#: src/contributing/devenv-setup.md:71 -msgid "Build MuPDF and wrapper for native development" +#: src/ui/keyboard.md:42 +msgid "ő" msgstr "" -#: src/contributing/devenv-setup.md:72 -msgid "`build:kobo`" +#: src/ui/keyboard.md:42 +msgid "`O \"`" msgstr "" -#: src/contributing/devenv-setup.md:72 -msgid "Build for Kobo device (Linux only)" +#: src/ui/keyboard.md:42 +msgid "Ő" msgstr "" -#: src/contributing/devenv-setup.md:74 -msgid "All tasks delegate to `cargo xtask` under the hood." +#: src/ui/keyboard.md:43 +msgid "`e ^`" msgstr "" -#: src/contributing/devenv-setup.md:76 -msgid "How Tasks Work" +#: src/ui/keyboard.md:43 +msgid "ê" msgstr "" -#: src/contributing/devenv-setup.md:78 -msgid "" -"Tasks with dependencies automatically run their dependencies first. For " -"example:" +#: src/ui/keyboard.md:43 +msgid "`E ^`" msgstr "" -#: src/contributing/devenv-setup.md:87 -msgid "" -"The `docs:build` task uses `execIfModified` to only rebuild when " -"documentation files have actually changed." +#: src/ui/keyboard.md:43 +msgid "Ê" msgstr "" -#: src/contributing/devenv-setup.md:89 -msgid "Documentation Portal" +#: src/ui/keyboard.md:43 +msgid "`a :`" msgstr "" -#: src/contributing/devenv-setup.md:91 -msgid "" -"Cadmus provides a unified documentation portal that combines user guides, " -"API reference, and contribution guides in one place." +#: src/ui/keyboard.md:43 +msgid "ä" msgstr "" -#: src/contributing/devenv-setup.md:94 -msgid "Building and Serving Locally" +#: src/ui/keyboard.md:43 +msgid "`A :`" msgstr "" -#: src/contributing/devenv-setup.md:96 -msgid "To build the documentation portal:" +#: src/ui/keyboard.md:43 +msgid "Ä" msgstr "" -#: src/contributing/devenv-setup.md:104 -msgid "This runs the full build pipeline:" +#: src/ui/keyboard.md:44 +msgid "`i ^`" msgstr "" -#: src/contributing/devenv-setup.md:106 -msgid "Builds the mdBook user guide (`docs/book/html/`)" +#: src/ui/keyboard.md:44 +msgid "î" msgstr "" -#: src/contributing/devenv-setup.md:107 -msgid "Generates Rust API documentation (`target/doc/`)" +#: src/ui/keyboard.md:44 +msgid "`I ^`" msgstr "" -#: src/contributing/devenv-setup.md:108 -msgid "Builds the Zola landing page and integrates all documentation" +#: src/ui/keyboard.md:44 +msgid "Î" msgstr "" -#: src/contributing/devenv-setup.md:110 -msgid "To serve the portal locally with live reload:" +#: src/ui/keyboard.md:44 +msgid "`e :`" msgstr "" -#: src/contributing/devenv-setup.md:118 -msgid "" -"The portal will be available at with automatic " -"rebuilds when you change documentation files or Rust code." +#: src/ui/keyboard.md:44 +msgid "ë" msgstr "" -#: src/contributing/devenv-setup.md:121 -msgid "Documentation Structure" +#: src/ui/keyboard.md:44 +msgid "`E :`" msgstr "" -#: src/contributing/devenv-setup.md:123 -msgid "The portal provides three integrated sections:" +#: src/ui/keyboard.md:44 +msgid "Ë" msgstr "" -#: src/contributing/devenv-setup.md:125 -msgid "**Landing Page** (`/`) - Overview and feature highlights" +#: src/ui/keyboard.md:45 +msgid "`o ^`" msgstr "" -#: src/contributing/devenv-setup.md:126 -msgid "**User Guide** (`/guide/`) - User-facing documentation from mdBook" +#: src/ui/keyboard.md:45 +msgid "ô" msgstr "" -#: src/contributing/devenv-setup.md:127 -msgid "**API Reference** (`/api/`) - Auto-generated Rust API documentation" +#: src/ui/keyboard.md:45 +msgid "`O ^`" msgstr "" -#: src/contributing/devenv-setup.md:129 -msgid "" -"All three sections are deployed as a single artifact to GitHub Pages at " -"." +#: src/ui/keyboard.md:45 +msgid "Ô" msgstr "" -#: src/contributing/devenv-setup.md:132 -msgid "Continuous Integration" +#: src/ui/keyboard.md:45 +msgid "`i :`" msgstr "" -#: src/contributing/devenv-setup.md:134 -msgid "" -"Documentation is automatically built and validated on every pull request and " -"deployed on push to `main` or `master`. The CI pipeline checks:" +#: src/ui/keyboard.md:45 +msgid "ï" msgstr "" -#: src/contributing/devenv-setup.md:137 -msgid "mdBook documentation compiles" +#: src/ui/keyboard.md:45 +msgid "`I :`" msgstr "" -#: src/contributing/devenv-setup.md:138 -msgid "Rust code documentation is valid" +#: src/ui/keyboard.md:45 +msgid "Ï" msgstr "" -#: src/contributing/devenv-setup.md:139 -msgid "Zola landing page builds successfully" +#: src/ui/keyboard.md:46 +msgid "`u ^`" msgstr "" -#: src/contributing/devenv-setup.md:141 -msgid "Running Tests" +#: src/ui/keyboard.md:46 +msgid "û" msgstr "" -#: src/contributing/devenv-setup.md:143 -msgid "" -"Tests require the `TEST_ROOT_DIR` environment variable to be set. The " -"easiest way to run the full test matrix is:" +#: src/ui/keyboard.md:46 +msgid "`U ^`" msgstr "" -#: src/contributing/devenv-setup.md:152 -msgid "" -"This sets `TEST_ROOT_DIR` automatically and runs tests across all feature " -"combinations. To run a single feature combination:" +#: src/ui/keyboard.md:46 +msgid "Û" msgstr "" -#: src/contributing/devenv-setup.md:161 -msgid "Or to run tests manually without xtask:" +#: src/ui/keyboard.md:46 +msgid "`o :`" msgstr "" -#: src/contributing/devenv-setup.md:169 -msgid "" -"`TEST_ROOT_DIR` is automatically configured in CI but must be set manually " -"when running `cargo test` directly." +#: src/ui/keyboard.md:46 +msgid "ö" msgstr "" -#: src/contributing/devenv-setup.md:172 -msgid "Platform Support" +#: src/ui/keyboard.md:46 +msgid "`O :`" msgstr "" -#: src/contributing/devenv-setup.md:174 -msgid "Linux (Full Support)" +#: src/ui/keyboard.md:46 +msgid "Ö" msgstr "" -#: src/contributing/devenv-setup.md:176 -msgid "Linux provides full development capabilities including:" +#: src/ui/keyboard.md:47 +msgid "`w ^`" msgstr "" -#: src/contributing/devenv-setup.md:178 -msgid "Native development (emulator, tests)" +#: src/ui/keyboard.md:47 +msgid "ŵ" msgstr "" -#: src/contributing/devenv-setup.md:179 -msgid "Cross-compilation for Kobo devices using the Linaro ARM toolchain" +#: src/ui/keyboard.md:47 +msgid "`W ^`" msgstr "" -#: src/contributing/devenv-setup.md:180 -msgid "Git hooks (actionlint, shellcheck, shfmt, markdownlint, prettier)" +#: src/ui/keyboard.md:47 +msgid "Ŵ" msgstr "" -#: src/contributing/devenv-setup.md:182 -msgid "" -"The Linaro toolchain is automatically added to `PATH` and provides `arm-" -"linux-gnueabihf-*` commands." +#: src/ui/keyboard.md:47 +msgid "`u :`" msgstr "" -#: src/contributing/devenv-setup.md:184 -msgid "macOS (Native Development Only)" +#: src/ui/keyboard.md:47 +msgid "ü" msgstr "" -#: src/contributing/devenv-setup.md:186 -msgid "macOS supports native development but has some limitations:" +#: src/ui/keyboard.md:47 +msgid "`U :`" msgstr "" -#: src/contributing/devenv-setup.md:188 -msgid "Feature" +#: src/ui/keyboard.md:47 +msgid "Ü" msgstr "" -#: src/contributing/devenv-setup.md:188 -msgid "Status" +#: src/ui/keyboard.md:48 +msgid "`y ^`" msgstr "" -#: src/contributing/devenv-setup.md:188 -msgid "Notes" +#: src/ui/keyboard.md:48 +msgid "ŷ" msgstr "" -#: src/contributing/devenv-setup.md:190 -msgid "Native builds" +#: src/ui/keyboard.md:48 +msgid "`Y ^`" msgstr "" -#: src/contributing/devenv-setup.md:190 -msgid "Supported" +#: src/ui/keyboard.md:48 +msgid "Ŷ" msgstr "" -#: src/contributing/devenv-setup.md:190 -msgid "Emulator and tests work" +#: src/ui/keyboard.md:48 +msgid "`y :`" msgstr "" -#: src/contributing/devenv-setup.md:191 -msgid "Cross-compilation" +#: src/ui/keyboard.md:48 +msgid "ÿ" msgstr "" -#: src/contributing/devenv-setup.md:191 -msgid "Not supported" +#: src/ui/keyboard.md:49 +msgid "`a ~`" msgstr "" -#: src/contributing/devenv-setup.md:191 -msgid "Linaro toolchain is Linux-only" +#: src/ui/keyboard.md:49 +msgid "ã" msgstr "" -#: src/contributing/devenv-setup.md:193 -msgid "macOS-Specific Notes" +#: src/ui/keyboard.md:49 +msgid "`A ~`" msgstr "" -#: src/contributing/devenv-setup.md:195 -msgid "" -"**Cross-compilation for Kobo**: The Linaro ARM cross-compilation toolchain " -"consists of x86_64 Linux ELF binaries that cannot run on macOS. To build for " -"Kobo devices on macOS, use Docker with a Linux container or a Linux VM." +#: src/ui/keyboard.md:49 +msgid "Ã" msgstr "" -#: src/contributing/devenv-setup.md:199 -msgid "" -"**MuPDF build**: On macOS, the native setup script manually gathers pkg-" -"config CFLAGS for system libraries because MuPDF's build system doesn't " -"properly detect them on Darwin." +#: src/ui/keyboard.md:49 +msgid "`c ,`" msgstr "" -#: src/contributing/devenv-setup.md:202 -msgid "Observability Stack" +#: src/ui/keyboard.md:49 +msgid "ç" msgstr "" -#: src/contributing/devenv-setup.md:204 -msgid "The devenv includes a full observability stack for development:" +#: src/ui/keyboard.md:49 +msgid "`C ,`" msgstr "" -#: src/contributing/devenv-setup.md:216 -msgid "Services available after `devenv up`:" +#: src/ui/keyboard.md:49 +msgid "Ç" msgstr "" -#: src/contributing/devenv-setup.md:218 -msgid "Service" +#: src/ui/keyboard.md:50 +msgid "`o ~`" msgstr "" -#: src/contributing/devenv-setup.md:218 -msgid "URL" +#: src/ui/keyboard.md:50 +msgid "õ" msgstr "" -#: src/contributing/devenv-setup.md:218 -#: src/contributing/library-database.md:152 -msgid "Purpose" +#: src/ui/keyboard.md:50 +msgid "`O ~`" msgstr "" -#: src/contributing/devenv-setup.md:220 -msgid "Grafana" +#: src/ui/keyboard.md:50 +msgid "Õ" msgstr "" -#: src/contributing/devenv-setup.md:220 -msgid "" +#: src/ui/keyboard.md:50 +msgid "`c '`" msgstr "" -#: src/contributing/devenv-setup.md:220 -msgid "Dashboards and exploration" +#: src/ui/keyboard.md:50 +msgid "ć" msgstr "" -#: src/contributing/devenv-setup.md:221 -msgid "Tempo" +#: src/ui/keyboard.md:50 +msgid "`C '`" msgstr "" -#: src/contributing/devenv-setup.md:221 -msgid "" +#: src/ui/keyboard.md:50 +msgid "Ć" msgstr "" -#: src/contributing/devenv-setup.md:221 -msgid "Distributed tracing" +#: src/ui/keyboard.md:51 +msgid "`n ~`" msgstr "" -#: src/contributing/devenv-setup.md:222 -msgid "Loki" +#: src/ui/keyboard.md:51 +msgid "ñ" msgstr "" -#: src/contributing/devenv-setup.md:222 -msgid "" +#: src/ui/keyboard.md:51 +msgid "`N ~`" msgstr "" -#: src/contributing/devenv-setup.md:222 -msgid "Log aggregation" +#: src/ui/keyboard.md:51 +msgid "Ñ" msgstr "" -#: src/contributing/devenv-setup.md:223 -msgid "Prometheus" +#: src/ui/keyboard.md:51 +msgid "`z '`" msgstr "" -#: src/contributing/devenv-setup.md:223 -msgid "" +#: src/ui/keyboard.md:51 +msgid "ź" msgstr "" -#: src/contributing/devenv-setup.md:223 -msgid "Metrics" +#: src/ui/keyboard.md:51 +msgid "`Z '`" msgstr "" -#: src/contributing/devenv-setup.md:224 -msgid "OTLP Collector" +#: src/ui/keyboard.md:51 +msgid "Ź" msgstr "" -#: src/contributing/devenv-setup.md:224 -msgid "" +#: src/ui/keyboard.md:52 +msgid "`a ;`" msgstr "" -#: src/contributing/devenv-setup.md:224 -msgid "Telemetry ingestion" +#: src/ui/keyboard.md:52 +msgid "ą" msgstr "" -#: src/contributing/devenv-setup.md:226 -msgid "" -"For more details on telemetry, see [OpenTelemetry Integration](telemetry.md)." +#: src/ui/keyboard.md:52 +msgid "`A ;`" msgstr "" -#: src/contributing/devenv-setup.md:230 -msgid "Shell takes a long time to start" +#: src/ui/keyboard.md:52 +msgid "Ą" msgstr "" -#: src/contributing/devenv-setup.md:232 -msgid "" -"The first `devenv shell` invocation downloads and builds dependencies, which " -"can take several minutes. Subsequent invocations are cached and should be " -"fast." +#: src/ui/keyboard.md:52 +msgid "`s '`" msgstr "" -#: src/contributing/devenv-setup.md:235 -msgid "poedit fails to build (`boost::detail::run_it` error)" +#: src/ui/keyboard.md:52 +msgid "ś" msgstr "" -#: src/contributing/devenv-setup.md:237 -msgid "" -"poedit 3.6.2 fails to compile against boost 1.87.0 due to a bug in " -"`boost::detail::run_it`'s move-assignment operator. The `devenv.nix` pins " -"poedit to build against `boost186` (1.86.0) as a workaround until nixpkgs " -"upgrades to a fixed boost release." +#: src/ui/keyboard.md:52 +msgid "`S '`" msgstr "" -#: src/contributing/devenv-setup.md:242 -msgid "Tests fail with \"TEST_ROOT_DIR must be set\"" +#: src/ui/keyboard.md:52 +msgid "Ś" msgstr "" -#: src/contributing/devenv-setup.md:244 -msgid "Set the environment variable before running tests:" +#: src/ui/keyboard.md:53 +msgid "`e ;`" msgstr "" -#: src/contributing/devenv-setup.md:252 -msgid "Local Configuration" +#: src/ui/keyboard.md:53 +msgid "ę" msgstr "" -#: src/contributing/devenv-setup.md:254 -msgid "" -"Create `devenv.local.nix` to override settings without modifying the tracked " -"configuration:" +#: src/ui/keyboard.md:53 +msgid "`E ;`" msgstr "" -#: src/contributing/devenv-setup.md:269 -msgid "This file is gitignored and won't affect other contributors." +#: src/ui/keyboard.md:53 +msgid "Ę" msgstr "" -#: src/contributing/event-system.md:3 +#: src/ui/keyboard.md:53 +msgid "`n '`" +msgstr "" + +#: src/ui/keyboard.md:53 +msgid "ń" +msgstr "" + +#: src/ui/keyboard.md:53 +msgid "`N '`" +msgstr "" + +#: src/ui/keyboard.md:53 +msgid "Ń" +msgstr "" + +#: src/ui/keyboard.md:54 +msgid "`z .`" +msgstr "" + +#: src/ui/keyboard.md:54 +msgid "ż" +msgstr "" + +#: src/ui/keyboard.md:54 +msgid "`Z .`" +msgstr "" + +#: src/ui/keyboard.md:54 +msgid "Ż" +msgstr "" + +#: src/ui/keyboard.md:54 +msgid "`t h`" +msgstr "" + +#: src/ui/keyboard.md:54 +msgid "þ" +msgstr "" + +#: src/ui/keyboard.md:54 +msgid "`T h`" +msgstr "" + +#: src/ui/keyboard.md:54 +msgid "Þ" +msgstr "" + +#: src/ui/keyboard.md:55 +msgid "`a o`" +msgstr "" + +#: src/ui/keyboard.md:55 +msgid "å" +msgstr "" + +#: src/ui/keyboard.md:55 +msgid "`A o`" +msgstr "" + +#: src/ui/keyboard.md:55 +msgid "Å" +msgstr "" + +#: src/ui/keyboard.md:55 +msgid "`l /`" +msgstr "" + +#: src/ui/keyboard.md:55 +msgid "ł" +msgstr "" + +#: src/ui/keyboard.md:55 +msgid "`L /`" +msgstr "" + +#: src/ui/keyboard.md:55 +msgid "Ł" +msgstr "" + +#: src/ui/keyboard.md:56 +msgid "`d /`" +msgstr "" + +#: src/ui/keyboard.md:56 +msgid "đ" +msgstr "" + +#: src/ui/keyboard.md:56 +msgid "`D /`" +msgstr "" + +#: src/ui/keyboard.md:56 +msgid "Đ" +msgstr "" + +#: src/ui/keyboard.md:56 +msgid "`o /`" +msgstr "" + +#: src/ui/keyboard.md:56 +msgid "ø" +msgstr "" + +#: src/ui/keyboard.md:56 +msgid "`O /`" +msgstr "" + +#: src/ui/keyboard.md:56 +msgid "Ø" +msgstr "" + +#: src/ui/keyboard.md:57 +msgid "`o e`" +msgstr "" + +#: src/ui/keyboard.md:57 +msgid "œ" +msgstr "" + +#: src/ui/keyboard.md:57 +msgid "`O e`" +msgstr "" + +#: src/ui/keyboard.md:57 +msgid "Œ" +msgstr "" + +#: src/ui/keyboard.md:57 +msgid "`a e`" +msgstr "" + +#: src/ui/keyboard.md:57 +msgid "æ" +msgstr "" + +#: src/ui/keyboard.md:57 +msgid "`A E`" +msgstr "" + +#: src/ui/keyboard.md:57 +msgid "Æ" +msgstr "" + +#: src/ui/keyboard.md:58 +msgid "`s s`" +msgstr "" + +#: src/ui/keyboard.md:58 +msgid "ß" +msgstr "" + +#: src/ui/keyboard.md:58 +msgid "`S s`" +msgstr "" + +#: src/ui/keyboard.md:58 +msgid "ẞ" +msgstr "" + +#: src/ui/keyboard.md:58 +msgid "`m u`" +msgstr "" + +#: src/ui/keyboard.md:58 +msgid "µ" +msgstr "" + +#: src/ui/keyboard.md:58 +msgid "`l -`" +msgstr "" + +#: src/ui/keyboard.md:58 +msgid "£" +msgstr "" + +#: src/ui/keyboard.md:59 +msgid "`p p`" +msgstr "" + +#: src/ui/keyboard.md:59 +msgid "¶" +msgstr "" + +#: src/ui/keyboard.md:59 +msgid "`s o`" +msgstr "" + +#: src/ui/keyboard.md:59 +msgid "§" +msgstr "" + +#: src/ui/keyboard.md:59 +msgid "`o _`" +msgstr "" + +#: src/ui/keyboard.md:59 +msgid "º" +msgstr "" + +#: src/ui/keyboard.md:59 +msgid "`a _`" +msgstr "" + +#: src/ui/keyboard.md:59 +msgid "ª" +msgstr "" + +#: src/ui/keyboard.md:60 +msgid "`o o`" +msgstr "" + +#: src/ui/keyboard.md:60 +msgid "°" +msgstr "" + +#: src/ui/keyboard.md:60 +msgid "`e =`" +msgstr "" + +#: src/ui/keyboard.md:60 +msgid "€" +msgstr "" + +#: src/ui/keyboard.md:60 +msgid "`o r`" +msgstr "" + +#: src/ui/keyboard.md:60 +msgid "®" +msgstr "" + +#: src/ui/keyboard.md:60 +msgid "`o c`" +msgstr "" + +#: src/ui/keyboard.md:60 +msgid "©" +msgstr "" + +#: src/ui/keyboard.md:61 +msgid "`o p`" +msgstr "" + +#: src/ui/keyboard.md:61 +msgid "℗" +msgstr "" + +#: src/ui/keyboard.md:61 +msgid "`t m`" +msgstr "" + +#: src/ui/keyboard.md:61 +msgid "™" +msgstr "" + +#: src/ui/keyboard.md:61 +msgid "`] ]`" +msgstr "" + +#: src/ui/keyboard.md:61 +msgid "⟧" +msgstr "" + +#: src/ui/keyboard.md:61 +msgid "`[ [`" +msgstr "" + +#: src/ui/keyboard.md:61 +msgid "⟦" +msgstr "" + +#: src/ui/keyboard.md:62 +msgid "`\\| -`" +msgstr "" + +#: src/ui/keyboard.md:62 +msgid "†" +msgstr "" + +#: src/ui/keyboard.md:62 +msgid "`\\| =`" +msgstr "" + +#: src/ui/keyboard.md:62 +msgid "‡" +msgstr "" + +#: src/ui/keyboard.md:62 +msgid "`- ,`" +msgstr "" + +#: src/ui/keyboard.md:62 +msgid "¬" +msgstr "" + +#: src/ui/keyboard.md:62 +msgid "`~ ~`" +msgstr "" + +#: src/ui/keyboard.md:62 +msgid "≈" +msgstr "" + +#: src/ui/keyboard.md:63 +msgid "`< <`" +msgstr "" + +#: src/ui/keyboard.md:63 +msgid "«" +msgstr "" + +#: src/ui/keyboard.md:63 +msgid "`> >`" +msgstr "" + +#: src/ui/keyboard.md:63 +msgid "»" +msgstr "" + +#: src/ui/keyboard.md:63 +msgid "`! !`" +msgstr "" + +#: src/ui/keyboard.md:63 +msgid "¡" +msgstr "" + +#: src/ui/keyboard.md:63 +msgid "`? ?`" +msgstr "" + +#: src/ui/keyboard.md:63 +msgid "¿" +msgstr "" + +#: src/ui/keyboard.md:64 +msgid "`. -`" +msgstr "" + +#: src/ui/keyboard.md:64 +msgid "·" +msgstr "" + +#: src/ui/keyboard.md:64 +msgid "`. =`" +msgstr "" + +#: src/ui/keyboard.md:64 +msgid "•" +msgstr "" + +#: src/ui/keyboard.md:64 +msgid "`. >`" +msgstr "" + +#: src/ui/keyboard.md:64 +msgid "›" +msgstr "" + +#: src/ui/keyboard.md:64 +msgid "`. <`" +msgstr "" + +#: src/ui/keyboard.md:64 +msgid "‹" +msgstr "" + +#: src/ui/keyboard.md:65 +msgid "`' 1`" +msgstr "" + +#: src/ui/keyboard.md:65 +msgid "′" +msgstr "" + +#: src/ui/keyboard.md:65 +msgid "`' 2`" +msgstr "" + +#: src/ui/keyboard.md:65 +msgid "″" +msgstr "" + +#: src/ui/keyboard.md:65 +msgid "`+ -`" +msgstr "" + +#: src/ui/keyboard.md:65 +msgid "±" +msgstr "" + +#: src/ui/keyboard.md:65 +msgid "`- :`" +msgstr "" + +#: src/ui/keyboard.md:65 +msgid "÷" +msgstr "" + +#: src/ui/keyboard.md:66 +msgid "`< =`" +msgstr "" + +#: src/ui/keyboard.md:66 +msgid "≤" +msgstr "" + +#: src/ui/keyboard.md:66 +msgid "`> =`" +msgstr "" + +#: src/ui/keyboard.md:66 +msgid "≥" +msgstr "" + +#: src/ui/keyboard.md:66 +msgid "`= /`" +msgstr "" + +#: src/ui/keyboard.md:66 +msgid "≠" +msgstr "" + +#: src/ui/keyboard.md:66 +msgid "`% o`" +msgstr "" + +#: src/ui/keyboard.md:66 +msgid "‰" +msgstr "" + +#: src/ui/keyboard.md:67 +msgid "`# f`" +msgstr "" + +#: src/ui/keyboard.md:67 +msgid "♭" +msgstr "" + +#: src/ui/keyboard.md:67 +msgid "`# n`" +msgstr "" + +#: src/ui/keyboard.md:67 +msgid "♮" +msgstr "" + +#: src/ui/keyboard.md:67 +msgid "`# s`" +msgstr "" + +#: src/ui/keyboard.md:67 +msgid "♯" +msgstr "" + +#: src/ui/keyboard.md:68 +msgid "`1 2`" +msgstr "" + +#: src/ui/keyboard.md:68 +msgid "½" +msgstr "" + +#: src/ui/keyboard.md:68 +msgid "`1 3`" +msgstr "" + +#: src/ui/keyboard.md:68 +msgid "⅓" +msgstr "" + +#: src/ui/keyboard.md:68 +msgid "`2 3`" +msgstr "" + +#: src/ui/keyboard.md:68 +msgid "⅔" +msgstr "" + +#: src/ui/keyboard.md:68 +msgid "`1 4`" +msgstr "" + +#: src/ui/keyboard.md:68 +msgid "¼" +msgstr "" + +#: src/ui/keyboard.md:69 +msgid "`3 4`" +msgstr "" + +#: src/ui/keyboard.md:69 +msgid "¾" +msgstr "" + +#: src/ui/keyboard.md:69 +msgid "`1 5`" +msgstr "" + +#: src/ui/keyboard.md:69 +msgid "⅕" +msgstr "" + +#: src/ui/keyboard.md:69 +msgid "`2 5`" +msgstr "" + +#: src/ui/keyboard.md:69 +msgid "⅖" +msgstr "" + +#: src/ui/keyboard.md:69 +msgid "`3 5`" +msgstr "" + +#: src/ui/keyboard.md:69 +msgid "⅗" +msgstr "" + +#: src/ui/keyboard.md:70 +msgid "`4 5`" +msgstr "" + +#: src/ui/keyboard.md:70 +msgid "⅘" +msgstr "" + +#: src/ui/keyboard.md:70 +msgid "`1 6`" +msgstr "" + +#: src/ui/keyboard.md:70 +msgid "⅙" +msgstr "" + +#: src/ui/keyboard.md:70 +msgid "`5 6`" +msgstr "" + +#: src/ui/keyboard.md:70 +msgid "⅚" +msgstr "" + +#: src/ui/keyboard.md:70 +msgid "`1 8`" +msgstr "" + +#: src/ui/keyboard.md:70 +msgid "⅛" +msgstr "" + +#: src/ui/keyboard.md:71 +msgid "`3 8`" +msgstr "" + +#: src/ui/keyboard.md:71 +msgid "⅜" +msgstr "" + +#: src/ui/keyboard.md:71 +msgid "`5 8`" +msgstr "" + +#: src/ui/keyboard.md:71 +msgid "⅝" +msgstr "" + +#: src/ui/keyboard.md:71 +msgid "`7 8`" +msgstr "" + +#: src/ui/keyboard.md:71 +msgid "⅞" +msgstr "" + +#: src/ui/keyboard.md:73 +msgid "Custom Keyboard Layouts" +msgstr "" + +#: src/ui/keyboard.md:75 +msgid "" +"Keyboard layouts are defined as JSON files with the following structure:" +msgstr "" + +#: src/ui/keyboard.md:77 +msgid "**name** — Display name shown in the keyboard layouts menu." +msgstr "" + +#: src/ui/keyboard.md:78 +msgid "" +"**outputs** — List of output keys for each modifier combination (_none_, " +"_shift_, _alt_, _shift+alt_)." +msgstr "" + +#: src/ui/keyboard.md:80 +msgid "" +"**keys** — Description of each key. Special key names: _Shift_ (_Sft_), " +"_Return_ (_Ret_), _Alternate_ (_Alt_), _Combine_ (_Cmb_), _MoveFwd_ " +"(_MoveF_, _MF_), _MoveBwd_ (_MoveB_, _MB_), _DelFwd_ (_DelF_, _DF_), " +"_DelBwd_ (_DelB_, _DB_), _Space_ (_Spc_). Use _▢_ to mark output keys." +msgstr "" + +#: src/ui/keyboard.md:84 +msgid "" +"**widths** — Width/height ratio for each key. The key gap ratio is 0.06." +msgstr "" + +#: src/ui/settings/index.md:3 +msgid "" +"The settings editor lets you change how Cadmus works. Open it from **Main " +"Menu → Settings**." +msgstr "" + +#: src/ui/settings/index.md:6 +msgid "Settings are organised into tabs — tap a category to open it." +msgstr "" + +#: src/ui/settings/index.md:8 +msgid "![settings editor screenshot](../screenshots/settings-editor.png)" +msgstr "" + +#: src/ui/settings/index.md:10 +msgid "Categories" +msgstr "" + +#: src/ui/settings/index.md:12 +msgid "**General** — language, sleep, auto-suspend, button layout" +msgstr "" + +#: src/ui/settings/index.md:13 +msgid "**Reader** — what happens when you finish a book" +msgstr "" + +#: src/ui/settings/index.md:14 +msgid "**Libraries** — add, edit, or remove your book libraries" +msgstr "" + +#: src/ui/settings/index.md:15 +msgid "**Dictionaries** — download and manage offline dictionaries" +msgstr "" + +#: src/ui/settings/index.md:16 +msgid "**Import** — control how new books are picked up automatically" +msgstr "" + +#: src/ui/settings/index.md:17 +msgid "**OTA** — download Cadmus updates directly to your Kobo" +msgstr "" + +#: src/ui/settings/index.md:18 +msgid "**Telemetry** — logging options" +msgstr "" + +#: src/ui/settings/dictionaries.md:3 +msgid "" +"Cadmus supports offline word definitions. You can look up any word while " +"reading by long-pressing it. Dictionaries are stored on your device and work " +"without an internet connection once downloaded." +msgstr "" + +#: src/ui/settings/dictionaries.md:7 +msgid "" +"Cadmus integrates with [reader-dict](https://github.com/reader-dict/" +"monolingual), an open-source project that provides high-quality monolingual " +"dictionaries (where you look up a word and get a definition in the same " +"language) for many languages." +msgstr "" + +#: src/ui/settings/dictionaries.md:12 +msgid "" +"![dictionaries settings screenshot](../screenshots/settings-editor-" +"dictionaries.png)" +msgstr "" + +#: src/ui/settings/dictionaries.md:14 +msgid "Opening the Dictionaries Tab" +msgstr "" + +#: src/ui/settings/dictionaries.md:16 +msgid "Go to **Main Menu → Settings → Dictionaries**." +msgstr "" + +#: src/ui/settings/dictionaries.md:18 +msgid "" +"You will see a list of available languages. Each row shows the language code " +"and its current status." +msgstr "" + +#: src/ui/settings/dictionaries.md:21 +msgid "Statuses" +msgstr "" + +#: src/ui/settings/dictionaries.md:23 +msgid "Status" +msgstr "" + +#: src/ui/settings/dictionaries.md:23 +msgid "What it means" +msgstr "" + +#: src/ui/settings/dictionaries.md:25 +msgid "Download" +msgstr "" + +#: src/ui/settings/dictionaries.md:25 +msgid "Not yet on your device — tap to download" +msgstr "" + +#: src/ui/settings/dictionaries.md:26 +msgid "Downloading" +msgstr "" + +#: src/ui/settings/dictionaries.md:26 +msgid "A download is in progress" +msgstr "" + +#: src/ui/settings/dictionaries.md:27 +msgid "Installed" +msgstr "" + +#: src/ui/settings/dictionaries.md:27 +msgid "Ready to use" +msgstr "" + +#: src/ui/settings/dictionaries.md:28 +msgid "Update Available" +msgstr "" + +#: src/ui/settings/dictionaries.md:28 +msgid "A newer version is available" +msgstr "" + +#: src/ui/settings/dictionaries.md:30 +msgid "Downloading a Dictionary" +msgstr "" + +#: src/ui/settings/dictionaries.md:32 +msgid "" +"\\[!IMPORTANT\\] Your device must be connected to Wi-Fi before you can " +"download a dictionary." +msgstr "" + +#: src/ui/settings/dictionaries.md:35 +msgid "Open **Main Menu → Settings → Dictionaries**." +msgstr "" + +#: src/ui/settings/dictionaries.md:36 +msgid "Find the language you want." +msgstr "" + +#: src/ui/settings/dictionaries.md:37 +msgid "Tap **Download** next to it." +msgstr "" + +#: src/ui/settings/dictionaries.md:39 +msgid "" +"A progress notification appears at the top of the screen while the file " +"downloads. Once the download finishes, Cadmus begins [indexing](#how-" +"indexing-works) the dictionary automatically." +msgstr "" + +#: src/ui/settings/dictionaries.md:43 +msgid "Updating a Dictionary" +msgstr "" + +#: src/ui/settings/dictionaries.md:45 +msgid "" +"When a newer version is available the status shows **Update Available**." +msgstr "" + +#: src/ui/settings/dictionaries.md:47 src/ui/settings/dictionaries.md:57 +#: src/ui/settings/dictionaries.md:62 +msgid "Tap the language row." +msgstr "" + +#: src/ui/settings/dictionaries.md:48 +msgid "Select **Update** from the menu." +msgstr "" + +#: src/ui/settings/dictionaries.md:50 +msgid "The updated dictionary replaces the old one automatically." +msgstr "" + +#: src/ui/settings/dictionaries.md:52 +msgid "Re-downloading a Dictionary" +msgstr "" + +#: src/ui/settings/dictionaries.md:54 +msgid "" +"If a dictionary is already installed you can re-download it to get a fresh " +"copy:" +msgstr "" + +#: src/ui/settings/dictionaries.md:58 +msgid "Select **Re-download** from the menu." +msgstr "" + +#: src/ui/settings/dictionaries.md:60 +msgid "Deleting a Dictionary" +msgstr "" + +#: src/ui/settings/dictionaries.md:63 +msgid "Select **Delete** from the menu." +msgstr "" + +#: src/ui/settings/dictionaries.md:65 +msgid "The dictionary files are removed from your device." +msgstr "" + +#: src/ui/settings/dictionaries.md:67 +msgid "How Indexing Works" +msgstr "" + +#: src/ui/settings/dictionaries.md:69 +msgid "" +"After you download, update, or re-download a dictionary, Cadmus needs to " +"**index** it before you can look up words. Indexing reads every word in the " +"dictionary and stores it in a database on disk so that lookups are fast " +"without loading the entire dictionary into memory. This is especially " +"important on devices with limited memory." +msgstr "" + +#: src/ui/settings/dictionaries.md:75 +msgid "" +"A notification with a progress bar appears at the top of the screen while " +"indexing is in progress." +msgstr "" + +#: src/ui/settings/dictionaries.md:78 +msgid "" +"\\[!NOTE\\] You can keep reading while indexing runs in the background. " +"Words that have already been indexed are available for lookup right away, so " +"you may get partial results until indexing finishes." +msgstr "" + +#: src/ui/settings/dictionaries.md:83 +msgid "What happens when you restart your Kobo" +msgstr "" + +#: src/ui/settings/dictionaries.md:85 +msgid "" +"If your Kobo restarts or shuts down while indexing is still running, Cadmus " +"picks up where it left off the next time it starts. It does not start over " +"from the beginning." +msgstr "" + +#: src/ui/settings/dictionaries.md:89 +msgid "When does re-indexing happen" +msgstr "" + +#: src/ui/settings/dictionaries.md:91 +msgid "Cadmus automatically re-indexes a dictionary when you:" +msgstr "" + +#: src/ui/settings/dictionaries.md:93 +msgid "**Update** it to a newer version" +msgstr "" + +#: src/ui/settings/dictionaries.md:94 +msgid "**Re-download** it" +msgstr "" + +#: src/ui/settings/dictionaries.md:95 +msgid "**Delete** it (the old index is removed)" +msgstr "" + +#: src/ui/settings/dictionaries.md:97 +msgid "" +"You do not need to trigger indexing yourself — it happens automatically " +"whenever the dictionary files change." +msgstr "" + +#: src/ui/settings/dictionaries.md:100 +msgid "Where Dictionaries are Stored" +msgstr "" + +#: src/ui/settings/dictionaries.md:102 +msgid "" +"Cadmus stores dictionaries in different locations depending on whether your " +"device has an SD card and whether you are using a test build:" +msgstr "" + +#: src/ui/settings/dictionaries.md:105 +msgid "**On devices with an SD card**:" +msgstr "" + +#: src/ui/settings/dictionaries.md:106 +msgid "Production: `/mnt/sd/.cadmus/dictionaries/reader-dict//`" +msgstr "" + +#: src/ui/settings/dictionaries.md:107 +msgid "Test build: `/mnt/sd/.cadmus-tst/dictionaries/reader-dict//`" +msgstr "" + +#: src/ui/settings/dictionaries.md:108 +msgid "**On devices without an SD card**:" +msgstr "" + +#: src/ui/settings/dictionaries.md:109 +msgid "" +"Production: `/mnt/onboard/.adds/cadmus/dictionaries/reader-dict//`" +msgstr "" + +#: src/ui/settings/dictionaries.md:110 +msgid "" +"Test build: `/mnt/onboard/.adds/cadmus-tst/dictionaries/reader-dict//`" +msgstr "" + +#: src/ui/settings/dictionaries.md:112 +msgid "" +"Each language gets its own subfolder containing a `.dict.dz` (or `.dict`) " +"and a `.index` file." +msgstr "" + +#: src/ui/settings/dictionaries.md:114 +msgid "" +"\\[!NOTE\\] If your SD card is already inserted when you upgrade Cadmus, " +"your dictionaries are moved to the SD card automatically on the next boot. " +"If you insert an SD card after Cadmus has already run, move the " +"`dictionaries/` folder manually — see [Moving data to an SD card](../../" +"installation/migration.md#moving-data-to-an-sd-card)." +msgstr "" + +#: src/library/index.md:3 +msgid "" +"The library is where all your books live. Cadmus scans your device's storage " +"and keeps track of everything you've added, so you can browse, search, and " +"organize your collection right on the device." +msgstr "" + +#: src/library/index.md:7 +msgid "What's in this section" +msgstr "" + +#: src/library/index.md:9 +msgid "" +"[Importing books](importing.md) — how Cadmus finds and adds books to your " +"library" +msgstr "" + +#: src/library/importing.md:1 +msgid "Importing Books" +msgstr "" + +#: src/library/importing.md:3 +msgid "" +"Cadmus scans your device's storage and adds books to its database " +"automatically. This process is called **importing**." +msgstr "" + +#: src/library/importing.md:6 +msgid "Automatic import on startup" +msgstr "" + +#: src/library/importing.md:8 +msgid "" +"Cadmus automatically imports books every time it starts. The import is " +"**incremental**: files whose modification time and file size haven't changed " +"since the last import are skipped, avoiding unnecessary re-fingerprinting. " +"Only new or modified files are processed, significantly improving startup " +"performance for large libraries." +msgstr "" + +#: src/library/importing.md:14 +msgid "" +"Copy files to your device, restart the app, and they'll appear in your " +"library right away." +msgstr "" + +#: src/library/importing.md:17 +msgid "Force full import" +msgstr "" + +#: src/library/importing.md:19 +msgid "" +"If you suspect the import cache is stale or corrupted, you can force a full " +"re-import:" +msgstr "" + +#: src/library/importing.md:22 +msgid "Open **Settings**." +msgstr "" + +#: src/library/importing.md:23 +msgid "Navigate to the **Import** section." +msgstr "" + +#: src/library/importing.md:24 +msgid "Tap **Force Full Import**." +msgstr "" + +#: src/library/importing.md:25 +msgid "Confirm when prompted." +msgstr "" + +#: src/library/importing.md:27 +msgid "" +"This bypasses the incremental import cache and re-fingerprints all files in " +"your library directories. Be aware that this can take time and drain the " +"battery for large libraries, so keep your device plugged in while it runs." +msgstr "" + +#: src/troubleshooting/index.md:5 +msgid "" +"When something isn't working right, logs will help with figuring out what " +"went wrong. If you're reporting an issue, sharing your logs makes it much " +"easier to debug." +msgstr "" + +#: src/troubleshooting/index.md:9 +msgid "Where to find Cadmus logs" +msgstr "" + +#: src/troubleshooting/index.md:11 +msgid "" +"Cadmus saves logs in a `logs` folder. Here's where to find it on each " +"platform:" +msgstr "" + +#: src/troubleshooting/index.md:13 src/investigations/index.md:7 +msgid "Platform" +msgstr "" + +#: src/troubleshooting/index.md:13 +msgid "Stable build" +msgstr "" + +#: src/troubleshooting/index.md:13 +msgid "Test build" +msgstr "" + +#: src/troubleshooting/index.md:15 src/investigations/index.md:9 +msgid "Kobo" +msgstr "" + +#: src/troubleshooting/index.md:15 +msgid "`/mnt/onboard/.adds/cadmus/logs`" +msgstr "" + +#: src/troubleshooting/index.md:15 +msgid "`/mnt/onboard/.adds/cadmus-tst/logs`" +msgstr "" + +#: src/troubleshooting/index.md:17 +msgid "" +"Each time you start Cadmus, it creates a new log file with a unique ID. By " +"default, only the 3 most recent log files are kept — older ones are deleted " +"automatically. You can change this with the [`logging.max-files`](../" +"settings/index.md#loggingmax-files) setting." +msgstr "" + +#: src/troubleshooting/index.md:22 +msgid "The log files look like this:" +msgstr "" + +#: src/troubleshooting/index.md:28 +msgid "Finding your run ID" +msgstr "" + +#: src/troubleshooting/index.md:30 +msgid "" +"Every time Cadmus starts, it prints a run ID to help you identify which log " +"file belongs to that session. You can find this in:" +msgstr "" + +#: src/troubleshooting/index.md:34 +msgid "" +"**info.log** - The startup log in the Cadmus folder. Look for the line that " +"says `Cadmus run started with ID:` followed by a string of letters and " +"numbers." +msgstr "" + +#: src/troubleshooting/index.md:37 +msgid "For example:" +msgstr "" + +#: src/troubleshooting/index.md:43 +msgid "" +"Copy only the UUID part — the string of letters and numbers between `ID:` " +"and the `(version` text." +msgstr "" + +#: src/troubleshooting/index.md:46 +msgid "" +"**Console output** - If you're running Cadmus from a terminal, the same run " +"ID is printed when it starts." +msgstr "" + +#: src/troubleshooting/index.md:49 +msgid "Kernel logs" +msgstr "" + +#: src/troubleshooting/index.md:51 +msgid "" +"Kernel logs can be useful to debug lower level system issues, for example a " +"kernel panic, which triggers a device reboot." +msgstr "" + +#: src/troubleshooting/index.md:54 +msgid "" +"Kernel logs are only available in [test builds](../installation/test-builds." +"md). If you're using a test build and want to include kernel logs:" +msgstr "" + +#: src/troubleshooting/index.md:57 +msgid "Open **Main Menu → Settings**" +msgstr "" + +#: src/troubleshooting/index.md:58 +msgid "Go to `Telemetry`" +msgstr "" + +#: src/troubleshooting/index.md:59 +msgid "Enable kernel logs" +msgstr "" + +#: src/troubleshooting/index.md:60 +msgid "Restart Cadmus" +msgstr "" + +#: src/troubleshooting/index.md:62 +msgid "" +"Kernel logs will then be saved in the same log file as your Cadmus logs." +msgstr "" + +#: src/troubleshooting/index.md:64 +msgid "" +"\\[!NOTE\\] Kernel logs will use more disk space, so don't forget to turn it " +"back off." +msgstr "" + +#: src/troubleshooting/index.md:67 +msgid "Crashloop recovery" +msgstr "" + +#: src/troubleshooting/index.md:69 +msgid "" +"If Cadmus crashes 3 times in a row, it will exit back to Nickel instead of " +"restarting. This prevents the device from getting stuck in an infinite loop " +"of crashes." +msgstr "" + +#: src/troubleshooting/index.md:73 +msgid "When this happens:" +msgstr "" + +#: src/troubleshooting/index.md:75 +msgid "Check `info.log` in the Cadmus folder for the panic error" +msgstr "" + +#: src/troubleshooting/index.md:76 +msgid "" +"The crash counter resets when you start Cadmus manually (using the restart " +"option in the menu or rebooting)" +msgstr "" + +#: src/contributing/devenv-setup.md:1 +msgid "Development Environment Setup" +msgstr "" + +#: src/contributing/devenv-setup.md:3 +msgid "" +"Cadmus uses [devenv](https://devenv.sh/) with Nix to provide a reproducible " +"development environment. This guide covers setup on both Linux and macOS." +msgstr "" + +#: src/contributing/devenv-setup.md:6 src/contributing/translations/docs.md:6 +msgid "Prerequisites" +msgstr "" + +#: src/contributing/devenv-setup.md:8 +msgid "" +"Install Nix with flakes enabled. The easiest way is using the [Determinate " +"Nix Installer](https://github.com/DeterminateSystems/nix-installer)." +msgstr "" + +#: src/contributing/devenv-setup.md:9 +msgid "Install [devenv](https://devenv.sh/getting-started)." +msgstr "" + +#: src/contributing/devenv-setup.md:11 +msgid "Quick Start" +msgstr "" + +#: src/contributing/devenv-setup.md:13 +msgid "Clone the repository and enter the devenv shell:" +msgstr "" + +#: src/contributing/devenv-setup.md:21 +msgid "Download the packaged runtime assets used by Kobo builds:" +msgstr "" + +#: src/contributing/devenv-setup.md:27 +msgid "" +"\\[!NOTE\\] `cadmus-core` generates some compile-time metadata from the " +"bundled asset directories. For Kobo builds, make sure `bin/`, `resources/`, " +"and `hyphenation-patterns/` are present before `cargo xtask build-kobo` so " +"the generated asset list is complete." +msgstr "" + +#: src/contributing/devenv-setup.md:33 +msgid "" +"Thirdparty C/C++ dependencies (MuPDF, libwebp, zlib, etc.) are tracked as " +"git submodules and built automatically by `build.rs` when you run `cargo " +"build` or `cargo xtask run-emulator`. No separate setup step is required." +msgstr "" + +#: src/contributing/devenv-setup.md:37 +msgid "Run the emulator:" +msgstr "" + +#: src/contributing/devenv-setup.md:43 +msgid "Available Commands" +msgstr "" + +#: src/contributing/devenv-setup.md:45 +msgid "Once inside the devenv shell, these commands are available:" +msgstr "" + +#: src/contributing/devenv-setup.md:47 +msgid "Command" +msgstr "" + +#: src/contributing/devenv-setup.md:49 +msgid "`cargo xtask download-assets`" +msgstr "" + +#: src/contributing/devenv-setup.md:49 +msgid "Download packaged Plato runtime assets" +msgstr "" + +#: src/contributing/devenv-setup.md:50 +msgid "`cargo xtask test`" +msgstr "" + +#: src/contributing/devenv-setup.md:50 +msgid "Run the test suite across the feature matrix" +msgstr "" + +#: src/contributing/devenv-setup.md:51 +msgid "`cargo xtask run-emulator`" +msgstr "" + +#: src/contributing/devenv-setup.md:51 +msgid "Run the emulator" +msgstr "" + +#: src/contributing/devenv-setup.md:52 +msgid "`cargo xtask build-kobo`" +msgstr "" + +#: src/contributing/devenv-setup.md:52 +msgid "Cross-compile for Kobo device" +msgstr "" + +#: src/contributing/devenv-setup.md:53 +msgid "`cargo xtask dist`" +msgstr "" + +#: src/contributing/devenv-setup.md:53 +msgid "Assemble the Kobo distribution directory" +msgstr "" + +#: src/contributing/devenv-setup.md:54 +msgid "`cargo xtask bundle`" +msgstr "" + +#: src/contributing/devenv-setup.md:54 +msgid "Package KoboRoot.tgz for installation" +msgstr "" + +#: src/contributing/devenv-setup.md:55 +msgid "`cadmus-dev-otel`" +msgstr "" + +#: src/contributing/devenv-setup.md:55 +msgid "Run emulator with tracing and profiling enabled" +msgstr "" + +#: src/contributing/devenv-setup.md:56 +msgid "`devenv up`" +msgstr "" + +#: src/contributing/devenv-setup.md:56 +msgid "Start observability stack (Grafana, Tempo, Loki)" +msgstr "" + +#: src/contributing/devenv-setup.md:57 +msgid "`cargo xtask docs`" +msgstr "" + +#: src/contributing/devenv-setup.md:57 +msgid "Build documentation portal (mdBook + Cargo docs)" +msgstr "" + +#: src/contributing/devenv-setup.md:58 +msgid "`cadmus-docs-serve`" +msgstr "" + +#: src/contributing/devenv-setup.md:58 +msgid "Serve documentation portal locally on port 1111" +msgstr "" + +#: src/contributing/devenv-setup.md:59 +msgid "`cadmus-translate`" +msgstr "" + +#: src/contributing/devenv-setup.md:59 +msgid "Generate the docs translation template (.pot)" +msgstr "" + +#: src/contributing/devenv-setup.md:61 +msgid "" +"Run `cargo xtask --help` to see all available subcommands, or `cargo xtask " +" --help` for options on a specific command." +msgstr "" + +#: src/contributing/devenv-setup.md:64 +msgid "" +"Or have a look at the rustdocs for `xtask` here." +msgstr "" + +#: src/contributing/devenv-setup.md:66 +msgid "Tasks" +msgstr "" + +#: src/contributing/devenv-setup.md:68 +msgid "" +"The devenv environment uses [tasks](https://devenv.sh/tasks/) to manage " +"build dependencies. Tasks are defined in `devenv.nix` and can be run with " +"`devenv tasks run `." +msgstr "" + +#: src/contributing/devenv-setup.md:71 +msgid "Available Tasks" +msgstr "" + +#: src/contributing/devenv-setup.md:73 +msgid "Task" +msgstr "" + +#: src/contributing/devenv-setup.md:73 +msgid "Dependencies" +msgstr "" + +#: src/contributing/devenv-setup.md:75 src/contributing/devenv-setup.md:76 +msgid "`docs:build`" +msgstr "" + +#: src/contributing/devenv-setup.md:75 +msgid "Build documentation EPUB (only rebuilds if files changed)" +msgstr "" + +#: src/contributing/devenv-setup.md:75 +msgid "None" +msgstr "" + +#: src/contributing/devenv-setup.md:76 +msgid "`build:kobo`" +msgstr "" + +#: src/contributing/devenv-setup.md:76 +msgid "Build for Kobo device" +msgstr "" + +#: src/contributing/devenv-setup.md:78 +msgid "All tasks delegate to `cargo xtask` under the hood." +msgstr "" + +#: src/contributing/devenv-setup.md:80 +msgid "How Tasks Work" +msgstr "" + +#: src/contributing/devenv-setup.md:82 +msgid "" +"Tasks with dependencies automatically run their dependencies first. For " +"example:" +msgstr "" + +#: src/contributing/devenv-setup.md:85 +msgid "# This will first run docs:build (if needed), then build for Kobo\n" +msgstr "" + +#: src/contributing/devenv-setup.md:89 +msgid "" +"The `docs:build` task uses `execIfModified` to only rebuild when " +"documentation files have actually changed." +msgstr "" + +#: src/contributing/devenv-setup.md:91 +msgid "Kobo Build Notes" +msgstr "" + +#: src/contributing/devenv-setup.md:93 +msgid "`cargo xtask download-assets` must run before `cargo xtask build-kobo`." +msgstr "" + +#: src/contributing/devenv-setup.md:94 +msgid "" +"OTA updates delete Cadmus-owned bundled files before reboot, then Kobo " +"extracts the new `KoboRoot.tgz` over the install directory." +msgstr "" + +#: src/contributing/devenv-setup.md:96 +msgid "" +"User files outside the generated Cadmus-owned asset list must be preserved." +msgstr "" + +#: src/contributing/devenv-setup.md:98 +msgid "Documentation Portal" +msgstr "" + +#: src/contributing/devenv-setup.md:100 +msgid "" +"Cadmus provides a unified documentation portal that combines user guides, " +"API reference, and contribution guides in one place." +msgstr "" + +#: src/contributing/devenv-setup.md:103 +msgid "Building and Serving Locally" +msgstr "" + +#: src/contributing/devenv-setup.md:105 +msgid "To build the documentation portal:" +msgstr "" + +#: src/contributing/devenv-setup.md:111 +msgid "This runs the full build pipeline:" +msgstr "" + +#: src/contributing/devenv-setup.md:113 +msgid "Builds the mdBook user guide (`docs/book/html/`)" +msgstr "" + +#: src/contributing/devenv-setup.md:114 +msgid "Generates Rust API documentation (`target/doc/`)" +msgstr "" + +#: src/contributing/devenv-setup.md:115 +msgid "Builds the Zola landing page and integrates all documentation" +msgstr "" + +#: src/contributing/devenv-setup.md:117 +msgid "To serve the portal locally with live reload:" +msgstr "" + +#: src/contributing/devenv-setup.md:123 +msgid "" +"The portal will be available at with automatic " +"rebuilds when you change documentation files or Rust code." +msgstr "" + +#: src/contributing/devenv-setup.md:126 +msgid "Documentation Structure" +msgstr "" + +#: src/contributing/devenv-setup.md:128 +msgid "The portal provides three integrated sections:" +msgstr "" + +#: src/contributing/devenv-setup.md:130 +msgid "**Landing Page** (`/`) - Overview and feature highlights" +msgstr "" + +#: src/contributing/devenv-setup.md:131 +msgid "**User Guide** (`/guide/`) - User-facing documentation from mdBook" +msgstr "" + +#: src/contributing/devenv-setup.md:132 +msgid "**API Reference** (`/api/`) - Auto-generated Rust API documentation" +msgstr "" + +#: src/contributing/devenv-setup.md:134 +msgid "" +"All three sections are deployed as a single artifact to GitHub Pages at " +"." +msgstr "" + +#: src/contributing/devenv-setup.md:137 +msgid "Continuous Integration" +msgstr "" + +#: src/contributing/devenv-setup.md:139 +msgid "" +"Documentation is automatically built and validated on every pull request and " +"deployed on push to `main` or `master`. The CI pipeline checks:" +msgstr "" + +#: src/contributing/devenv-setup.md:142 +msgid "mdBook documentation compiles" +msgstr "" + +#: src/contributing/devenv-setup.md:143 +msgid "Rust code documentation is valid" +msgstr "" + +#: src/contributing/devenv-setup.md:144 +msgid "Zola landing page builds successfully" +msgstr "" + +#: src/contributing/devenv-setup.md:146 +msgid "Running Tests" +msgstr "" + +#: src/contributing/devenv-setup.md:148 +msgid "" +"Tests require the `TEST_ROOT_DIR` environment variable to be set. The " +"easiest way to run the full test matrix is:" +msgstr "" + +#: src/contributing/devenv-setup.md:155 +msgid "" +"This sets `TEST_ROOT_DIR` automatically and runs tests across all feature " +"combinations. To run a single feature combination:" +msgstr "" + +#: src/contributing/devenv-setup.md:159 +msgid "\"emulator + test\"" +msgstr "" + +#: src/contributing/devenv-setup.md:162 +msgid "Or to run tests manually without xtask:" +msgstr "" + +#: src/contributing/devenv-setup.md:165 src/contributing/devenv-setup.md:233 +msgid "$(pwd)" +msgstr "" + +#: src/contributing/devenv-setup.md:168 +msgid "" +"`TEST_ROOT_DIR` is automatically configured in CI but must be set manually " +"when running `cargo test` directly." +msgstr "" + +#: src/contributing/devenv-setup.md:171 +msgid "Platform Support" +msgstr "" + +#: src/contributing/devenv-setup.md:173 +msgid "Linux (Full Support)" +msgstr "" + +#: src/contributing/devenv-setup.md:175 +msgid "Linux provides full development capabilities including:" +msgstr "" + +#: src/contributing/devenv-setup.md:177 src/contributing/devenv-setup.md:187 +msgid "Native development (emulator, tests)" +msgstr "" + +#: src/contributing/devenv-setup.md:178 src/contributing/devenv-setup.md:188 +msgid "Cross-compilation for Kobo devices using the Linaro ARM toolchain" +msgstr "" + +#: src/contributing/devenv-setup.md:179 src/contributing/devenv-setup.md:189 +msgid "Git hooks (actionlint, shellcheck, shfmt, markdownlint, prettier)" +msgstr "" + +#: src/contributing/devenv-setup.md:181 +msgid "" +"The Linaro toolchain is automatically added to `PATH` and provides `arm-" +"linux-gnueabihf-*` commands." +msgstr "" + +#: src/contributing/devenv-setup.md:183 +msgid "macOS (Full Support)" +msgstr "" + +#: src/contributing/devenv-setup.md:185 +msgid "macOS supports full development capabilities including:" +msgstr "" + +#: src/contributing/devenv-setup.md:191 +msgid "macOS-Specific Notes" +msgstr "" + +#: src/contributing/devenv-setup.md:193 +msgid "" +"**MuPDF build**: On macOS, the native build script manually gathers pkg-" +"config CFLAGS for system libraries because MuPDF's build system doesn't " +"properly detect them on Darwin." +msgstr "" + +#: src/contributing/devenv-setup.md:196 +msgid "Observability Stack" +msgstr "" + +#: src/contributing/devenv-setup.md:198 +msgid "The devenv includes a full observability stack for development:" +msgstr "" + +#: src/contributing/devenv-setup.md:201 +msgid "# Start all services\n" +msgstr "" + +#: src/contributing/devenv-setup.md:203 +msgid "# In another terminal, run the instrumented emulator\n" +msgstr "" + +#: src/contributing/devenv-setup.md:208 +msgid "Services available after `devenv up`:" +msgstr "" + +#: src/contributing/devenv-setup.md:210 +msgid "Service" +msgstr "" + +#: src/contributing/devenv-setup.md:210 +msgid "URL" +msgstr "" + +#: src/contributing/devenv-setup.md:210 +#: src/contributing/library-database.md:159 +msgid "Purpose" +msgstr "" + +#: src/contributing/devenv-setup.md:212 +msgid "Grafana" +msgstr "" + +#: src/contributing/devenv-setup.md:212 +msgid "" +msgstr "" + +#: src/contributing/devenv-setup.md:212 +msgid "Dashboards and exploration" +msgstr "" + +#: src/contributing/devenv-setup.md:213 +msgid "Tempo" +msgstr "" + +#: src/contributing/devenv-setup.md:213 +msgid "" +msgstr "" + +#: src/contributing/devenv-setup.md:213 +msgid "Distributed tracing" +msgstr "" + +#: src/contributing/devenv-setup.md:214 +msgid "Loki" +msgstr "" + +#: src/contributing/devenv-setup.md:214 +msgid "" +msgstr "" + +#: src/contributing/devenv-setup.md:214 +msgid "Log aggregation" +msgstr "" + +#: src/contributing/devenv-setup.md:215 +msgid "Prometheus" +msgstr "" + +#: src/contributing/devenv-setup.md:215 +msgid "" +msgstr "" + +#: src/contributing/devenv-setup.md:215 +msgid "Metrics" +msgstr "" + +#: src/contributing/devenv-setup.md:216 +msgid "OTLP Collector" +msgstr "" + +#: src/contributing/devenv-setup.md:216 +msgid "" +msgstr "" + +#: src/contributing/devenv-setup.md:216 +msgid "Telemetry ingestion" +msgstr "" + +#: src/contributing/devenv-setup.md:217 +msgid "Pyroscope" +msgstr "" + +#: src/contributing/devenv-setup.md:217 +msgid "" +msgstr "" + +#: src/contributing/devenv-setup.md:217 +msgid "Continuous profiling" +msgstr "" + +#: src/contributing/devenv-setup.md:219 +msgid "For more details on telemetry, see [Telemetry](telemetry/index.md)." +msgstr "" + +#: src/contributing/devenv-setup.md:223 +msgid "Shell takes a long time to start" +msgstr "" + +#: src/contributing/devenv-setup.md:225 +msgid "" +"The first `devenv shell` invocation downloads and builds dependencies, which " +"can take several minutes. Subsequent invocations are cached and should be " +"fast." +msgstr "" + +#: src/contributing/devenv-setup.md:228 +msgid "Tests fail with \"TEST_ROOT_DIR must be set\"" +msgstr "" + +#: src/contributing/devenv-setup.md:230 +msgid "Set the environment variable before running tests:" +msgstr "" + +#: src/contributing/devenv-setup.md:236 +msgid "Local Configuration" +msgstr "" + +#: src/contributing/devenv-setup.md:238 +msgid "" +"Create `devenv.local.nix` to override settings without modifying the tracked " +"configuration:" +msgstr "" + +#: src/contributing/devenv-setup.md:240 +msgid "" +"```nix\n" +"{ pkgs, ... }:\n" +"\n" +"{\n" +" env = {\n" +" # Example: Set TEST_ROOT_DIR automatically\n" +" TEST_ROOT_DIR = builtins.getEnv \"PWD\";\n" +" };\n" +"}\n" +"```" +msgstr "" + +#: src/contributing/devenv-setup.md:251 +msgid "This file is gitignored and won't affect other contributors." +msgstr "" + +#: src/contributing/code-style.md:3 +msgid "" +"Cadmus enforces a consistent code style across all languages using [treefmt]" +"(https://treefmt.com/) for formatting and several linters for static " +"analysis. The pre-commit hooks run all checks automatically, and CI enforces " +"the same rules on every pull request." +msgstr "" + +#: src/contributing/code-style.md:8 +msgid "Formatters and Linters" +msgstr "" + +#: src/contributing/code-style.md:10 +msgid "Tool" +msgstr "" + +#: src/contributing/code-style.md:10 +msgid "Languages / Files" +msgstr "" + +#: src/contributing/code-style.md:10 +msgid "Key Configuration" +msgstr "" + +#: src/contributing/code-style.md:12 +msgid "`rustfmt`" +msgstr "" + +#: src/contributing/code-style.md:12 +msgid "Rust (`*.rs`)" +msgstr "" + +#: src/contributing/code-style.md:12 +msgid "Workspace `rustfmt.toml`" +msgstr "" + +#: src/contributing/code-style.md:13 +msgid "`prettier`" +msgstr "" + +#: src/contributing/code-style.md:13 +msgid "JSON, YAML, Markdown, CSS, JS" +msgstr "" + +#: src/contributing/code-style.md:13 +msgid "`.prettierrc.json`" +msgstr "" + +#: src/contributing/code-style.md:14 +msgid "`shfmt`" +msgstr "" + +#: src/contributing/code-style.md:14 src/contributing/code-style.md:15 +msgid "Shell (`*.sh`, `*.bash`)" +msgstr "" + +#: src/contributing/code-style.md:14 +msgid "`-i 2 -ci` (2-space, case-indent)" +msgstr "" + +#: src/contributing/code-style.md:15 +msgid "`shellcheck`" +msgstr "" + +#: src/contributing/code-style.md:15 +msgid "`.editorconfig`" +msgstr "" + +#: src/contributing/code-style.md:16 +msgid "`yamllint`" +msgstr "" + +#: src/contributing/code-style.md:16 +msgid "YAML" +msgstr "" + +#: src/contributing/code-style.md:16 +msgid "`extends: default`, several rules disabled" +msgstr "" + +#: src/contributing/code-style.md:17 +msgid "`rumdl`" +msgstr "" + +#: src/contributing/code-style.md:17 +msgid "Markdown (`*.md`)" +msgstr "" + +#: src/contributing/code-style.md:17 +msgid "Default rules" +msgstr "" + +#: src/contributing/code-style.md:18 +msgid "`actionlint`" +msgstr "" + +#: src/contributing/code-style.md:18 +msgid "GitHub Actions workflows" +msgstr "" + +#: src/contributing/code-style.md:18 +msgid "`-ignore \"rust-toolchain\"`" +msgstr "" + +#: src/contributing/code-style.md:19 +msgid "`clippy`" +msgstr "" + +#: src/contributing/code-style.md:19 +msgid "Rust" +msgstr "" + +#: src/contributing/code-style.md:19 +msgid "`-D warnings`" +msgstr "" + +#: src/contributing/code-style.md:21 +msgid "Running treefmt" +msgstr "" + +#: src/contributing/code-style.md:23 +msgid "All formatters run through `treefmt`. Inside the devenv shell:" +msgstr "" + +#: src/contributing/code-style.md:26 +msgid "# Format all files tracked by treefmt\n" +msgstr "" + +#: src/contributing/code-style.md:28 +msgid "# Check without writing (dry run)\n" +msgstr "" + +#: src/contributing/code-style.md:33 +msgid "" +"The pre-commit hook (`git-hooks.hooks.treefmt`) runs `treefmt --fail-on-" +"change` automatically on every commit, so format issues are caught before " +"they reach CI." +msgstr "" + +#: src/contributing/code-style.md:36 +msgid "Rust Style" +msgstr "" + +#: src/contributing/code-style.md:38 +msgid "Formatting" +msgstr "" + +#: src/contributing/code-style.md:40 +msgid "" +"`rustfmt` with the workspace configuration handles formatting automatically. " +"Run it via treefmt or directly:" +msgstr "" + +#: src/contributing/code-style.md:47 +msgid "Linting (Clippy)" +msgstr "" + +#: src/contributing/code-style.md:49 +msgid "Clippy runs with `-D warnings` — all warnings are errors:" +msgstr "" + +#: src/contributing/code-style.md:55 +msgid "" +"Clippy runs across every feature flag combination in CI. When adding a new " +"feature flag, update `.github/workflows/cargo.yml` to include the new matrix " +"entries." +msgstr "" + +#: src/contributing/code-style.md:59 +msgid "Key Conventions" +msgstr "" + +#: src/contributing/code-style.md:61 +msgid "Prefer `?` over `unwrap()` / `expect()` in library and app code." +msgstr "" + +#: src/contributing/code-style.md:62 +msgid "Use iterators over index-based loops." +msgstr "" + +#: src/contributing/code-style.md:63 +msgid "" +"Use `&str` over `String` in function parameters when ownership is not needed." +msgstr "" + +#: src/contributing/code-style.md:64 +msgid "Prefer borrowing over cloning." +msgstr "" + +#: src/contributing/code-style.md:65 +msgid "Avoid premature `collect()` — keep iterators lazy." +msgstr "" + +#: src/contributing/code-style.md:66 +msgid "Use newtype wrappers over raw primitives for domain concepts." +msgstr "" + +#: src/contributing/code-style.md:68 +msgid "Shell Style" +msgstr "" + +#: src/contributing/code-style.md:70 +msgid "" +"Shell scripts are formatted with `shfmt` (`-i 2 -ci`) and checked with " +"`shellcheck`. The `-ci` flag indents `case` statement arms relative to the " +"`case` keyword:" +msgstr "" + +#: src/contributing/code-style.md:75 +msgid "# Correct — case arms indented with -ci\n" +msgstr "" + +#: src/contributing/code-style.md:76 +msgid "\"${VAR}\"" +msgstr "" + +#: src/contributing/code-style.md:86 +msgid "Scripts must declare their shell variant. For bash scripts, use:" +msgstr "" + +#: src/contributing/code-style.md:89 +msgid "#! /bin/bash\n" +msgstr "" + +#: src/contributing/code-style.md:92 +msgid "Structured Logging" +msgstr "" + +#: src/contributing/code-style.md:94 +msgid "" +"Use the `tracing` crate with structured fields — never string formatting for " +"log data:" +msgstr "" + +#: src/contributing/code-style.md:98 +msgid "// Correct\n" +msgstr "" + +#: src/contributing/code-style.md:99 +msgid "\"Found artifacts\"" +msgstr "" + +#: src/contributing/code-style.md:100 +msgid "// Wrong\n" +msgstr "" + +#: src/contributing/code-style.md:102 +msgid "\"[OTA] Found {} artifacts for PR #{}\"" +msgstr "" + +#: src/contributing/code-style.md:105 +msgid "See [Logging](telemetry/logging.md) for log level guidance." +msgstr "" + +#: src/contributing/code-style.md:107 +msgid "Comments" +msgstr "" + +#: src/contributing/code-style.md:109 +msgid "" +"Comment **why**, not **what**. Most code needs no comments — good naming is " +"preferred. The rules:" +msgstr "" + +#: src/contributing/code-style.md:112 +msgid "" +"No inline comments — if one feels necessary, extract the code into a well-" +"named function instead." +msgstr "" + +#: src/contributing/code-style.md:114 +msgid "No commented-out code." +msgstr "" + +#: src/contributing/code-style.md:115 +msgid "No changelog comments (`Modified by X on date`)." +msgstr "" + +#: src/contributing/code-style.md:116 +msgid "No decorative dividers (`//=====`)." +msgstr "" + +#: src/contributing/code-style.md:117 +msgid "`TODO`, `FIXME`, `HACK`, `NOTE` annotations are fine with context." +msgstr "" + +#: src/contributing/code-style.md:119 +msgid "Public API items must have doc comments." +msgstr "" + +#: src/contributing/code-style.md:121 +msgid "CI Checks" +msgstr "" + +#: src/contributing/code-style.md:123 +msgid "The following CI workflows enforce style:" +msgstr "" + +#: src/contributing/code-style.md:125 +msgid "Workflow" +msgstr "" + +#: src/contributing/code-style.md:125 +msgid "What it checks" +msgstr "" + +#: src/contributing/code-style.md:127 +msgid "`cargo.yml`" +msgstr "" + +#: src/contributing/code-style.md:127 +msgid "`rustfmt`, `clippy` (full feature matrix), tests" +msgstr "" + +#: src/contributing/code-style.md:128 +msgid "`shell.yml`" +msgstr "" + +#: src/contributing/code-style.md:128 +msgid "`shellcheck`, `shfmt` (changed lines only)" +msgstr "" + +#: src/contributing/code-style.md:129 +msgid "`docs-lint.yml`" +msgstr "" + +#: src/contributing/code-style.md:129 +msgid "`prettier`, `markdownlint` (docs and markdown files)" +msgstr "" + +#: src/contributing/code-style.md:131 +msgid "" +"CI uses `filter_mode: added` for shell checks, meaning only lines changed in " +"the PR are flagged. Running `treefmt` locally before pushing will catch " +"everything." +msgstr "" + +#: src/contributing/event-system.md:3 +msgid "" +"Cadmus uses a tree-based event system where views are organized " +"hierarchically and events flow through two distinct channels: the **Hub** " +"and the **Bus**. Understanding the difference between these channels is " +"essential for implementing correct event handling in views." +msgstr "" + +#: src/contributing/event-system.md:7 +msgid "" +"\\[!TIP\\] CSS is hard, this page might render better on 80% zoom on smaller " +"screens. I tried to make the mermaid diagrams as big as possible to make " +"them easier to read, which made them overflow a bit." +msgstr "" + +#: src/contributing/event-system.md:12 +msgid "You might also want to hide the sidebar." +msgstr "" + +#: src/contributing/event-system.md:16 +msgid "" +"The UI is a tree of `View` objects. Each view can have children, forming a " +"hierarchy like:" +msgstr "" + +#: src/contributing/event-system.md:18 +msgid "" +"```mermaid\n" +"flowchart TD\n" +" Home[\"Home (root view)\"]\n" +" TopBar[\"TopBar\"]\n" +" Shelf[\"Shelf\"]\n" +" Book1[\"Book\"]\n" +" Book2[\"Book\"]\n" +" BottomBar[\"BottomBar\"]\n" +" Dialog[\"Dialog ← overlay child\"]\n" +" Label[\"Label\"]\n" +" Button[\"Button\"]\n" +"\n" +" Home --> TopBar\n" +" Home --> Shelf\n" +" Home --> BottomBar\n" +" Home --> Dialog\n" +" Shelf --> Book1\n" +" Shelf --> Book2\n" +" Dialog --> Label\n" +" Dialog --> Button\n" +"```" +msgstr "" + +#: src/contributing/event-system.md:40 +msgid "" +"Events enter the tree from the main loop and travel **top-down** (root to " +"leaves), with the highest z-level children checked first. Views can " +"communicate back **up** the tree via the bus, or **globally** via the hub." +msgstr "" + +#: src/contributing/event-system.md:44 +msgid "Hub vs Bus" +msgstr "" + +#: src/contributing/event-system.md:46 +msgid "The two channels serve fundamentally different purposes:" +msgstr "" + +#: src/contributing/event-system.md:48 +msgid "" +"```mermaid\n" +"flowchart TB\n" +" subgraph MainLoop[\"Main Loop\"]\n" +" rx[\"rx.recv()\"]\n" +" match[\"match evt { ... }\"]\n" +" handle[\"handle_event(root, &evt, &tx, &bus, ...)\"]\n" +" Hub[\"Hub (tx)\"]\n" +" Bus[\"Bus (VecDeque)\"]\n" +"\n" +" rx --> match\n" +" match -->|dispatches to view tree| handle\n" +" handle --> Hub\n" +" handle --> Bus\n" +" Bus -->|unhandled bus events forwarded to hub| Hub\n" +" end\n" +"```" +msgstr "" + +#: src/contributing/event-system.md:65 +msgid "Hub (`Sender`)" +msgstr "" + +#: src/contributing/event-system.md:67 +msgid "" +"The hub is an `mpsc::Sender` — a global channel that sends events to " +"the **main loop**. Events sent to the hub are processed in the **next " +"iteration** of the main loop, not immediately." +msgstr "" + +#: src/contributing/event-system.md:70 +msgid "**Use the hub when:**" +msgstr "" + +#: src/contributing/event-system.md:72 +msgid "" +"The event needs to be handled by the main loop directly (e.g., `Event::" +"Close`, `Event::Open`, `Event::Notification`)" +msgstr "" + +#: src/contributing/event-system.md:74 +msgid "" +"The event should reach all views in a future dispatch cycle (e.g., `Event::" +"Focus`)" +msgstr "" + +#: src/contributing/event-system.md:75 +msgid "You need to communicate across unrelated parts of the view tree" +msgstr "" + +#: src/contributing/event-system.md:78 +msgid "// Close a view — handled by the main loop's match statement\n" +msgstr "" + +#: src/contributing/event-system.md:80 +msgid "// Show a notification — main loop creates the Notification view\n" +msgstr "" + +#: src/contributing/event-system.md:83 +msgid "// Set focus — dispatched to all views in the next loop iteration\n" +msgstr "" + +#: src/contributing/event-system.md:88 +msgid "Bus (`VecDeque`)" +msgstr "" + +#: src/contributing/event-system.md:90 +msgid "" +"The bus is a local `VecDeque` that passes events **from a child to " +"its parent**. Events placed on the bus are handled synchronously during the " +"current dispatch cycle." +msgstr "" + +#: src/contributing/event-system.md:93 +msgid "**Use the bus when:**" +msgstr "" + +#: src/contributing/event-system.md:95 +msgid "A child needs to communicate with its direct parent" +msgstr "" + +#: src/contributing/event-system.md:96 +msgid "" +"The parent is expected to handle the event (e.g., a button telling its " +"parent dialog it was pressed)" +msgstr "" + +#: src/contributing/event-system.md:98 +msgid "The event should bubble up through the view hierarchy" +msgstr "" + +#: src/contributing/event-system.md:101 +msgid "// Child tells parent about a submission\n" +msgstr "" + +#: src/contributing/event-system.md:103 +msgid "// Child requests parent to close it\n" +msgstr "" + +#: src/contributing/event-system.md:108 +msgid "Bus Bubbling" +msgstr "" + +#: src/contributing/event-system.md:110 +msgid "" +"When a bus event is not handled by any ancestor view, it reaches the root " +"and gets **forwarded to the hub** for processing in the next main loop " +"iteration:" +msgstr "" + +#: src/contributing/event-system.md:114 +msgid "" +"// End of main loop iteration — unhandled bus events become hub events\n" +msgstr "" + +#: src/contributing/event-system.md:120 +msgid "Event Dispatch" +msgstr "" + +#: src/contributing/event-system.md:122 +msgid "" +"The core dispatch function in `view/mod.rs` controls how events flow through " +"the tree:" +msgstr "" + +#: src/contributing/event-system.md:124 +msgid "" +"```mermaid\n" +"flowchart TD\n" +" Start[\"handle_event(view, event)\"] --> CheckLeaf{\"Is view a leaf
(no children)?\"}\n" +"\n" +" CheckLeaf -->|YES| HandleDirect[\"call view.handle_event()
return " +"result\"]\n" +" CheckLeaf -->|NO| ReverseIter[\"Step 1 - Iterate children in REVERSE " +"order
(highest z-level first)\"]\n" +"\n" +" ReverseIter --> CheckChildren[\"For each child:
call " +"handle_event(child, event)\"]\n" +" CheckChildren --> Captured{\"Child returns true?\"}\n" +" Captured -->|YES| SetCaptured[\"captured = true
BREAK\"]\n" +" Captured -->|NO| NextChild[\"Next child\"]\n" +" NextChild --> CheckChildren\n" +"\n" +" SetCaptured --> ProcessBus[\"Step 2 - Process child_bus events\"]\n" +" CheckChildren -->|No more children| ProcessBus\n" +"\n" +" ProcessBus --> BusEvent{\"For each event
on child_bus\"}\n" +" BusEvent --> HandleBus[\"call view.handle_event(child_evt)\"]\n" +" HandleBus --> ViewHandles{\"View handles it?\"}\n" +" ViewHandles -->|YES| RemoveBus[\"remove from bus\"]\n" +" ViewHandles -->|NO| KeepBus[\"keep on bus
(bubbles up)\"]\n" +" RemoveBus --> BusEvent\n" +" KeepBus --> BusEvent\n" +"\n" +" BusEvent -->|No more bus events| CheckCaptured{\"NOT captured
by any " +"child?\"}\n" +" CheckCaptured -->|YES| HandleParent[\"Step 3 - call view." +"handle_event(event)
return captured || view's result\"]\n" +" CheckCaptured -->|NO| ReturnCaptured[\"return captured\"]\n" +"```" +msgstr "" + +#: src/contributing/event-system.md:153 +msgid "Key Rules" +msgstr "" + +#: src/contributing/event-system.md:155 msgid "" -"Cadmus uses a tree-based event system where views are organized " -"hierarchically and events flow through two distinct channels: the **Hub** " -"and the **Bus**. Understanding the difference between these channels is " -"essential for implementing correct event handling in views." +"**Reverse iteration**: Children are checked from last to first (highest z-" +"level first). This ensures overlays like dialogs and menus receive events " +"before the views beneath them." msgstr "" -#: src/contributing/event-system.md:14 -msgid "You might also want to hide the sidebar." +#: src/contributing/event-system.md:158 +msgid "" +"**Short-circuit on capture**: Once a child returns `true`, no other children " +"or the parent view receive the event. This is why the Dialog's outside-tap " +"handler works — it returns `true` to prevent the event from reaching views " +"behind it." msgstr "" -#: src/contributing/event-system.md:18 +#: src/contributing/event-system.md:162 msgid "" -"The UI is a tree of `View` objects. Each view can have children, forming a " -"hierarchy like:" +"**Parent only runs if uncaptured**: The parent's `handle_event` is only " +"called if no child captured the event (`captured || view." +"handle_event(...)`). If `captured` is `true`, the parent is short-circuited." +msgstr "" + +#: src/contributing/event-system.md:166 +msgid "Main Loop Event Handling" +msgstr "" + +#: src/contributing/event-system.md:168 +msgid "" +"The main loop (`app.rs`) receives events from the hub and handles them in " +"two stages. First, `TaskManager` gets a chance to observe every event — this " +"is where `ImportLibrary`, `ImportFinished`, and `ReindexDictionaries` are " +"intercepted to schedule or coalesce background tasks. Then the event enters " +"the large `match` statement, where some events are dispatched into the view " +"tree and others are handled directly:" +msgstr "" + +#: src/contributing/event-system.md:175 +msgid "" +"```mermaid\n" +"flowchart TB\n" +" subgraph MainLoop[\"Main Loop\"]\n" +" direction TB\n" +"\n" +" Recv[\"rx.recv() → evt\"]\n" +" TaskManager[\"TaskManager::handle_event()
(ImportLibrary, " +"ImportFinished, ReindexDictionaries)\"]\n" +"\n" +" Gesture[\"Event::Gesture(Tap/Swipe/...)\"]\n" +" GestureAction[\"Dispatched into view tree via handle_event()\"]\n" +"\n" +" Close[\"Event::Close(id)\"]\n" +" CloseAction[\"Handled directly:
locate_by_id() + remove\"]\n" +"\n" +" Notification[\"Event::Notification(...)\"]\n" +" NotificationAction[\"Handled directly:
create/update " +"Notification\"]\n" +"\n" +" Open[\"Event::Open(info)\"]\n" +" OpenAction[\"Handled directly:
push Reader onto history\"]\n" +"\n" +" Focus[\"Event::Focus(...)\"]\n" +" FocusAction[\"Dispatched into view tree via handle_event()\"]\n" +"\n" +" Select[\"Event::Select(...)\"]\n" +" SelectAction[\"Some handled directly,
some dispatched\"]\n" +"\n" +" ImportLibrary[\"Event::ImportLibrary / ImportFinished / " +"ReindexDictionaries\"]\n" +" ImportAction[\"Handled by TaskManager
(schedules/coalesces " +"tasks)\"]\n" +"\n" +" Recv --> TaskManager\n" +" TaskManager --> Gesture\n" +" TaskManager --> Close\n" +" TaskManager --> Notification\n" +" TaskManager --> Open\n" +" TaskManager --> Focus\n" +" TaskManager --> Select\n" +" TaskManager --> ImportLibrary\n" +"\n" +" Gesture --> GestureAction\n" +" Close --> CloseAction\n" +" Notification --> NotificationAction\n" +" Open --> OpenAction\n" +" Focus --> FocusAction\n" +" Select --> SelectAction\n" +" ImportLibrary --> ImportAction\n" +" end\n" +"```" +msgstr "" + +#: src/contributing/event-system.md:223 +msgid "Event::Close" +msgstr "" + +#: src/contributing/event-system.md:225 +msgid "" +"`Event::Close(ViewId)` is handled **directly** by the main loop using " +"`locate_by_id()`, which searches only the **top-level children** of the root " +"view:" +msgstr "" + +#: src/contributing/event-system.md:238 +msgid "" +"**Key limitation**: The main loop can only remove **direct children of the " +"root**. To close nested views, either:" +msgstr "" + +#: src/contributing/event-system.md:241 +msgid "" +"**Use the parent's ViewId** (via hub): Removes the entire parent container" +msgstr "" + +#: src/contributing/event-system.md:242 +msgid "" +"**Use the bus**: Parent handles the close and removes just the specific child" +msgstr "" + +#: src/contributing/event-system.md:244 +msgid "" +"See [Why ViewId Matters for Close](#why-viewid-matters-for-close) and " +"[Closing Nested Views via the Bus](#closing-nested-views-via-the-bus) for " +"details." +msgstr "" + +#: src/contributing/event-system.md:247 +msgid "Practical Example: Dialog Outside-Tap" +msgstr "" + +#: src/contributing/event-system.md:249 +msgid "" +"When a `Dialog` is tapped outside its bounds, the following sequence occurs:" +msgstr "" + +#: src/contributing/event-system.md:278 +msgid "Why ViewId Matters for Close" +msgstr "" + +#: src/contributing/event-system.md:280 +msgid "" +"When using the **hub** to close views, `locate_by_id()` only finds top-level " +"children. If a dialog is nested inside a parent, you must use the " +"**parent's** `ViewId` — this removes the entire parent:" +msgstr "" + +#: src/contributing/event-system.md:284 +msgid "" +"```mermaid\n" +"flowchart TD\n" +" Root[\"Root View\"]\n" +" Home[\"Home\"]\n" +" OtaView[\"OtaView (top-level)\"]\n" +" Dialog[\"Dialog (nested)\"]\n" +"\n" +" Root --> Home\n" +" Root --> OtaView\n" +" OtaView --> Dialog\n" +"\n" +" Note[\"hub.send(Event::Close(ViewId::Ota(Main)))
→ Removes OtaView + " +"Dialog\"]\n" +"```" +msgstr "" + +#: src/contributing/event-system.md:298 +msgid "" +"To close just the nested view without affecting siblings, use the **bus** " +"instead (see below)." +msgstr "" + +#: src/contributing/event-system.md:300 +msgid "Closing Nested Views via the Bus" +msgstr "" + +#: src/contributing/event-system.md:302 +msgid "" +"Send `Event::Close` via the **bus** to close nested views. Parents handle " +"bus events synchronously and can remove any child directly:" +msgstr "" + +#: src/contributing/event-system.md:306 +msgid "// Child sends close via bus\n" +msgstr "" + +#: src/contributing/event-system.md:323 +msgid "**Comparison:**" +msgstr "" + +#: src/contributing/event-system.md:325 +#: src/contributing/library-database.md:159 +msgid "Method" +msgstr "" + +#: src/contributing/event-system.md:325 +msgid "Code" +msgstr "" + +#: src/contributing/event-system.md:325 +msgid "Handler" +msgstr "" + +#: src/contributing/event-system.md:325 src/contributing/event-system.md:349 +msgid "Scope" +msgstr "" + +#: src/contributing/event-system.md:327 +msgid "Hub close" +msgstr "" + +#: src/contributing/event-system.md:327 +msgid "`hub.send(Event::Close(id))`" +msgstr "" + +#: src/contributing/event-system.md:327 +msgid "Main loop" +msgstr "" + +#: src/contributing/event-system.md:327 +msgid "Top-level children only" +msgstr "" + +#: src/contributing/event-system.md:328 +msgid "Bus close" +msgstr "" + +#: src/contributing/event-system.md:328 +msgid "`bus.push_back(Event::Close(id))`" +msgstr "" + +#: src/contributing/event-system.md:328 +msgid "Parent view" +msgstr "" + +#: src/contributing/event-system.md:328 +msgid "Any child" +msgstr "" + +#: src/contributing/event-system.md:330 +msgid "**Example implementation:**" +msgstr "" + +#: src/contributing/event-system.md:336 +msgid "// Return false to bubble up so grandparent removes us\n" +msgstr "" + +#: src/contributing/event-system.md:344 +#: src/investigations/kobo/issue-51-dhcp-investigation.md:10 +msgid "Summary" +msgstr "" + +#: src/contributing/event-system.md:346 +msgid "Aspect" +msgstr "" + +#: src/contributing/event-system.md:346 +msgid "Hub" +msgstr "" + +#: src/contributing/event-system.md:346 +msgid "Bus" +msgstr "" + +#: src/contributing/event-system.md:348 +#: src/investigations/kobo/issue-51-dhcp-investigation.md:147 +msgid "Type" +msgstr "" + +#: src/contributing/event-system.md:348 +msgid "`mpsc::Sender`" +msgstr "" + +#: src/contributing/event-system.md:348 +msgid "`VecDeque`" +msgstr "" + +#: src/contributing/event-system.md:349 +msgid "Global (main loop)" +msgstr "" + +#: src/contributing/event-system.md:349 +msgid "Local (parent-child)" +msgstr "" + +#: src/contributing/event-system.md:350 +msgid "Timing" +msgstr "" + +#: src/contributing/event-system.md:350 +msgid "Next loop iteration" +msgstr "" + +#: src/contributing/event-system.md:350 +msgid "Current dispatch cycle" +msgstr "" + +#: src/contributing/event-system.md:351 +msgid "Direction" +msgstr "" + +#: src/contributing/event-system.md:351 +msgid "View → Main loop" +msgstr "" + +#: src/contributing/event-system.md:351 +msgid "Child → Parent" +msgstr "" + +#: src/contributing/event-system.md:352 +msgid "Unhandled events" +msgstr "" + +#: src/contributing/event-system.md:352 +msgid "Processed by main loop `match`" +msgstr "" + +#: src/contributing/event-system.md:352 +msgid "Forwarded to hub" +msgstr "" + +#: src/contributing/event-system.md:353 +msgid "Use for" +msgstr "" + +#: src/contributing/event-system.md:353 +msgid "Close, Focus, Notifications" +msgstr "" + +#: src/contributing/event-system.md:353 +msgid "Submit, child-to-parent signals" +msgstr "" + +#: src/contributing/telemetry/index.md:3 +msgid "Cadmus has three related observability paths:" +msgstr "" + +#: src/contributing/telemetry/index.md:5 +msgid "`logging` for structured log export and local log files" +msgstr "" + +#: src/contributing/telemetry/index.md:6 +msgid "`tracing` for distributed tracing and OTLP export" +msgstr "" + +#: src/contributing/telemetry/index.md:7 +msgid "`profiling` for continuous profiling with Pyroscope" +msgstr "" + +#: src/contributing/telemetry/index.md:9 +msgid "" +"Use this section when you need to understand how those features fit " +"together, what each one depends on, and how to run them locally." +msgstr "" + +#: src/contributing/telemetry/index.md:12 +msgid "" +"Cadmus assigns each run a unique Run ID using UUID v7. That Run ID ties " +"together local log files, OTLP exports, and profiling data for the same app " +"session." +msgstr "" + +#: src/contributing/telemetry/index.md:16 +msgid "Pages" +msgstr "" + +#: src/contributing/telemetry/index.md:18 +#: src/contributing/telemetry/tracing.md:109 +msgid "[Logging](logging.md)" +msgstr "" + +#: src/contributing/telemetry/index.md:19 +#: src/contributing/telemetry/logging.md:80 +msgid "[Tracing](tracing.md)" +msgstr "" + +#: src/contributing/telemetry/index.md:20 +#: src/contributing/telemetry/logging.md:81 +#: src/contributing/telemetry/tracing.md:110 +msgid "[Profiling](profiling.md)" +msgstr "" + +#: src/contributing/telemetry/index.md:22 +#: src/contributing/telemetry/profiling.md:12 +msgid "Feature flags" +msgstr "" + +#: src/contributing/telemetry/index.md:24 +msgid "`tracing` enables structured logs, distributed traces, and OTLP export." +msgstr "" + +#: src/contributing/telemetry/index.md:25 +msgid "`profiling` enables heap and CPU profiling with Pyroscope." +msgstr "" + +#: src/contributing/telemetry/index.md:26 +msgid "`telemetry` enables both `tracing` and `profiling` together in the app." +msgstr "" + +#: src/contributing/telemetry/index.md:28 +msgid "Architecture" +msgstr "" + +#: src/contributing/telemetry/index.md:30 +msgid "The telemetry stack is split into three layers:" +msgstr "" + +#: src/contributing/telemetry/index.md:32 +msgid "" +"Logging writes newline-delimited JSON to disk and can export log records " +"over OTLP." +msgstr "" + +#: src/contributing/telemetry/index.md:34 +msgid "" +"Tracing creates spans around instrumented operations and exports them over " +"OTLP." +msgstr "" + +#: src/contributing/telemetry/index.md:36 +msgid "" +"Profiling samples CPU and heap activity and pushes profiles to Pyroscope." +msgstr "" + +#: src/contributing/telemetry/index.md:38 +msgid "" +"When `telemetry` is enabled, Cadmus runs all three paths together. In local " +"development this matches the default observability stack exposed by `devenv " +"up`." +msgstr "" + +#: src/contributing/telemetry/index.md:42 +msgid "Local setup" +msgstr "" + +#: src/contributing/telemetry/index.md:44 +msgid "" +"The development environment includes a full observability stack. Use the " +"`cadmus-dev-otel` command to run the emulator with telemetry enabled." msgstr "" -#: src/contributing/event-system.md:44 +#: src/contributing/telemetry/logging.md:3 msgid "" -"Events enter the tree from the main loop and travel **top-down** (root to " -"leaves), with the highest z-level children checked first. Views can " -"communicate back **up** the tree via the bus, or **globally** via the hub." +"Cadmus writes structured JSON logs to disk and can export logs to an OTLP " +"backend when the `tracing` feature is enabled." msgstr "" -#: src/contributing/event-system.md:51 -msgid "The two channels serve fundamentally different purposes:" +#: src/contributing/telemetry/logging.md:6 +#: src/contributing/telemetry/tracing.md:6 +#: src/contributing/telemetry/profiling.md:6 +msgid "What it does" msgstr "" -#: src/contributing/event-system.md:72 -msgid "Hub (`Sender`)" +#: src/contributing/telemetry/logging.md:8 +msgid "Writes newline-delimited JSON log files to the configured log directory" msgstr "" -#: src/contributing/event-system.md:74 +#: src/contributing/telemetry/logging.md:9 msgid "" -"The hub is an `mpsc::Sender` — a global channel that sends events to " -"the **main loop**. Events sent to the hub are processed in the **next " -"iteration** of the main loop, not immediately." +"Adds run metadata so a single app session can be traced through log output" msgstr "" -#: src/contributing/event-system.md:77 -msgid "**Use the hub when:**" +#: src/contributing/telemetry/logging.md:10 +msgid "Optionally exports logs to an OpenTelemetry collector" msgstr "" -#: src/contributing/event-system.md:79 -msgid "" -"The event needs to be handled by the main loop directly (e.g., " -"`Event::Close`, `Event::Open`, `Event::Notification`)" +#: src/contributing/telemetry/logging.md:12 +#: src/contributing/telemetry/tracing.md:12 +msgid "Feature flag" msgstr "" -#: src/contributing/event-system.md:81 -msgid "" -"The event should reach all views in a future dispatch cycle (e.g., " -"`Event::Focus`)" +#: src/contributing/telemetry/logging.md:14 +msgid "`tracing` enables OTLP log export and shared tracing/logging context" msgstr "" -#: src/contributing/event-system.md:82 -msgid "You need to communicate across unrelated parts of the view tree" +#: src/contributing/telemetry/logging.md:16 +msgid "Log file format" msgstr "" -#: src/contributing/event-system.md:97 -msgid "Bus (`VecDeque`)" +#: src/contributing/telemetry/logging.md:18 +msgid "Cadmus writes newline-delimited JSON files named like this:" msgstr "" -#: src/contributing/event-system.md:99 -msgid "" -"The bus is a local `VecDeque` that passes events **from a child to " -"its parent**. Events placed on the bus are handled synchronously during the " -"current dispatch cycle." +#: src/contributing/telemetry/logging.md:24 +msgid "Each record includes these fields:" msgstr "" -#: src/contributing/event-system.md:102 -msgid "**Use the bus when:**" +#: src/contributing/telemetry/logging.md:26 +msgid "`timestamp`" msgstr "" -#: src/contributing/event-system.md:104 -msgid "A child needs to communicate with its direct parent" +#: src/contributing/telemetry/logging.md:27 +msgid "`level`" msgstr "" -#: src/contributing/event-system.md:105 -msgid "" -"The parent is expected to handle the event (e.g., a button telling its " -"parent dialog it was pressed)" +#: src/contributing/telemetry/logging.md:28 +msgid "`target`" msgstr "" -#: src/contributing/event-system.md:107 -msgid "The event should bubble up through the view hierarchy" +#: src/contributing/telemetry/logging.md:29 +msgid "`fields`" msgstr "" -#: src/contributing/event-system.md:119 -msgid "Bus Bubbling" +#: src/contributing/telemetry/logging.md:30 +msgid "`spans`" msgstr "" -#: src/contributing/event-system.md:121 +#: src/contributing/telemetry/logging.md:32 msgid "" -"When a bus event is not handled by any ancestor view, it reaches the root " -"and gets **forwarded to the hub** for processing in the next main loop " -"iteration:" +"The `spans` array carries active tracing context so log records can be " +"matched back to the traced operation that emitted them." msgstr "" -#: src/contributing/event-system.md:133 -msgid "Event Dispatch" +#: src/contributing/telemetry/logging.md:35 +#: src/contributing/telemetry/tracing.md:84 +msgid "Resource attributes" msgstr "" -#: src/contributing/event-system.md:135 +#: src/contributing/telemetry/logging.md:37 msgid "" -"The core dispatch function in `view/mod.rs` controls how events flow through " -"the tree:" +"When OTLP export is enabled, log records include the same resource metadata " +"as traces:" msgstr "" -#: src/contributing/event-system.md:168 -msgid "Key Rules" +#: src/contributing/telemetry/logging.md:40 +#: src/contributing/telemetry/tracing.md:88 +msgid "`service.name = cadmus`" msgstr "" -#: src/contributing/event-system.md:170 -msgid "" -"**Reverse iteration**: Children are checked from last to first (highest z-" -"level first). This ensures overlays like dialogs and menus receive events " -"before the views beneath them." +#: src/contributing/telemetry/logging.md:41 +#: src/contributing/telemetry/tracing.md:89 +msgid "`service.version = `" msgstr "" -#: src/contributing/event-system.md:173 -msgid "" -"**Short-circuit on capture**: Once a child returns `true`, no other children " -"or the parent view receive the event. This is why the Dialog's outside-tap " -"handler works — it returns `true` to prevent the event from reaching views " -"behind it." +#: src/contributing/telemetry/logging.md:42 +#: src/contributing/telemetry/tracing.md:90 +msgid "`cadmus.run_id = `" msgstr "" -#: src/contributing/event-system.md:177 -msgid "" -"**Parent only runs if uncaptured**: The parent's `handle_event` is only " -"called if no child captured the event (`captured || " -"view.handle_event(...)`). If `captured` is `true`, the parent is short-" -"circuited." +#: src/contributing/telemetry/logging.md:43 +#: src/contributing/telemetry/tracing.md:91 +msgid "`hostname = `" msgstr "" -#: src/contributing/event-system.md:181 -msgid "Main Loop Event Handling" +#: src/contributing/telemetry/logging.md:45 +#: src/contributing/telemetry/tracing.md:93 +#: src/contributing/telemetry/profiling.md:27 +msgid "Configuration" msgstr "" -#: src/contributing/event-system.md:183 +#: src/contributing/telemetry/logging.md:47 msgid "" -"The main loop (`app.rs`) receives events from the hub and handles them in a " -"large `match` statement. Some events are dispatched into the view tree, " -"while others are handled directly:" +"See the [settings reference](../../settings/index.md#logging) for the full " +"logging configuration. The main options are:" msgstr "" -#: src/contributing/event-system.md:220 -msgid "Event::Close" +#: src/contributing/telemetry/logging.md:53 +msgid "`logging.directory`" msgstr "" -#: src/contributing/event-system.md:222 +#: src/contributing/telemetry/logging.md:56 +#: src/contributing/telemetry/tracing.md:98 msgid "" -"`Event::Close(ViewId)` is handled **directly** by the main loop using " -"`locate_by_id()`, which searches only the **top-level children** of the root " -"view:" +"`OTEL_EXPORTER_OTLP_ENDPOINT` overrides `logging.otlp-endpoint` when both " +"are set." msgstr "" -#: src/contributing/event-system.md:237 +#: src/contributing/telemetry/logging.md:59 msgid "" -"**Key limitation**: The main loop can only remove **direct children of the " -"root**. To close nested views, either:" +"`RUST_LOG` overrides the configured log level. This is useful when you need " +"trace-level output for a single subsystem without editing `Settings.toml`." msgstr "" -#: src/contributing/event-system.md:240 -msgid "" -"**Use the parent's ViewId** (via hub): Removes the entire parent container" +#: src/contributing/telemetry/logging.md:63 +msgid "# Enable debug logs globally\n" msgstr "" -#: src/contributing/event-system.md:241 -msgid "" -"**Use the bus**: Parent handles the close and removes just the specific child" +#: src/contributing/telemetry/logging.md:64 +msgid "debug" msgstr "" -#: src/contributing/event-system.md:243 -msgid "" -"See [Why ViewId Matters for Close](#why-viewid-matters-for-close) and " -"[Closing Nested Views via the Bus](#closing-nested-views-via-the-bus) for " -"details." +#: src/contributing/telemetry/logging.md:65 +msgid "# Enable trace logs for a specific module\n" msgstr "" -#: src/contributing/event-system.md:246 -msgid "Practical Example: Dialog Outside-Tap" +#: src/contributing/telemetry/logging.md:70 +#: src/contributing/telemetry/profiling.md:17 +msgid "Runtime behavior" msgstr "" -#: src/contributing/event-system.md:248 +#: src/contributing/telemetry/logging.md:72 msgid "" -"When a `Dialog` is tapped outside its bounds, the following sequence occurs:" +"When `tracing` is enabled, log export is initialized during app startup and " +"shut down during app exit." msgstr "" -#: src/contributing/event-system.md:279 -msgid "Why ViewId Matters for Close" +#: src/contributing/telemetry/logging.md:75 +msgid "" +"Cadmus always writes local JSON logs. OTLP export is an additional sink " +"layered on top when an endpoint is configured." msgstr "" -#: src/contributing/event-system.md:281 -msgid "" -"When using the **hub** to close views, `locate_by_id()` only finds top-level " -"children. If a dialog is nested inside a parent, you must use the " -"**parent's** `ViewId` — this removes the entire parent:" +#: src/contributing/telemetry/logging.md:78 +#: src/contributing/telemetry/tracing.md:107 +msgid "Related docs" msgstr "" -#: src/contributing/event-system.md:301 +#: src/contributing/telemetry/tracing.md:3 msgid "" -"To close just the nested view without affecting siblings, use the **bus** " -"instead (see below)." +"Cadmus uses `tracing` instrumentation to capture execution flow through the " +"app and export spans to an OTLP backend." msgstr "" -#: src/contributing/event-system.md:303 -msgid "Closing Nested Views via the Bus" +#: src/contributing/telemetry/tracing.md:8 +msgid "Adds spans around key operations in the app and core crates" msgstr "" -#: src/contributing/event-system.md:305 -msgid "" -"Send `Event::Close` via the **bus** to close nested views. Parents handle " -"bus events synchronously and can remove any child directly:" +#: src/contributing/telemetry/tracing.md:9 +msgid "Captures timing, fields, and parent-child relationships between spans" msgstr "" -#: src/contributing/event-system.md:330 -msgid "**Comparison:**" +#: src/contributing/telemetry/tracing.md:10 +msgid "Exports traces to an OpenTelemetry collector when configured" msgstr "" -#: src/contributing/event-system.md:332 -#: src/contributing/library-database.md:152 -msgid "Method" +#: src/contributing/telemetry/tracing.md:14 +msgid "`tracing` enables tracing support in the codebase" msgstr "" -#: src/contributing/event-system.md:332 -msgid "Code" +#: src/contributing/telemetry/tracing.md:16 +msgid "Instrumentation" msgstr "" -#: src/contributing/event-system.md:332 -msgid "Handler" +#: src/contributing/telemetry/tracing.md:18 +msgid "" +"Most view and runtime chokepoints are instrumented with conditional " +"compilation so tracing can be compiled out when the feature is disabled." msgstr "" -#: src/contributing/event-system.md:332 src/contributing/event-system.md:358 -msgid "Scope" +#: src/contributing/telemetry/tracing.md:21 +msgid "Each instrumented function can capture:" msgstr "" -#: src/contributing/event-system.md:334 -msgid "Hub close" +#: src/contributing/telemetry/tracing.md:23 +msgid "function and module name" msgstr "" -#: src/contributing/event-system.md:334 -msgid "`hub.send(Event::Close(id))`" +#: src/contributing/telemetry/tracing.md:24 +msgid "selected input fields" msgstr "" -#: src/contributing/event-system.md:334 -msgid "Main loop" +#: src/contributing/telemetry/tracing.md:25 +msgid "execution duration" msgstr "" -#: src/contributing/event-system.md:334 -msgid "Top-level children only" +#: src/contributing/telemetry/tracing.md:26 +msgid "return values at `TRACE` level" msgstr "" -#: src/contributing/event-system.md:335 -msgid "Bus close" +#: src/contributing/telemetry/tracing.md:27 +msgid "parent-child span relationships" msgstr "" -#: src/contributing/event-system.md:335 -msgid "`bus.push_back(Event::Close(id))`" +#: src/contributing/telemetry/tracing.md:29 +msgid "View code is instrumented around two high-value paths:" msgstr "" -#: src/contributing/event-system.md:335 -msgid "Parent view" +#: src/contributing/telemetry/tracing.md:31 +msgid "`handle_event` methods for event flow through the UI tree" msgstr "" -#: src/contributing/event-system.md:335 -msgid "Any child" +#: src/contributing/telemetry/tracing.md:32 +msgid "`render` methods for rendering and layout timing" msgstr "" -#: src/contributing/event-system.md:337 -msgid "**Example implementation:**" +#: src/contributing/telemetry/tracing.md:34 +msgid "" +"For detailed instrumentation conventions, see `.github/instructions/rust-" +"instrumentation.instructions.md`." msgstr "" -#: src/contributing/event-system.md:353 -msgid "Summary" +#: src/contributing/telemetry/tracing.md:37 +msgid "Example: instrument a function" msgstr "" -#: src/contributing/event-system.md:355 -msgid "Aspect" +#: src/contributing/telemetry/tracing.md:39 +msgid "" +"Use `#[cfg_attr(feature = \"tracing\", tracing::instrument(...))]` on " +"functions that should create a span for each call." msgstr "" -#: src/contributing/event-system.md:355 -msgid "Hub" +#: src/contributing/telemetry/tracing.md:44 +msgid "\"tracing\"" msgstr "" -#: src/contributing/event-system.md:355 -msgid "Bus" +#: src/contributing/telemetry/tracing.md:48 +msgid "\"Saving cover image\"" msgstr "" -#: src/contributing/event-system.md:357 -msgid "Type" +#: src/contributing/telemetry/tracing.md:56 +msgid "" +"Use `skip(...)` for large values, borrowed buffers, or types with noisy " +"`Debug` output. Add `fields(...)` for the identifiers you will actually " +"query in Tempo or logs." msgstr "" -#: src/contributing/event-system.md:357 -msgid "`mpsc::Sender`" +#: src/contributing/telemetry/tracing.md:60 +msgid "Example: instrument a closure" msgstr "" -#: src/contributing/event-system.md:357 -msgid "`VecDeque`" +#: src/contributing/telemetry/tracing.md:62 +msgid "" +"For a short synchronous closure, create a span and run the closure inside it " +"with `in_scope()`." msgstr "" -#: src/contributing/event-system.md:358 -msgid "Global (main loop)" +#: src/contributing/telemetry/tracing.md:66 +msgid "\"sorting\"" msgstr "" -#: src/contributing/event-system.md:358 -msgid "Local (parent-child)" +#: src/contributing/telemetry/tracing.md:73 +msgid "" +"This is the usual pattern when you want to time a single closure body " +"without extracting it into a separate function." msgstr "" -#: src/contributing/event-system.md:359 -msgid "Timing" +#: src/contributing/telemetry/tracing.md:76 +msgid "OTLP export" msgstr "" -#: src/contributing/event-system.md:359 -msgid "Next loop iteration" +#: src/contributing/telemetry/tracing.md:78 +msgid "" +"When `tracing` is enabled, Cadmus initializes a tracer provider that exports " +"to `/v1/traces` using batch span processors." msgstr "" -#: src/contributing/event-system.md:359 -msgid "Current dispatch cycle" +#: src/contributing/telemetry/tracing.md:81 +msgid "" +"This gives you distributed traces in backends like Tempo, Jaeger, or any " +"other OTLP-compatible collector." msgstr "" -#: src/contributing/event-system.md:360 -msgid "Direction" +#: src/contributing/telemetry/tracing.md:86 +msgid "Each exported span includes shared process metadata:" msgstr "" -#: src/contributing/event-system.md:360 -msgid "View → Main loop" +#: src/contributing/telemetry/tracing.md:95 +msgid "" +"See the [settings reference](../../settings/index.md#logging) for the full " +"logging and OTLP configuration. The main option is `logging.otlp-endpoint`." msgstr "" -#: src/contributing/event-system.md:360 -msgid "Child → Parent" +#: src/contributing/telemetry/tracing.md:101 +msgid "To build Cadmus with tracing support:" msgstr "" -#: src/contributing/event-system.md:361 -msgid "Unhandled events" +#: src/contributing/telemetry/profiling.md:3 +msgid "" +"Cadmus supports continuous profiling with Pyroscope when the `profiling` " +"feature is enabled." msgstr "" -#: src/contributing/event-system.md:361 -msgid "Processed by main loop `match`" +#: src/contributing/telemetry/profiling.md:8 +msgid "Uses jemalloc heap profiling for allocation data" msgstr "" -#: src/contributing/event-system.md:361 -msgid "Forwarded to hub" +#: src/contributing/telemetry/profiling.md:9 +msgid "Uses pprof for CPU profiling data" msgstr "" -#: src/contributing/event-system.md:362 -msgid "Use for" +#: src/contributing/telemetry/profiling.md:10 +msgid "Pushes both profile types to Pyroscope" msgstr "" -#: src/contributing/event-system.md:362 -msgid "Close, Focus, Notifications" +#: src/contributing/telemetry/profiling.md:14 +msgid "`profiling` enables profiling support in the codebase" msgstr "" -#: src/contributing/event-system.md:362 -msgid "Submit, child-to-parent signals" +#: src/contributing/telemetry/profiling.md:15 +msgid "`telemetry` enables profiling together with tracing" msgstr "" -#: src/contributing/telemetry.md:3 +#: src/contributing/telemetry/profiling.md:19 msgid "" -"Cadmus supports exporting logs and traces to OpenTelemetry-compatible " -"backends when built with the `otel` feature flag." +"Profiling is initialized during app startup and shut down during app exit. " +"If a Pyroscope endpoint is configured, Cadmus starts collecting profiles " +"right away so early startup work is included." msgstr "" -#: src/contributing/telemetry.md:7 +#: src/contributing/telemetry/profiling.md:23 msgid "" -"The OpenTelemetry (OTEL) integration allows Cadmus to export both " -"**structured logs** and **distributed traces** to observability platforms " -"like Grafana Loki/Tempo, Jaeger, or any OTLP-compatible service. Both logs " -"and traces are first-class features that work together to provide " -"comprehensive observability for monitoring application behavior, debugging " -"issues, and analyzing performance." +"The app configures jemalloc as the global allocator when `profiling` is " +"enabled and enables heap profiling at process startup. CPU profiling runs " +"alongside it through the Pyroscope agent." msgstr "" -#: src/contributing/telemetry.md:12 -msgid "Architecture" +#: src/contributing/telemetry/profiling.md:29 +msgid "" +"See the [settings reference](../../settings/index.md#logging) for the full " +"logging and profiling configuration." msgstr "" -#: src/contributing/telemetry.md:14 -msgid "The telemetry system consists of three main components:" +#: src/contributing/telemetry/profiling.md:33 +msgid "`PYROSCOPE_SERVER_URL`" msgstr "" -#: src/contributing/telemetry.md:16 +#: src/contributing/telemetry/profiling.md:35 msgid "" -"**Logging**: JSON-structured logs written to disk via `tracing_subscriber`" +"`PYROSCOPE_SERVER_URL` overrides `logging.pyroscope-endpoint` when both are " +"set." msgstr "" -#: src/contributing/telemetry.md:17 -msgid "**Tracing**: Distributed traces capturing execution flow and timing" +#: src/contributing/telemetry/profiling.md:38 +msgid "To build Cadmus with profiling support:" msgstr "" -#: src/contributing/telemetry.md:18 -msgid "" -"**OTLP Export**: Optional export of both logs and traces to a remote OTLP " -"endpoint" +#: src/contributing/telemetry/profiling.md:44 +msgid "To build Cadmus with tracing and profiling together:" msgstr "" -#: src/contributing/telemetry.md:20 -msgid "When the `otel` feature is enabled, Cadmus initializes:" +#: src/contributing/telemetry/profiling.md:50 +msgid "Profile types" msgstr "" -#: src/contributing/telemetry.md:22 -msgid "" -"**Tracer Provider**: Exports distributed traces to `/v1/traces` " -"using batch span processors for async delivery" +#: src/contributing/telemetry/profiling.md:52 +msgid "Cadmus currently exports:" +msgstr "" + +#: src/contributing/telemetry/profiling.md:54 +msgid "heap allocation profiles via jemalloc" +msgstr "" + +#: src/contributing/telemetry/profiling.md:55 +msgid "CPU profiles via pprof-rs" +msgstr "" + +#: src/contributing/telemetry/profiling.md:57 +msgid "Both profile streams are pushed to the configured Pyroscope server." msgstr "" -#: src/contributing/telemetry.md:23 +#: src/contributing/telemetry/profiling.md:59 +msgid "Local development" +msgstr "" + +#: src/contributing/telemetry/profiling.md:61 msgid "" -"**Logger Provider**: Exports structured logs to `/v1/logs` using " -"batch log processors" +"The `cadmus-dev-otel` command starts the emulator with profiling enabled and " +"the local Pyroscope service available at ." msgstr "" -#: src/contributing/telemetry.md:25 +#: src/contributing/telemetry/profiling.md:64 msgid "" -"Each Cadmus run is assigned a unique Run ID (UUID v7) that ties together all " -"logs and traces for that session, enabling correlation between trace spans " -"and log events." +"With the full devenv stack running, traces go to Tempo, logs go to Loki, and " +"profiles go to Pyroscope. That makes it possible to correlate a single run " +"across all three observability backends." msgstr "" -#: src/contributing/telemetry.md:28 -msgid "Building with OTEL Support" +#: src/contributing/documentation.md:1 +msgid "Documentation Deployment" msgstr "" -#: src/contributing/telemetry.md:30 -msgid "To enable OpenTelemetry, build Cadmus with the `otel` feature:" +#: src/contributing/documentation.md:3 +msgid "Cadmus documentation is deployed to Cloudflare Pages." msgstr "" -#: src/contributing/telemetry.md:38 -msgid "Configuration" +#: src/contributing/documentation.md:5 +msgid "URLs" msgstr "" -#: src/contributing/telemetry.md:40 -msgid "Settings File" +#: src/contributing/documentation.md:7 +msgid "**Production**: " msgstr "" -#: src/contributing/telemetry.md:42 -msgid "Configure OpenTelemetry in your `Settings.toml`:" +#: src/contributing/documentation.md:8 +msgid "**PR Preview**: `https://pr-{NUMBER}.cadmus-dt6.pages.dev/`" msgstr "" -#: src/contributing/telemetry.md:55 -msgid "Configuration Options" +#: src/contributing/documentation.md:10 +msgid "Reviewing Documentation Changes" msgstr "" -#: src/contributing/telemetry.md:57 -msgid "**enabled**: Enable or disable logging entirely" +#: src/contributing/documentation.md:12 +msgid "" +"When you open a pull request that modifies documentation files, a preview " +"deployment is automatically created. The PR will show a deployment status " +"with a link to the preview URL." msgstr "" -#: src/contributing/telemetry.md:58 +#: src/contributing/documentation.md:14 msgid "" -"**level**: Minimum log level (`trace`, `debug`, `info`, `warn`, `error`)" +"Preview URLs follow the pattern: `https://pr-{NUMBER}.cadmus-dt6.pages.dev/`" msgstr "" -#: src/contributing/telemetry.md:59 -msgid "**max-files**: Number of log files to retain (0 = keep all)" +#: src/contributing/documentation.md:16 +msgid "Local Development" msgstr "" -#: src/contributing/telemetry.md:60 -msgid "" -"**directory**: Path to log directory (relative to installation directory)" +#: src/contributing/documentation.md:18 +msgid "Building and Serving" msgstr "" -#: src/contributing/telemetry.md:61 -msgid "**otlp-endpoint**: OTLP HTTP endpoint URL (optional)" +#: src/contributing/documentation.md:20 +msgid "Build and serve documentation locally:" msgstr "" -#: src/contributing/telemetry.md:63 -msgid "Environment Variables" +#: src/contributing/documentation.md:24 +msgid "# Build all documentation\n" msgstr "" -#: src/contributing/telemetry.md:65 -msgid "You can override the OTLP endpoint using an environment variable:" +#: src/contributing/documentation.md:25 +msgid "# Serve at http://localhost:1111\n" msgstr "" -#: src/contributing/telemetry.md:74 +#: src/contributing/documentation.md:28 msgid "" -"Environment variables take precedence over `Settings.toml` configuration." +"`cargo xtask docs` handles the full pipeline: installing Mermaid assets, " +"building mdBook, generating Rust API docs, and assembling the Zola portal. " +"Pass `--mdbook-only` to skip the Zola step when you only need to check the " +"mdBook output." msgstr "" -#: src/contributing/telemetry.md:76 -msgid "Log Level Control" +#: src/contributing/documentation.md:32 +msgid "To serve with live reload after building:" msgstr "" -#: src/contributing/telemetry.md:78 -msgid "" -"The log level can be controlled via the `RUST_LOG` environment variable, " -"which overrides the `level` setting:" +#: src/contributing/documentation.md:38 +msgid "Build Process" msgstr "" -#: src/contributing/telemetry.md:92 -msgid "Distributed Tracing" +#: src/contributing/documentation.md:40 +msgid "Documentation is built from three sources:" msgstr "" -#: src/contributing/telemetry.md:94 -msgid "" -"Distributed tracing captures the execution flow of operations through the " -"application, providing timing information and context about how different " -"components interact." +#: src/contributing/documentation.md:42 +msgid "**mdBook** (`docs/`) - User and contributor guides" msgstr "" -#: src/contributing/telemetry.md:97 -msgid "How Tracing Works in Cadmus" +#: src/contributing/documentation.md:43 +msgid "**Cargo doc** (`crates/`) - Rust API documentation" msgstr "" -#: src/contributing/telemetry.md:99 +#: src/contributing/documentation.md:44 msgid "" -"When the `otel` feature is enabled, Cadmus automatically instruments key " -"operations using the `tracing` crate. Each instrumented function creates a " -"**span** that records:" +"**Zola** (`docs-portal/`) - Documentation portal that combines everything" msgstr "" -#: src/contributing/telemetry.md:102 -msgid "Function name and module path" +#: src/contributing/documentation.md:46 +msgid "" +"The build is orchestrated by `cargo xtask docs` (see `xtask/src/tasks/docs." +"rs`). The GitHub Actions workflow (`.github/workflows/cadmus-docs.yml`) runs " +"this command automatically on every push to `main` and for every pull " +"request." msgstr "" -#: src/contributing/telemetry.md:103 -msgid "Input parameters (selectively captured)" +#: src/contributing/translations/index.md:3 +msgid "" +"Cadmus has two separate translation systems, each covering a different part " +"of the project." msgstr "" -#: src/contributing/telemetry.md:104 -msgid "Execution duration" +#: src/contributing/translations/index.md:6 +msgid "What" msgstr "" -#: src/contributing/telemetry.md:105 -msgid "Return values (at TRACE level)" +#: src/contributing/translations/index.md:6 +msgid "System" msgstr "" -#: src/contributing/telemetry.md:106 -msgid "Hierarchical relationships between spans" +#: src/contributing/translations/index.md:6 +msgid "Files" msgstr "" -#: src/contributing/telemetry.md:108 -msgid "" -"Spans are organized hierarchically, showing which operations triggered which " -"other operations, making it easier to understand execution flow and identify " -"performance bottlenecks." +#: src/contributing/translations/index.md:8 +msgid "[Documentation](docs.md)" msgstr "" -#: src/contributing/telemetry.md:111 -msgid "Instrumentation" +#: src/contributing/translations/index.md:8 +msgid "GNU gettext / PO" msgstr "" -#: src/contributing/telemetry.md:113 -msgid "View components in Cadmus are instrumented at critical chokepoints:" +#: src/contributing/translations/index.md:8 +msgid "`docs/po/*.po`" msgstr "" -#: src/contributing/telemetry.md:115 -msgid "" -"**`handle_event` methods**: Capture event flow through the UI hierarchy with " -"event type and return value" +#: src/contributing/translations/index.md:9 +msgid "[UI strings](source-code.md)" msgstr "" -#: src/contributing/telemetry.md:116 -msgid "" -"**`render` methods**: Capture rendering operations with rectangle dimensions " -"for layout debugging" +#: src/contributing/translations/index.md:9 +msgid "Fluent (FTL)" msgstr "" -#: src/contributing/telemetry.md:118 -msgid "" -"All instrumentation uses conditional compilation (`#[cfg_attr(feature = " -"\"otel\", ...)]`) to ensure zero runtime overhead when the feature is " -"disabled." +#: src/contributing/translations/index.md:9 +msgid "`crates/core/i18n/**/*.ftl`" msgstr "" -#: src/contributing/telemetry.md:121 -msgid "" -"For detailed instrumentation guidelines and examples, see `.github/" -"instructions/rust-instrumentation.instructions.md`." +#: src/contributing/translations/index.md:11 +msgid "Pick the guide that matches what you want to translate." msgstr "" -#: src/contributing/telemetry.md:123 -msgid "Resource Attributes" +#: src/contributing/translations/source-code.md:1 +msgid "Translating Source Strings" msgstr "" -#: src/contributing/telemetry.md:125 +#: src/contributing/translations/source-code.md:3 msgid "" -"Each telemetry export (both logs and traces) includes the following resource " -"attributes:" +"The Cadmus UI uses [Fluent](https://projectfluent.org) for all user-visible " +"strings. Translations are embedded directly into the binary at compile time " +"— no external files are needed on the device." msgstr "" -#: src/contributing/telemetry.md:127 -msgid "**service.name**: Always `cadmus`" +#: src/contributing/translations/source-code.md:7 +msgid "How it works" msgstr "" -#: src/contributing/telemetry.md:128 -msgid "**service.version**: Git version from build metadata" +#: src/contributing/translations/source-code.md:9 +msgid "FTL files live under `crates/core/i18n//cadmus_core.ftl`." msgstr "" -#: src/contributing/telemetry.md:129 -msgid "**cadmus.run_id**: Unique identifier for the application run" +#: src/contributing/translations/source-code.md:10 +msgid "" +"The fallback language is **en-GB**; any string missing from a translation " +"falls back to the English text automatically." msgstr "" -#: src/contributing/telemetry.md:130 -msgid "**hostname**: System hostname" +#: src/contributing/translations/source-code.md:12 +msgid "" +"`crates/core/src/i18n.rs` loads the correct language at startup based on " +"`settings.locale`." msgstr "" -#: src/contributing/telemetry.md:132 -msgid "Log File Format" +#: src/contributing/translations/source-code.md:14 +msgid "" +"The `fl!(\"message-id\")` macro resolves message IDs at **compile time**." msgstr "" -#: src/contributing/telemetry.md:134 -msgid "Logs are written as newline-delimited JSON to files named:" +#: src/contributing/translations/source-code.md:16 +#: src/contributing/translations/docs.md:18 +msgid "Adding a new language" msgstr "" -#: src/contributing/telemetry.md:142 -msgid "Each log entry includes:" +#: src/contributing/translations/source-code.md:18 +msgid "1. Create the FTL file" msgstr "" -#: src/contributing/telemetry.md:144 -msgid "**timestamp**: ISO 8601 formatted timestamp" +#: src/contributing/translations/source-code.md:20 +msgid "" +"Create a new file at the path matching the BCP 47 tag for your language:" msgstr "" -#: src/contributing/telemetry.md:145 -msgid "**level**: Log level (TRACE, DEBUG, INFO, WARN, ERROR)" +#: src/contributing/translations/source-code.md:26 +msgid "For example, for French:" msgstr "" -#: src/contributing/telemetry.md:146 -msgid "**target**: Module path where the log originated" +#: src/contributing/translations/source-code.md:32 +msgid "2. Copy and translate the English strings" msgstr "" -#: src/contributing/telemetry.md:147 -msgid "**fields**: Structured log data" +#: src/contributing/translations/source-code.md:34 +msgid "Use the English fallback file as your starting point:" msgstr "" -#: src/contributing/telemetry.md:148 -msgid "**spans**: Active tracing spans providing context" +#: src/contributing/translations/source-code.md:40 +msgid "" +"Translate each message value. The message ID (left of `=`) must stay " +"unchanged — only the value (right of `=`) changes:" msgstr "" -#: src/contributing/documentation.md:1 -msgid "Documentation Deployment" +#: src/contributing/translations/source-code.md:51 +msgid "3. Set the locale in Settings" msgstr "" -#: src/contributing/documentation.md:3 -msgid "Cadmus documentation is deployed to Cloudflare Pages." +#: src/contributing/translations/source-code.md:53 +msgid "" +"To activate the new language locally during development, add a `locale` key " +"to your `Settings.toml`:" msgstr "" -#: src/contributing/documentation.md:5 -msgid "URLs" +#: src/contributing/translations/source-code.md:56 +msgid "" +"```toml\n" +"locale = \"fr\"\n" +"```" msgstr "" -#: src/contributing/documentation.md:13 -msgid "Reviewing Documentation Changes" +#: src/contributing/translations/source-code.md:60 +msgid "4. Build and verify" msgstr "" -#: src/contributing/documentation.md:15 +#: src/contributing/translations/source-code.md:66 msgid "" -"When you open a pull request that modifies documentation files, a preview " -"deployment is automatically created. The PR will show a deployment status " -"with a link to the preview URL." +"The `fl!()` macro validates all message IDs at compile time. A successful " +"build confirms the FTL file is well-formed and all IDs referenced in code " +"are present." +msgstr "" + +#: src/contributing/translations/source-code.md:69 +msgid "Adding a string to the source code" msgstr "" -#: src/contributing/documentation.md:17 +#: src/contributing/translations/source-code.md:71 +msgid "When you add a new UI string:" +msgstr "" + +#: src/contributing/translations/source-code.md:73 msgid "" -"Preview URLs follow the pattern: `https://pr-{NUMBER}.cadmus-dt6.pages.dev/`" +"Add the message to `en-GB`, and any other languages you know how to " +"translate it into." msgstr "" -#: src/contributing/documentation.md:19 -msgid "Local Development" +#: src/contributing/translations/source-code.md:75 +msgid "Use the `fl!()` macro in the Rust source:" msgstr "" -#: src/contributing/documentation.md:21 -msgid "Building and Serving" +#: src/contributing/translations/source-code.md:78 +msgid "\"my-new-message\"" msgstr "" -#: src/contributing/documentation.md:23 -msgid "Build and serve documentation locally:" +#: src/contributing/translations/source-code.md:81 +msgid "For messages with variables, use named arguments:" msgstr "" -#: src/contributing/documentation.md:32 -msgid "" -"`cargo xtask docs` handles the full pipeline: installing Mermaid assets, " -"building mdBook, generating Rust API docs, and assembling the Zola portal. " -"Pass `--mdbook-only` to skip the Zola step when you only need to check the " -"mdBook output." +#: src/contributing/translations/source-code.md:89 +msgid "\"books-loaded\"" msgstr "" -#: src/contributing/documentation.md:36 -msgid "To serve with live reload after building:" +#: src/contributing/translations/source-code.md:92 +msgid "FTL file format" msgstr "" -#: src/contributing/documentation.md:43 -msgid "Build Process" +#: src/contributing/translations/source-code.md:94 +msgid "Fluent uses a straightforward syntax. A few rules to keep in mind:" msgstr "" -#: src/contributing/documentation.md:45 -msgid "Documentation is built from three sources:" +#: src/contributing/translations/source-code.md:96 +msgid "Message IDs use `kebab-case`." msgstr "" -#: src/contributing/documentation.md:47 -msgid "**mdBook** (`docs/`) - User and contributor guides" +#: src/contributing/translations/source-code.md:97 +msgid "Values can span multiple lines by indenting continuation lines." msgstr "" -#: src/contributing/documentation.md:48 -msgid "**Cargo doc** (`crates/`) - Rust API documentation" +#: src/contributing/translations/source-code.md:98 +msgid "Use Unicode characters directly — no escaping needed." msgstr "" -#: src/contributing/documentation.md:49 -msgid "" -"**Zola** (`docs-portal/`) - Documentation portal that combines everything" +#: src/contributing/translations/source-code.md:99 +msgid "Comments start with `#`." msgstr "" -#: src/contributing/documentation.md:51 +#: src/contributing/translations/source-code.md:101 msgid "" -"The build is orchestrated by `cargo xtask docs` (see `xtask/src/tasks/" -"docs.rs`). The GitHub Actions workflow (`.github/workflows/cadmus-docs.yml`) " -"runs this command automatically on every push to `main` and for every pull " -"request." +"For the full syntax reference see the [Fluent syntax guide](https://" +"projectfluent.org/fluent/guide/)." msgstr "" -#: src/contributing/translations.md:1 +#: src/contributing/translations/docs.md:1 msgid "Translating Cadmus Documentation" msgstr "" -#: src/contributing/translations.md:3 +#: src/contributing/translations/docs.md:3 msgid "" "This guide explains how to translate the Cadmus documentation into other " "languages. Translations live in `docs/po/` as standard GNU gettext PO files." msgstr "" -#: src/contributing/translations.md:8 +#: src/contributing/translations/docs.md:8 msgid "" "If you are using the `devenv` environment, all required tools are already " "available:" msgstr "" -#: src/contributing/translations.md:11 +#: src/contributing/translations/docs.md:11 msgid "" "`mdbook-xgettext` / `mdbook-gettext` — string extraction and preprocessing" msgstr "" -#: src/contributing/translations.md:12 +#: src/contributing/translations/docs.md:12 msgid "`msginit` / `msgmerge` / `msgfmt` — gettext utilities (from `gettext`)" msgstr "" -#: src/contributing/translations.md:13 +#: src/contributing/translations/docs.md:13 msgid "`poedit` — graphical PO editor" msgstr "" -#: src/contributing/translations.md:15 +#: src/contributing/translations/docs.md:15 msgid "" "Install them outside devenv with `cargo install mdbook-i18n-helpers` and " "your system's `gettext` package." msgstr "" -#: src/contributing/translations.md:18 -msgid "Adding a new language" -msgstr "" - -#: src/contributing/translations.md:20 +#: src/contributing/translations/docs.md:20 msgid "1. Extract the POT template" msgstr "" -#: src/contributing/translations.md:22 +#: src/contributing/translations/docs.md:22 msgid "Run the `cadmus-translate` script (devenv) or the equivalent command:" msgstr "" -#: src/contributing/translations.md:32 +#: src/contributing/translations/docs.md:28 msgid "" "This writes `docs/po/messages.pot` — the source template every translation " "derives from. Commit this file whenever the English source changes so " "translators have an up-to-date starting point." msgstr "" -#: src/contributing/translations.md:36 +#: src/contributing/translations/docs.md:32 msgid "2. Create a PO file for your locale" msgstr "" -#: src/contributing/translations.md:47 +#: src/contributing/translations/docs.md:35 +msgid "# Replace 'fr' with the BCP 47 language tag you are adding.\n" +msgstr "" + +#: src/contributing/translations/docs.md:41 msgid "" "Open `docs/po/fr.po` and set the `Language-Name` header so the language " "picker displays a readable label:" msgstr "" -#: src/contributing/translations.md:56 +#: src/contributing/translations/docs.md:44 +msgid "" +"```po\n" +"\"Language-Name: Français\\n\"\n" +"```" +msgstr "" + +#: src/contributing/translations/docs.md:48 msgid "" "The xtask reads this header when generating `locales.json`; without it the " "locale code (e.g. `fr`) is shown instead." msgstr "" -#: src/contributing/translations.md:59 +#: src/contributing/translations/docs.md:51 msgid "3. Translate the strings" msgstr "" -#: src/contributing/translations.md:61 +#: src/contributing/translations/docs.md:53 msgid "" "Open the PO file in Poedit or any text editor and fill in each `msgstr`:" msgstr "" -#: src/contributing/translations.md:70 +#: src/contributing/translations/docs.md:55 +msgid "" +"```po\n" +"msgid \"Welcome to Cadmus!\"\n" +"msgstr \"Bienvenue dans Cadmus !\"\n" +"```" +msgstr "" + +#: src/contributing/translations/docs.md:60 msgid "" "Preserve Markdown formatting — bold, code spans, links — exactly as in the " "`msgid`. Untranslated or _fuzzy_ entries fall back to the English source." msgstr "" -#: src/contributing/translations.md:73 -msgid "Check your progress with:" +#: src/contributing/translations/docs.md:63 +msgid "4. Build and preview" msgstr "" -#: src/contributing/translations.md:81 -msgid "4. Build and preview" +#: src/contributing/translations/docs.md:66 +msgid "# Build everything including translated books\n" msgstr "" -#: src/contributing/translations.md:94 +#: src/contributing/translations/docs.md:68 +msgid "# Serve\n" +msgstr "" + +#: src/contributing/translations/docs.md:74 msgid "" -"Navigate to `http://localhost:3000` and use the language picker in the " -"sidebar to switch to your locale." +"Navigate to `http://localhost:1111/guide/` and use the language picker in " +"the sidebar to switch to your locale." msgstr "" -#: src/contributing/translations.md:97 +#: src/contributing/translations/docs.md:77 msgid "Keeping translations up to date" msgstr "" -#: src/contributing/translations.md:99 +#: src/contributing/translations/docs.md:79 msgid "" "When English source files change, regenerate the template and merge new " "strings into existing PO files:" msgstr "" -#: src/contributing/translations.md:110 -msgid "" -"`msgmerge` marks changed strings as _fuzzy_ so translators know they need " -"review. Commit both the updated `.pot` and the merged `.po` together." -msgstr "" - -#: src/contributing/translations.md:113 -msgid "" -"\\[!IMPORTANT\\] CI fails if `docs/po/messages.pot` is out of date with the " -"English source. Always run `cadmus-translate` and commit the result when " -"editing docs." +#: src/contributing/translations/docs.md:83 +msgid "# regenerate docs/po/messages.pot\n" msgstr "" -#: src/contributing/translations.md:117 +#: src/contributing/translations/docs.md:87 msgid "Excluding content from extraction" msgstr "" -#: src/contributing/translations.md:119 +#: src/contributing/translations/docs.md:89 msgid "" "Wrap any block you want to keep in English with `` " "comments:" msgstr "" -#: src/contributing/translations.md:131 +#: src/contributing/translations/docs.md:99 msgid "How the build works" msgstr "" -#: src/contributing/translations.md:133 +#: src/contributing/translations/docs.md:101 msgid "" "`cargo xtask docs` calls `mdbook build -d book/` for each `.po` file " "found in `docs/po/`, passing `MDBOOK_BOOK__LANGUAGE=`." msgstr "" -#: src/contributing/translations.md:135 +#: src/contributing/translations/docs.md:103 msgid "" "The `[preprocessor.gettext]` in `docs/book.toml` substitutes translated " "strings at build time." msgstr "" -#: src/contributing/translations.md:137 +#: src/contributing/translations/docs.md:105 msgid "" "`locales.json` is written to `docs/book/html/` with the available locales; " "`lang-picker.js` fetches it at runtime to populate the language dropdown." msgstr "" -#: src/contributing/translations.md:139 +#: src/contributing/translations/docs.md:107 msgid "" "Symlinks under `docs-portal/static/guide//` expose each locale build " "to Zola so it is served at `/guide//`." msgstr "" -#: src/contributing/sqlite-sqlx.md:4 +#: src/contributing/sqlite-sqlx.md:3 msgid "" "Cadmus uses [SQLite](https://sqlite.org) as its embedded database and [SQLx]" "(https://github.com/launchbadge/sqlx) as the Rust database library. SQLx " @@ -2739,11 +6119,11 @@ msgid "" "the real schema before the code ships." msgstr "" -#: src/contributing/sqlite-sqlx.md:9 +#: src/contributing/sqlite-sqlx.md:8 msgid "The `.sqlx` directory" msgstr "" -#: src/contributing/sqlite-sqlx.md:11 +#: src/contributing/sqlite-sqlx.md:10 msgid "" "The `.sqlx/` directory at the repository root contains one JSON metadata " "file per SQL query. Each file stores the resolved column names, types, and " @@ -2751,145 +6131,161 @@ msgid "" "`cargo sqlx prepare` was last run." msgstr "" -#: src/contributing/sqlite-sqlx.md:25 +#: src/contributing/sqlite-sqlx.md:22 msgid "Regenerating query metadata" msgstr "" -#: src/contributing/sqlite-sqlx.md:27 +#: src/contributing/sqlite-sqlx.md:24 msgid "After adding or changing any SQL query, regenerate the metadata:" msgstr "" -#: src/contributing/sqlite-sqlx.md:35 +#: src/contributing/sqlite-sqlx.md:30 msgid "" "This connects to the database, re-introspects every query macro in the " "workspace, and rewrites the `.sqlx/` JSON files. Commit the updated files " "alongside your code change." msgstr "" -#: src/contributing/sqlite-sqlx.md:39 +#: src/contributing/sqlite-sqlx.md:34 msgid "" "\\[!IMPORTANT\\] If you forget to run `cargo sqlx prepare`, the CI `check` " "job will fail because the cached metadata will be out of date with your " "query changes." msgstr "" -#: src/contributing/sqlite-sqlx.md:43 +#: src/contributing/sqlite-sqlx.md:38 msgid "Compile-time SQL checking" msgstr "" -#: src/contributing/sqlite-sqlx.md:45 +#: src/contributing/sqlite-sqlx.md:40 msgid "" "SQLx's typed query macros (`query!`, `query_as!`, `query_scalar!`) verify " "SQL at compile time using the metadata in `.sqlx/`. This means:" msgstr "" -#: src/contributing/sqlite-sqlx.md:48 +#: src/contributing/sqlite-sqlx.md:43 msgid "Typos in column names are **compiler errors**, not runtime panics." msgstr "" -#: src/contributing/sqlite-sqlx.md:49 +#: src/contributing/sqlite-sqlx.md:44 msgid "Binding the wrong type to a `?` placeholder is a **type error**." msgstr "" -#: src/contributing/sqlite-sqlx.md:50 +#: src/contributing/sqlite-sqlx.md:45 msgid "" "Adding or removing a column in a migration without updating queries is " "caught **before deployment**." msgstr "" -#: src/contributing/sqlite-sqlx.md:53 +#: src/contributing/sqlite-sqlx.md:48 msgid "" "The macros require the `DATABASE_URL` environment variable to point at a " "live database when running `cargo sqlx prepare`, but **not** during regular " "`cargo build` or `cargo check` — those use the pre-generated `.sqlx/` files." msgstr "" -#: src/contributing/sqlite-sqlx.md:57 +#: src/contributing/sqlite-sqlx.md:52 msgid "" "\\[!IMPORTANT\\] `.sqlx/` is only used when the `SQLX_OFFLINE=true` field is " "set which is the default if you're using devenv.nix." msgstr "" -#: src/contributing/sqlite-sqlx.md:61 +#: src/contributing/sqlite-sqlx.md:56 msgid "Review rules" msgstr "" -#: src/contributing/sqlite-sqlx.md:63 +#: src/contributing/sqlite-sqlx.md:58 msgid "" "The following rules are enforced during code review for all SQLx queries." msgstr "" -#: src/contributing/sqlite-sqlx.md:65 +#: src/contributing/sqlite-sqlx.md:60 msgid "Use typed macros only" msgstr "" -#: src/contributing/sqlite-sqlx.md:67 +#: src/contributing/sqlite-sqlx.md:62 msgid "" "Always use the typed macros. Never call the untyped `query()`, `query_as()`, " "or `query_scalar()` functions:" msgstr "" -#: src/contributing/sqlite-sqlx.md:70 +#: src/contributing/sqlite-sqlx.md:65 msgid "Goal" msgstr "" -#: src/contributing/sqlite-sqlx.md:70 +#: src/contributing/sqlite-sqlx.md:65 msgid "Use" msgstr "" -#: src/contributing/sqlite-sqlx.md:72 +#: src/contributing/sqlite-sqlx.md:67 msgid "`INSERT`, `UPDATE`, `DELETE`, raw `SELECT`" msgstr "" -#: src/contributing/sqlite-sqlx.md:72 +#: src/contributing/sqlite-sqlx.md:67 msgid "`sqlx::query!`" msgstr "" -#: src/contributing/sqlite-sqlx.md:73 +#: src/contributing/sqlite-sqlx.md:68 msgid "`SELECT` mapped into a named struct" msgstr "" -#: src/contributing/sqlite-sqlx.md:73 +#: src/contributing/sqlite-sqlx.md:68 msgid "`sqlx::query_as!`" msgstr "" -#: src/contributing/sqlite-sqlx.md:74 +#: src/contributing/sqlite-sqlx.md:69 msgid "Single-column `SELECT`" msgstr "" -#: src/contributing/sqlite-sqlx.md:74 +#: src/contributing/sqlite-sqlx.md:69 msgid "`sqlx::query_scalar!`" msgstr "" -#: src/contributing/sqlite-sqlx.md:76 +#: src/contributing/sqlite-sqlx.md:71 msgid "" "When the column is nullable, call `.flatten()` on the result to collapse " "`Option>` into `Option`:" msgstr "" -#: src/contributing/sqlite-sqlx.md:89 +#: src/contributing/sqlite-sqlx.md:76 +msgid "\"SELECT id FROM libraries WHERE path = ?\"" +msgstr "" + +#: src/contributing/sqlite-sqlx.md:82 msgid "List explicit column names" msgstr "" -#: src/contributing/sqlite-sqlx.md:91 +#: src/contributing/sqlite-sqlx.md:84 msgid "Never use `SELECT *`. Always name every column you need:" msgstr "" -#: src/contributing/sqlite-sqlx.md:103 +#: src/contributing/sqlite-sqlx.md:87 src/contributing/sqlite-sqlx.md:100 +msgid "-- ✅ Good\n" +msgstr "" + +#: src/contributing/sqlite-sqlx.md:89 src/contributing/sqlite-sqlx.md:102 +msgid "-- ❌ Bad\n" +msgstr "" + +#: src/contributing/sqlite-sqlx.md:94 msgid "Store timestamps as Unix epoch integers" msgstr "" -#: src/contributing/sqlite-sqlx.md:105 +#: src/contributing/sqlite-sqlx.md:96 msgid "" "All date/time values must be stored as `INTEGER NOT NULL` (Unix epoch " "seconds). Do not use `TEXT` columns for timestamps:" msgstr "" -#: src/contributing/sqlite-sqlx.md:118 +#: src/contributing/sqlite-sqlx.md:104 +msgid "'now'" +msgstr "" + +#: src/contributing/sqlite-sqlx.md:107 msgid "Add only indexes that are actively used" msgstr "" -#: src/contributing/sqlite-sqlx.md:120 +#: src/contributing/sqlite-sqlx.md:109 msgid "" "Every index must be used by at least one query in the codebase. Unused " "indexes waste write performance and storage without any read benefit. Before " @@ -2897,35 +6293,35 @@ msgid "" "column(s)." msgstr "" -#: src/contributing/sqlite-sqlx.md:124 -#: src/contributing/runtime-migrations.md:176 +#: src/contributing/sqlite-sqlx.md:113 +#: src/contributing/runtime-migrations.md:164 msgid "API reference" msgstr "" -#: src/contributing/sqlite-sqlx.md:126 +#: src/contributing/sqlite-sqlx.md:115 msgid "The primary database types live in the `cadmus_core::db` module:" msgstr "" -#: src/contributing/sqlite-sqlx.md:128 +#: src/contributing/sqlite-sqlx.md:117 msgid "" "`cadmus_core::db::Database` — the top-level sync/async bridge that owns the connection pool" msgstr "" -#: src/contributing/sqlite-sqlx.md:130 +#: src/contributing/sqlite-sqlx.md:119 msgid "" -"`cadmus_core::db::migrations::MigrationRunner` " -"— executes all pending runtime migrations" +"`cadmus_core::db::migrations::MigrationRunner` — " +"executes all pending runtime migrations" msgstr "" -#: src/contributing/sqlite-sqlx.md:132 +#: src/contributing/sqlite-sqlx.md:121 msgid "" "`cadmus_core::migration!` — " "macro for declaring one-time runtime migrations" msgstr "" -#: src/contributing/sqlite-sqlx.md:135 +#: src/contributing/sqlite-sqlx.md:124 msgid "" "See [Library Database](library-database.md) for how the library subsystem " "uses the database, and [Runtime Migrations](runtime-migrations.md) for how " @@ -2950,37 +6346,129 @@ msgid "" "aggregating view:" msgstr "" -#: src/contributing/library-database.md:100 -msgid "Key design choices" +#: src/contributing/library-database.md:13 +msgid "" +"```mermaid\n" +"erDiagram\n" +" books {\n" +" TEXT fingerprint PK\n" +" TEXT title\n" +" TEXT file_path\n" +" TEXT file_kind\n" +" INTEGER file_size\n" +" INTEGER added_at\n" +" }\n" +"\n" +" authors {\n" +" INTEGER id PK\n" +" TEXT name\n" +" }\n" +"\n" +" book_authors {\n" +" TEXT book_fingerprint FK\n" +" INTEGER author_id FK\n" +" INTEGER position\n" +" }\n" +"\n" +" categories {\n" +" INTEGER id PK\n" +" TEXT name\n" +" }\n" +"\n" +" book_categories {\n" +" TEXT book_fingerprint FK\n" +" INTEGER category_id FK\n" +" }\n" +"\n" +" reading_states {\n" +" TEXT fingerprint PK\n" +" INTEGER opened\n" +" INTEGER current_page\n" +" INTEGER pages_count\n" +" INTEGER finished\n" +" }\n" +"\n" +" thumbnails {\n" +" TEXT fingerprint PK\n" +" BLOB thumbnail_data\n" +" }\n" +"\n" +" toc_entries {\n" +" TEXT id PK\n" +" TEXT book_fingerprint FK\n" +" TEXT parent_id FK\n" +" INTEGER position\n" +" TEXT title\n" +" TEXT location_kind\n" +" }\n" +"\n" +" libraries {\n" +" INTEGER id PK\n" +" TEXT path\n" +" TEXT name\n" +" INTEGER created_at\n" +" }\n" +"\n" +" library_books {\n" +" INTEGER library_id FK\n" +" TEXT book_fingerprint FK\n" +" INTEGER added_to_library_at\n" +" TEXT file_path\n" +" TEXT absolute_path\n" +" INTEGER mtime\n" +" INTEGER file_size\n" +" }\n" +"\n" +" _cadmus_migrations {\n" +" TEXT id PK\n" +" INTEGER executed_at\n" +" TEXT status\n" +" }\n" +"\n" +" books ||--o{ book_authors : \"\"\n" +" authors ||--o{ book_authors : \"\"\n" +" books ||--o{ book_categories : \"\"\n" +" categories ||--o{ book_categories : \"\"\n" +" books ||--o| reading_states : \"\"\n" +" books ||--o| thumbnails : \"\"\n" +" books ||--o{ toc_entries : \"\"\n" +" toc_entries ||--o{ toc_entries : \"parent_id\"\n" +" libraries ||--o{ library_books : \"\"\n" +" books ||--o{ library_books : \"\"\n" +"```" msgstr "" #: src/contributing/library-database.md:102 +msgid "Key design choices" +msgstr "" + +#: src/contributing/library-database.md:104 msgid "" -"**`books` is the main table.** Every other per-book table references " -"`books.fingerprint` with `ON DELETE CASCADE`, so deleting a book row removes " -"all associated data automatically." +"**`books` is the main table.** Every other per-book table references `books." +"fingerprint` with `ON DELETE CASCADE`, so deleting a book row removes all " +"associated data automatically." msgstr "" -#: src/contributing/library-database.md:105 +#: src/contributing/library-database.md:107 msgid "" "**Authors are normalised.** `authors` holds unique author names; " "`book_authors` is the join table and carries a `position` column that " "preserves display order." msgstr "" -#: src/contributing/library-database.md:108 +#: src/contributing/library-database.md:110 msgid "" "**All tables use `STRICT` mode.** SQLite's `STRICT` pragma enforces column " "type constraints at the storage layer, catching type mismatches early." msgstr "" -#: src/contributing/library-database.md:110 +#: src/contributing/library-database.md:112 msgid "" "**Timestamps are Unix epoch integers.** `added_at`, `created_at`, and " "similar columns are `INTEGER NOT NULL`; never `TEXT`." msgstr "" -#: src/contributing/library-database.md:112 +#: src/contributing/library-database.md:114 msgid "" "**TOC tree via adjacency list.** `toc_entries.parent_id` is a self-" "reference; `position` preserves sibling order. The `id` is a UUID7 " @@ -2988,7 +6476,18 @@ msgid "" "without a growing rowid." msgstr "" -#: src/contributing/library-database.md:115 +#: src/contributing/library-database.md:117 +msgid "" +"**Incremental import via `mtime` and `file_size`.** The `library_books` " +"table stores the file's last modification time (ceiling-rounded to 2-second " +"FAT32 precision) and size in bytes. During import scans, files whose `mtime` " +"and `file_size` have not changed are skipped without re-fingerprinting, " +"improving startup performance. An index on `(library_id, absolute_path)` " +"enables efficient per-path lookups. Both columns are nullable to support " +"books imported before migration 011." +msgstr "" + +#: src/contributing/library-database.md:124 msgid "" "**`library_books_full_info` view.** An aggregating view joins `books`, " "`reading_states`, `book_authors`, `authors`, `book_categories`, and " @@ -2996,20 +6495,38 @@ msgid "" "exposed so callers can filter with a plain `WHERE library_id = ?`." msgstr "" -#: src/contributing/library-database.md:120 +#: src/contributing/library-database.md:129 msgid "Data access layer" msgstr "" -#: src/contributing/library-database.md:122 +#: src/contributing/library-database.md:131 +msgid "" +"The `cadmus_core::library::" +"db::Db` struct is the entry point for all library database operations. " +"It wraps the shared `SqlitePool` and exposes a **synchronous** API by " +"bridging every async SQLx call through the global Tokio runtime:" +msgstr "" + +#: src/contributing/library-database.md:137 msgid "" -"The `cadmus_core::library::db::Db` struct is the entry point for " -"all library database operations. It wraps the shared `SqlitePool` and " -"exposes a **synchronous** API by bridging every async SQLx call through the " -"global Tokio runtime:" +"```mermaid\n" +"flowchart LR\n" +" caller[\"Caller (sync event loop)\"]\n" +" Db[\"library::db::Db\"]\n" +" RUNTIME[\"RUNTIME.block_on()\"]\n" +" SQLx[\"SQLx async query\"]\n" +" SQLite[(\"SQLite\")]\n" +"\n" +" caller -->|sync call| Db\n" +" Db -->|wraps in| RUNTIME\n" +" RUNTIME -->|awaits| SQLx\n" +" SQLx -->|reads/writes| SQLite\n" +" SQLx -->|result| RUNTIME\n" +" RUNTIME -->|returns| caller\n" +"```" msgstr "" -#: src/contributing/library-database.md:146 +#: src/contributing/library-database.md:153 msgid "" "The sync bridge exists because Cadmus's UI event loop is single-threaded and " "synchronous. The global `RUNTIME` (a `tokio::runtime::Runtime` singleton) " @@ -3017,109 +6534,109 @@ msgid "" "async." msgstr "" -#: src/contributing/library-database.md:150 +#: src/contributing/library-database.md:157 msgid "Key methods on `Db`:" msgstr "" -#: src/contributing/library-database.md:154 +#: src/contributing/library-database.md:161 msgid "" -"`register_library`" +"`register_library`" msgstr "" -#: src/contributing/library-database.md:154 +#: src/contributing/library-database.md:161 msgid "Insert a new library row and return its id" msgstr "" -#: src/contributing/library-database.md:155 +#: src/contributing/library-database.md:162 msgid "" -"`get_library_by_path`" +"`get_library_by_path`" msgstr "" -#: src/contributing/library-database.md:155 +#: src/contributing/library-database.md:162 msgid "Look up a library id by filesystem path" msgstr "" -#: src/contributing/library-database.md:156 +#: src/contributing/library-database.md:163 msgid "" -"`get_all_books`" +"`get_all_books`" msgstr "" -#: src/contributing/library-database.md:156 +#: src/contributing/library-database.md:163 msgid "Fetch every book in a library via the full-info view" msgstr "" -#: src/contributing/library-database.md:157 +#: src/contributing/library-database.md:164 msgid "" -"`insert_book`" +"`insert_book`" msgstr "" -#: src/contributing/library-database.md:157 +#: src/contributing/library-database.md:164 msgid "Write a new book and its authors/categories" msgstr "" -#: src/contributing/library-database.md:158 +#: src/contributing/library-database.md:165 msgid "" -"`save_reading_state`" +"`save_reading_state`" msgstr "" -#: src/contributing/library-database.md:158 +#: src/contributing/library-database.md:165 msgid "Save or update reading progress for a book" msgstr "" -#: src/contributing/library-database.md:159 +#: src/contributing/library-database.md:166 msgid "" -"`save_toc`" +"`save_toc`" msgstr "" -#: src/contributing/library-database.md:159 +#: src/contributing/library-database.md:166 msgid "Bulk-write a book's table of contents" msgstr "" -#: src/contributing/library-database.md:160 +#: src/contributing/library-database.md:167 msgid "" -"`get_thumbnail`" +"`get_thumbnail`" msgstr "" -#: src/contributing/library-database.md:160 +#: src/contributing/library-database.md:167 msgid "Retrieve the stored cover thumbnail BLOB" msgstr "" -#: src/contributing/library-database.md:161 +#: src/contributing/library-database.md:168 msgid "" -"`save_thumbnail`" +"`save_thumbnail`" msgstr "" -#: src/contributing/library-database.md:161 +#: src/contributing/library-database.md:168 msgid "Save or replace a cover thumbnail" msgstr "" -#: src/contributing/library-database.md:163 +#: src/contributing/library-database.md:170 msgid "How a book scan flows into the database" msgstr "" -#: src/contributing/library-database.md:165 +#: src/contributing/library-database.md:172 msgid "When a library directory is scanned, Cadmus follows this sequence:" msgstr "" -#: src/contributing/library-database.md:198 +#: src/contributing/library-database.md:203 msgid "Related pages" msgstr "" -#: src/contributing/library-database.md:200 +#: src/contributing/library-database.md:205 msgid "" "[SQLite & SQLx](sqlite-sqlx.md) — compile-time query verification, review " "rules" msgstr "" -#: src/contributing/library-database.md:201 +#: src/contributing/library-database.md:206 msgid "" "[Runtime Migrations](runtime-migrations.md) — one-time data migrations using " "the `migration!` macro" @@ -3148,82 +6665,115 @@ msgstr "" msgid "How runtime migrations work" msgstr "" -#: src/contributing/runtime-migrations.md:38 +#: src/contributing/runtime-migrations.md:15 +msgid "" +"```mermaid\n" +"flowchart TD\n" +" ctor[\"#[ctor] runs at process start\"]\n" +" registry[\"Global REGISTRY HashMap
(migration id → async fn)\"]\n" +" startup[\"Database::migrate() called on startup\"]\n" +" schema[\"sqlx::migrate!() — applies .sql files\"]\n" +" runner[\"MigrationRunner::run_all()\"]\n" +" table[\"_cadmus_migrations table
(id, executed_at, status)\"]\n" +" pending[\"Filter: id NOT IN already-succeeded rows\"]\n" +" exec[\"Execute each pending migration in id order\"]\n" +" record[\"Record success or failure in _cadmus_migrations\"]\n" +"\n" +" ctor --> registry\n" +" startup --> schema\n" +" schema --> runner\n" +" runner --> table\n" +" table --> pending\n" +" pending --> exec\n" +" exec --> record\n" +"```" +msgstr "" + +#: src/contributing/runtime-migrations.md:36 msgid "" "At process start the `#[ctor]` attribute runs for every `migration!` call " -"and inserts the migration function into a global `HashMap`. When " -"`Database::migrate` is called during application startup, it first applies " -"all pending SQL schema migrations, then calls `MigrationRunner::run_all()`, " -"which:" +"and inserts the migration function into a global `HashMap`. When `Database::" +"migrate` is called during application startup, it first applies all pending " +"SQL schema migrations, then calls `MigrationRunner::run_all()`, which:" msgstr "" -#: src/contributing/runtime-migrations.md:43 +#: src/contributing/runtime-migrations.md:41 msgid "Reads `_cadmus_migrations` and collects IDs that already succeeded." msgstr "" -#: src/contributing/runtime-migrations.md:44 +#: src/contributing/runtime-migrations.md:42 msgid "Skips those; runs the remaining ones sorted by ID." msgstr "" -#: src/contributing/runtime-migrations.md:45 +#: src/contributing/runtime-migrations.md:43 msgid "Records each result (`success` or `failed`) before moving on." msgstr "" -#: src/contributing/runtime-migrations.md:46 +#: src/contributing/runtime-migrations.md:44 msgid "Continues past failures so one broken migration does not block others." msgstr "" -#: src/contributing/runtime-migrations.md:48 +#: src/contributing/runtime-migrations.md:46 msgid "" "A failed migration can be retried by deleting its tracking row (see [Re-" "running a migration](#re-running-a-migration))." msgstr "" -#: src/contributing/runtime-migrations.md:51 +#: src/contributing/runtime-migrations.md:49 msgid "The `migration!` macro" msgstr "" -#: src/contributing/runtime-migrations.md:53 +#: src/contributing/runtime-migrations.md:51 msgid "" "`cadmus_core::migration!` " "takes a stable string ID and an `async fn` definition:" msgstr "" -#: src/contributing/runtime-migrations.md:71 +#: src/contributing/runtime-migrations.md:56 +msgid "" +"/// One-line doc comment forwarded to rustdoc.\n" +" \"v1_my_migration\"" +msgstr "" + +#: src/contributing/runtime-migrations.md:59 +msgid "\"UPDATE books SET title = TRIM(title)\"" +msgstr "" + +#: src/contributing/runtime-migrations.md:67 msgid "The macro:" msgstr "" -#: src/contributing/runtime-migrations.md:73 +#: src/contributing/runtime-migrations.md:69 msgid "Generates the `async fn` with the provided body." msgstr "" -#: src/contributing/runtime-migrations.md:74 +#: src/contributing/runtime-migrations.md:70 msgid "" "Creates a public submodule named after the function that exposes a " "`MIGRATION_ID` constant — useful for tests and cross-references." msgstr "" -#: src/contributing/runtime-migrations.md:76 +#: src/contributing/runtime-migrations.md:72 msgid "" "Registers the function in the global `REGISTRY` via `#[ctor]` so it runs " "automatically without any manual wiring." msgstr "" -#: src/contributing/runtime-migrations.md:78 +#: src/contributing/runtime-migrations.md:74 msgid "" "Forwards doc comments onto the generated items so rustdoc picks them up." msgstr "" -#: src/contributing/runtime-migrations.md:79 +#: src/contributing/runtime-migrations.md:75 msgid "" "Appends the migration ID and a re-run SQL snippet to the generated docs." msgstr "" -#: src/contributing/runtime-migrations.md:81 +#: src/contributing/runtime-migrations.md:77 msgid "Where to put migration code" msgstr "" -#: src/contributing/runtime-migrations.md:83 +#: src/contributing/runtime-migrations.md:79 msgid "" "Co-locate migrations with the feature they belong to. The library " "subsystem's migrations live in `crates/core/src/library/migrations.rs`; a " @@ -3231,98 +6781,507 @@ msgid "" "reader/migrations.rs`." msgstr "" -#: src/contributing/runtime-migrations.md:88 +#: src/contributing/runtime-migrations.md:84 msgid "" "The module only needs to be declared once so the `#[ctor]` registration " "runs. There is no central registry file to update." msgstr "" -#: src/contributing/runtime-migrations.md:91 +#: src/contributing/runtime-migrations.md:87 msgid "Writing a migration step by step" msgstr "" -#: src/contributing/runtime-migrations.md:93 +#: src/contributing/runtime-migrations.md:89 msgid "1. Choose a stable ID" msgstr "" -#: src/contributing/runtime-migrations.md:95 +#: src/contributing/runtime-migrations.md:91 msgid "" "The ID is the primary key in `_cadmus_migrations`. Once a migration has been " "deployed it must never be renamed, because existing installations track it " "by this string." msgstr "" -#: src/contributing/runtime-migrations.md:99 +#: src/contributing/runtime-migrations.md:95 msgid "" "Convention: `v_`, for example " "`v1_backfill_book_language`." msgstr "" -#: src/contributing/runtime-migrations.md:101 +#: src/contributing/runtime-migrations.md:97 msgid "2. Create the migration file (or add to an existing one)" msgstr "" -#: src/contributing/runtime-migrations.md:126 +#: src/contributing/runtime-migrations.md:100 +msgid "// crates/core/src/my_feature/migrations.rs\n" +msgstr "" + +#: src/contributing/runtime-migrations.md:105 +msgid "" +"/// Backfills the `language` column for books that were imported before\n" +" /// language detection was added.\n" +" \"v1_backfill_book_language\"" +msgstr "" + +#: src/contributing/runtime-migrations.md:110 +msgid "" +"\"UPDATE books SET language = 'en' WHERE language = '' OR language IS NULL\"" +msgstr "" + +#: src/contributing/runtime-migrations.md:120 msgid "3. Use SQLx typed macros" msgstr "" -#: src/contributing/runtime-migrations.md:128 +#: src/contributing/runtime-migrations.md:122 msgid "" "All SQL inside a migration must use the typed macros for compile-time " "verification (see [SQLite & SQLx](sqlite-sqlx.md)):" msgstr "" -#: src/contributing/runtime-migrations.md:146 +#: src/contributing/runtime-migrations.md:126 +msgid "// ✅ Good — compile-time checked\n" +msgstr "" + +#: src/contributing/runtime-migrations.md:127 +#: src/contributing/runtime-migrations.md:132 +msgid "\"DELETE FROM books WHERE file_path = ?\"" +msgstr "" + +#: src/contributing/runtime-migrations.md:130 +msgid "// ❌ Bad — untyped, bypasses verification\n" +msgstr "" + +#: src/contributing/runtime-migrations.md:138 msgid "4. Make it idempotent" msgstr "" -#: src/contributing/runtime-migrations.md:148 +#: src/contributing/runtime-migrations.md:140 msgid "" "Use `INSERT OR IGNORE`, `ON CONFLICT DO NOTHING`, or guard with a `WHERE` " "clause so the migration is safe to re-run without corrupting data." msgstr "" -#: src/contributing/runtime-migrations.md:151 +#: src/contributing/runtime-migrations.md:143 msgid "5. Regenerate `.sqlx` metadata" msgstr "" -#: src/contributing/runtime-migrations.md:153 +#: src/contributing/runtime-migrations.md:145 msgid "" "After adding or changing any query in the migration, regenerate the compile-" "time metadata:" msgstr "" -#: src/contributing/runtime-migrations.md:162 +#: src/contributing/runtime-migrations.md:152 msgid "Commit the updated `.sqlx/` files alongside your migration code." msgstr "" -#: src/contributing/runtime-migrations.md:164 +#: src/contributing/runtime-migrations.md:154 msgid "Re-running a migration" msgstr "" -#: src/contributing/runtime-migrations.md:166 +#: src/contributing/runtime-migrations.md:156 msgid "Delete the tracking row, then restart the application:" msgstr "" -#: src/contributing/runtime-migrations.md:174 +#: src/contributing/runtime-migrations.md:159 +msgid "'v1_my_migration'" +msgstr "" + +#: src/contributing/runtime-migrations.md:162 msgid "The next startup will treat the migration as pending and run it again." msgstr "" -#: src/contributing/runtime-migrations.md:178 +#: src/contributing/runtime-migrations.md:166 msgid "" "`cadmus_core::migration!` — " "macro for declaring runtime migrations" msgstr "" -#: src/contributing/runtime-migrations.md:180 +#: src/contributing/runtime-migrations.md:168 msgid "" -"`cadmus_core::db::migrations::MigrationRunner` " -"— executes all pending registered migrations" +"`cadmus_core::db::migrations::MigrationRunner` — " +"executes all pending registered migrations" msgstr "" -#: src/contributing/runtime-migrations.md:182 +#: src/contributing/runtime-migrations.md:170 msgid "" "`cadmus_core::db::Database` — owns the connection pool and orchestrates both migration stages" msgstr "" + +#: src/investigations/index.md:3 +msgid "" +"When something needs investigating, the investigation and its results are " +"documented here. This is to ensure that the investigation is not lost and " +"can be referred to in the future if needed." +msgstr "" + +#: src/investigations/index.md:7 +msgid "Date" +msgstr "" + +#: src/investigations/index.md:7 +msgid "Title" +msgstr "" + +#: src/investigations/index.md:9 +msgid "2026-03-24" +msgstr "" + +#: src/investigations/index.md:9 +msgid "" +"[DHCP IP Address Changes on WiFi Toggle](./kobo/issue-51-dhcp-investigation." +"md)" +msgstr "" + +#: src/investigations/kobo/issue-51-dhcp-investigation.md:3 +msgid "" +"After identifying that killing the original `dhcpcd` and replacing it with " +"`udhcpc` caused the issue reported in [\\#51](https://github.com/OGKevin/" +"cadmus/issues/51), I confirmed the fix works in testing but wanted to " +"understand the root cause before finalising PR [\\#299](https://github.com/" +"OGKevin/cadmus/pull/299)." +msgstr "" + +#: src/investigations/kobo/issue-51-dhcp-investigation.md:12 +msgid "" +"Investigated the DHCP behaviour by inspecting the running device alongside " +"the KOReader source tree and the original Plato shell scripts." +msgstr "" + +#: src/investigations/kobo/issue-51-dhcp-investigation.md:15 +msgid "What is actually running on the device" +msgstr "" + +#: src/investigations/kobo/issue-51-dhcp-investigation.md:23 +msgid "" +"Nickel uses **`dhcpcd`**, not `udhcpc`. There is no `udhcpc` running under " +"normal operation." +msgstr "" + +#: src/investigations/kobo/issue-51-dhcp-investigation.md:25 +msgid "Why the old script caused a new IP every toggle" +msgstr "" + +#: src/investigations/kobo/issue-51-dhcp-investigation.md:27 +msgid "" +"The original [`scripts/wifi-enable.sh`](https://github.com/OGKevin/cadmus/" +"blob/253edbe8958a44d108676d57b85942f21bb7c899/scripts/wifi-enable.sh#L92-" +"L93) (inherited from Plato) always spawned a fresh `udhcpc`:" +msgstr "" + +#: src/investigations/kobo/issue-51-dhcp-investigation.md:53 +msgid "" +"'t request any options (unless -O is given)\n" +" -O OPT Request option OPT from server (cumulative)\n" +" -x OPT:VAL Include option OPT in sent packets (cumulative)\n" +" Examples of string, numeric, and hex byte opts:\n" +" -x hostname:bbox - option 12\n" +" -x lease:3600 - option 51 (lease time)\n" +" -x 0x3d:0100BEEFC0FFEE - option 61 (client id)\n" +" -x 14:'\"dumpfile\"' - option 14 (shell-quoted)\n" +" -F NAME Ask server to update DNS mapping for NAME\n" +" -V VENDOR Vendor identifier (default '" +msgstr "" + +#: src/investigations/kobo/issue-51-dhcp-investigation.md:62 +msgid "" +"')\n" +" -C Don'" +msgstr "" + +#: src/investigations/kobo/issue-51-dhcp-investigation.md:71 +msgid "\"$INTERFACE\"" +msgstr "" + +#: src/investigations/kobo/issue-51-dhcp-investigation.md:74 +msgid "" +"The `-q` flag is the core issue. It tells `udhcpc` to **quit immediately " +"after obtaining a lease**. The full lifecycle on every WiFi toggle was:" +msgstr "" + +#: src/investigations/kobo/issue-51-dhcp-investigation.md:77 +msgid "" +"`udhcpc` spawned → sends `DISCOVER` → gets `OFFER` → sends `REQUEST` → gets " +"`ACK` → runs `default.script bound` (sets IP, rewrites `resolv.conf`) → " +"**process exits**" +msgstr "" + +#: src/investigations/kobo/issue-51-dhcp-investigation.md:79 +msgid "" +"WiFi disabled → `killall udhcpc default.script` → nothing to kill anyway, " +"already exited" +msgstr "" + +#: src/investigations/kobo/issue-51-dhcp-investigation.md:80 +msgid "" +"WiFi enabled again → repeat from step 1 with **zero memory of the previous " +"lease**" +msgstr "" + +#: src/investigations/kobo/issue-51-dhcp-investigation.md:82 +msgid "" +"Because the process exits after getting the lease, there is no daemon to " +"renew it and no lease file written anywhere. Busybox's `udhcpc` has no lease " +"persistence mechanism. On the next cycle it sends a bare `DISCOVER` with no " +"preferred-IP hint (no DHCP Option 50), so the DHCP server is free to hand " +"out any address from its pool." +msgstr "" + +#: src/investigations/kobo/issue-51-dhcp-investigation.md:87 +msgid "" +"Additionally, [`cadmus.sh`](https://github.com/OGKevin/cadmus/" +"blob/253edbe8958a44d108676d57b85942f21bb7c899/contrib/cadmus.sh#L18-L20) " +"(previously `plato.sh`) was killing Nickel's already-running `dhcpcd` at " +"startup:" +msgstr "" + +#: src/investigations/kobo/issue-51-dhcp-investigation.md:93 +msgid "" +"So even the stateful daemon that Nickel had set up (which _would_ have " +"requested the same IP again) was torn down before Cadmus replaced it with a " +"stateless `udhcpc -q`." +msgstr "" + +#: src/investigations/kobo/issue-51-dhcp-investigation.md:96 +msgid "What `default.script` does" +msgstr "" + +#: src/investigations/kobo/issue-51-dhcp-investigation.md:98 +msgid "" +"`/etc/udhcpc.d/default.script` is a minimal Busybox hook called by `udhcpc` " +"on lease events. The relevant part:" +msgstr "" + +#: src/investigations/kobo/issue-51-dhcp-investigation.md:102 +msgid "\"$1\"" +msgstr "" + +#: src/investigations/kobo/issue-51-dhcp-investigation.md:105 +msgid "# ... deletes all default routes, adds new ones from $router ...\n" +msgstr "" + +#: src/investigations/kobo/issue-51-dhcp-investigation.md:106 +msgid "# ← truncates resolv.conf to zero\n" +msgstr "" + +#: src/investigations/kobo/issue-51-dhcp-investigation.md:112 +msgid "\"$ip\"" +msgstr "" + +#: src/investigations/kobo/issue-51-dhcp-investigation.md:115 +msgid "Notable behaviours:" +msgstr "" + +#: src/investigations/kobo/issue-51-dhcp-investigation.md:117 +msgid "" +"**Wipes `resolv.conf` on every `bound` event.** This is why KOReader's " +"[`disable-wifi.sh`](https://github.com/koreader/koreader/blob/" +"d98dd9f244c5697c08a3bb9ac068f381d70b42c4/platform/kobo/disable-wifi.sh#L3-" +"L6) saves and restores `resolv.conf` with an md5 check. This is a safety net " +"against `udhcpc`'s script wiping DNS on lease release." +msgstr "" + +#: src/investigations/kobo/issue-51-dhcp-investigation.md:120 +msgid "" +"**Writes to `/tmp/nickel-hardware-status`**, which is a FIFO Nickel listens " +"on for network events. [KOReader explicitly removes this FIFO](https://" +"github.com/koreader/koreader/blob/d98dd9f244c5697c08a3bb9ac068f381d70b42c4/" +"platform/kobo/koreader.sh#L232-L233) (`rm -f /tmp/nickel-hardware-status`) " +"to prevent scripts hanging when Nickel is not running." +msgstr "" + +#: src/investigations/kobo/issue-51-dhcp-investigation.md:124 +msgid "Why `dhcpcd` produces stable IPs" +msgstr "" + +#: src/investigations/kobo/issue-51-dhcp-investigation.md:126 +msgid "`dhcpcd` writes a per-SSID lease file to **`/var/db/`**:" +msgstr "" + +#: src/investigations/kobo/issue-51-dhcp-investigation.md:135 +msgid "" +"`/var/db` lives on the root eMMC partition (`/dev/mmcblk0p10`), not a tmpfs. " +"It survives reboots." +msgstr "" + +#: src/investigations/kobo/issue-51-dhcp-investigation.md:137 +msgid "" +"When reconnecting to a known SSID, `dhcpcd` reads the matching `.lease` " +"file, parses the previously-held IP, and sends a DHCP `REQUEST` directly for " +"that IP (DHCP Option 50: Requested IP Address), skipping `DISCOVER` entirely " +"if the lease has not expired. The DHCP server sees a familiar MAC + familiar " +"IP request and simply ACKs it." +msgstr "" + +#: src/investigations/kobo/issue-51-dhcp-investigation.md:142 +msgid "" +"The lease file for the current network encodes `192.x.x.x` in the `yiaddr` " +"field of the saved BOOTREPLY, which is exactly the IP the device is using " +"right now." +msgstr "" + +#: src/investigations/kobo/issue-51-dhcp-investigation.md:145 +msgid "Filesystem layout: what is ephemeral vs persistent" +msgstr "" + +#: src/investigations/kobo/issue-51-dhcp-investigation.md:147 +msgid "Path" +msgstr "" + +#: src/investigations/kobo/issue-51-dhcp-investigation.md:147 +msgid "Persistent?" +msgstr "" + +#: src/investigations/kobo/issue-51-dhcp-investigation.md:149 +msgid "`/tmp`" +msgstr "" + +#: src/investigations/kobo/issue-51-dhcp-investigation.md:149 +msgid "`tmpfs`" +msgstr "" + +#: src/investigations/kobo/issue-51-dhcp-investigation.md:149 +#: src/investigations/kobo/issue-51-dhcp-investigation.md:150 +#: src/investigations/kobo/issue-51-dhcp-investigation.md:151 +#: src/investigations/kobo/issue-51-dhcp-investigation.md:152 +msgid "No" +msgstr "" + +#: src/investigations/kobo/issue-51-dhcp-investigation.md:150 +msgid "`/var/lib`" +msgstr "" + +#: src/investigations/kobo/issue-51-dhcp-investigation.md:150 +#: src/investigations/kobo/issue-51-dhcp-investigation.md:152 +msgid "`tmpfs` (16 KiB)" +msgstr "" + +#: src/investigations/kobo/issue-51-dhcp-investigation.md:151 +msgid "`/var/run`" +msgstr "" + +#: src/investigations/kobo/issue-51-dhcp-investigation.md:151 +msgid "`tmpfs` (128 KiB)" +msgstr "" + +#: src/investigations/kobo/issue-51-dhcp-investigation.md:152 +msgid "`/var/log`" +msgstr "" + +#: src/investigations/kobo/issue-51-dhcp-investigation.md:153 +msgid "`/var/db`" +msgstr "" + +#: src/investigations/kobo/issue-51-dhcp-investigation.md:153 +msgid "eMMC (`mmcblk0p10`)" +msgstr "" + +#: src/investigations/kobo/issue-51-dhcp-investigation.md:153 +#: src/investigations/kobo/issue-51-dhcp-investigation.md:154 +msgid "**Yes**" +msgstr "" + +#: src/investigations/kobo/issue-51-dhcp-investigation.md:154 +msgid "`/etc`" +msgstr "" + +#: src/investigations/kobo/issue-51-dhcp-investigation.md:154 +msgid "eMMC" +msgstr "" + +#: src/investigations/kobo/issue-51-dhcp-investigation.md:156 +msgid "" +"`dhcpcd`'s runtime state (`/var/run/dhcpcd.pid`, `/var/run/dhcpcd.sock`) is " +"ephemeral as expected, but the lease database in `/var/db/` persists. That " +"is the architectural key." +msgstr "" + +#: src/investigations/kobo/issue-51-dhcp-investigation.md:159 +msgid "" +"Why KOReader kills `dhcpcd` in its startup script but still gets stable IPs" +msgstr "" + +#: src/investigations/kobo/issue-51-dhcp-investigation.md:161 +msgid "" +"From [`koreader.sh`](https://github.com/koreader/koreader/blob/" +"d98dd9f244c5697c08a3bb9ac068f381d70b42c4/platform/kobo/koreader.sh#L216-" +"L220):" +msgstr "" + +#: src/investigations/kobo/issue-51-dhcp-investigation.md:164 +msgid "# NOTE: We kill Nickel's master dhcpcd daemon on purpose,\n" +msgstr "" + +#: src/investigations/kobo/issue-51-dhcp-investigation.md:165 +msgid "" +"# A SIGTERM does not break anything, it'll just prevent automatic " +"lease renewal\n" +msgstr "" + +#: src/investigations/kobo/issue-51-dhcp-investigation.md:167 +msgid "" +"'ll do)...\n" +"killall -q -TERM nickel ... dhcpcd-dbus dhcpcd ...\n" +msgstr "" + +#: src/investigations/kobo/issue-51-dhcp-investigation.md:171 +msgid "" +"KOReader kills Nickel's `dhcpcd` because it wants to start its own instance " +"with custom arguments later via [`obtain-ip.sh`](https://github.com/koreader/" +"koreader/blob/d98dd9f244c5697c08a3bb9ac068f381d70b42c4/platform/kobo/obtain-" +"ip.sh#L54-L60). Crucially, `obtain-ip.sh` prefers `dhcpcd` over `udhcpc`:" +msgstr "" + +#: src/investigations/kobo/issue-51-dhcp-investigation.md:175 +msgid "" +"# NOTE: Prefer dhcpcd over udhcpc if available. That's what Nickel uses,\n" +msgstr "" + +#: src/investigations/kobo/issue-51-dhcp-investigation.md:177 +msgid "\"/sbin/dhcpcd\"" +msgstr "" + +#: src/investigations/kobo/issue-51-dhcp-investigation.md:178 +#: src/investigations/kobo/issue-51-dhcp-investigation.md:180 +msgid "\"${INTERFACE}\"" +msgstr "" + +#: src/investigations/kobo/issue-51-dhcp-investigation.md:184 +msgid "" +"The new `dhcpcd` instance KOReader starts reads the same `/var/db/*.lease` " +"files and requests the same IP. Stable address despite the kill/restart " +"cycle." +msgstr "" + +#: src/investigations/kobo/issue-51-dhcp-investigation.md:187 +msgid "" +"Why the fix in PR [\\#299](https://github.com/OGKevin/cadmus/pull/299) works" +msgstr "" + +#: src/investigations/kobo/issue-51-dhcp-investigation.md:189 +msgid "The fix is twofold:" +msgstr "" + +#: src/investigations/kobo/issue-51-dhcp-investigation.md:191 +msgid "" +"**Remove `dhcpcd` from the kill list in [`cadmus.sh`](https://github.com/" +"OGKevin/cadmus/blob/253edbe8958a44d108676d57b85942f21bb7c899/contrib/cadmus." +"sh#L18-L20)**. Nickel's running `dhcpcd -d -z wlan0` instance survives into " +"the Cadmus session, continuously managing the lease." +msgstr "" + +#: src/investigations/kobo/issue-51-dhcp-investigation.md:194 +msgid "" +"**Do not start `udhcpc` at all in the native Rust WiFi implementation**. No " +"new DHCP client is spawned on toggle, so the already-running `dhcpcd` is " +"never displaced." +msgstr "" + +#: src/investigations/kobo/issue-51-dhcp-investigation.md:197 +msgid "" +"The result: one long-lived `dhcpcd` daemon manages the lease for the entire " +"session, renews it in the background, and requests the same IP on every " +"reconnect using the persisted `/var/db/*.lease` file." +msgstr "" diff --git a/docs/src/contributing/devenv-setup.md b/docs/src/contributing/devenv-setup.md index 6b569baf..426c2764 100644 --- a/docs/src/contributing/devenv-setup.md +++ b/docs/src/contributing/devenv-setup.md @@ -18,13 +18,7 @@ This guide covers setup on both Linux and macOS. devenv shell ``` -2. Run the one-time setup to build native dependencies: - - ```bash - cargo xtask setup-native - ``` - -3. Download the packaged runtime assets used by Kobo builds: +2. Download the packaged runtime assets used by Kobo builds: ```bash cargo xtask download-assets @@ -35,8 +29,12 @@ This guide covers setup on both Linux and macOS. > directories. For Kobo builds, make sure `bin/`, `resources/`, and > `hyphenation-patterns/` are present before `cargo xtask build-kobo` so the > generated asset list is complete. + > + > Thirdparty C/C++ dependencies (MuPDF, libwebp, zlib, etc.) are tracked as git + > submodules and built automatically by `build.rs` when you run `cargo build` or + > `cargo xtask run-emulator`. No separate setup step is required. -4. Run the emulator: +3. Run the emulator: ```bash cargo xtask run-emulator @@ -48,7 +46,6 @@ Once inside the devenv shell, these commands are available: | Command | Description | | ----------------------------- | ------------------------------------------------ | -| `cargo xtask setup-native` | Build MuPDF for native development (run once) | | `cargo xtask download-assets` | Download packaged Plato runtime assets | | `cargo xtask test` | Run the test suite across the feature matrix | | `cargo xtask run-emulator` | Run the emulator | @@ -73,11 +70,10 @@ Tasks are defined in `devenv.nix` and can be run with `devenv tasks run `. ### Available Tasks -| Task | Description | Dependencies | -| ------------- | --------------------------------------------------------- | ------------ | -| `docs:build` | Build documentation EPUB (only rebuilds if files changed) | None | -| `deps:native` | Build MuPDF and wrapper for native development | None | -| `build:kobo` | Build for Kobo device | `docs:build` | +| Task | Description | Dependencies | +| ------------ | --------------------------------------------------------- | ------------ | +| `docs:build` | Build documentation EPUB (only rebuilds if files changed) | None | +| `build:kobo` | Build for Kobo device | `docs:build` | All tasks delegate to `cargo xtask` under the hood. @@ -194,7 +190,7 @@ macOS supports full development capabilities including: #### macOS-Specific Notes -**MuPDF build**: On macOS, the native setup script manually gathers pkg-config CFLAGS for system +**MuPDF build**: On macOS, the native build script manually gathers pkg-config CFLAGS for system libraries because MuPDF's build system doesn't properly detect them on Darwin. ## Observability Stack diff --git a/thirdparty/AGENTS.md b/thirdparty/AGENTS.md index 1b80c783..432a4137 100644 --- a/thirdparty/AGENTS.md +++ b/thirdparty/AGENTS.md @@ -2,23 +2,21 @@ ## What this directory is -Build scripts and patches for cross-compiling C/C++ dependencies to ARM -(Kobo). Source code is **not** committed — it is downloaded at build time -via `cargo xtask build-kobo`. +Thirdparty C/C++ dependencies for cross-compiling to ARM (Kobo). Each +subdirectory is a git submodule tracked in `.gitmodules`. All build logic +lives in the `crates/build-deps` crate. ## Build order Libraries are built in dependency order as defined by `LIBRARY_NAMES` in -`xtask/src/tasks/util/thirdparty.rs`. A library later in the list may link -against libraries earlier in the list. Respect this ordering when adding new -entries. +`crates/build-deps/src/versions.rs`. A library later in the list may link against +libraries earlier in the list. Respect this ordering when adding new entries. ## Per-library layout -Each subdirectory may contain: +Each subdirectory is a git submodule containing upstream source code. Build +patches and additional files are kept in `build-scripts//`: -- `build-kobo.sh` — invoked by xtask to configure and compile the library - for `arm-linux-gnueabihf` with `-mcpu=cortex-a9 -mfpu=neon`. - `kobo.patch` — applied before building. Used when upstream sources need modification for the cross-compilation environment. - Additional patches named `*-kobo.patch` — when a library requires @@ -28,12 +26,13 @@ Each subdirectory may contain: from upstream, and build quirks for the cross-compilation target. - `README-cadmus.md` — project-specific notes: why the library is needed, what Cadmus-specific modifications exist, and integration context. -- Additional files (e.g. `Makefile-kobo`, meson cross-file) when the - upstream build system cannot be driven solely via environment variables. +- Additional files (e.g. meson cross-file) when the upstream build system + cannot be driven solely via environment variables. ## Patched libraries -Some libraries carry a `kobo.patch`. Common reasons for patching: +Some libraries carry a `kobo.patch` in their `build-scripts//` +directory. Common reasons for patching: - Replacing pkg-config dependency lookups with hard-coded paths to sibling thirdparty build directories (needed because pkg-config is unavailable @@ -49,26 +48,29 @@ upstream. ## Version management and Renovate -Library versions are defined as constants in -`xtask/src/tasks/util/thirdparty.rs`. This file is the **single source of -truth** for download URLs. +Thirdparty dependencies are tracked as git submodules in `.gitmodules`. Each +submodule pins a specific commit from the upstream repository. -Version updates are managed via `renovate.json` — Renovate regex managers -match the version constants and open PRs when new upstream releases are -available. When adding a new library: +Renovate monitors submodule commits via its `git-submodules` manager and +opens PRs when new upstream versions are available. When adding a new +library: -1. Add a version constant to `thirdparty.rs`. -2. Check `renovate.json` for an existing regex manager that would cover the - new constant. If none exists, add one so the library receives automated - update PRs. +1. Add the submodule under `thirdparty/` and pin it to a release branch + or tag in `.gitmodules`. +2. Add build logic in `crates/build-deps/src/build/kobo/recipes.rs`. +3. Add the library name to `LIBRARY_NAMES` in `crates/build-deps/src/versions.rs`, + respecting dependency order. + +Renovate's `git-submodules` manager will automatically track the new +submodule — no manual Renovate configuration is required. ## Rules -- Never commit downloaded source trees — only build scripts and patches. +- Never commit built artifacts — only source code via submodules and patches + in `build-scripts/`. - All builds target `arm-linux-gnueabihf` with `-mcpu=cortex-a9 -mfpu=neon`. -- Update the version constant in `xtask/src/tasks/util/thirdparty.rs` when - upgrading — do not hard-code URLs in build scripts. +- Update the submodule commit in `.gitmodules` when upgrading a library. - Insert new libraries at the correct position in `LIBRARY_NAMES` (respecting the dependency chain). -- Prefer a `kobo.patch` over modifying `build-kobo.sh` to work around - upstream issues — patches make the delta explicit and reviewable. +- Prefer a `kobo.patch` over modifying build logic to work around upstream + issues — patches make the delta explicit and reviewable.