Vocalinux

vocalinux.com
Vocalinux

Offline system-wide voice dictation for Linux (X11 and Wayland). Uses local models (whisper.cpp, Whisper, or VOSK) so microphone audio never leaves the device. Tray app with hotkeys; GPLv3. Currently beta software.

Open Source

Vocalinux Source Code

Author

VocaHQ

Description

Free, open-source, 100% offline voice dictation for Linux. Speak and type anywhere via whisper.cpp, Whisper & VOSK engines, GPU-accelerated, works on X11 + Wayland!

#accessibility#dictation#gpu-acceleration#linux#offline-first#privacy-first#python#speech-recognition#speech-to-text#voice#voice-typing#vosk#wayland#whisper#whisper-cpp

Homepage

https://vocalinux.com/

Repository

  • LicenseAGPL-3.0
  • Created12 Apr 25
  • Primary languagePython
  • Size29,077 KB
  • Stars804
  • Forks87
  • Watchers804

Language Usage

Language Usage

Project Health

  • Last commit5 hours ago
  • Open issues60
  • Latest releasev0.16.2

Recent Commits

  • Jatin K Malik(06 Sept 26)

    feat(release): attach Flatpak bundles to v* GitHub Releases (#786) * feat(release): attach Flatpak bundles to v* GitHub Releases On each version tag, build x86_64 and aarch64 .flatpak assets with the same builder pins as flatpak.yml, upload them as workflow artifacts, and attach Vocalinux-${VERSION}-{arch}.flatpak next to the AppImages. SHA256SUMS now checksums *.flatpak as well. The Flathub builder image has no GitHub CLI, so attach is a follow-up job on ubuntu-latest using gh release upload --clobber. Snap jobs are unchanged. Fixes #784 * fix(ci): pin privileged Flatpak builder to an immutable digest The gnome-50 tag is mutable; both release Flatpak jobs and flatpak.yml now pull the image by OCI index digest. The integrity test requires @sha256 rather than the tag alone.

  • Jatin K Malik(06 Sept 26)

    feat(snap): Snap packaging recipe and Snap Store publish strategy (#519) * feat(snap): add Snap packaging recipe and store publish strategy Add a strict-confinement snapcraft.yaml aligned with the current app version and Flatpak sandbox lessons (mic, desktop, network models, xdotool/xsel injection). Document human-only Snap Store steps and mark install docs as not published yet. Refs #48 * fix(snap): adopt-info so craftctl can set version from version.py snapcraft pack failed: craftctl set version requires adopt-info on the part. * fix(snap): stop staging GTK/GLib over the gnome platform Prepending snap-staged GI typelibs and lib paths caused a segfault on from gi.repository import Gtk. Use platform AppIndicator/GTK and only stage PortAudio plus xdotool/clipboard helpers. * docs(snap): note platform GTK path to avoid GI segfault * fix(snap): route ALSA through Pulse so Settings does not abort PortAudio enumerates ALSA devices when Settings opens. Missing /usr/share/alsa/alsa.conf inside the snap triggered a PortAudio assert. Ship an asound.conf that uses the pulse plugin, stage libasound2-plugins, and bind ALSA paths via layout. * fix(snap): keep ALSA conf under snap/local for snapcraft Move asound.conf out of snap/alsa so snapcraft stops warning about non-snapcraft paths in the snap directory. * fix(snap): request raw-input for global keyboard shortcuts Strict confinement blocks /proc/bus/input and /dev/input, so Wayland evdev hotkeys never start. Declare the raw-input plug (manual connect) and print snap-specific connect instructions instead of host input-group hints. * fix(hotkeys): discover keyboards via evdev when /proc is blocked Snap raw-input grants /dev/input/event* but still denies /proc/bus/input/devices, so find_keyboard_devices() returned empty and hotkeys never started. Fall back to evdev.list_devices() when /proc cannot be read. * fix(snap): restore UI sounds and prefer wtype for Wayland inject Stage paplay (pulseaudio-utils) and wtype; teach audio_feedback about pw-play/canberra. xdotool-only injection often hits the wrong window on native Wayland even when it reports success. * fix(snap): sync 0.16.2/AGPL metadata and AppImage-safe audio env Align snapcraft fallback version and store metadata with current main (VocaHQ URLs, AGPL-3.0), keep docs pointing at #48 on VocaHQ, pass host_env to pw-play/canberra paths, and document ordered Snap Store human deploy steps. * test(snap): harden audio host_env unit test against suite pollution Patch os.path.exists like sibling audio_feedback tests instead of relying on a real tempfile path that can collide with other suite mocks. * style(snap): black-format new audio and snap packaging tests * docs(snap): treat Store listing as live; edge→candidate→stable refresh Name vocalinux is already on snapcraft.io (edge @ 0.14.0-beta rev 6). Document upload/release without re-register, add snap-store-refresh.sh helper, and point INSTALL/README/DISTRO at the live listing. * fix(snap): move to core24 and close evdev capability FDs core22 only ships Python 3.10, which fails requires-python >=3.11, so the snap base is now core24 (Python 3.12) with the gnome-46 platform and the Ubuntu 24.04 libasound2t64 package. Also close InputDevice after failed capability scans and annotate the proc parser with TextIO. * Snap: align store listing metadata and icon Match snapcraft.yaml summary, description, license, website, and store icon to the live Snap Store listing. * Snap: trim packaging docs, script, and unused SVG Drop store refresh wrapper, long packaging README, duplicate snap/gui SVG, and speculative pw-play/canberra paths; keep recipe and confinement fixes. * ci: publish snap to edge and candidate on release tags * Snap: set grade stable for candidate channel CI * Snap: skip pip PyGObject; use gnome extension GI

  • Jatin K Malik(05 Sept 26)

    chore(release): prepare v0.16.2 (#782) Bump version metadata and series-aware notes for the 0.16.2 patch: KDE leftover IBus, Wayland/IBus shortcuts, BackSpace delete that, installer glslc, nightly stamp, release integrity, and CI/docs gates.

  • Sergey(05 Sept 26)

    chore(ci): test the distros the docs promise, and fix the docs that drifted (#773)

  • Sergey(05 Sept 26)

    feat(ci): gate AUR PKGBUILD builds on every PR (#772) * feat(ci): gate AUR PKGBUILD builds on every PR Nothing ever ran makepkg on the PKGBUILD before a tag pushed it to AUR users, and the channel broke twice that way (#736, #757) — a PKGBUILD change did not even start a CI job, because no paths filter matched packaging/aur/**. The gate (packaging/aur/build-test.sh, run in archlinux:latest — the AUR is a rolling channel, so the image stays deliberately unpinned): - swaps the tag-archive source= for a git archive of the checkout under the same filename and prefix, so build()/package() run unchanged and the gate answers "does this commit build on Arch" - installs depends/makedepends from the repos; python-pywhispercpp (virtual, provided by the AUR backends, #579) and python-pynput (AUR) are satisfied by a stub so makepkg still verifies the full depends set, and pip-installed from the hash-pinned requirements/runtime.txt for the import smoke - runs namcap on the PKGBUILD and the built package, failing on errors only (its warnings are noise around lazy imports and virtual depends) - installs the package and imports the whole UI/recognition stack, plus the vocalinux --version entry point Part of #701 (phase 6). * fix(ci): stop partial-upgrading in the AUR gate and correct the #736 attribution

  • Benjamin Eisele(02 Sept 26)

    fix(actions): send real BackSpace key events for "delete that" (#714) `_handle_delete_last` built its deletion as `"\b" * len(text)` and passed it to `inject_text`, treating U+0008 as typed input. No backend delivers that as a keypress: - `ydotool type` has no keymap entry for U+0008, so it types nothing. - IBus `commit_text()` commits it as a literal control character, which the client inserts or drops rather than treating as a deletion. Only an actual key event deletes, so the "delete that" voice command did nothing on those paths while still reporting success. Add `press_backspace(count)`, which sends `count` real BackSpace events using the tool that fits the environment: `xdotool key --repeat` on X11 and the xdotool-backed Wayland path, `wtype -k BackSpace` or the ydotool keycode pair `14:1 14:0` otherwise. WAYLAND_IBUS resolves a virtual-keyboard tool of its own, since IBus cannot send key events. `inject_text` is untouched; only the delete_last action changes. The existing delete_last tests asserted the U+0008 string was injected, so they now pin the new contract instead: press_backspace called once with the character count, and inject_text not called at all.

  • Benjamin Eisele(02 Sept 26)

    fix(ibus): route keyboard shortcuts to a virtual-keyboard tool (#716) `_inject_keyboard_shortcut` dispatched X11 and WAYLAND_XDOTOOL to xdotool and sent everything else to `_inject_shortcut_with_wayland_tool`, putting both IBus environments on a path that cannot serve them. IBus commits text; it has no way to synthesise a key combination. WAYLAND_IBUS failed worse than it looked. `wayland_tool` is only set when dependency detection picks a Wayland injection tool, so on an IBus session it can be unset, and the attribute access raised inside the enclosing try. The except logs and returns False, so every shortcut reported a clean failure rather than a missing tool. Route X11_IBUS through xdotool, which reaches the same X server the XIM path is already using, and give WAYLAND_IBUS a virtual-keyboard tool, preferring wtype and falling back to ydotool. With neither present the error names the real cause instead of surfacing an AttributeError. X11_IBUS folds into the existing xdotool condition rather than adding a branch, since the call is identical. The `wayland_tool` write is taken under `_state_lock`, matching every other write to it; the injection call stays outside the block, as the lock is not reentrant. Text injection is unchanged; this only affects shortcut delivery, and only in the two IBus environments. Five tests cover the routing: X11_IBUS reaching xdotool, WAYLAND_IBUS preferring wtype, its ydotool fallback, the no-tool-available failure injecting nothing, and an already-chosen tool being left alone.

  • Benjamin Eisele(02 Sept 26)

    fix(wayland): drive keyboard shortcuts with wtype and ydotool (#715) `_inject_shortcut_with_wayland_tool` could not deliver a shortcut on either backend: - wtype was refused outright with "Keyboard shortcuts not supported with wtype", but wtype does chord: `-M` presses a modifier, `-k` types a key, `-m` releases it. Modifiers must be released explicitly, as wtype only drops them when the process exits. - ydotool was handed the shortcut string as-is, `ydotool key ctrl+z`. `ydotool key` takes raw evdev keycodes ("29:1 44:1 44:0 29:0") and documents non-interpretable values as only causing a delay, so this was a silent no-op that still returned True. Parse the shortcut into a list of `(modifiers, key)` steps and emit the right form per tool. Tokens are classified by name rather than by position, because the mappings in `ActionHandler._SHORTCUT_ACTIONS` are not all modifiers-first: `select_line` is "Home+shift+End", meaning press Home, then Shift+End -- two sequential steps, not one chord. Each non-modifier token closes a step and consumes the modifiers seen since the previous one. An unmappable ydotool key now fails loudly instead of emitting a partial sequence, and a trailing modifier with no key is rejected at parse time. The X11 and WAYLAND_XDOTOOL paths are untouched -- xdotool already accepts the string form directly. Verified on Hyprland 0.56.2 with wtype 0.4 and ydotool 1.0.4, injecting into a GTK3 window that logs key-press events. `wtype -M ctrl -k z -m ctrl` arrives as z with the control modifier set; the two-step form arrives as Home with no modifier, then End with shift; the raw keycode form arrives as Control_L followed by z with control. Run on its own, `ydotool key ctrl+z` exits 0 and delivers no key event at all -- the failure this fixes. `test_wtype_not_supported` asserted the old refusal, so it is replaced by tests pinning the emitted argv for both tools, the two-step case, and the unmappable-key rejection.

  • Jatin K Malik(02 Sept 26)

    fix(inject): skip leftover IBus on KDE so dictation actually types (#753) * fix(inject): skip leftover IBus on KDE so dictation actually types Test Dictation and clipboard paste worked. Inject claimed success and nothing showed up in Kate, Obsidian, terminals, or browsers. We started ibus-daemon on X11 even when the session never opted into IBus, then treated that daemon plus a bare xkb engine as active. commit_text() returns True. Qt/GTK on KDE never hear it. Do not spawn the daemon unless GTK_IM_MODULE / QT_IM_MODULE / XMODIFIERS already name IBus. On KDE, a running daemon without those env vars is not the session IM. Prefer a live WAYLAND_DISPLAY over a stale XDG_SESSION_TYPE=x11, and do not take the GNOME scoped-IBus shortcut on KDE. See #752 * style: wrap IBus helpers so black is happy CI lint failed on two lines in the #753 inject fix. Same behavior. * test(inject): leftover IBus on KDE should be skipped The CheckDependencies case still expected IBus for a leftover daemon on KDE. That is the #752 bug. It now expects skip and wtype instead.

  • dependabot[bot](01 Sept 26)

    chore(deps): bump the github-actions group with 11 updates (#766) Bumps the github-actions group with 11 updates: | Package | From | To | | --- | --- | --- | | [actions/checkout](https://github.com/actions/checkout) | `5.1.0` | `7.0.1` | | [actions/labeler](https://github.com/actions/labeler) | `6.2.0` | `7.0.0` | | [actions/setup-python](https://github.com/actions/setup-python) | `5.6.0` | `7.0.0` | | [actions/cache](https://github.com/actions/cache) | `4.3.0` | `6.1.0` | | [actions/upload-artifact](https://github.com/actions/upload-artifact) | `4.6.2` | `7.0.1` | | [softprops/action-gh-release](https://github.com/softprops/action-gh-release) | `2.6.2` | `3.0.2` | | [actions/download-artifact](https://github.com/actions/download-artifact) | `4.3.0` | `8.0.1` | | [actions/setup-node](https://github.com/actions/setup-node) | `5.0.0` | `7.0.0` | | [dorny/paths-filter](https://github.com/dorny/paths-filter) | `3.0.4` | `4.0.3` | | [astral-sh/setup-uv](https://github.com/astral-sh/setup-uv) | `6.8.0` | `10.0.1` | | [codecov/codecov-action](https://github.com/codecov/codecov-action) | `4.6.0` | `7.0.0` | Updates `actions/checkout` from 5.1.0 to 7.0.1 - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09...3d3c42e5aac5ba805825da76410c181273ba90b1) Updates `actions/labeler` from 6.2.0 to 7.0.0 - [Release notes](https://github.com/actions/labeler/releases) - [Commits](https://github.com/actions/labeler/compare/b8dd2d9be0f68b860e7dae5dae7d772984eacd6d...bf12e9b00b37c5c0ca2b87b79b2daf7891dbda13) Updates `actions/setup-python` from 5.6.0 to 7.0.0 - [Release notes](https://github.com/actions/setup-python/releases) - [Commits](https://github.com/actions/setup-python/compare/a26af69be951a213d495a4c3e4e4022e16d87065...5fda3b95a4ea91299a34e894583c3862153e4b97) Updates `actions/cache` from 4.3.0 to 6.1.0 - [Release notes](https://github.com/actions/cache/releases) - [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md) - [Commits](https://github.com/actions/cache/compare/0057852bfaa89a56745cba8c7296529d2fc39830...55cc8345863c7cc4c66a329aec7e433d2d1c52a9) Updates `actions/upload-artifact` from 4.6.2 to 7.0.1 - [Release notes](https://github.com/actions/upload-artifact/releases) - [Commits](https://github.com/actions/upload-artifact/compare/ea165f8d65b6e75b540449e92b4886f43607fa02...043fb46d1a93c77aae656e7c1c64a875d1fc6a0a) Updates `softprops/action-gh-release` from 2.6.2 to 3.0.2 - [Release notes](https://github.com/softprops/action-gh-release/releases) - [Changelog](https://github.com/softprops/action-gh-release/blob/master/CHANGELOG.md) - [Commits](https://github.com/softprops/action-gh-release/compare/3bb12739c298aeb8a4eeaf626c5b8d85266b0e65...3d0d9888cb7fd7b750713d6e236d1fcb99157228) Updates `actions/download-artifact` from 4.3.0 to 8.0.1 - [Release notes](https://github.com/actions/download-artifact/releases) - [Commits](https://github.com/actions/download-artifact/compare/d3f86a106a0bac45b974a628896c90dbdf5c8093...3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c) Updates `actions/setup-node` from 5.0.0 to 7.0.0 - [Release notes](https://github.com/actions/setup-node/releases) - [Commits](https://github.com/actions/setup-node/compare/a0853c24544627f65ddf259abe73b1d18a591444...820762786026740c76f36085b0efc47a31fe5020) Updates `dorny/paths-filter` from 3.0.4 to 4.0.3 - [Release notes](https://github.com/dorny/paths-filter/releases) - [Changelog](https://github.com/dorny/paths-filter/blob/master/CHANGELOG.md) - [Commits](https://github.com/dorny/paths-filter/compare/0e4a8c6effa4802afeda77dc8d303f8176d7dfad...ceb8a2b8f2d89434be7ff52d3de7ec3738c5cc9d) Updates `astral-sh/setup-uv` from 6.8.0 to 10.0.1 - [Release notes](https://github.com/astral-sh/setup-uv/releases) - [Commits](https://github.com/astral-sh/setup-uv/compare/d0cc045d04ccac9d8b7881df0226f9e82c39688e...20cfd1bf945f4377ade1205e4dbc17946fc9a30d) Updates `codecov/codecov-action` from 4.6.0 to 7.0.0 - [Release notes](https://github.com/codecov/codecov-action/releases) - [Changelog](https://github.com/codecov/codecov-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/codecov/codecov-action/compare/b9fd7d16f6d7d1b5d2bec1a2887e65ceed900238...fb8b3582c8e4def4969c97caa2f19720cb33a72f) --- updated-dependencies: - dependency-name: actions/checkout dependency-version: 7.0.1 dependency-type: direct:production update-type: version-update:semver-major dependency-group: github-actions - dependency-name: actions/labeler dependency-version: 7.0.0 dependency-type: direct:production update-type: version-update:semver-major dependency-group: github-actions - dependency-name: actions/setup-python dependency-version: 7.0.0 dependency-type: direct:production update-type: version-update:semver-major dependency-group: github-actions - dependency-name: actions/cache dependency-version: 6.1.0 dependency-type: direct:production update-type: version-update:semver-major dependency-group: github-actions - dependency-name: actions/upload-artifact dependency-version: 7.0.1 dependency-type: direct:production update-type: version-update:semver-major dependency-group: github-actions - dependency-name: softprops/action-gh-release dependency-version: 3.0.2 dependency-type: direct:production update-type: version-update:semver-major dependency-group: github-actions - dependency-name: actions/download-artifact dependency-version: 8.0.1 dependency-type: direct:production update-type: version-update:semver-major dependency-group: github-actions - dependency-name: actions/setup-node dependency-version: 7.0.0 dependency-type: direct:production update-type: version-update:semver-major dependency-group: github-actions - dependency-name: dorny/paths-filter dependency-version: 4.0.3 dependency-type: direct:production update-type: version-update:semver-major dependency-group: github-actions - dependency-name: astral-sh/setup-uv dependency-version: 10.0.1 dependency-type: direct:production update-type: version-update:semver-major dependency-group: github-actions - dependency-name: codecov/codecov-action dependency-version: 7.0.0 dependency-type: direct:production update-type: version-update:semver-major dependency-group: github-actions ... Signed-off-by: dependabot[bot] <[email protected]> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

  • Sergey(31 Aug 26)

    chore(release): make a release verifiable, and pin what builds it (epic #701, phase 5) (#759) * chore(release): build once, and publish sha256sums and provenance (epic #701, phase 5) * chore(ci): pin every third-party action by commit sha (epic #701, phase 5) * test(ci): anchor the dependabot guard on the ecosystem it must cover

  • Sergey(31 Aug 26)

    fix(nightly): stamp the version before building instead of renaming the wheel. (#762) * fix(nightly): stamp the version before building instead of renaming the wheel * test(nightly): require the known build jobs rather than an exact count

  • Jatin K Malik(31 Aug 26)

    fix(install): Fedora and Arch need glslc, not glslang (#763) * fix(install): Fedora and Arch need glslc, not glslang ggml's Vulkan CMake looks for glslc and will not take glslangValidator. install.sh was still installing glslang on Fedora, Arch, Alpine, Void, Solus, and Gentoo, so the pywhispercpp source build fell back to a CPU wheel. Ubuntu and Debian already pull glslc plus patchelf. See #604. * fix(install): Gentoo patchelf is under dev-util sys-devel/patchelf does not exist. emerge fails and install.sh exits, so a fresh Gentoo install never reaches the whisper.cpp build. See #604.

  • Jatin K Malik(31 Aug 26)

    fix(site): VocaGateway family card is Beta (#765) * fix(site): VocaGateway family card is Beta PRODUCT.md and vocagateway.vocahq.com already say Beta. The homepage chip still said Early. See #764. * fix(docs): README family table also says Gateway Beta Same leftover as the homepage chip. Still optional self-hosted, not on-device. See #764.

  • Jatin K Malik(30 Aug 26)

    Change Vocalinux logo image source Updated logo image source in README.

  • Jatin K Malik(30 Aug 26)

    Update README badges and privacy text Updated badge links and improved privacy badge text in README.

  • Jatin K Malik(30 Aug 26)

    fix(aur): build on Arch setuptools 84 and skip 1.4 context_params (#757) AUR --no-isolation uses extra python-setuptools 84, so the <82 cap blocked the 0.16.0 rebuild. AUR pywhispercpp backends are still 1.4 and crash if we pass context_params; skip that argument below 1.5.

  • Jatin K Malik(30 Aug 26)

    chore(release): prepare v0.16.1 (#755) Bump the stable line from 0.16.0 to 0.16.1. README and UPDATE keep the 0.16 series highlights and add the patch-only fixes. Website changelog, schema, AppStream, and AUR PKGBUILD follow.

  • Jatin K Malik(30 Aug 26)

    feat(settings): even out dropdowns and tidy the About page (#754) * feat(settings): even out dropdowns and tidy the About page Pin combos to a shared right-hand column so long labels cannot stretch a row. Fold the dictation-tone preview into a two-stage Start/Stop button, quiet the model status card, shorten Performance copy, and rebuild About with combined links, channel+Check, family platform tiles, and Talk to us moved up next to the license. * fix(settings): keep tone preview on start if the cue fails Do not flip Start/Stop when preview_tone_cue returns false, so a missing file or silent player cannot skip the start cue. Also annotate PreferenceRow.set_title / set_subtitle. * fix(settings): paint About icons for dark dialogs Dark ink on Adwaita/Yaru-dark buttons made the family tiles and Talk to us marks disappear. Pick a light foreground on dark themes (and keep the original ink on light ones) so the marks match the labels. * fix(settings): show Android and iOS marks on VocaPhone VocaPhone covers both, so the family tile now uses the Android robot and the Apple mark instead of a single phone icon. * fix(settings): cut repeated About copy Name the AGPL once on the License row. The VocaHQ blurb is just the private-dictation line. VocaPhone says TestFlight, not source-only, and Gateway is self-hosted and headless. * fix(settings): space About icons from their labels Talk-to-us buttons and the family tiles were packing the mark against the text. Give the image an end margin and a wider gap in the family tile.

  • Jatin K Malik(29 Aug 26)

    fix(ui): use the canonical VocaHQ Discord invite (#749)

  • Sergey(29 Aug 26)

    test(appimage): boot the built AppImage on six distros (#744) * test(appimage): boot the built AppImage on six distros * docs(agents): document the AppImage boot matrix

  • Sergey(29 Aug 26)

    fix(appimage): build against a glibc floor users actually have (#743) * build(appimage): pin the interpreter, packages and tooling * ci(appimage): build inside a pinned ubuntu 22.04 image * test(appimage): guard the pins and the base image * docs: state the AppImage glibc floor * fix(appimage): bundle libibus with its typelib * ci(appimage): let the container run the isolated GI smoke test * fix(appimage): keep the bundle's libraries out of host binaries * fix(appimage): bundle libgpg-error with the libgcrypt that needs it * docs(agents): require host_env() for spawned binaries * ci: run the AppImage jobs when its other inputs change * fix(appimage): check every bundled typelib, not just the seeded ones * fix(ibus): keep the bundle when respawning our own interpreter * fix(injection): pass host_env() to setxkbmap and notify-send * test(ibus): wait for the IBus warmup thread before asserting

  • Kacper Paczos(29 Aug 26)

    feat(tray): offer the recommended model from the missing-model notification (#687) * feat(tray): offer the recommended model from the missing-model notification Dictation without a model on disk showed a dead-end notification telling the user to open Settings, find the model picker and start the download by hand, at the moment they wanted to talk. There was no Download button anywhere: downloads only happen as a side effect of changing a selection. The notification now carries a "Download <model> (<size>)" button for the model the picker already stars for this machine, so recommendations stay in one place: get_recommended_model() for whisper.cpp, including the English-only variant when an English language is selected, and the RAM based recommendations for VOSK and Whisper. Clicking it downloads in the background, reports progress in the notification, and saves the model for that engine only after it loaded. Notification servers that do not advertise the actions capability, and systems without libnotify, still get the plain text they got before. Dictation is not started for the user afterwards: a download can take several minutes, and grabbing the microphone unannounced would surprise someone who has long since moved on. * feat(tray): actually fetch the offered model, and only claim it when loaded Review follow-ups on #687. reconfigure() only re-initializes when engine, model or language change, and force_download is read during that re-init. First-run defaults are whisper_cpp + tiny, and tiny is what get_recommended_model() returns on a CPU-only machine under 16GB, so the click was a no-op that logged "Applied VAD/silence timeout changes.", saved the model, said "Speech model ready", and left the next shortcut showing the same missing-model notification. Add force_reinit to reconfigure() and pass it here, then gate the save and the success notification on speech_engine.model_ready instead of on reconfigure() not raising. The "starting..." notification is now created in _download_recommended_model, which runs on the main loop where libnotify dispatches the action; only the handle travels to the worker, which already routes update/close/notify through _idle_once. The offer body no longer promises a button the server cannot draw: with no action it points at Settings, like the notification it replaced. Tests cover the case that breaks the feature — the recommendation equal to the configured model — behaviourally, through a stand-in engine that gates re-init the way reconfigure() does, plus the real reconfigure() contract in both directions. They also stop touching sys.modules at collection time, which is the desync #686 hit, and black is clean. * test(notifications): cover the libnotify/notify-send boundary directly Every test that touches notifications so far patches the whole module away, so the module itself had no coverage: the fallback to notify-send when the server has no action support or libnotify fails to initialize, the handle kept alive in _pending until the user answers, and update() refusing a fallback handle. These drive it with a fake Notify installed through the module's own lazy cache rather than by patching gi.repository, which is shared with every other test in the process. * fix(download): allow only one model download at a time across tray and Settings The tray's download worker and the Settings dialog's both drive the same engine, which has a single progress callback slot and a single configuration. Started together, the later one overwrote the earlier one's progress callback, the earlier one's finally block then cleared it mid-download, and whichever finished last became the persisted model. Give the engine a non-blocking claim. Both Settings entry points and the tray take it before starting a download and release it when the worker ends; whoever loses the race is told a download is already running and the Settings picker goes back to the saved model. --------- Co-authored-by: Kacper Paczos <[email protected]>

  • Jatin K Malik(29 Aug 26)

    fix(ibus): sync XWayland layout from GNOME after scoped inject (#742) Super+Space during recording on GNOME Wayland updates Mutter and IBus but can leave XWayland on the previous map. Scoped inject restores the IBus engine and skips setxkbmap (#474), so X apps keep the old layout. After engine restore, copy GNOME's current XKB source onto XWayland when DISPLAY is set. Do not replay an empty capture and do not call restore_xkb_layout for this. Sessions with no X display are left alone.

  • Jatin K Malik(29 Aug 26)

    fix(tray): stay idle after leftover transcription on toggle stop (#741) * fix(tray): stay idle after leftover transcription on toggle stop Toggle stop can time out while leftover audio is still transcribing. The recognition thread then painted PROCESSING and never went idle, so the tray stayed red until another Alt+F12. Skip PROCESSING and LISTENING updates once recording has stopped, always set IDLE when that thread exits, and paint the engine's current state in the tray instead of a stale GLib.idle_add argument. * fix(tray): leftover drain must not idle a new dictation session stop_recognition already marks IDLE. Forcing IDLE when the leftover worker exits can clobber a new LISTENING start during leftover drain.

  • Jatin K Malik(29 Aug 26)

    fix(tests): stop leaking Factory.new onto MagicMock (#748) The IBus test stubs assigned Bus and Factory to the MagicMock class, then set Factory.new. That attribute lands on unittest.mock.MagicMock itself, so every mock in the pytest session shares the same .new. Bus and Factory are instances now, matching Text. A regression test fails if importing the module puts new on the class.

  • Jatin K Malik(27 Aug 26)

    fix(injection): use Ctrl+Shift+V when pasting into terminals (#734) * fix(injection): use Ctrl+Shift+V when pasting into terminals Clipboard injection always sent Ctrl+V, which most terminal emulators ignore. Detect standalone terminal windows and send Ctrl+Shift+V, with a Settings override for nested IDE panels that window-class detection cannot see. * fix(injection): fail closed when terminal window probe is mocked Clipboard tests patch text_injector.subprocess.run, which replaces stdlib subprocess.run. Window detection now ignores non-string probe output so injection still uses Ctrl+V instead of raising TypeError. * docs: fix overclaimed terminal auto-detect in USER_GUIDE Auto-detect only runs on X11 and on Hyprland, Sway, and niri. GNOME and KDE Wayland stay on Ctrl+V unless you set Clipboard Paste Shortcut to Ctrl+Shift+V.

  • Kacper Paczos(27 Aug 26)

    fix(settings): save the model only once the engine loaded it (#685) * fix(settings): save the model only once the engine loaded it Applying settings wrote the config first and reconfigured the engine afterwards, and reconfiguring is what downloads a missing model. A download that failed, was cancelled, or was interrupted therefore left the config naming a model that never reached the disk: the next start reported "No Speech Model" while the previously working model sat on disk, deselected. Reconfigure first and persist only on success, so a failed switch keeps the settings that are actually running. On failure the pickers are put back on the saved model. They otherwise kept showing the rejected one, which also blocked a retry, since re-selecting the same entry emits no "changed" signal. Selecting an engine now applies as well. Every other control on the page applies on change, but the engine only repainted the pickers: the config and the recognizer stayed on the previous engine until another control was touched, and closing the dialog dropped the change without a word. Remote API is deferred until its server URL is filled in, since applying it empty can only fail validation. * fix(settings): restore the engine picker after a failed engine switch Fixes the known limitation called out in the previous commit (#692): when applying a newly selected engine failed, the resync restored the model pickers but left the engine combo displaying the engine that was never applied, so the page described a state that was not running. Set the combo back to the saved engine while holding the apply suppression flag, so _on_engine_changed() repaints the pickers without cascading into another apply or download prompt. * fix(settings): resync the engine picker on every path that never applied Review follow-ups on #685. #692 was not actually closed. The resync lived only in the download thread's except block, and _apply_settings_internal() reports failure by returning False as well as by raising, so the thread fell through to set_complete(True, "") and reported a switch that never happened. Act on the return value instead of depending on #690 to make that except live, and add the same handling to apply_settings()'s modal path, whose except never resynced at all. Add _resync_engine_ui_if_unapplied() as the backstop the review asked for: after the modal closes, and when the dialog closes, compare the engine on screen against the engine in the config and restore the picker when they disagree. That also covers Remote API left without a server URL, where applying stays deliberately deferred. _on_engine_changed now checks _initializing / _applying_settings itself, like every other control on this page, so the combo move made by a resync repaints without rewriting the user's language or cascading into another apply. Tests no longer touch sys.modules at collection time: the reimport moved into a module-scoped fixture that also restores the attribute on vocalinux.ui, which is what desynced test_model_deletion on 3.9/3.10. New cases cover the modal path that #692 is about — needs_download true with the apply returning False, and with it raising "Download cancelled" — plus the close hook. The stub now uses RecognitionState.IDLE rather than the string "idle", so tests stop taking the stop_recognition branch and sleeping 0.5s.

  • Kacper Paczos(27 Aug 26)

    fix(config): share one ConfigManager across the process (#691) * fix(config): share one ConfigManager across the process main(), the tray, and the sound-effects check each constructed their own ConfigManager. Every instance loads the whole config into memory at construction time and save_config() writes that whole cache back, so any save from a stale instance silently reverts what another instance saved in the meantime — no race needed, the second save just wins with old values. The disk-read workarounds in main (_should_append_trailing_space) already existed to dodge exactly this. Add get_shared_config_manager() and route the three call sites through it. It keeps ConfigManager constructible for tests, and the tray/dialog pair already shared one instance, so this extends that arrangement to the rest of the process. Tests: a regression test runs the two-instance scenario through the accessor, and a source guard fails if application code constructs ConfigManager directly again. conftest drops the shared instance between tests, since it would otherwise be created under whichever mocks the first caller had active and leak them into later tests. The two single-instance tests now patch acquire_lock itself: they used patch.dict(sys.modules, ...), which main()'s "from . import" ignores once an earlier test has imported the real module — they only passed through an unrelated mock leaking into the tray's shortcut setup, which this change stops. * fix(config): lock the shared accessor and prove the revert it prevents Review follow-ups on #691: - guard the lazy create with a lock, so two threads racing the first call cannot each build a ConfigManager and reintroduce #689 - take both handles before the first save in the accessor test, and assert identity there — the old order let a fresh ConfigManager pass - add a characterization test: two direct ConfigManager instances still revert each other, which is the footgun the accessor exists to hide - patch get_shared_config_manager in test_main and test_audio_feedback instead of the class, so a leftover singleton cannot skip the mock - close the ExitStack in _boot_under_patches when main() raises; the caller only closes a stack it received, so a failure there leaked those patches into every later test * fix(audio): route the tone lookup through the shared ConfigManager The tone picker merged on main (#707) constructs its own ConfigManager, which is exactly the stale-cache overwrite this branch removes; the new source guard in test_shared_config_manager.py flags it.

  • Kacper Paczos(27 Aug 26)

    fix(settings): size the unused downloads list to its real rows (#686) * fix(settings): size the unused downloads list to its real rows The list reserved 56px per row, but a row renders taller than that once its margins and the Delete button are counted. With two leftover models the header read "2 leftover models" while only the first row fitted in the viewport, and the second could neither be seen nor deleted. The overlay scrollbar gave no hint either, since it stays invisible until hovered. Measure the list once its rows are in place and cap that at the existing 220px, and keep the scrollbar in the layout so a list that does reach the cap looks scrollable. * fix(settings): test the list sizing without reimporting the dialog Review follow-ups on #686. The test helper popped vocalinux.ui.settings_dialog from sys.modules at collection time and reimported it under a patched Gtk, restoring only sys.modules and not the attribute on the package. Whichever module object a later test reached then depended on collection order, which is why test_model_deletion::test_whisper_list_and_delete failed on 3.9/3.10 with assert [] == ['tiny'] while main stayed green. Move the arithmetic into a module-level _clamp_unused_downloads_height() and unit-test that directly, the way _list_downloaded_whisper_models is already tested. Nothing touches sys.modules now. The dialog cannot be instantiated under the mocked-GTK harness, so the wiring the old tests never covered is asserted against the method source, following test_settings_mode_change.py: refresh calls the fit, the fit measures the listbox and feeds set_min_content_height, the per-row estimate stays gone, and the scrollbar stays out of overlay mode. * fix(settings): remeasure the unused-downloads list when it expands The refresh measures while the expander is collapsed and the dialog may not be mapped; if that first measurement ever comes back short, the expand handler remeasures so the rows cannot stay clipped.

Vocalinux Website

Website

Vocalinux: Offline Voice Dictation for Linux

Free and open-source voice dictation for Linux. Convert speech to text with whisper.cpp, VOSK, Remote API servers, Silero VAD, X11, and Wayland.

Redirects

Does not redirect

Security Checks

All 65 security checks passed

Server Details

  • IP Address185.199.110.153
  • Hostnamecdn-185-199-110-153.github.com
  • LocationFrancisco,Indiana,United States of America,NA
  • ISPGitHub Inc.
  • ASNAS54113

Associated Countries

  • USUS

Safety Score

Website marked as safe

100%

Blacklist Check

vocalinux.com was found on 0 blacklists

  • AntiSocial Blacklist
  • Artists Against 419
  • Badbitcoin
  • Bambenek Consulting
  • CERT Polska
  • CoinBlockerLists
  • CRDF
  • CryptoScamDB
  • EtherAddressLookup
  • EtherScamDB
  • Fake Website Buster
  • MetaMask EthPhishing
  • NABP Not Recommended Sites
  • OpenPhish
  • PetScams
  • PhishFeed
  • PhishFort
  • Phishing.Database
  • PhishStats
  • PhishTank
  • Phishunt
  • RPiList Not Serious
  • Scam.Directory
  • SecureReload Phishing List
  • Spam404
  • StopGunScams
  • Suspicious Hosting IP
  • ThreatFox
  • ThreatLog
  • TweetFeed
  • URLhaus
  • ViriBack C2 Tracker

Website Preview

Website preview

Vocalinux Reviews

More Accessibility

⚠️ This section is still a work in progress ⚠️
Check back soon, or help us complete it by submiting a pull request on GitHub.
Or submit an entry here

About the Data: Vocalinux

Change History

Edit Vocalinux Data

You can edit Vocalinux's entry in this section of awesome-privacy.yml by submitting a PR to our GitHub repo.
Note that some of the information shown above has been aggregated from external sources, a list of these can be found data documentation.

Origin Data

Modify Data

API

You can access Vocalinux's data programmatically via our API. Simply make a GET request to:

https://api.awesome-privacy.xyz/v1/services/vocalinux

The REST API is free, no-auth and CORS-enabled. To learn more, view the API Docs or read the API Usage Guide.

Share Vocalinux

Help your friends compare Accessibility, and pick privacy-respecting software and services.
Share Vocalinux and Awesome Privacy with your network!