No description
  • Rust 54.6%
  • JavaScript 26.3%
  • Python 13.8%
  • Shell 5%
  • CSS 0.2%
Find a file
salix 97f908fdc6
All checks were successful
Build x86_64 Flatpak / flatpak (push) Successful in 3m2s
Skip pasting when dictating into the Logia window
End the recording without pasting when Logia itself is focused, since the transcript is already in the editor.
2026-10-12 00:27:55 +02:00
.forgejo Support GNOME 51 and bump extension version to 14 2026-10-09 22:54:02 +02:00
data Ship both CPU engines in Flatpak and download models at runtime 2026-09-16 23:46:03 +02:00
examples Build local GNOME dictation app with selectable engines and correction dictionary 2026-09-16 17:56:14 +02:00
extension/logia@salix.host Support GNOME 51 and bump extension version to 14 2026-10-09 22:54:02 +02:00
packaging/flatpak Update Parakeet engine dependencies 2026-09-21 14:37:53 +02:00
scripts Decode the whole recording in the final transcript 2026-10-08 18:29:46 +02:00
src Skip pasting when dictating into the Logia window 2026-10-12 00:27:55 +02:00
tests Support GNOME 51 and bump extension version to 14 2026-10-09 22:54:02 +02:00
.gitignore Build self-contained offline Flatpak on the unraid-docker Forgejo runner 2026-09-16 18:01:12 +02:00
Cargo.lock Update Cargo dependencies 2026-10-10 20:20:36 +02:00
Cargo.toml Move the top-bar icon from the GNOME extension into the app tray 2026-09-17 00:43:54 +02:00
LICENSE Build local GNOME dictation app with selectable engines and correction dictionary 2026-09-16 17:56:14 +02:00
README.md Support GNOME 51 and bump extension version to 14 2026-10-09 22:54:02 +02:00

Logia

A small native GNOME voice-to-text app, built in Rust with GTK 4 and libadwaita. Dictate into your active app with a global shortcut, a live waveform and persistent GNOME top-bar controls. Speech recognition runs on your computer. The main window provides models, settings, transcripts and corrections.

For English and Dutch on an Intel CPU, start with Parakeet v3 INT8. Its local ONNX runtime uses the CPU; an NVIDIA GPU is not required. The Intel integrated GPU is not used in this version.

Run

Requirements: Rust/Cargo, a C toolchain, pkg-config, GTK ≥ 4.12, libadwaita ≥ 1.5, Python ≥ 3.10, and PipeWire's pw-record. Run the app inside your desktop session so it can access your display, microphone and clipboard.

On Arch/CachyOS, the relevant system packages are base-devel rust pkgconf gtk4 libadwaita python pipewire. On Ubuntu 24.04, they are build-essential pkg-config libgtk-4-dev libadwaita-1-dev python3-venv pipewire-bin, plus a current Rust toolchain (edition 2024).

# One-time download: Python runtime dependencies and the quantized Parakeet model.
./scripts/setup-parakeet.sh

cargo run --locked

The setup script downloads public model weights from Hugging Face into ~/.local/share/logia/engines/parakeet/ (or the corresponding $XDG_DATA_HOME directory). The app defaults to that installation. No account or API key is needed. Normal transcription sets offline mode and never intentionally downloads models or sends audio to a service.

To install the app and its desktop launcher for your user:

./scripts/install.sh

The executable is installed at ~/.local/bin/logia. Model weights have their own upstream licenses, separate from this app's MIT license.

Flatpak builds in Forgejo

Build x86_64 Flatpak runs on the same unraid-docker runner as Invio. Pushes to master/main, pull requests, and manual dispatch build an installable bundle. Download the logia-flatpak-x86_64-<commit> artifact from the successful run; it includes the bundle, SHA-256 checksum, and build information.

sha256sum --check logia-x86_64.flatpak.sha256
flatpak install --user ./logia-x86_64.flatpak
flatpak run host.salix.Logia

The x86_64 bundle includes the Parakeet and Whisper CPU runtimes, without model weights. On first launch, set up the optional desktop controls, then choose a speech model and click Download & use model. The GNOME 51 runtime is downloaded separately by Flatpak. Network access is enabled for explicit model downloads from Hugging Face; transcription stays offline. The sandbox has display, GPU rendering, and PipeWire microphone access; it does not have general home-directory access or host command execution.

Flatpak settings and dictionary are separate from the native installation, under ~/.var/app/host.salix.Logia/data/logia/. Existing native settings are not imported automatically. To bind a GNOME shortcut, use flatpak run host.salix.Logia --toggle. Parakeet and Whisper work directly in the Flatpak. Qwen remains a manually configured native option; host executables and virtual environments are not available inside the sandbox.

Without publication secrets, the workflow builds and uploads an unsigned CI artifact. Artifacts are retained for 14 days. Docker source staging works with remote daemons, and the app has its own persistent SDK/Cargo/download cache volume. The build pins Rust, Python dependencies, whisper.cpp and model revisions, runs unit tests, checks runtime linkage, and exercises both CPU engines with networking disabled. Test models stay in the CI cache and are not exported in the bundle.

Enable publishing to the Salix Flatpak repository

Add these repository Actions secrets in Forgejo settings, using the same values as Invio:

  • FLATPAK_SSH_PRIVATE_KEY
  • FLATPAK_SSH_KNOWN_HOSTS
  • FLATPAK_GPG_PRIVATE_KEY_BASE64

Once all three are present, a push to master/main or a manual run signs and publishes host.salix.Logia/x86_64/stable to the shared repository. Pull requests only build artifacts and never receive publication secrets. If any secret is missing, publication is skipped and the normal artifact build still runs. Invalid credentials fail the publication build rather than silently uploading an unsigned bundle.

The GPG secret must contain the existing Salix signing key 1DB12F26F11C181D76531A98F410EF207A72627C, usable without an interactive passphrase. CI uses the restricted flatpak-publisher@salix.host publish command with strict SSH host-key verification, then checks the exact uploaded commit against a fresh client view of the signed repository. Signing credentials stay in a temporary container directory and are not stored in build caches or artifacts. The existing server publisher must accept the app ref host.salix.Logia/x86_64/stable.

After the first successful publication, users can install from the shared repository:

flatpak remote-add --user --if-not-exists salix https://salix.host/flatpak/salix.flatpakrepo
flatpak install --user salix host.salix.Logia//stable

To run the same build locally with Docker:

.forgejo/ci/run-container.sh
# Output: dist/flatpak/logia-x86_64.flatpak

Download and switch speech models

The welcome screen offers these multilingual CPU models:

Model Download size Tradeoff
Parakeet v3 INT8 639 MiB Automatic detection of 25 European languages
Whisper Tiny 74 MiB Smallest download, lower accuracy
Whisper Base 141 MiB Compact, more accurate than Tiny
Whisper Small 465 MiB Larger download, more accurate than Base

Open Speech models in the header to download another model or switch to an installed one. Downloads show progress, can be cancelled, and resume when retried. Pinned SHA-256 checksums are verified before a model is marked ready. No account is required. Downloaded models and their model cards live under $XDG_DATA_HOME/logia/models/, or ~/.var/app/host.salix.Logia/data/logia/models/ in the Flatpak. They survive app updates. The previous bundled Parakeet path migrates to this location and prompts for a one-time download.

Native builds offer the same model downloads, but still require engine dependencies: run scripts/setup-parakeet.sh for Parakeet or install whisper-cli for Whisper. Custom model paths remain available in Engine settings. Model licenses are shown in the chooser; Parakeet uses CC BY 4.0 and Whisper uses MIT.

Dictate

  1. Choose an engine. Parakeet automatically detects English, Dutch and its other supported languages. Whisper and Qwen also allow an explicit language.
  2. Click Start dictation, or press Ctrl + Space. Click again to stop and transcribe. Recordings stop automatically at 2 minutes for all engines.
  3. The result appears in the editor and is copied automatically by default. Switch to another app and paste.
  4. Correct one short phrase in the editor and click Save corrections. Future transcripts apply that correction. Open the dictionary to add or remove rules manually.

Desktop dictation and top-bar controls

Logia owns its microphone icon and menu through StatusNotifierItem (AppIndicator). Quitting the app removes the icon. The companion GNOME Shell extension handles the global shortcut, pasting, and the waveform at the bottom of the monitor containing your focused window; it creates no top-bar icon. It works on GNOME 48–51, including Wayland. The overlay never takes keyboard focus. While you speak, the live transcript appears in the focused text field as uncommitted input-method text, and the final transcript replaces it when pasted. Password fields and apps without input-method support show the live transcript in the overlay instead. Your desktop needs a StatusNotifierItem host to show the icon; on GNOME, the welcome flow can install or enable AppIndicator and KStatusNotifierItem Support if it is not already provided by your distribution. Without a tray host, Logia remains usable through its launcher and global shortcut.

Install the native app with ./scripts/install.sh, or install the Flatpak, then launch Logia. The welcome flow has separate Top-bar icon and Dictation overlay & shortcut steps before speech-model selection. The icon step detects a working tray host, enables existing AppIndicator support, or offers Install icon support. GNOME downloads that extension from extensions.gnome.org and asks for confirmation; cancellation leaves the rest of Logia usable. The overlay step offers Install & enable extension for Logia’s companion extension. Logia’s companion extension is bundled with the app, so the overlay step needs no download or terminal command. You can skip either step and use the recording window instead. Missing icon support stays visible in the main window, and the app registers its icon automatically when support becomes available. Reopen setup through Settings → Set up desktop controls.

GNOME sometimes requires a fresh login before it discovers a newly installed extension. In that case, Logia shows Installed, explains the next step, and revisits setup when you reopen it. Log out and back in, then click Enable extension. Setup distinguishes enabled, disabled, unsupported and failed installations. If the installed extension differs from the bundled copy, Logia prompts with Update extension and Later when you open the app, including from the tray after a background start. Choosing Update extension starts installation; Later dismisses the prompt for this app session. You can also update from Settings → Set up desktop controls. Background dictation does not open the prompt. Log out and back in afterward to load the updated extension code.

Flatpak builds have narrowly scoped write access to Logia’s own extension directory and permission to contact GNOME’s extension manager. New installations from Flatpak default to launching the Flatpak app. An existing extension’s Use Flatpak installation preference can be changed in Shortcut settings. The extension runs outside the sandbox; Logia still has no general home-directory access or host command execution.

For manual installation, ./scripts/install-extension.sh remains available.

  1. Open Show Logia from the microphone icon and select/download a speech model.
  2. Focus a text field in any app. Press Super + Shift + Space (the default), or choose Start dictation from the icon.
  3. Speak. The waveform reflects your microphone level. Press the shortcut again or pause for 1.5 seconds after speech to transcribe and insert text into the focused application.
  4. Open Shortcut settings from the icon to record your own shortcut. Disable automatic stopping in Logia's settings if you prefer longer pauses.
  5. If Logia misheard a word, just fix it where it was typed. Once you pause, switch windows, or send the text, Logia adds the corrected spelling to its dictionary and shows a notification with Undo. Turn off Learn from your corrections in Shortcut settings to stop this.
  6. Prefer holding the key? Turn on Push to talk in Shortcut settings: record while the shortcut is held, then release its key to transcribe and insert. Pauses do not stop push-to-talk recording; the two-minute limit still applies. Releasing only a modifier in a combination keeps recording. With push to talk, the shortcut can be a single key — for example Right Alt or F5 — instead of a combination.

Closing the main window hides it; the icon's Show Logia brings it back. The icon also offers cancellation and quitting the background app. logia --dictate toggles desktop dictation without opening the window; --cancel, --background, and --quit are also available. --toggle retains the main-window recording workflow. Ctrl + Q quits from the main window.

After transcription finishes, Logia pastes the full result into your selected text field using Ctrl + V. Contractions, repeated letters and multiline formatting are preserved. Apps using a different paste shortcut need manual pasting. The transcript remains in Logia’s editor and on the clipboard.

Keep the intended text field selected until insertion finishes. Switching windows or locking the screen prevents automatic insertion; changing fields within the same window cannot be detected.

The default input is your PipeWire microphone. Choose a different default in GNOME Sound settings before recording. Pause detection uses an audio-level threshold rather than a speech model; background noise or a very quiet microphone may require stopping with the shortcut. Recordings always stop at two minutes. Cancel stops recording or inference and deletes temporary audio. The icon remains available while Logia runs, including when its window is hidden. Quit Logia removes it immediately; the extension can remain enabled to start Logia again with the shortcut.

Engines

Engine Runtime Language selection Personal vocabulary
Parakeet TDT 0.6B v3 onnx-asr, ONNX Runtime CPU, INT8 or full precision Automatic; 25 European languages, including English and Dutch Corrections applied after recognition
Whisper whisper.cpp / whisper-cli Automatic or explicit Initial prompt plus corrections
Qwen3-ASR 0.6B / 1.7B qwen-asr, PyTorch Automatic or explicit supported language Context vocabulary plus corrections

Parakeet splits longer recordings into chunks of at most 25 seconds, choosing quiet points in the final five seconds of each chunk, and joins their transcripts in order. The model loads once per recording. Continuous speech without pauses may lose accuracy at chunk boundaries. Engines load for each dictation, so model startup adds latency. A persistent model worker is a future improvement.

Whisper

For native installations, install/build whisper.cpp following its instructions and download a multilingual model, such as ggml-base.bin or ggml-small.bin. Models ending in .en are English-only. Select Whisper in Logia, open Settings, and set the full model-file path and your whisper-cli executable. No Python dependency is needed for this engine. Runtime accelerator support depends on how you build whisper.cpp.

Qwen

Use a separate virtual environment; upstream recommends Python 3.12. This is an optional, heavier alternative to the default CPU setup:

python3.12 -m venv .venv-qwen
.venv-qwen/bin/pip install qwen-asr huggingface_hub
.venv-qwen/bin/hf download Qwen/Qwen3-ASR-0.6B --local-dir models/qwen3-asr-0.6b

Choose Qwen in Logia and set the absolute paths to .venv-qwen/bin/python and models/qwen3-asr-0.6b. Use the original Qwen checkpoint above, not the newer -hf conversion, for this adapter. Qwen uses CPU float32 here unless CUDA is available. Expect higher memory usage and latency on a CPU. Python executable settings are shared between the two Python engines; change this setting when switching between separate environments.

The model choice is deliberately swappable: no single model is guaranteed to be best for every accent, language, or device. Primary references: Parakeet model card, ONNX runtime usage, Qwen3-ASR, and Whisper CLI.

What “learning” means

Logia stores phrase replacements locally. It does not retrain model weights. In the main window, saving an edit extracts the changed span after removing matching leading/trailing words; only replacements of up to six words on either side become rules. Insertions, deletions and longer rewrites require a manual dictionary entry instead. Save separate corrections one at a time to avoid learning an overly specific sentence.

In other applications, the GNOME extension follows the text it just inserted, much like a screen reader does. It listens to accessibility text-change events only from the start of a dictation until its transcript is learned from: after four seconds without edits, when you switch windows, when the text is sent or cleared, at the next dictation, or ten minutes later. It keeps only the inserted range, ignores other text and fields, and never follows password fields. Logia then compares the original transcript with the edited text and learns each changed phrase that is spelled like the words it replaces, so one edit can teach several fixes, such as open eye → OpenAI. Rewording (meeting → call), added or removed words, sentence capitalisation, and swaps between two plain lowercase words (their → there) are not learned. Undo removes the rule, and the same edit is not learned again.

This works in apps that report text changes to accessibility tools: GTK 3 and GTK 4 apps, including GNOME's own, and LibreOffice. For GTK 4 apps, the extension locates the focused field while you speak so the app starts reporting changes. Firefox, Chromium, Electron and Qt apps often expose text only when accessibility support is turned on, for example with a screen reader; elsewhere, nothing is learned and dictation works as before. Use the main window's editor or the dictionary for those apps.

Rules are case-insensitive, respect Unicode word boundaries, preserve surrounding punctuation, and are applied in one pass to avoid cascading replacements. Each rule has a language scope: English rules apply in English mode, Dutch rules in Dutch mode, and Automatic rules apply to any automatically detected language. Parakeet uses this shared Automatic scope. Entries are not automatically migrated between scopes. Preferred spellings also become vocabulary hints for Whisper/Qwen; Parakeet uses post-processing only.

Local data

  • Settings, dictionary and optional history: $XDG_DATA_HOME/logia/state.json, normally ~/.local/share/logia/state.json. Writes use an atomic replacement and a user-private file. A corrupt state file is preserved and reported rather than reset.
  • History is off by default. When enabled, it retains at most 50 initial transcripts; later editor changes are not saved into history. Use Clear history to delete existing entries; disabling history only stops new entries.
  • Audio, engine requests and logs live in a private temporary directory and are deleted on completion/cancellation. A force kill, system crash or power loss can leave temporary files until the system clears its temporary directory. No audio archive is created.
  • Clipboard text is available to your desktop's clipboard manager. Dictionary entries and saved transcripts are plain text, not encrypted.

Development / checks

cargo fmt --check
cargo test --locked
cargo clippy --all-targets --locked -- -D warnings
python3 -m unittest discover -s tests
node --test tests/extension.test.mjs
glib-compile-schemas --strict --dry-run extension/logia@salix.host/schemas

# Optional background recording / D-Bus smoke test (needs gjs and gtk4-broadwayd):
cargo build --locked
LOGIA_DESKTOP_TEST=1 python3 -m unittest discover -s tests -p test_desktop.py

# Exercise the actual local default engine without a display or microphone:
cargo run --example transcribe -- /absolute/path/to/mono-16khz.wav

The headless example expects a short 16 kHz mono PCM WAV. Unit tests cover phrase learning, language scope, replacement boundaries, persistence and subprocess handling. A real microphone and cross-application paste must be checked on a running GNOME desktop. Extension lifecycle, monitor placement, focus protection and paste key release have mocked Shell tests; these do not replace testing inside GNOME Shell.

Initial validation: Rust tests and Clippy passed; Python adapter tests passed; Parakeet INT8 transcribed the whisper.cpp JFK sample locally. The GTK recording/editor/correction workflow was exercised under Broadway using a fake recorder and engine, and the correction persisted successfully. Flatpak CI also checks Whisper inference using a cached Tiny model. Qwen still needs real-model end-to-end testing; neither optional runtime is installed by the native Parakeet setup script.