docs: publish Void xbps as the only install channel (upstream reference cleanup) #38

Merged
xavierk merged 6 commits from docs/upstream-reference-cleanup into main 2026-11-06 16:17:05 +00:00
77 changed files with 1419 additions and 5344 deletions
+14 -20
View File
@@ -8,17 +8,17 @@ Participation is covered by our [Code of Conduct](CODE_OF_CONDUCT.md). Be respec
## Ways to contribute
- **Report bugs** - [Open an issue](https://github.com/VocaHQ/vocalinux/issues/new?template=bug_report.md)
- **Suggest features** - [Feature request](https://github.com/VocaHQ/vocalinux/issues/new?template=feature_request.md) or [Discussions](https://github.com/VocaHQ/vocalinux/discussions)
- **Report bugs** - [Open an issue](https://git.bongbetic.com/xavierk/voculinux/issues/new)
- **Suggest features** - [Open an issue](https://git.bongbetic.com/xavierk/voculinux/issues/new) with the capability you want
- **Improve documentation** - Fixes and clarity are always useful
- **Fix bugs or add features** - Check [open issues](https://github.com/VocaHQ/vocalinux/issues), especially [`good first issue`](https://github.com/VocaHQ/vocalinux/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22)
- **Fix bugs or add features** - Check [open issues](https://git.bongbetic.com/xavierk/voculinux/issues)
## Development setup
### Automated (recommended)
```bash
git clone https://github.com/YOUR-USERNAME/voculinux.git
git clone https://git.bongbetic.com/YOUR-USERNAME/voculinux.git
cd voculinux
./install.sh --dev
```
@@ -30,7 +30,9 @@ tools, and runs the test suite. See [The two environments](#the-two-environments
1. Fork and clone the repository.
2. Install system dependencies (examples):
2. Install system dependencies (examples). Voculinux is published only as a
Void Linux xbps package; the distro examples below are for building from a
source checkout:
**Ubuntu 24.04+:**
```bash
@@ -117,10 +119,6 @@ The repository uses two virtual environments on purpose. Do not merge them:
an activated venv and rebuilds one created by the wrong interpreter. It uses
`/usr/bin/python3` unless overridden, e.g. `SYSTEM_PYTHON=/usr/bin/python3.12 ./install.sh`.
`just appimage` bundles its own Python/PyGObject in the pinned Ubuntu Docker image.
For GI failures, check `Checking bundled PyGObject after linuxdeploy` and the smoke
test's path diagnostics; installing `python3-gobject` on the host won't fix the bundle.
## Making changes
### Branch naming
@@ -163,9 +161,9 @@ voculinux/
├── resources/ # Icons and sounds
├── docs/
├── install.d/ # Installer modules, including generated package data
├── packaging/ # AppImage, AUR, Flatpak
├── packaging/ # Packaging inputs; Void xbps is the published channel
├── scripts/ # Maintenance tools and distro package-map source
├── snap/ # Snap recipe
├── snap/ # Snap recipe (not published for Voculinux)
└── web/ # Marketing site (Next.js)
```
@@ -274,16 +272,12 @@ Maintainers follow [docs/RELEASE_PROCESS.md](docs/RELEASE_PROCESS.md) (version f
## Community
- [SUPPORT.md](SUPPORT.md) - where to get help
- [Discord](https://discord.gg/t6muquAJbm) - fastest place to talk with maintainers and other contributors
- [GitHub Discussions](https://github.com/VocaHQ/vocalinux/discussions)
- [GitHub Issues](https://github.com/VocaHQ/vocalinux/issues)
- [@vocahq on X](https://x.com/vocahq)
- [Issues](https://git.bongbetic.com/xavierk/voculinux/issues)
## License
Contributions are licensed under the [GNU Affero General Public License v3.0](LICENSE)
(AGPL-3.0), matching the other [VocaHQ](https://github.com/VocaHQ) distribution
projects ([VocaMac](https://github.com/VocaHQ/vocamac),
[VocaPhone](https://github.com/VocaHQ/vocaphone),
[VocaGateway](https://github.com/VocaHQ/vocagateway)). By opening a pull request, you
agree that your contribution may be distributed under that license.
(AGPL-3.0). By opening a pull request, you agree that your contribution may be
distributed under that license.
Voculinux is a hard fork of Vocalinux by VocaHQ: https://github.com/VocaHQ/vocalinux
+53 -199
View File
@@ -6,51 +6,33 @@
**Voice dictation for Linux**
[![Ubuntu](https://img.shields.io/badge/Ubuntu-24.04+-E95420?logo=ubuntu&logoColor=white)](docs/DISTRO_COMPATIBILITY.md)
[![Debian](https://img.shields.io/badge/Debian-12+-A81D33?logo=debian&logoColor=white)](docs/DISTRO_COMPATIBILITY.md)
[![Fedora](https://img.shields.io/badge/Fedora-42+-51A2DA?logo=fedora&logoColor=white)](docs/DISTRO_COMPATIBILITY.md)
[![Arch](https://img.shields.io/badge/Arch-rolling-1793D1?logo=archlinux&logoColor=white)](docs/DISTRO_COMPATIBILITY.md)
[![openSUSE](https://img.shields.io/badge/openSUSE-Tumbleweed-73BA25?logo=opensuse&logoColor=white)](docs/DISTRO_COMPATIBILITY.md)
[![Privacy: on-device](https://img.shields.io/badge/privacy-on--device-success)](https://github.com/VocaHQ/vocalinux#privacy-and-security)
[![X11 & Wayland](https://img.shields.io/badge/display-X11%20%7C%20Wayland-lightgrey)](https://github.com/VocaHQ/vocalinux#features)
[![License: AGPL-3.0](https://img.shields.io/badge/License-AGPL--3.0-blue.svg)](https://www.gnu.org/licenses/agpl-3.0)
[![Void Linux](https://img.shields.io/badge/Void%20Linux-glibc%20x86__64-478061)](#void-linux-signed-xbps-repo)
[![Python 3.11+](https://img.shields.io/badge/python-3.11+-blue.svg)](https://www.python.org/downloads/)
[![GitHub release](https://img.shields.io/github/v/release/VocaHQ/vocalinux)](https://github.com/VocaHQ/vocalinux/releases)
[![License: AGPL-3.0](https://img.shields.io/badge/License-AGPL--3.0-blue.svg)](https://www.gnu.org/licenses/agpl-3.0)
[![Privacy: on-device](https://img.shields.io/badge/privacy-on--device-success)](#privacy-and-security)
[![X11 & Wayland](https://img.shields.io/badge/display-X11%20%7C%20Wayland-lightgrey)](#features)
[![Release](https://img.shields.io/badge/release-v0.18.5-478061)](https://git.bongbetic.com/xavierk/voculinux/releases/tag/v0.18.5)
[![AppImage](https://img.shields.io/badge/AppImage-x86__64%20%7C%20aarch64-2C5BB4)](https://github.com/VocaHQ/vocalinux/releases/latest)
[![.deb](https://img.shields.io/badge/.deb-Debian%20%7C%20Ubuntu-A81D33?logo=debian&logoColor=white)](https://github.com/VocaHQ/vocalinux/releases/latest)
[![.rpm](https://img.shields.io/badge/.rpm-Fedora-EE0000?logo=fedora&logoColor=white)](https://github.com/VocaHQ/vocalinux/releases/latest)
[![Flatpak](https://img.shields.io/badge/Flatpak-vocahq%20remote-4A90D9?logo=flatpak&logoColor=white)](#flatpak-any-distro)
[![Snap](https://img.shields.io/badge/Snap-edge-82BEA0?logo=snapcraft&logoColor=white)](https://snapcraft.io/voculinux)
[![AUR](https://img.shields.io/aur/version/voculinux)](https://aur.archlinux.org/packages/voculinux)
[![PyPI](https://img.shields.io/pypi/v/voculinux)](https://pypi.org/project/voculinux/)
[![Nix flake](https://img.shields.io/badge/Nix-flake-5277C3?logo=nixos&logoColor=white)](packaging/nix/README.md)
[![Discord](https://img.shields.io/discord/1538633755877580810?logo=discord&logoColor=white&label=Discord)](https://discord.gg/t6muquAJbm)
[![VocaHQ](https://img.shields.io/badge/VocaHQ-vocahq.com-1a7f4e)](https://vocahq.com)
[![Follow on X](https://img.shields.io/badge/Follow%20%40vocahq-000000?style=flat&logo=x&logoColor=white)](https://x.com/vocahq)
[Website](https://voculinux.com) · [Install](#install) · [Docs](#documentation) · [Releases](https://github.com/VocaHQ/vocalinux/releases)
[Website](https://voculinux.com) · [Install](#install) · [Docs](#documentation) · [Releases](https://git.bongbetic.com/xavierk/voculinux/releases) · [Issues](https://git.bongbetic.com/xavierk/voculinux/issues)
</div>
Voculinux turns speech into typed text in whatever app has focus. It is a free, AGPL-3.0-licensed desktop app for X11 and Wayland. After you download a model, local engines (whisper.cpp by default, plus OpenAI Whisper, Faster Whisper, VOSK, and Parakeet) run speech-to-text on your machine. An optional remote HTTP API is off unless you configure it.
No Voca account is required. Models download once. After that, speech-to-text stays on your machine.
No account is required. Models download once. After that, speech-to-text stays on your machine.
**Current release:** [v0.18.5](https://github.com/VocaHQ/vocalinux/releases/tag/v0.18.5). Patch: a stop that races a recording start can no longer leave the mic recording forever, keyboards stay alive through suspend and the GNOME lock screen, Ctrl+mouse-wheel zoom works again, Dictation Pad and Settings stop travelling workspaces together, paste-injection copies are marked sensitive for clipboard history on wl-clipboard 2.2+, and `install.sh` gains an upgrade mode that keeps your existing configuration. Details: [docs/UPDATE.md](docs/UPDATE.md).
**Current release:** [v0.18.5](https://git.bongbetic.com/xavierk/voculinux/releases/tag/v0.18.5). Patch: a stop that races a recording start can no longer leave the mic recording forever, keyboards stay alive through suspend and the GNOME lock screen, Ctrl+mouse-wheel zoom works again, Dictation Pad and Settings stop travelling workspaces together, and paste-injection copies are marked sensitive for clipboard history on wl-clipboard 2.2+. Details: [docs/UPDATE.md](docs/UPDATE.md).
## Features
- **On-device after model download**: Local engines; speech-to-text stays on your machine
- **X11 and Wayland**: Text injection via xdotool, IBus, wtype, ydotool, or clipboard fallback
- **Several engines**: whisper.cpp (default), OpenAI Whisper, Faster Whisper, VOSK, Parakeet, plus optional remote HTTP API or local VocaGateway from Settings (not on-device)
- **Several engines**: whisper.cpp (default), OpenAI Whisper, Faster Whisper, VOSK, Parakeet, plus optional remote HTTP API or local gateway from Settings (not on-device)
- **GPU acceleration**: Vulkan for AMD, Intel, and NVIDIA with whisper.cpp
- **Toggle or push-to-talk**: New installs default to hold Right Alt; existing configs keep their shortcut
- **System tray + settings**: Searchable sidebar, Speech Model simple setup with Advanced as an island, status icons, audio feedback
- **Start on login**: XDG autostart (desktop session, not a systemd service)
- **Packaging**: install script, `.deb` / `.rpm`, AppImage, AUR, PyPI, Snap (`--edge`), Flatpak (release bundles and local build; not on Flathub), Nix flake
- **Packaging**: signed Void Linux xbps repository (glibc, x86_64). No other channel is published for Voculinux
## Screenshots
@@ -126,115 +108,9 @@ Voculinux in action. Full gallery on the [website screenshots page](https://vocu
## Install
### Recommended (interactive installer)
```bash
curl -fsSL https://raw.githubusercontent.com/VocaHQ/vocalinux/main/install.sh -o /tmp/vl.sh
bash /tmp/vl.sh
```
Prefer to review the script first: open `/tmp/vl.sh` before running it, or clone the repo and run `./install.sh` locally.
The installer detects hardware, recommends an engine, downloads a default model (~74MB for whisper.cpp tiny), installs neural VAD when ONNX Runtime is available, and sets up desktop integration. Typical install time with whisper.cpp is about 1-2 minutes.
| Engine | When to use |
|--------|-------------|
| **whisper.cpp** (default) | Best default; Vulkan GPU on AMD, Intel, and NVIDIA |
| **Whisper** (OpenAI) | PyTorch path; NVIDIA/CUDA |
| **Faster Whisper** | CPU-friendly Whisper via CTranslate2 / INT8 |
| **VOSK** | Low RAM / minimal footprint |
| **Parakeet** | CPU; NVIDIA NeMo ASR via sherpa-onnx; 25 European languages |
| **Remote API** | Offload to a server you configure |
Non-interactive options:
```bash
bash /tmp/vl.sh --auto # whisper.cpp defaults
bash /tmp/vl.sh --auto --engine=whisper # OpenAI Whisper
bash /tmp/vl.sh --auto --engine=faster_whisper # Faster Whisper (CPU)
bash /tmp/vl.sh --auto --engine=vosk # VOSK only
bash /tmp/vl.sh --auto --engine=parakeet # Parakeet (CPU)
```
For a specific release tag, see [GitHub Releases](https://github.com/VocaHQ/vocalinux/releases) or `./install.sh --tag=v0.18.5`.
### Arch Linux (AUR)
```bash
yay -S voculinux
```
See [docs/AUR.md](docs/AUR.md).
### AppImage
Download the `x86_64` or `aarch64` AppImage from [Releases](https://github.com/VocaHQ/vocalinux/releases), mark it executable, and run it. Built against glibc 2.35 (Debian 12+, Ubuntu 22.04+, Fedora 36+, Arch, Tumbleweed). Host text-injection tools (`xdotool` on X11; `wtype` / `ydotool` / clipboard tools on Wayland) are still required. Current AppImages rebuild whisper.cpp with Vulkan and use the host GPU driver. Prefer the installer when you want system deps, a CUDA build, and models set up automatically.
### Distro packages (.deb / .rpm)
Download the `.deb` (Debian 12+, Ubuntu 24.04+) or `.rpm` (Fedora) matching your CPU from [Releases](https://github.com/VocaHQ/vocalinux/releases) and install with `sudo apt install ./voculinux_*.deb` or `sudo dnf install ./voculinux-*.rpm`. Thin packages: Python and GTK come from the distro. No auto-update; CPU whisper.cpp. Details in [docs/INSTALL.md](docs/INSTALL.md).
### Flatpak (any distro)
Install and auto-update via the self-hosted VocaHQ remote (`flatpak update` picks up each release):
```bash
flatpak install https://vocahq.github.io/voculinux-flatpak/com.voculinux.Voculinux.flatpakref
```
The `.flatpakref` adds the `vocahq` remote and resolves the GNOME runtime through Flathub. Manually instead:
```bash
flatpak remote-add --if-not-exists flathub https://dl.flathub.org/repo/flathub.flatpakrepo
flatpak remote-add --if-not-exists vocahq https://vocahq.github.io/voculinux-flatpak/vocahq.flatpakrepo
flatpak install vocahq com.voculinux.Voculinux
```
Or sideload a GitHub Release bundle (`Voculinux-<version>-x86_64.flatpak` or `-aarch64.flatpak`) after the Flathub GNOME runtime is present — bundles do not auto-update:
```bash
flatpak install --user ./Voculinux-<version>-x86_64.flatpak
```
Local build:
```bash
flatpak install flathub org.gnome.Platform//50 org.gnome.Sdk//50
flatpak-builder --user --install --force-clean build-dir \
packaging/flatpak/com.voculinux.Voculinux.yml
flatpak run com.voculinux.Voculinux
```
Ships whisper.cpp with Vulkan. It is **not on Flathub** (submission [flathub#9368](https://github.com/flathub/flathub/pull/9368) closed 2026-07-23 on policy grounds). Details: [packaging/flatpak/README.md](packaging/flatpak/README.md).
### Snap (Ubuntu Snap Store)
Listing: [snapcraft.io/voculinux](https://snapcraft.io/voculinux). `stable` is promoted from `candidate` after QA. Tagged releases also attach `voculinux_<version>_amd64.snap` on GitHub.
```bash
sudo snap install voculinux --edge
sudo snap connect voculinux:audio-record # if mic is not auto-connected
sudo snap connect voculinux:raw-input # global keyboard shortcuts (evdev)
sudo snap connect voculinux:hardware-observe # list keyboards (/proc/bus/input/devices)
```
Native Wayland typing uses the RemoteDesktop portal: the compositor asks once on first dictation, nothing to connect. The recipe ships **no `uinput` plug** and no ydotool; the Store denied the super-privileged grant, which held the 0.17.0+ revisions and left **v0.16.2 (rev 7)** on edge. On KDE Plasma, KWin's portal can mistype letter case ([KDE bug 489021](https://bugs.kde.org/show_bug.cgi?id=489021)); set Virtual Keyboard to "IBus Wayland" so dictation types correctly.
GitHub `.snap` files through v0.18.2 declare `uinput`, so KDE users who want ydotool can sideload that build and connect the plug manually:
```bash
sudo snap install --dangerous ./voculinux_0.18.2_amd64.snap
sudo snap connect voculinux:uinput # only in the v0.18.x sideload; the recipe no longer ships it
```
### Nix / NixOS (flake)
```bash
nix run github:VocaHQ/vocalinux # try it
nix profile install github:VocaHQ/vocalinux
```
The flake also ships `nixosModules.voculinux` and `homeManagerModules.voculinux` for declarative installs (`programs.voculinux.enable = true`). The bundled pywhispercpp builds with Vulkan GPU inference. Details: [packaging/nix/README.md](packaging/nix/README.md).
Voculinux is published only as a signed Void Linux xbps repository. Other
channels (AUR, AppImage, `.deb` / `.rpm`, Flatpak, Snap, Nix, PyPI and the
upstream install script) are not published for Voculinux.
### Void Linux (signed xbps repo)
@@ -269,42 +145,50 @@ sync and asks nothing. If xbps still shows a fingerprint prompt, answer
`FINGERPRINT` file attached to the release). Do not add `-y` to the first
sync: it would accept an unknown key without asking.
### From source
### Speech engines
```bash
git clone https://github.com/VocaHQ/vocalinux.git
cd voculinux
./install.sh
# or pick the engine up front
./install.sh --engine=whisper_cpp
./install.sh --engine=vosk
./install.sh --engine=parakeet --auto
```
| Engine | When to use |
|--------|-------------|
| **whisper.cpp** (default) | Best default; Vulkan GPU on AMD, Intel, and NVIDIA |
| **Whisper** (OpenAI) | PyTorch path; NVIDIA/CUDA |
| **Faster Whisper** | CPU-friendly Whisper via CTranslate2 / INT8 |
| **VOSK** | Low RAM / minimal footprint |
| **Parakeet** | CPU; NVIDIA NeMo ASR via sherpa-onnx; 25 European languages |
| **Remote API** | Offload to a server you configure |
The xbps package does not include the optional engines (Faster Whisper, Whisper,
VOSK, Parakeet) or the Silero VAD runtime (`onnxruntime`). Add them from your own
Python environment, as the package's install message describes. Models download
on first run.
### After installation
```bash
voculinux # if ~/.local/bin is on PATH
# or
~/.local/share/voculinux/venv/bin/voculinux
voculinux # from the xbps package, on your PATH
```
You can also launch Voculinux from your application menu.
### Nightly builds
### From a source checkout (developers)
Daily builds from `main` appear on [Releases](https://github.com/VocaHQ/vocalinux/releases). Use the latest stable or beta release for production; nightlies are untested.
```bash
git clone https://git.bongbetic.com/xavierk/voculinux.git
cd voculinux
./install.sh --dev
```
See [CONTRIBUTING.md](CONTRIBUTING.md) for the two-venv layout and the dev workflow.
## Requirements
| | |
|--|--|
| **OS** | Linux (Ubuntu 24.04+, Debian 12+, Fedora 42+, Arch, openSUSE Tumbleweed) |
| **OS** | Linux. The published package targets Void Linux (glibc, x86_64) |
| **Python** | 3.11 or newer |
| **Display** | X11 or Wayland |
| **Hardware** | Microphone; GPU optional (Vulkan) |
The distro must ship Python 3.11+ because Voculinux uses distro PyGObject (`python3-gi`). Ubuntu 22.04 (Python 3.10) and Debian 11 (3.9) are below that floor. Distribution notes: [docs/DISTRO_COMPATIBILITY.md](docs/DISTRO_COMPATIBILITY.md).
Python 3.11+ is required, with PyGObject from the system (`python3-gi` or the Void `python3-gobject` package). Ubuntu 22.04 (Python 3.10) and Debian 11 (3.9) are below that floor. Source-checkout distribution notes: [docs/DISTRO_COMPATIBILITY.md](docs/DISTRO_COMPATIBILITY.md).
## Usage
@@ -359,14 +243,13 @@ voculinux --start-minimized
Stored at `~/.config/voculinux/config.json`. Prefer the Settings dialog for day-to-day changes. **Settings → Speech Model** starts with a simple setup; expand **Advanced** for engine, size, and specialization. Set `post_processing.script_path` to an executable to transform each transcription result (stdin → stdout) before it is injected; leave empty to disable.
Neural VAD (Silero) is used when `onnxruntime` is available; install via `pip install "voculinux[vad]"` for manual/PyPI installs. The installer attempts this automatically.
Neural VAD (Silero) is used when `onnxruntime` is available. The xbps package does not pull it in; add `onnxruntime` from your own Python environment.
## Documentation
| Document | Description |
|----------|-------------|
| [Installation](docs/INSTALL.md) | Installer, AppImage, AUR, Flatpak, Snap, Nix flake, running |
| [Manual / PyPI install](docs/INSTALL_MANUAL.md) | Package lists and pip workflows |
| [Installation](docs/INSTALL.md) | Void xbps repository and running |
| [Troubleshooting](docs/TROUBLESHOOTING.md) | Common failures |
| [User guide](docs/USER_GUIDE.md) | Dictation, engines, models, tips |
| [Update guide](docs/UPDATE.md) | Upgrade steps and release notes |
@@ -374,7 +257,6 @@ Neural VAD (Silero) is used when `onnxruntime` is available; install via `pip in
| [Support](SUPPORT.md) | Where to get help |
| [Distribution compatibility](docs/DISTRO_COMPATIBILITY.md) | Distro matrix and session notes |
| [Remote HTTP API](docs/HTTP_REMOTE.md) | Offload transcription to a server |
| [Local VocaGateway (optional)](docs/GATEWAY_EMBED.md) | Start a local gateway from Settings; not on-device |
| [Contributing](CONTRIBUTING.md) | Dev setup, style, PR process |
| [Security](SECURITY.md) | Supported versions and vulnerability reporting |
| [Docs index](docs/README.md) | Full documentation map |
@@ -390,7 +272,7 @@ Report vulnerabilities privately per [SECURITY.md](SECURITY.md).
## Development
```bash
git clone https://github.com/VocaHQ/vocalinux.git
git clone https://git.bongbetic.com/xavierk/voculinux.git
cd voculinux
./install.sh --dev
source venv/bin/activate
@@ -402,69 +284,41 @@ See [CONTRIBUTING.md](CONTRIBUTING.md) for the two-venv layout, `just` recipes,
## Roadmap
Shipped: graphical settings, multi-language support, whisper.cpp default, Vulkan GPU, Wayland/IBus, Flatpak packaging, AppImage, `.deb` / `.rpm` packages, in-app update checker, Parakeet and Faster Whisper engines, Snap recipe.
Shipped: graphical settings, multi-language support, whisper.cpp default, Vulkan GPU, Wayland/IBus, in-app update checker, Parakeet and Faster Whisper engines, Void Linux xbps packaging.
Planned:
- [ ] Application-specific voice commands
- [ ] User-customizable voice command map
- [ ] Flathub publication (not currently listed; see #167)
## Voca ecosystem
## Origin
Voculinux is part of [VocaHQ](https://vocahq.com). On-device speech-to-text first, one app per platform. Optional [VocaGateway](https://vocagateway.vocahq.com) is self-hosted and not on-device.
Voculinux is a hard fork of Vocalinux by VocaHQ: https://github.com/VocaHQ/vocalinux
| Platform | Project | Website | GitHub |
|----------|---------|---------|--------|
| Linux | **Voculinux** | [voculinux.com](https://voculinux.com) | [VocaHQ/vocalinux](https://github.com/VocaHQ/vocalinux) |
| macOS | **VocaMac** | [vocamac.com](https://vocamac.com) | [VocaHQ/vocamac](https://github.com/VocaHQ/vocamac) |
| Windows | **VocaWin** | [vocawin.com](https://vocawin.com) | [VocaHQ/vocawin](https://github.com/VocaHQ/vocawin) |
| Phone | **VocaPhone** | [vocaphone.vocahq.com](https://vocaphone.vocahq.com) | [VocaHQ/vocaphone](https://github.com/VocaHQ/vocaphone) |
| Gateway | **VocaGateway** | [vocagateway.vocahq.com](https://vocagateway.vocahq.com) | [VocaHQ/vocagateway](https://github.com/VocaHQ/vocagateway) |
Talk to us: [Discord](https://discord.gg/t6muquAJbm) · [X @vocahq](https://x.com/vocahq) · [hello@vocahq.com](mailto:hello@vocahq.com)
Voculinux is that project's fork for Void Linux. Its source, issues and releases
live on the fork's own host.
## Contributing
Bug reports, docs, and code are welcome. Start with [CONTRIBUTING.md](CONTRIBUTING.md) and [good first issues](https://github.com/VocaHQ/vocalinux/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22).
Bug reports, docs, and code are welcome. Start with [CONTRIBUTING.md](CONTRIBUTING.md) and [open issues](https://git.bongbetic.com/xavierk/voculinux/issues).
- [Report a bug](https://github.com/VocaHQ/vocalinux/issues/new?template=bug_report.md)
- [Request a feature](https://github.com/VocaHQ/vocalinux/issues/new?template=feature_request.md)
- [Discussions](https://github.com/VocaHQ/vocalinux/discussions)
- [Report a bug](https://git.bongbetic.com/xavierk/voculinux/issues/new)
- [Request a feature](https://git.bongbetic.com/xavierk/voculinux/issues/new)
### Contributors
## Repository
Thanks to everyone who has contributed code, docs, or fixes:
<a href="https://github.com/VocaHQ/vocalinux/graphs/contributors">
<img src="https://contrib.rocks/image?repo=VocaHQ/vocalinux" alt="Voculinux contributors" />
</a>
## Repository mirrors
GitHub is the primary forge for issues, PRs, CI, and releases.
| Role | URL |
|------|-----|
| Primary | https://github.com/VocaHQ/vocalinux |
| Read-only mirror (Codeberg) | https://codeberg.org/jatinkrmalik/voculinux |
Open issues and pull requests on GitHub only.
The source, issues, pull requests, and releases live at https://git.bongbetic.com/xavierk/voculinux. Open issues and
pull requests there.
## License
This project is licensed under the **GNU Affero General Public License v3.0**
([AGPL-3.0](LICENSE)), aligning with the other [VocaHQ](https://github.com/VocaHQ)
distribution projects.
([AGPL-3.0](LICENSE)).
You may use, study, modify, and redistribute the software under AGPL-3.0. If you
run a modified version as a network service, AGPL also requires that you make the
corresponding source available.
## Star Chart
[![Star History Chart](https://api.star-history.com/chart?repos=VocaHQ/vocalinux&type=date&legend=top-left&sealed_token=ZWyQQLhSORoR4mKf6UXMGFSCBXRxM_yEZgc8MFCH_ysBjaFUm_OCH-bI3TD7OivczEzm-ADRIpF9xCWFOMHvBPW95eQBxzfRMpNksChz7rN_eiqL7AIMDw)](https://www.star-history.com/?type=date&repos=VocaHQ%2Fvocalinux)
### Optional Orukeet model
With the Parakeet engine installed, Settings → Speech Model → Parakeet also
+8 -10
View File
@@ -18,7 +18,7 @@ Upgrade to the latest release for security and reliability fixes. See [docs/UPDA
Do not open a public GitHub issue for security vulnerabilities.
**Preferred:** email the maintainer at **jatinkrmalik@gmail.com** with subject:
Email the maintainer at **xavier@bongbetic.com** with subject:
`Voculinux Security Vulnerability Report`
@@ -31,14 +31,12 @@ Include:
You should receive acknowledgment within 48 hours. We will assess severity, share an expected timeline, coordinate disclosure, and credit you in release notes if you want credit.
If email is unavailable, use [GitHub private vulnerability reporting](https://github.com/VocaHQ/vocalinux/security/advisories/new) when enabled for the repository.
## Privacy design
Voculinux is built for local-first dictation:
- Local engines process audio on-device after you download a model
- No Voca account is required for local engines
- No account is required for local engines
- No usage telemetry in the installed application
- Models download once and stay in local cache
- Downloads are checked against pinned checksums before install
@@ -73,17 +71,17 @@ When the user enables **Remote API**, audio is uploaded to the configured server
## Keeping installs current
On Void Linux, update through the signed xbps repository:
```bash
# Re-run the official installer (preserves config and models)
curl -fsSL https://raw.githubusercontent.com/VocaHQ/vocalinux/main/install.sh -o /tmp/vl.sh
bash /tmp/vl.sh
sudo xbps-install -Su voculinux
```
Or follow [docs/UPDATE.md](docs/UPDATE.md) for source, AUR, AppImage, Snap, and Flatpak paths.
Source checkouts and other paths are in [docs/UPDATE.md](docs/UPDATE.md).
## Contact
| Topic | Contact |
|-------|---------|
| Security | jatinkrmalik@gmail.com |
| General bugs | https://github.com/VocaHQ/vocalinux/issues |
| Security | xavier@bongbetic.com |
| General bugs | https://git.bongbetic.com/xavierk/voculinux/issues |
+5 -8
View File
@@ -6,8 +6,7 @@ Where to get help with Voculinux.
| Topic | Doc |
|-------|-----|
| Install | [docs/INSTALL.md](docs/INSTALL.md) |
| Manual / PyPI install | [docs/INSTALL_MANUAL.md](docs/INSTALL_MANUAL.md) |
| Install (Void Linux xbps) | [docs/INSTALL.md](docs/INSTALL.md) |
| Troubleshooting | [docs/TROUBLESHOOTING.md](docs/TROUBLESHOOTING.md) |
| Day-to-day use | [docs/USER_GUIDE.md](docs/USER_GUIDE.md) |
| Upgrading | [docs/UPDATE.md](docs/UPDATE.md) |
@@ -20,12 +19,10 @@ Where to get help with Voculinux.
| Channel | Use for |
|---------|---------|
| [Discord](https://discord.gg/t6muquAJbm) | Fastest place to talk with maintainers |
| [GitHub Discussions](https://github.com/VocaHQ/vocalinux/discussions) | Questions, setup help, ideas |
| [Bug report](https://github.com/VocaHQ/vocalinux/issues/new?template=bug_report.md) | Something is broken |
| [Feature request](https://github.com/VocaHQ/vocalinux/issues/new?template=feature_request.md) | New capability proposals |
| [Issues](https://git.bongbetic.com/xavierk/voculinux/issues) | Bug reports, feature ideas, and questions |
| [New issue](https://git.bongbetic.com/xavierk/voculinux/issues/new) | Something is broken or a new capability to propose |
When filing a bug, include OS/distro, `voculinux --version`, install method, display server (X11/Wayland), engine, and a `voculinux --debug` snippet.
When filing a bug, include OS/distro, `voculinux --version`, install method (the Void xbps package or a source checkout), display server (X11/Wayland), engine, and a `voculinux --debug` snippet.
## Security
@@ -37,4 +34,4 @@ Code and docs: [CONTRIBUTING.md](CONTRIBUTING.md). Community standards: [CODE_OF
## Maintainer contact
General product questions are best on Discussions or Discord so others can find the answer. Security only: see [SECURITY.md](SECURITY.md).
Post general questions as issues so others can find the answer. Security only: see [SECURITY.md](SECURITY.md).
+35 -176
View File
@@ -2,11 +2,16 @@
How Voculinux behaves across distributions, what is tested, and what to install manually when needed.
For install steps, prefer [INSTALL.md](INSTALL.md). Flatpak packaging (useful on immutable or non-standard layouts): [packaging/flatpak/README.md](../packaging/flatpak/README.md). Snap Store listing: [snapcraft.io/voculinux](https://snapcraft.io/voculinux) (`--edge`; pack/upload steps in [INSTALL.md](INSTALL.md)). Flatpak is not on Flathub: submission [flathub#9368](https://github.com/flathub/flathub/pull/9368) closed 2026-07-23 on policy grounds; channel tracked in [#167](https://github.com/VocaHQ/vocalinux/issues/167).
**The published package targets Void Linux (glibc, x86_64).** It is the only Voculinux package
this project publishes: a signed xbps repository, registered as described in
[INSTALL.md](INSTALL.md#void-linux-signed-xbps-repo). The distribution tables below describe
source-checkout builds with `install.sh`, for developers. They are not published packages.
For install steps, prefer [INSTALL.md](INSTALL.md).
## Cross-distro behavior
The installer and launch wrappers handle most path differences.
For source checkouts, the installer and launch wrappers handle most path differences.
### GI_TYPELIB_PATH detection
@@ -24,18 +29,19 @@ The installer uses a multi-step approach to detect the correct GI_TYPELIB_PATH:
### Wrapper Scripts
The installer creates wrapper scripts (`~/.local/bin/voculinux` and `~/.local/bin/voculinux-gui`) that:
The source installer creates wrapper scripts (`~/.local/bin/voculinux` and `~/.local/bin/voculinux-gui`) that:
- Set the correct `GI_TYPELIB_PATH` environment variable
- Handle input group permissions for Wayland keyboard shortcuts
- Provide seamless cross-distro compatibility
### Desktop Entry
The `.desktop` entry is automatically configured with the detected `GI_TYPELIB_PATH`, ensuring the application launches correctly from the application menu regardless of distribution.
For source installs, the `.desktop` entry is automatically configured with the detected `GI_TYPELIB_PATH`, ensuring the application launches correctly from the application menu regardless of distribution.
## Officially Supported Distributions
## Officially Supported Distributions (source checkouts)
These distributions are tested and known to work well with Voculinux. A
For source-checkout builds with `install.sh`, these distributions are tested and known to
work well with Voculinux. A
derivative is judged by the Python and package repositories it ships rather
than by its own version number, which rarely tracks the base release. In
particular, `install.sh` does not interpret a Debian-based derivative's product
@@ -63,7 +69,7 @@ These distributions may work but have known limitations or are less tested:
|--------------|--------|--------------|
| Gentoo | ⚠️ Experimental | No package manager support, manual install required. Packages are compiled from source which takes longer. |
| Alpine Linux | ⚠️ Experimental | Uses musl libc (not glibc). Some Python packages may not have pre-built wheels and may need to be compiled. |
| Void Linux | ⚠️ Experimental | Untested, manual install required. Uses xbps package manager. |
| Void Linux | ⚠️ Experimental | Source-checkout builds are untested here. The published xbps package is the supported route; see [Void Linux](#void-linux). |
| Solus | ⚠️ Experimental | Untested, manual install required. Uses eopkg package manager. |
| Mageia | ⚠️ Experimental | Untested, uses dnf/urpmi package managers. |
@@ -78,11 +84,11 @@ These distributions are known to be incompatible:
## Requirements by Distribution
These are baseline commands for manual installations. The exact inventory used
These are baseline commands for source-checkout builds. The exact inventory used
by `install.sh`, including package alternatives and conditional dependencies,
lives in [`scripts/distro-package-map.yaml`](../scripts/distro-package-map.yaml).
For the complete manual setup sequence, including native build prerequisites,
use [INSTALL_MANUAL.md](INSTALL_MANUAL.md#system-packages-by-distribution).
For the complete developer setup sequence, including native build prerequisites,
see [CONTRIBUTING.md](../CONTRIBUTING.md#manual-setup).
### Ubuntu/Debian-based
```bash
@@ -121,24 +127,22 @@ sudo systemctl enable --now ydotoold
```
**Scoped source builds.** If you need to force `pywhispercpp` to rebuild from source, use the
package-scoped flag to avoid compiling unrelated deps like NumPy from source (which takes a very
long time and can fail):
installer's flag. It rebuilds the whisper.cpp bindings and leaves the rest of the venv alone,
so unrelated dependencies such as NumPy are not compiled from source:
```bash
source ~/.local/share/voculinux/venv/bin/activate
PYWHISPERCPP_CLEAN=1 pip install --force-reinstall --no-binary=pywhispercpp pywhispercpp
deactivate
./install.sh --auto --rebuild-whispercpp
```
**Verifying libwhisper.so resolution.** If Voculinux starts with
`libwhisper.so.1: cannot open shared object file`, check for unresolved symbols:
```bash
ldd $(find ~/.local/share/voculinux/venv -name '*.so' -path '*/pywhispercpp*' 2>/dev/null | head -1) | grep 'not found'
ldd $(find venv -name '*.so' -path '*/pywhispercpp*' 2>/dev/null | head -1) | grep 'not found'
```
If any libraries are listed, re-run the installer with `--rebuild-whispercpp` or switch to
the VOSK engine (`--engine=vosk`) as a fully-packaged alternative.
the VOSK engine (`--engine=vosk`) as an alternative.
### Fedora/RHEL-based
```bash
@@ -192,9 +196,16 @@ sudo apk add py3-gobject3 py3-pip gtk+3.0 py3-cairo portaudio-dev \
**Note:** Alpine uses musl libc. Some Python packages may need to be compiled from source.
### Void Linux
The published Voculinux package for Void is described in
[INSTALL.md](INSTALL.md#void-linux-signed-xbps-repo). This section covers source-checkout
builds on Void, which are experimental. Packages from the `install.sh` inventory:
```bash
sudo xbps-install -Sy python3-pip python3-gobject gtk+3 libayatana-appindicator \
gobject-introspection portaudio-devel python3-devel pkg-config
sudo xbps-install -S python3-pip python3-gobject gtk+3 libayatana-appindicator \
gobject-introspection portaudio-devel cairo-devel python3-devel pkg-config cmake \
base-devel wget curl unzip shaderc patchelf Vulkan-Tools xclip xsel wl-clipboard \
xdotool wtype
```
Note: `ydotool` is not packaged for Void, so the ydotool injection
@@ -217,154 +228,6 @@ sudo urpmi -y python3-gobject gtk3-devel gobject-introspection-devel \
portaudio-devel python3-devel python3-virtualenv pkg-config
```
## PyPI, pip, and pipx Installation
The PyPI package is useful when you want a standard Python package install, or
when you want to manage Voculinux with `pipx`. However, **Voculinux requires
system desktop packages** that cannot be installed by pip or pipx.
Use the official installer when possible. It installs the required system
packages, creates the virtual environment, installs Voculinux, sets up desktop
integration, and downloads the default speech model:
```bash
curl -fsSL https://raw.githubusercontent.com/VocaHQ/vocalinux/main/install.sh -o /tmp/vl.sh
bash /tmp/vl.sh
```
### Important: Install System Packages First
Before running `pip install voculinux`, `pip install "voculinux[vosk]"`, or
`pipx install voculinux`, you **must** install the required system packages:
#### Ubuntu/Debian
```bash
sudo apt install python3-gi gir1.2-gtk-3.0 gir1.2-ibus-1.0 gir1.2-ayatanaappindicator3-0.1 \
libgirepository1.0-dev libcairo2-dev portaudio19-dev python3-dev \
python3-venv pkg-config xdotool wtype wl-clipboard xclip xsel
```
On Ubuntu 24.04+ or Pop!_OS, install `libgirepository-2.0-dev` if
`libgirepository1.0-dev` is not available.
#### Fedora
```bash
sudo dnf install python3-gobject gtk3 gtk3-devel ibus-devel cairo-devel libayatana-appindicator-gtk3 \
gobject-introspection-devel portaudio-devel python3-devel \
python3-virtualenv pkg-config xdotool wtype wl-clipboard xclip xsel
```
#### Arch Linux
```bash
sudo pacman -S python-gobject gtk3 ibus libayatana-appindicator gobject-introspection \
python-cairo portaudio python-virtualenv pkg-config xdotool wtype wl-clipboard xclip xsel
```
#### openSUSE
```bash
PYVER=$(python3 -c 'import sys; print(f"python{sys.version_info.major}{sys.version_info.minor}")')
sudo zypper install "${PYVER}-gobject" "${PYVER}-gobject-cairo" gtk3 ibus typelib-1_0-IBus-1_0 \
gcc gcc-c++ make patchelf \
typelib-1_0-AyatanaAppIndicator3-0_1 libayatana-appindicator3-1 \
typelib-1_0-Notify-0_7 libnotify4 \
portaudio-devel "${PYVER}-devel" "${PYVER}-virtualenv" pkg-config xdotool wtype wl-clipboard xclip xsel
```
### pip Installation Steps
1. **Install system packages** (see above for your distribution)
2. **Create a virtual environment with system site-packages enabled**:
```bash
python3 -m venv ~/.local/share/voculinux-pypi/venv --system-site-packages
source ~/.local/share/voculinux-pypi/venv/bin/activate
pip install --upgrade pip setuptools wheel
```
`--system-site-packages` lets the venv use distro-provided GTK bindings such
as `python3-gi`, which are often more reliable than building PyGObject from
source in a venv.
3. **Install Voculinux from PyPI**:
```bash
pip install voculinux
# vosk engine: pip install "voculinux[vosk]"
```
4. **Run Voculinux**:
```bash
voculinux
```
### pipx Installation Steps
1. **Install system packages** (see above for your distribution)
2. **Install pipx** (if not already installed):
```bash
# Ubuntu/Debian
sudo apt install pipx
pipx ensurepath
# Fedora
sudo dnf install pipx
pipx ensurepath
# Arch Linux
sudo pacman -S pipx
pipx ensurepath
```
3. **Install Voculinux**:
```bash
pipx install voculinux
# vosk engine: pipx install "voculinux[vosk]"
```
4. **Run Voculinux**:
```bash
voculinux
```
### Why System Packages Are Required
Voculinux uses GTK3 for its GUI, AppIndicator/Ayatana for the system tray icon,
PortAudio for microphone capture, and tools such as `xdotool`, `wtype`, and clipboard utilities (`wl-clipboard`, `xclip`, `xsel`) for
text injection and layout-independent Wayland paste fallback. These are system libraries and desktop tools that must be
installed with your distribution's package manager. Python packages from PyPI
can provide Python bindings, but they do not provide the underlying GTK
typelibs, tray support, audio development libraries, or text injection binaries.
### Troubleshooting pip/pipx Installation
If `voculinux` fails to start:
1. **Check for missing system packages** - The error message will indicate which packages are missing
2. **Verify GI_TYPELIB_PATH** - In rare cases, you may need to set this environment variable:
```bash
export GI_TYPELIB_PATH=/usr/lib/x86_64-linux-gnu/girepository-1.0
voculinux
```
3. **Check package metadata** - Run `pip show voculinux` or
`pipx runpip voculinux show voculinux` to confirm which version was installed.
4. **Check Python dependencies** - Run `pip check` inside the venv, or
`pipx runpip voculinux check` for pipx.
If Voculinux reports that a speech recognition model is missing, open Settings
and download a model, or use the official installer to download the default model
during setup.
### Recommended Alternative
For the best experience with automatic dependency handling, use the official installer:
```bash
curl -fsSL https://raw.githubusercontent.com/VocaHQ/vocalinux/main/install.sh -o /tmp/vl.sh
bash /tmp/vl.sh
```
This installer automatically detects your distribution and installs all required system packages.
## Text Injection Tools
Voculinux requires text injection tools to work with X11 or Wayland:
@@ -380,7 +243,8 @@ Voculinux requires text injection tools to work with X11 or Wayland:
### Installation by Distribution
```bash
# Ubuntu/Debian/Fedora/Arch/openSUSE
# Void/Ubuntu/Debian/Fedora/Arch/openSUSE
sudo xbps-install -S xdotool wtype wl-clipboard xclip xsel # Void
sudo apt install xdotool wtype wl-clipboard xclip xsel # Ubuntu/Debian
sudo dnf install xdotool wtype wl-clipboard xclip xsel # Fedora
sudo pacman -S xdotool wtype wl-clipboard xclip xsel # Arch
@@ -425,12 +289,8 @@ For unsupported or experimental distributions:
./install.sh --skip-system-deps
```
4. **Or install from source in a virtual environment:**
```bash
python3 -m venv venv
source venv/bin/activate
pip install -e .[whisper]
```
4. **Or set up a development checkout** with `./install.sh --dev`, as described in
[CONTRIBUTING.md](../CONTRIBUTING.md#development-setup).
## Desktop Environment Compatibility
@@ -561,8 +421,7 @@ If you successfully get Voculinux working on an unsupported or experimental dist
## See also
- [Installation guide](INSTALL.md)
- [Manual / PyPI install](INSTALL_MANUAL.md)
- [Troubleshooting](TROUBLESHOOTING.md)
- [User guide](USER_GUIDE.md)
- [Update guide](UPDATE.md)
- [GitHub issue tracker](https://github.com/VocaHQ/vocalinux/issues)
- [Open issues](https://git.bongbetic.com/xavierk/voculinux/issues)
+136 -256
View File
@@ -1,248 +1,92 @@
# Installation guide
How to install Voculinux on Linux. Short overview: [project README](../README.md).
How to install Voculinux. Short overview: [project README](../README.md).
Voculinux is published only as a signed Void Linux xbps repository (glibc, x86_64).
Other install channels are not published for it. Source checkouts are a developer path.
| Path | When to use |
|------|-------------|
| [Recommended installer](#recommended-installer) | Most users |
| [AppImage](#appimage) | Portable binary; no system package install |
| [Distro packages (.deb / .rpm)](#distro-packages-deb--rpm) | Debian 12+, Ubuntu 24.04+, Fedora |
| [AUR](#arch-linux-aur) | Arch / Manjaro |
| [Flatpak](#flatpak) | Release `.flatpak` or local build |
| [Snap](#snap-ubuntu-snap-store) | Ubuntu Snap Store (`--edge`) or GitHub `.snap` |
| [Nix](#nix--nixos) | Flake package, Home Manager, or the NixOS module |
| [From source](#from-source) | Contributors or custom trees |
| [Manual / PyPI](INSTALL_MANUAL.md) | Full control or pip-only workflows |
| [Void package](#install) | Void Linux, glibc, x86_64: the published install |
| [Source checkout](#from-a-source-checkout-developers) | Contributors and developers |
| [Update](#update) | Upgrading an existing install |
| [Troubleshooting](TROUBLESHOOTING.md) | Tray, audio, injection, models |
## Recommended installer
## Install
Download, review if you like, then run:
### Void Linux (signed xbps repo)
```bash
curl -fsSL https://raw.githubusercontent.com/VocaHQ/vocalinux/main/install.sh -o /tmp/vl.sh
bash /tmp/vl.sh
Void Linux **glibc `x86_64` only**. Each version tag publishes a signed
personal xbps repository to the project's rolling `latest` release:
binary packages (`*.x86_64.xbps`) with their signatures, the signed
repository metadata (`x86_64-repodata`), and the client key
(`FINGERPRINT` plus the fingerprint-named `.plist`). The registration URL
below always serves the newest release.
This is a **third-party repository** — it is published by this project,
not by upstream Void Linux, and its packages are built from the
templates in `packaging/void/srcpkgs/`. Details: [packaging/void/README.md](../packaging/void/README.md).
Repository key, RSA 4096. Pin this fingerprint:
```text
fa:d1:00:32:ce:b1:b9:fd:73:05:05:76:75:54:37:25
```
Installs the latest release with **whisper.cpp** by default. For a pinned version, see [GitHub Releases](https://github.com/VocaHQ/vocalinux/releases) or pass `--tag=...`.
The installer:
- Installs whisper.cpp (typically ~1-2 minutes; no full PyTorch stack)
- Detects GPU / Vulkan (AMD, Intel, NVIDIA)
- Installs neural VAD when ONNX Runtime is available
- Downloads the default whisper.cpp tiny model (~74MB), verified against pinned checksums
- Sets up desktop integration and launch wrappers
The source installer uses committed, hash-verified Python dependency exports,
including its pip and source-build tools. GTK/PyGObject still comes from your
distribution, and the venv retains access to system packages. A missing export
or unavailable pinned package causes that installation step to fail; the
existing optional-engine and VAD fallbacks still apply.
Remote installs run the installer from the selected release tag. Older tags
therefore retain their original dependency-install behavior; downloading a new
bootstrap script does not change an older release's pins.
### Installer modes
```bash
./install.sh # Interactive (recommended)
./install.sh --auto # Defaults: whisper.cpp
./install.sh --auto --engine=whisper # OpenAI Whisper
./install.sh --auto --engine=faster_whisper # Faster Whisper (CPU)
./install.sh --auto --engine=vosk # VOSK only
./install.sh --auto --engine=parakeet # Parakeet (CPU)
./install.sh --auto --engine=remote_api # Remote HTTP API
./install.sh --dev # Editable install + test tools
./install.sh --help # Full flag list
# as root on the Void system: trust the key, register the repository, install
curl -fsSL -o '/var/db/xbps/keys/fa:d1:00:32:ce:b1:b9:fd:73:05:05:76:75:54:37:25.plist' \
https://git.bongbetic.com/xavierk/voculinux/raw/branch/main/packaging/void/repository-key.plist
echo 'repository=https://git.bongbetic.com/xavierk/voculinux/releases/download/latest' > /etc/xbps.d/voculinux.conf
xbps-install -S voculinux
```
| Engine | When to use | Typical install |
|--------|-------------|-----------------|
| **whisper.cpp** (default) | Best default; Vulkan GPU | ~1-2 min, ~74MB model |
| **Whisper** (OpenAI) | PyTorch / CUDA | ~5-10 min, large download |
| **Faster Whisper** | CPU Whisper via CTranslate2 / INT8 | Model sizes like OpenAI Whisper |
| **VOSK** | Low RAM / minimal | ~30 sec, ~40MB |
| **Parakeet** | CPU; 25 European languages | Model ~639MB |
| **Remote API** | User-configured HTTP server | No local model |
The key file is named by its fingerprint, so xbps finds it on the first
sync and asks nothing. If xbps still shows a fingerprint prompt, answer
`N` unless it reads exactly the fingerprint above (it must also match the
`FINGERPRINT` file attached to the release). Do not add `-y` to the first
sync: it would accept an unknown key without asking.
Useful flags: `--tag=TAG`, `--skip-models`, `--rebuild-whispercpp`, `--no-rebuild-whispercpp`, `--venv-dir=PATH`, `--test`.
### Speech engines
`--engine=NAME` accepts `whisper_cpp` (default), `whisper`, `faster_whisper`, `vosk`, `parakeet`, `remote_api`.
| Engine | When to use |
|--------|-------------|
| **whisper.cpp** (default) | Best default; Vulkan GPU on AMD, Intel, and NVIDIA |
| **Whisper** (OpenAI) | PyTorch path; NVIDIA/CUDA |
| **Faster Whisper** | CPU-friendly Whisper via CTranslate2 / INT8 |
| **VOSK** | Low RAM / minimal footprint |
| **Parakeet** | CPU; NVIDIA NeMo ASR via sherpa-onnx; 25 European languages |
| **Remote API** | Offload to a server you configure |
### What the installer does
The xbps package does not include the optional engines (Faster Whisper, Whisper,
VOSK, Parakeet) or the Silero VAD runtime (`onnxruntime`). Add them from your own
Python environment, as the package's install message describes. Models download
on first run.
1. Detects the distribution and installs system packages
2. Creates a Python venv from the system interpreter with `--system-site-packages` (for distro GTK / PyGObject)
3. Installs Voculinux and the chosen speech engine
4. Optionally installs neural VAD (`onnxruntime`)
5. Downloads a default speech model (checksum-verified)
6. Installs icons, desktop entry, and `~/.local/bin` wrappers
## AppImage
From [GitHub Releases](https://github.com/VocaHQ/vocalinux/releases):
### After installation
```bash
chmod +x Voculinux-*-x86_64.AppImage # or aarch64
./Voculinux-*-x86_64.AppImage
voculinux # from the xbps package, on your PATH
```
Built against glibc 2.35, so it starts on Debian 12+, Ubuntu 22.04+, Fedora 36+, Arch, and Tumbleweed. Older bases (RHEL 9, Debian 11, Ubuntu 20.04) are below that AppImage floor. The installer and PyPI package still require a distro that ships Python 3.11+, so they are not a workaround for Debian 11 or Ubuntu 20.04.
You can also launch Voculinux from your application menu.
Still needs host text-injection tools (`xdotool` on X11; `wtype` / `ydotool` / clipboard tools on Wayland). Current AppImages rebuild whisper.cpp with Vulkan and use the host GPU driver (`vulkaninfo --summary`). Prefer the installer when you want system deps, a CUDA build, and models set up automatically.
### Wayland hotkeys need the input group
## Distro packages (.deb / .rpm)
From [GitHub Releases](https://github.com/VocaHQ/vocalinux/releases), pick the file matching your distro and CPU:
```bash
# Debian / Ubuntu (amd64; arm64 files are attached too)
sudo apt install ./voculinux_*_amd64.deb
# Fedora (x86_64; aarch64 files are attached too)
sudo dnf install ./voculinux-*.x86_64.rpm
```
These are thin packages: Python, GTK, PyGObject and the AppIndicator typelib come from the distribution, and `apt`/`dnf` resolves them. `xdotool`, `wtype`, `ydotool`, the clipboard tools and IBus are `Recommends`, matching the optional feature set of the other paths. Only `pywhispercpp` and `pynput` are vendored inside the package — `pywhispercpp` is packaged by neither distro and `pynput` is missing on Fedora. They land under `/usr/lib/voculinux/vendor`, on `sys.path` via the `voculinux` launcher.
The `.deb` needs a distro that ships Python 3.11+: Debian 12+ and Ubuntu 24.04+ (Ubuntu 22.04 ships 3.10). whisper.cpp runs on CPU in these packages — the PyPI wheel, not the Vulkan build the AppImage carries. There is no auto-update: a new release is a new package install. A PPA and a COPR are not published yet.
On Wayland, the default Right Alt shortcut reads keyboard devices through evdev, so your user needs `input` group membership or the hotkey is silently disabled:
On Wayland, the default Right Alt shortcut reads keyboard devices through evdev. Your user
needs `input` group membership, or the evdev hotkey backend cannot open `/dev/input` devices:
```bash
sudo usermod -aG input $USER # then log out and back in
```
Joining `input` grants read access to every keyboard and pointer device — X11 needs none of this. The launcher re-execs through `sg input` when `/etc/group` already lists the membership but the current session has not picked it up, so an `usermod` between package install and next login applies without a reboot. The source installer does this step for you; the packages cannot.
Joining `input` gives read access to every keyboard and pointer device. X11 does not need
this step. The package does not add the group for you; its install message says the same.
## Arch Linux (AUR)
### Audio capture
```bash
yay -S voculinux
```
See [AUR.md](AUR.md).
## Flatpak
Install and auto-update via the self-hosted VocaHQ remote (`flatpak update` picks up each release):
```bash
flatpak install https://vocahq.github.io/voculinux-flatpak/com.voculinux.Voculinux.flatpakref
```
The `.flatpakref` adds the `vocahq` remote and resolves the GNOME runtime through Flathub. Manually instead:
```bash
flatpak remote-add --if-not-exists flathub https://dl.flathub.org/repo/flathub.flatpakrepo
flatpak remote-add --if-not-exists vocahq https://vocahq.github.io/voculinux-flatpak/vocahq.flatpakrepo
flatpak install vocahq com.voculinux.Voculinux
```
Or sideload a GitHub Release bundle (`Voculinux-<version>-x86_64.flatpak` or `-aarch64.flatpak`) after the Flathub GNOME runtime is present — bundles do not auto-update:
```bash
flatpak install --user ./Voculinux-<version>-x86_64.flatpak
```
Local build (contributors):
```bash
flatpak install flathub org.gnome.Platform//50 org.gnome.Sdk//50
flatpak-builder --user --install --force-clean build-dir \
packaging/flatpak/com.voculinux.Voculinux.yml
flatpak run com.voculinux.Voculinux
```
Whisper.cpp + Vulkan. It is **not on Flathub** (submission [flathub#9368](https://github.com/flathub/flathub/pull/9368) closed 2026-07-23 on policy grounds). Details: [packaging/flatpak/README.md](../packaging/flatpak/README.md).
On native X11, hotkeys work without device access (GNOME X11 is the exception:
its X server hides physical keys from the X11 listener, so it also needs
`/dev/input`). On Wayland, the built-in hotkey needs `/dev/input` (`--device=input` on Flatpak 1.15.6 or newer, or
`--device=all` on older versions); otherwise enable External Activation and
bind a desktop shortcut to the D-Bus `Toggle` command in
[Running without device access](../packaging/flatpak/README.md#running-without-device-access).
Desktop shortcuts only toggle dictation, so they cannot support push-to-talk.
## Snap (Ubuntu Snap Store)
Listing: [snapcraft.io/voculinux](https://snapcraft.io/voculinux) (issue [#48](https://github.com/VocaHQ/vocalinux/issues/48)). Recipe: `snap/snapcraft.yaml`. Tagged `v*` releases attach `voculinux_<version>_amd64.snap` to the GitHub Release and try Snap Store `edge` and `candidate` when credentials are set. `stable` is promoted from `candidate` after QA.
Build inputs, the PR/release artifact gate, and its verification limits are
documented in [SNAP.md](SNAP.md).
Store install, once Canonical lists the revision:
```bash
sudo snap install voculinux --edge
sudo snap connect voculinux:audio-record # if mic is not auto-connected
sudo snap connect voculinux:raw-input # global keyboard shortcuts (evdev)
sudo snap connect voculinux:hardware-observe # list keyboards (/proc/bus/input/devices)
```
Native Wayland typing uses the RemoteDesktop portal: the compositor asks once on first dictation, nothing to connect. The recipe ships **no `uinput` plug** and no ydotool; the Store denied the super-privileged grant ([the request thread](https://forum.snapcraft.io/t/53528)), which held the 0.17.0+ revisions and left **v0.16.2 (rev 7)** on edge. On KDE Plasma, KWin's portal can mistype letter case ([KDE bug 489021](https://bugs.kde.org/show_bug.cgi?id=489021)); set Virtual Keyboard to "IBus Wayland" so dictation types correctly.
Under strict confinement the snap reaches audio through the PulseAudio/PipeWire server, not the sound card, so Settings → Audio lists the server's microphones (via `pactl`) and records from the one you pick. whisper.cpp in the snap is built without a GPU backend and runs on the CPU, and host tools such as `nvidia-smi` and `vulkaninfo` are not visible inside it. For Vulkan acceleration use the [AppImage](#appimage) or the [installer](#recommended-installer) instead.
GitHub `.snap` files through v0.18.2 declare `uinput`, so KDE users who want ydotool can sideload that build and connect the plug manually:
```bash
sudo snap install --dangerous ./voculinux_0.18.2_amd64.snap
sudo snap connect voculinux:audio-record
sudo snap connect voculinux:raw-input
sudo snap connect voculinux:hardware-observe
sudo snap connect voculinux:uinput # only in the v0.18.x sideload; the recipe no longer ships it
```
`--dangerous` is required because this file is not a Store revision and will not refresh from the Store; once the Store carries a newer revision, `sudo snap install voculinux --edge` (or `snap refresh`) replaces it.
## Nix / NixOS
The repo is a flake: GTK, PyGObject and the AppIndicator typelib come from nixpkgs, and `pywhispercpp` (absent from nixpkgs, same gap as Debian and Fedora) is built from its vendored PyPI sdist. The wrapped binary carries every injector and clipboard tool on PATH.
```bash
nix run github:VocaHQ/vocalinux # try it
nix profile install github:VocaHQ/vocalinux
```
On NixOS, `voculinux.nixosModules.voculinux` provides `programs.voculinux.enable`, which installs the package and enables `hardware.uinput`. Add each dictating user to `input` and `uinput` for Wayland shortcuts and ydotool typing. Home Manager users get `voculinux.homeManagerModules.voculinux` with the same `programs.voculinux` options — device permissions stay on the system side. Usage snippets and scope notes: [packaging/nix/README.md](../packaging/nix/README.md).
## From source
```bash
git clone https://github.com/VocaHQ/vocalinux.git
cd voculinux
./install.sh
```
## System requirements
| Requirement | Details |
|-------------|---------|
| **OS** | Ubuntu 24.04+, Debian 12+, Fedora 42+, Arch, openSUSE Tumbleweed |
| **Python** | 3.11 or newer |
| **Display** | X11 or Wayland |
| **Hardware** | Microphone; GPU optional (Vulkan) |
| **Disk** | ~200MB with default whisper.cpp model |
| **RAM** | 4GB minimum; 8GB comfortable |
The distro must ship Python 3.11+ because Voculinux uses distro PyGObject (`python3-gi`). Ubuntu 22.04 (Python 3.10) and Debian 11 (3.9) are below that floor. Distro matrix: [DISTRO_COMPATIBILITY.md](DISTRO_COMPATIBILITY.md).
### GPU (optional)
whisper.cpp uses Vulkan when available (AMD, Intel, NVIDIA). Check with `vulkaninfo --summary`.
## Running Voculinux
```bash
voculinux # if ~/.local/bin is on PATH
~/.local/share/voculinux/venv/bin/voculinux # direct
source venv/bin/activate && voculinux # source checkout
```
Or launch from the application menu.
The package captures audio through PipeWire (`pw-record`, `pw-dump`) and PortAudio.
PipeWire must be running in your session.
### CLI
@@ -262,22 +106,67 @@ voculinux --wayland
voculinux --start-minimized
```
## Autostart
The optional engines (`whisper`, `vosk`, `parakeet`) need their Python packages in your own
environment first; see [Speech engines](#speech-engines).
**Start on Login** writes an XDG autostart desktop entry (`~/.config/autostart/`). It does not install a systemd unit. Enable from the first-run dialog, tray menu, or Settings.
### Autostart
**Start on Login** writes an XDG autostart desktop entry (`~/.config/autostart/`). It does
not install a systemd unit. Enable from the first-run dialog, tray menu, or Settings.
## From a source checkout (developers)
Source checkouts are for development. For the two-venv layout and the dev workflow, see
[CONTRIBUTING.md](../CONTRIBUTING.md).
```bash
git clone https://git.bongbetic.com/xavierk/voculinux.git
cd voculinux
./install.sh --dev
source venv/bin/activate
voculinux
```
`./install.sh --help` lists the installer flags. The installer needs Python 3.11+ and the
distribution's PyGObject. Distribution notes: [DISTRO_COMPATIBILITY.md](DISTRO_COMPATIBILITY.md).
## Requirements
| | |
|--|--|
| **OS** | Void Linux (glibc, x86_64) for the published package. Source checkouts: see [DISTRO_COMPATIBILITY.md](DISTRO_COMPATIBILITY.md) |
| **Python** | 3.11 or newer |
| **Display** | X11 or Wayland |
| **Hardware** | Microphone; GPU optional (Vulkan) |
| **Disk** | ~200MB with default whisper.cpp model |
| **RAM** | 4GB minimum; 8GB comfortable |
The Void package depends on Void's own Python, PyGObject and GTK 3 packages. A source
checkout needs Python 3.11+ with PyGObject from the system (`python3-gobject` on Void).
Ubuntu 22.04 (Python 3.10) and Debian 11 (Python 3.9) are below that floor.
### GPU (optional)
whisper.cpp uses Vulkan when available (AMD, Intel, NVIDIA). The Void build enables Vulkan
by default. Check the driver with `vulkaninfo --summary`.
## Data locations (XDG)
| Directory | Purpose |
|-----------|---------|
| Path | Purpose |
|------|---------|
| `~/.config/voculinux/` | Configuration |
| `~/.local/share/voculinux/` | Data and speech models |
| `~/.local/share/applications/` | Desktop entry |
| `~/.local/share/icons/hicolor/scalable/apps/` | Icons |
| `~/.local/share/voculinux/` | Data and speech models (downloaded on first use) |
| `~/.config/autostart/` | Start on Login entry |
| `/usr/share/applications/voculinux.desktop` | Desktop entry, installed by the package |
| `/usr/share/icons/hicolor/scalable/apps/` | Icons, installed by the package |
Source installs put the desktop entry and icons under `~/.local/share/applications/` and
`~/.local/share/icons/hicolor/scalable/apps/` instead.
## whisper.cpp (default engine)
Default engine: C++ Whisper port with Vulkan GPU support and lower install cost than the PyTorch Whisper stack.
Default engine: C++ Whisper port with Vulkan GPU support and lower install cost than the
PyTorch Whisper stack.
| Size | Approx. | Use |
|------|---------|-----|
@@ -287,20 +176,32 @@ Default engine: C++ Whisper port with Vulkan GPU support and lower install cost
| medium | ~1.5GB | High accuracy |
| large | ~3.0GB | Best accuracy |
In Settings → Speech Model, simple setup steers language and speed/accuracy. Expand **Advanced** for engine, **Model Size**, and **Specialization** (multilingual, English-only, quantized, Turbo, legacy large). More detail: [USER_GUIDE.md](USER_GUIDE.md).
In Settings → Speech Model, simple setup steers language and speed/accuracy. Expand
**Advanced** for engine, **Model Size**, and **Specialization** (multilingual, English-only,
quantized, Turbo, legacy large). More detail: [USER_GUIDE.md](USER_GUIDE.md).
Reuse an existing `pywhispercpp` build:
```bash
./install.sh --auto # reuse if present
./install.sh --auto --rebuild-whispercpp # force rebuild
```
Switch engines in Settings → Speech Model (Advanced), or edit `~/.config/voculinux/config.json` (`engine`: `whisper_cpp` | `whisper` | `vosk` | `parakeet` | `remote_api`).
Switch engines in Settings → Speech Model (Advanced), or edit `~/.config/voculinux/config.json`
(`engine`: `whisper_cpp` | `whisper` | `vosk` | `parakeet` | `remote_api`).
## Uninstall
From a source checkout, `./uninstall.sh` removes the install and that checkout's `venv/`, `build/`, `dist/`, and Python bytecode. `--keep-config` and `--keep-data` leave `~/.config/voculinux` and `~/.local/share/voculinux` in place.
Remove the Void package:
```bash
sudo xbps-remove voculinux
```
This removes the package files only. Your configuration, downloaded models and autostart
entry live in your home directory and stay until you delete them:
```bash
rm -rf ~/.config/voculinux ~/.local/share/voculinux
rm -f ~/.config/autostart/voculinux.desktop
```
From a source checkout, `./uninstall.sh` removes the install and that checkout's `venv/`,
`build/`, `dist/`, and Python bytecode. `--keep-config` and `--keep-data` leave
`~/.config/voculinux` and `~/.local/share/voculinux` in place.
```bash
./uninstall.sh
@@ -308,41 +209,20 @@ From a source checkout, `./uninstall.sh` removes the install and that checkout's
./uninstall.sh --keep-data
```
The website command downloads this same script and runs it:
```bash
curl -fsSL https://raw.githubusercontent.com/VocaHQ/vocalinux/main/uninstall.sh -o /tmp/vul.sh
bash /tmp/vul.sh
```
That removes Voculinux from your home directory (config, data, launchers, desktop entry, icons). It does not delete `venv/`, `build/`, `dist/`, or `.pyc` files in the directory where you ran it.
From the source checkout, the equivalent by hand:
```bash
rm -rf venv ~/.config/voculinux ~/.local/share/voculinux
rm -f activate-voculinux.sh
rm -f ~/.local/share/applications/voculinux.desktop
rm -f ~/.local/share/icons/hicolor/scalable/apps/voculinux*.svg
gtk-update-icon-cache -f -t ~/.local/share/icons/hicolor
```
## Update
See [UPDATE.md](UPDATE.md).
Packaged installs update through xbps. See [UPDATE.md](UPDATE.md).
```bash
curl -fsSL https://raw.githubusercontent.com/VocaHQ/vocalinux/main/install.sh -o /tmp/vl.sh
bash /tmp/vl.sh
sudo xbps-install -Su voculinux
```
## More documentation
| Doc | Contents |
|-----|----------|
| [INSTALL_MANUAL.md](INSTALL_MANUAL.md) | Manual install, PyPI/pipx, per-distro packages |
| [TROUBLESHOOTING.md](TROUBLESHOOTING.md) | Common failures and fixes |
| [DISTRO_COMPATIBILITY.md](DISTRO_COMPATIBILITY.md) | Support matrix |
| [DISTRO_COMPATIBILITY.md](DISTRO_COMPATIBILITY.md) | Support matrix and source-checkout notes |
| [USER_GUIDE.md](USER_GUIDE.md) | Day-to-day use |
| [SUPPORT.md](../SUPPORT.md) | Where to get help |
| [Documentation index](README.md) | Full list |
+79 -59
View File
@@ -1,6 +1,7 @@
# Troubleshooting
Common install and runtime problems. Install overview: [INSTALL.md](INSTALL.md). Support channels: [SUPPORT.md](../SUPPORT.md).
Common install and runtime problems. Install overview: [INSTALL.md](INSTALL.md). Support
channels: [SUPPORT.md](../SUPPORT.md).
Always try once with debug logs:
@@ -10,15 +11,19 @@ voculinux --debug
## Command not found: `voculinux`
Void package: the package puts `voculinux` on your PATH. Check the install:
```bash
# Official install wrappers
export PATH="$HOME/.local/bin:$PATH"
voculinux
command -v voculinux # should print a path under /usr/bin
xbps-query voculinux # shows the installed package and its version
```
# Or run the venv binary
~/.local/share/voculinux/venv/bin/voculinux
If the package is missing, install it with `sudo xbps-install -S voculinux` (see
[INSTALL.md](INSTALL.md#install)).
# Source checkout
Source checkout:
```bash
source venv/bin/activate
voculinux
```
@@ -26,10 +31,12 @@ voculinux
## No audio / microphone not working
1. Check system sound settings and mute state
2. List capture devices: `arecord -l`
3. Run `voculinux --debug` and confirm the selected device
4. Reset a bad device index: set `audio.device_index` to `null` in `~/.config/voculinux/config.json`
5. Bluetooth mics: try a wired device to isolate SCO capture issues
2. Confirm PipeWire is running in your session. The package captures through PipeWire
(`pw-record`, `pw-dump`), and `pactl list short sources` should list your microphone
3. List ALSA capture devices: `arecord -l`
4. Run `voculinux --debug` and confirm the selected device
5. Reset a bad device index: set `audio.device_index` to `null` in `~/.config/voculinux/config.json`
6. Bluetooth mics: try a wired device to isolate SCO capture issues
### Microphone Stalled
@@ -42,39 +49,36 @@ dictation. **Test Dictation** is a separate speech-recognition test. See
[Audio input](USER_GUIDE.md#audio-input) for explicit microphone selection and
**System Default** behavior.
## GTK / AppIndicator / `No module named gi`
## Tray icon missing / `No module named gi`
Recreate the venv with system site packages:
Void package: the package depends on `python3-gobject`, `gtk+3` and
`libayatana-appindicator`. Update the package, which also pulls in any missing dependency:
```bash
sudo apt install python3-gi python3-gi-cairo # Debian/Ubuntu example
# tray: gir1.2-ayatanaappindicator3-0.1 or gir1.2-appindicator3-0.1
deactivate 2>/dev/null || true
rm -rf venv
/usr/bin/python3 -m venv venv --system-site-packages
source venv/bin/activate
pip install -e .
sudo xbps-install -Su voculinux
```
On GNOME, enable the AppIndicator extension if the tray icon never appears:
Source checkout: the venv must see the system PyGObject. Rebuild it with the installer:
```bash
# Debian/Ubuntu
sudo apt install gnome-shell-extension-appindicator
# Fedora
sudo dnf install gnome-shell-extension-appindicator
# Arch
sudo pacman -S gnome-shell-extension-appindicator
deactivate 2>/dev/null || true
rm -rf venv
./install.sh --dev
```
On GNOME, the tray icon needs an AppIndicator (StatusNotifier) extension. If the icon
never appears, install an AppIndicator extension from your distribution, then enable it:
```bash
gnome-extensions enable appindicatorsupport@rgcjonas.gmail.com
# log out/in if needed
```
Refresh icons:
Refresh icons. The package installs them under `/usr/share/icons/hicolor`; a source install
uses `~/.local/share/icons/hicolor`:
```bash
gtk-update-icon-cache -f -t ~/.local/share/icons/hicolor
sudo gtk-update-icon-cache -f -t /usr/share/icons/hicolor
voculinux --debug
```
@@ -83,36 +87,39 @@ voculinux --debug
**X11:**
```bash
sudo apt install xdotool # or distro equivalent
xdotool type "hello"
```
**Wayland:**
```bash
sudo apt install wtype wl-clipboard xclip xsel
wtype "hello"
printf "bonjour" | xsel --clipboard --input
printf "bonjour" | wl-copy
```
**KDE Plasma Wayland:** `wtype` may fail if the compositor does not expose a virtual keyboard to normal clients. Prefer **System Settings → Keyboard → Virtual Keyboard → IBus Wayland**, then restart Voculinux (or log out/in).
Clipboard paste uses `wl-clipboard`, `xclip` or `xsel`, whichever matches your session.
Install IBus if needed:
**Wayland hotkeys do nothing:** your user is not in the `input` group. See
[Wayland hotkeys need the input group](INSTALL.md#wayland-hotkeys-need-the-input-group).
**KDE Plasma Wayland:** `wtype` may fail if the compositor does not expose a virtual keyboard
to normal clients. Prefer **System Settings → Keyboard → Virtual Keyboard → IBus Wayland**,
then restart Voculinux (or log out/in). The package does not depend on IBus, so install it
first:
```bash
# Ubuntu/Debian
sudo apt install ibus
# Fedora
sudo dnf install ibus
# Arch
sudo pacman -S ibus
sudo xbps-install -S ibus
```
Compositor-specific notes: [DISTRO_COMPATIBILITY.md](DISTRO_COMPATIBILITY.md).
## Model download fails
Models download on first use. Manual VOSK example:
Models download on first use. whisper.cpp models are managed from Settings or the
first-run download.
VOSK is optional and is not in the package. If you install it in your own Python
environment, you can also place a model by hand. Example:
```bash
mkdir -p ~/.local/share/voculinux/models
@@ -122,19 +129,23 @@ unzip vosk-model-small-en-us-0.15.zip
```
More VOSK models: https://alphacephei.com/vosk/models
whisper.cpp models are managed from Settings or first-run download.
## `libwhisper.so` / pywhispercpp load errors
Void package: the package provides pywhispercpp. Import it directly to see the real error:
```bash
# Unresolved libs
ldd $(find ~/.local/share/voculinux/venv -name '*.so' -path '*/pywhispercpp*' 2>/dev/null | head -1) | grep 'not found'
python3 -c "import pywhispercpp.model"
```
# Rebuild whisper.cpp bindings via installer
If that fails, update the package (`sudo xbps-install -Su voculinux`) and run
`voculinux --debug` again.
Source checkout: look for unresolved libraries in the venv's bindings, then rebuild them:
```bash
ldd $(find venv -name '*.so' -path '*/pywhispercpp*' 2>/dev/null | head -1) | grep 'not found'
./install.sh --auto --rebuild-whispercpp
# Or switch to VOSK temporarily
voculinux --engine vosk
```
## Old process still running after update
@@ -149,27 +160,36 @@ voculinux
## Clean reinstall
Void package. Your configuration and models in your home directory are kept:
```bash
./uninstall.sh --keep-config --keep-data
curl -fsSL https://raw.githubusercontent.com/VocaHQ/vocalinux/main/install.sh -o /tmp/vl.sh
bash /tmp/vl.sh
sudo xbps-remove voculinux
sudo xbps-install -S voculinux
```
## Distro PyGObject not importable in the venv
Source checkout:
The distro `python3-gi` / `python3-gobject` package is installed, but `venv/` was built by a different Python than the one it targets. Current `install.sh` ignores an activated virtualenv and rebuilds a mismatched `venv/`. Otherwise:
```bash
./uninstall.sh --keep-config --keep-data
./install.sh --dev
```
## Source checkout: PyGObject not importable in the venv
The distribution's PyGObject package is installed, but `venv/` was built by a different
Python than the one it targets. Current `install.sh` ignores an activated virtualenv and
rebuilds a mismatched `venv/`. Otherwise:
```bash
deactivate 2>/dev/null || true
rm -rf venv
./install.sh
./install.sh --dev
```
On systems with several system interpreters: `SYSTEM_PYTHON=/usr/bin/python3.12 ./install.sh`.
On systems with several system interpreters: `SYSTEM_PYTHON=/usr/bin/python3.12 ./install.sh --dev`.
## Still stuck
- [SUPPORT.md](../SUPPORT.md)
- [GitHub Issues](https://github.com/VocaHQ/vocalinux/issues)
- [Discussions](https://github.com/VocaHQ/vocalinux/discussions)
- [Discord](https://discord.gg/t6muquAJbm)
- [Open issues](https://git.bongbetic.com/xavierk/voculinux/issues)
- [Report a bug](https://git.bongbetic.com/xavierk/voculinux/issues/new)
+61 -129
View File
@@ -4,30 +4,23 @@ How to upgrade an existing install, plus release notes for the current series.
## Quick update
Voculinux checks its own release server, the Gitea repository at `git.bongbetic.com/xavierk/voculinux`, in the background about every six hours (and shortly after startup). Upstream VocaHQ is never queried. When a newer build is on your selected channel, the tray menu gains an **Update Available...** entry and Settings -> About shows a green **New** badge; open that page for release notes and download links. Updates are not installed automatically. Re-run the installer (or your package manager) as below.
A package-managed install (the Void `voculinux` package) makes no update check at all. Its updates arrive with `sudo xbps-install -Su`, and Settings -> About says so.
### Official installer
Void package installs (the signed xbps repository registered in [INSTALL.md](INSTALL.md)):
```bash
curl -fsSL https://raw.githubusercontent.com/VocaHQ/vocalinux/main/install.sh -o /tmp/vl.sh
bash /tmp/vl.sh
sudo xbps-install -Su voculinux
```
The installer detects a running instance, updates in place, preserves configuration and models, and pulls new dependencies (including neural VAD when available).
A package-managed install makes no release check of its own, and Settings → About says so.
Updates arrive through xbps only.
On an existing install (a virtual environment plus `~/.config/voculinux/config.json`), the installer reports the saved configuration and defaults to **Upgrade (keep current settings)**: the configured engine stays selected, `config.json` is never rewritten, the virtual environment is reused, downloaded models are kept, and pywhispercpp is rebuilt only when this release pins a new version or GPU support is missing. Pick the interactive or automatic modes instead if you want to reconfigure the install.
Source checkouts (developers) check this project's own release server, the Gitea repository at
`git.bongbetic.com/xavierk/voculinux`, in the background about every six hours and shortly
after startup. When a newer build is on your selected channel, the tray menu gains an
**Update Available...** entry and Settings → About shows a **New** badge; open that page for
release notes and download links. Updates are not installed automatically. Re-run the
installer as below.
For a scripted, non-interactive upgrade:
```bash
bash /tmp/vl.sh --upgrade
```
`--upgrade` on a machine with no existing install falls back to a normal automatic installation.
### Installed from source
### Source checkout
```bash
cd voculinux
@@ -44,15 +37,20 @@ git pull origin main
./install.sh
```
### Other install methods
On an existing install (a virtual environment plus `~/.config/voculinux/config.json`),
`./install.sh` reports the saved configuration and defaults to **Upgrade (keep current
settings)**: the configured engine stays selected, `config.json` is never rewritten, the
virtual environment is reused, downloaded models are kept, and pywhispercpp is rebuilt only
when this release pins a new version or GPU support is missing. Pick the interactive or
automatic modes instead if you want to reconfigure the install.
| Method | Upgrade |
|--------|---------|
| AUR | `yay -S voculinux` (or your AUR helper) |
| AppImage | Download the new file from [Releases](https://github.com/VocaHQ/vocalinux/releases) |
| Snap | Store: `sudo snap refresh voculinux` (`--edge` until stable is promoted). The recipe ships no `uinput` plug (the Store denied the grant); Wayland typing uses the RemoteDesktop portal. The GitHub `voculinux_0.18.2_amd64.snap` still declares `uinput` for KDE users who want ydotool (`sudo snap install --dangerous`, then `sudo snap connect voculinux:uinput`). |
| Flatpak (release bundle) | Install the new `.flatpak` from Releases; bundles do not auto-update |
| PyPI | Reinstall in the same venv after system packages are current |
For a scripted, non-interactive upgrade:
```bash
./install.sh --upgrade
```
`--upgrade` on a machine with no existing install falls back to a normal automatic installation.
### Check your version
@@ -64,21 +62,22 @@ python3 -c "import vocalinux; print(vocalinux.version.__version__)"
### Update problems
Clean reinstall (keeps config and models by default):
Clean reinstall (keeps config and models by default). Void package:
```bash
# Source checkout
./uninstall.sh --keep-config --keep-data
# Curl install (safe to run from any directory)
curl -fsSL https://raw.githubusercontent.com/VocaHQ/vocalinux/main/uninstall.sh -o /tmp/vul.sh
bash /tmp/vul.sh --keep-config --keep-data
curl -fsSL https://raw.githubusercontent.com/VocaHQ/vocalinux/main/install.sh -o /tmp/vl.sh
bash /tmp/vl.sh
sudo xbps-remove voculinux
sudo xbps-install -S voculinux
```
If an old process is stuck, stop via the tray or the PID in the instance lock file (do not use `pkill -f voculinux`; it can kill unrelated processes):
Source checkout:
```bash
./uninstall.sh --keep-config --keep-data
./install.sh
```
If an old process is stuck, stop via the tray or the PID in the instance lock file (do not use
`pkill -f voculinux`; it can kill unrelated processes):
```bash
kill "$(tr -d '[:space:]' < "${XDG_DATA_HOME:-$HOME/.local/share}/voculinux/instance.lock")"
@@ -104,20 +103,16 @@ Missing system packages: see [INSTALL.md](INSTALL.md) or [DISTRO_COMPATIBILITY.m
| **Keyboards survive suspend and lock** | The evdev forwarder releases its EVIOCGRAB when forwarding cannot run, so keyboards no longer go dead at the GNOME lock screen after a suspend/resume (#1025; fixes #1021 reported by @iholder101) |
| **Dialogs stay on their own workspace** | Dictation Pad, Settings and other standalone windows no longer bind to other windows on Wayland, so opening Settings no longer summons or minimizes the Dictation Pad with it (#1024; fixes #984 reported by @hopsayer) |
| **Ctrl+mouse-wheel zoom works** | Ctrl key events pass through for applications again, so holding Ctrl and scrolling zooms in Firefox and elsewhere even with a double-tap Ctrl shortcut configured (#967 by @loject; fixes #950 reported by @loject) |
| **Upgrade mode in the installer** | `install.sh` on an existing install offers and defaults to an upgrade that keeps your configuration and models, and `bash /tmp/vl.sh --upgrade` runs it non-interactively (#1026; fixes #1009 reported by @plaffitt) |
| **Upgrade mode in the installer** | `./install.sh` on an existing install offers and defaults to an upgrade that keeps your configuration and models, and `./install.sh --upgrade` runs it non-interactively (#1026; fixes #1009 reported by @plaffitt) |
### Also in v0.18.5
- Paste-injection copies are marked sensitive (`wl-copy --sensitive`) on wl-clipboard 2.2+, so clipboard managers that honor the hint, such as Klipper or cliphist, keep dictated text out of their history; the xclip/xsel path and older wl-copy have no such hint (#956 by @Nosion)
- AUR source publication is gated on attested release sdists: the recipe is prepared only from the canonical sdist whose checksum and build provenance verify against this repository's release workflow (#1020 by @sesav)
- The tray prefers libayatana-appindicator-glib when it is available (#1019; fixes #1013 reported by @jo-so)
- The Snap package lists PulseAudio microphones and explains that its whisper.cpp build is CPU-only (#1018; fixes #1011 reported by @cmcclung-ANGENG)
- The Wayland fallback overlay stays alive when the dock closes it, covering the leftover case from 0.18.4 (#1017; fixes #1010 reported by @lavoie005)
- uv pins are aligned across CI and the release workflows (#1016 by @sesav), release build tooling is pinned with a reproducible-wheel gate (#945 by @sesav), the supported uv range is widened with Fedora/AppImage build hardening (#952 by @sesav), and Dependabot bumps cover pip and GitHub Actions plus flagged vulnerable dependencies (#1014, #1015, #1008 by @Mr-Sunglasses)
- uv pins are aligned across CI and the release workflows (#1016 by @sesav), release build tooling is pinned with a reproducible-wheel gate (#945 by @sesav), the supported uv range is widened with Fedora build hardening (#952 by @sesav), and Dependabot bumps cover pip and GitHub Actions plus flagged vulnerable dependencies (#1014, #1015, #1008 by @Mr-Sunglasses)
AppImage, Flatpak, and Snap still ship whisper.cpp only.
See the [full changelog](https://github.com/VocaHQ/vocalinux/releases/tag/v0.18.5).
See the [full changelog](https://git.bongbetic.com/xavierk/voculinux/releases/tag/v0.18.5).
---
@@ -139,8 +134,6 @@ See the [full changelog](https://github.com/VocaHQ/vocalinux/releases/tag/v0.18.
### Also in v0.18.4
- Keyboards whose evdev node also reports pointer axes, such as the Logitech K860, are monitored again without being grabbed, so `right_alt` and other hotkeys fire (#978 by @ikarius; fixes #953 reported by @dherdt)
- The Flatpak bundles onnxruntime, so Silero VAD works there instead of falling back to amplitude VAD (#1002; fixes #969 reported by @samodadela)
- The Flatpak uses the X11 hotkey listener on native X11 sessions, so it no longer needs `--device=all` there (#997; fixes #971 reported by @samodadela)
- RemoteDesktop portal requests each get a unique handle token, which fixes the missing GNOME consent dialog and the 15 second timeout (#996; fixes #993 reported by @hal2100)
- XWayland apps that are not connected to IBus, such as SoftMaker TextMaker, receive text through xdotool (#1003; fixes #972 reported by @kdseattlewa)
- On GNOME Wayland the dictation overlay is mapped at startup, so the first recording no longer steals focus from the app you are typing into (#998; fixes #992 reported by @lavoie005)
@@ -148,15 +141,11 @@ See the [full changelog](https://github.com/VocaHQ/vocalinux/releases/tag/v0.18.
- Update checks follow the build channel, so stable installs only hear about stable releases, and Settings can turn them off (#999; fixes #977 reported by @hopsayer)
- Faster Settings, typing and dictation, plus mypy as a CI gate (#968)
AppImage, Flatpak, and Snap still ship whisper.cpp only.
See the [full changelog](https://github.com/VocaHQ/vocalinux/releases/tag/v0.18.4).
---
## What's New in v0.18.3
0.18.3 is a **patch** on the stable line. Default engine is still whisper.cpp. This one is all fixes: "new line" and "new paragraph" finally insert real line breaks, hotkeys come back on GNOME Shell X11, packaged builds stop crashing on older CPUs, and the Snap package can publish again now that the denied `uinput` plug is gone.
0.18.3 is a **patch** on the stable line. Default engine is still whisper.cpp. This one is all fixes: "new line" and "new paragraph" finally insert real line breaks, hotkeys come back on GNOME Shell X11, and release builds stop crashing on older CPUs.
### 0.18.3 highlights
@@ -165,51 +154,36 @@ See the [full changelog](https://github.com/VocaHQ/vocalinux/releases/tag/v0.18.
| **Real line breaks** | "new line" and "new paragraph" insert an actual newline standalone or mid-utterance, and the stray punctuation they used to leave behind is gone (#958, #962; fixes #955 reported by @rastarr) |
| **GNOME X11 hotkeys** | Push-to-talk and toggle shortcuts work on gnome-shell X11 sessions again; the app now prefers the evdev backend there (#963; fixes #954 reported by @jepst) |
| **Layout restore on quit** | Quitting no longer collapses a multi-layout keyboard map (us,de staying us,de) because the IBus engine restore is skipped when a real engine is already active (#963) |
| **Older CPUs unblocked** | Snap, Flatpak, AppImage and the .deb/.rpm builds now compile the bundled pywhispercpp at the true x86-64 baseline, ending the SIGILL crash on pre-Haswell processors (#959; fixes #957 reported by @bucketsofmail) |
| **Snap can publish** | The `uinput` plug the Snap Store denied is removed, so new revisions clear automatic review again; Wayland typing uses the RemoteDesktop portal (#960) |
| **Older CPUs unblocked** | Release builds now compile the bundled pywhispercpp at the true x86-64 baseline, ending the SIGILL crash on pre-Haswell processors (#959; fixes #957 reported by @bucketsofmail) |
### Also in v0.18.3
- The Flatpak remote publish warns on the release run when its secrets are not configured, instead of skipping silently for tags (#961; fixes #951 reported by @andreacasini)
- README gains a badge row for every install format plus a Nix flake section (#949)
- Transcribe File's modal model-download dialog now closes itself once TinyDiarize lands, releasing its grab so the transcript's Copy and Save As buttons are clickable (#965; found during release e2e testing)
AppImage, Flatpak, and Snap still ship whisper.cpp only.
See the [full changelog](https://github.com/VocaHQ/vocalinux/releases/tag/v0.18.3).
---
## What's New in v0.18.2
0.18.2 is a **patch** on the stable line. Default engine is still whisper.cpp. The headline is a livelier dictation overlay plus a round of fixes: the overlay orb now reacts to live microphone level, Nix users get a flake package with NixOS and Home Manager modules and Vulkan GPU inference, and the dictation shortcut can no longer strand a key when you are typing while the app starts.
0.18.2 is a **patch** on the stable line. Default engine is still whisper.cpp. The headline is a livelier dictation overlay plus a round of fixes: the overlay orb now reacts to live microphone level, and the dictation shortcut can no longer strand a key when you are typing while the app starts.
### 0.18.2 highlights
| Feature | Description |
|---------|-------------|
| **Live mic orb** | Dictation overlay orb pulses and ripples with your real audio level instead of sitting static (#940) |
| **Nix packaging** | Flake package plus NixOS and Home Manager modules (#936), and Vulkan GPU inference for pywhispercpp on Nix (#942) |
| **Safer startup grab** | A keyboard with a held key or queued key events is left ungrabbed at startup, so typing during launch cannot stick a key on the desktop (#941) |
| **Older CPU fix** | Bundled pywhispercpp builds no longer use -march=native, so AppImage and Flatpak stop crashing on CPUs without the build host's instruction set (#939) |
| **Piped installer** | `curl \| bash` keeps the script on stdin and prompts via /dev/tty, so interactive prompts work mid-pipe (#935) |
| **Older CPU fix** | Bundled pywhispercpp builds no longer use -march=native, so release builds stop crashing on CPUs without the build host's instruction set (#939) |
### Also in v0.18.2
- Installer package map covers cairo headers on openSUSE, Void, Solus and Alpine (#937)
- evdev monitors remapper virtual keyboards ungrabbed so remappers like keyd keep working (#932)
- The install section on voculinux.com showcases the .deb, .rpm, Snap, Flatpak and AUR packages (#929)
- The remote `curl | bash` install path is gated end to end in CI (#898)
AppImage, Flatpak, and Snap still ship whisper.cpp only.
See the [full changelog](https://github.com/VocaHQ/vocalinux/releases/tag/v0.18.2).
---
## What's New in v0.18.1
0.18.1 is a **patch** on the stable line. Default engine is still whisper.cpp. This one is a reliability release: most of it is fixes, and the new features are plumbing you will notice only when you need it. Settings gains a Proxy page so model downloads and update checks work behind SOCKS5 and HTTP proxies, and an opt-in switch that persists transcripts to disk so a failed injection stops meaning a lost sentence. Releases now attach native .deb and .rpm packages, and the tray menu drops Start on Login and About (both already lived in Settings).
0.18.1 is a **patch** on the stable line. Default engine is still whisper.cpp. This one is a reliability release: most of it is fixes, and the new features are plumbing you will notice only when you need it. Settings gains a Proxy page so model downloads and update checks work behind SOCKS5 and HTTP proxies, and an opt-in switch that persists transcripts to disk so a failed injection stops meaning a lost sentence. The tray menu drops Start on Login and About (both already lived in Settings).
### 0.18.1 highlights
@@ -217,7 +191,6 @@ See the [full changelog](https://github.com/VocaHQ/vocalinux/releases/tag/v0.18.
|---------|-------------|
| **Proxy settings** | New Settings -> Proxy page: off, system, or manual SOCKS5/HTTP CONNECT with optional auth; covers every model download and the update checker (#909, fixes #655) |
| **Transcript persistence** | Opt-in JSONL history on disk; a transcript survives restarts and failed injections (#907, fixes #758) |
| **Native packages** | Thin .deb and .rpm for Debian/Ubuntu and Fedora, x86_64 and aarch64, attached to the GitHub Release (#913, step 1 of #600) |
| **Slimmer tray menu** | Start on Login and About removed from the tray; both already live in Settings (#905, fixes #654) |
| **Staged model picker** | Speech Model Advanced pickers no longer start a download per click; changes stage until you confirm Download (#908, fixes #894) |
| **Wayland pad fix** | Dictation Pad no longer ghosts away after sitting idle under a Wayland compositor (#912, fixes #896) |
@@ -231,12 +204,6 @@ See the [full changelog](https://github.com/VocaHQ/vocalinux/releases/tag/v0.18.
- Test Dictation transcription readout moved above its controls in the sidebar footer (#904, fixes #677 reported by @hopsayer)
- `uninstall.sh` no longer deletes files in the directory you ran it from (#901, fixes #897 reported by @SilverChatte)
- Settings > Advanced Initial Prompt accepts typed input again (clicks were being stolen by the enclosing row), and transcript and log saves no longer write a doubled `.txt.txt` name (#927)
- Packaging: `.deb` declares `python3-socks` and `.rpm` declares `python3-pysocks` for SOCKS5 proxy support (#920)
- CI: snap attach uses `--repo`, checksums still publish when an asset attach fails, the Vulkan pywhispercpp build is reused across runs, and the remote `curl | bash` install path is gated end to end (#899, #903, #906)
AppImage, Flatpak, and Snap still ship whisper.cpp only.
See the [full changelog](https://github.com/VocaHQ/vocalinux/releases/tag/v0.18.1).
---
@@ -273,22 +240,16 @@ See the [full changelog](https://github.com/VocaHQ/vocalinux/releases/tag/v0.18.
- Settings: scrollable category list, Dictation Tone grayed while sound effects are off, Test Dictation textbox artifact and missing transcription fixed, speech-model follow-ups (#886, #877, #853, #836, fixes #678, #849, #847, #720, #834)
- Update checker falls back when GitHub API rate-limits instead of erroring (#846, fixes #845 reported by @lmstud)
- Audio: stop passing an explicit index when zero devices enumerate; View Logs closes via the titlebar X (#891, #892)
- Snap gains `hardware-observe` so hotkeys can read input devices (#858, fixes #857 reported by @RhysU)
- Installer installs hash-pinned dependencies and build tools, and is split into sourced modules with a generated distro package map (#856, #862, #872 by @sesav)
- AUR `voculinux-bin` ships the AppImage with per-arch digests; releases publish a signed self-hosted Flatpak OSTree remote; a snap-promote workflow gates stable promotion (#879, #875, #881, fixes #817, #785, #783)
- VocaGateway can run locally from Settings → Advanced (podman-first) (#774)
- The local gateway can run from Settings → Advanced (podman-first) (#774)
- Dependency advisories cleared and workflow tokens scoped to repository reads (#869, #865, #866 by @Mr-Sunglasses, #870)
- Settings dialog consolidated: guard flags and widget construction in one place each (#878, fixes #793)
AppImage, Flatpak, and Snap still ship whisper.cpp plus the same engine matrix as 0.17.
See the [full changelog](https://github.com/VocaHQ/vocalinux/releases/tag/v0.18.0).
---
## What's New in v0.17.0
0.17.0 is a **minor** on the stable line. Default engine is still whisper.cpp. This release adds two optional local engines (Faster Whisper and Parakeet), a simpler Speech Model page, Snap packaging with native Wayland typing, and Flatpak bundles built by the release workflow. It also fixes XWayland/layout paste, a KDE xdotool crash, push-to-talk tray redraw, clipped start/stop cues, HDA analog mics that abort when opened below native channel count, native GTK installs that ignored OS dark/light, and English-only models that hid every other language.
0.17.0 is a **minor** on the stable line. Default engine is still whisper.cpp. This release adds two optional local engines (Faster Whisper and Parakeet) and a simpler Speech Model page. It also fixes XWayland/layout paste, a KDE xdotool crash, push-to-talk tray redraw, clipped start/stop cues, HDA analog mics that abort when opened below native channel count, native GTK installs that ignored OS dark/light, and English-only models that hid every other language.
### 0.17 series highlights
@@ -301,8 +262,6 @@ See the [full changelog](https://github.com/VocaHQ/vocalinux/releases/tag/v0.18.
| **Searchable open picker** | Language list filters while open (#798) |
| **Localized punctuation commands** | it/fr/de/es/pt/nl/pl/ru; English phrases still work (#642) |
| **Bare F-keys** | F1–F24 are valid push-to-talk shortcuts (#815) |
| **Snap** | Store listing, ydotool + `uinput` for native Wayland. The Store later denied the `uinput` grant, so the plug was dropped and Wayland typing moved to the RemoteDesktop portal (#519, #823, #822) |
| **Flatpak on the tag** | Workflow attaches `.flatpak` assets and checksums them (#786) |
### Also in v0.17.0
@@ -314,27 +273,23 @@ See the [full changelog](https://github.com/VocaHQ/vocalinux/releases/tag/v0.18.
- Applying an already-downloaded model no longer freezes Settings (#790)
- Selected language picks the whisper.cpp variant (English → `.en`). Picking another language, or auto-detect, switches off `.en` / `.en-q*` instead of hiding the rest of the list (#795, #780, #826)
- Speech Model labels are nouns (Language, Other languages, Speed vs accuracy). Unused downloads is a sibling expander of Advanced, not nested inside it (#826)
- About family grid includes VocaWin (#826)
- Push-to-talk tray icon turns red on every hold (#809)
- Sink-wake no longer clips start/stop cues (#804)
- Open 3-8 channel HDA analog mics at native layout instead of forcing 2ch/48kHz, so PortAudio no longer aborts after read() (`free(): corrupted unsorted chunks`); capture is downmixed to mono for engines (#829, fixes #813)
- Native GTK installs (AUR / install.sh) follow OS dark/light via the appearance portal then gsettings; skipped when `GTK_THEME` is set so AppImage and user overrides stay in charge (#830, fixes #816)
- Native GTK installs (`install.sh`) follow OS dark/light via the appearance portal then gsettings; skipped when `GTK_THEME` is set so user overrides stay in charge (#830, fixes #816)
- Parakeet keeps recognizing after a decode error (#803)
- Flatpak/AUR deps derived from `uv.lock` (#819)
- Hash-pinned exports for VOSK, Parakeet, and Faster Whisper extras so `install.sh` no longer installs those unpinned; extras are version-capped (#828)
- `just verify-release` checks the published GitHub Release (#791)
- Distro matrix runs `install.sh` (#810)
- `install.sh` Faster Whisper box is CPU CTranslate2 / INT8, not NVIDIA CUDA. The whisper.cpp GPU step does not install the CUDA toolkit; Vulkan is first, CUDA only if a toolkit is already on the machine (#826)
AppImage, Flatpak and Snap remain whisper.cpp only. Faster Whisper and Parakeet are optional extras on `install.sh` / source installs.
See the [full changelog](https://github.com/VocaHQ/vocalinux/releases/tag/v0.17.0).
Faster Whisper and Parakeet are optional extras on `install.sh` / source installs. They are not part of the Void package.
---
## What's New in v0.16.2
0.16.2 is a **stability patch** on the 0.16 series. The feature set is the same as 0.16.x. This release fixes KDE leftover IBus so dictation types into real apps, Wayland and IBus keyboard shortcuts through wtype/ydotool, real BackSpace for "delete that", Fedora/Arch installer glslc packages, nightly version stamping, and release integrity pins. CI now gates AUR PKGBUILD builds and the distros the docs promise.
0.16.2 is a **stability patch** on the 0.16 series. The feature set is the same as 0.16.x. This release fixes KDE leftover IBus so dictation types into real apps, Wayland and IBus keyboard shortcuts through wtype/ydotool, real BackSpace for "delete that", Fedora/Arch installer glslc packages, nightly version stamping, and release integrity pins. CI now gates the distros the docs promise.
### 0.16 series highlights
@@ -344,32 +299,28 @@ See the [full changelog](https://github.com/VocaHQ/vocalinux/releases/tag/v0.17.
| **Right Alt PTT default** | New installs default to hold Right Alt (push-to-talk); existing configs keep their shortcut (#648) |
| **Searchable languages** | Type to filter the Speech Model language list (#672) |
| **Delete unused models** | Remove leftover downloaded speech models from Settings (#671) |
| **AGPL-3.0** | License aligned with other VocaHQ projects (#660) |
| **AGPL-3.0** | License is AGPL-3.0 (#660) |
| **Family mic icons** | App icon, tray states, and site favicons use the shared Voca family mic (#704) |
| **Tone picker** | Settings → Audio: Lift, Flick, Ember, Step, Voca, Soft, Chirp, Scale, Drop, Glass, Off, plus Preview. New installs default to Voca. Catalog uses family preview WAVs (#707, #708) |
| **Installer** | Justfile, uv lockfiles, distro python3-gi required (no pip sdist of PyGObject). Epic #701 still open (#700, #705, #706) |
### Bug fixes in v0.16.2
- **KDE inject**: skip leftover IBus when it is not the session IM so dictation types into Kate, browsers, and terminals (#753 by @jatinkrmalik, fixes #752 by @justTravis)
- **KDE inject**: skip leftover IBus when it is not the session IM so dictation types into Kate, browsers, and terminals (#753, fixes #752 by @justTravis)
- **Wayland shortcuts**: wtype and ydotool deliver real chords instead of refusing or silently doing nothing (#715 by @eiseleb47)
- **IBus shortcuts**: route X11_IBUS through xdotool and WAYLAND_IBUS through wtype/ydotool (#716 by @eiseleb47)
- **Delete that**: send real BackSpace key events instead of U+0008 text (#714 by @eiseleb47)
- **Installer**: Fedora and Arch need glslc/shaderc packages, not glslang (#763 by @jatinkrmalik, see #604)
- **Installer**: Fedora and Arch need glslc/shaderc packages, not glslang (#763, see #604)
- **Nightly**: stamp `version.py` before `python -m build` so wheel metadata matches the filename (#762 by @sesav)
- **Release integrity**: checksums, signatures, and pinned builders for release artifacts (#759 by @sesav, epic #701 phase 5)
- **CI**: gate AUR PKGBUILD builds on every PR (#772 by @sesav)
- **CI / docs**: test the distros the docs promise and fix docs drift (#773 by @sesav)
- **Site**: VocaGateway family card is Beta; README logo, badges, and privacy copy (#765 by @jatinkrmalik, #764)
- **Snap**: v0.16.2 `latest/edge` (rev 7) has no `uinput` plug. `sudo snap connect voculinux:uinput` errors with `snap "voculinux" has no plug named "uinput"`. Dictation only reaches XWayland apps. (v0.17.0+ added the plug, but the Store denied it, so later revisions drop it again and type via the RemoteDesktop portal.) (#823)
See the [full changelog](https://github.com/VocaHQ/vocalinux/releases/tag/v0.16.2).
- **Site**: the gateway card is Beta; README logo, badges, and privacy copy (#765, #764)
---
## What's New in v0.16.1
0.16.1 is a **stability patch** on the 0.16 series. The feature set is the same as 0.16.x. This release fixes wrong-model startup, leftover tray state after toggle stop, paste into terminals, GNOME XWayland layout after inject, and AppImages that needed a glibc newer than Debian 12. The installer now requires Python 3.11 and verifies model downloads.
0.16.1 is a **stability patch** on the 0.16 series. The feature set is the same as 0.16.x. This release fixes wrong-model startup, leftover tray state after toggle stop, paste into terminals, and GNOME XWayland layout after inject. The installer now requires Python 3.11 and verifies model downloads.
### 0.16 series highlights
@@ -379,7 +330,7 @@ See the [full changelog](https://github.com/VocaHQ/vocalinux/releases/tag/v0.16.
| **Right Alt PTT default** | New installs default to hold Right Alt (push-to-talk); existing configs keep their shortcut (#648) |
| **Searchable languages** | Type to filter the Speech Model language list (#672) |
| **Delete unused models** | Remove leftover downloaded speech models from Settings (#671) |
| **AGPL-3.0** | License aligned with other VocaHQ projects (#660) |
| **AGPL-3.0** | License is AGPL-3.0 (#660) |
| **Family mic icons** | App icon, tray states, and site favicons use the shared Voca family mic (#704) |
| **Tone picker** | Settings → Audio: Lift, Flick, Ember, Step, Voca, Soft, Chirp, Scale, Drop, Glass, Off, plus Preview. New installs default to Voca. Catalog uses family preview WAVs (#707, #708) |
| **Installer** | Justfile, uv lockfiles, distro python3-gi required (no pip sdist of PyGObject). Epic #701 still open (#700, #705, #706) |
@@ -396,13 +347,9 @@ See the [full changelog](https://github.com/VocaHQ/vocalinux/releases/tag/v0.16.
- **IBus**: sync the XWayland layout from GNOME after scoped inject (#742)
- **Installer**: survive a release without a checksum manifest; stop inventing GPUs (#736 by @sesav)
- **Installer**: leftover engine repair, just `--no-sync`, ggml verify from #713 (#732)
- **AppImage**: build against a glibc floor Debian 12 can run; boot tests on six distros (#743, #744 by @sesav)
- **Installer**: Python 3.11 floor, uv tooling, verified model downloads (epic #701 phases 2.5 and 5) (#713 by @sesav)
- **Settings / About**: even dropdowns, quieter About, family platform marks (#754)
- **AUR**: `python -m build --no-isolation` works with Arch extra setuptools 84 (was capped at `<82`, AUR comment by simona). Skip `context_params` on AUR pywhispercpp 1.4.x so startup no longer dies with `whisper_full_params` (AUR comments by avocadoboat, Masalababa; GitHub #625)
- **Docs**: canonical VocaHQ Discord invite; VocaWin is unsigned beta; screenshots page says v0.16; README family/on-device copy (#749, #733, #735, #737)
See the [full changelog](https://github.com/VocaHQ/vocalinux/releases/tag/v0.16.1).
- **Docs**: screenshots page says v0.16; README family/on-device copy (#749, #733, #735, #737)
---
@@ -418,7 +365,7 @@ See the [full changelog](https://github.com/VocaHQ/vocalinux/releases/tag/v0.16.
| **Right Alt PTT default** | New installs default to hold Right Alt (push-to-talk); existing configs keep their shortcut (#648) |
| **Searchable languages** | Type to filter the Speech Model language list (#672) |
| **Delete unused models** | Remove leftover downloaded speech models from Settings (#671) |
| **AGPL-3.0** | License aligned with other VocaHQ projects (#660) |
| **AGPL-3.0** | License is AGPL-3.0 (#660) |
| **Family mic icons** | App icon, tray states, and site favicons use the shared Voca family mic (#704) |
| **Tone picker** | Settings → Audio: Lift, Flick, Ember, Step, Voca, Soft, Chirp, Scale, Drop, Glass, Off, plus Preview. New installs default to Voca. Catalog uses family preview WAVs (#707, #708) |
| **Installer** | Justfile, uv lockfiles, distro python3-gi required (no pip sdist of PyGObject). Epic #701 still open (#700, #705, #706) |
@@ -433,7 +380,6 @@ See the [full changelog](https://github.com/VocaHQ/vocalinux/releases/tag/v0.16.
- **Disable missing-tray warning**: Settings toggle for desktops that false-positive the tray check (#628, fixes #620)
- **Family mic icons**: App icon, tray states, and site favicons use the shared Voca family mic instead of the old Linux rounded-rect (#704)
- **Family dictation tone picker**: Settings → Audio dropdown (Lift, Flick, Ember, Step, Voca, Soft, Chirp, Scale, Drop, Glass, Off) plus Preview. New installs and unknown saved names default to Voca. A saved catalog id, including Off, is left alone. Enable remains the master mute; Off skips start/stop only (#707)
- **About page**: Settings → About groups this app, VocaHQ family sites, and talk-to-us (GitHub issues, Discord, X, email) (#718)
### Bug Fixes
@@ -444,7 +390,6 @@ See the [full changelog](https://github.com/VocaHQ/vocalinux/releases/tag/v0.16.
- **Tray / Settings**: Prefer Ayatana AppIndicator on KDE (#621); reuse Settings/Logs windows (#669, fixes #653); separate Close from Test Dictation (#670, fixes #651)
- **Settings**: Test Dictation no longer reports no speech when recognition never started (missing model / auto-pause / live engine out of sync) (#702)
- **Clipboard**: Restore after ydotool clipboard-paste (#588); text-only reads and safer overlapping restore (#646)
- **AppImage**: Ship transitive GI typelibs for non-Debian hosts (also hotfixed onto the v0.15.0 AppImages on 2026-08-03) (#637); pin pywhispercpp to the version `install.sh` declares so a newer PyPI wheel cannot break the Vulkan rebuild (#718)
- **Installer**: Distro python3-gi is required; pip no longer builds PyGObject from sdist. Unset `XDG_SESSION_TYPE` / `XDG_CURRENT_DESKTOP` no longer crash under `set -u` (#706)
- **Installer / downloads / tests**: Gate `util-linux-extra` to Ubuntu 24.04+ (#635, fixes #526); report failed model downloads (#690); stop the suite from overwriting real `config.json` (#694)
@@ -453,27 +398,16 @@ See the [full changelog](https://github.com/VocaHQ/vocalinux/releases/tag/v0.16.
- Website screenshot refresh for v0.15 (#630)
- Prefer Ayatana AppIndicator in Fedora/Arch packaging hints (#638)
- CUDA device 0 note for dual NVIDIA (#644)
- Discord and VocaHQ README shields; VocaHQ URL migration; VocaGateway rename (#695, #696, #697)
- Discord invite and X handle point at VocaHQ (#722)
- README badges; gateway rename (#695, #696, #697)
- voculinux.com restyled to the Voca family workbench; Open Graph card uses the flat Tux (#728, #729)
- Website copy drops the stale 100% offline claim and marks VocaWin as alpha on the site (#717)
- Codeberg mirror tag force-push (#633)
- Website copy drops the stale 100% offline claim (#717)
- Installer hardening, Justfile in place of Makefile, and uv lockfiles with pinned build inputs. Epic #701 remains open (#700, #705 by @sesav)
See the [full changelog](https://github.com/VocaHQ/vocalinux/releases/tag/v0.16.0).
---
## Older releases
Notes for v0.15.0 and earlier live on GitHub Releases:
- [v0.15.0](https://github.com/VocaHQ/vocalinux/releases/tag/v0.15.0)
- [v0.14.2](https://github.com/VocaHQ/vocalinux/releases/tag/v0.14.2)
- [v0.14.1](https://github.com/VocaHQ/vocalinux/releases/tag/v0.14.1)
- [v0.14.0-beta](https://github.com/VocaHQ/vocalinux/releases/tag/v0.14.0-beta)
Full history: https://github.com/VocaHQ/vocalinux/releases
Release notes for v0.15.0 and earlier are not included in this document.
---
@@ -483,6 +417,4 @@ Full history: https://github.com/VocaHQ/vocalinux/releases
- [Troubleshooting](TROUBLESHOOTING.md)
- [User guide](USER_GUIDE.md)
- [Support](../SUPPORT.md)
- [Report issues](https://github.com/VocaHQ/vocalinux/issues)
- [Discussions](https://github.com/VocaHQ/vocalinux/discussions)
- [Discord](https://discord.gg/t6muquAJbm)
- [Report issues](https://git.bongbetic.com/xavierk/voculinux/issues)
+6 -1
View File
@@ -9,6 +9,11 @@ Speech models are not in this package: they
download on first run into ~/.local/share/voculinux
and are checked against pinned checksums.
Audio capture uses PipeWire (pw-record and pw-dump,
from the pipewire package this one depends on), so
PipeWire must be running in your session. PortAudio
(python3-PyAudio) is the other capture path.
Optional engines are manual additions outside the
guaranteed set: vosk, OpenAI whisper, Parakeet
(sherpa-onnx), faster-whisper and onnxruntime (VAD)
@@ -26,4 +31,4 @@ injection fallback rung is absent.
Update through your package manager instead of the
in-app release downloads:
sudo xbps-install -Syu voculinux
sudo xbps-install -Su voculinux
+42 -181
View File
@@ -1,4 +1,4 @@
"""About page content and trusted family links for Settings."""
"""About page content and trusted project links for Settings."""
import logging
import os
@@ -20,41 +20,29 @@ from ..version import __copyright__, __url__, __version__
logger = logging.getLogger(__name__)
VOCULINUX_SITE_URL = "https://voculinux.com"
VOCAHQ_SITE_URL = "https://vocahq.com"
VOCAMAC_SITE_URL = "https://vocamac.com"
VOCAWIN_SITE_URL = "https://vocawin.com"
VOCAPHONE_SITE_URL = "https://vocaphone.vocahq.com"
VOCAGATEWAY_SITE_URL = "https://vocagateway.vocahq.com"
GITHUB_REPO_URL = __url__
GITHUB_ISSUES_URL = "https://github.com/VocaHQ/vocalinux/issues"
VOCAHQ_DISCORD_URL = "https://discord.gg/t6muquAJbm"
VOCAHQ_X_URL = "https://x.com/vocahq"
VOCAHQ_MAILTO_URL = "mailto:hello@vocahq.com"
# The fork's own source, issue and release host. The update checker reads the
# same host, so About and the updater agree on where releases live.
SOURCE_REPO_URL = __url__
SOURCE_ISSUES_URL = f"{SOURCE_REPO_URL}/issues"
# The one upstream credit: the project this fork was made from.
UPSTREAM_REPO_URL = "https://github.com/VocaHQ/vocalinux"
UPSTREAM_CREDIT_LINE = f"Voculinux is a hard fork of Vocalinux by VocaHQ: {UPSTREAM_REPO_URL}"
# About page links that are not release URLs.
_ABOUT_OPEN_URLS = frozenset(
{
VOCULINUX_SITE_URL,
VOCAHQ_SITE_URL,
VOCAMAC_SITE_URL,
VOCAWIN_SITE_URL,
VOCAPHONE_SITE_URL,
VOCAGATEWAY_SITE_URL,
GITHUB_REPO_URL,
GITHUB_ISSUES_URL,
VOCAHQ_DISCORD_URL,
VOCAHQ_X_URL,
VOCAHQ_MAILTO_URL,
SOURCE_REPO_URL,
SOURCE_ISSUES_URL,
UPSTREAM_REPO_URL,
}
)
# Official Talk-to-us marks from VocaHQ/.github brand/vocahq/social (commit 61c8eee).
# Do not redraw. fill is currentColor; paint to match the dialog foreground so
# the marks stay visible on both light and dark surfaces.
# Currentcolor brand marks. fill is currentColor; paint to match the dialog
# foreground so the marks stay visible on both light and dark surfaces.
_ABOUT_INK = "#14231C"
_ABOUT_INK_ON_DARK = "#E6E1D8"
_ABOUT_ICON_TEXT_PX = 18
_FAMILY_ICON_PX = 22
def _color_luminance(color: Any) -> float:
@@ -119,55 +107,6 @@ def _about_ink_hex(
return _ABOUT_INK_ON_DARK if dark else _ABOUT_INK
# Official platform marks from VocaHQ/.github brand/promo/cards/platform
# (currentColor copies live in web/public/brand/platforms). Home is a simple
# house so HQ does not reuse a product lockup.
_VOCAHQ_FAMILY_LINKS = (
(
VOCAHQ_SITE_URL,
"VocaHQ",
"Family site",
("platform-home",),
"Open vocahq.com",
),
(
VOCULINUX_SITE_URL,
"Voculinux",
"Linux, available now",
("platform-linux",),
"Open voculinux.com",
),
(
VOCAMAC_SITE_URL,
"VocaMac",
"macOS, available now",
("platform-apple",),
"Open vocamac.com",
),
(
VOCAWIN_SITE_URL,
"VocaWin",
"Windows, unsigned beta",
("platform-windows",),
"Open vocawin.com",
),
(
VOCAPHONE_SITE_URL,
"VocaPhone",
"Android beta / iOS TestFlight",
("platform-android", "platform-apple"),
"Open vocaphone.vocahq.com",
),
(
VOCAGATEWAY_SITE_URL,
"VocaGateway",
"Self-hosted, headless",
("platform-server",),
"Open vocagateway.vocahq.com",
),
)
def _can_open_url(url: str) -> bool:
"""Return True for this project's release URLs or the About page allowlist."""
return bool(url) and (url in _ABOUT_OPEN_URLS or is_trusted_release_url(url))
@@ -239,50 +178,6 @@ class AboutSectionMixin(Gtk.Dialog):
button.connect("clicked", lambda *_args, dest=url: self._open_web_url(dest))
return button
def _family_tile(
self: Any,
url: str,
title: str,
subtitle: str,
icon_names: tuple[str, ...],
tooltip: str,
dark: Optional[bool] = None,
) -> Gtk.Button:
"""Compact icon + name tile that opens a family site."""
from .settings_dialog import _set_accessible_name
inner = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=16)
marks = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=6)
marks.set_valign(Gtk.Align.CENTER)
for icon_name in icon_names:
icon = self._about_mark_image(
icon_name,
pixel_size=_FAMILY_ICON_PX,
dark=dark,
)
if icon is not None:
marks.pack_start(icon, False, False, 0)
inner.pack_start(marks, False, False, 0)
text = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=0)
name_label = Gtk.Label(label=title, xalign=0)
name_label.get_style_context().add_class("family-tile-title")
status_label = Gtk.Label(label=subtitle, xalign=0)
status_label.get_style_context().add_class("family-tile-subtitle")
status_label.set_line_wrap(True)
text.pack_start(name_label, False, False, 0)
text.pack_start(status_label, False, False, 0)
inner.pack_start(text, True, True, 0)
button = Gtk.Button()
button.add(inner)
button.get_style_context().add_class("family-tile")
button.set_tooltip_text(tooltip)
button.set_hexpand(True)
button.set_halign(Gtk.Align.FILL)
_set_accessible_name(button, tooltip)
button.connect("clicked", lambda *_args, dest=url: self._open_web_url(dest))
return button
def _build_about_section(self: Any) -> None:
"""Build the About page using the same PreferenceRow cards as other pages."""
about_tab = getattr(self, "about_tab")
@@ -320,7 +215,7 @@ class AboutSectionMixin(Gtk.Dialog):
app_group = PreferencesGroup(
title="Voculinux",
description=(
"Available now on Linux (X11 and Wayland). After a model is downloaded, "
"Voice dictation for Linux (X11 and Wayland). After a model is downloaded, "
"speech is processed on this PC."
),
keywords=("about", "version", "app"),
@@ -353,8 +248,8 @@ class AboutSectionMixin(Gtk.Dialog):
)
link_box.pack_start(
self._about_open_button(
GITHUB_REPO_URL,
"Open the Voculinux GitHub repository",
SOURCE_REPO_URL,
"Open the Voculinux source repository",
label="Source code",
width=120,
dark=dark,
@@ -365,9 +260,9 @@ class AboutSectionMixin(Gtk.Dialog):
)
links_row = PreferenceRow(
title="Open source",
subtitle="Product site at voculinux.com, source on GitHub.",
subtitle="Product site at voculinux.com, source on the project's Gitea host.",
widget=link_box,
keywords=("website", "voculinux.com", "github", "source", "repo", "code", "agpl"),
keywords=("website", "voculinux.com", "gitea", "source", "repo", "code", "agpl"),
)
app_group.add_row(links_row)
@@ -381,53 +276,22 @@ class AboutSectionMixin(Gtk.Dialog):
talk_group = PreferencesGroup(
title="Talk to us",
description=(
"Bugs, feedback, and feature ideas open a GitHub issue. You pick the "
"template on the next screen."
),
keywords=("github", "discord", "contact", "bug", "feedback", "email"),
description=("Bugs, feedback, and feature ideas go to the Voculinux issue tracker."),
keywords=("issues", "bug", "feedback", "contact", "report", "idea"),
)
report_btn = self._about_open_button(
GITHUB_ISSUES_URL,
"Open GitHub issues for a bug or idea",
SOURCE_ISSUES_URL,
"Open Voculinux issues for a bug or idea",
label="Report a bug or idea",
icon_name="github",
width=200,
dark=dark,
)
talk_group.add_row(
PreferenceRow(
title="GitHub issues",
subtitle="https://github.com/VocaHQ/vocalinux/issues",
title="Issues",
subtitle=SOURCE_ISSUES_URL,
widget=report_btn,
keywords=("github", "issue", "bug", "idea"),
)
)
contact_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=8)
for url, label, tooltip, icon_name, width in (
(VOCAHQ_DISCORD_URL, "Join Discord", "Open the VocaHQ Discord", "discord", 140),
(VOCAHQ_X_URL, "Follow on X", "Open X @vocahq", "x", 130),
(VOCAHQ_MAILTO_URL, "Email us", "Email hello@vocahq.com", "mail", 120),
):
contact_box.pack_start(
self._about_open_button(
url,
tooltip,
label=label,
icon_name=icon_name,
width=width,
dark=dark,
),
False,
False,
0,
)
talk_group.add_row(
PreferenceRow(
title="Community",
subtitle="Discord, X, and email",
widget=contact_box,
keywords=("discord", "x", "twitter", "email", "mail"),
keywords=("issue", "bug", "idea"),
)
)
about_tab.pack_start(talk_group, False, False, 0)
@@ -519,24 +383,21 @@ class AboutSectionMixin(Gtk.Dialog):
about_tab.pack_start(self.release_notes_group, False, False, 0)
self.release_notes_group.hide()
family_group = PreferencesGroup(
title="Part of VocaHQ",
description="Private dictation for Linux, Mac, Windows, and phone.",
keywords=("vocahq", "vocamac", "vocawin", "vocaphone", "vocagateway", "family"),
origin_group = PreferencesGroup(
title="Origin",
keywords=("upstream", "fork", "origin", "credit", "project"),
)
family_grid = Gtk.Grid()
family_grid.set_column_spacing(8)
family_grid.set_row_spacing(4)
family_grid.set_column_homogeneous(True)
family_grid.set_margin_start(8)
family_grid.set_margin_end(8)
family_grid.set_margin_top(4)
family_grid.set_margin_bottom(8)
for index, (url, title, subtitle, icon_names, open_name) in enumerate(_VOCAHQ_FAMILY_LINKS):
tile = self._family_tile(url, title, subtitle, icon_names, open_name, dark=dark)
family_grid.attach(tile, index % 2, index // 2, 1, 1)
family_row = Gtk.ListBoxRow()
family_row.set_activatable(False)
family_row.add(family_grid)
family_group.add_row(family_row)
about_tab.pack_start(family_group, False, False, 0)
upstream_row = PreferenceRow(
title="Upstream project",
subtitle=UPSTREAM_CREDIT_LINE,
widget=self._about_open_button(
UPSTREAM_REPO_URL,
"Open the upstream project repository",
label="Upstream",
width=110,
dark=dark,
),
keywords=("upstream", "fork", "credit"),
)
origin_group.add_row(upstream_row)
about_tab.pack_start(origin_group, False, False, 0)
+7 -13
View File
@@ -151,18 +151,10 @@ from .settings_about import ( # noqa: F401
_ABOUT_INK,
_ABOUT_INK_ON_DARK,
_ABOUT_OPEN_URLS,
_FAMILY_ICON_PX,
_VOCAHQ_FAMILY_LINKS,
GITHUB_ISSUES_URL,
GITHUB_REPO_URL,
VOCAGATEWAY_SITE_URL,
VOCAHQ_DISCORD_URL,
VOCAHQ_MAILTO_URL,
VOCAHQ_SITE_URL,
VOCAHQ_X_URL,
VOCAMAC_SITE_URL,
VOCAPHONE_SITE_URL,
VOCAWIN_SITE_URL,
SOURCE_ISSUES_URL,
SOURCE_REPO_URL,
UPSTREAM_CREDIT_LINE,
UPSTREAM_REPO_URL,
VOCULINUX_SITE_URL,
AboutSectionMixin,
_about_ink_hex,
@@ -318,7 +310,9 @@ def _engine_from_display(display_name: str) -> str:
return display_name.lower()
MODEL_SELECTION_DOC_URL = "https://github.com/VocaHQ/vocalinux/blob/main/docs/MODEL_SELECTION.md"
MODEL_SELECTION_DOC_URL = (
"https://git.bongbetic.com/xavierk/voculinux/src/branch/main/docs/MODEL_SELECTION.md"
)
def _model_display_name(model_name: str) -> str:
+1 -1
View File
@@ -8,5 +8,5 @@ __author__ = "Jatin K Malik"
__email__ = "jatinkrmalik@gmail.com"
__license__ = "AGPL-3.0-only"
__copyright__ = "Copyright 2023-2026 Jatin K Malik"
__url__ = "https://github.com/VocaHQ/vocalinux"
__url__ = "https://git.bongbetic.com/xavierk/voculinux"
__description__ = "Free, offline voice dictation for Linux - speak and type anywhere"
+4 -2
View File
@@ -322,10 +322,12 @@ def test_wayland_input_group_is_documented() -> None:
already listed in /etc/group: a fresh Wayland install leaves the default
hotkey dead unless the docs name the step."""
install_md = (REPO_ROOT / "docs" / "INSTALL.md").read_text(encoding="utf-8")
section = install_md.split("## Distro packages", 1)[1].split("\n## ", 1)[0]
section = install_md.split("### Wayland hotkeys need the input group", 1)[1].split("\n## ", 1)[
0
]
assert (
"usermod -aG input" in section
), "INSTALL.md's .deb/.rpm section must document the Wayland input-group step"
), "INSTALL.md's Wayland hotkey section must document the input-group step"
release = RELEASE_YML.read_text(encoding="utf-8")
assert (
"usermod -aG input" in release
+1 -1
View File
@@ -131,5 +131,5 @@ def test_recommendation_does_not_change_picker_or_config_state(
def test_model_selection_docs_url_is_stable(settings_dialog: Any) -> None:
assert (
settings_dialog.MODEL_SELECTION_DOC_URL
== "https://github.com/VocaHQ/vocalinux/blob/main/docs/MODEL_SELECTION.md"
== "https://git.bongbetic.com/xavierk/voculinux/src/branch/main/docs/MODEL_SELECTION.md"
)
+115 -12
View File
@@ -20,6 +20,12 @@ scoped to what the surface is, not to a word that happens to co-occur:
``CHANGELOG.md``) and the git worktree pointer file ``.git``;
- web upstream surfaces (ticket #24): the nix flake snippets on the nixos page.
The Void release cleanup adds the fork surfaces (``FORK_SURFACES``): README,
the support and security docs, the install docs, the About page and the Void
install message. On those, upstream references are not allowlisted by pattern
at all. An upstream URL, handle or host is a regression unless the line is
exactly the one credit line recorded for that file.
``tests/test_rename_sweep.py`` also checks the allowlist itself, so a loosened
rule fails here before it lets an old name onto a user-visible surface.
"""
@@ -83,6 +89,44 @@ RETAINED_MODULE_HEADS = frozenset(
]
) - {"__init__"}
#: The one upstream credit per fork surface, matched as whole stripped lines.
CREDIT_LINE = "Voculinux is a hard fork of Vocalinux by VocaHQ: https://github.com/VocaHQ/vocalinux"
FORK_SURFACE_CREDITS: dict[str, frozenset[str]] = {
"README.md": frozenset({CREDIT_LINE}),
"CONTRIBUTING.md": frozenset({CREDIT_LINE}),
"src/vocalinux/ui/settings_about.py": frozenset(
{
'UPSTREAM_REPO_URL = "https://github.com/VocaHQ/vocalinux"',
'UPSTREAM_CREDIT_LINE = f"Voculinux is a hard fork of Vocalinux by VocaHQ: {UPSTREAM_REPO_URL}"',
}
),
}
FORK_SURFACES = frozenset(
{
"README.md",
"SECURITY.md",
"SUPPORT.md",
"CONTRIBUTING.md",
"docs/UPDATE.md",
"docs/INSTALL.md",
"docs/TROUBLESHOOTING.md",
"docs/DISTRO_COMPATIBILITY.md",
"src/vocalinux/ui/settings_about.py",
"src/vocalinux/ui/settings_dialog.py",
"packaging/void/srcpkgs/voculinux/INSTALL.msg",
}
)
#: Upstream hosts, handles and URL forms. Plain words such as "Flathub" are not
#: listed, so a sentence that says a channel is not published stays legal.
UPSTREAM_REFERENCE = re.compile(
r"VocaHQ|vocahq|jatinkrmalik|raw\.githubusercontent|discord\.gg|(?<![\w.])x\.com/"
r"|mailto:|codeberg|vocamac\.com|vocawin\.com|vocaphone\.|vocagateway\.|flathub\.org"
r"|dl\.flathub"
r"|snapcraft\.io|aur\.archlinux|pypi\.org/project/voculinux|github:|star-history"
r"|contrib\.rocks"
r"|shields\.io/(?:aur|pypi|discord|github)",
re.IGNORECASE,
)
_UPSTREAM_SLUG = re.compile(r"(?:VocaHQ|jatinkrmalik)/vocalinux", re.IGNORECASE)
_UPSTREAM_LABEL = re.compile(
r"upstream[^\n]{0,40}vocalinux|vocalinux[^\n]{0,40}upstream|fork of vocalinux",
@@ -148,19 +192,43 @@ _LEGACY_LINE = re.compile(
)
#: Pre-rename file names a packaging guard rejects (ticket #28).
_PRE_RENAME_GUARD = re.compile(r"usr/share/(?:applications|icons/[^\s\"]*)/vocalinux")
_NIX_FLAKE_IDENTIFIER = re.compile(
r"packages\.<system>\.|apps\.<system>\.|nixosModules\.|homeManagerModules\.|programs\."
r"|\bflake\b|\binputs?\b|outputs = \{|nixpkgs, |pkgs\.vocalinux|nix run|nix profile"
r"|github:VocaHQ",
re.IGNORECASE,
WEB_REBRAND_TEST = "web/src/lib/__tests__/voculinux-rebrand.test.ts"
WEB_REBRAND_SCANNER_LINES = frozenset(
{
"const OLD_NAME = /Vocalinux/;",
'expect(home).not.toContain("Then launch <code>vocalinux</code>");',
}
)
def _fork_surface_allowed(path: str, line: str) -> bool:
"""Fork surfaces allow the exact credit, retained module references and legacy prose."""
if line.strip() in FORK_SURFACE_CREDITS.get(path, frozenset()):
return True
if UPSTREAM_REFERENCE.search(line):
return False
return bool(
_MODULE_PATH.search(line)
or _DOTTED_MODULE.search(line)
or _IMPORT_FORM.search(line)
or _CAMEL_CLASS.search(line)
or _RETAINED_PROSE.search(line)
or _LEGACY_PROSE.search(line)
)
def _allowed(path: str, line: str) -> bool:
"""Return True when an old-name match is an allowlisted retained surface."""
if path == "tests/test_rename_sweep.py":
# This file defines the allowlist and quotes the old name to do so.
return True
if path in FORK_SURFACES:
return _fork_surface_allowed(path, line)
if path == WEB_REBRAND_TEST and line.strip() in WEB_REBRAND_SCANNER_LINES:
# The web rebrand test names the old word only to scan for it.
return True
if _UPSTREAM_SLUG.search(line) or _UPSTREAM_LABEL.search(line):
return True
if _MODULE_PATH.search(line) or _DOTTED_MODULE.search(line):
@@ -169,12 +237,6 @@ def _allowed(path: str, line: str) -> bool:
return True
if _LEGACY_PROSE.search(line):
return True
if path == "web/src/app/install/nixos/page.tsx":
# The Nix flake is upstream's: flake URL, input and output names and
# programs.* options spell the retained upstream API. The page carries
# a fork-status note saying those snippets install Vocalinux, not
# Voculinux, so only flake-shaped lines are allowed here.
return bool(_NIX_FLAKE_IDENTIFIER.search(line))
if path in _LEGACY_SOURCE_FILES and _LEGACY_LINE.search(line):
return True
if path.startswith(_TOOLING_PREFIXES) and _PACKAGE_SEGMENT.search(line):
@@ -221,6 +283,28 @@ def test_naming_sweep_no_old_name_outside_allowlist() -> None:
), f"{len(offenders)} old-name string(s) outside the allowlist:\n" + "\n".join(offenders[:60])
def test_fork_surfaces_name_no_upstream_but_the_one_credit() -> None:
"""A fork surface may name upstream only on its recorded credit line."""
offenders: list[str] = []
for rel in sorted(FORK_SURFACES):
with open(os.path.join(REPO_ROOT, rel), encoding="utf-8") as handle:
text = handle.read()
credits = FORK_SURFACE_CREDITS.get(rel, frozenset())
for number, line in enumerate(text.splitlines(), 1):
if UPSTREAM_REFERENCE.search(line) and line.strip() not in credits:
offenders.append(f"{rel}:{number}:{line.strip()[:160]}")
assert offenders == [], "upstream reference outside the credit:\n" + "\n".join(offenders)
def test_fork_surfaces_carry_their_credit_exactly_once() -> None:
"""The credit is present, once, on every file that records one."""
for rel, credits in sorted(FORK_SURFACE_CREDITS.items()):
with open(os.path.join(REPO_ROOT, rel), encoding="utf-8") as handle:
lines = [line.strip() for line in handle.read().splitlines()]
for credit in credits:
assert lines.count(credit) == 1, f"{rel}: credit line must appear exactly once"
def test_owned_web_files_carry_new_name() -> None:
"""The two web rename surfaces name the fork (URLs aside)."""
for rel in sorted(WEB_OWNED):
@@ -232,6 +316,20 @@ def test_owned_web_files_carry_new_name() -> None:
def test_allowlist_denies_user_facing_old_names() -> None:
"""A user-facing old name is never allowlisted, wherever it sits."""
denied = [
("README.md", "Upstream: https://github.com/VocaHQ/vocalinux/issues"),
("README.md", "Join us on [Discord](https://discord.gg/t6muquAJbm)."),
("docs/INSTALL.md", "flatpak install vocahq com.voculinux.Voculinux"),
("SUPPORT.md", "| [Discussions](https://github.com/VocaHQ/vocalinux/discussions) |"),
("SECURITY.md", "Email hello@vocahq.com for security reports."),
("src/vocalinux/ui/settings_about.py", 'VOCAHQ_X_URL = "https://x.com/vocahq"'),
(
"src/vocalinux/ui/settings_dialog.py",
'MODEL_SELECTION_DOC_URL = "https://github.com/VocaHQ/vocalinux/x"',
),
(
"packaging/void/srcpkgs/voculinux/INSTALL.msg",
"Report bugs at github.com/VocaHQ/vocalinux",
),
("web/src/app/page.tsx", "<h1>Install vocalinux today</h1>"),
("README.md", "Launch vocalinux from your app menu."),
("docs/UPDATE.md", "Your settings live in ~/.local/share/vocalinux."),
@@ -247,7 +345,12 @@ def test_allowlist_denies_user_facing_old_names() -> None:
def test_allowlist_keeps_the_retained_surfaces() -> None:
"""Upstream provenance, the retained package and the legacy import stay allowed."""
kept = [
("README.md", "Upstream: https://github.com/VocaHQ/vocalinux/issues"),
("README.md", CREDIT_LINE),
("CONTRIBUTING.md", CREDIT_LINE),
(
"src/vocalinux/ui/settings_about.py",
'UPSTREAM_REPO_URL = "https://github.com/VocaHQ/vocalinux"',
),
("web/src/app/page.tsx", "Voculinux is a hard fork of Vocalinux for Void Linux."),
("web/src/app/install/[distro]/page.tsx", "installs Vocalinux (upstream), not Voculinux."),
("src/vocalinux/ui/tray_indicator.py", "from vocalinux.utils import paths"),
+61 -71
View File
@@ -1329,7 +1329,7 @@ class TestSettingsNavigation(unittest.TestCase):
class TestAboutPage(unittest.TestCase):
"""About page: this app, VocaHQ family, talk-to-us. Updater stays."""
"""About page: this app, talk-to-us, origin credit. Updater stays."""
@classmethod
def setUpClass(cls):
@@ -1343,7 +1343,7 @@ class TestAboutPage(unittest.TestCase):
def test_three_groups_keep_updater(self) -> None:
self.assertIn('title="Voculinux"', self.about)
self.assertIn('title="Part of VocaHQ"', self.about)
self.assertIn('title="Origin"', self.about)
self.assertIn('title="Talk to us"', self.about)
self.assertIn('title="Updates"', self.about)
self.assertIn("What's New", self.about)
@@ -1352,12 +1352,8 @@ class TestAboutPage(unittest.TestCase):
self.assertIn("self.update_checks_switch", self.about)
def test_product_status_and_honest_network_copy(self):
self.assertIn("Available now on Linux (X11 and Wayland)", self.about)
self.assertIn("Voice dictation for Linux (X11 and Wayland)", self.about)
self.assertIn("After a model is downloaded", self.about)
self.assertIn("Private dictation for Linux, Mac, Windows, and phone.", self.about)
self.assertIn("Android beta / iOS TestFlight", self.source)
self.assertIn("Windows, unsigned beta", self.source)
self.assertIn("Self-hosted, headless", self.source)
self.assertNotIn("iOS source build", self.source)
self.assertNotIn("SmartScreen", self.about)
self.assertNotIn("100% offline", self.about)
@@ -1367,28 +1363,44 @@ class TestAboutPage(unittest.TestCase):
# License is named once, on the License row, not in the intro or links.
self.assertEqual(self.about.count("GNU AGPL v3"), 1)
def test_family_and_talk_urls(self):
for url in (
"https://voculinux.com",
"https://vocahq.com",
"https://vocamac.com",
"https://vocawin.com",
"https://vocaphone.vocahq.com",
"https://vocagateway.vocahq.com",
"https://github.com/VocaHQ/vocalinux/issues",
"https://discord.gg/t6muquAJbm",
"https://x.com/vocahq",
"mailto:hello@vocahq.com",
):
self.assertIn(url, self.source)
def test_links_point_at_the_fork_and_one_credit(self):
from vocalinux.ui.settings_about import (
SOURCE_ISSUES_URL,
SOURCE_REPO_URL,
UPSTREAM_CREDIT_LINE,
UPSTREAM_REPO_URL,
VOCULINUX_SITE_URL,
)
self.assertEqual(VOCULINUX_SITE_URL, "https://voculinux.com")
self.assertEqual(SOURCE_REPO_URL, "https://git.bongbetic.com/xavierk/voculinux")
self.assertEqual(SOURCE_ISSUES_URL, f"{SOURCE_REPO_URL}/issues")
self.assertEqual(UPSTREAM_REPO_URL, "https://github.com/VocaHQ/vocalinux")
self.assertEqual(UPSTREAM_CREDIT_LINE.count(UPSTREAM_REPO_URL), 1)
self.assertIn('label="Report a bug or idea"', self.about)
self.assertIn("Join Discord", self.about)
self.assertIn("Follow on X", self.about)
self.assertIn("Email us", self.about)
self.assertIn("https://github.com/VocaHQ/vocalinux/issues", self.about)
self.assertIn("https://discord.gg/t6muquAJbm", self.source)
self.assertIn("https://x.com/vocahq", self.source)
self.assertIn("mailto:hello@vocahq.com", self.source)
self.assertIn("SOURCE_ISSUES_URL", self.about)
self.assertIn("UPSTREAM_CREDIT_LINE", self.about)
def test_family_and_social_links_are_gone(self):
"""Voca family sites and social channels are not Voculinux's to offer."""
for banned in (
"Part of VocaHQ",
"_VOCAHQ_FAMILY_LINKS",
"_family_tile",
"family_grid",
"Join Discord",
"Follow on X",
"Email us",
"discord.gg",
"x.com/",
"mailto:",
"vocahq.com",
"vocamac.com",
"vocawin.com",
"vocaphone.vocahq.com",
"vocagateway.vocahq.com",
):
self.assertNotIn(banned, self.source, banned)
def test_open_buttons_have_unique_accessible_names(self):
helper = self.source.split("def _about_open_button")[1].split("\n def ")[0]
@@ -1396,13 +1408,9 @@ class TestAboutPage(unittest.TestCase):
self.assertIn('if label == "Open"', helper)
for name in (
"Open voculinux.com website",
"Open vocahq.com",
"Open voculinux.com",
"Open vocamac.com",
"Open vocawin.com",
"Open vocaphone.vocahq.com",
"Open vocagateway.vocahq.com",
"Open the Voculinux GitHub repository",
"Open the Voculinux source repository",
"Open Voculinux issues for a bug or idea",
"Open the upstream project repository",
"Open the latest release page",
):
self.assertIn(name, self.source)
@@ -1412,12 +1420,13 @@ class TestAboutPage(unittest.TestCase):
self.assertIn("VOCULINUX_SITE_URL", self.about)
self.assertIn("Product site at voculinux.com", self.about)
self.assertNotIn("Open the Voculinux GitHub page", self.about)
self.assertNotIn("GitHub", self.about)
def test_source_code_row_sits_beside_website(self):
self.assertIn('label="Website"', self.about)
self.assertIn('label="Source code"', self.about)
self.assertIn("GITHUB_REPO_URL", self.about)
self.assertIn("Open the Voculinux GitHub repository", self.about)
self.assertIn("SOURCE_REPO_URL", self.about)
self.assertIn("Open the Voculinux source repository", self.about)
self.assertLess(self.about.find('label="Website"'), self.about.find('label="Source code"'))
def test_talk_to_us_sits_above_updates(self):
@@ -1434,52 +1443,34 @@ class TestAboutPage(unittest.TestCase):
self.assertNotIn('title="Latest Release"', self.about)
self.assertIn("self.latest_release_row = self.release_notes_row", self.about)
def test_family_uses_platform_icons(self):
self.assertIn("def _family_tile", self.source)
self.assertIn("platform-linux", self.source)
self.assertIn("platform-apple", self.source)
self.assertIn("platform-windows", self.source)
self.assertIn("platform-android", self.source)
self.assertIn("platform-server", self.source)
self.assertIn("platform-home", self.source)
self.assertIn('("platform-android", "platform-apple")', self.source)
self.assertIn("family_grid.attach", self.about)
self.assertIn("image.set_margin_end(8)", self.source)
helper = self.source.split("def _family_tile")[1].split("\n def ")[0]
self.assertIn("spacing=16", helper)
def test_origin_group_names_the_upstream_credit_once(self):
self.assertIn("origin_group = PreferencesGroup(", self.about)
self.assertIn("UPSTREAM_CREDIT_LINE", self.about)
self.assertIn("UPSTREAM_REPO_URL", self.about)
self.assertLess(
self.about.find('title="Updates"'),
self.about.find('title="Origin"'),
)
def test_can_open_about_urls(self):
from vocalinux.ui.settings_dialog import (
GITHUB_ISSUES_URL,
GITHUB_REPO_URL,
VOCAGATEWAY_SITE_URL,
VOCAHQ_DISCORD_URL,
VOCAHQ_MAILTO_URL,
VOCAHQ_SITE_URL,
VOCAHQ_X_URL,
VOCAMAC_SITE_URL,
VOCAPHONE_SITE_URL,
VOCAWIN_SITE_URL,
SOURCE_ISSUES_URL,
SOURCE_REPO_URL,
UPSTREAM_REPO_URL,
VOCULINUX_SITE_URL,
_can_open_url,
)
for url in (
VOCULINUX_SITE_URL,
VOCAHQ_SITE_URL,
VOCAMAC_SITE_URL,
VOCAWIN_SITE_URL,
VOCAPHONE_SITE_URL,
VOCAGATEWAY_SITE_URL,
GITHUB_REPO_URL,
GITHUB_ISSUES_URL,
VOCAHQ_DISCORD_URL,
VOCAHQ_X_URL,
VOCAHQ_MAILTO_URL,
SOURCE_REPO_URL,
SOURCE_ISSUES_URL,
UPSTREAM_REPO_URL,
"https://git.bongbetic.com/xavierk/voculinux/releases/tag/v0.15.0",
):
self.assertTrue(_can_open_url(url), url)
self.assertFalse(_can_open_url("https://github.com/VocaHQ/vocalinux/releases/tag/v0.15.0"))
self.assertFalse(_can_open_url("https://vocahq.com"))
self.assertFalse(_can_open_url("https://evil.example/payload"))
self.assertFalse(_can_open_url("mailto:evil@example.com"))
self.assertFalse(_can_open_url(""))
@@ -1594,7 +1585,6 @@ class TestAboutPage(unittest.TestCase):
self.assertIn("def _about_ink_hex(", self.source)
self.assertIn("def _about_surface_is_dark(", self.source)
self.assertIn('replace("currentColor", ink)', self.source)
self.assertIn("_FAMILY_ICON_PX", self.source)
class TestUpdatePreferences(unittest.TestCase):
+6 -10
View File
@@ -59,24 +59,20 @@ def test_snap_puts_gnome_platform_first_on_ld_library_path() -> None:
def test_snap_docs_tell_users_to_connect_hardware_observe() -> None:
"""Hotkeys need hardware-observe for /proc/bus/input/devices (#857)."""
readme = (REPO_ROOT / "README.md").read_text(encoding="utf-8")
install = (REPO_ROOT / "docs" / "INSTALL.md").read_text(encoding="utf-8")
# Only the recipe still names the Snap plugs; the fork docs carry no Snap install text.
snapcraft = SNAPCRAFT_YAML.read_text(encoding="utf-8")
for text in (readme, install, snapcraft):
for text in (snapcraft,):
assert "sudo snap connect voculinux:raw-input" in text
assert "sudo snap connect voculinux:hardware-observe" in text
def test_snap_docs_explain_typing_without_uinput_plug() -> None:
"""The Store denied the uinput grant; docs must point at the portal, not a connect."""
readme = (REPO_ROOT / "README.md").read_text(encoding="utf-8")
install = (REPO_ROOT / "docs" / "INSTALL.md").read_text(encoding="utf-8")
update = (REPO_ROOT / "docs" / "UPDATE.md").read_text(encoding="utf-8")
changelog = (REPO_ROOT / "web" / "src" / "app" / "changelog" / "page.tsx").read_text(
encoding="utf-8"
)
# The Snap channel is not published for Voculinux, so the recipe is the one
# surface that still describes the plug set. The fork docs no longer carry
# Snap install text.
snapcraft = SNAPCRAFT_YAML.read_text(encoding="utf-8")
for text in (readme, install, update, changelog, snapcraft):
for text in (snapcraft,):
uinput_lines = "\n".join(line for line in text.splitlines() if "uinput" in line.lower())
lowered = uinput_lines.lower()
assert "uinput" in lowered
+7 -7
View File
@@ -18,11 +18,11 @@ Offline-first Linux voice typing with real desktop integration (system tray, X11
## Operating context
- Install via a one-line shell installer, AppImage from GitHub Releases, AUR, Snap (`--edge`), or Flatpak release bundle; then run from PATH, app menu, or the AppImage binary
- Install from the signed Void Linux xbps repository (glibc, x86_64), then run `voculinux` from PATH or the application menu; source checkouts use the install script
- System tray indicator and settings GUI (GTK) with searchable sidebar navigation and an always-visible sidebar footer for dictation status / Test Dictation / Close
- Dictation into terminals, browsers, IDEs, office apps
- Engines and models chosen for hardware (CPU, optional Vulkan GPU with discrete-device preference)
- Guides and comparison pages on voculinux.com; source on the fork's own Gitea host (`https://git.bongbetic.com/xavierk/voculinux`); upstream GitHub is linked only as labelled provenance
- Guides and comparison pages on voculinux.com; source, issues and releases on the fork's own host (`https://git.bongbetic.com/xavierk/voculinux`); the upstream project is credited once, in the site footer
## Capabilities and constraints
@@ -43,21 +43,21 @@ Offline-first Linux voice typing with real desktop integration (system tray, X11
- Optional JSONL persistence of transcription history to disk (off by default)
- Settings Proxy page: outbound SOCKS5 or HTTP CONNECT proxy (optional auth) for model downloads and update checks
- Speech Model Advanced pickers stage engine/size/variant/language changes until Download is confirmed
- In-app update checker (stable/nightly) with tray notification when a newer GitHub release is available
- Settings About page groups this app, upstream project links retained as provenance, and talk-to-us links (GitHub, Discord, X, email)
- In-app update checker (stable/nightly) with tray notification when a newer release is available on the fork's release host; packaged installs point to xbps for updates
- Settings About page groups this app, with the website, the source and issues links, and one labelled upstream credit; no Discord, X or email
- Optional disable of the missing-tray warning dialog
- Settings → Audio tone picker (Lift, Flick, Ember, Step, Voca, Soft, Chirp, Scale, Drop, Glass, Off) with Preview; new installs default to Voca; catalog files are the preview WAVs
- Vulkan discrete GPU auto-select with manual device override in Advanced settings
- Native GTK installs follow the OS dark/light preference unless `GTK_THEME` is already set
- Wayland: IBus when `ibus-wayland` is running, including on compositors previously treated as unbridged
- Packaging: install.sh (distro python3-gi required; no pip sdist of PyGObject), .deb/.rpm (x86_64/aarch64), AppImage (x86_64/aarch64), AUR, Flatpak (local/Flathub status as documented); uv.lock pins Python deps; Justfile replaces Makefile
- Packaging: the signed Void Linux xbps repository (glibc, x86_64) is the only published channel; source checkouts use install.sh (distro python3-gi required; no pip sdist of PyGObject); uv.lock pins Python deps; Justfile replaces Makefile
- No usage telemetry in the installed app
- AGPL-3.0; marketing version string is tracked in site package/version surfaces
- Website is Next.js marketing + SEO guides (static export); languages page documents per-engine support honestly
## Brand commitments
- Name: Voculinux, a hard fork of Vocalinux for Void Linux (glibc, Wayland-primary under niri). Upstream project links are retained as provenance; the fork site carries the new name everywhere a user looks
- Name: Voculinux, a hard fork for Void Linux (glibc, Wayland-primary under niri). The only upstream link is the labelled provenance credit in the site footer; the fork site carries the new name everywhere a user looks
- Mark: fork mic for the app icon, tray states, and site favicons (`public/`); Bongbetic appears only as attribution chrome in the site header and footer, linking the hosted brand assets at `https://bongbetic.com/branding/` — no brand binaries are vendored into the repository
- Visual system: Carbon-mapped — Carbon spacing, type-role, motion, and contrast discipline remapped to the house palette (warm paper canvas, teal interactive colour, system type stacks only, red reserved for recording/destructive); no Carbon blue, no remote webfonts
- Engine promise: whisper.cpp is the guaranteed default engine (packaged, checksum-pinned, budgeted); Faster Whisper, Whisper, VOSK, and Parakeet are best-effort installable extras; the Remote API engine is user-configured and audio leaves the machine — stated plainly wherever remote is described
@@ -66,7 +66,7 @@ Offline-first Linux voice typing with real desktop integration (system tray, X11
## Evidence on hand
- Real app screenshots in `public/screenshots/`
- Install/uninstall commands pointing at GitHub raw install scripts
- Install commands for the signed Void xbps repository (the install guide and the home page), with the repository key fingerprint
- JSON-LD on the home page (software application, FAQ, how-to)
- Do not invent user counts, testimonials, or benchmarks
+1 -1
View File
@@ -73,7 +73,7 @@ web/
- Custom domain: `deploy` script writes `out/CNAME`
- Subdirectory deploy: set `basePath` in `next.config.js` if needed
- Family workbench UI: warm paper, system type, same language as VocaHQ ([DESIGN.md](DESIGN.md))
- Visual system: warm paper, system type, documented in [DESIGN.md](DESIGN.md)
## Troubleshooting
-4
View File
@@ -1,4 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" role="img" aria-hidden="true">
<title>Discord</title>
<path fill="currentColor" d="M20.317 4.3698a19.7913 19.7913 0 00-4.8851-1.5152.0741.0741 0 00-.0785.0371c-.211.3753-.4447.8648-.6083 1.2495-1.8447-.2762-3.68-.2762-5.4868 0-.1636-.3933-.4058-.8742-.6177-1.2495a.077.077 0 00-.0785-.037 19.7363 19.7363 0 00-4.8852 1.515.0699.0699 0 00-.0321.0277C.5334 9.0458-.319 13.5799.0992 18.0578a.0824.0824 0 00.0312.0561c2.0528 1.5076 4.0413 2.4228 5.9929 3.0294a.0777.0777 0 00.0842-.0276c.4616-.6304.8731-1.2952 1.226-1.9942a.076.076 0 00-.0416-.1057c-.6528-.2476-1.2743-.5495-1.8722-.8923a.077.077 0 01-.0076-.1277c.1258-.0943.2517-.1923.3718-.2914a.0743.0743 0 01.0776-.0105c3.9278 1.7933 8.18 1.7933 12.0614 0a.0739.0739 0 01.0785.0095c.1202.099.246.1981.3728.2924a.077.077 0 01-.0066.1276 12.2986 12.2986 0 01-1.873.8914.0766.0766 0 00-.0407.1067c.3604.698.7719 1.3628 1.225 1.9932a.076.076 0 00.0842.0286c1.961-.6067 3.9495-1.5219 6.0023-3.0294a.077.077 0 00.0313-.0552c.5004-5.177-.8382-9.6739-3.5485-13.6604a.061.061 0 00-.0312-.0286zM8.02 15.3312c-1.1825 0-2.1569-1.0857-2.1569-2.419 0-1.3332.9555-2.4189 2.157-2.4189 1.2108 0 2.1757 1.0952 2.1568 2.419 0 1.3332-.9555 2.4189-2.1569 2.4189zm7.9748 0c-1.1825 0-2.1569-1.0857-2.1569-2.419 0-1.3332.9554-2.4189 2.1569-2.4189 1.2108 0 2.1757 1.0952 2.1568 2.419 0 1.3332-.946 2.4189-2.1568 2.4189Z"/>
</svg>

Before

Width:  |  Height:  |  Size: 1.4 KiB

-4
View File
@@ -1,4 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" role="img" aria-hidden="true">
<title>Email</title>
<path fill="currentColor" d="M3 5h18a2 2 0 0 1 2 2v10a2 2 0 0 1-2 2H3a2 2 0 0 1-2-2V7a2 2 0 0 1 2-2zm0 2v.2l9 6.3 9-6.3V7H3zm18 2.4-8.4 5.9a1 1 0 0 1-1.2 0L3 9.4V17h18V9.4z"/>
</svg>

Before

Width:  |  Height:  |  Size: 299 B

-4
View File
@@ -1,4 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" role="img" aria-hidden="true">
<title>X</title>
<path fill="currentColor" d="M18.901 1.153h3.68l-8.04 9.19L24 22.846h-7.406l-5.8-7.584-6.638 7.584H.474l8.6-9.83L0 1.154h7.594l5.243 6.932ZM17.61 20.644h2.039L6.486 3.24H4.298Z"/>
</svg>

Before

Width:  |  Height:  |  Size: 299 B

-66
View File
@@ -12,36 +12,12 @@
<changefreq>weekly</changefreq>
<priority>0.9</priority>
</url>
<url>
<loc>https://voculinux.com/install/ubuntu/</loc>
<lastmod>2026-11-01</lastmod>
<changefreq>monthly</changefreq>
<priority>0.8</priority>
</url>
<url>
<loc>https://voculinux.com/install/fedora/</loc>
<lastmod>2026-11-01</lastmod>
<changefreq>monthly</changefreq>
<priority>0.8</priority>
</url>
<url>
<loc>https://voculinux.com/install/arch/</loc>
<lastmod>2026-11-01</lastmod>
<changefreq>monthly</changefreq>
<priority>0.8</priority>
</url>
<url>
<loc>https://voculinux.com/compare/</loc>
<lastmod>2026-11-01</lastmod>
<changefreq>monthly</changefreq>
<priority>0.8</priority>
</url>
<url>
<loc>https://voculinux.com/changelog/</loc>
<lastmod>2026-06-11</lastmod>
<changefreq>weekly</changefreq>
<priority>0.7</priority>
</url>
<url>
<loc>https://voculinux.com/screenshots/</loc>
<lastmod>2026-07-14</lastmod>
@@ -241,46 +217,4 @@
<changefreq>monthly</changefreq>
<priority>0.7</priority>
</url>
<url>
<loc>https://voculinux.com/local-gateway/</loc>
<lastmod>2026-11-01</lastmod>
<changefreq>monthly</changefreq>
<priority>0.7</priority>
</url>
<url>
<loc>https://voculinux.com/flatpak/</loc>
<lastmod>2026-11-01</lastmod>
<changefreq>monthly</changefreq>
<priority>0.7</priority>
</url>
<url>
<loc>https://voculinux.com/install/nixos/</loc>
<lastmod>2026-11-01</lastmod>
<changefreq>monthly</changefreq>
<priority>0.8</priority>
</url>
<url>
<loc>https://voculinux.com/install/debian/</loc>
<lastmod>2026-11-01</lastmod>
<changefreq>monthly</changefreq>
<priority>0.8</priority>
</url>
<url>
<loc>https://voculinux.com/install/opensuse/</loc>
<lastmod>2026-11-01</lastmod>
<changefreq>monthly</changefreq>
<priority>0.8</priority>
</url>
<url>
<loc>https://voculinux.com/install/linux-mint/</loc>
<lastmod>2026-11-01</lastmod>
<changefreq>monthly</changefreq>
<priority>0.8</priority>
</url>
<url>
<loc>https://voculinux.com/install/pop-os/</loc>
<lastmod>2026-11-01</lastmod>
<changefreq>monthly</changefreq>
<priority>0.8</priority>
</url>
</urlset>
+11 -13
View File
@@ -41,9 +41,6 @@ describe("SEO Assets", () => {
const expectedUrls = [
"https://voculinux.com/",
"https://voculinux.com/install/",
"https://voculinux.com/install/ubuntu/",
"https://voculinux.com/install/fedora/",
"https://voculinux.com/install/arch/",
"https://voculinux.com/compare/",
"https://voculinux.com/remote-api/",
"https://voculinux.com/voice-activity-detection/",
@@ -59,13 +56,6 @@ describe("SEO Assets", () => {
"https://voculinux.com/pipewire/",
"https://voculinux.com/global-shortcuts/",
"https://voculinux.com/hyprland-sway/",
"https://voculinux.com/local-gateway/",
"https://voculinux.com/flatpak/",
"https://voculinux.com/install/nixos/",
"https://voculinux.com/install/debian/",
"https://voculinux.com/install/opensuse/",
"https://voculinux.com/install/linux-mint/",
"https://voculinux.com/install/pop-os/",
];
expectedUrls.forEach((url) => {
@@ -101,8 +91,16 @@ describe("SEO Assets", () => {
(match) => match[1],
);
expect(new Set(locs).size).toBe(locs.length);
routes.forEach((url) => {
expect(locs).toContain(url);
});
// Notice routes stay in the app but are dropped from navigation and the
// sitemap, so they are the only static routes allowed to be absent.
const NOTICE_ROUTES = [
"https://voculinux.com/flatpak/",
"https://voculinux.com/install/nixos/",
"https://voculinux.com/changelog/",
"https://voculinux.com/local-gateway/",
];
const missing = routes.filter((url) => !locs.includes(url));
expect(missing.sort()).toEqual([...NOTICE_ROUTES].sort());
});
});
+2 -2
View File
@@ -84,8 +84,8 @@ export default function AdvancedSettingsPage() {
dateModified: "2026-06-11",
author: {
"@type": "Person",
name: "Jatin K Malik",
url: "https://github.com/jatinkrmalik",
name: "Xavier Karma",
url: "https://bongbetic.com",
},
publisher: {
"@type": "Organization",
+2 -2
View File
@@ -79,8 +79,8 @@ export default function AlternativesPage() {
dateModified: "2026-02-19",
author: {
"@type": "Person",
name: "Jatin K Malik",
url: "https://github.com/jatinkrmalik",
name: "Xavier Karma",
url: "https://bongbetic.com",
},
publisher: {
"@type": "Organization",
+2 -2
View File
@@ -121,8 +121,8 @@ export default function AutostartPage() {
dateModified: "2026-11-01",
author: {
"@type": "Person",
name: "Jatin K Malik",
url: "https://github.com/jatinkrmalik",
name: "Xavier Karma",
url: "https://bongbetic.com",
},
publisher: {
"@type": "Organization",
+19 -687
View File
@@ -1,697 +1,29 @@
import Link from "next/link";
import { type Metadata } from "next";
import {
BookOpen,
CheckCircle2,
ChevronRight,
Clock,
Download,
Sparkles,
Tag,
Zap,
} from "lucide-react";
import { SeoSubpageShell } from "@/components/seo-subpage-shell";
import {
absoluteUrl,
buildPageMetadata,
UPSTREAM_REPO_URL,
} from "@/lib/seo";
const releases = [
{
version: "v0.18.5",
date: "2026-10-27",
type: "stable",
highlights: [
"A stop pressed while a recording is starting can no longer leave the microphone recording forever (PR #1023)",
"A microphone that stalls mid-read can no longer hang dictation in PROCESSING; capture reads happen outside the buffer lock (PR #1023)",
"Keyboards survive suspend and the GNOME lock screen again: the evdev grab is released when forwarding cannot run (PR #1025)",
"Dictation Pad and Settings no longer bind to other windows or travel workspaces together on Wayland (PR #1024)",
"Ctrl+mouse-wheel zoom works again in Firefox and other apps (PR #967)",
"install.sh gains an upgrade mode that keeps your existing configuration and models (PR #1026)",
"Paste-injection copies are marked sensitive on wl-clipboard 2.2+, so clipboard managers that honor the hint keep dictated text out of history (PR #956); AUR source publication is gated on attested sdists (PR #1020)",
"Tray prefers libayatana-appindicator (PR #1019), the Snap lists PulseAudio microphones (PR #1018), and the Wayland fallback overlay survives dock closes (PR #1017)",
],
},
{
version: "v0.18.4",
date: "2026-10-21",
type: "stable",
highlights: [
"Long push-to-talk dictations are queued in segments before the buffer cap, so nothing after the first two minutes is lost (PR #976)",
"Model downloads only start after you confirm them, Cancel cancels the whole change without freezing Settings, and switching models keeps your language settings (PR #1006)",
"Test Dictation always shows its own result; the Dictation Pad routing toggle moves to Settings > Dictation (PR #1005)",
"The GPU picker follows ggml's device list and CPU runs use most of your cores instead of 2 threads (PR #1001)",
"Press the recording hotkey again to cancel a slow transcription; nothing is typed or saved (PR #1004)",
"Wayland fixes: unique RemoteDesktop portal request tokens (PR #996), xdotool for XWayland apps outside IBus (PR #1003), and the first recording keeps focus on GNOME (PR #998)",
"Flatpak bundles onnxruntime for Silero VAD (PR #1002) and uses the X11 hotkey listener on X11 sessions (PR #997); keyboards with pointer axes like the Logitech K860 work again (PR #978)",
"Parakeet installs work again (PR #1000), update checks follow the build channel and can be turned off (PR #999), and Slovenian is supported (PR #995)",
],
},
{
version: "v0.18.3",
date: "2026-10-17",
type: "stable",
highlights: [
"\"new line\" and \"new paragraph\" voice commands insert real line breaks, standalone and mid-utterance, with no stray punctuation left behind (PR #958, #962)",
"Hotkeys work again on GNOME Shell X11: the evdev backend is preferred there over pynput (PR #963)",
"Quitting no longer collapses multi-layout keyboards; the IBus engine restore is skipped when a real engine is live (PR #963)",
"Snap, Flatpak, AppImage and .deb/.rpm builds compile pywhispercpp at the portable x86-64 baseline, ending SIGILL crashes on pre-Haswell CPUs (PR #959)",
"Snap recipe drops the denied `uinput` plug so new revisions clear Store review; Wayland typing uses the RemoteDesktop portal (PR #960)",
"Transcribe File's modal download dialog releases its grab once TinyDiarize lands, so the transcript's Copy and Save As buttons work (PR #965)",
"Flatpak remote publish warns loudly when unconfigured instead of skipping silently (PR #961); README gains install-format badges and a Nix section (PR #949)",
],
},
{
version: "v0.18.2",
date: "2026-10-12",
type: "stable",
highlights: [
"Dictation overlay orb pulses and ripples with live microphone level (PR #940)",
"Nix flake package with NixOS and Home Manager modules (PR #936), plus Vulkan GPU inference for pywhispercpp on Nix (PR #942)",
"Held keyboards are left ungrabbed at startup, so typing during launch cannot strand a key (PR #941)",
"Bundled pywhispercpp builds drop -march=native, fixing AppImage and Flatpak crashes on older CPUs (PR #939)",
"Piped curl | bash install keeps the script on stdin and prompts via /dev/tty (PR #935)",
"Installer covers cairo headers on openSUSE, Void, Solus and Alpine; evdev monitors remapper virtual keyboards ungrabbed (PR #937, #932)",
"CI gates the remote curl | bash install path end to end (PR #898)",
],
},
{
version: "v0.18.1",
date: "2026-10-08",
type: "stable",
highlights: [
"Settings > Proxy page for model downloads and update checks behind SOCKS5 or HTTP CONNECT proxies, with optional auth (PR #909, fixes #655)",
"Opt-in JSONL transcript persistence: a transcript survives restarts and failed text injections instead of being lost (PR #907, fixes #758)",
"Native .deb and .rpm packages for Debian/Ubuntu and Fedora, x86_64 and aarch64, attached to the GitHub Release (PR #913)",
"Slimmer tray menu: Start on Login and About now live only in Settings (PR #905, fixes #654)",
"Speech Model Advanced pickers stage changes until you confirm Download instead of starting a fetch per click (PR #908, fixes #894)",
"Dictation Pad no longer ghosts away after sitting idle under a Wayland compositor (PR #912, fixes #896)",
"evdev keyboard discovery stops grabbing Logitech mice, Goodix touchpads, and power buttons as keyboards (PR #902, #917, #918, fixes #900, #914, #915)",
"KDE Plasma 6 Wayland: KWin VirtualKeyboard detection fixed, so dictated letter case stops scrambling (PR #919, fixes #911)",
"Text injection ends option parsing before typed text, so a chunk starting with a hyphen no longer truncates dictation (PR #922, fixes #921)",
"Startup reuses an on-disk same-size whisper.cpp weight instead of re-resolving it (PR #923, fixes #916)",
"uninstall.sh no longer deletes files in the directory you ran it from (PR #901, fixes #897)",
"Settings > Advanced Initial Prompt accepts typed input again, and transcript and log saves no longer double the .txt extension (PR #927)",
"deb declares python3-socks, rpm declares python3-pysocks; snap attach keeps checksums on failure; remote curl | bash install gated end to end (PR #920, #899, #906)",
],
},
{
version: "v0.18.0",
date: "2026-10-02",
type: "stable",
highlights: [
"Dictation Pad: in-app window receives dictation and you copy text out by hand, so Wayland injection quirks cannot touch it (PR #887, fixes #726)",
"Hotkey suppression: evdev grabs the dictation shortcut and forwards everything else through a uinput clone; the key no longer types into the focused app, and still works when /dev/uinput is write-only (PR #873, #893, fixes #871)",
"PipeWire capture path with microphone and system-audio sources (PR #889, #883, fixes #751, #760)",
"RemoteDesktop portal text injection on Wayland, no ydotoold needed (PR #885, fixes #750)",
"Per-language dictation shortcuts, follow the active keyboard layout while dictating, and recent dictations in the tray menu (PR #880, #837, #487, fixes #805, #821)",
"File transcription with TinyDiarize per-speaker labels via --transcribe-file and the tray (PR #884, fixes #756)",
"Opt-in D-Bus activation so compositor global shortcuts and scripts can start dictation (PR #568, fixes #761)",
"Combined custom dictionary with transcript corrections, postprocessing script hook, bilingual dictation candidates (PR #890, #479, #424)",
"Floating glowing dictation overlay, lower other audio while dictating, keep recording while an idle model reloads (PR #516, #861, #851)",
"config.json can pin the text-injection backend (PR #649, fixes #476)",
"GNOME Wayland: Alt+Shift and Win+Space layout switching keeps working; IBus guard no longer flips GNOME/X11 to a US layout (PR #876, #827, fixes #848)",
"Shortcut recorder learns unmapped F19/F24 and XF86-aliased F13-F23 (PR #844, fixes #843)",
"Vosk refuses a model that would exceed available memory instead of tripping the OOM killer (PR #850, fixes #676)",
"Model downloads cancel stalled fetches; the download dialog shows verifying instead of a stalled 100% (PR #888, #864, fixes #679, #863)",
"Update checker falls back when GitHub API rate-limits (PR #846, fixes #845)",
"Settings: scrollable category list, Dictation Tone grayed while sounds are off, Test Dictation artifact and missing transcription fixed (PR #886, #877, #853, fixes #678, #849, #847, #720)",
"AUR voculinux-bin ships the AppImage with per-arch digests; releases publish a signed self-hosted Flatpak remote; snap-promote workflow gates stable promotion; snap gains hardware-observe (PR #879, #875, #881, #858, fixes #817, #785, #783, #857)",
"Installer: hash-pinned dependencies and build tools, sourced modules, generated distro package map (PR #856, #862, #872)",
"Optional verified Orukeet model for Parakeet; VocaGateway runs locally from Settings > Advanced (PR #840, #774)",
],
},
{
version: "v0.17.0",
date: "2026-09-16",
type: "stable",
highlights: [
"Faster Whisper engine: CTranslate2 / INT8 Whisper on CPU, optional extra, installer --engine=faster_whisper (PR #543)",
"Parakeet TDT 0.6B via sherpa-onnx: default v3-european bundle or v2-english, installer --engine=parakeet (PR #802)",
"Speech Model simple setup: language + speed/accuracy first; engine/size under Advanced (PR #801)",
"First run seeds recognition language from keyboard layout / locale (PR #796, #777)",
"Language picker filters while the list is open (PR #798)",
"Recommended model button sets size and specialization together and will not ignore a file already on disk (PR #797, #778)",
"Localized punctuation voice commands for it/fr/de/es/pt/nl/pl/ru (PR #642, #640)",
"Bare F1–F24 push-to-talk shortcuts (PR #815)",
"Snap packaging with ydotool and uinput for native Wayland; the Store denied the uinput grant, so later revisions drop the plug and type via the RemoteDesktop portal (PR #519, #823, #822)",
"Release workflow attaches Voculinux-<version>-{x86_64,aarch64}.flatpak (PR #786, #784)",
"XWayland clipboard paste instead of layout-garbled xdotool type (PR #680, #657)",
"Layout-aware Ctrl+V paste; ydotool releases Ctrl after a timed-out paste (PR #788, #675, #658)",
"Read WM_CLASS with xprop so xdotool stops dumping core on KDE Plasma Wayland (PR #807)",
"Settings freeze on already-downloaded model, English whisper.cpp .en variant, PTT tray redraw, sink-wake audio cues, Parakeet decode survival (PR #790, #795, #780, #809, #804, #803)",
"English-only models no longer hide other languages; picking Polish or auto-detect switches to the multilingual sibling (PR #826)",
"Speech Model copy and Unused downloads as a sibling expander of Advanced; VocaWin on the About family grid (PR #826)",
"install.sh: Faster Whisper is CPU CTranslate2/INT8; GPU backend does not install the CUDA toolkit (PR #826)",
"HDA analog mics (3-8 channel) open at native layout instead of forced 2ch/48kHz; capture is downmixed to mono (PR #829, fixes #813)",
"Native GTK installs (AUR / install.sh) follow OS dark/light; skipped when GTK_THEME is set so AppImage and user overrides stay in charge (PR #830, fixes #816)",
"Lock-derived Flatpak/AUR deps, hash-pinned exports for every engine extra, published-release verification, install.sh distro matrix (PR #819, #828, #791, #810)",
],
},
{
version: "v0.16.2",
date: "2026-09-05",
type: "stable",
highlights: [
"KDE: skip leftover IBus when it is not the session IM so dictation types into Kate, browsers, and terminals (PR #753, fixes #752)",
"Wayland shortcuts: wtype and ydotool deliver real chords instead of refusing or silently doing nothing (PR #715)",
"IBus shortcuts: route X11_IBUS through xdotool and WAYLAND_IBUS through wtype/ydotool (PR #716)",
"Voice command delete that: send real BackSpace key events instead of U+0008 text (PR #714)",
"Installer: Fedora and Arch need glslc/shaderc packages, not glslang (PR #763, #604)",
"Nightly: stamp version.py before python -m build so wheel metadata matches the filename (PR #762)",
"Release integrity: checksums, signatures, and pinned builders for release artifacts (PR #759, epic #701 phase 5)",
"CI: gate AUR PKGBUILD builds on every PR; test the distros the docs promise and fix docs drift (PR #772, #773)",
"Site: VocaGateway family card is Beta; README logo, badges, and privacy copy (PR #765, #764)",
"Deps: bump the github-actions group (PR #766)",
"Snap 0.16.2 edge (rev 7) has no uinput plug: snap connect voculinux:uinput fails, and dictation only reaches XWayland apps. Later revisions ship the RemoteDesktop portal instead; the Store denied the uinput grant (PR #823)",
],
},
{
version: "v0.16.1",
date: "2026-08-30",
type: "stable",
highlights: [
"Startup uses the engine's own model size instead of a leftover generic key; the model is saved only after it loaded (PR #684, #685, fixes #681)",
"Unused leftover-model list shows every row; one ConfigManager so Settings writes stick (PR #686, #691, fixes #683, #689)",
"Tray stays idle after leftover transcription on toggle stop, and the missing-model notification can download the recommended model (PR #741, #687)",
"Terminals get Ctrl+Shift+V paste; GNOME XWayland layout is restored after scoped IBus inject (PR #734, #742)",
"Installer requires Python 3.11, verifies model downloads, and no longer invents GPUs (PR #713, #736)",
"AppImage built against a glibc floor that boots on Debian 12 through current Fedora (PR #743, #744)",
"AUR: build against Arch extra setuptools 84, and skip context_params on AUR pywhispercpp 1.4 so the app starts",
"Settings dropdowns share a width; About is quieter with family platform marks (PR #754)",
],
},
{
version: "v0.16.0",
date: "2026-08-23",
type: "stable",
highlights: [
"In-app update checker (stable/nightly) plus tray Update Available when GitHub has a newer release (PR #631, #645)",
"New installs default to hold Right Alt push-to-talk; existing configs keep their shortcut (PR #648)",
"Searchable language list and delete unused downloaded speech models (PR #672, #671)",
"Family dictation tone picker in Settings → Audio: Lift, Flick, Ember, Step, Voca, Soft, Chirp, Scale, Drop, Glass, Off, plus Preview. New installs default to Voca. Catalog tones are the family preview WAVs, not synthesized files (PR #707, #708)",
"License migrated to AGPL-3.0 (PR #660)",
"App icon, tray states, and site favicons use the shared Voca family mic instead of the old Linux rounded-rect (PR #704)",
"Installer hardened with Justfile and uv lockfiles; distro python3-gi required (no pip sdist of PyGObject). Epic #701 remains open (PR #700, #705, #706)",
"Test Dictation no longer reports no speech when recognition never started (PR #702)",
"IBus/X11 reliability: restorable scoped injection, engine restore after teardown, XKB layout restore (PR #623, #643, #665)",
"Audio, clipboard, and AppImage/GPU packaging fixes (PR #629, #673, #588, #646, #674, #637)",
"Settings About page groups this app, the VocaHQ family, and talk-to-us links (PR #718)",
"voculinux.com restyled to the Voca family workbench; Discord and X links point at VocaHQ (PR #728, #729, #722, #717)",
],
},
{
version: "v0.15.0",
date: "2026-07-28",
type: "stable",
highlights: [
"Searchable sidebar settings with live search replace the seven-tab notebook (PR #601)",
"AppImage packages for x86_64 and aarch64 on GitHub Releases (PR #573, #602)",
"Expanded speech-language catalog (~33 languages + Auto-detect), including Hungarian; VOSK only lists languages with official models (PR #616, fixes #565)",
"Settings: dictation status / mic level / Test Dictation / Close live in the sidebar footer (PR #618)",
"Settings: Custom Shortcut Record/Set controls show again (PR #619)",
"Languages: English (India) maps to Whisper code en for whisper.cpp / Whisper / remote API (PR #617)",
"Auto-capitalize after sentence punctuation; trailing space so the next utterance does not glue on (PR #554, #608)",
"Auto-pause competing apps and idle model keep-alive unload for battery/GPU headroom (PR #592, closes #445, #591)",
"Vulkan: auto-select discrete GPU and pick a device in Advanced settings (PR #590, closes #589)",
"Wayland: use IBus on previously unbridged compositors when ibus-wayland is running (PR #614, closes #607)",
"IBus: keep engine teardown correct when parent destroy fails (PR #613, fixes #606)",
"CLI: voculinux --version (PR #563, closes #555)",
"Settings info notices flattened; Bluetooth mic heap-corruption fix; KDE unbridged-IBus skip when ibus-wayland is absent; xdotool focus preserve; installer/AUR fixes (PR #615, #599, #577, #564, #583, #569, #597, #579, #586)",
"Marketing site redesign; languages page per-engine badges; robots.txt indexing fix (PR #582, #616, #610)",
],
},
{
version: "v0.14.2",
date: "2026-07-17",
type: "stable",
highlights: [
"IBus: restore engine process launch after Flatpak XDG path import so the engine no longer dies with ImportError and falls back to ydotool/clipboard paste (PR #534)",
"IBus: wait for FocusIn before commit on scoped injection so the first dictation of a session is not dropped on GNOME Wayland (PR #533, fixes #523)",
"Settings UI: notebook tabs scroll so the dialog fits the monitor; wheel events from unfocused combos/spins reach the tab scroller (PR #538, #541)",
],
},
{
version: "v0.14.1",
date: "2026-07-17",
type: "stable",
highlights: [
"Flatpak packaging for universal distribution: whisper.cpp engine, XDG sandbox paths, global hotkeys via evdev, Wayland text injection via wl-copy + ydotool (PR #484, closes #167)",
"AUR package and CI publish path for Arch Linux (PR #518)",
"Layout-aware combo keys so custom shortcuts work on non-US keyboard layouts (PR #514)",
"Installer fix for sg not found on Ubuntu 26.04 / Debian 13 (PR #524)",
"Text injection treats XIM none as unset (PR #512)",
"Website screenshot gallery refresh and Dependabot npm alert fixes (PR #521, #515)",
],
},
{
version: "v0.14.0-beta",
date: "2026-07-13",
type: "beta",
highlights: [
"Configurable modifier+key hotkeys: set custom shortcuts with any combination of Ctrl, Alt, Shift, and Super plus a letter/number key, e.g. Alt+R or Ctrl+Shift+V (PR #493)",
"Remote API engine now supports FunASR/SenseVoice models via OpenAI-compatible endpoints; SenseVoice metadata labels are stripped before text injection (PR #468)",
"GNOME Wayland IBus text injection restored when only a bare xkb engine is configured; engine restore fallback now picks the correct IM engine (PR #506, #500)",
"KDE Plasma Wayland IBus text-injection path restored after recent compositor-detection regressions (PR #502)",
"Wayland text injection now waits for held modifiers to release before typing, preventing accidental shortcut triggers and garbled output (PR #494)",
"Shortcuts UI keeps preset and custom shortcut selection exclusive: selecting a preset clears the custom field, and setting a custom combo selects the Custom Shortcut preset (PR #509)",
"whisper.cpp no longer defaults to all CPU cores on hybrid processors, improving UI responsiveness and battery life (PR #492)",
"Fixed a crash on recording start when the selected audio device index no longer matches the current system enumeration (PR #499)",
"Installer includes xsel as a fallback for the Wayland clipboard path when xclip is unavailable (PR #496)",
"Removed an outdated long comment about whisper.cpp default thread counts (PR #505)",
],
},
{
version: "v0.13.0-beta",
date: "2026-06-30",
type: "beta",
highlights: [
"Guided whisper.cpp model selection: pick a size plus a specialization (English-only, quantized Q5/Q8, or Large v3 Turbo) through split Model Size and Specialization dropdowns with in-app guidance; the --model flag also accepts exact IDs like medium.en-q5_0 and large-v3-turbo (PR #465)",
"Dictation now keeps a space between segments spoken with a pause in between, so words no longer run together after a silence (PR #464)",
"Keyboard shortcuts now work on keyboards hotplugged after Voculinux starts, with the evdev backend rescanning for new devices and recovering from disconnects (PR #467)",
"Voculinux now detects KDE Plasma Wayland sessions and points you to enable IBus Wayland for reliable text injection, surfaced during install and when wtype injection fails (PR #466)",
"Wayland: fixed garbled text on non-US keyboard layouts (AZERTY/QWERTZ/Dvorak) and a clipboard-copy hang; ydotool now pastes through the clipboard, which is layout-independent (PR #480)",
"Wayland: use wtype/ydotool instead of IBus on compositors that don't bridge it to native apps like COSMIC, Sway, and Hyprland, fixing silent text drops (PR #486)",
"Wayland/IBus: require a real IM engine before using IBus, so a bare xkb layout no longer causes silent text drops on GNOME/Mutter and other compositors (#478)",
"Wayland: keep the keyboard layout intact by not running setxkbmap, which was flipping XWayland apps to us after dictation (#474)",
"Faster ydotool text injection via an explicit --key-delay (PR #488)",
"Settings dialog height capped on high-resolution displays (PR #465)",
"Refreshed website docs with new feature pages for Remote API, Silero VAD, advanced whisper.cpp settings, and desktop reliability, plus responsive layout polish (PR #470)",
],
},
{
version: "v0.12.0-beta",
date: "2026-06-07",
type: "beta",
highlights: [
"Remote API speech recognition engine with installation and configuration support (PR #335)",
"Silero VAD drops silence-only buffers for cleaner dictation when ONNX Runtime support is available (PR #447)",
"Thread safety hardening for Remote API, IBus, and text injection paths (PR #452)",
"IBus preserves user engines for dead keys and captures the current engine during scoped activation (PR #457, #458)",
"Remote Server settings now respect the Advanced toggle and the settings dialog fits lower-resolution screens (PR #454, #456)",
"CUDA diagnostics now include auto-remediation and behavioral tests (PR #451)",
"Corrected whisper.cpp and VOSK model download size metadata (PR #453)",
"Startup now works without the pynput backend (PR #448)",
"Remote API developer test server documentation (PR #455)",
"Website speech demo browser support clarification (PR #449) and GitHub Sponsors funding configuration",
],
},
{
version: "v0.11.0-beta",
date: "2026-05-30",
type: "beta",
highlights: [
"New Advanced Settings tab with whisper.cpp anti-hallucination parameters - temperature, no_speech_threshold, max segment length, and more (PR #415)",
"IBus engine readiness probe at startup with hardened retries (PR #391)",
"IBus runtime failure recovery without app restart (PR #411)",
"IBus engine instance destruction handled on keyboard layout switch (fixes #388, closes #389)",
"Preserve final speech on stop - no more truncated transcriptions (fixes #401)",
"Play stop sound immediately on release and after audio thread joins (PR #426, #436)",
"Repair pywhispercpp library loading in installer (PR #433)",
"Reduce whisper.cpp CPU threads and ensure GPU backend builds in dev mode (PR #439)",
"Correct openSUSE Tumbleweed dependencies with fallback handling (PR #418, #420)",
"Harden Debian compatibility layer in installer (PR #437)",
"Add Python 3.14 support and bump lxml>=6.1.0 (fixes #404)",
"Validate pyproject.toml/setup.py content before entering local repo mode (fixes #396)",
"Reuse existing whispercpp builds during install (PR #421)",
"Refresh ldconfig after openSUSE typelib install, clarify python3XY placeholder convention (PR #438)",
"Clean up runtime log noise and cache hardware detection",
"Test coverage: recognition internals, IBus edge cases, CI notification suppression (PR #410, #414)",
"Clarify PyPI installation requirements (PR #423)",
"Dependency bumps: Next.js security updates (PR #399, #429), PostCSS",
],
},
{
version: "v0.10.2-beta",
date: "2026-04-08",
type: "beta",
highlights: [
"Handle non-ASCII characters (á, é, ñ, etc.) with ydotool via clipboard paste fallback (fixes #362, PR #376)",
"Detect IBus on Wayland without legacy env vars and fix text injection (PR #381)",
"Start IBus engine process before checking registration to fix startup on some systems (fixes #360, PR #361)",
"Add missing dependencies for Pop!_OS and Ubuntu 24.04+ including cmake, libcairo2-dev, libgirepository (PR #379)",
"Systematic code quality refactor across 20 dimensions (PR #377)",
"Clarify missing GNOME AppIndicator support on Debian (PR #385)",
"Redesigned OG image for voculinux.com - cleaner, professional, text-based layout (PR #392)",
"Test coverage improvements: mock Notify module, tray degraded-startup, IBus socket-readiness branches (PR #384, #386, #390)",
],
},
{
version: "v0.10.1-beta",
date: "2026-03-30",
type: "beta",
highlights: [
"Bundled package resources to prevent missing system tray icons (fixes #349, PR #354)",
"Stopped recognition before engine switches to prevent segfaults (fixes #350, PR #355)",
"Added a dedicated Close button in Settings for better WM compatibility (fixes #323, PR #356)",
"Preserved XKB layout state during Voculinux IBus activation (fixes #292, PR #343)",
"Auto-recover speech recognition after system suspend/resume via new D-Bus handler (fixes #367, PR #369)",
"Restart keyboard shortcut backend after resume to keep shortcuts working (PR #371)",
"Delayed keyboard restart to allow USB re-enumeration after resume (PR #372)",
"Fixed premature transcription during push-to-talk silence (fixes #358, PR #359)",
"Disabled copy-to-clipboard by default in Settings (PR #370)",
"Maintenance updates: npm/yarn dependency refresh and brace-expansion dev dependency bump (PR #346, #357)",
],
},
{
version: "v0.10.0-beta",
date: "2026-03-25",
type: "beta",
highlights: [
"Generalized keyboard modifier alias matching across layouts for more reliable shortcuts",
"Audio channel probing now validates device-supported sample rates before selection",
"evdev now handles SYN_DROPPED to prevent stale modifier state",
"IBus engine activation now uses register_component for stronger text-injection startup",
"Settings dialog forces window decorations to prevent missing-titlebar behavior",
"Tray icon refresh now uses icon names for better AppIndicator compatibility",
"Coverage increased to 80%+ with additional IBus launch/main-entry tests",
"Installer and CI polish: latest-tag fallback via GitHub API, Node 24 deploy, and path-filtered workflows",
],
},
{
version: "v0.9.0-beta",
date: "2026-03-14",
type: "beta",
highlights: [
"Left/right modifier key distinction - choose Left Ctrl vs Right Ctrl for your shortcut",
"Sound effects toggle - enable or disable audio feedback from Settings",
"Wayland clipboard fallback - auto-copies text when virtual keyboard injection isn't available",
"Display availability check - graceful error when running in headless environments",
"Fixed unwanted leading space at the start of each new transcription session",
"Fixed shortcut mode (toggle/push-to-talk) not applying on startup",
"Improved Debian/pipx installation guidance and cross-distro error messages",
"Grouped shortcut selector UI - shortcuts organised by Either/Left/Right side",
],
},
{
version: "v0.8.0-beta",
date: "2026-03-01",
type: "beta",
highlights: [
"Push-to-talk shortcut mode (hold to speak, release to stop)",
"Optional voice commands with VOSK auto-enable behavior",
"Improved shortcut mode switching and callback reliability",
"IBus active-method detection before text injection",
"Audio hardware compatibility fixes (sample rate and channel count)",
"Fedora startup dialog stability fix",
"Web SEO expansion and homepage visual refresh",
],
},
{
version: "v0.7.0-beta",
date: "2026-02-22",
type: "beta",
highlights: [
"Autostart on login support (XDG autostart)",
"Tabbed settings dialog (Speech Engine, Recognition, Text Injection, Audio Feedback, General)",
"Intel GPU compatibility detection - auto fallback to CPU for incompatible GPUs",
"Single instance prevention - prevents multiple Voculinux running",
"Evdev device management - removes disconnected devices to prevent CPU spin",
"Improved GPU detection - avoids false positives on systems without dev libraries",
"IBus fallback - skip setup when daemon not running",
"Fedora dnf check-update fix",
"Web SEO - 7 new optimized pages",
],
},
{
version: "v0.6.3-beta",
date: "2026-02-19",
type: "beta",
highlights: [
"Fixed installer default tag pointing to correct version",
"Added missing psutil dependency for fresh installs",
"Process check and interactive prompts in install/uninstall",
"Removed leading space from first speech transcription",
],
},
{
version: "v0.6.2-beta",
date: "2026-02-18",
type: "beta",
highlights: [
"Interactive backend selection (GPU/CPU)",
"Enhanced welcome message",
"Simplified install commands",
"Better GPU support and Vulkan detection",
],
},
{
version: "v0.6.0-beta",
date: "2026-02-12",
type: "beta",
highlights: [
"whisper.cpp as default engine",
"Multi-language support with auto-detection",
"System tray indicator",
"Full Wayland support",
"IBus text injection engine",
],
},
{
version: "v0.5.0-beta",
date: "2026-02-06",
type: "beta",
highlights: [
"First beta release",
"Stable core functionality",
"Multiple speech engine support",
"Improved text injection",
],
},
{
version: "v0.4.1-alpha",
date: "2026-01-29",
type: "alpha",
highlights: [
"Language selector UI",
"App drawer launch fix",
"Better commit handling",
"Improved update mechanism",
],
},
{
version: "v0.4.0-alpha",
date: "2026-01-29",
type: "alpha",
highlights: [
"Multi-language support (French, German, Russian)",
"Debian 13+ compatibility",
"Python 3.12+ support",
"Tag-based version selection in installer",
],
},
{
version: "v0.3.0-alpha",
date: "2026-01-21",
type: "alpha",
highlights: [
"Initial public alpha",
"Basic speech recognition",
"X11 text injection",
"VOSK engine support",
],
},
];
const getTypeStyles = (type: string) => {
switch (type) {
case "stable":
return {
badge:
"bg-primary/10 text-primary",
label: "Stable",
};
case "beta":
return {
badge:
"bg-primary/10 text-primary",
label: "Beta",
};
case "alpha":
return {
badge:
"bg-muted text-muted-foreground",
label: "Alpha",
};
default:
return {
badge: "bg-muted text-muted-foreground",
label: type,
};
}
};
import { ChannelNotice } from "@/components/channel-notice";
import { buildPageMetadata, SOURCE_REPO_URL } from "@/lib/seo";
export const metadata: Metadata = buildPageMetadata({
title: "Changelog and Release Notes",
title: "Changelog: Voculinux Release Notes",
description:
"Track Voculinux release history and version updates. See what's new in each version of our Linux voice dictation software.",
"Voculinux release notes are on the fork's releases page. The upstream changelog is not published for Voculinux.",
path: "/changelog",
keywords: [
"voculinux changelog",
"voculinux release notes",
"voice dictation linux updates",
"speech to text linux versions",
],
});
export default function ChangelogPage() {
const articleJsonLd = {
"@context": "https://schema.org",
"@type": "Article",
headline: "Voculinux Changelog - Release History",
description:
"Complete release history for Voculinux, the offline voice dictation software for Linux.",
dateModified: "2026-10-27",
author: {
"@type": "Person",
name: "Jatin K Malik",
url: "https://github.com/jatinkrmalik",
},
publisher: {
"@type": "Organization",
name: "Voculinux",
logo: {
"@type": "ImageObject",
url: absoluteUrl("/voculinux.png"),
},
},
mainEntityOfPage: absoluteUrl("/changelog"),
};
export default function ChangelogNoticePage() {
return (
<SeoSubpageShell>
<script
type="application/ld+json"
dangerouslySetInnerHTML={{ __html: JSON.stringify(articleJsonLd) }}
/>
<section>
<p className="border-primary/30 bg-primary/10 mb-4 inline-flex items-center gap-2 rounded-full border px-4 py-1.5 text-sm font-medium text-primary">
<Clock className="h-4 w-4" />
Release History
</p>
<h1 className="mb-5 font-display text-4xl font-semibold tracking-tight sm:text-5xl">
Voculinux Changelog
</h1>
<p className="mb-8 max-w-4xl text-lg text-muted-foreground">
Track every release and see how Voculinux has evolved. From initial
alpha to stable releases, follow the journey of Linux voice dictation.
</p>
<p className="mb-8 max-w-4xl text-sm text-muted-foreground">
Fork status: the releases below are the upstream Vocalinux history
this fork is ported from. Voculinux fork releases are not published
yet, so each release link goes to upstream GitHub Releases.
</p>
</section>
<section className="space-y-6">
{releases.map((release, index) => {
const styles = getTypeStyles(release.type);
return (
<article
key={release.version}
className="rounded-[12px] border border-border bg-background p-6"
>
<div className="mb-4 flex flex-wrap items-center gap-3">
<div className="flex items-center gap-2">
<Tag className="h-5 w-5 text-primary" />
<span className="font-display text-2xl font-semibold">{release.version}</span>
</div>
<span
className={`rounded-full px-3 py-1 text-xs font-semibold ${styles.badge}`}
>
{styles.label}
</span>
{index === 0 && (
<span className="bg-primary/10 rounded-full px-3 py-1 text-xs font-semibold text-primary">
Latest
</span>
)}
<span className="text-sm text-muted-foreground">
{release.date}
</span>
</div>
<ul className="space-y-2">
{release.highlights.map((highlight) => (
<li
key={highlight}
className="flex items-start gap-2 text-muted-foreground"
>
<CheckCircle2 className="mt-0.5 h-4 w-4 flex-shrink-0 text-primary" />
{highlight}
</li>
))}
</ul>
{index === 0 && (
<div className="mt-5 flex flex-wrap gap-3">
{[
{ href: "/remote-api/", label: "Remote API guide" },
{
href: "/voice-activity-detection/",
label: "Silero VAD guide",
},
{ href: "/advanced-settings/", label: "Advanced settings" },
{
href: "/desktop-reliability/",
label: "Reliability overview",
},
].map((link) => (
<Link
key={link.href}
href={link.href}
className="border-primary/30 bg-primary/10 hover:bg-primary/15 inline-flex items-center gap-1.5 rounded-full border px-3 py-1.5 text-sm font-semibold text-primary"
>
{link.label}
<ChevronRight className="h-3.5 w-3.5" />
</Link>
))}
</div>
)}
<div className="mt-4 border-t border-border pt-4">
<a
href={`${UPSTREAM_REPO_URL}/releases/tag/${release.version}`}
target="_blank"
rel="noopener noreferrer"
className="inline-flex items-center gap-1.5 text-sm font-medium text-primary hover:underline"
>
<Download className="h-4 w-4" />
View upstream release on GitHub
<ChevronRight className="h-4 w-4" />
</a>
</div>
</article>
);
})}
</section>
<section className="mt-12 rounded-[12px] border border-border bg-muted p-8">
<h2 className="mb-4 font-display text-2xl font-semibold">Stay Updated</h2>
<ul className="space-y-3 text-muted-foreground">
<li className="flex items-center gap-2">
<Sparkles className="h-4 w-4 text-primary" />
Watch the repository on GitHub for release notifications
</li>
<li className="flex items-center gap-2">
<Zap className="h-4 w-4 text-primary" />
Re-run the installer to update to the latest version
</li>
<li className="flex items-center gap-2">
<BookOpen className="h-4 w-4 text-primary" />
Check the{" "}
<Link
href="/install/"
className="font-semibold text-primary hover:underline"
>
install guide
</Link>{" "}
for update instructions
</li>
</ul>
</section>
</SeoSubpageShell>
<ChannelNotice
title="Changelog"
lede="The upstream changelog is not published for Voculinux (Void only). Release notes for Voculinux are on the fork's releases page."
>
<p>
<a
href={`${SOURCE_REPO_URL}/releases`}
className="font-semibold text-primary hover:underline"
rel="noopener noreferrer"
>
Voculinux releases
</a>
</p>
</ChannelNotice>
);
}
+9 -23
View File
@@ -42,7 +42,7 @@ const engineTable = [
speed: "Fast CPU inference (CTranslate2 / INT8)",
hardware: "CPU",
accuracy: "High (Whisper-class)",
footprint: "Optional extra; not in AppImage/Flatpak/Snap",
footprint: "Optional extra; not in the Void package",
bestFor: "CPU-only machines that want Whisper quality without PyTorch",
icon: Sparkles,
iconColor: "text-primary",
@@ -111,8 +111,8 @@ export default function CompareEnginesPage() {
dateModified: "2026-06-11",
author: {
"@type": "Person",
name: "Jatin K Malik",
url: "https://github.com/jatinkrmalik",
name: "Xavier Karma",
url: "https://bongbetic.com",
},
publisher: {
"@type": "Organization",
@@ -279,7 +279,7 @@ export default function CompareEnginesPage() {
<h2 className="mb-3 text-2xl font-semibold">When to pick Faster Whisper</h2>
<p className="text-sm text-muted-foreground">
Choose Faster Whisper for Whisper-class accuracy on CPU without the
PyTorch stack. Optional extra; not bundled in AppImage, Flatpak, or Snap.
PyTorch stack. Optional extra; not in the Void package.
</p>
</article>
@@ -296,8 +296,8 @@ export default function CompareEnginesPage() {
<p className="text-sm text-muted-foreground">
Choose Parakeet for English or European dictation on CPU via
sherpa-onnx. It uses a bundled language set (v3-european default)
instead of the Whisper catalog picker. Optional extra; not in
AppImage, Flatpak, or Snap.
instead of the Whisper catalog picker. Optional extra; not in the
Void package.
</p>
</article>
@@ -325,26 +325,12 @@ export default function CompareEnginesPage() {
<li className="flex gap-2">
<CheckCircle2 className="mt-1 h-4 w-4 flex-shrink-0 text-primary" />
<span>
Install by distro:
Install on Void Linux, from the signed xbps repository:{" "}
<Link
href="/install/ubuntu/"
href="/install/"
className="font-semibold text-primary hover:underline"
>
Ubuntu
</Link>
,
<Link
href="/install/fedora/"
className="font-semibold text-primary hover:underline"
>
Fedora
</Link>
,
<Link
href="/install/arch/"
className="font-semibold text-primary hover:underline"
>
Arch Linux
install guide
</Link>
.
</span>
+3 -3
View File
@@ -96,8 +96,8 @@ export default function CustomDictionaryPage() {
dateModified: "2026-11-01",
author: {
"@type": "Person",
name: "Jatin K Malik",
url: "https://github.com/jatinkrmalik",
name: "Xavier Karma",
url: "https://bongbetic.com",
},
publisher: {
"@type": "Organization",
@@ -349,7 +349,7 @@ Okonkwo`}</code>
],
[
"Environment",
"Inherits Voculinux's environment. When Voculinux runs from the AppImage, the bundle's library paths are removed first.",
"Inherits Voculinux's environment, with any bundled library paths removed first.",
],
["Timeout", "10 seconds."],
[
+13 -10
View File
@@ -12,7 +12,11 @@ import {
Wrench,
} from "lucide-react";
import { SeoSubpageShell } from "@/components/seo-subpage-shell";
import { absoluteUrl, buildPageMetadata } from "@/lib/seo";
import {
absoluteUrl,
buildPageMetadata,
SOURCE_REPO_URL,
} from "@/lib/seo";
const reliabilityFeatures = [
{
@@ -62,7 +66,7 @@ const releaseMap = [
{
version: "v0.17.0",
highlights:
"Faster Whisper and Parakeet engines, Speech Model simple setup, first-run system language, Snap ydotool/uinput, workflow Flatpak bundles, XWayland clipboard paste, xprop WM_CLASS instead of crashing xdotool, HDA analog mics at native layout, native GTK OS dark/light, English-only models no longer trap other languages, and PTT tray/audio cue fixes.",
"Faster Whisper and Parakeet engines, Speech Model simple setup, first-run system language, ydotool/uinput packaging fixes, workflow bundles, XWayland clipboard paste, xprop WM_CLASS instead of crashing xdotool, HDA analog mics at native layout, native GTK OS dark/light, English-only models no longer trap other languages, and PTT tray/audio cue fixes.",
},
{
version: "v0.16.2",
@@ -72,12 +76,12 @@ const releaseMap = [
{
version: "v0.16.1",
highlights:
"Per-engine model at startup, leftover tray idle after toggle stop, Ctrl+Shift+V paste in terminals, GNOME XWayland layout after scoped IBus inject, Python 3.11 installer floor with verified downloads, and AppImage glibc that boots on Debian 12.",
"Per-engine model at startup, leftover tray idle after toggle stop, Ctrl+Shift+V paste in terminals, GNOME XWayland layout after scoped IBus inject, Python 3.11 installer floor with verified downloads, and a glibc floor that boots on Debian 12.",
},
{
version: "v0.16.0",
highlights:
"Update checker with tray notifications, Right Alt PTT default for new installs, searchable language list, unused model deletion, Test Dictation missing-model message, IBus/XKB restore fixes, safer audio capture, clipboard restore, installer python3-gi requirement, and AppImage/GPU packaging hardening.",
"Update checker with tray notifications, Right Alt PTT default for new installs, searchable language list, unused model deletion, Test Dictation missing-model message, IBus/XKB restore fixes, safer audio capture, clipboard restore, installer python3-gi requirement, and packaging and GPU hardening.",
},
{
version: "v0.15.0",
@@ -129,8 +133,8 @@ export default function DesktopReliabilityPage() {
dateModified: "2026-10-02",
author: {
"@type": "Person",
name: "Jatin K Malik",
url: "https://github.com/jatinkrmalik",
name: "Xavier Karma",
url: "https://bongbetic.com",
},
publisher: {
"@type": "Organization",
@@ -219,7 +223,6 @@ export default function DesktopReliabilityPage() {
{ href: "/hyprland-sway/", label: "Hyprland, sway, and i3" },
{ href: "/faster-whisper/", label: "Faster Whisper engine" },
{ href: "/parakeet/", label: "Parakeet engine" },
{ href: "/flatpak/", label: "VocaHQ Flatpak remote" },
].map((guide) => (
<li key={guide.href}>
<Link href={guide.href} className="font-medium text-primary hover:underline">
@@ -286,15 +289,15 @@ export default function DesktopReliabilityPage() {
Shortcut guide <ChevronRight className="h-4 w-4" />
</span>
</Link>
<Link href="/changelog/" className="group">
<a href={`${SOURCE_REPO_URL}/releases`} className="group" rel="noopener noreferrer">
<h3 className="font-semibold">Release history</h3>
<p className="mt-1 text-sm text-muted-foreground">
Review each beta release and the reliability fixes it included.
</p>
<span className="mt-3 inline-flex items-center gap-1 text-sm font-semibold text-primary group-hover:underline">
Changelog <ChevronRight className="h-4 w-4" />
Releases <ChevronRight className="h-4 w-4" />
</span>
</Link>
</a>
</div>
</section>
</SeoSubpageShell>
+15 -5
View File
@@ -8,7 +8,11 @@ import {
Monitor,
} from "lucide-react";
import { SeoSubpageShell } from "@/components/seo-subpage-shell";
import { absoluteUrl, buildPageMetadata } from "@/lib/seo";
import {
absoluteUrl,
buildPageMetadata,
SOURCE_REPO_URL,
} from "@/lib/seo";
const PAGE_PATH = "/dictation-pad";
@@ -110,8 +114,8 @@ export default function DictationPadPage() {
dateModified: "2026-11-01",
author: {
"@type": "Person",
name: "Jatin K Malik",
url: "https://github.com/jatinkrmalik",
name: "Xavier Karma",
url: "https://bongbetic.com",
},
publisher: {
"@type": "Organization",
@@ -247,8 +251,14 @@ export default function DictationPadPage() {
<p className="mt-4 text-sm text-muted-foreground">
In v0.18.4 the switch moved to Settings → Dictation, next to the Open
Dictation Pad button. On older 0.18 releases, update first; the{" "}
<Link href="/changelog/">changelog</Link> lists what changed in each
patch.
<a
href={`${SOURCE_REPO_URL}/releases`}
rel="noopener noreferrer"
className="font-semibold text-primary hover:underline"
>
release notes
</a>{" "}
list what changed in each patch.
</p>
</section>
+3 -3
View File
@@ -22,11 +22,11 @@ const faqCategories = [
questions: [
{
q: "How do I install Voculinux?",
a: "Run the one-liner installer: curl -fsSL https://raw.githubusercontent.com/VocaHQ/vocalinux/main/install.sh | bash. Fork status: this fetches the upstream install script, so it installs Vocalinux (upstream), not Voculinux; fork release artefacts are not published yet. The installer handles dependencies and configuration. You can also download an AppImage from upstream GitHub Releases if you prefer a no-root portable binary (host xdotool/wtype/ydotool still required for text injection), or grab a .deb, .rpm, Snap, or Flatpak package for your distro.",
a: "Voculinux is published for Void Linux (glibc, x86_64) as a signed xbps repository. Trust the repository key, register the repository, then run xbps-install -S voculinux as root; the install guide has the exact commands. This is a third-party repository published by this project, not by Void Linux upstream. Other channels are not published for Voculinux (Void only).",
},
{
q: "Which Linux distributions are supported?",
a: "Voculinux works on Ubuntu 24.04+, Debian 12+, Fedora 39+, Arch Linux, Manjaro, and most other Linux distributions. See our install guides for distro-specific instructions.",
a: "The published package is for Void Linux (glibc, x86_64) only. Voculinux runs on X11 and Wayland desktops. See the Void install guide for the setup steps.",
},
{
q: "What are the system requirements?",
@@ -45,7 +45,7 @@ const faqCategories = [
},
{
q: "Does Voculinux send any data to external servers?",
a: "Local engines do not send voice data externally. Remote API mode sends audio only to the transcription server URL you configure. The app also uses the network for model downloads and for its update check against GitHub Releases; neither carries your audio or transcripts.",
a: "Local engines do not send voice data externally. Remote API mode sends audio only to the transcription server URL you configure. The app also uses the network for model downloads and for its update check against the Voculinux release host; neither carries your audio or transcripts.",
},
{
q: "Is my voice data stored anywhere?",
+19 -50
View File
@@ -12,15 +12,10 @@ import {
import { SeoSubpageShell } from "@/components/seo-subpage-shell";
import { absoluteUrl, buildPageMetadata } from "@/lib/seo";
const installCommands = [
"curl -fsSL https://raw.githubusercontent.com/VocaHQ/vocalinux/main/install.sh -o /tmp/vl.sh",
"bash /tmp/vl.sh --auto --engine=faster_whisper",
];
const setupSteps = [
{
title: "Install the Faster Whisper engine",
text: "Faster Whisper is an optional extra. Run the installer and choose Faster-Whisper in the engine menu, or pass --engine=faster_whisper. If the faster-whisper package fails to install, the installer falls back to whisper.cpp and says so.",
text: "Faster Whisper is an optional extra and is not in the Void package. Make faster-whisper importable from your own Python environment, as the Void install guide describes. Voculinux lists Faster Whisper in Settings once its libraries can be imported.",
},
{
title: "Open Settings → Speech Model",
@@ -75,42 +70,25 @@ const sizes = [
const packages = [
{
name: "install.sh / source install",
name: "Void package (voculinux)",
included: false,
note: "Ships whisper.cpp only; add faster-whisper from your own Python environment",
},
{
name: "Source checkout (install.sh)",
included: true,
note: "Menu option or --engine=faster_whisper",
},
{
name: "AUR (voculinux)",
included: true,
note: "Listed as an optional dependency: python-faster-whisper",
},
{
name: "AppImage (and AUR voculinux-bin)",
included: false,
note: "Ships whisper.cpp only",
},
{ name: "Flatpak", included: false, note: "Ships whisper.cpp only" },
{ name: "Snap", included: false, note: "Ships whisper.cpp only" },
{
name: ".deb / .rpm",
included: false,
note: "Thin packages; whisper.cpp on CPU",
},
{
name: "Nix flake",
included: false,
note: "faster_whisper extra is not packaged",
},
];
const choices = [
{
engine: "whisper.cpp",
pick: "The default. Ships in every package, uses a Vulkan GPU on AMD, Intel and NVIDIA, and offers quantized and English-only variants. Start here.",
pick: "The default. Ships in the Void package, uses a Vulkan GPU on AMD, Intel and NVIDIA, and offers quantized and English-only variants. Start here.",
},
{
engine: "Faster Whisper",
pick: "You installed with install.sh, from source or from the AUR and want a Whisper model on a CPU-only machine, running through CTranslate2 instead of whisper.cpp.",
pick: "You have faster-whisper available in your Python environment and want a Whisper model on a CPU-only machine, running through CTranslate2 instead of whisper.cpp.",
},
{
engine: "Whisper (OpenAI)",
@@ -141,12 +119,12 @@ const faqs = [
{
question: "Does audio leave my machine?",
answer:
"No. Faster Whisper is a local engine. After the model download, recognition runs on your machine. Only the Remote API engine and VocaGateway send audio to an endpoint you configure.",
"No. Faster Whisper is a local engine. After the model download, recognition runs on your machine. Only the Remote API engine sends audio, and only to an endpoint you configure.",
},
{
question: "Why is Faster Whisper not in my Engine list?",
answer:
"Settings lists only engines whose libraries are installed. The AppImage, Flatpak, Snap, .deb and .rpm packages do not include faster-whisper. Re-run the installer with --engine=faster_whisper, or on Arch install python-faster-whisper.",
"Settings lists only engines whose libraries are installed. The Void package does not include faster-whisper. Install faster-whisper into your own Python environment, and Settings lists it once the libraries import.",
},
];
@@ -177,8 +155,8 @@ export default function FasterWhisperPage() {
dateModified: "2026-11-01",
author: {
"@type": "Person",
name: "Jatin K Malik",
url: "https://github.com/jatinkrmalik",
name: "Xavier Karma",
url: "https://bongbetic.com",
},
publisher: {
"@type": "Organization",
@@ -298,20 +276,10 @@ export default function FasterWhisperPage() {
<h2 className="mb-6 font-display text-2xl font-semibold">
Install and select Faster Whisper
</h2>
<div className="mb-6 rounded-lg bg-[color:var(--terminal)] p-4">
{installCommands.map((command) => (
<code
key={command}
className="block text-sm text-[color:var(--terminal-fg)]"
>
{command}
</code>
))}
</div>
<p className="mb-4 text-sm text-muted-foreground">
Fork status: these commands fetch the upstream install script, so they
install Vocalinux (upstream), not Voculinux. Fork release artefacts
are not published yet.
Faster Whisper is not in the Void package. Add faster-whisper from your
own Python environment, then select it. The Void install guide lists
the optional engines and where they come from.
</p>
<ol className="space-y-4">
{setupSteps.map((step, index) => (
@@ -600,8 +568,9 @@ export default function FasterWhisperPage() {
Try Faster Whisper
</h2>
<p className="mb-6 text-muted-foreground">
Install Voculinux with the Faster Whisper engine, or keep whisper.cpp
and switch later from Settings → Speech Model.
Install Voculinux from the Void repository and keep whisper.cpp, or
add faster-whisper to your Python environment and switch later from
Settings → Speech Model.
</p>
<div className="flex flex-wrap gap-4">
<Link
+8 -482
View File
@@ -1,488 +1,14 @@
import Link from "next/link";
import { type Metadata } from "next";
import {
AlertTriangle,
CheckCircle2,
ChevronRight,
CircleHelp,
Download,
Keyboard,
RefreshCw,
ShieldCheck,
XCircle,
} from "lucide-react";
import { SeoSubpageShell } from "@/components/seo-subpage-shell";
import { absoluteUrl, buildPageMetadata } from "@/lib/seo";
const PAGE_PATH = "/flatpak";
const PAGE_TITLE = "Install Voculinux via Flatpak (VocaHQ Remote) | Voculinux";
const PAGE_DESCRIPTION =
"Install Voculinux from the signed VocaHQ Flatpak remote (not Flathub): exact commands, updates, sandbox and Wayland notes, what is included, and uninstall.";
const FLATPAKREF_COMMAND =
"flatpak install https://vocahq.github.io/voculinux-flatpak/com.voculinux.Voculinux.flatpakref";
const MANUAL_COMMANDS = `flatpak remote-add --if-not-exists flathub https://dl.flathub.org/repo/flathub.flatpakrepo
flatpak remote-add --if-not-exists vocahq https://vocahq.github.io/voculinux-flatpak/vocahq.flatpakrepo
flatpak install vocahq com.voculinux.Voculinux`;
const DBUS_TOGGLE_COMMAND =
"gdbus call --session --dest com.voculinux.Voculinux --object-path /com/voculinux/Voculinux --method com.voculinux.Voculinux.Toggle";
const OVERRIDE_COMMAND =
"flatpak override --user --nodevice=all --device=dri com.voculinux.Voculinux";
const RUN_COMMAND = "flatpak run com.voculinux.Voculinux";
const installSteps = [
{
name: "Install from the VocaHQ remote",
text: `Run ${FLATPAKREF_COMMAND}. The .flatpakref adds the vocahq remote and resolves the GNOME runtime (org.gnome.Platform//50) through Flathub, so no separate Flathub setup is needed.`,
},
{
name: "Or add the remotes by hand",
text: "If you prefer explicit remotes, add flathub, add vocahq, then install com.voculinux.Voculinux from vocahq with the three commands below.",
},
{
name: "Start Voculinux",
text: `Launch Voculinux from your app menu or run ${RUN_COMMAND}.`,
},
{
name: "Download a speech model",
text: "Pick a model in Settings → Speech Model. Since v0.18.4 Voculinux shows the download size and asks before downloading anything.",
},
];
const faqs = [
{
question: "Is Voculinux on Flathub?",
answer:
"No. The Flathub submission was closed on policy grounds in July 2026 and the project is not re-submitting. The app comes from the self-hosted vocahq remote; only the GNOME runtime is pulled from Flathub.",
},
{
question: "Does the Flatpak update itself?",
answer:
"Flatpak updates it, not the app. Run flatpak update (or let your software center do it) and each stable release arrives as an incremental delta. Prerelease tags never reach the remote, and GitHub Release .flatpak bundles do not auto-update.",
},
{
question: "Can I use Parakeet or Faster Whisper in the Flatpak?",
answer:
"No. The Flatpak ships whisper.cpp only. Faster Whisper, Parakeet, OpenAI Whisper and VOSK are available through the install script or a source install.",
},
{
question: "My push-to-talk key does nothing on Wayland. Why?",
answer:
"On Wayland the built-in hotkey reads /dev/input. The manifest grants --device=all by default; if you removed device access with flatpak override, grant --device=input (Flatpak 1.15.6 or newer) or --device=all again, or enable External Activation and bind a desktop shortcut to the D-Bus Toggle command. Desktop shortcuts only toggle.",
},
{
question: "Can I start a local VocaGateway from the Flatpak?",
answer:
"No. The sandbox cannot reach the host podman or docker socket. Start VocaGateway on the host yourself and point Settings → Remote Server at it, or use the installer or AppImage. Either way, audio leaves Voculinux for the endpoint you configured, and leaves the machine if that endpoint is elsewhere. VocaGateway is not on-device, so keep it on localhost, a trusted LAN, an encrypted private network such as Tailscale, or HTTPS.",
},
];
import { ChannelNotice } from "@/components/channel-notice";
import { buildPageMetadata } from "@/lib/seo";
export const metadata: Metadata = buildPageMetadata({
title: PAGE_TITLE,
description: PAGE_DESCRIPTION,
path: PAGE_PATH,
keywords: [
"voculinux flatpak",
"linux voice dictation flatpak",
"flatpak speech to text",
"vocahq flatpak remote",
"com.voculinux.Voculinux",
"flatpak dictation wayland",
],
title: "Flatpak: Not Published for Voculinux",
description:
"Voculinux has no Flatpak package. It is published for Void Linux (glibc, x86_64) as a signed xbps repository.",
path: "/flatpak",
});
function Commands({ children }: { children: string }) {
return (
<div className="subpage-terminal mt-3 p-4">
<pre>
<code>{children}</code>
</pre>
</div>
);
}
export default function FlatpakPage() {
const articleJsonLd = {
"@context": "https://schema.org",
"@type": "TechArticle",
headline: "Install Voculinux with Flatpak from the VocaHQ remote",
description: PAGE_DESCRIPTION,
dateModified: "2026-11-01",
author: {
"@type": "Person",
name: "Jatin K Malik",
url: "https://github.com/jatinkrmalik",
},
publisher: {
"@type": "Organization",
name: "Voculinux",
logo: {
"@type": "ImageObject",
url: absoluteUrl("/voculinux.png"),
},
},
mainEntityOfPage: absoluteUrl(PAGE_PATH),
};
const howToJsonLd = {
"@context": "https://schema.org",
"@type": "HowTo",
name: "Install Voculinux from the VocaHQ Flatpak remote",
description: PAGE_DESCRIPTION,
tool: [{ "@type": "HowToTool", name: "Flatpak" }],
step: installSteps.map((step) => ({
"@type": "HowToStep",
name: step.name,
text: step.text,
url: absoluteUrl(PAGE_PATH),
})),
};
const faqJsonLd = {
"@context": "https://schema.org",
"@type": "FAQPage",
mainEntity: faqs.map((faq) => ({
"@type": "Question",
name: faq.question,
acceptedAnswer: { "@type": "Answer", text: faq.answer },
})),
};
return (
<SeoSubpageShell>
<script
type="application/ld+json"
dangerouslySetInnerHTML={{ __html: JSON.stringify(articleJsonLd) }}
/>
<script
type="application/ld+json"
dangerouslySetInnerHTML={{ __html: JSON.stringify(howToJsonLd) }}
/>
<script
type="application/ld+json"
dangerouslySetInnerHTML={{ __html: JSON.stringify(faqJsonLd) }}
/>
<section>
<p className="subpage-kicker">Flatpak remote added in v0.18.0</p>
<h1 className="mb-5 font-display text-4xl font-semibold tracking-tight sm:text-5xl">
Install Voculinux with Flatpak
</h1>
<p className="mb-8 max-w-4xl text-lg text-muted-foreground">
Voculinux publishes a signed, self-hosted Flatpak remote called{" "}
<code>vocahq</code> on every stable release. It is{" "}
<strong>not on Flathub</strong>: the app comes from VocaHQ, and only
the GNOME runtime comes from Flathub. Install once, and{" "}
<code>flatpak update</code> picks up each new release.
</p>
</section>
<section className="mb-12 rounded-[12px] border border-border bg-background p-6">
<h2 className="mb-4 font-display text-2xl font-semibold">
Who the Flatpak is for
</h2>
<p className="mb-3 text-muted-foreground">
Pick the Flatpak if your distribution already uses Flatpak, if you run
an immutable or non-standard layout, or if you want updates handled by
the same tool as your other desktop apps. It runs on x86_64 and
aarch64, and it ships the default whisper.cpp engine with Vulkan GPU
support.
</p>
<p className="text-muted-foreground">
Pick the <Link href="/install/">install script</Link> instead if you
want Faster Whisper, Parakeet, OpenAI Whisper or VOSK, or the local
VocaGateway launcher. Those are not in the Flatpak.
</p>
</section>
<section className="mb-12">
<h2 className="mb-6 inline-flex items-center gap-2 font-display text-2xl font-semibold">
<Download className="h-5 w-5 text-primary" aria-hidden="true" />
Install from the VocaHQ remote
</h2>
<p className="mb-6 text-sm text-muted-foreground">
Fork status: the fork Flatpak remote below is not published yet —
these commands resolve once the first fork release lands. The install
script installs upstream Vocalinux today.
</p>
<ol className="space-y-6">
<li className="rounded-[12px] border border-border bg-background p-5">
<h3 className="font-semibold">1. {installSteps[0].name}</h3>
<p className="mt-2 text-sm text-muted-foreground">
One command. The <code>.flatpakref</code> adds the{" "}
<code>vocahq</code> remote and resolves the GNOME runtime (
<code>org.gnome.Platform//50</code>) through Flathub, so no
separate Flathub setup is needed.
</p>
<Commands>{FLATPAKREF_COMMAND}</Commands>
</li>
<li className="rounded-[12px] border border-border bg-background p-5">
<h3 className="font-semibold">2. {installSteps[1].name}</h3>
<p className="mt-2 text-sm text-muted-foreground">
If you prefer explicit remotes, add flathub, add vocahq, then
install <code>com.voculinux.Voculinux</code> from vocahq:
</p>
<Commands>{MANUAL_COMMANDS}</Commands>
</li>
<li className="rounded-[12px] border border-border bg-background p-5">
<h3 className="font-semibold">3. {installSteps[2].name}</h3>
<p className="mt-2 text-sm text-muted-foreground">
Launch Voculinux from your app menu, or run:
</p>
<Commands>{RUN_COMMAND}</Commands>
</li>
<li className="rounded-[12px] border border-border bg-background p-5">
<h3 className="font-semibold">4. {installSteps[3].name}</h3>
<p className="mt-2 text-sm text-muted-foreground">
Pick a model in Settings → Speech Model. Since v0.18.4 Voculinux
shows the download size and asks before downloading anything.
After that one download, the whisper.cpp engine transcribes on
your machine. Then hold Right Alt and speak.
</p>
</li>
</ol>
</section>
<section className="mb-12 rounded-[12px] border border-border bg-background p-6">
<h2 className="mb-4 inline-flex items-center gap-2 font-display text-2xl font-semibold">
<RefreshCw className="h-5 w-5 text-primary" aria-hidden="true" />
Updates
</h2>
<p className="mb-3 text-muted-foreground">
Every stable <code>v*</code> tag is imported into the remote, signed
with the VocaHQ GPG key and published with static deltas, so an update
downloads only what changed:
</p>
<Commands>flatpak update</Commands>
<ul className="mt-4 space-y-2 text-sm text-muted-foreground">
<li>
Alpha, beta and rc tags never reach the remote. The only Flatpak for
a prerelease is the <code>.flatpak</code> bundle on GitHub Releases.
</li>
<li>
Release bundles (
<code>Voculinux-&lt;version&gt;-x86_64.flatpak</code> or{" "}
<code>-aarch64.flatpak</code>) do not auto-update. Switch to the
remote if you installed one.
</li>
<li>
The in-app update checker only tells you a release exists; it never
installs anything.
</li>
</ul>
<h3 className="mb-2 mt-6 font-semibold">
If an update fails signature verification
</h3>
<p className="text-sm text-muted-foreground">
After a signing-key rotation, existing installs must import the new
public key. Re-running <code>remote-add</code> does not replace it.
The documented fix:
</p>
<Commands>{`curl -fsSL https://vocahq.github.io/voculinux-flatpak/vocahq.gpg -o /tmp/vocahq.gpg
flatpak remote-modify --gpg-import=/tmp/vocahq.gpg vocahq
flatpak update`}</Commands>
</section>
<section className="mb-12 rounded-[12px] border border-border bg-background p-6">
<h2 className="mb-4 font-display text-2xl font-semibold">
What the Flatpak includes and leaves out
</h2>
<div className="overflow-x-auto">
<table className="w-full text-left text-sm">
<thead>
<tr className="border-b border-border">
<th className="pb-3 pr-4 font-semibold">Feature</th>
<th className="pb-3 pr-4 font-semibold">In the Flatpak?</th>
<th className="pb-3 font-semibold">Notes</th>
</tr>
</thead>
<tbody>
<tr className="border-b border-border">
<td className="py-3 pr-4 font-medium">whisper.cpp engine</td>
<td className="py-3 pr-4">Yes</td>
<td className="py-3 text-muted-foreground">
Built with Vulkan; the GPU is reached through{" "}
<code>--device=dri</code>
</td>
</tr>
<tr className="border-b border-border">
<td className="py-3 pr-4 font-medium">Silero neural VAD</td>
<td className="py-3 pr-4">Yes</td>
<td className="py-3 text-muted-foreground">
onnxruntime bundled since v0.18.4
</td>
</tr>
<tr className="border-b border-border">
<td className="py-3 pr-4 font-medium">Remote API engine</td>
<td className="py-3 pr-4">Yes</td>
<td className="py-3 text-muted-foreground">
Audio leaves the machine for the endpoint you configure
</td>
</tr>
<tr className="border-b border-border">
<td className="py-3 pr-4 font-medium">
Faster Whisper, Parakeet, OpenAI Whisper, VOSK
</td>
<td className="py-3 pr-4">No</td>
<td className="py-3 text-muted-foreground">
Use the install script or a source install
</td>
</tr>
<tr className="border-b border-border">
<td className="py-3 pr-4 font-medium">
Local VocaGateway launcher
</td>
<td className="py-3 pr-4">No</td>
<td className="py-3 text-muted-foreground">
The sandbox cannot reach the host podman or docker socket
</td>
</tr>
</tbody>
</table>
</div>
<p className="mt-4 text-sm text-muted-foreground">
Config and models live under{" "}
<code>~/.var/app/com.voculinux.Voculinux/</code>, not{" "}
<code>~/.config/voculinux</code>.
</p>
</section>
<section className="mb-12 rounded-[12px] border border-border bg-background p-6">
<h2 className="mb-4 inline-flex items-center gap-2 font-display text-2xl font-semibold">
<Keyboard className="h-5 w-5 text-primary" aria-hidden="true" />
Sandbox permissions and Wayland
</h2>
<p className="mb-3 text-muted-foreground">
The manifest asks for the microphone (<code>--socket=pulseaudio</code>
), the GPU (<code>--device=dri</code>), network for model downloads,
the Wayland and X11 sockets, IBus and the tray watcher on D-Bus, and{" "}
<code>--device=all</code> for keyboard devices and{" "}
<code>/dev/uinput</code>. Flatpak has no narrower flag for uinput.
</p>
<h3 className="mb-2 mt-6 font-semibold">Hotkeys</h3>
<ul className="space-y-2 text-sm text-muted-foreground">
<li>
Native X11: since v0.18.4 the Flatpak uses the X11 listener, so the
hotkey needs no device access. GNOME on X11 is the exception and
still needs <code>/dev/input</code>.
</li>
<li>
Wayland: the built-in hotkey reads <code>/dev/input</code>. If you
tighten permissions, grant <code>--device=input</code> on Flatpak
1.15.6 or newer, or <code>--device=all</code> on older versions.
</li>
<li>
No device access at all: enable Settings → Shortcuts → External
Activation (Desktop Shortcut) and bind a desktop shortcut to the
D-Bus Toggle command below. Desktop shortcuts toggle dictation; they
cannot do push-to-talk.
</li>
</ul>
<Commands>{DBUS_TOGGLE_COMMAND}</Commands>
<p className="mt-3 text-sm text-muted-foreground">
To drop device access but keep the GPU for whisper.cpp:
</p>
<Commands>{OVERRIDE_COMMAND}</Commands>
<h3 className="mb-2 mt-6 font-semibold">Typing text</h3>
<p className="text-sm text-muted-foreground">
Text injection prefers the RemoteDesktop portal, which asks for
permission once and handles non-US layouts. When the portal is
unavailable it falls back to <code>wl-copy</code> plus ydotool paste,
then xdotool for X11 and XWayland clients. Without{" "}
<code>/dev/uinput</code> the ydotool fallback is gone, but the portal
and xdotool remain. On KDE Plasma Wayland, set System Settings →
Keyboard → Virtual Keyboard to &ldquo;IBus Wayland&rdquo;. On GNOME,
the tray icon needs the AppIndicator extension. For more, see{" "}
<Link href="/wayland/">Wayland voice typing</Link> and{" "}
<Link href="/global-shortcuts/">global shortcuts via D-Bus</Link>.
</p>
</section>
<section className="mb-12 rounded-[12px] border border-border bg-background p-6">
<h2 className="mb-4 inline-flex items-center gap-2 font-display text-2xl font-semibold">
<XCircle className="h-5 w-5 text-primary" aria-hidden="true" />
Uninstall
</h2>
<p className="mb-3 text-muted-foreground">
The <code>uninstall.sh</code> script is for script and source
installs. For the Flatpak, use Flatpak itself. Remove the app:
</p>
<Commands>flatpak uninstall com.voculinux.Voculinux</Commands>
<p className="mt-3 text-sm text-muted-foreground">
Add <code>--delete-data</code> to also remove settings and downloaded
models under <code>~/.var/app/com.voculinux.Voculinux/</code>. If no
other app uses the remote, drop it too:
</p>
<Commands>flatpak remote-delete vocahq</Commands>
</section>
<section className="mb-12">
<h2 className="mb-6 inline-flex items-center gap-2 font-display text-2xl font-semibold">
<CircleHelp className="h-5 w-5 text-primary" aria-hidden="true" />
Flatpak FAQ
</h2>
<div className="space-y-4">
{faqs.map((faq) => (
<div
key={faq.question}
className="rounded-[12px] border border-border bg-background p-5"
>
<h3 className="mb-2 font-semibold">{faq.question}</h3>
<p className="text-sm text-muted-foreground">{faq.answer}</p>
</div>
))}
</div>
<p className="mt-6 inline-flex items-start gap-2 text-sm text-muted-foreground">
<AlertTriangle
className="mt-0.5 h-4 w-4 flex-shrink-0 text-primary"
aria-hidden="true"
/>
<span>
More fixes for hotkeys, audio and text injection are on the{" "}
<Link href="/troubleshooting/">troubleshooting page</Link>. To keep
the whole stack local, see the{" "}
<Link href="/gpu-acceleration/">GPU acceleration guide</Link> and{" "}
<Link href="/voice-activity-detection/">Silero VAD</Link>.
</span>
</p>
</section>
<section className="rounded-[12px] border border-border bg-muted p-8">
<h2 className="mb-4 inline-flex items-center gap-2 font-display text-2xl font-semibold">
<ShieldCheck className="h-5 w-5 text-primary" aria-hidden="true" />
Need a different package?
</h2>
<p className="mb-6 text-muted-foreground">
The install script covers every engine, the Nix flake fits NixOS, and
the .deb and .rpm suit Debian, Ubuntu and Fedora. The AUR packages for
Arch are not published yet.
</p>
<div className="flex flex-wrap gap-4">
<Link
href="/install/"
className="inline-flex items-center gap-2 rounded-full bg-primary px-4 py-2 text-sm font-semibold text-primary-foreground"
>
All install options
<ChevronRight className="h-4 w-4" aria-hidden="true" />
</Link>
<Link
href="/install/nixos/"
className="inline-flex items-center gap-2 rounded-lg border border-border bg-background px-4 py-2 text-sm font-semibold hover:bg-muted"
>
Nix flake and NixOS module
</Link>
<Link
href="/local-gateway/"
className="inline-flex items-center gap-2 rounded-lg border border-border bg-background px-4 py-2 text-sm font-semibold hover:bg-muted"
>
Local VocaGateway
</Link>
</div>
</section>
</SeoSubpageShell>
);
export default function FlatpakNoticePage() {
return <ChannelNotice title="Flatpak" />;
}
+2 -2
View File
@@ -138,8 +138,8 @@ export default function ForDevelopersPage() {
dateModified: "2026-02-22",
author: {
"@type": "Person",
name: "Jatin K Malik",
url: "https://github.com/jatinkrmalik",
name: "Xavier Karma",
url: "https://bongbetic.com",
},
publisher: {
"@type": "Organization",
+5 -18
View File
@@ -53,7 +53,7 @@ const steps = [
},
{
name: "Pick the command",
text: "On a native install, use voculinux --toggle. On Flatpak, use the gdbus call shown below, because voculinux --toggle is not on the host PATH.",
text: "Use voculinux --toggle, or the gdbus call shown below if you prefer calling D-Bus directly.",
},
{
name: "Bind it in your desktop",
@@ -138,8 +138,8 @@ export default function GlobalShortcutsPage() {
dateModified: "2026-11-01",
author: {
"@type": "Person",
name: "Jatin K Malik",
url: "https://github.com/jatinkrmalik",
name: "Xavier Karma",
url: "https://bongbetic.com",
},
publisher: {
"@type": "Organization",
@@ -279,9 +279,8 @@ export default function GlobalShortcutsPage() {
</div>
<h3 className="mb-2 font-semibold">Calling D-Bus directly</h3>
<p className="mb-3 text-sm text-muted-foreground">
The <InlineCode>gdbus</InlineCode> form is what the Flatpak
documentation recommends: it is immediate and does not start another
sandbox on every press. Swap <InlineCode>Toggle</InlineCode> for{" "}
The <InlineCode>gdbus</InlineCode> form talks to the running instance
directly. Swap <InlineCode>Toggle</InlineCode> for{" "}
<InlineCode>Start</InlineCode> or <InlineCode>Stop</InlineCode> as
needed.
</p>
@@ -378,14 +377,6 @@ export default function GlobalShortcutsPage() {
While a transcription is still processing, Toggle cancels it, the
same as pressing the recording hotkey again (since v0.18.4).
</li>
<li>
On Flatpak,{" "}
<InlineCode>
flatpak run com.voculinux.Voculinux --toggle
</InlineCode>{" "}
also works but starts a sandbox on each press; prefer{" "}
<InlineCode>gdbus</InlineCode>.
</li>
<li>
This changes how dictation starts, not how text is typed. If text
does not appear, see{" "}
@@ -418,10 +409,6 @@ export default function GlobalShortcutsPage() {
,{" "}
<Link href="/gnome-kde/" className="text-primary underline">
GNOME and KDE
</Link>
,{" "}
<Link href="/flatpak/" className="text-primary underline">
Flatpak
</Link>{" "}
and{" "}
<Link href="/troubleshooting/" className="text-primary underline">
+2 -2
View File
@@ -68,8 +68,8 @@ export default function GnomeVsKdePage() {
dateModified: "2026-03-30",
author: {
"@type": "Person",
name: "Jatin K Malik",
url: "https://github.com/jatinkrmalik",
name: "Xavier Karma",
url: "https://bongbetic.com",
},
publisher: {
"@type": "Organization",
+2 -2
View File
@@ -156,8 +156,8 @@ export default function GpuAccelerationPage() {
dateModified: "2026-02-22",
author: {
"@type": "Person",
name: "Jatin K Malik",
url: "https://github.com/jatinkrmalik",
name: "Xavier Karma",
url: "https://bongbetic.com",
},
publisher: {
"@type": "Organization",
+4 -4
View File
@@ -152,8 +152,8 @@ export default function HyprlandSwayPage() {
dateModified: "2026-11-01",
author: {
"@type": "Person",
name: "Jatin K Malik",
url: "https://github.com/jatinkrmalik",
name: "Xavier Karma",
url: "https://bongbetic.com",
},
publisher: {
"@type": "Organization",
@@ -362,7 +362,7 @@ export default function HyprlandSwayPage() {
On Wayland the built-in hotkey reads{" "}
<InlineCode>/dev/input</InlineCode>, so your user must be in the{" "}
<InlineCode>input</InlineCode> group (the source installer does this;
.deb and .rpm packages cannot). The alternative, added in v0.18.0, is
the Void package leaves it to you). The alternative, added in v0.18.0, is
to turn on Settings → Shortcuts → External Activation (Desktop
Shortcut) and let the compositor own the key. These binds toggle
dictation; they cannot do push-to-talk.
@@ -384,7 +384,7 @@ export default function HyprlandSwayPage() {
displayCommand="sudo usermod -aG input $USER"
/>
<p className="mt-4 text-sm text-muted-foreground">
The full D-Bus interface and Flatpak command are in{" "}
The full D-Bus interface is in{" "}
<Link href="/global-shortcuts/" className="text-primary underline">
global shortcuts
</Link>
+25 -637
View File
@@ -1,415 +1,30 @@
import Link from "next/link";
import { type Metadata } from "next";
import { notFound } from "next/navigation";
import {
CheckCircle2,
ChevronRight,
Cpu,
Download,
Laptop,
Monitor,
RefreshCw,
Server,
Shield,
Sparkles,
Terminal,
} from "lucide-react";
import { SeoSubpageShell } from "@/components/seo-subpage-shell";
import { absoluteUrl, buildPageMetadata } from "@/lib/seo";
import { ChannelNotice } from "@/components/channel-notice";
import { buildPageMetadata } from "@/lib/seo";
// Distro guides are not published for Voculinux. Each slug keeps its route as a
// short notice that points at the Void install hub.
const DISTRO_NAMES: Record<string, string> = {
ubuntu: "Ubuntu",
debian: "Debian",
fedora: "Fedora",
arch: "Arch",
opensuse: "openSUSE",
"linux-mint": "Linux Mint",
"pop-os": "Pop!_OS",
};
type DistroSlug =
| "ubuntu"
| "debian"
| "fedora"
| "arch"
| "opensuse"
| "linux-mint"
| "pop-os";
type DistroParams = { distro: string } | Promise<{ distro: string }>;
interface InstallOption {
label: string;
note: string;
command: string;
}
interface DistroConfig {
name: string;
title: string;
description: string;
intro: string;
prerequisites: string[];
supportedOn: string[];
installCommand: string;
otherInstallOptions: InstallOption[];
setupNotes: string[];
postInstallChecks: string[];
}
const INSTALL_SCRIPT_COMMAND = `curl -fsSL https://raw.githubusercontent.com/VocaHQ/vocalinux/main/install.sh -o /tmp/vl.sh
bash /tmp/vl.sh`;
const DEB_OPTION: InstallOption = {
label: "Native .deb package",
note: "Fork status: the fork .deb is not published yet. Download the .deb for your CPU from upstream GitHub Releases in the meantime (arm64 files are attached too). It is a thin package: Python and GTK come from the distro, whisper.cpp runs on the CPU, and there is no auto-update. On Wayland, add yourself to the input group (sudo usermod -aG input $USER, then log out and back in) or the default hotkey stays off.",
command: "sudo apt install ./voculinux_*_amd64.deb",
};
const RPM_OPTION: InstallOption = {
label: "Native .rpm package",
note: "Fork status: the fork .rpm is not published yet. Download the .rpm for your CPU from upstream GitHub Releases in the meantime (aarch64 files are attached too). It is a thin package: Python and GTK come from Fedora, whisper.cpp runs on the CPU, and there is no auto-update or COPR yet. On Wayland, add yourself to the input group (sudo usermod -aG input $USER, then log out and back in) or the default hotkey stays off.",
command: "sudo dnf install ./voculinux-*.x86_64.rpm",
};
const FLATPAK_OPTION: InstallOption = {
label: "Flatpak from the VocaHQ remote",
note: "Fork status: the fork Flatpak remote is not published yet. Not on Flathub. The self-hosted vocahq remote updates with flatpak update and ships whisper.cpp only once published.",
command:
"flatpak install https://vocahq.github.io/voculinux-flatpak/com.voculinux.Voculinux.flatpakref",
};
const COMMON_CHECKS = [
"Run voculinux --version to confirm the installed release.",
"Launch voculinux and confirm the tray icon appears. On GNOME, the tray needs the AppIndicator extension.",
"Hold Right Alt (Option) to dictate, or switch to toggle mode in Settings → Shortcuts, and confirm text lands in a terminal and a browser.",
"If anything misbehaves, run voculinux --debug and check the troubleshooting guide.",
];
const distroConfig: Record<DistroSlug, DistroConfig> = {
ubuntu: {
name: "Ubuntu",
title: "Install Voice Dictation on Ubuntu",
description:
"Step-by-step Ubuntu voice dictation setup for offline speech-to-text with whisper.cpp. Covers Ubuntu 24.04+, the install script, the .deb, X11 and Wayland.",
intro:
"This Ubuntu guide is tuned for practical desktop use: terminals, browsers, IDEs, office suites, and chat apps. Local engines keep audio on your machine after the one-time model download. Remote API is optional.",
prerequisites: [
"Ubuntu 24.04 or newer (Ubuntu 22.04 ships Python 3.10, below the 3.11 floor)",
"Python 3.11+ with the distro's python3-gi (the installer handles this)",
"Working microphone",
"curl installed (sudo apt update && sudo apt install -y curl)",
],
supportedOn: [
"Ubuntu 24.04 LTS and newer: full support",
"Ubuntu-based derivatives on a 24.04 base, such as Linux Mint 22, Pop!_OS 24.04, elementary OS 8 and Zorin OS on 24.04",
],
installCommand: INSTALL_SCRIPT_COMMAND,
otherInstallOptions: [DEB_OPTION, FLATPAK_OPTION],
setupNotes: [
"Keep the default whisper.cpp engine: it installs in about 1-2 minutes and uses a Vulkan GPU when one is available.",
"On lower-RAM systems, VOSK is the lightweight option (--engine=vosk with the install script).",
"On KDE Plasma Wayland (Kubuntu), set System Settings → Keyboard → Virtual Keyboard to IBus Wayland.",
"The install script adds you to the input group for Wayland hotkeys; log out and back in afterwards.",
],
postInstallChecks: [
...COMMON_CHECKS,
"Set language and model size in Settings → Speech Model for your workload.",
],
},
debian: {
name: "Debian",
title: "Install Voice Dictation on Debian",
description:
"Install Voculinux offline voice dictation on Debian 12, 13 and Testing with the install script or the native .deb. Covers AppIndicator, ydotool and Wayland.",
intro:
"Debian 12 and newer are a primary target for Voculinux. The install script detects Debian, installs the system packages it needs, and sets up whisper.cpp with a checksum-verified tiny model.",
prerequisites: [
"Debian 12 or newer, including Testing (Debian 11 and earlier are below the Python 3.11 floor)",
"Python 3.11+ with the distro's python3-gi (the installer handles this)",
"Working microphone",
"curl installed (sudo apt update && sudo apt install -y curl)",
],
supportedOn: [
"Debian 12 and newer: full support",
"Debian Testing: full support",
],
installCommand: INSTALL_SCRIPT_COMMAND,
otherInstallOptions: [DEB_OPTION, FLATPAK_OPTION],
setupNotes: [
"Debian uses the Ayatana AppIndicator for the tray (gir1.2-ayatanaappindicator3-0.1); the installer picks it.",
"Clean Debian installs lack some build tools for compiling pywhispercpp (libssl-dev, autoconf, automake, libtool, patchelf). The installer adds them; add them yourself before a manual rebuild.",
"ydotool is not in Debian's standard repositories. Voculinux falls back to IBus or wtype for most Wayland compositors.",
"On KDE Plasma Wayland, set System Settings → Keyboard → Virtual Keyboard to IBus Wayland.",
],
postInstallChecks: [
...COMMON_CHECKS,
"On GNOME, install gnome-shell-extension-appindicator if the tray icon never appears.",
],
},
fedora: {
name: "Fedora",
title: "Install Voice Dictation on Fedora",
description:
"Fedora speech-to-text installation guide for Voculinux. Set up offline Linux voice typing on Fedora 42+ with the install script or .rpm, on Wayland or X11.",
intro:
"Fedora 42 and newer ship the Python and GTK stack Voculinux needs, and Fedora 42 is the release in the project's CI matrix. This guide focuses on reliable setup and minimal friction.",
prerequisites: [
"Fedora 42 or newer (Workstation or a spin)",
"Python 3.11+ with python3-gobject (the installer handles this)",
"Working microphone",
"curl installed (sudo dnf install -y curl)",
],
supportedOn: ["Fedora 42 and newer: good support (uses lib64 paths)"],
installCommand: INSTALL_SCRIPT_COMMAND,
otherInstallOptions: [RPM_OPTION, FLATPAK_OPTION],
setupNotes: [
"If prompted for optional dependencies, accept defaults unless your desktop setup requires custom input tooling.",
"Keep the default whisper.cpp engine for the best speed-to-accuracy tradeoff.",
"Use the smallest model first, then scale up once dictation feels stable on your hardware.",
"On the KDE spin under Wayland, set System Settings → Keyboard → Virtual Keyboard to IBus Wayland.",
],
postInstallChecks: [
...COMMON_CHECKS,
"Confirm startup behavior and language settings persist after reboot.",
],
},
arch: {
name: "Arch Linux",
title: "Install Voice Dictation on Arch Linux",
description:
"Arch Linux voice dictation guide for offline speech recognition on Arch, Manjaro and EndeavourOS with the install script. The Voculinux AUR packages are not published yet.",
intro:
"Arch users can set up dictation with the install script, which keeps whisper.cpp as the default engine and works across rolling-release desktops. The Voculinux AUR packages are not published yet, so there is no AUR install path today.",
prerequisites: [
"Arch Linux, Manjaro or EndeavourOS",
"Python 3.11+ with python-gobject",
"Working microphone",
"curl installed (sudo pacman -Sy --needed curl)",
],
supportedOn: [
"Arch Linux (rolling): good support",
"Manjaro (rolling): good support, inherits Arch compatibility",
"EndeavourOS (rolling): good support, inherits Arch compatibility",
],
installCommand: INSTALL_SCRIPT_COMMAND,
otherInstallOptions: [
{
label: "AUR packages (not published yet)",
note: "Fork status: fork AUR packages are not published yet. Once published, voculinux builds the canonical release sdist against Arch dependencies. voculinux-bin installs the prebuilt release AppImage. voculinux-git is a community package that tracks main. The three conflict, so install only one.",
command: `yay -S voculinux # canonical release sdist, built against Arch dependencies
yay -S voculinux-git # community package; tracks main`,
},
],
setupNotes: [
"Prefer whisper.cpp: it avoids the full PyTorch stack that OpenAI Whisper pulls in.",
"On custom compositor setups, verify text injection behavior in both terminal and browser contexts.",
"If you tweak keybinds heavily, set a non-conflicting activation shortcut in Voculinux settings.",
"Once the AUR packages are published they will update with your AUR helper; the in-app update checker only notifies.",
],
postInstallChecks: [
...COMMON_CHECKS,
"Back up ~/.config/voculinux/config.json after tuning preferences.",
],
},
opensuse: {
name: "openSUSE",
title: "Install Voice Dictation on openSUSE",
description:
"Install Voculinux offline voice dictation on openSUSE Tumbleweed with the install script. Covers versioned Python packages, Vulkan builds, X11 and Wayland.",
intro:
"On openSUSE Tumbleweed the install script uses zypper, resolves the versioned Python packages for your snapshot, and sets up whisper.cpp with a checksum-verified tiny model.",
prerequisites: [
"openSUSE Tumbleweed",
"Python 3.11+ with the matching pythonXY-gobject package (the installer handles this)",
"Working microphone",
"curl installed (sudo zypper install curl)",
],
supportedOn: [
"openSUSE Tumbleweed: good support",
"openSUSE Leap is not listed in the compatibility matrix",
],
installCommand: INSTALL_SCRIPT_COMMAND,
otherInstallOptions: [
FLATPAK_OPTION,
{
label: "AppImage",
note: "Download the x86_64 or aarch64 AppImage from upstream GitHub Releases until fork releases land. It starts on Tumbleweed and uses Vulkan through the host driver, but still needs host text-injection tools (xdotool on X11; wtype, ydotool or clipboard tools on Wayland).",
command: `chmod +x Voculinux-*-x86_64.AppImage # or aarch64
./Voculinux-*-x86_64.AppImage`,
},
],
setupNotes: [
"Tumbleweed uses versioned Python package names such as python313-devel. The -devel suffix means development headers, not beta packages.",
"For a manual install, if ${PYVER}-virtualenv is unavailable on your snapshot, try ${PYVER}-venv.",
"For whisper.cpp Vulkan GPU builds, install vulkan-tools, vulkan-devel and shaderc (sudo zypper install -y vulkan-tools vulkan-devel shaderc).",
"On KDE Plasma Wayland, set System Settings → Keyboard → Virtual Keyboard to IBus Wayland.",
],
postInstallChecks: [
...COMMON_CHECKS,
"Run vulkaninfo --summary if you expect GPU acceleration.",
],
},
"linux-mint": {
name: "Linux Mint",
title: "Install Voice Dictation on Linux Mint",
description:
"Install Voculinux offline voice dictation on Linux Mint 22 with the install script or .deb. Covers Cinnamon, the Ubuntu 24.04 base and why Mint 21 is too old.",
intro:
"Linux Mint is judged by its Ubuntu base, not its own version number. Mint 22 is built on Ubuntu 24.04, so the install script treats it as Ubuntu and installs everything Voculinux needs.",
prerequisites: [
"Linux Mint 22 or newer, on an Ubuntu 24.04+ base (Mint 21 is on Ubuntu 22.04, below the floor)",
"Python 3.11+ with the distro's python3-gi (the installer handles this)",
"Working microphone",
"curl installed (sudo apt update && sudo apt install -y curl)",
],
supportedOn: [
"Linux Mint on Ubuntu 24.04+ (Mint 22): good support",
"Cinnamon, MATE and Xfce desktops: full support",
],
installCommand: INSTALL_SCRIPT_COMMAND,
otherInstallOptions: [
{
...DEB_OPTION,
note: `The .deb targets Debian 12+ and Ubuntu 24.04+, the base Mint 22 is built on. ${DEB_OPTION.note}`,
},
FLATPAK_OPTION,
],
setupNotes: [
"Keep the default whisper.cpp engine unless you have a specific reason to switch.",
"Cinnamon is fully supported and works with AppIndicator for the tray icon.",
"Text is typed with xdotool on X11 sessions; on Wayland, Voculinux picks from IBus, the RemoteDesktop portal, wtype or ydotool.",
"The install script adds you to the input group for Wayland hotkeys; log out and back in afterwards.",
],
postInstallChecks: [...COMMON_CHECKS],
},
"pop-os": {
name: "Pop!_OS",
title: "Install Voice Dictation on Pop!_OS",
description:
"Install Voculinux offline voice dictation on Pop!_OS 24.04 with the install script or .deb. Covers the Ubuntu base, Wayland hotkeys and fallback options.",
intro:
"Pop!_OS uses Ubuntu's own version numbering, and 24.04 and newer meet the Python 3.11 floor. The install script detects Pop!_OS as part of the Ubuntu family.",
prerequisites: [
"Pop!_OS 24.04 or newer",
"Python 3.11+ with the distro's python3-gi (the installer handles this)",
"Working microphone",
"curl installed (sudo apt update && sudo apt install -y curl)",
],
supportedOn: ["Pop!_OS 24.04 and newer: good support"],
installCommand: INSTALL_SCRIPT_COMMAND,
otherInstallOptions: [DEB_OPTION, FLATPAK_OPTION],
setupNotes: [
"For a manual install, use libgirepository-2.0-dev if libgirepository1.0-dev is not available.",
"The install script adds you to the input group for Wayland hotkeys; log out and back in afterwards.",
"If the hotkey or typing does not work in your session, enable Settings → Shortcuts → External Activation (Desktop Shortcut) and bind a desktop shortcut to voculinux --toggle (toggle only, no push-to-talk), or dictate into the Dictation Pad and copy the text out.",
"whisper.cpp uses a Vulkan GPU when available; check with vulkaninfo --summary.",
],
postInstallChecks: [...COMMON_CHECKS],
},
};
const distroKeywords: Record<DistroSlug, string[]> = {
ubuntu: [
"ubuntu voice dictation",
"speech to text ubuntu",
"ubuntu voice typing",
"offline dictation ubuntu",
],
debian: [
"debian voice dictation",
"speech to text debian",
"debian voice typing",
"offline dictation debian",
],
fedora: [
"fedora voice dictation",
"speech to text fedora",
"fedora voice typing",
"offline dictation fedora",
],
arch: [
"arch linux voice dictation",
"speech to text arch linux",
"manjaro dictation",
"voculinux aur",
],
opensuse: [
"opensuse voice dictation",
"speech to text opensuse tumbleweed",
"opensuse voice typing",
"offline dictation opensuse",
],
"linux-mint": [
"linux mint voice dictation",
"speech to text linux mint",
"linux mint voice typing",
"cinnamon dictation",
],
"pop-os": [
"pop os voice dictation",
"speech to text pop!_os",
"pop os voice typing",
"offline dictation pop os",
],
};
const isDistroSlug = (value: string): value is DistroSlug => {
return value in distroConfig;
};
const resolveParams = async (
params: DistroParams,
): Promise<{ distro: string }> => {
return Promise.resolve(params);
};
const getDistroStyles = (distro: DistroSlug) => {
switch (distro) {
case "ubuntu":
return {
icon: Laptop,
iconClass: "text-primary",
chipClass: "border-border bg-muted text-muted-foreground",
panelClass: "bg-primary/10",
};
case "debian":
return {
icon: Server,
iconClass: "text-primary",
chipClass: "border-border bg-muted text-muted-foreground",
panelClass: "bg-primary/10",
};
case "fedora":
return {
icon: Sparkles,
iconClass: "text-primary",
chipClass: "border-border bg-primary/10 text-primary",
panelClass: "bg-primary/10",
};
case "opensuse":
return {
icon: RefreshCw,
iconClass: "text-primary",
chipClass: "border-border bg-primary/10 text-primary",
panelClass: "bg-primary/10",
};
case "linux-mint":
return {
icon: Monitor,
iconClass: "text-primary",
chipClass: "border-border bg-muted text-muted-foreground",
panelClass: "bg-primary/10",
};
case "pop-os":
return {
icon: Laptop,
iconClass: "text-primary",
chipClass: "border-border bg-primary/10 text-primary",
panelClass: "bg-primary/10",
};
case "arch":
default:
return {
icon: Cpu,
iconClass: "text-primary",
chipClass: "border-border bg-primary/10 text-muted-foreground",
panelClass: "bg-primary/10",
};
}
};
export function generateStaticParams() {
return Object.keys(distroConfig).map((distro) => ({ distro }));
return Object.keys(DISTRO_NAMES).map((distro) => ({ distro }));
}
export async function generateMetadata({
@@ -418,258 +33,31 @@ export async function generateMetadata({
params: DistroParams;
}): Promise<Metadata> {
const { distro } = await resolveParams(params);
if (!isDistroSlug(distro)) {
return buildPageMetadata({
title: "Linux Voice Dictation Install Guide",
description:
"Install Voculinux on Linux with distro-specific setup instructions for Ubuntu, Debian, Fedora, Arch, openSUSE, Linux Mint, and Pop!_OS.",
path: "/install",
});
}
const config = distroConfig[distro];
const name = DISTRO_NAMES[distro] ?? "Linux";
return buildPageMetadata({
// Search title leads with the query people type ("ubuntu speech to text");
// the on-page h1 stays config.title.
title: `${config.name} Speech to Text: Offline Voice Typing Setup`,
description: config.description,
title: `${name}: Not Published for Voculinux`,
description: `Voculinux is not published for ${name}. It is available for Void Linux (glibc, x86_64) as a signed xbps repository.`,
path: `/install/${distro}`,
keywords: distroKeywords[distro],
});
}
export default async function DistroInstallPage({
export default async function DistroNoticePage({
params,
}: {
params: DistroParams;
}) {
const { distro } = await resolveParams(params);
const name = DISTRO_NAMES[distro];
if (!isDistroSlug(distro)) {
if (!name) {
notFound();
}
const config = distroConfig[distro];
const styles = getDistroStyles(distro);
const DistroIcon = styles.icon;
const howToJsonLd = {
"@context": "https://schema.org",
"@type": "HowTo",
name: `${config.title} in 3 Steps`,
description: config.description,
inLanguage: "en-US",
totalTime: "PT10M",
supply: [
{
"@type": "HowToSupply",
name: "Linux computer",
},
{
"@type": "HowToSupply",
name: "Microphone",
},
],
tool: [
{
"@type": "HowToTool",
name: "Terminal",
},
],
step: [
{
"@type": "HowToStep",
name: "Open terminal",
text: "Launch your terminal application and ensure curl is available.",
url: absoluteUrl(`/install/${distro}`),
},
{
"@type": "HowToStep",
name: "Run installer",
text: `Run: ${config.installCommand}`,
url: absoluteUrl(`/install/${distro}`),
},
{
"@type": "HowToStep",
name: "Start dictating",
text: "Launch voculinux and hold Right Alt (Option) to begin dictation, or choose toggle mode in Settings.",
url: absoluteUrl(`/install/${distro}`),
},
],
};
return (
<SeoSubpageShell>
<script
type="application/ld+json"
dangerouslySetInnerHTML={{ __html: JSON.stringify(howToJsonLd) }}
/>
<section>
<p
className={`mb-4 inline-flex items-center gap-2 rounded-full border px-4 py-1.5 text-sm font-medium ${styles.chipClass}`}
>
<DistroIcon className={`h-4 w-4 ${styles.iconClass}`} />
{config.name} Setup Guide
</p>
<h1 className="mb-5 font-display text-4xl font-semibold tracking-tight sm:text-5xl">
{config.title}
</h1>
<p className="mb-8 max-w-4xl text-lg text-muted-foreground">
{config.intro}
</p>
<div
className={`rounded-[12px] border border-border p-6 ${styles.panelClass}`}
>
<h2 className="mb-3 inline-flex items-center gap-2 text-xl font-semibold">
<Terminal className="h-5 w-5 text-primary" />
Install command
</h2>
<div className="rounded-[12px] bg-[color:var(--terminal)] p-4">
<code className="block overflow-x-auto whitespace-pre-wrap break-all text-sm text-[color:var(--terminal-fg)] sm:text-base">
{config.installCommand}
</code>
</div>
<p className="mt-4 text-sm text-muted-foreground">
Fork status: this command fetches the upstream install script, so it
installs Vocalinux (upstream), not Voculinux. Fork release artefacts
are not published yet.
</p>
</div>
</section>
<section className="mt-10 grid gap-6 md:grid-cols-2">
<article className="rounded-[12px] border border-border bg-background p-6">
<h2 className="mb-4 inline-flex items-center gap-2 text-2xl font-semibold">
<Shield className="h-5 w-5 text-primary" />
Prerequisites
</h2>
<ul className="space-y-2 text-muted-foreground">
{config.prerequisites.map((item) => (
<li key={item} className="flex items-start gap-2">
<CheckCircle2 className="mt-0.5 h-4 w-4 flex-shrink-0 text-primary" />
{item}
</li>
))}
</ul>
</article>
<article className="rounded-[12px] border border-border bg-background p-6">
<h2 className="mb-4 inline-flex items-center gap-2 text-2xl font-semibold">
<Sparkles className="h-5 w-5 text-primary" aria-hidden="true" />
Supported on
</h2>
<ul className="space-y-2 text-muted-foreground">
{config.supportedOn.map((item) => (
<li key={item} className="flex items-start gap-2">
<CheckCircle2
className="mt-0.5 h-4 w-4 flex-shrink-0 text-primary"
aria-hidden="true"
/>
{item}
</li>
))}
</ul>
<p className="mt-4 text-sm text-muted-foreground">
Support levels come from the project&rsquo;s distribution
compatibility matrix. They describe expected compatibility, not a
list of test machines.
</p>
</article>
</section>
{config.otherInstallOptions.length > 0 && (
<section className="mt-10 rounded-[12px] border border-border bg-background p-6">
<h2 className="mb-4 inline-flex items-center gap-2 text-2xl font-semibold">
<Download className="h-5 w-5 text-primary" aria-hidden="true" />
Other install options for {config.name}
</h2>
<div className="space-y-6">
{config.otherInstallOptions.map((option) => (
<div key={option.label}>
<h3 className="mb-2 font-semibold">{option.label}</h3>
<p className="mb-3 text-sm text-muted-foreground">
{option.note}
</p>
<div className="rounded-[12px] bg-[color:var(--terminal)] p-4">
<code className="block overflow-x-auto whitespace-pre-wrap break-all text-sm text-[color:var(--terminal-fg)]">
{option.command}
</code>
</div>
</div>
))}
</div>
<p className="mt-6 text-sm text-muted-foreground">
Compare every package format on the
<Link
href="/install/"
className="ml-1 font-semibold text-primary hover:underline"
>
install overview
</Link>
, or read the
<Link
href="/flatpak/"
className="ml-1 font-semibold text-primary hover:underline"
>
Flatpak guide
</Link>
.
</p>
</section>
)}
<section className="mt-10 rounded-[12px] border border-border bg-background p-6">
<h2 className="mb-4 inline-flex items-center gap-2 text-2xl font-semibold">
<Cpu className="h-5 w-5 text-primary" />
Setup notes for {config.name}
</h2>
<ol className="space-y-3 text-muted-foreground">
{config.setupNotes.map((item) => (
<li key={item} className="flex items-start gap-2">
<CheckCircle2 className="mt-0.5 h-4 w-4 flex-shrink-0 text-primary" />
{item}
</li>
))}
</ol>
</section>
<section className="/70 mt-10 rounded-[12px] border border-border bg-muted p-6">
<h2 className="mb-4 inline-flex items-center gap-2 text-2xl font-semibold">
<CheckCircle2 className="h-5 w-5 text-primary" />
Post-install checklist
</h2>
<ul className="space-y-3 text-muted-foreground">
{config.postInstallChecks.map((item) => (
<li key={item} className="flex items-start gap-2">
<CheckCircle2 className="mt-0.5 h-4 w-4 flex-shrink-0 text-primary" />
{item}
</li>
))}
</ul>
<p className="mt-6 text-sm text-muted-foreground">
Need help selecting an engine? Read the
<Link
href="/compare/"
className="ml-1 font-semibold text-primary hover:underline"
>
engine comparison
</Link>
. Stuck? See
<Link
href="/troubleshooting/"
className="ml-1 font-semibold text-primary hover:underline"
>
troubleshooting
</Link>
.
</p>
</section>
</SeoSubpageShell>
<ChannelNotice
title={name}
lede={`Voculinux is not published for ${name} (Void only).`}
/>
);
}
+8 -416
View File
@@ -1,422 +1,14 @@
import Link from "next/link";
import { type Metadata } from "next";
import {
AlertTriangle,
Braces,
CheckCircle2,
ChevronRight,
CircleHelp,
Settings2,
Terminal,
} from "lucide-react";
import { SeoSubpageShell } from "@/components/seo-subpage-shell";
import { absoluteUrl, buildPageMetadata } from "@/lib/seo";
const PAGE_PATH = "/install/nixos";
const PAGE_TITLE = "Voculinux on NixOS: Nix Flake and NixOS Module | Voculinux";
const PAGE_DESCRIPTION =
"Install Voculinux on NixOS or any Nix system with the official flake: nix run, nix profile, the NixOS and Home Manager modules, Wayland groups and scope.";
const TRY_COMMANDS = `nix run github:VocaHQ/vocalinux # try it
nix profile install github:VocaHQ/vocalinux`;
const NIXOS_SNIPPET = `{
inputs.vocalinux.url = "github:VocaHQ/vocalinux";
outputs = { nixpkgs, vocalinux, ... }: {
nixosConfigurations.host = nixpkgs.lib.nixosSystem {
modules = [
vocalinux.nixosModules.vocalinux
{
programs.vocalinux.enable = true;
users.users.you.extraGroups = [ "input" "uinput" ];
}
];
};
};
}`;
const HOME_MANAGER_SNIPPET = `{
inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
inputs.home-manager.url = "github:nix-community/home-manager";
inputs.home-manager.inputs.nixpkgs.follows = "nixpkgs";
inputs.vocalinux.url = "github:VocaHQ/vocalinux";
outputs = { nixpkgs, home-manager, vocalinux, ... }: {
homeConfigurations.you = home-manager.lib.homeManagerConfiguration {
pkgs = nixpkgs.legacyPackages.x86_64-linux;
modules = [
vocalinux.homeManagerModules.vocalinux
{ programs.vocalinux.enable = true; }
];
};
};
}`;
const flakeOutputs = [
{
output: "packages.<system>.vocalinux (also default)",
purpose: "The Voculinux package for x86_64-linux and aarch64-linux",
},
{
output: "apps.<system>.vocalinux (also default)",
purpose: "What nix run github:VocaHQ/vocalinux starts",
},
{
output: "overlays.default",
purpose: "Adds pkgs.vocalinux to your own nixpkgs",
},
{
output: "nixosModules.vocalinux (also default)",
purpose: "programs.vocalinux.enable and programs.vocalinux.package",
},
{
output: "homeManagerModules.vocalinux (also default)",
purpose: "The same programs.vocalinux options, per user",
},
];
const moduleSteps = [
{
name: "Add the flake input",
text: 'Add inputs.vocalinux.url = "github:VocaHQ/vocalinux" to your system flake.',
},
{
name: "Import the module and enable it",
text: "Add vocalinux.nixosModules.vocalinux to your modules and set programs.vocalinux.enable = true. The module installs the package and enables hardware.uinput.",
},
{
name: "Add each dictating user to input and uinput",
text: 'Set users.users.<name>.extraGroups = [ "input" "uinput" ] for Wayland shortcuts and ydotool typing. X11 sessions can skip this.',
},
{
name: "Rebuild and log in again",
text: "Rebuild your system as usual, then log out and back in so the new group membership applies.",
},
];
const faqs = [
{
question: "Is NixOS supported?",
answer:
"Yes, through the Nix flake added in v0.18.2. The standard install.sh script does not work on NixOS because of the /nix/store layout, which is why the distro compatibility doc still lists NixOS as unsupported for that installer. Use the flake instead.",
},
{
question: "Does the Nix package use my GPU?",
answer:
"The bundled pywhispercpp is built with the Vulkan backend, so whisper.cpp can use an AMD, Intel or NVIDIA GPU through Vulkan. It is also built with GGML_NATIVE=OFF, a portable baseline rather than -march=native.",
},
{
question:
"Can I use Parakeet, Faster Whisper, VOSK or OpenAI Whisper from the flake?",
answer:
"No. The flake packages whisper.cpp plus the Silero VAD extra. The other engines are not packaged because of the PyTorch footprint or because they are not in nixpkgs.",
},
{
question: "Why do I need the input group?",
answer:
"On Wayland the built-in push-to-talk hotkey reads keyboard devices under /dev/input. Membership in input grants read access to every keyboard and pointer device, so the module leaves that choice to each user. The alternative is a compositor shortcut bound to the D-Bus Toggle command, which only toggles.",
},
{
question: "How do I update?",
answer:
"Update the vocalinux input in your flake lock and rebuild, or upgrade your Nix profile. The in-app update checker only tells you a release exists; it never installs anything.",
},
];
import { ChannelNotice } from "@/components/channel-notice";
import { buildPageMetadata } from "@/lib/seo";
export const metadata: Metadata = buildPageMetadata({
title: PAGE_TITLE,
description: PAGE_DESCRIPTION,
path: PAGE_PATH,
keywords: [
"nixos voice dictation",
"voculinux nix flake",
"nixos speech to text",
"nixos module voculinux",
"home manager voculinux",
"nix whisper.cpp dictation",
],
title: "NixOS: Not Published for Voculinux",
description:
"Voculinux has no Nix flake or NixOS module. It is published for Void Linux (glibc, x86_64) as a signed xbps repository.",
path: "/install/nixos",
});
function Commands({ children }: { children: string }) {
return (
<div className="subpage-terminal mt-3 p-4">
<pre>
<code>{children}</code>
</pre>
</div>
);
}
export default function NixosInstallPage() {
const articleJsonLd = {
"@context": "https://schema.org",
"@type": "TechArticle",
headline: "Install Voculinux on NixOS with the Nix flake",
description: PAGE_DESCRIPTION,
dateModified: "2026-11-01",
author: {
"@type": "Person",
name: "Jatin K Malik",
url: "https://github.com/jatinkrmalik",
},
publisher: {
"@type": "Organization",
name: "Voculinux",
logo: {
"@type": "ImageObject",
url: absoluteUrl("/voculinux.png"),
},
},
mainEntityOfPage: absoluteUrl(PAGE_PATH),
};
const howToJsonLd = {
"@context": "https://schema.org",
"@type": "HowTo",
name: "Enable Voculinux with the NixOS module",
description: PAGE_DESCRIPTION,
tool: [{ "@type": "HowToTool", name: "Nix with flakes" }],
step: moduleSteps.map((step) => ({
"@type": "HowToStep",
name: step.name,
text: step.text,
url: absoluteUrl(PAGE_PATH),
})),
};
const faqJsonLd = {
"@context": "https://schema.org",
"@type": "FAQPage",
mainEntity: faqs.map((faq) => ({
"@type": "Question",
name: faq.question,
acceptedAnswer: { "@type": "Answer", text: faq.answer },
})),
};
return (
<SeoSubpageShell>
<script
type="application/ld+json"
dangerouslySetInnerHTML={{ __html: JSON.stringify(articleJsonLd) }}
/>
<script
type="application/ld+json"
dangerouslySetInnerHTML={{ __html: JSON.stringify(howToJsonLd) }}
/>
<script
type="application/ld+json"
dangerouslySetInnerHTML={{ __html: JSON.stringify(faqJsonLd) }}
/>
<section>
<p className="subpage-kicker">Added in v0.18.2</p>
<h1 className="mb-5 font-display text-4xl font-semibold tracking-tight sm:text-5xl">
Install on NixOS with the Nix flake
</h1>
<p className="mb-4 max-w-4xl text-lg text-muted-foreground">
Voculinux does not yet publish its own Nix flake. The flake below is
the upstream Vocalinux flake: it builds the app against nixpkgs, ships
a NixOS module and a Home Manager module, and wires every
text-injection and clipboard tool onto the wrapped binary&rsquo;s
PATH. Its snippets install Vocalinux (upstream), not Voculinux. For
Voculinux on Void Linux, use the install script today; a signed xbps
repository will ship with fork releases once published.
</p>
</section>
<section className="mb-12 rounded-[12px] border border-border bg-background p-6">
<h2 className="mb-4 inline-flex items-center gap-2 font-display text-2xl font-semibold">
<Terminal className="h-5 w-5 text-primary" aria-hidden="true" />
Try it or install into a profile
</h2>
<p className="mb-3 text-muted-foreground">
No configuration needed. <code>nix run</code> starts Vocalinux
(upstream) once; <code>nix profile install</code> keeps it in your
user profile:
</p>
<Commands>{TRY_COMMANDS}</Commands>
<p className="mt-4 text-sm text-muted-foreground">
On a non-NixOS host, pair <code>nix profile install</code> with your
distro&rsquo;s udev rule and group membership for{" "}
<code>/dev/uinput</code> and <code>/dev/input</code> if you use
Wayland.
</p>
</section>
<section className="mb-12">
<h2 className="mb-6 inline-flex items-center gap-2 font-display text-2xl font-semibold">
<Settings2 className="h-5 w-5 text-primary" aria-hidden="true" />
Enable the NixOS module
</h2>
<ol className="mb-6 space-y-3">
{moduleSteps.map((step, index) => (
<li
key={step.name}
className="rounded-[12px] border border-border bg-background p-5"
>
<h3 className="font-semibold">
{index + 1}. {step.name}
</h3>
<p className="mt-2 text-sm text-muted-foreground">{step.text}</p>
</li>
))}
</ol>
<p className="text-sm text-muted-foreground">
The documented flake, with <code>host</code> and <code>you</code>{" "}
replaced by your hostname and user:
</p>
<Commands>{NIXOS_SNIPPET}</Commands>
<p className="mt-4 text-sm text-muted-foreground">
<code>hardware.uinput.enable</code>, which the module sets, loads the
uinput kernel module, creates the <code>uinput</code> group and
installs the matching udev rule. ydotool and the evdev backend&rsquo;s
keyboard clone both open <code>/dev/uinput</code>. Group membership
stays per user because <code>input</code> grants read access to every
keyboard and pointer device.
</p>
<h3 className="mb-2 mt-8 font-semibold">Home Manager</h3>
<p className="text-sm text-muted-foreground">
<code>homeManagerModules.vocalinux</code> takes the same{" "}
<code>programs.vocalinux</code> options and installs into your home
profile. Device permissions are a system concern, so on NixOS keep the
NixOS module (or the group lines) on the system side.
</p>
<Commands>{HOME_MANAGER_SNIPPET}</Commands>
</section>
<section className="mb-12 rounded-[12px] border border-border bg-background p-6">
<h2 className="mb-4 inline-flex items-center gap-2 font-display text-2xl font-semibold">
<Braces className="h-5 w-5 text-primary" aria-hidden="true" />
What the flake provides
</h2>
<div className="overflow-x-auto">
<table className="w-full text-left text-sm">
<thead>
<tr className="border-b border-border">
<th className="pb-3 pr-4 font-semibold">Flake output</th>
<th className="pb-3 font-semibold">Use</th>
</tr>
</thead>
<tbody>
{flakeOutputs.map((row) => (
<tr key={row.output} className="border-b border-border">
<td className="py-3 pr-4 font-mono text-xs">{row.output}</td>
<td className="py-3 text-muted-foreground">{row.purpose}</td>
</tr>
))}
</tbody>
</table>
</div>
<h3 className="mb-2 mt-6 font-semibold">Included in the package</h3>
<ul className="space-y-2 text-sm text-muted-foreground">
{[
"whisper.cpp (the default engine), with pywhispercpp built from its PyPI sdist with the Vulkan backend",
"Silero neural VAD through onnxruntime (the [vad] extra)",
"GTK 3, PyGObject and the AppIndicator typelib from nixpkgs",
"xdotool, wtype, ydotool, wl-clipboard, xclip, xsel, xprop, setxkbmap, ibus, gdbus, notify-send, paplay, aplay and vulkaninfo on PATH",
"A desktop entry and the app icon",
].map((item) => (
<li key={item} className="flex items-start gap-2">
<CheckCircle2
className="mt-0.5 h-4 w-4 flex-shrink-0 text-primary"
aria-hidden="true"
/>
{item}
</li>
))}
</ul>
<h3 className="mb-2 mt-6 font-semibold">Not included</h3>
<p className="text-sm text-muted-foreground">
The <code>vosk</code>, <code>whisper</code>, <code>parakeet</code> and{" "}
<code>faster_whisper</code> extras are not packaged, so{" "}
<Link href="/parakeet/">Parakeet</Link> and{" "}
<Link href="/faster-whisper/">Faster Whisper</Link> need the install
script on another distro. The flake follows{" "}
<code>nixos-unstable</code> and builds for x86_64-linux and
aarch64-linux.
</p>
</section>
<section className="mb-12 rounded-[12px] border border-border bg-background p-6">
<h2 className="mb-4 inline-flex items-center gap-2 font-display text-2xl font-semibold">
<AlertTriangle className="h-5 w-5 text-primary" aria-hidden="true" />
Gotchas
</h2>
<ul className="space-y-2 text-sm text-muted-foreground">
<li>
Do not run <code>install.sh</code> on NixOS. It expects a
conventional filesystem layout; the flake is the supported path.
</li>
<li>
Speech models are not part of the Nix store. Voculinux downloads one
only after you confirm it, and shows the size first.
</li>
<li>
On Wayland without <code>input</code> membership, the hotkey is
disabled. Bind a compositor shortcut through{" "}
<Link href="/global-shortcuts/">D-Bus activation</Link> instead; it
toggles, it cannot push-to-talk.
</li>
<li>
On KDE Plasma Wayland, set System Settings → Keyboard → Virtual
Keyboard to &ldquo;IBus Wayland&rdquo;. More in the{" "}
<Link href="/wayland/">Wayland guide</Link>.
</li>
</ul>
</section>
<section className="mb-12">
<h2 className="mb-6 inline-flex items-center gap-2 font-display text-2xl font-semibold">
<CircleHelp className="h-5 w-5 text-primary" aria-hidden="true" />
NixOS FAQ
</h2>
<div className="space-y-4">
{faqs.map((faq) => (
<div
key={faq.question}
className="rounded-[12px] border border-border bg-background p-5"
>
<h3 className="mb-2 font-semibold">{faq.question}</h3>
<p className="text-sm text-muted-foreground">{faq.answer}</p>
</div>
))}
</div>
</section>
<section className="rounded-[12px] border border-border bg-muted p-8">
<h2 className="mb-4 font-display text-2xl font-semibold">
Other ways to install
</h2>
<p className="mb-6 text-muted-foreground">
Not on Nix? The install script and the upstream .deb, .rpm and
AppImage downloads cover most other systems. The AUR packages and the
fork Flatpak remote are not published yet.
</p>
<div className="flex flex-wrap gap-4">
<Link
href="/install/"
className="inline-flex items-center gap-2 rounded-full bg-primary px-4 py-2 text-sm font-semibold text-primary-foreground"
>
All install options
<ChevronRight className="h-4 w-4" aria-hidden="true" />
</Link>
<Link
href="/flatpak/"
className="inline-flex items-center gap-2 rounded-lg border border-border bg-background px-4 py-2 text-sm font-semibold hover:bg-muted"
>
Flatpak remote
</Link>
<Link
href="/gpu-acceleration/"
className="inline-flex items-center gap-2 rounded-lg border border-border bg-background px-4 py-2 text-sm font-semibold hover:bg-muted"
>
GPU acceleration
</Link>
</div>
</section>
</SeoSubpageShell>
);
export default function NixosNoticePage() {
return <ChannelNotice title="NixOS" />;
}
+77 -194
View File
@@ -1,236 +1,104 @@
import Link from "next/link";
import { type Metadata } from "next";
import { ChevronRight, Cpu, Laptop, Terminal } from "lucide-react";
import { SeoSubpageShell } from "@/components/seo-subpage-shell";
import { TerminalBlock } from "@/components/terminal-block";
import { buildPageMetadata, SOURCE_REPO_URL } from "@/lib/seo";
import {
buildPageMetadata,
SOURCE_REPO_URL,
UPSTREAM_REPO_URL,
} from "@/lib/seo";
const installCommand =
"curl -fsSL https://raw.githubusercontent.com/VocaHQ/vocalinux/main/install.sh -o /tmp/vl.sh && bash /tmp/vl.sh --interactive";
const installDisplayCommand = `curl -fsSL \\
https://raw.githubusercontent.com/VocaHQ/vocalinux/main/install.sh \\
-o /tmp/vl.sh && \\
bash /tmp/vl.sh --interactive`;
const codeClass =
"rounded bg-muted px-1.5 py-0.5 font-mono text-sm text-foreground";
const distroGuides = [
{
name: "Ubuntu",
href: "/install/ubuntu/",
description:
"Install offline voice dictation on Ubuntu 24.04+ with GNOME, KDE, X11, or Wayland.",
icon: Laptop,
},
{
name: "Fedora",
href: "/install/fedora/",
description:
"Set up speech-to-text on Fedora Workstation and Silverblue with distro-specific notes.",
icon: Terminal,
},
{
name: "Arch Linux",
href: "/install/arch/",
description:
"Install Voculinux on Arch, Manjaro, and EndeavourOS with package prerequisites and tips.",
icon: Cpu,
},
{
name: "Debian",
href: "/install/debian/",
description:
"Debian 12+ with the install script or the native .deb package.",
icon: Terminal,
},
{
name: "openSUSE",
href: "/install/opensuse/",
description: "Set up offline voice typing on openSUSE Tumbleweed.",
icon: Cpu,
},
{
name: "Linux Mint",
href: "/install/linux-mint/",
description:
"Voice dictation on Linux Mint releases built on Ubuntu 24.04.",
icon: Laptop,
},
{
name: "Pop!_OS",
href: "/install/pop-os/",
description: "Install Voculinux on Pop!_OS 24.04 and later.",
icon: Laptop,
},
{
name: "NixOS",
href: "/install/nixos/",
description: "Use the Nix flake or the NixOS module shipped since v0.18.2.",
icon: Terminal,
},
];
VOID_INSTALL_COMMANDS,
VOID_KEY_FINGERPRINT,
VOID_UPDATE_COMMAND,
} from "@/lib/void-channel";
export const metadata: Metadata = buildPageMetadata({
title: "Install Speech to Text on Linux (Ubuntu, Fedora, Arch)",
title: "Install Voculinux on Void Linux",
description:
"Linux voice dictation install guides for Ubuntu, Debian, Fedora, Arch, openSUSE, Mint, Pop!_OS, NixOS, and Flatpak. Start offline speech-to-text.",
"Install Voculinux on Void Linux (glibc, x86_64) from its signed xbps repository: trust the key, register the repository, then run xbps-install.",
path: "/install",
keywords: [
"install voice dictation linux",
"linux speech to text setup",
"ubuntu voice typing install",
"fedora speech recognition",
"arch linux dictation",
"void linux voice dictation",
"install voice dictation void linux",
"xbps voice dictation",
"void linux speech to text",
"offline voice typing linux",
],
});
export default function InstallGuidesPage() {
export default function InstallPage() {
return (
<SeoSubpageShell>
<section>
<p className="subpage-kicker">Linux installation</p>
<h1>Install Voice Dictation on Linux</h1>
<p className="subpage-kicker">Void Linux install</p>
<h1>Install Voculinux on Void Linux</h1>
<p className="subpage-lede">
Pick your distro guide and follow a proven setup flow for offline
speech-to-text on Linux. Every guide is written for real desktop
usage.
Voculinux is packaged for Void Linux (glibc, x86_64) as a signed xbps
repository. Trust the key, register the repository, then install the
package with xbps.
</p>
<p className="mt-4 text-sm text-muted-foreground">
This is a third-party repository published by this project, not by
Void Linux upstream. It covers Void Linux glibc x86_64 only. Other
channels are not published for Voculinux.
</p>
<div className="mt-8">
<TerminalBlock
command={installCommand}
displayCommand={installDisplayCommand}
label="install.sh --interactive"
command={VOID_INSTALL_COMMANDS}
displayCommand={VOID_INSTALL_COMMANDS}
label="xbps, as root"
/>
</div>
<p className="mt-4 text-sm text-muted-foreground">
Then launch <code>vocalinux</code> — the upstream binary the script
installs today. Fork releases will ship <code>voculinux</code>.
</p>
<p className="mt-4 text-sm text-muted-foreground">
Fork status: this command fetches the upstream install script, so it
installs Vocalinux (upstream), not Voculinux. Fork release artefacts
are not published yet.
</p>
<div className="install-alts">
<a
className="install-alt"
href={`${UPSTREAM_REPO_URL}/releases`}
rel="noopener noreferrer"
>
<p className="install-alt-kicker">No installer</p>
<h2>AppImage</h2>
<p>
x86_64 or aarch64 from upstream GitHub Releases. No root. Host
text-injection tools are still required.
</p>
</a>
<a
className="install-alt"
href={`${SOURCE_REPO_URL}/src/branch/main/docs/INSTALL.md#distro-packages-deb--rpm`}
rel="noopener noreferrer"
>
<p className="install-alt-kicker">New</p>
<h2>Native packages</h2>
<p>
.deb for Debian 12+ and Ubuntu 24.04+. .rpm for Fedora. x86_64 and
aarch64 on every fork release once published.
</p>
</a>
<a className="install-alt" href="/flatpak/">
<p className="install-alt-kicker">Auto-updates</p>
<h2>Flatpak</h2>
<p>
Install once from the fork Flatpak remote once published and{" "}
<code>flatpak update</code> carries each stable release. Or
sideload the .flatpak bundle.
</p>
</a>
<div className="install-alt">
<p className="install-alt-kicker">Snap Store</p>
<h2>Snap</h2>
<p>
<code>snap install voculinux --edge</code>, once the fork Snap is
published (not published yet). amd64 only; a .snap will be
attached to fork releases too.
</p>
</div>
<div className="install-alt">
<p className="install-alt-kicker">Arch</p>
<h2>AUR</h2>
<p>
<code>yay -S voculinux</code>, or voculinux-bin for the prebuilt
package. Fork packages are not published yet.
</p>
</div>
<a
className="install-alt"
href={`${SOURCE_REPO_URL}/src/branch/main/docs/INSTALL.md#from-source`}
rel="noopener noreferrer"
>
<p className="install-alt-kicker">From source</p>
<h2>Build it yourself</h2>
<p>
Clone the repo and run <code>./install.sh</code>. The install
guide in the Voculinux repository has the full source path.
</p>
</a>
</div>
<p className="mt-4 text-sm text-muted-foreground">
Fork status: packaged artefacts (AppImage, .deb/.rpm, Flatpak, Snap,
AUR) are not published yet. The AppImage card links upstream
artefacts; the Snap and AUR cards name the planned fork packages and
do not link anywhere until the first fork release lands.
Repository key fingerprint: <code>{VOID_KEY_FINGERPRINT}</code>. The
key file is named by its fingerprint, so xbps finds it on the first
sync and asks nothing. If xbps still shows a fingerprint prompt,
answer <code>N</code> unless it reads exactly this fingerprint; the
release&apos;s <code>FINGERPRINT</code> file carries the same value.
Do not add <code>-y</code> to the first sync: it would accept an
unknown key without asking.
</p>
</section>
<section className="mt-12 grid gap-4 md:grid-cols-3">
{distroGuides.map((guide) => {
const Icon = guide.icon;
return (
<Link
key={guide.href}
href={guide.href}
className="subpage-card group p-5"
>
<div className="bg-primary/10 mb-3 inline-flex rounded-[10px] p-2.5">
<Icon className="h-5 w-5 text-primary" />
</div>
<h2 className="mb-2 text-xl font-semibold">{guide.name}</h2>
<p className="mb-4 text-sm text-muted-foreground">
{guide.description}
</p>
<span className="inline-flex items-center gap-1 text-sm font-semibold text-primary">
Read {guide.name} guide
<ChevronRight className="h-4 w-4 transition-transform group-hover:translate-x-0.5" />
</span>
</Link>
);
})}
<section className="subpage-panel mt-12 p-6 sm:p-8">
<h2 className="mb-4">Update</h2>
<TerminalBlock
command={VOID_UPDATE_COMMAND}
displayCommand={VOID_UPDATE_COMMAND}
label="xbps, update"
/>
<p className="mt-4 text-sm text-muted-foreground">
Packaged installs point their update prompts at xbps, so update with
this command.
</p>
</section>
<section className="subpage-panel mt-12 p-6 sm:p-8">
<h2 className="mb-4">After installation</h2>
<ul className="space-y-3 text-muted-foreground">
<li>
Launch with <code className={codeClass}>voculinux</code> once fork
releases are published. The install command above fetches the
upstream script, so today it puts{" "}
<code className={codeClass}>vocalinux</code> (upstream) on your PATH
instead.
Launch with <code>voculinux</code> from a terminal or the
application menu.
</li>
<li>
Hotkeys need the input group: <code>sudo usermod -aG input</code>{" "}
with your user name, then log out and back in.
</li>
<li>
Hold Right Alt (Option) to dictate, or switch to toggle mode in
Settings.
</li>
<li>
Choose whisper.cpp for speed, Whisper for maximum model
compatibility, or VOSK for low-RAM systems.
Speech models are not in the package. They download on first run
into <code>~/.local/share/voculinux</code> and are checked against
pinned checksums.
</li>
<li>
Optional engines (VOSK, OpenAI Whisper, Parakeet, Faster Whisper and
onnxruntime for VAD) come from your own Python environment, not
xbps.
</li>
<li>
ydotool is not packaged for Void, so that text-injection fallback is
absent.
</li>
</ul>
<p className="mt-6 text-sm text-muted-foreground">
@@ -244,6 +112,21 @@ export default function InstallGuidesPage() {
.
</p>
</section>
<section className="subpage-panel mt-12 p-6 sm:p-8">
<h2 className="mb-4">Source</h2>
<p className="text-muted-foreground">
The source, issues and releases for Voculinux are on the fork host:{" "}
<a
href={SOURCE_REPO_URL}
className="font-semibold text-primary hover:underline"
rel="noopener noreferrer"
>
git.bongbetic.com/xavierk/voculinux
</a>
.
</p>
</section>
</SeoSubpageShell>
);
}
+2 -2
View File
@@ -339,8 +339,8 @@ export default function LanguagesPage() {
dateModified: "2026-07-29",
author: {
"@type": "Person",
name: "Jatin K Malik",
url: "https://github.com/jatinkrmalik",
name: "Xavier Karma",
url: "https://bongbetic.com",
},
publisher: {
"@type": "Organization",
+12 -10
View File
@@ -8,7 +8,6 @@ import {
SOURCE_REPO_URL,
SITE_NAME,
SITE_URL,
TWITTER_HANDLE,
} from "@/lib/seo";
export const viewport = {
@@ -41,12 +40,12 @@ export const metadata: Metadata = {
"wayland voice dictation",
"x11 speech recognition",
"open source dictation software",
"voice typing ubuntu",
"voice typing fedora",
"voice typing arch linux",
"voice dictation void linux",
"void linux speech to text",
"xbps voice typing",
],
authors: [{ name: "Jatin K Malik", url: "https://github.com/jatinkrmalik" }],
creator: "Jatin K Malik",
authors: [{ name: "Xavier Karma", url: "https://bongbetic.com" }],
creator: "Xavier Karma",
publisher: SITE_NAME,
icons: {
icon: [
@@ -80,7 +79,7 @@ export const metadata: Metadata = {
siteName: SITE_NAME,
title: "Voculinux: Offline Speech to Text & Voice Dictation for Linux",
description:
"Voice dictation for Linux with local engines or an optional Remote API. Install in minutes and dictate in any app on Ubuntu, Fedora, Arch, X11, or Wayland.",
"Voice dictation for Linux with local engines or an optional Remote API. Install from the signed Void Linux repository and dictate in any app on X11 or Wayland.",
images: [
{
url: DEFAULT_OG_IMAGE_PATH,
@@ -96,8 +95,6 @@ export const metadata: Metadata = {
description:
"Speech-to-text for Linux with whisper.cpp and VOSK. Local engines by default, optional Remote API, open source.",
images: [DEFAULT_OG_IMAGE_PATH],
creator: TWITTER_HANDLE,
site: TWITTER_HANDLE,
},
robots: {
index: true,
@@ -127,7 +124,12 @@ const organizationJsonLd = {
url: absoluteUrl("/"),
logo: absoluteUrl("/voculinux.png"),
description: "Open-source, offline voice dictation software for Linux.",
sameAs: [SOURCE_REPO_URL, "https://bongbetic.com"],
parentOrganization: {
"@type": "Organization",
name: "Bongbetic",
url: "https://bongbetic.com",
},
sameAs: [SOURCE_REPO_URL],
};
const webSiteJsonLd = {
+8 -464
View File
@@ -1,470 +1,14 @@
import Link from "next/link";
import { type Metadata } from "next";
import type { ReactNode } from "react";
import {
AlertTriangle,
ChevronRight,
ExternalLink,
Lock,
Server,
} from "lucide-react";
import { SeoSubpageShell } from "@/components/seo-subpage-shell";
import { absoluteUrl, buildPageMetadata } from "@/lib/seo";
const PAGE_PATH = "/local-gateway";
const PAGE_TITLE = "Run a Local VocaGateway for Linux Dictation";
const PAGE_DESCRIPTION =
"Start a local VocaGateway from Voculinux Settings with podman or docker, connect it via Remote API, and learn why gateway mode is not on-device transcription.";
const comparisonRows = [
{
mode: "Local engine (default whisper.cpp)",
audio: "Stays on this machine; transcribed in the app",
setup: "Download a model once",
network: "None for transcription",
},
{
mode: "Local VocaGateway on 127.0.0.1",
audio: "Sent over HTTP to the gateway container on this machine",
setup: "podman or docker with Compose",
network: "Loopback only by default",
},
{
mode: "Gateway or Remote API on another machine",
audio: "Leaves this machine for the endpoint you configured",
setup: "Server URL and token in Remote Server",
network: "Your LAN, VPN or the internet",
},
];
const statusRows = [
{ status: "Stopped", meaning: "Not running." },
{
status: "Starting",
meaning: "Compose up in progress, or the live probe is not green yet.",
},
{ status: "Live", meaning: "The gateway answers /health/live." },
{
status: "Pairable",
meaning:
"Live, plus a non-loopback phone URL and bearer token for pairing.",
},
{
status: "Ready",
meaning: "/health/ready returns 200 (a model is selected in the gateway).",
},
{
status: "Error",
meaning: "Start, stop or probe failure; the subtitle says which.",
},
];
const steps = [
{
name: "Open the Advanced section",
text: "Open Settings → Speech Model and expand Advanced. The Local VocaGateway group sits next to Remote Server.",
},
{
name: "Run the gateway",
text: "Click Run VocaGateway locally. The first start builds the local image vocagateway:v0.1.0 from the pinned tag, so it takes longer than later starts.",
},
{
name: "Wait for Ready",
text: "Watch the Status row. Select a model in the gateway's own web UI; the status shows Ready once the gateway reports it can transcribe.",
},
{
name: "Use this Gateway",
text: "Click Use this Gateway. Voculinux fills Remote Server with the gateway URL and bearer token, sets the endpoint to /v1/audio/transcriptions and switches the engine to Remote API.",
},
{
name: "Dictate and verify",
text: "Use Test Connection in Remote Server, then dictate as usual with your normal shortcut.",
},
];
const faqs = [
{
question: "Is a local gateway the same as on-device dictation?",
answer:
"No. Even on 127.0.0.1, audio leaves the Voculinux client and is transcribed by a separate gateway process over HTTP. The on-device path is a local engine such as whisper.cpp, which stays the default.",
},
{
question: "Does quitting Voculinux stop the gateway?",
answer:
"No. The pinned compose file uses restart: unless-stopped, so the container keeps running after Quit. Use Stop local Gateway in Settings or the tray menu.",
},
{
question: "Can I run it from the Flatpak?",
answer:
"No. The Flatpak sandbox cannot reach the host podman or docker socket. Use the native package or AppImage, or start VocaGateway on the host yourself and point Remote Server at it.",
},
{
question: "Do I need a Voca account?",
answer:
"No. VocaGateway is optional self-hosted software. There is no Voca account and no hosted Voca cloud.",
},
{
question: "How do I go back to on-device transcription?",
answer:
"Choose a local engine again under Speech Engine in Settings → Speech Model → Advanced. Stop the gateway too if you no longer need it.",
},
];
import { ChannelNotice } from "@/components/channel-notice";
import { buildPageMetadata } from "@/lib/seo";
export const metadata: Metadata = buildPageMetadata({
title: `${PAGE_TITLE} | Voculinux`,
description: PAGE_DESCRIPTION,
path: PAGE_PATH,
keywords: [
"vocagateway linux",
"local speech gateway podman",
"self hosted dictation gateway",
"voculinux remote api gateway",
"docker speech to text linux dictation",
"phone dictation gateway linux",
],
title: "Local Gateway: Not Published for Voculinux",
description:
"The local gateway is not published for Voculinux. The Void Linux package ships the on-device engines; remote engines are configured in Settings.",
path: "/local-gateway",
});
function InlineCode({ children }: { children: ReactNode }) {
return (
<code className="rounded bg-muted px-1.5 py-0.5 font-mono text-[0.85em] text-foreground">
{children}
</code>
);
}
export default function LocalGatewayPage() {
const jsonLd = {
"@context": "https://schema.org",
"@graph": [
{
"@type": "TechArticle",
headline: PAGE_TITLE,
description: PAGE_DESCRIPTION,
dateModified: "2026-11-01",
author: {
"@type": "Person",
name: "Jatin K Malik",
url: "https://github.com/jatinkrmalik",
},
publisher: {
"@type": "Organization",
name: "Voculinux",
logo: {
"@type": "ImageObject",
url: absoluteUrl("/voculinux.png"),
},
},
mainEntityOfPage: absoluteUrl(PAGE_PATH),
},
{
"@type": "HowTo",
name: "Run a local VocaGateway from Voculinux Settings",
step: steps.map((step, index) => ({
"@type": "HowToStep",
position: index + 1,
name: step.name,
text: step.text,
})),
},
{
"@type": "FAQPage",
mainEntity: faqs.map((faq) => ({
"@type": "Question",
name: faq.question,
acceptedAnswer: { "@type": "Answer", text: faq.answer },
})),
},
],
};
return (
<SeoSubpageShell>
<script
type="application/ld+json"
dangerouslySetInnerHTML={{ __html: JSON.stringify(jsonLd) }}
/>
<section>
<p className="subpage-kicker">Added in v0.18.0 · optional</p>
<h1 className="mb-5 font-display text-4xl font-semibold tracking-tight sm:text-5xl">
Run a local VocaGateway from Voculinux
</h1>
<p className="mb-6 max-w-4xl text-lg text-muted-foreground">
Voculinux can start{" "}
<a
href="https://vocagateway.vocahq.com/"
target="_blank"
rel="noopener noreferrer"
className="text-primary underline"
>
VocaGateway
</a>
, the self-hosted speech gateway in the Voca family, as a podman or
docker container from its own Settings. It is a power-user path for
people who want one transcription service shared by the desktop and a
paired phone.
</p>
</section>
<section className="bg-primary/5 mb-12 rounded-[12px] border border-border p-6">
<h2 className="mb-3 flex items-center gap-2 text-xl font-semibold">
<Lock className="h-5 w-5 text-primary" aria-hidden="true" />
Gateway mode is not the on-device engine
</h2>
<p className="text-sm text-muted-foreground">
With a local engine, audio is transcribed on this machine by Voculinux
itself. In gateway mode it is sent over HTTP to the gateway, even when
that gateway runs on <InlineCode>127.0.0.1</InlineCode>. If you point
Voculinux at a gateway on another machine, your audio leaves this
machine for that endpoint. Keep it on a trusted LAN, an encrypted
private network such as Tailscale, or HTTPS, and never expose port
8765 to the public internet. Enabling the gateway does not change the
default engine; whisper.cpp stays on-device until you click Use this
Gateway.
</p>
</section>
<section className="mb-12 rounded-[12px] border border-border bg-background p-6">
<h2 className="mb-4 font-display text-2xl font-semibold">
Where your audio goes
</h2>
<div className="overflow-x-auto">
<table className="w-full text-left text-sm">
<thead>
<tr className="border-b border-border">
<th scope="col" className="pb-3 pr-4 font-semibold">
Mode
</th>
<th scope="col" className="pb-3 pr-4 font-semibold">
Audio
</th>
<th scope="col" className="pb-3 pr-4 font-semibold">
Setup
</th>
<th scope="col" className="pb-3 font-semibold">
Network
</th>
</tr>
</thead>
<tbody>
{comparisonRows.map((row) => (
<tr key={row.mode} className="border-b border-border align-top">
<th scope="row" className="py-3 pr-4 font-medium">
{row.mode}
</th>
<td className="py-3 pr-4 text-muted-foreground">
{row.audio}
</td>
<td className="py-3 pr-4 text-muted-foreground">
{row.setup}
</td>
<td className="py-3 text-muted-foreground">{row.network}</td>
</tr>
))}
</tbody>
</table>
</div>
</section>
<section className="mb-12 rounded-[12px] border border-border bg-background p-6">
<h2 className="mb-4 flex items-center gap-2 font-display text-2xl font-semibold">
<Server className="h-5 w-5 text-primary" aria-hidden="true" />
Requirements
</h2>
<ul className="list-disc space-y-2 pl-5 text-sm text-muted-foreground">
<li>
podman (preferred) or docker, with a working Compose plugin, or{" "}
<InlineCode>podman-compose</InlineCode> /{" "}
<InlineCode>docker-compose</InlineCode>. If the engine is present
but Compose is missing, Settings tells you which to install.
</li>
<li>
A native install or the AppImage. The AppImage does not bundle a
container engine; it uses the host&apos;s podman or docker socket
when available.
</li>
<li>
Not the Flatpak: its sandbox cannot reach the host container socket,
so the controls are blocked there.
</li>
</ul>
</section>
<section className="mb-12">
<h2 className="mb-4 font-display text-2xl font-semibold">Set it up</h2>
<ol className="space-y-4">
{steps.map((step, index) => (
<li
key={step.name}
className="rounded-[12px] border border-border bg-background p-5"
>
<div className="mb-2 flex items-center gap-3">
<span className="flex h-7 w-7 items-center justify-center rounded-full bg-primary text-sm font-semibold text-primary-foreground">
{index + 1}
</span>
<h3 className="font-semibold">{step.name}</h3>
</div>
<p className="ml-10 text-sm text-muted-foreground">{step.text}</p>
</li>
))}
</ol>
<h3 className="mb-2 mt-6 font-semibold">Status labels</h3>
<div className="overflow-x-auto rounded-[12px] border border-border bg-background p-5">
<table className="w-full text-left text-sm">
<tbody>
{statusRows.map((row) => (
<tr
key={row.status}
className="border-b border-border last:border-0"
>
<th scope="row" className="py-2 pr-4 font-medium">
{row.status}
</th>
<td className="py-2 text-muted-foreground">{row.meaning}</td>
</tr>
))}
</tbody>
</table>
</div>
<p className="mt-3 text-sm text-muted-foreground">
Pairable can appear before Ready, so a phone can pair while a model is
still downloading.
</p>
</section>
<section className="mb-12 rounded-[12px] border border-border bg-background p-6">
<h2 className="mb-4 font-display text-2xl font-semibold">
What Voculinux sets up
</h2>
<ul className="list-disc space-y-2 pl-5 text-sm text-muted-foreground">
<li>
A checkout of VocaGateway pinned to the{" "}
<InlineCode>v0.1.0</InlineCode> tag under your XDG cache, run as the
Compose project <InlineCode>vocagateway</InlineCode> with the{" "}
<InlineCode>cpu</InlineCode> profile. It never floats on{" "}
<InlineCode>latest</InlineCode>;{" "}
<InlineCode>VOCAGATEWAY_IMAGE</InlineCode> overrides the image.
</li>
<li>
A private bootstrap token in{" "}
<InlineCode>~/.config/voculinux/gateway_embed/</InlineCode> with
mode 600, plus a matching Compose <InlineCode>.env</InlineCode>.
</li>
<li>
A port published on <InlineCode>127.0.0.1:8765</InlineCode> by
default, reachable from this desktop only.
</li>
<li>
The usual Remote API settings. Use this Gateway writes the URL,
token, endpoint and the model name{" "}
<InlineCode>whisper-1</InlineCode> into the same{" "}
<InlineCode>remote_api_*</InlineCode> keys described on the{" "}
<Link href="/remote-api/" className="text-primary underline">
Remote API page
</Link>
. The token is stored in plain JSON in{" "}
<InlineCode>config.json</InlineCode>, like any Remote API key.
</li>
</ul>
</section>
<section className="mb-12 rounded-[12px] border border-border bg-background p-6">
<h2 className="mb-4 flex items-center gap-2 font-display text-2xl font-semibold">
<AlertTriangle className="h-5 w-5 text-primary" aria-hidden="true" />
LAN access, stopping and gotchas
</h2>
<h3 className="mb-2 font-semibold">Allow LAN access for Phone</h3>
<p className="mb-4 text-sm text-muted-foreground">
This switch publishes the gateway on{" "}
<InlineCode>0.0.0.0:8765</InlineCode> and advertises a LAN URL for
phone pairing. Open port 8765 in your firewall only on networks you
trust. If the switch fails, Compose keeps the previous bind and the
switch snaps back with an error; stop and run again, or retry.
</p>
<h3 className="mb-2 font-semibold">Quit does not stop it</h3>
<p className="mb-4 text-sm text-muted-foreground">
The container keeps running after you quit Voculinux, including a
LAN-published one. Use Stop local Gateway in Settings or the tray
menu, which appears whenever Compose is running. Stopping keeps the
named data volume, so downloaded models survive. Voculinux does not
start the gateway on login.
</p>
<h3 className="mb-2 font-semibold">Other machines</h3>
<p className="text-sm text-muted-foreground">
You can also run VocaGateway on a stronger box and enter its address
in Remote Server by hand. Audio then crosses your network, so prefer a
trusted LAN, Tailscale or HTTPS. Read the{" "}
<a
href="https://vocagateway.vocahq.com/"
target="_blank"
rel="noopener noreferrer"
className="inline-flex items-center gap-1 text-primary underline"
>
VocaGateway documentation
<ExternalLink className="h-3 w-3" aria-hidden="true" />
</a>{" "}
for server-side setup.
</p>
</section>
<section className="mb-12">
<h2 className="mb-4 font-display text-2xl font-semibold">FAQ</h2>
<div className="space-y-4">
{faqs.map((faq) => (
<div
key={faq.question}
className="rounded-[12px] border border-border bg-background p-5"
>
<h3 className="mb-2 font-semibold">{faq.question}</h3>
<p className="text-sm text-muted-foreground">{faq.answer}</p>
</div>
))}
</div>
<p className="mt-4 text-sm text-muted-foreground">
Related:{" "}
<Link href="/offline/" className="text-primary underline">
offline dictation
</Link>
,{" "}
<Link href="/privacy/" className="text-primary underline">
privacy
</Link>
,{" "}
<Link href="/flatpak/" className="text-primary underline">
Flatpak limitations
</Link>{" "}
and{" "}
<Link href="/gpu-acceleration/" className="text-primary underline">
GPU acceleration
</Link>{" "}
for faster on-device engines.
</p>
</section>
<section className="rounded-[12px] border border-border bg-muted p-8">
<h2 className="mb-4 font-display text-2xl font-semibold">
Start on-device, add a gateway later
</h2>
<p className="mb-6 text-muted-foreground">
Install Voculinux with the default on-device engine. Add a local
VocaGateway only if you need a shared transcription service.
</p>
<div className="flex flex-wrap gap-4">
<Link
href="/install/"
className="inline-flex items-center gap-2 rounded-full bg-primary px-4 py-2 text-sm font-semibold text-primary-foreground"
>
Install Voculinux
<ChevronRight className="h-4 w-4" aria-hidden="true" />
</Link>
<Link
href="/remote-api/"
className="inline-flex items-center gap-2 rounded-lg border border-border bg-background px-4 py-2 text-sm font-semibold hover:bg-muted"
>
Remote API settings
</Link>
</div>
</section>
</SeoSubpageShell>
);
export default function LocalGatewayNoticePage() {
return <ChannelNotice title="Local gateway" />;
}
+2 -2
View File
@@ -155,8 +155,8 @@ export default function MultilingualDictationPage() {
dateModified: "2026-11-01",
author: {
"@type": "Person",
name: "Jatin K Malik",
url: "https://github.com/jatinkrmalik",
name: "Xavier Karma",
url: "https://bongbetic.com",
},
publisher: {
"@type": "Organization",
-1
View File
@@ -15,7 +15,6 @@ const quickLinks = [
{ href: "/faq/", label: "FAQ" },
{ href: "/compare/", label: "Compare engines" },
{ href: "/troubleshooting/", label: "Troubleshooting" },
{ href: "/changelog/", label: "Changelog" },
];
// 404 is a fine number. These are all true; check them in a shell.
+2 -2
View File
@@ -102,8 +102,8 @@ export default function OfflinePage() {
dateModified: "2026-02-22",
author: {
"@type": "Person",
name: "Jatin K Malik",
url: "https://github.com/jatinkrmalik",
name: "Xavier Karma",
url: "https://bongbetic.com",
},
publisher: {
"@type": "Organization",
+6 -6
View File
@@ -111,8 +111,8 @@ export default function OpenSourcePage() {
dateModified: "2026-02-22",
author: {
"@type": "Person",
name: "Jatin K Malik",
url: "https://github.com/jatinkrmalik",
name: "Xavier Karma",
url: "https://bongbetic.com",
},
publisher: {
"@type": "Organization",
@@ -141,10 +141,10 @@ export default function OpenSourcePage() {
Free & Open Source Voice Dictation
</h1>
<p className="mb-8 max-w-4xl text-lg text-muted-foreground">
Voculinux is 100% open source under the AGPL-3.0 license, a hard fork of
Vocalinux (upstream, by VocaHQ). No subscriptions, no vendor lock-in, no
hidden costs. Local engines keep audio on the machine. Remote API is
optional.
Voculinux is 100% open source under the AGPL-3.0 license and is a fork
of an upstream project, credited in the site footer. No subscriptions,
no vendor lock-in, no hidden costs. Local engines keep audio on the
machine. Remote API is optional.
</p>
</section>
+57 -196
View File
@@ -5,25 +5,12 @@ import Link from "next/link";
import Image from "next/image";
import { SiteChrome } from "@/components/site-chrome";
import { TerminalBlock, TerminalPrompt } from "@/components/terminal-block";
import { SOURCE_REPO_URL, UPSTREAM_REPO_URL } from "@/lib/seo";
// The install and uninstall scripts below are fetched from upstream: the fork
// has no published raw script host yet. The install section labels this.
const UPSTREAM_REPO_PATH = UPSTREAM_REPO_URL.replace("https://github.com/", "");
const UPSTREAM_RAW_BASE = `https://raw.githubusercontent.com/${UPSTREAM_REPO_PATH}`;
const installCommands = {
interactiveInstallCommand: `curl -fsSL ${UPSTREAM_RAW_BASE}/main/install.sh -o /tmp/vl.sh && bash /tmp/vl.sh --interactive`,
interactiveInstallDisplayCommand: `curl -fsSL \\
${UPSTREAM_RAW_BASE}/main/install.sh \\
-o /tmp/vl.sh && \\
bash /tmp/vl.sh --interactive`,
uninstallCommand: `curl -fsSL ${UPSTREAM_RAW_BASE}/main/uninstall.sh -o /tmp/vul.sh && bash /tmp/vul.sh`,
uninstallDisplayCommand: `curl -fsSL \\
${UPSTREAM_RAW_BASE}/main/uninstall.sh \\
-o /tmp/vul.sh && \\
bash /tmp/vul.sh`,
};
import { absoluteUrl, SOURCE_REPO_URL } from "@/lib/seo";
import {
VOID_INSTALL_COMMANDS,
VOID_KEY_FINGERPRINT,
VOID_UPDATE_COMMAND,
} from "@/lib/void-channel";
const homeJsonLd = [
{
@@ -45,11 +32,11 @@ const homeJsonLd = [
softwareVersion: "0.18.5",
author: {
"@type": "Person",
name: "Jatin K Malik",
url: "https://github.com/jatinkrmalik",
name: "Xavier Karma",
url: "https://bongbetic.com",
},
url: "https://voculinux.com/",
downloadUrl: SOURCE_REPO_URL,
downloadUrl: absoluteUrl("/install/"),
screenshot: "https://voculinux.com/og-image.png",
featureList: [
"Local speech recognition with whisper.cpp, Faster Whisper, Whisper, VOSK, and Parakeet",
@@ -60,8 +47,7 @@ const homeJsonLd = [
"Left/right modifier key distinction for shortcuts",
"Optional voice commands with VOSK-aware defaults",
"Searchable sidebar settings",
"AppImage, .deb, .rpm, and Flatpak packages for x86_64 and aarch64",
"Snap package on the Ubuntu Snap Store",
"Signed xbps package for Void Linux (glibc, x86_64)",
"Expanded speech-language catalog including Hungarian (per-engine availability)",
"Auto-capitalize sentences after punctuation",
"Trailing space after completed transcriptions for continuous dictation",
@@ -98,7 +84,7 @@ const homeJsonLd = [
name: "Does Voculinux collect usage telemetry?",
acceptedAnswer: {
"@type": "Answer",
text: "No. The installed app does not send usage telemetry or analytics events. Its update checker only asks GitHub Releases whether a newer version exists.",
text: "No. The installed app does not send usage telemetry or analytics events. Its update checker only asks the Voculinux release host whether a newer version exists.",
},
},
{
@@ -106,7 +92,7 @@ const homeJsonLd = [
name: "Which Linux distributions are supported?",
acceptedAnswer: {
"@type": "Answer",
text: "Voculinux supports Ubuntu, Fedora, Debian, Arch, Linux Mint, Pop!_OS, and other modern Linux distributions across X11 and Wayland.",
text: "The published package is for Void Linux (glibc, x86_64), installed from a signed xbps repository. Voculinux runs on X11 and Wayland.",
},
},
{
@@ -138,27 +124,28 @@ const homeJsonLd = [
{
"@context": "https://schema.org",
"@type": "HowTo",
name: "Install Voculinux on Linux",
description: "Install offline voice dictation on Linux in a few minutes.",
name: "Install Voculinux on Void Linux",
description:
"Install offline voice dictation on Void Linux from the signed xbps repository.",
totalTime: "PT10M",
step: [
{
"@type": "HowToStep",
name: "Run the install command",
text: "Copy the install command from voculinux.com and run it in terminal.",
url: "https://voculinux.com/#install",
name: "Trust the key and add the repository",
text: "As root, fetch the repository key into /var/db/xbps/keys and write the xbps.d repository line.",
url: absoluteUrl("/install/"),
},
{
"@type": "HowToStep",
name: "Complete the guided setup",
text: "Pick your speech engine and model size in the interactive installer.",
url: "https://voculinux.com/#install",
name: "Install the package",
text: "Run xbps-install -S voculinux.",
url: absoluteUrl("/install/"),
},
{
"@type": "HowToStep",
name: "Start dictating",
text: "Launch voculinux and hold Right Alt (Option) to begin dictation, or choose toggle mode in Settings.",
url: "https://voculinux.com/#install",
url: absoluteUrl("/install/"),
},
],
},
@@ -229,10 +216,7 @@ function formatPanelClock(date: Date): string {
}
const ribbonItems = [
"Ubuntu",
"Fedora",
"Debian",
"Arch",
"Void Linux",
"X11",
"Wayland",
"on-device",
@@ -245,12 +229,6 @@ const ribbonItems = [
export default function HomePage() {
const [panelClock, setPanelClock] = useState("");
const {
interactiveInstallCommand,
interactiveInstallDisplayCommand,
uninstallCommand,
uninstallDisplayCommand,
} = installCommands;
useEffect(() => {
const tick = () => setPanelClock(formatPanelClock(new Date()));
@@ -668,8 +646,8 @@ export default function HomePage() {
Your voice has a path. We show you which one.
</h2>
<p className="section-lede">
Voculinux does not need a Voca cloud to transcribe. The important
boundary is the engine you selected.
Voculinux does not need a cloud service to transcribe. The
important boundary is the engine you selected.
</p>
</div>
<ol className="route">
@@ -716,9 +694,9 @@ export default function HomePage() {
<article className="boundary">
<h3>What uses a network</h3>
<p>
Model downloads, the update check against GitHub Releases, and
any Remote API host you add. Nothing is sent to a Voca speech
cloud.
Model downloads, the update check against the Voculinux release
host, and any Remote API host you add. The default engines send
no audio off the machine.
</p>
</article>
<article className="boundary">
@@ -736,138 +714,39 @@ export default function HomePage() {
<div className="shell min-w-0">
<div className="section-head">
<h2 className="section-title" id="install-title">
Install in one command.
Install from the Void repository.
</h2>
<p className="section-lede">
The interactive installer detects hardware, lets you pick an
engine, and wires the desktop app. Ubuntu, Fedora, Debian, Arch,
and openSUSE. Native .deb, .rpm, Snap, and Flatpak packages are
planned for every fork release.
Voculinux is packaged for Void Linux (glibc, x86_64) as a signed
xbps repository. Trust the key, register the repository, then
install the package. This is a third-party repository published by
this project, not by Void Linux upstream.
</p>
</div>
<TerminalBlock
command={interactiveInstallCommand}
displayCommand={interactiveInstallDisplayCommand}
label="install.sh --interactive"
command={VOID_INSTALL_COMMANDS}
displayCommand={VOID_INSTALL_COMMANDS}
label="xbps, as root"
/>
<p className="section-lede">
Then launch <code>vocalinux</code> — the upstream binary the script
installs today. Fork releases will ship <code>voculinux</code>.
<p className="mt-4 text-sm text-muted-foreground">
Repository key fingerprint: <code>{VOID_KEY_FINGERPRINT}</code>. If
xbps asks about a fingerprint, answer <code>N</code> unless it
matches exactly. Do not add <code>-y</code> to the first sync.
</p>
<p className="mt-4 text-sm text-muted-foreground">
Fork status: this command fetches the upstream install script, so it
installs Vocalinux (upstream), not Voculinux. Fork release artefacts
are not published yet.
Update later with <code>{VOID_UPDATE_COMMAND}</code>. Other channels
are not published for Voculinux (Void only). The full install notes
are in the <Link href="/install/">Void install guide</Link>. The
source, issues and releases are on the{" "}
<a
href={SOURCE_REPO_URL}
rel="noopener noreferrer"
className="font-semibold text-primary hover:underline"
>
fork host
</a>
.
</p>
<div className="install-alts">
<a
className="install-alt"
href={`${SOURCE_REPO_URL}/releases`}
rel="noopener noreferrer"
>
<p className="install-alt-kicker">Void Linux</p>
<h3>xbps repository</h3>
<p>
Signed xbps repository data will ship with releases once the
packaging pipeline lands. Until then, use the install script or
build from source.
</p>
</a>
<a
className="install-alt"
href={`${UPSTREAM_REPO_URL}/releases`}
rel="noopener noreferrer"
>
<p className="install-alt-kicker">No installer</p>
<h3>AppImage</h3>
<p>
x86_64 or aarch64 from upstream GitHub Releases. No root. Host
text-injection tools are still required.
</p>
</a>
<a
className="install-alt"
href={`${SOURCE_REPO_URL}/src/branch/main/docs/INSTALL.md#distro-packages-deb--rpm`}
rel="noopener noreferrer"
>
<p className="install-alt-kicker">New</p>
<h3>Native packages</h3>
<p>
.deb for Debian 12+ and Ubuntu 24.04+. .rpm for Fedora. x86_64
and aarch64 on every fork release once published.
</p>
</a>
<a
className="install-alt"
href={`${SOURCE_REPO_URL}/src/branch/main/docs/INSTALL.md#flatpak`}
rel="noopener noreferrer"
>
<p className="install-alt-kicker">Auto-updates</p>
<h3>Flatpak</h3>
<p>
Install once from the fork Flatpak remote once published and{" "}
<code>flatpak update</code> carries each stable release. Or
sideload the .flatpak bundle.
</p>
</a>
<div className="install-alt">
<p className="install-alt-kicker">Snap Store</p>
<h3>Snap</h3>
<p>
<code>snap install voculinux --edge</code>, once the fork Snap
is published (not published yet). amd64 only; a .snap will be
attached to fork releases too.
</p>
</div>
<div className="install-alt">
<p className="install-alt-kicker">Arch</p>
<h3>AUR</h3>
<p>
<code>yay -S voculinux</code>, or voculinux-bin for the prebuilt
package. Fork packages are not published yet.
</p>
</div>
<a
className="install-alt"
href={`${SOURCE_REPO_URL}/src/branch/main/docs/INSTALL.md#from-source`}
rel="noopener noreferrer"
>
<p className="install-alt-kicker">From source</p>
<h3>Build it yourself</h3>
<p>
Clone the repo and run <code>./install.sh</code>. The install
guide in the Voculinux repository has the full source path.
</p>
</a>
</div>
<p className="mt-4 text-sm text-muted-foreground">
Fork status: packaged artefacts (AppImage, .deb/.rpm, Flatpak, Snap,
AUR, xbps) are not published yet. The AppImage card links upstream
artefacts; the Snap and AUR cards name the planned fork packages and
do not link anywhere until the first fork release lands.
</p>
<p className="section-link" style={{ marginTop: "1.6rem" }}>
<Link href="/install/ubuntu/">Ubuntu guide</Link>
{" · "}
<Link href="/install/fedora/">Fedora guide</Link>
{" · "}
<Link href="/install/arch/">Arch guide</Link>
{" · "}
<Link href="/install/">All distro notes</Link>
</p>
<div className="section-head" style={{ marginTop: "3.5rem" }}>
<h3
className="section-title"
style={{ fontSize: "clamp(1.6rem, 3vw, 2.2rem)" }}
>
Uninstall
</h3>
</div>
<TerminalBlock
command={uninstallCommand}
displayCommand={uninstallDisplayCommand}
label="uninstall.sh"
/>
</div>
</section>
@@ -896,15 +775,16 @@ export default function HomePage() {
{
question: "Does Voculinux collect usage telemetry?",
answer:
"No. The installed app does not send usage telemetry or analytics events. Its update checker only asks GitHub Releases whether a newer version exists.",
"No. The installed app does not send usage telemetry or analytics events. Its update checker only asks the Voculinux release host whether a newer version exists.",
},
{
question: "Which Linux distributions are supported?",
answer: (
<>
Ubuntu 24.04+, Debian 12+, Fedora 39+, Arch, openSUSE
Tumbleweed, and most modern desktops on X11 or Wayland.{" "}
<Link href="/install/">Install guides</Link>.
The published package is for Void Linux (glibc, x86_64),
installed from a signed xbps repository. Voculinux runs on
X11 and Wayland. <Link href="/install/">Install guide</Link>
.
</>
),
},
@@ -1010,25 +890,6 @@ export default function HomePage() {
Linux.
</p>
</div>
<div className="family-card">
<div className="family-card-top">
<span className="family-platform">
<img
src="/brand/platforms/linux.svg"
alt=""
width={22}
height={22}
/>
Upstream
</span>
<span className="chip">Provenance</span>
</div>
<h3>Vocalinux (upstream)</h3>
<p>
The upstream Linux dictation app this fork is ported from. Fixes
are taken by hand, one at a time.
</p>
</div>
<a
className="family-card"
href="https://bongbetic.com"
+25 -60
View File
@@ -12,15 +12,10 @@ import {
import { SeoSubpageShell } from "@/components/seo-subpage-shell";
import { absoluteUrl, buildPageMetadata } from "@/lib/seo";
const installCommands = [
"curl -fsSL https://raw.githubusercontent.com/VocaHQ/vocalinux/main/install.sh -o /tmp/vl.sh",
"bash /tmp/vl.sh --auto --engine=parakeet",
];
const setupSteps = [
{
title: "Install the Parakeet engine",
text: "Parakeet is an optional extra. Download the installer and run it with --engine=parakeet. The interactive engine menu does not list Parakeet, so pass the flag. The installer adds sherpa-onnx and points your config at the Parakeet engine.",
text: "Parakeet is an optional extra and is not in the Void package. Make sherpa-onnx importable from your own Python environment, as the Void install guide describes. Voculinux lists Parakeet in Settings once sherpa-onnx can be imported.",
},
{
title: "Open Settings → Speech Model",
@@ -66,32 +61,15 @@ const models = [
const packages = [
{
name: "install.sh / source install",
name: "Void package (voculinux)",
parakeet: false,
note: "Ships whisper.cpp only; add sherpa-onnx from your own Python environment",
},
{
name: "Source checkout (install.sh)",
parakeet: true,
note: "Use --engine=parakeet",
},
{
name: "AUR (voculinux)",
parakeet: true,
note: "Listed as an optional dependency: python-sherpa-onnx",
},
{
name: "AppImage (and AUR voculinux-bin)",
parakeet: false,
note: "Ships whisper.cpp only",
},
{ name: "Flatpak", parakeet: false, note: "Ships whisper.cpp only" },
{ name: "Snap", parakeet: false, note: "Ships whisper.cpp only" },
{
name: ".deb / .rpm",
parakeet: false,
note: "Thin packages; whisper.cpp on CPU",
},
{
name: "Nix flake",
parakeet: false,
note: "Parakeet extra is not packaged",
},
];
const comparison = [
@@ -127,8 +105,8 @@ const comparison = [
},
{
topic: "Packages",
parakeet: "install.sh, source, AUR optional dependency",
whisperCpp: "Every package",
parakeet: "Source checkouts, with sherpa-onnx added; not in the Void package",
whisperCpp: "Every build, the Void package included",
},
];
@@ -136,12 +114,12 @@ const faqs = [
{
question: "Is Parakeet better than whisper.cpp?",
answer:
"It depends on your languages and hardware. Parakeet is a strong CPU option if you dictate in English or one of the 25 European languages its v3 model covers. whisper.cpp stays the default because it covers the whole language catalog, can use a Vulkan or CUDA GPU, and ships in every package. Try both on your own voice and machine.",
"It depends on your languages and hardware. Parakeet is a strong CPU option if you dictate in English or one of the 25 European languages its v3 model covers. whisper.cpp stays the default because it covers the whole language catalog, can use a Vulkan or CUDA GPU, and ships in every build. Try both on your own voice and machine.",
},
{
question: "Does Parakeet send my audio anywhere?",
answer:
"No. Parakeet is a local engine. After the model download, recognition runs on your machine and audio is not sent to a server. Only the Remote API engine and VocaGateway send audio to an endpoint you configure.",
"No. Parakeet is a local engine. After the model download, recognition runs on your machine and audio is not sent to a server. Only the Remote API engine sends audio, and only to an endpoint you configure.",
},
{
question: "Why is the Language picker greyed out or ignored with Parakeet?",
@@ -149,9 +127,9 @@ const faqs = [
"Parakeet's language coverage comes from the model, not from the Voculinux language catalog. Voculinux treats the language as auto for Parakeet, so pick v2-english for English only or v3-european for the European set. Follow keyboard layout is not available for Parakeet.",
},
{
question: "Can I use Parakeet from the AppImage, Flatpak or Snap?",
question: "Can I use Parakeet from the Void package?",
answer:
"No. Those packages ship whisper.cpp only. Use the install.sh installer or a source install with --engine=parakeet, or the AUR package with python-sherpa-onnx installed.",
"Not as shipped. The Void package ships whisper.cpp only. Add sherpa-onnx from your own Python environment and select Parakeet in Settings, or use a source checkout with --engine=parakeet.",
},
{
question: "What is Orukeet?",
@@ -187,8 +165,8 @@ export default function ParakeetPage() {
dateModified: "2026-11-01",
author: {
"@type": "Person",
name: "Jatin K Malik",
url: "https://github.com/jatinkrmalik",
name: "Xavier Karma",
url: "https://bongbetic.com",
},
publisher: {
"@type": "Organization",
@@ -310,20 +288,10 @@ export default function ParakeetPage() {
<h2 className="mb-6 font-display text-2xl font-semibold">
Install and select Parakeet
</h2>
<div className="mb-6 rounded-lg bg-[color:var(--terminal)] p-4">
{installCommands.map((command) => (
<code
key={command}
className="block text-sm text-[color:var(--terminal-fg)]"
>
{command}
</code>
))}
</div>
<p className="mb-4 text-sm text-muted-foreground">
Fork status: these commands fetch the upstream install script, so they
install Vocalinux (upstream), not Voculinux. Fork release artefacts
are not published yet.
Parakeet is not in the Void package. Add sherpa-onnx from your own
Python environment, then select Parakeet. The Void install guide lists
the optional engines and where they come from.
</p>
<ol className="space-y-4">
{setupSteps.map((step, index) => (
@@ -548,10 +516,10 @@ export default function ParakeetPage() {
</div>
<p className="mt-4 max-w-4xl text-sm text-muted-foreground">
Pick Parakeet if you dictate in English or a supported European
language on a CPU-only machine and installed with install.sh, from
source or from the AUR. Stay on whisper.cpp if you use a language
outside that set, want GPU acceleration, rely on custom dictionary
terms, or installed the AppImage, Flatpak, Snap, .deb or .rpm.
language on a CPU-only machine and have sherpa-onnx available in your
Python environment. Stay on whisper.cpp if you use a language outside
that set, want GPU acceleration, rely on custom dictionary terms, or
use the Void package, which ships whisper.cpp only.
</p>
</section>
@@ -566,12 +534,9 @@ export default function ParakeetPage() {
</h3>
<p>
Settings lists only engines whose libraries are installed.
Parakeet appears once sherpa-onnx can be imported. Re-run the
installer with{" "}
<code className="rounded bg-muted px-1">--engine=parakeet</code>.
Installs of this engine through install.sh were broken before
v0.18.4, so use a current installer; on Arch, install
python-sherpa-onnx.
Parakeet appears once sherpa-onnx can be imported by the Python
environment Voculinux runs in. Engine installs were broken before
v0.18.4, so use a current build.
</p>
</div>
<div>
+3 -9
View File
@@ -72,7 +72,7 @@ const faqs = [
{
question: "Does system-audio capture send anything online?",
answer:
"Not with a local engine. Audio from either path goes to the engine you selected, and local engines run on your machine. If you switch to the Remote API engine or a VocaGateway, audio is sent to the endpoint you configured.",
"Not with a local engine. Audio from either path goes to the engine you selected, and local engines run on your machine. If you switch to the Remote API engine, audio is sent to the endpoint you configured.",
},
{
question: "Which version added PipeWire capture?",
@@ -115,8 +115,8 @@ export default function PipeWirePage() {
dateModified: "2026-11-01",
author: {
"@type": "Person",
name: "Jatin K Malik",
url: "https://github.com/jatinkrmalik",
name: "Xavier Karma",
url: "https://bongbetic.com",
},
publisher: {
"@type": "Organization",
@@ -318,12 +318,6 @@ export default function PipeWirePage() {
A sink monitor records the full mix for that output, including
notification sounds and any other app playing at the time.
</li>
<li>
The Flatpak release notes list PipeWire system-audio capture as a
native-install path. Inside the Snap, Settings → Audio lists the
sound server&apos;s microphones through{" "}
<InlineCode>pactl</InlineCode> instead.
</li>
</ul>
</section>
+3 -3
View File
@@ -27,8 +27,8 @@ export default function PrivacyPage() {
dateModified: "2026-11-06",
author: {
"@type": "Person",
name: "Jatin K Malik",
url: "https://github.com/jatinkrmalik",
name: "Xavier Karma",
url: "https://bongbetic.com",
},
publisher: {
"@type": "Organization",
@@ -141,7 +141,7 @@ export default function PrivacyPage() {
</p>
<p>
The installed app does use the network for model downloads and for
its update check against GitHub Releases. Neither request carries
its update check against the Voculinux release host. Neither request carries
your audio or transcripts. Settings has a proxy page for both.
</p>
<p>
+2 -2
View File
@@ -83,8 +83,8 @@ export default function RemoteApiPage() {
dateModified: "2026-06-11",
author: {
"@type": "Person",
name: "Jatin K Malik",
url: "https://github.com/jatinkrmalik",
name: "Xavier Karma",
url: "https://bongbetic.com",
},
publisher: {
"@type": "Organization",
+2 -2
View File
@@ -115,8 +115,8 @@ export default function RsiPreventionPage() {
dateModified: "2026-02-22",
author: {
"@type": "Person",
name: "Jatin K Malik",
url: "https://github.com/jatinkrmalik",
name: "Xavier Karma",
url: "https://bongbetic.com",
},
publisher: {
"@type": "Organization",
+3 -3
View File
@@ -97,7 +97,7 @@ const settingsShots: Screenshot[] = [
alt: "Voculinux About settings page with update checker",
title: "About",
description:
"Version info plus Stable/Nightly update checks from GitHub Releases.",
"Version info plus stable and nightly update checks from the Voculinux release host.",
...SETTINGS_SIZE,
},
];
@@ -126,8 +126,8 @@ export default function ScreenshotsPage() {
dateModified: "2026-08-03", // refreshed after About moved into Settings sidebar
author: {
"@type": "Person",
name: "Jatin K Malik",
url: "https://github.com/jatinkrmalik",
name: "Xavier Karma",
url: "https://bongbetic.com",
},
publisher: {
"@type": "Organization",
+2 -2
View File
@@ -122,8 +122,8 @@ export default function ShortcutsPage() {
dateModified: "2026-03-30",
author: {
"@type": "Person",
name: "Jatin K Malik",
url: "https://github.com/jatinkrmalik",
name: "Xavier Karma",
url: "https://bongbetic.com",
},
publisher: {
"@type": "Organization",
+2 -2
View File
@@ -106,8 +106,8 @@ export default function TranscribeAudioFilesPage() {
dateModified: "2026-11-01",
author: {
"@type": "Person",
name: "Jatin K Malik",
url: "https://github.com/jatinkrmalik",
name: "Xavier Karma",
url: "https://bongbetic.com",
},
publisher: {
"@type": "Organization",
+4 -4
View File
@@ -102,7 +102,7 @@ const troubleshootingItems = [
"Silence-only buffers still appear",
],
solutions: [
'Install neural VAD support: pip install "voculinux[vad]"',
"Add onnxruntime to your own Python environment for neural VAD; the Void package does not ship it",
"Restart Voculinux after installing ONNX Runtime support",
"Use the VAD sensitivity setting in Recognition to tune quiet speech detection",
"If Silero cannot load, Voculinux falls back safely to amplitude-based VAD",
@@ -114,7 +114,7 @@ const troubleshootingItems = [
solutions: [
"Ensure you have internet connectivity for downloading dependencies",
"Run with sufficient permissions (don't use sudo unless specifically needed)",
"Check your distribution is supported (Ubuntu 24.04+, Debian 12+, Fedora 39+, Arch)",
"Check the Python version is 3.11 or newer; the published package targets Void Linux (glibc, x86_64)",
"Install system dependencies manually: sudo apt install python3-pip python3-gi python3-venv",
"Check disk space: df -h",
],
@@ -190,8 +190,8 @@ export default function TroubleshootingPage() {
dateModified: "2026-06-11",
author: {
"@type": "Person",
name: "Jatin K Malik",
url: "https://github.com/jatinkrmalik",
name: "Xavier Karma",
url: "https://bongbetic.com",
},
publisher: {
"@type": "Organization",
+2 -2
View File
@@ -114,8 +114,8 @@ export default function UseCasesPage() {
dateModified: "2026-02-19",
author: {
"@type": "Person",
name: "Jatin K Malik",
url: "https://github.com/jatinkrmalik",
name: "Xavier Karma",
url: "https://bongbetic.com",
},
publisher: {
"@type": "Organization",
+6 -10
View File
@@ -62,8 +62,8 @@ export default function VoiceActivityDetectionPage() {
dateModified: "2026-06-11",
author: {
"@type": "Person",
name: "Jatin K Malik",
url: "https://github.com/jatinkrmalik",
name: "Xavier Karma",
url: "https://bongbetic.com",
},
publisher: {
"@type": "Organization",
@@ -141,15 +141,11 @@ export default function VoiceActivityDetectionPage() {
</h2>
<div className="space-y-4 text-sm text-muted-foreground">
<p>
The official installer attempts to install neural VAD support. If
ONNX Runtime is not available, Voculinux logs the fallback and
continues with amplitude-based VAD.
Neural VAD needs onnxruntime. The Void package does not ship it, so
add onnxruntime to your own Python environment, as the Void install
guide describes. If ONNX Runtime is not available, Voculinux logs
the fallback and continues with amplitude-based VAD.
</p>
<div className="rounded-lg bg-[color:var(--terminal)] p-4">
<code className="text-[color:var(--terminal-fg)]">
pip install &quot;voculinux[vad]&quot;
</code>
</div>
<p>
The Recognition tab shows which backend is active. Higher VAD
sensitivity values are more responsive to quiet speech for both
+2 -2
View File
@@ -113,8 +113,8 @@ export default function VoiceTypingVsCodePage() {
dateModified: "2026-03-30",
author: {
"@type": "Person",
name: "Jatin K Malik",
url: "https://github.com/jatinkrmalik",
name: "Xavier Karma",
url: "https://bongbetic.com",
},
publisher: {
"@type": "Organization",
+2 -2
View File
@@ -88,8 +88,8 @@ export default function VsNerdDictationPage() {
dateModified: "2026-03-30",
author: {
"@type": "Person",
name: "Jatin K Malik",
url: "https://github.com/jatinkrmalik",
name: "Xavier Karma",
url: "https://bongbetic.com",
},
publisher: {
"@type": "Organization",
+2 -2
View File
@@ -132,8 +132,8 @@ export default function WaylandPage() {
dateModified: "2026-06-11",
author: {
"@type": "Person",
name: "Jatin K Malik",
url: "https://github.com/jatinkrmalik",
name: "Xavier Karma",
url: "https://bongbetic.com",
},
publisher: {
"@type": "Organization",
+2 -2
View File
@@ -84,8 +84,8 @@ export default function WhisperModelGuidePage() {
dateModified: "2026-03-30",
author: {
"@type": "Person",
name: "Jatin K Malik",
url: "https://github.com/jatinkrmalik",
name: "Xavier Karma",
url: "https://bongbetic.com",
},
publisher: {
"@type": "Organization",
+2 -2
View File
@@ -93,8 +93,8 @@ export default function WritersPage() {
dateModified: "2026-03-30",
author: {
"@type": "Person",
name: "Jatin K Malik",
url: "https://github.com/jatinkrmalik",
name: "Xavier Karma",
url: "https://bongbetic.com",
},
publisher: {
"@type": "Organization",
@@ -22,10 +22,12 @@ describe('DidYouMean', () => {
});
it('links to the closest page for the current URL', async () => {
window.history.pushState({}, '', '/install/ubunt');
// Suggestions come from the live navigation. Distro pages are notices now
// and are not listed, so the typo case uses a page that is still in nav.
window.history.pushState({}, '', '/compar');
render(<DidYouMean />);
// Jest's next/link ignores trailingSlash, so check the visible path.
expect(await screen.findByRole('link')).toHaveTextContent('/install/ubuntu/');
expect(await screen.findByRole('link')).toHaveTextContent('/compare/');
});
it('does not crash on a malformed percent-escape', () => {
+53
View File
@@ -0,0 +1,53 @@
import React from "react";
import Link from "next/link";
import { SeoSubpageShell } from "@/components/seo-subpage-shell";
import { SOURCE_REPO_URL } from "@/lib/seo";
interface ChannelNoticeProps {
title: string;
lede?: string;
children?: React.ReactNode;
}
// The one notice used by every page that describes a channel Voculinux does not
// publish. It names the Void channel, links the install hub and the fork source,
// and nothing else. Keep it free of install commands.
export function ChannelNotice({ title, lede, children }: ChannelNoticeProps) {
return (
<SeoSubpageShell>
<section>
<p className="subpage-kicker">Not published for Voculinux</p>
<h1>{title}</h1>
<p className="subpage-lede">
{lede ?? `${title} is not published for Voculinux (Void only).`}
</p>
<p className="mt-4 text-muted-foreground">
Voculinux is published for Void Linux (glibc, x86_64) as a signed xbps
repository. That is the only channel this project publishes.
</p>
{children ? (
<div className="mt-4 text-muted-foreground">{children}</div>
) : null}
<ul className="mt-6 space-y-3">
<li>
<Link
href="/install/"
className="font-semibold text-primary hover:underline"
>
Install Voculinux on Void Linux
</Link>
</li>
<li>
<a
href={SOURCE_REPO_URL}
rel="noopener noreferrer"
className="font-semibold text-primary hover:underline"
>
Voculinux source, issues and releases
</a>
</li>
</ul>
</section>
</SeoSubpageShell>
);
}
+1 -1
View File
@@ -68,7 +68,7 @@ export function SiteChrome({
variant === "home"
? [
{ href: "/screenshots/", label: "Screenshots" },
{ href: "/install/", label: "Distro guides" },
{ href: "/install/", label: "Void Linux install" },
...mobileExtraNav,
]
: mobileExtraNav;
+308 -140
View File
@@ -6,9 +6,111 @@ import {
SOURCE_REPO_URL,
UPSTREAM_REPO_URL,
} from "../seo";
import { socialLinks } from "../site-nav";
import {
VOID_INSTALL_COMMANDS,
VOID_KEY_FINGERPRINT,
VOID_REPO_URL,
VOID_UPDATE_COMMAND,
} from "../void-channel";
const ROOT = process.cwd();
const read = (...segments: string[]): string =>
fs.readFileSync(path.join(ROOT, ...segments), "utf-8");
const readPage = (...segments: string[]): string =>
fs.readFileSync(path.join(process.cwd(), "src", "app", ...segments), "utf-8");
read("src", "app", ...segments);
// Every non-test file under an absolute directory, as paths relative to ROOT.
const walk = (absDir: string): string[] =>
fs
.readdirSync(absDir, { withFileTypes: true })
.flatMap((entry) => {
const abs = `${absDir}/${entry.name}`;
if (entry.isDirectory()) {
return entry.name === "__tests__" ? [] : walk(abs);
}
return /\.test\./.test(entry.name)
? []
: [path.posix.relative(ROOT, abs)];
});
// Every non-test file under src, plus the sitemap, the README and PRODUCT.
const SCANNED_FILES = [
...walk(`${ROOT}/src`),
"public/sitemap.xml",
"README.md",
"PRODUCT.md",
];
// Upstream and unpublished-channel tokens. Each may appear only on a line that
// is explicitly allow-listed below, matched by file and exact trimmed text.
const CHANNEL_TOKENS: RegExp[] = [
/vocahq/i,
/github\.com\/VocaHQ/,
/raw\.githubusercontent/,
/flathub/i,
/snapcraft/i,
/aur\.archlinux/i,
/discord\.gg/i,
// \b keeps https://voculinux.com/ (which contains "x.com/") out of the match.
/\bx\.com\//i,
/mailto:/i,
/vocagateway/i,
/github:VocaHQ/,
/jatin/i,
];
// The old name, case-sensitive: only the origin sentence and the footer credit.
const OLD_NAME = /Vocalinux/;
interface AllowedLine {
file: string;
line: string;
}
const ALLOWED_TOKEN_LINES: AllowedLine[] = [
{
file: "src/lib/seo.ts",
line: 'export const UPSTREAM_REPO_URL = "https://github.com/VocaHQ/vocalinux";',
},
{
file: "src/components/site-chrome.tsx",
line: "Vocalinux (upstream, by VocaHQ)",
},
];
const ALLOWED_OLD_NAME_LINES: AllowedLine[] = [
{
file: "src/app/page.tsx",
line: "Voculinux is a hard fork of Vocalinux for Void Linux (glibc,",
},
{
file: "src/components/site-chrome.tsx",
line: "Vocalinux (upstream, by VocaHQ)",
},
];
const violationsIn = (file: string, text: string): string[] => {
const found: string[] = [];
text.split("\n").forEach((raw, index) => {
const line = raw.trim();
const where = `${file}:${index + 1}: ${line}`;
const allowed = (list: AllowedLine[]): boolean =>
list.some((entry) => entry.file === file && entry.line === line);
if (
CHANNEL_TOKENS.some((token) => token.test(line)) &&
!allowed(ALLOWED_TOKEN_LINES)
) {
found.push(where);
}
if (OLD_NAME.test(line) && !allowed(ALLOWED_OLD_NAME_LINES)) {
found.push(where);
}
});
return found;
};
describe("voculinux rebrand (ticket #24)", () => {
it("names the fork site", () => {
@@ -18,102 +120,226 @@ describe("voculinux rebrand (ticket #24)", () => {
it("points at the fork domain", () => {
expect(SITE_URL).toBe("https://voculinux.com");
});
it("credits the fork maintainer, not the upstream author, in metadata", () => {
const layout = read("src", "app", "layout.tsx");
expect(layout).toContain('name: "Xavier Karma"');
expect(layout).toContain('url: "https://bongbetic.com"');
expect(layout).not.toContain("Jatin");
expect(layout).toContain("sameAs: [SOURCE_REPO_URL]");
});
});
describe("fork install honesty (ticket #24, AC1)", () => {
describe("tight upstream and channel scan", () => {
it("finds no upstream or unpublished-channel token outside the allow-list", () => {
const violations = SCANNED_FILES.flatMap((file) =>
violationsIn(file, read(file)),
);
expect(violations).toEqual([]);
});
it("keeps every allow-list entry matched by a real line", () => {
const stale = [...ALLOWED_TOKEN_LINES, ...ALLOWED_OLD_NAME_LINES].filter(
({ file, line }) =>
!read(file)
.split("\n")
.some((raw) => raw.trim() === line),
);
expect(stale).toEqual([]);
});
it("flags a new upstream URL wherever it is added", () => {
expect(
violationsIn(
"src/app/new-page/page.tsx",
'<a href="https://github.com/VocaHQ/vocalinux/releases">releases</a>',
),
).toHaveLength(1);
expect(
violationsIn("src/app/new-page/page.tsx", "Join at x.com/vocahq"),
).toHaveLength(1);
});
it("does not flag the fork domain as an X link", () => {
expect(
violationsIn(
"src/app/new-page/page.tsx",
"https://voculinux.com/install/",
),
).toEqual([]);
});
it("uses the upstream constant only in seo.ts and the footer credit", () => {
const users = SCANNED_FILES.filter((file) =>
read(file).includes("UPSTREAM_REPO_URL"),
).sort();
expect(users).toEqual(["src/components/site-chrome.tsx", "src/lib/seo.ts"]);
});
it("labels the one footer credit as provenance", () => {
const chrome = read("src", "components", "site-chrome.tsx");
expect(chrome.match(/href=\{UPSTREAM_REPO_URL\}/g)).toHaveLength(1);
expect(chrome).toContain("Vocalinux (upstream, by VocaHQ)");
});
});
describe("Void install surfaces", () => {
const home = readPage("page.tsx");
const install = readPage("install", "page.tsx");
const distro = readPage("install", "[distro]", "page.tsx");
const nixos = readPage("install", "nixos", "page.tsx");
const flatpak = readPage("flatpak", "page.tsx");
const faq = readPage("faq", "page.tsx");
const fasterWhisper = readPage("faster-whisper", "page.tsx");
const parakeet = readPage("parakeet", "page.tsx");
it("does not promise packaged artefacts with every release", () => {
for (const [name, source] of [
["home", home],
["install", install],
] as const) {
expect(`${name}: ${source}`).not.toContain("ship with every release");
}
});
it("does not tell users to launch the fork binary after the upstream script", () => {
for (const [name, source] of [
["home", home],
["install", install],
] as const) {
expect(`${name}: ${source}`).not.toContain(
"Then launch <code>voculinux</code>",
);
}
});
it("discloses fork status on every packaged-artefact surface", () => {
for (const [name, source] of [
["home", home],
["install", install],
["distro", distro],
["flatpak", flatpak],
] as const) {
expect(`${name} discloses fork status: ${source}`).toContain(
"Fork status:",
);
expect(`${name} states artefacts are unpublished: ${source}`).toMatch(
/not published yet|not yet published|once published/,
);
}
});
it("does not present the signed xbps repository as available today", () => {
expect(`nixos: ${nixos}`).not.toContain("use the signed xbps repository");
});
it("discloses fork status on the faq install answer", () => {
expect(`faq discloses fork status: ${faq}`).toContain("Fork status:");
expect(`faq marks upstream script: ${faq}`).toMatch(
/upstream install script|upstream Vocalinux|upstream GitHub Releases/,
it("takes the signed xbps facts from one module", () => {
expect(VOID_KEY_FINGERPRINT).toBe(
"fa:d1:00:32:ce:b1:b9:fd:73:05:05:76:75:54:37:25",
);
expect(`faq states artefacts unpublished: ${faq}`).toMatch(
/not published yet|not yet published|once published/,
expect(VOID_REPO_URL).toBe(`${SOURCE_REPO_URL}/releases/download/latest`);
expect(VOID_INSTALL_COMMANDS).toContain(
`/var/db/xbps/keys/${VOID_KEY_FINGERPRINT}.plist`,
);
expect(VOID_INSTALL_COMMANDS).toContain(
`echo 'repository=${VOID_REPO_URL}' > /etc/xbps.d/voculinux.conf`,
);
expect(VOID_INSTALL_COMMANDS).toContain("xbps-install -S voculinux");
});
it("labels the upstream install script on engine option pages", () => {
for (const [name, source] of [
["faster-whisper", fasterWhisper],
["parakeet", parakeet],
] as const) {
expect(`${name} discloses fork status: ${source}`).toContain(
"Fork status:",
);
expect(`${name} marks upstream script: ${source}`).toMatch(
/upstream install script/,
);
expect(`${name} states artefacts unpublished: ${source}`).toMatch(
/not published yet|not yet published|once published/,
it("updates with -Su, never -Syu", () => {
expect(VOID_UPDATE_COMMAND).toBe("sudo xbps-install -Su voculinux");
expect(install).not.toContain("-Syu");
});
it("runs no upstream install or uninstall script", () => {
for (const source of [home, install, faq, fasterWhisper, parakeet]) {
expect(source).not.toMatch(/\/tmp\/vl\.sh|\/tmp\/vul\.sh|uninstall\.sh/);
}
});
it("describes the install hub as Void-only and third-party", () => {
// Source lines wrap, so compare the prose with whitespace collapsed.
const prose = install.replace(/\s+/g, " ");
expect(prose).toContain("Void Linux (glibc, x86_64)");
expect(prose).toContain(
"third-party repository published by this project, not by Void Linux upstream",
);
expect(prose).toContain("Other channels are not published for Voculinux");
});
it("offers no unpublished channel on the hub or the home page", () => {
for (const source of [home, install]) {
expect(source).not.toMatch(
/AppImage|Flatpak|\bSnap\b|\bAUR\b|\.deb\b|\.rpm\b|PyPI|Nix flake/,
);
}
});
it("labels upstream release links as upstream until fork releases land", () => {
for (const [name, source] of [
["home", home],
["install", install],
["distro", distro],
] as const) {
expect(`${name} marks upstream artefacts: ${source}`).toMatch(
/upstream GitHub Releases|upstream artefacts|upstream install script/,
);
it("keeps the home install section Void-first", () => {
expect(home).toContain("Install from the Void repository.");
expect(home).toContain("VOID_INSTALL_COMMANDS");
expect(home).not.toContain("Then launch <code>vocalinux</code>");
});
it("names the launcher voculinux on the hub", () => {
expect(install).not.toMatch(/vocalinux/);
});
it("says the optional engines are added from the user's own Python environment", () => {
for (const source of [fasterWhisper, parakeet]) {
expect(source).toContain("not in the Void package");
expect(source).toContain("your own Python environment");
expect(source).not.toContain("upstream install script");
expect(source).not.toContain("Fork status:");
}
});
});
describe("fork source and star count (spec16 review)", () => {
const read = (...segments: string[]): string =>
fs.readFileSync(path.join(process.cwd(), ...segments), "utf-8");
describe("channel notices", () => {
const notice = read("src", "components", "channel-notice.tsx");
const noticePages: string[][] = [
["flatpak", "page.tsx"],
["install", "nixos", "page.tsx"],
["changelog", "page.tsx"],
["local-gateway", "page.tsx"],
["install", "[distro]", "page.tsx"],
];
it("keeps the one notice phrase in the shared component", () => {
expect(notice).toContain("not published for Voculinux (Void only)");
expect(notice).toContain('href="/install/"');
expect(notice).toContain("SOURCE_REPO_URL");
});
it.each(noticePages)(
"renders %s through the shared notice with no schema",
(...segments) => {
const source = readPage(...segments);
expect(source).toContain('from "@/components/channel-notice"');
expect(source).not.toContain("application/ld+json");
expect(source).not.toContain("HowTo");
expect(source).not.toMatch(/install\.sh|curl /);
},
);
it("titles the channel notices as not published", () => {
for (const segments of [
["flatpak", "page.tsx"],
["install", "nixos", "page.tsx"],
["local-gateway", "page.tsx"],
["install", "[distro]", "page.tsx"],
]) {
expect(readPage(...segments)).toMatch(/Not Published/);
}
});
it("points the changelog notice at the fork releases page", () => {
expect(readPage("changelog", "page.tsx")).toContain(
"${SOURCE_REPO_URL}/releases",
);
});
it("keeps generateStaticParams on the distro notice route", () => {
expect(readPage("install", "[distro]", "page.tsx")).toContain(
"export function generateStaticParams",
);
});
});
describe("navigation and sitemap", () => {
const navSource = read("src", "lib", "site-nav.ts");
const sitemap = read("public", "sitemap.xml");
const removed = [
"/flatpak/",
"/install/nixos/",
"/install/ubuntu/",
"/install/debian/",
"/install/fedora/",
"/install/arch/",
"/install/linux-mint/",
"/install/pop-os/",
"/install/opensuse/",
"/changelog/",
"/local-gateway/",
];
it.each(removed)("removes %s from site-nav.ts", (href) => {
expect(navSource).not.toContain(`"${href}"`);
});
it.each(removed)("removes %s from sitemap.xml", (href) => {
expect(sitemap).not.toContain(`<loc>https://voculinux.com${href}</loc>`);
});
it("keeps the Void install hub in navigation and the sitemap", () => {
expect(navSource).toContain('"/install/"');
expect(sitemap).toContain("<loc>https://voculinux.com/install/</loc>");
});
it("lists the fork source as the only social link", () => {
expect(socialLinks.map((link) => link.href)).toEqual([SOURCE_REPO_URL]);
});
});
describe("fork source and star count (spec16 review)", () => {
it("points the source link at the fork host, not upstream", () => {
expect(SOURCE_REPO_URL).toBe("https://git.bongbetic.com/xavierk/voculinux");
expect(UPSTREAM_REPO_URL).toBe("https://github.com/VocaHQ/vocalinux");
@@ -122,79 +348,21 @@ describe("fork source and star count (spec16 review)", () => {
it("never fetches or shows the upstream repository's star count", () => {
const config = read("next.config.js");
const chrome = read("src", "components", "site-chrome.tsx");
for (const [name, source] of [
["next.config.js", config],
["site-chrome", chrome],
] as const) {
expect(`${name}: ${source}`).not.toMatch(/api\.github\.com/);
expect(`${name}: ${source}`).not.toMatch(
/stargazers|NEXT_PUBLIC_GITHUB_STARS/,
);
for (const source of [config, chrome]) {
expect(source).not.toMatch(/api\.github\.com/);
expect(source).not.toMatch(/stargazers|NEXT_PUBLIC_GITHUB_STARS/);
}
expect(chrome).toContain("SOURCE_REPO_URL");
});
it("uses the upstream constant only for the one labelled footer credit", () => {
const chrome = read("src", "components", "site-chrome.tsx");
const nav = read("src", "lib", "site-nav.ts");
expect(chrome.match(/href=\{UPSTREAM_REPO_URL\}/g)).toHaveLength(1);
expect(chrome).toContain("Vocalinux (upstream, by VocaHQ)");
expect(nav).not.toContain("UPSTREAM_REPO_URL");
});
it("keeps VocaHQ channels out of the footer social and nav links", () => {
const nav = read("src", "lib", "site-nav.ts");
expect(nav).not.toMatch(/x\.com|discord\.gg|mailto:|vocahq\.com/);
});
it("keeps a single upstream credit link on the about copy and home", () => {
const openSource = readPage("open-source", "page.tsx");
const home = readPage("page.tsx");
expect(openSource).not.toContain("github.com/VocaHQ/vocalinux");
expect(home).not.toContain("href={UPSTREAM_REPO_URL}");
});
});
describe("AUR and privacy honesty (spec16 review)", () => {
const distro = readPage("install", "[distro]", "page.tsx");
const install = readPage("install", "page.tsx");
const offline = readPage("offline", "page.tsx");
const home = readPage("page.tsx");
it("does not tell Arch users the AUR package is installable today", () => {
expect(distro).not.toContain(
"Arch users can install Voculinux from the AUR",
);
expect(distro).not.toContain("Install Voculinux from the AUR");
expect(distro).toContain("AUR packages (not published yet)");
});
it("does not link the unpublished AUR and Snap packages", () => {
for (const [name, source] of [
["home", home],
["install", install],
] as const) {
expect(`${name}: ${source}`).not.toContain(
"https://aur.archlinux.org/packages/voculinux",
);
expect(`${name}: ${source}`).not.toContain(
"https://snapcraft.io/voculinux",
);
}
});
describe("privacy claims (spec16 review)", () => {
it("claims no more than no usage telemetry in the installed app", () => {
const offline = readPage("offline", "page.tsx");
const home = readPage("page.tsx");
expect(offline).not.toContain("Zero Data Collection");
expect(offline).not.toContain("Truly Private");
expect(home).not.toContain("cannot see how many people install");
expect(home).not.toContain("<span>No telemetry</span>");
});
it("names the install-page launcher as upstream wherever it spells the old name", () => {
const bare = install
.split("\n")
.filter((line) => /vocalinux/i.test(line))
.filter((line) => !/upstream|VocaHQ\//i.test(line));
expect(bare).toEqual([]);
});
});
-594
View File
@@ -1,594 +0,0 @@
export class AIServiceError extends Error {
statusCode: number;
provider: string;
constructor(message: string, statusCode: number, provider: string) {
super(message);
this.name = 'AIServiceError';
this.statusCode = statusCode;
this.provider = provider;
}
}
export type TextGenerationResult = {
text: string;
usage: {
promptTokens: number;
completionTokens: number;
totalTokens: number;
};
finishReason?: 'stop' | 'length' | 'content_filter';
};
// Vision-enabled Text Types
export type ContentItem =
| { type: 'text'; text: string }
| { type: 'image_url'; image_url: { url: string } };
export type Message = {
role: 'user' | 'assistant' | 'system';
content: ContentItem[];
};
// We reuse TextGenerationResult since the output format is the same
const CONFIG: {
webId: string;
availableProviders: {
text: string[];
visionEnabled: string[];
};
} = {
webId: "6818365e6da47c0013cf7afd",
availableProviders: {
"text": [
"azure-gpt-4o"
],
"visionEnabled": [
"azure-gpt-4o"
]
}
};
const API_KEYS = {};
// ===== TEXT GENERATION MODULE =====
export interface TextProviderImplementation {
generate(prompt: string): Promise<TextGenerationResult>;
generateStream(prompt: string, onChunk: (text: string) => void): Promise<TextGenerationResult>;
}
export interface VisionTextProviderImplementation extends TextProviderImplementation {
generateWithImages(prompt: string, imageUrls: string[]): Promise<TextGenerationResult>;
generateWithImagesStream(prompt: string, imageUrls: string[], onChunk: (text: string) => void): Promise<TextGenerationResult>;
}
/**
* Generate text using a specific provider
* @param prompt - The prompt to generate text from
* @param provider - The provider to use
* @returns A promise that resolves to the generated text
*/
export async function generateText(
prompt: string,
provider: string
): Promise<TextGenerationResult> {
if (!prompt) throw new Error('Prompt is required');
if (!provider) throw new Error('Provider is required');
if (!CONFIG.availableProviders.text.includes(provider)) {
throw new Error(`Provider ${provider} is not available`);
}
try {
return await textProviderImplementations[provider].generate(prompt);
} catch (error) {
if (error instanceof AIServiceError) {
throw error;
} else {
throw new AIServiceError(
`Error generating text: ${error instanceof Error ? error.message : String(error)}`,
500,
provider
);
}
}
}
/**
* Generate text using a specific provider and stream the results
* @param prompt - The prompt to generate text from
* @param onChunk - A callback function that receives a chunk of text, can be used to update a UI or display the text in real-time
* @param provider - The provider to use
* @returns A promise that resolves to the generated text
*/
export async function generateTextStream(
prompt: string,
onChunk: (text: string) => void,
provider: string
): Promise<TextGenerationResult> {
if (!prompt) throw new Error('Prompt is required');
if (!onChunk) throw new Error('onChunk callback is required');
if (!provider) throw new Error('Provider is required');
if (!CONFIG.availableProviders.text.includes(provider)) {
throw new Error(`Provider ${provider} is not available`);
}
try {
return await textProviderImplementations[provider].generateStream(prompt, onChunk);
} catch (error) {
if (error instanceof AIServiceError) {
throw error;
} else {
throw new AIServiceError(
`Error streaming text: ${error instanceof Error ? error.message : String(error)}`,
500,
provider
);
}
}
}
/**
* Get all available text providers
* @returns An array of all available text providers
*/
export function getTextProviders(): string[] {
return CONFIG.availableProviders.text;
}
/**
* Get all available vision-enabled providers
* @returns An array of all available vision-enabled providers
*/
export function getVisionEnabledProviders(): string[] {
return CONFIG.availableProviders.visionEnabled;
}
/**
* Generate text using a specific provider and provide images
* @param prompt - The prompt to generate text from
* @param imageUrls - An array of image URLs
* @param provider - The provider to use
* @returns A promise that resolves to the generated text
*/
export async function generateTextWithImages(
prompt: string,
imageUrls: string[],
provider: string
): Promise<TextGenerationResult> {
if (!prompt) throw new Error('Prompt is required');
if (!imageUrls || !Array.isArray(imageUrls)) throw new Error('Image URLs must be an array');
if (!provider) throw new Error('Provider is required');
if (!CONFIG.availableProviders.visionEnabled.includes(provider)) {
throw new Error(`Provider ${provider} does not support image inputs`);
}
try {
const implementation = textProviderImplementations[provider] as VisionTextProviderImplementation;
return await implementation.generateWithImages(prompt, imageUrls);
} catch (error) {
if (error instanceof AIServiceError) {
throw error;
} else {
throw new AIServiceError(
`Error generating text with images: ${error instanceof Error ? error.message : String(error)}`,
500,
provider
);
}
}
}
/**
* Generate text using a specific provider and provide images and stream the results
* @param prompt - The prompt to generate text from
* @param imageUrls - An array of image URLs
* @param onChunk - A callback function that receives a chunk of text, can be used to update a UI or display the text in real-time
* @param provider - The provider to use
* @returns A promise that resolves to the generated text
*/
export async function generateTextWithImagesStream(
prompt: string,
imageUrls: string[],
onChunk: (text: string) => void,
provider: string
): Promise<TextGenerationResult> {
if (!prompt) throw new Error('Prompt is required');
if (!imageUrls || !Array.isArray(imageUrls)) throw new Error('Image URLs must be an array');
if (!onChunk) throw new Error('onChunk callback is required');
if (!provider) throw new Error('Provider is required');
if (!CONFIG.availableProviders.visionEnabled.includes(provider)) {
throw new Error(`Provider ${provider} does not support image inputs`);
}
try {
const implementation = textProviderImplementations[provider] as VisionTextProviderImplementation;
return await implementation.generateWithImagesStream(prompt, imageUrls, onChunk);
} catch (error) {
if (error instanceof AIServiceError) {
throw error;
} else {
throw new AIServiceError(
`Error streaming text with images: ${error instanceof Error ? error.message : String(error)}`,
500,
provider
);
}
}
}
// Shared implementation for all LiteLLM text providers
/**
* Generate text using a specific provider
* @param prompt - The prompt to generate text from
* @param integrationId - The integration ID to use
* @param model - The model to use
* @returns A promise that resolves to the generated text
*/
async function proxyGenerate(prompt: string, integrationId: string, model: string): Promise<TextGenerationResult> {
const requestBody = {
webId: CONFIG.webId,
integrationId,
prompt,
stream: false,
model
};
try {
const response = await fetch('https://proxy.getcreatr.com/v1/proxy/llm', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(requestBody),
});
if (!response.ok) {
throw new AIServiceError(
`Proxy error: ${response.status} ${response.statusText}`,
response.status,
model
);
}
const data = await response.json();
console.log(data);
return {
text: data.data.choices[0].message.content,
usage: {
promptTokens: data.data.usage.prompt_tokens,
completionTokens: data.data.usage.completion_tokens,
totalTokens: data.data.usage.total_tokens
},
finishReason: data.data.choices[0].finish_reason
};
} catch (error) {
if (error instanceof AIServiceError) throw error;
throw new Error(`Failed to call proxy: ${error instanceof Error ? error.message : String(error)}`);
}
}
/**
* Generate text using a specific provider and stream the results
* @param prompt - The prompt to generate text from
* @param onChunk - A callback function that receives a chunk of text, can be used to update a UI or display the text in real-time
* @param integrationId - The integration ID to use
* @param model - The model to use
* @returns A promise that resolves to the generated text
*/
async function proxyGenerateStream(prompt: string, onChunk: (text: string) => void, integrationId: string, model: string): Promise<TextGenerationResult> {
const requestBody = {
webId: CONFIG.webId,
integrationId,
prompt,
stream: true,
model
};
let fullText = '';
let usage = { promptTokens: 0, completionTokens: 0, totalTokens: 0 };
let finishReason;
try {
const response = await fetch('https://proxy.getcreatr.com/v1/proxy/llm/stream', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Accept': 'text/event-stream',
},
body: JSON.stringify(requestBody),
});
if (!response.ok) {
throw new AIServiceError(
`Proxy streaming error: ${response.status} ${response.statusText}`,
response.status,
model
);
}
if (!response.body) throw new Error('Response body is null or undefined');
const reader = response.body.getReader();
const decoder = new TextDecoder('utf-8');
let buffer = '';
let isStreamActive = true;
while (isStreamActive) {
const { done, value } = await reader.read();
if (done) {
isStreamActive = false;
continue;
}
buffer += decoder.decode(value, { stream: true });
const events = buffer.split('\n\n');
buffer = events.pop() || '';
for (const event of events) {
if (event.startsWith('data: ')) {
const data = event.slice(6).trim();
if (data === '[DONE]') {
isStreamActive = false;
continue;
}
try {
const parsed = JSON.parse(data);
const chunkContent = parsed.choices[0]?.delta?.content || '';
if (chunkContent) {
fullText += chunkContent;
onChunk(chunkContent);
}
if (parsed.choices[0]?.finish_reason) {
finishReason = parsed.choices[0].finish_reason;
}
// Update usage information from the final chunk
if (parsed.usage) {
usage = {
promptTokens: parsed.usage.prompt_tokens,
completionTokens: parsed.usage.completion_tokens,
totalTokens: parsed.usage.total_tokens
};
}
} catch (e) {
console.error('Error parsing stream chunk:', e);
}
}
}
}
return { text: fullText, usage, finishReason };
} catch (error) {
if (error instanceof AIServiceError) throw error;
throw new Error(`Failed to stream from proxy: ${error instanceof Error ? error.message : String(error)}`);
}
}
// Shared implementation for vision-enabled text providers
/**
* Generate text using a specific provider and provide images
* @param prompt - The prompt to generate text from
* @param imageUrls - An array of image URLs
* @param integrationId - The integration ID to use
* @param model - The model to use
* @returns A promise that resolves to the generated text
*/
async function proxyGenerateWithImages(prompt: string, imageUrls: string[], integrationId: string, model: string): Promise<TextGenerationResult> {
// Build content array with text and images for each message
const content: ContentItem[] = [
{ type: 'text', text: prompt }
];
// Add each image URL to the content array
for (const url of imageUrls) {
content.push({
type: 'image_url',
image_url: {
url: url
}
});
}
// Create the request payload exactly matching the expected format
const requestBody = {
model,
webId: CONFIG.webId,
integrationId,
messages: [
{
role: 'user',
content
}
],
stream: false
};
try {
const response = await fetch('https://proxy.getcreatr.com/v1/proxy/llm', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(requestBody),
});
if (!response.ok) {
throw new AIServiceError(
`Vision proxy error: ${response.status} ${response.statusText}`,
response.status,
model
);
}
const data = await response.json();
console.log(data);
return {
text: data.data.choices[0].message.content,
usage: {
promptTokens: data.data.usage.prompt_tokens,
completionTokens: data.data.usage.completion_tokens,
totalTokens: data.data.usage.total_tokens
},
finishReason: data.data.choices[0].finish_reason
};
} catch (error) {
if (error instanceof AIServiceError) throw error;
throw new Error(`Failed to call vision proxy: ${error instanceof Error ? error.message : String(error)}`);
}
}
/**
* Generate text using a specific provider and provide images and stream the results
* @param prompt - The prompt to generate text from
* @param imageUrls - An array of image URLs
* @param onChunk - A callback function that receives a chunk of text, can be used to update a UI or display the text in real-time
* @param integrationId - The integration ID to use
* @param model - The model to use
* @returns A promise that resolves to the generated text
*/
async function proxyGenerateWithImagesStream(prompt: string, imageUrls: string[], onChunk: (text: string) => void, integrationId: string, model: string): Promise<TextGenerationResult> {
// Build content array with text and images
const content: ContentItem[] = [
{ type: 'text', text: prompt }
];
// Add each image URL to the content array
for (const url of imageUrls) {
content.push({
type: 'image_url',
image_url: {
url: url
}
});
}
// Create the request payload exactly matching the expected format
const requestBody = {
model,
webId: CONFIG.webId,
integrationId,
messages: [
{
role: 'user',
content
}
],
stream: true
};
let fullText = '';
let usage = { promptTokens: 0, completionTokens: 0, totalTokens: 0 };
let finishReason;
try {
const response = await fetch('https://proxy.getcreatr.com/v1/proxy/llm/stream', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Accept': 'text/event-stream',
},
body: JSON.stringify(requestBody),
});
if (!response.ok) {
throw new AIServiceError(
`Vision proxy streaming error: ${response.status} ${response.statusText}`,
response.status,
model
);
}
if (!response.body) throw new Error('Response body is null or undefined');
const reader = response.body.getReader();
const decoder = new TextDecoder('utf-8');
let buffer = '';
let isStreamActive = true;
while (isStreamActive) {
const { done, value } = await reader.read();
if (done) {
isStreamActive = false;
continue;
}
buffer += decoder.decode(value, { stream: true });
const events = buffer.split('\n\n');
buffer = events.pop() || '';
for (const event of events) {
if (event.startsWith('data: ')) {
const data = event.slice(6).trim();
if (data === '[DONE]') {
isStreamActive = false;
continue;
}
try {
const parsed = JSON.parse(data);
const chunkContent = parsed.choices[0]?.delta?.content || '';
if (chunkContent) {
fullText += chunkContent;
onChunk(chunkContent);
}
if (parsed.choices[0]?.finish_reason) {
finishReason = parsed.choices[0].finish_reason;
}
// Update usage information from the final chunk
if (parsed.usage) {
usage = {
promptTokens: parsed.usage.prompt_tokens,
completionTokens: parsed.usage.completion_tokens,
totalTokens: parsed.usage.total_tokens
};
}
} catch (e) {
console.error('Error parsing stream chunk:', e);
}
}
}
}
return { text: fullText, usage, finishReason };
} catch (error) {
if (error instanceof AIServiceError) throw error;
throw new Error(`Failed to stream from vision proxy: ${error instanceof Error ? error.message : String(error)}`);
}
}
// Text provider implementations
const textProviderImplementations: Record<string, TextProviderImplementation | VisionTextProviderImplementation> = {};
// Vision-enabled text provider: azure-gpt-4o
textProviderImplementations["azure-gpt-4o"] = {
async generate(prompt: string): Promise<TextGenerationResult> {
return proxyGenerate(prompt, "67e52e6e8e1618ee9ba4117f", "azure-gpt-4o");
},
async generateStream(prompt: string, onChunk: (text: string) => void): Promise<TextGenerationResult> {
return proxyGenerateStream(prompt, onChunk, "67e52e6e8e1618ee9ba4117f", "azure-gpt-4o");
},
async generateWithImages(prompt: string, imageUrls: string[]): Promise<TextGenerationResult> {
return proxyGenerateWithImages(prompt, imageUrls, "67e52e6e8e1618ee9ba4117f", "azure-gpt-4o");
},
async generateWithImagesStream(prompt: string, imageUrls: string[], onChunk: (text: string) => void): Promise<TextGenerationResult> {
return proxyGenerateWithImagesStream(prompt, imageUrls, onChunk, "67e52e6e8e1618ee9ba4117f", "azure-gpt-4o");
}
};
+2 -7
View File
@@ -5,14 +5,11 @@ export const SITE_NAME = "Voculinux";
export const DEFAULT_OG_IMAGE_PATH = "/og-image.png";
export const DEFAULT_OG_IMAGE_ALT =
"Dictate locally. In any app. Offline voice dictation for Linux.";
export const TWITTER_HANDLE = "@vocahq";
export const VOCAHQ_URL = "https://vocahq.com";
// The fork's own source, issue and release host. Everything that says "our
// source", "our issues" or "our releases" resolves here.
export const SOURCE_REPO_URL = "https://git.bongbetic.com/xavierk/voculinux";
// Upstream Vocalinux on GitHub. Retained only as labelled provenance (the
// upstream install script, the AppImage and packages published there, and the
// upstream release history behind the changelog).
// The upstream project this fork is based on. Retained only as the one labelled
// provenance link, in the site footer credit.
export const UPSTREAM_REPO_URL = "https://github.com/VocaHQ/vocalinux";
interface PageMetadataOptions {
@@ -90,8 +87,6 @@ export const buildPageMetadata = ({
title,
description,
images: [DEFAULT_OG_IMAGE_PATH],
creator: TWITTER_HANDLE,
site: TWITTER_HANDLE,
},
};
};
+2 -15
View File
@@ -23,12 +23,10 @@ export const pageNav: NavLink[] = [
export const mobileExtraNav: NavLink[] = [
{ href: "/compare/", label: "Compare engines" },
{ href: "/changelog/", label: "Changelog" },
{ href: "/troubleshooting/", label: "Troubleshooting" },
];
// The fork's own channels only. The VocaHQ X account, Discord and mailbox
// belong to upstream, and the fork has none of them, so none are listed.
// The fork's own source host is the only social link. Nothing else is published.
export const socialLinks: { href: string; label: string; icon: string }[] = [
{
href: SOURCE_REPO_URL,
@@ -54,16 +52,7 @@ export const footerGroups: { title: string; links: NavLink[] }[] = [
{
title: "Install",
links: [
{ href: "/install/", label: "Distro guides" },
{ href: "/install/ubuntu/", label: "Ubuntu" },
{ href: "/install/debian/", label: "Debian" },
{ href: "/install/fedora/", label: "Fedora" },
{ href: "/install/arch/", label: "Arch" },
{ href: "/install/linux-mint/", label: "Linux Mint" },
{ href: "/install/pop-os/", label: "Pop!_OS" },
{ href: "/install/opensuse/", label: "openSUSE" },
{ href: "/install/nixos/", label: "NixOS" },
{ href: "/flatpak/", label: "Flatpak" },
{ href: "/install/", label: "Void Linux install" },
],
},
{
@@ -90,7 +79,6 @@ export const footerGroups: { title: string; links: NavLink[] }[] = [
{ href: "/multilingual-dictation/", label: "Multilingual dictation" },
{ href: "/global-shortcuts/", label: "Global shortcuts" },
{ href: "/shortcuts/", label: "Voice commands" },
{ href: "/local-gateway/", label: "Local gateway" },
],
},
{
@@ -99,7 +87,6 @@ export const footerGroups: { title: string; links: NavLink[] }[] = [
{ href: "/faq/", label: "FAQ" },
{ href: "/troubleshooting/", label: "Troubleshooting" },
{ href: "/privacy/", label: "Privacy" },
{ href: "/changelog/", label: "Changelog" },
{ href: SOURCE_REPO_URL, label: "Source" },
{ href: "https://bongbetic.com", label: "Bongbetic" },
],
+22
View File
@@ -0,0 +1,22 @@
import { SOURCE_REPO_URL } from "@/lib/seo";
// The signed Void xbps repository. These facts mirror packaging/void/README.md
// in the source repository. They live here once so the install hub and the home
// page cannot drift apart.
export const VOID_KEY_FINGERPRINT =
"fa:d1:00:32:ce:b1:b9:fd:73:05:05:76:75:54:37:25";
export const VOID_KEY_PLIST_URL = `${SOURCE_REPO_URL}/raw/branch/main/packaging/void/repository-key.plist`;
export const VOID_REPO_URL = `${SOURCE_REPO_URL}/releases/download/latest`;
// First sync, as root. `sudo -i` opens the root shell first, so the redirect
// into /etc/xbps.d runs as root as well. The key file is named by its
// fingerprint, so xbps finds it on this first sync and asks nothing.
export const VOID_INSTALL_COMMANDS = [
"sudo -i",
`curl -fsSL -o '/var/db/xbps/keys/${VOID_KEY_FINGERPRINT}.plist' \\`,
` ${VOID_KEY_PLIST_URL}`,
`echo 'repository=${VOID_REPO_URL}' > /etc/xbps.d/voculinux.conf`,
"xbps-install -S voculinux",
].join("\n");
export const VOID_UPDATE_COMMAND = "sudo xbps-install -Su voculinux";
+1 -1
View File
@@ -870,7 +870,7 @@
letter-spacing: 0.08em;
text-transform: uppercase;
white-space: nowrap;
/* 3.5s per item, matching vocahq.com; 4 copies per group. */
/* 3.5s per item; 4 copies per group. */
animation: ribbon-move 168s linear infinite;
}