b90bdb075d544f81941c149191b4a5e1d3aeaa36
85
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
4b6e81d441 |
fix(snap): type into native Wayland apps with ydotool (#823)
* fix(snap): inject into native Wayland apps via ydotool Edge rev 7 only ships wtype and xdotool. GNOME rejects the virtual keyboard protocol, so the snap falls back to xdotool and types only into XWayland windows. Native GTK/Qt apps get nothing. Stage ydotool, request the uinput plug, and prefer ydotool when wtype is rejected. Distro ydotool 0.1.x still needs a writable /dev/uinput; log `snap connect vocalinux:uinput` when the Snap cannot open it. * docs(snap): v0.16.2 edge has no uinput plug The store snap (0.16.2, latest/edge rev 7) does not declare uinput. `sudo snap connect vocalinux:uinput` fails with "has no plug named uinput" and dictation only reaches XWayland apps. Spell that out in README, INSTALL, UPDATE, the website changelog, and the store copy so people do not run the 0.17 connect command against rev 7. * docs(snap): drop the unconfirmed 0.17 version from uinput notes Greptile P2: the tree is still 0.16.2 and the release process does not name the next tag. Tell people to refresh until `snap info` lists the uinput plug, instead of waiting on a version we have not cut. |
||
|
|
f48bdda525 |
docs: improve project documentation for a clearer public surface (#634)
* docs: polish project documentation for a clearer public surface Rewrite the README and supporting docs for a more professional first impression: less emoji and badge noise, install-first structure, and a real documentation index. Add a Contributor Covenant, tighten SECURITY and GitHub templates, and make distro/update guides read as product docs instead of internal status notes. * docs: split install guide and add changelog plus support pages Break INSTALL.md into a short primary guide, manual/PyPI package lists, and troubleshooting. Add root CHANGELOG.md and SUPPORT.md, wire them into the docs index and README, and drop the outdated screenshot lag note now that v0.15 assets are on main. * docs: humanize PR copy and remove em dashes Strip em/en dashes from the documentation rewritten in this PR, fix mid-sentence colon fallout from that pass, and smooth a few stiff phrases so the public docs read more natural. * docs: list Faster Whisper after rebase onto main Installer and CLI already accept faster_whisper. Document it next to the other engines, drop the outdated screenshot lag note, and keep the on-device wording honest for local vs remote. |
||
|
|
7664266823 |
feat(commands): localize punctuation voice commands (#640) (#642)
* feat(commands): localize punctuation voice commands by language Voice command phrases were English-only even when recognition language was set to Italian, French, etc. Merge locale punctuation/line-break aliases from the recognition language into CommandProcessor, keep English phrases available, and document the behavior. Fixes #640 Co-authored-by: Jatin K Malik <jatinkrmalik@users.noreply.github.com> * fix(commands): keep line breaks and French apostrophe aliases Attach-left punctuation now eats only horizontal whitespace, so a localized "new line" followed by a period or comma still inserts a newline. French aliases include ASCII, U+2019, and backtick apostrophes, and longest-match still wins so "point" cannot fire inside "point d'interrogation". --------- Co-authored-by: Cursor Agent <cursoragent@cursor.com> Co-authored-by: Jatin K Malik <jatinkrmalik@users.noreply.github.com> |
||
|
|
2c6acbc15e |
fix(inject): layout-aware clipboard paste for Neo and friends (#788)
* fix(inject): resolve paste chord for non-QWERTY layouts ydotool was sending physical KEY_V (47) for Ctrl+V. On German Neo that key types p, so paste opened Print instead. Prefer wtype keysyms when usable; otherwise resolve the evdev key for Latin v via xkbcommon, and read KDE kxkbrc so Plasma Wayland Neo users are covered. Fixes #787 * fix(inject): read Plasma active layout from real session sources kxkbrc has no CurrentLayout/LayoutIndex, so multi-layout Neo always fell back to us at index 0 and paste still hit KEY_V. Prefer D-Bus getLayout or layout_memory.xml for the active layout, keep kxkbrc for the list only, and prefer KDE over leftover GNOME on Plasma. |
||
|
|
46c7914639 |
feat(ci): verify the published release, not just the workflow (#791)
* feat(ci): verify the published release, not just the workflow * fix(ci): filter attestations by predicate, name a missing distribution Two findings from review on #791. The attestations query accepted any predicate, so an SBOM would have read as build provenance while `gh attestation verify`, the command the notes hand users, rejected the artifact. It now sends the same `predicate_type=https://slsa.dev/provenance/v1`. Checked on a real digest: the SLSA filter returns the bundle, a foreign predicate returns 404. check_pypi compared whatever distributions were left, so a release that lost its sdist passed on the wheel alone. Each expected suffix is now named when absent. The lazy urllib import moved past the early return, where it belonged. The other two findings do not hold. `workflow_run.head_branch` carries the tag for a tag-triggered run: the last four release runs report v0.16.2, v0.16.1, v0.16.0 and v0.15.0. And `host_env()` is for the app spawning host binaries out of an AppImage; `scripts/` is in no `packages` entry and the AppImage build never references it, so this cannot run from a bundle. * fix(ci): read release digests from the REST API gh release view --json assets omits digest on gh 2.46, which is what many distros ship and what just verify-release often runs, so every asset failed as undigested. Pull the release through gh api instead, treat a null body as notes FAIL, and prefer removeprefix for the tag. * fix(ci): fail verify when PyPI has an extra distribution Matching release digests alone left a differently named wheel or sdist on PyPI unchecked. Reject those extras too, same suffixes. * fix(test): clear tempfile mock before urllib import Other suites leave a MagicMock in sys.modules['tempfile'], which breaks urllib.request import with a metaclass conflict in the extra PyPI distribution check. Drop the stub (and stale urllib modules) first, and assert the full problem list. * fix(ci): restore real tempfile before urllib in verify test The MagicMock tempfile left by recognition_manager still broke urllib.error on CI. Put the real module back and drop cached urllib packages before the import. --------- Co-authored-by: Jatin K Malik <jatinkrmalik@gmail.com> |
||
|
|
50ea1109d7 |
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 |
||
|
|
bb2b098193 |
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. |
||
|
|
a46000e249 | chore(ci): test the distros the docs promise, and fix the docs that drifted (#773) | ||
|
|
e61133e246 |
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 |
||
|
|
0ba029b015 |
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 |
||
|
|
21b8ee8d05 |
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. |
||
|
|
68b00bd7d8 |
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. |
||
|
|
65381b4582 |
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 |
||
|
|
008fd3138e |
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. |
||
|
|
582d494633 |
chore(build): uv tooling, a Python 3.11 floor, and verified model downloads (epic #701, phases 2.5 & 5) (#713)
* chore(build): migrate justfile tooling to uv All recipes now run through 'uv run' against .venv, with a shared DEV_EXTRAS variable (dev + vad) because uv syncs the environment exactly and would otherwise uninstall the dev tools between recipes. 'deps' installs dev+vad; new 'deps-all' covers every extra. 'build' switches to 'uv build'; 'version' uses --no-sync. Bump [tool.uv] required-version to >=0.12,<0.13 for uv 0.12.x, and raise the mypy python_version target to 3.12: mypy 1.19 in the lock cannot target 3.9 and numpy 2.x stubs need a 3.12 target to parse (mypy is not run in CI or pre-commit; this only affects 'just typecheck'). * fix(tests): isolate test runs from real XDG state; fix Arch model-path crash Local test runs failed en masse (185 failures) while CI stayed green: tests picked up the developer's real ~/.local/share/vocalinux models via the default XDG paths and switched IBus/keyboard code paths through the desktop session env (XDG_CURRENT_DESKTOP/ XDG_SESSION_TYPE on GNOME/Wayland, issue #478 branches). conftest now points XDG base dirs at a throwaway session-wide directory and clears session variables at import time, mirroring a fresh CI runner (paths like MODELS_DIR freeze at first import during collection, so fixture-time isolation is too late). Also guard the Arch branch of _get_system_model_paths(): it removed /usr/local/share/vocalinux/models unconditionally and crashed with ValueError on Arch hosts when a custom XDG_DATA_DIRS never added that path. Add regression tests, and make the lock-file test assert the actual $XDG_DATA_HOME/vocalinux contract instead of the hardcoded ~/.local/share default. * fix(tests): keep the IBus engine double a plain class Python 3.14's unittest.mock re-instantiates a user subclass when it builds child mocks (klass(name=...)). VocalinuxEngine inherits from whatever IBus.Engine resolves to, and the test module bound that to the MagicMock class itself, so every engine instantiation raised TypeError: VocalinuxEngine.__init__() got an unexpected keyword argument 'name' on 3.14 while passing on 3.13. Replace it with a plain stand-in class that still hands out per-instance MagicMocks for the IBus API surface the engine touches. The same pattern remains in test_ibus_engine.py, test_ibus_engine_utils.py, test_ibus_active_engine_guard.py and test_ibus_xkb_wayland.py; those never instantiate the engine, so they are unaffected for now. * fix(install): build the venv from the system Python install.sh looked up a bare `python3` from PATH. Started from an activated virtualenv — a shell left in uv's .venv after `just deps` — it built venv/ from that interpreter, where distro PyGObject is invisible even with --system-site-packages, and the run aborted with Distro PyGObject (python3-gi / python3-gobject) is not importable in the venv. A mismatched venv/ then survived every retry, because the installer reuses whatever venv it finds. - drop an inherited VIRTUAL_ENV (and its bin from PATH) before any interpreter lookup - select the interpreter explicitly: $SYSTEM_PYTHON (default /usr/bin/python3) or PATH python3, preferring one that can import gi, and create the venv with it instead of `python3` - recreate venv/ when it was built by a different interpreter - name the interpreter in the PyGObject guard's error Documents the two environments (uv's .venv for tooling, venv/ for the installed app) in CONTRIBUTING, the justfile header and AGENTS.md, and adds the mismatch symptom to the INSTALL troubleshooting section. * chore(python): raise the supported floor to 3.11 requires-python promised 3.9 while the CI matrix ran 3.9/3.10/3.11/3.13, the classifiers advertised up to 3.14, mypy targeted 3.12 and install.sh enforced 3.9 — five different answers to "which Python does this support". Settle on 3.11 everywhere: pyproject (requires-python, classifiers, black targets), install.sh's MIN_VERSION, check-system-deps.sh, the distro package map, the CI matrix (now 3.11-3.14, matching the classifiers), the release and nightly workflows, and the docs. mypy now targets the floor instead of 3.12, so type checking covers what we promise; numpy 2.x ships stubs in 3.12 syntax that a 3.11 target cannot parse, so its imports are skipped. (mypy has 71 pre-existing errors either way; no CI job gates on it.) uv.lock and the requirements/* exports are regenerated — dropping 3.9/3.10 removes most of the version-conditional resolution branches. tests/test_python_version_policy.py pins the invariant: install.sh's floor, requires-python, the classifiers and the CI matrix must agree. * ci: run lint and tests from uv.lock The lock file and the hash-pinned exports were not enforced anywhere: CI still built a venv by hand and installed unpinned `PyGObject` plus `-e ".[dev,vad]"`, so the tool versions it checked against had nothing to do with the ones `just lint` uses locally. Both Python jobs now use astral-sh/setup-uv and run through `uv sync --locked` / `uv run --locked`, which also fails the build when uv.lock drifts from pyproject.toml. Partially covers phase 5 of #701 (tests install from the lock, lock freshness gate); trusted publishing, artifact checksums and action SHA pinning are still open there. * ci: lint from a dedicated dependency group The lint job failed on this branch: `uv run --extra dev` syncs the whole project, so it tried to build pyaudio from source on a runner that installs no system dependencies. src/pyaudio/device_api.c:9:10: fatal error: portaudio.h: No such file or directory Move black/isort/flake8 out of the `dev` extra into a `lint` dependency group. CI runs `uv run --locked --only-group lint`, which installs the three pure-Python tools and skips the project entirely — no portaudio or girepository headers needed. `just deps` and DEV_EXTRAS request the group alongside the dev extras, so the local environment still carries test and lint tooling at once and `just lint` matches CI version for version. pre-commit is unaffected: its hooks pin their own tool versions upstream. * fix(install): never configure an engine the venv cannot import vosk became an optional extra in #705, but the installer still writes `"engine": "vosk"` into config.json on the whisper.cpp fallback path even when installing the extra only produced a warning. config.json also survives reinstalls — every writer guards on "file does not exist" — so an engine picked by an earlier attempt outlives the venv that supported it. Either way Vocalinux dies at startup: ModuleNotFoundError: No module named 'vosk' - the fallback now writes the vosk config only when `import vosk` actually succeeds, and otherwise leaves the engine configuration alone - a new verify_configured_engine() runs near the end of the install: if config.json selects vosk and the venv cannot import it, it says so and prints both fixes instead of leaving a broken install behind * feat(models): verify every model download against a pinned digest Pins a sha256/md5 per downloadable speech model in src/vocalinux/utils/model_checksums.txt and verifies against it in both install.sh and the runtime downloaders, before the file is moved into place. Verification fails closed. Covers phase 2.5 of #701; see the PR for details. * docs(agents): rebase AGENTS.md onto the #727 rewrite * fix(install): hash models that are already on disk instead of trusting them * refactor(models): one download path per engine, and fix Whisper's * docs(install): drop distros that cannot meet the 3.11 floor * docs: retire the distros and Python versions the 3.11 floor rules out * fix(install): abort instead of warning when Python is below the floor * fix(install): fall back to the system Python, not the one on PATH * fix(install): match an existing venv by installation, not by X.Y * fix(install): repair a config naming an engine the venv cannot import * fix(models): pin VOSK by the sha256 of bytes we fetched, not upstream's md5 * fix(justfile): request the dev set in every recipe that syncs * test(version): scan the docs and website trees for a stale Python floor * docs: correct the stale model-checksum and distro claims * fix(models): stamp the VOSK tree the app extracts, as the installer does * fix(install): replace an unverified VOSK model only once its successor is ready * docs(install): align the Fedora floor with the rest of the tree * test(install): drop an f-string with no placeholders * test(models): stub the downloader probe the VOSK installer test relies on * docs: drop the long dashes from two model-checksum comments * fix(install): gate on the interpreter, not on the distro release label * fix(justfile): export the lint group into requirements/dev.txt * fix(models): unpack the VOSK tree into staging and stamp it before the swap * fix(whisper): adopt a checkpoint an earlier release stored under the catalog name * docs(settings): state what a stray Whisper copy really means for deletion * fix(models): hash a whisper.cpp model found on disk before it reaches ctypes * docs(contributing): stop telling readers to uninstall their linters * fix(install): stop warning off distros the project documents as supported * docs(distros): file the supported derivatives under supported |
||
|
|
ba9cca844b |
chore(release): prepare v0.16.0
Stable minor since v0.15.0. Bumps version to 0.16.0 across app, web, AUR, and Flatpak AppStream, and refreshes docs, changelog, and SECURITY. Tag v0.16.0 after this lands on main. |
||
|
|
994dda10a6 |
fix(install): leftover review notes from #700 and #705 (#706)
* fix(install): leftover review notes from #700 and #705 The scratch dir now holds pip logs and model download temps, so a failed run has something to inspect. Dep, venv, leftover-process, and pip failures use exit codes 2/3/4 instead of a blanket 1. --help says the transcript is written during installation, and the tag hint is --tag=<release>. AGENTS.md no longer claims the hash-pinned exports are consumed (they wait for phases 2/3/5). Docs add a just install line and vocalinux[vosk] next to the PyPI commands. Settings points at --engine=whisper. The whisper.cpp fallback installs ".[vosk]". Fixes leftover review notes from #700 and #705. Refs #701. * fix(install): do not let pip build PyGObject from sdist Fresh --auto --skip-system-deps died on pygobject-3.56.3 even with distro python3-gi present. Install the other runtime deps from pyproject.toml, then pip install --no-deps. Extra installs use the extra's packages only, not pip install ".[extra]". Fixes leftover review notes from #700 and #705. Refs #701. * fix(install): tolerate unset XDG_SESSION_TYPE under set -u --auto with DISPLAY but no login session died on XDG_SESSION_TYPE: unbound variable. The session probe now uses ${XDG_SESSION_TYPE:-}, ${WAYLAND_DISPLAY:-}, and ${DISPLAY:-}. |
||
|
|
95d135ebbf |
chore(deps): lock dependencies with uv and pin every build input
Phases 0-1 of #701. Drops dead deps, moves vosk to an optional extra, adds uv.lock and hash-pinned requirements exports, pins pywhispercpp to 1.5.0. Closes #705. Refs #701. |
||
|
|
71c84fe86b |
chore(build): harden installer script and replace Makefile with Justfile
Stacked bottom of #701. Replaces Makefile with justfile and hardens install.sh (strict mode, logging, HTTP probes, curl -f on model downloads, no v0.10.1-beta fallback). Closes #700. |
||
|
|
279d99fea0 |
fix(whisper): honor bundled GPU libs and skip software Vulkan devices (#674)
* fix(whisper): use bundled GPU libs and skip software Vulkan devices AppImage 0.15.0 shipped a CPU-only pywhispercpp wheel, so the Settings GPU picker and host vulkaninfo never moved inference off the CPU. Rebuild pywhispercpp with Vulkan in the AppImage, log the libraries that actually load, skip llvmpipe in auto-select, and document how to verify. Fixes #604 * fix(appimage): pin gcc/g++ for Vulkan pywhispercpp rebuild Hosts that default cc/c++ to clang fail CMAKE's C++ test because clang does not search gcc's libstdc++ path. Default the rebuild to gcc/g++. |
||
|
|
32a17a9659 |
chore: point GitHub URLs at VocaHQ (#696)
Repo links, install one-liners, and release checks now use github.com/VocaHQ/vocalinux. The site mentions VocaHQ without a redesign. The Codeberg mirror, personal email, and X handle are unchanged. |
||
|
|
ecd5839a13 |
feat(settings): delete unused downloaded speech models (#671)
* feat(settings): delete unused downloaded speech models Add a Remove Model dropdown on the Speech Model page so whisper.cpp, Whisper, and VOSK downloads can be deleted from Settings. The currently selected model is marked in use and cannot be removed until you switch away from it. Fixes #650 * fix(settings): move unused model deletion below the picker Unused downloads is its own group under the model info card. Only leftover files appear, each with a Delete button. The group hides when empty. * fix(settings): collapse unused downloads into an expander Keep leftover models behind one row so a long list does not stretch the Speech Model page. Expand to delete files one at a time; the list scrolls once it grows past a few entries. * fix(settings): keep leftover model list from clipping in the expander Wrap the unused-downloads list so the expander can show a few rows and scroll the rest, instead of painting only the first leftover. |
||
|
|
c337d8f56d |
feat(shortcuts): default to hold Right Alt (push-to-talk) for new installs (#648)
* feat(shortcuts): default to hold Right Alt push-to-talk on first install Double-tap Ctrl conflicts with many desktop and app shortcuts, so new installs now use hold Right Alt (Option) in push-to-talk mode. Existing config.json values are preserved unchanged. Updates README, user guide, AGENTS.md, first-run dialog, website copy, and tests to match the new defaults. * refactor(shortcuts): trim redundant default-change test and comments Drop the fresh-install assertion that duplicated DEFAULT_CONFIG coverage, remove the duplicated Right-Alt rationale from config_manager, and drop a vacuous source-string check from the settings shortcuts test. * fix(shortcuts): align install.sh seeds and preserve legacy toggle mode install.sh still wrote ctrl+ctrl without mode, so merges with the new push_to_talk default became hold-Ctrl. Seed right_alt + push_to_talk for new installs, and migrate existing shortcut-only configs to mode=toggle so prior behavior is unchanged. * style(config): fix black formatting after shortcuts migration * fix(shortcuts): preserve legacy defaults when shortcuts section is missing Existing config files without a shortcuts section previously inherited ctrl+ctrl toggle from DEFAULT_CONFIG. Pin those historical defaults on upgrade so the new first-install Right Alt push-to-talk defaults do not silently change behavior. * docs(agents): unwrap hard-broken dictation control paragraph Keep the AGENTS.md paragraph as a single line like the surrounding prose. |
||
|
|
06e8809c2a |
feat(updates): notify when a newer GitHub release is available (#645)
* feat(updates): notify when a newer GitHub release is available Check the configured release channel shortly after startup and every six hours. When an update exists, show a tray menu item and a green New badge on Settings → About, plus one desktop notification per version, so users can open release notes and update themselves. Co-authored-by: Jatin K Malik <jatinkrmalik@users.noreply.github.com> * refactor(updates): slim UpdateMonitor after ponytail review Drop unused APIs (tick/check_now/version injector/last_* state), integer-only GLib scheduling, and the local demo launcher. Keep tray notify-send local to avoid coupling to recognition_manager. Co-authored-by: Jatin K Malik <jatinkrmalik@users.noreply.github.com> * fix(updates): address Bugbot and harden notify persistence Persist last_notified_version only after notify-send spawns successfully, and sync the tray Update Available item from About-page checks without re-notifying. Also merge latest main and cover the new paths in tests. Co-authored-by: Jatin K Malik <jatinkrmalik@users.noreply.github.com> * test(tray): pin config mock for update-notification assertions CI on 3.9/3.10 showed empty MagicMock set call history after a successful notify-send spawn. Pin tray.config_manager to the test mock and record set() via side_effect so persistence checks are reliable across Python versions. Co-authored-by: Jatin K Malik <jatinkrmalik@users.noreply.github.com> * fix(updates): clear pending About update on failed lookup A failed About check hid the New badge but left _pending_update set, so clearing settings search could revive the badge while status still showed the failed lookup. Clear dialog pending state on failure (tray unchanged) and restore the badge via _set_about_update_badge. * fix(updates): ignore stale UpdateMonitor results after channel switch Bind the channel for each background check and discard the callback when the configured channel changed mid-flight, then re-check immediately — matching About-page stale-channel handling so the tray cannot show the wrong channel's update state. --------- Co-authored-by: Cursor Agent <cursoragent@cursor.com> Co-authored-by: Jatin K Malik <jatinkrmalik@users.noreply.github.com> |
||
|
|
67dde781f5 |
docs(user-guide): note CUDA device 0 limit for dual NVIDIA (#644)
Document that CUDA-backed pywhispercpp always uses device 0, and point to CUDA_VISIBLE_DEVICES or a Vulkan build as workarounds. Co-authored-by: Cursor Agent <cursoragent@cursor.com> Co-authored-by: Jatin K Malik <jatinkrmalik@users.noreply.github.com> |
||
|
|
9c340029e1 |
docs(packaging): prefer Ayatana AppIndicator in Fedora/Arch hints (#638)
* docs(packaging): prefer Ayatana AppIndicator in Fedora/Arch hints Follow-up to #621: align docs, AUR depends, package map, dependency check messages, and troubleshooting copy with the Ayatana-first install path so manual installs and AUR users get a working KDE tray icon. Co-authored-by: Jatin K Malik <jatinkrmalik@users.noreply.github.com> * style(main): black-format Ayatana install hint lines CI black --check failed on the multi-line logger.error calls for the Fedora/Arch package hints. Co-authored-by: Jatin K Malik <jatinkrmalik@users.noreply.github.com> --------- Co-authored-by: Cursor Agent <cursoragent@cursor.com> Co-authored-by: Jatin K Malik <jatinkrmalik@users.noreply.github.com> |
||
|
|
fbb81238f5 |
chore(release): prepare v0.15.0 (#609)
* chore(release): prepare v0.15.0 Bump version across app, web, AUR, and Flatpak metainfo. Document GitHub release-notes rules, refresh README/UPDATE/INSTALL/USER_GUIDE for the 0.15 series (sidebar settings, AppImage, auto-pause/keepalive, Vulkan device selection), and align website product surfaces with shipped features. Co-authored-by: Jatin K Malik <jatinkrmalik@users.noreply.github.com> * docs(release): fold latest main into v0.15.0 notes Include dictation capitalization/trailing-space, ibus-wayland injection, IBus teardown, and settings notice polish landed on main after the initial release prep rebase. Co-authored-by: Jatin K Malik <jatinkrmalik@users.noreply.github.com> * docs(release): include Bluetooth mic crash fix in v0.15.0 notes Fold #599 (PortAudio Bluetooth SCO probe heap corruption) into README, UPDATE, changelog, AppStream, and release history after rebasing onto main. Co-authored-by: Jatin K Malik <jatinkrmalik@users.noreply.github.com> * docs(release): include language catalog expansion in v0.15.0 notes Fold #616 (Hungarian and expanded speech languages, Fixes #565) into README, UPDATE, changelog, AppStream, PRODUCT.md, and release history after rebasing onto latest main. Co-authored-by: Jatin K Malik <jatinkrmalik@users.noreply.github.com> * docs(release): fold settings footer and language follow-ups into v0.15.0 Include #618 sidebar dictation footer, #619 Custom Shortcut Record/Set restore, and #617 English (India) Whisper language mapping after rebasing onto latest main. Co-authored-by: Jatin K Malik <jatinkrmalik@users.noreply.github.com> --------- Co-authored-by: Cursor Agent <cursoragent@cursor.com> Co-authored-by: Jatin K Malik <jatinkrmalik@users.noreply.github.com> |
||
|
|
40546e7f07 |
fix(uninstall): remove IBus data dir & stop app by PID file not pgrep -f (#597)
* fix(uninstall): remove IBus runtime data directory on uninstall Discussion #559 reported ~/.local/share/vocalinux-ibus surviving uninstall. PR #569 fixed launcher wrappers but not this IBus IPC directory (socket, PID file, stderr log). Stop the engine via engine.pid when present, remove the directory regardless of --keep-data, and verify it in post-checks. Co-authored-by: Jatin K Malik <jatinkrmalik@users.noreply.github.com> * fix(scripts): stop Vocalinux by PID file, not pgrep -f pgrep -f vocalinux matches any process whose argv mentions the repo path (editors, shells, test runners) and can kill unrelated local work. AGENTS.md already warns against this. - Prefer PIDs from instance.lock and vocalinux-ibus/engine.pid - Verify each candidate via is_vocalinux_process (python -m vocalinux.main, ibus_engine.py under vocalinux, or argv0 basename vocalinux/vocalinux-gui) - Narrow pgrep fallback only for those entrypoint patterns - Drop the redundant stop_ibus_engine helper (engine covered via PID file) - Still remove ~/.local/share/vocalinux-ibus on uninstall (#559) - Align install.sh and correct the pkill tip in docs/UPDATE.md Co-authored-by: Jatin K Malik <jatinkrmalik@users.noreply.github.com> * fix(scripts): detect setuptools console-script Vocalinux processes Real installs run as `python …/bin/vocalinux`, so argv0 is python and the previous argv0-basename check missed the live app (instance.lock PID was skipped). Scan all cmdline args for …/bin/vocalinux or …/bin/vocalinux-gui instead, which still avoids matching a bare repo directory path. Co-authored-by: Jatin K Malik <jatinkrmalik@users.noreply.github.com> --------- Co-authored-by: Cursor Agent <cursoragent@cursor.com> Co-authored-by: Jatin K Malik <jatinkrmalik@users.noreply.github.com> |
||
|
|
2a0e11e077 | fix: correct ydotool service setup guidance (#560) | ||
|
|
8e0439e359 |
docs: multi-distro tray icon FAQ (apt, dnf, pacman) (#584)
Tray troubleshooting previously only showed apt, which is wrong on Fedora and Arch. Document AppIndicator install for Debian/Ubuntu, Fedora, and Arch in INSTALL.md and the website troubleshooting page. Fixes #570 |
||
|
|
3d65e5cea9 |
chore(release): prepare v0.14.2 (#537)
* chore(release): prepare v0.14.2 IBus stability patch on the 0.14 series: engine process launch after XDG import (#534) and FocusIn gate for first dictation on GNOME Wayland (#533). Keep 0.14 series feature list in README/UPDATE for patch releases; changelog and AppStream note only the delta. Document that rule in RELEASE_PROCESS. * fix(release): address 0.14.2 review findings - Dates: v0.14.2 uses 2026-07-17 (not before 0.14.1) - AUR: sha256sums=SKIP until tag; document updpkgsums in RELEASE_PROCESS - Single version source: package __init__ re-exports version.py; User-Agent uses __version__ - Docs: drop redundant series recaps; shrink AppStream/changelog delta - README ecosystem status: Stable v0.14.2 (not Beta) * docs(release): include settings scroll fixes in 0.14.2 notes Cover #538 and #541 (scrollable settings tabs / wheel polish) in README, UPDATE, website changelog, AppStream, and version history after rebasing onto main. |
||
|
|
1783331c0d |
chore(release): prepare v0.14.1 (stable for Flathub) (#532)
Drop beta suffix so Flathub stable AppStream lint accepts the release. |
||
|
|
853c1504b2 |
chore(release): prepare v0.14.1-beta (#525)
Patch release for Flatpak/AUR packaging and post-0.14.0 fixes: installer wrappers, ydotool dual API and Flatpak socket probe, model download timeouts, layout-aware hotkeys, and related docs/web bumps. |
||
|
|
3deb523efd |
feat(flatpak): add Flatpak packaging for universal distribution (#484)
* feat(flatpak): add Flatpak packaging for universal distribution Adds a Flathub-ready Flatpak for the whisper.cpp engine, plus the minimal runtime adaptations Flatpak actually needs — deliberately scoped down from the earlier attempts (#416, #428), which bundled unrelated whisper.cpp GPU, audio, and clipboard changes. Packaging (packaging/flatpak/): - app manifest, AppStream metainfo, and desktop entry (com.vocalinux.Vocalinux) - generated offline Python dependency modules (flatpak-pip-generator) - bundled shared modules: PortAudio, libayatana-appindicator, xdotool, xsel Runtime changes (Flatpak-only, behaviour unchanged outside a sandbox): - utils/paths.py: honour XDG_CONFIG_HOME/XDG_DATA_HOME so config, models and runtime data live under ~/.var/app/<app-id> in the sandbox - autostart uses `flatpak run <app-id>` (the host launcher is always on PATH) - text injection and keyboard backend detect a Wayland host reached via XWayland (no Wayland socket granted) and route through xdotool / pynput-on-X11 - tray indicator references the exported `<app-id>-*` host icon theme names Also: a CI workflow building x86_64 + aarch64 bundles, and README / packaging README / distro-compatibility doc updates. Verified with a full flatpak-builder build + bundle install on aarch64: pywhispercpp loads its native libraries without LD_LIBRARY_PATH, XDG paths redirect correctly, bundled tools are present, and the entrypoint runs. Manifest and builddir lint are clean and AppStream metadata validates. Closes #167 * chore(flatpak): sync metainfo version to 0.14.0-beta Also ignore flatpak-build-dir/ so local builder output stays untracked. * refactor(flatpak): trim docs and comments after ponytail review Drop redundant packaging README command blocks, shrink path/tray/autostart comments, and stop ignoring a non-canonical build-dir name. * fix(flatpak): close readiness gaps for v0.14.0-beta Route log files through XDG data dirs so Flatpak keeps them under ~/.var/app, refresh AppStream screenshots to vocalinux.com, and use the SPDX GPL-3.0-only license id. * fix(flatpak): enable global hotkeys via evdev and --device=input pynput over XWayland only receives keys for the focused X client. Prefer evdev inside Flatpak and grant /dev/input so shortcuts work app-wide. * fix(flatpak): inject text with ydotool/uinput on Wayland xdotool only reaches XWayland clients, so typing appeared to succeed but never hit native apps. Ship ydotool, grant --device=all for uinput, start ydotoold on demand, and type via uinput inside the Flatpak. * fix(flatpak): paste via wl-copy + Ctrl+V instead of typing ydotool type injects character-by-character. Ship wl-clipboard, grant --socket=wayland, and prefer clipboard paste for instant insertion. * test(text-injection): expect ydotool default key-delay of 2 Matches the shipped VOCALINUX_YDOTOOL_KEY_DELAY default used when clipboard paste falls back to character typing. |
||
|
|
6545e4ea66 |
feat(packaging): add AUR release package and CI publish (#518)
* feat(packaging): add AUR release package and CI publish Ship packaging/aur/vocalinux for yay installs of tagged releases, wire AUR publish into the release workflow (skipped until secrets are set), and document one-time maintainer setup. Fixes #116 * refactor(packaging): slim AUR PR to PKGBUILD + CI Drop systemd unit, install script, website edits, and long docs. Keep release PKGBUILD, minimal publish job, and short AUR setup notes. |
||
|
|
8a085b5395 |
chore(release): prepare v0.14.0-beta
* chore(release): prepare v0.14.0-beta - Bump version 0.13.0-beta -> 0.14.0-beta (src/vocalinux/version.py) - README: refresh What's New for v0.14.0-beta and bump comparison table - docs/UPDATE.md: add v0.14.0-beta section and bump checkout example - SECURITY.md: mark 0.14.x supported, 0.13.x unsupported - docs/RELEASE_PROCESS.md: add v0.14.0-beta to version history - web: bump package.json / package-lock.json / page.tsx schema + badge - web changelog: add v0.14.0-beta entry * ci(codecov): allow 0.5% project coverage threshold Prevents version-bump/docs-only PRs from being blocked by minor coverage fluctuations (e.g. -0.04% on a release-prep branch). * docs(release): correct chronological order in version history table * docs(release): add #509 shortcut UI exclusivity fix to v0.14.0-beta notes |
||
|
|
85927be4b4 |
feat(asr): FunASR/SenseVoice support for the Remote API engine (#468)
* feat(asr): add chat audio remote benchmarking * style(asr): apply black formatting * test(main): include remote API model kwarg * test(asr): add coverage for chat-completions error handling and parsing Add 10 new tests to cover uncovered lines in PR #468: - ConnectionError handler in _try_chat_completions_audio_api (lines 1601-1602) - _parse_chat_completion_transcription edge cases: None, list content, empty/whitespace strings, JSON without text field, plain text, and single-line language metadata (lines 1610, 1613-1621, 1625, 1629-1640) Coverage for recognition_manager.py improved from 85% to 86%. * feat(asr): support FunASR remote responses * refactor(asr): scope remote FunASR support to the #469 path Drop chat-completions/Qwen client, evaluation notes, and the remote ASR benchmark harness. Keep model field, tolerant OpenAI response parsing, SenseVoice label stripping, FunASR docs, and tests. |
||
|
|
8f32f01f0e | fix(installer): include xsel for Wayland clipboard fallback (#496) | ||
|
|
63c1053d8b |
chore(release): prepare v0.13.0-beta (#489)
- Bump version 0.12.0-beta -> 0.13.0-beta (src/vocalinux/version.py) - README: refresh "What's New" for v0.13.0-beta and bump comparison table - docs/UPDATE.md: add v0.13.0-beta section and bump checkout example - SECURITY.md: mark 0.13.x supported, 0.12.x unsupported - docs/RELEASE_PROCESS.md: add v0.13.0-beta to version history - web: bump package.json / package-lock.json / page.tsx schema + badge - web changelog: add v0.13.0-beta entry and refresh dateModified |
||
|
|
e0ac5e409c |
feat(whisper): add guided whisper.cpp model variants (#465)
* feat(whisper): refine whisper.cpp model selection * refactor(whisper): exclude diarization model from picker * docs(whisper): document guided model variants * fix(ui): cap settings dialog height at 600px to prevent excessive height on high-res displays |
||
|
|
9c5065f452 | fix(input): clarify KDE Wayland IBus setup (#466) | ||
|
|
748d318515 |
chore(release): prepare v0.12.0-beta (#459)
* chore(release): bump version to v0.12.0-beta Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) * docs(release): document v0.12.0-beta changes Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) * docs(security): update supported beta version Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) * chore(web): refresh v0.12.0-beta website metadata Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) * fix(web): type lazy syntax highlighter Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) |
||
|
|
78f377fd0f |
fix(models): correct download size metadata for whisper.cpp and VOSK (#453)
* fix(models): correct download size metadata for whisper.cpp and VOSK The size_mb values in WHISPERCPP_MODEL_INFO were using parameter counts instead of actual download sizes, causing the UI to display incorrect sizes (e.g., showing 39 MB when actual download is 74 MB). Updated all whisper.cpp models with verified sizes from Hugging Face and adjusted VOSK medium/large sizes to better represent actual downloads. Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) * docs: update model download sizes to match actual downloads Update whisper.cpp size references from parameter counts to verified download sizes (e.g., tiny: 39MB → 74MB, medium: 769MB → 1.5GB, large: 1.5GB → 3.0GB). Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) * docs(web): update model sizes across website pages Sync whisper.cpp size references on gpu-acceleration, compare, whisper-model-guide, offline, and homepage with corrected download sizes. Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) |
||
|
+1 |
84d2e1a929 |
feat: add remote API speech recognition engine with installation and configuration support. (#335)
* feat: add remote API speech recognition engine with installation and configuration support. * fix: reformatted by black * fix: unit test in test_main_initializes_components * fix typo * Apply suggestion from @jatinkrmalik Co-authored-by: Jatin K Malik <jatinkrmalik@gmail.com> * feat: Validate remote API URL format, enhance connection testing, update default remote API endpoint. * fix typo * feat: add remote API recognition manager test suite * fix: add Python 3.14 support and lxml>=6.1.0 (fixes #404) - Add Python 3.14 to classifiers - Add lxml>=6.1.0 to dependencies to fix install on Python 3.14 (CachyOS, Arch) - Update black target-version to include py314 Fixes #404 Closes #405 * fix(install): validate pyproject.toml/setup.py content before entering local repo mode (fixes #396) Fix false local repo detection when users run `uv init` before installation. Changed detection from naive file existence check to content validation: - For pyproject.toml: check for `name = "vocalinux"` - For setup.py: check for "vocalinux" string - Falls through to remote clone mode if neither matches Fixes #396 * fix(recognition): preserve final speech on stop (fixes #401) Replace hard-coded stop tail trim with configurable stop_sound_guard_ms (default 200ms) so quick key release no longer clips the final spoken word in push-to-talk mode. Co-authored-by: Fakhareddine <fe.sabbari@claranet.com> * fix(ibus): handle engine instance destruction on layout switch (fixes #388, closes #389) Override do_destroy() to clear _active_instance when IBus destroys the engine on keyboard layout switch (Super+Space). Add retry logic in inject_text() — on NO_ENGINE, re-activates via switch_engine() and retries once to recover cleanly. Co-authored-by: Sbenazar <127324689+Sbenazar@users.noreply.github.com> * chore: ignore opentree worktree dir and config (#412) .opentree/ contains git worktrees created by the opentree CLI tool (https://github.com/axelgar/opentree). These are local developer artifacts and should not be tracked. opentree.toml is the per-repo opentree config - also local/personal, not something contributors need to share. * test(ibus): fix do_destroy CI failure (issubclass TypeError without IBus) (#410) * test(ibus): fix do_destroy super() test failing in CI without IBus installed Patch builtins.super instead of relying on mock_ibus.Engine.do_destroy, which fails with issubclass() TypeError when VocalinuxEngine is built with object as its base (IBus unavailable in CI). * test(ibus): fix MagicMock.__new__ TypeError on Python < 3.13 VocalinuxEngine inherits from MagicMock at import time (IBus is mocked via sys.modules at test-file level). Calling VocalinuxEngine.__new__(VocalinuxEngine) delegates to MagicMock.__new__ which runs issubclass(cls, AsyncMockMixin). On Python < 3.13 that raises TypeError when the module-cached class is a MagicMock spec object rather than a real type. Fix: introduce _make_engine() helper that allocates via a thin concrete subclass whose __new__ calls object.__new__ directly, bypassing the MagicMock allocation path entirely. Verified across all three TestVocalinuxEngineDestroy test methods. * test(ibus): use unbound do_destroy call to avoid MagicMock interference The previous fix (thin _ConcreteEngine subclass) still failed because VocalinuxEngine inherits MagicMock.__getattribute__, which intercepts .do_destroy on instances and returns a mock attribute rather than the real method - regardless of Python version. Call VocalinuxEngine.do_destroy(engine) directly with a plain object() as the self argument instead. do_destroy only uses self for identity comparison (is VocalinuxEngine._active_instance), so any object works. This bypasses MagicMock's MRO entirely. * test(ibus): reload module and instantiate real engine class for do_destroy tests * refactor(ibus): extract destroy handler for stable cross-version tests * test(ibus): cover destroy helper branches and do_destroy delegation * test(ibus): stabilize do_destroy CI behavior across environments Avoid brittle super() mocking in no-IBus environments and gate the IBus-only parent destroy path test behind runtime capability checks to prevent false CI failures on Python matrix runners. * test(ibus): remove flaky do_destroy class-path assertions Drop brittle class-method destroy tests that fail under mocked GI/IBus on older Python runners and keep deterministic helper-level coverage for destroy behavior. * fix(text-injection): recover from IBus runtime failures (#411) * fix(ibus): retry and recover from transient socket failures Harden IBus injection against connection-refused, timeout, and engine process races so dictation is more resilient in real-world desktop sessions. Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) * fix(text-injection): fallback to non-IBus backend on runtime IBus failures Allow dictation to continue when IBus runtime injection fails by automatically switching to xdotool/wtype/ydotool fallbacks instead of returning an immediate failure. Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) * fix(ibus): preserve live socket injection during recovery Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) * test(coverage): add runtime fallback and ibus retry coverage Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) * fix(ibus): probe engine readiness at startup and harden NO_ENGINE retries (#391) * fix(ibus): wait for engine readiness before first injection Add a startup readiness probe that pings the IBus engine after activation so first-use dictation no longer races do_enable(). Also switch NO_ENGINE retry behavior to exponential backoff and cover the new flow in tests. Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai> * test(ibus): cover readiness probe edge cases Add focused tests for the new startup readiness path, including direct PING success, retry exhaustion, unexpected responses, and engine re-activation before injection. This raises coverage on vocalinux.text_injection.ibus_engine from 68% to 73% in the targeted suite. Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai> * fix(text-injection): avoid blocking IBus and clipboard fallbacks Warm IBus in the background so startup no longer stalls while readiness probing, and make clipboard helpers fail fast with per-session health caching so non-ASCII ydotool fallback does not lock dictation. Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai> * test(ibus): update retry tests for merged NO_ENGINE recovery logic --------- Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai> * chore(deps-dev): bump postcss from 8.4.39 to 8.5.10 in /web Bump postcss dev dependency (CSS processing). No runtime impact. * fix(logging): clean up runtime log noise and cache hardware detection Clean up duplicated code artifacts from recent merges and cache hardware detection to eliminate ~1.3s UI lag when opening settings dialog. * test: cover recognition internals and suppress desktop notifications (#414) * test(recognition): cover _perform_recognition and _enqueue_audio_segment The previous commit demoted logger.info('DEBUG: ...') to logger.debug(...) and removed a dead loop body in _perform_recognition. Codecov flagged 12 uncovered lines — all inside _perform_recognition and _enqueue_audio_segment, which existing tests always mocked out. Add direct (non-mocked) tests for both methods: - _enqueue_audio_segment: * empty buffer → early return * normal buffer → enqueues correctly * full queue → drops oldest and retries - _perform_recognition: * processes a segment then exits on None signal * drains remaining items on None signal * exits cleanly when idle with empty queue * continues looping on queue timeout while recording These tests exercise the actual threading/queue logic and cover the logger.debug / logger.warning calls as a side effect. * test(conftest): suppress real desktop notifications during test runs _show_notification shells out to notify-send, which pops up real system notifications when tests exercise the model-not-ready path. Add an autouse fixture that patches _show_notification for all tests EXCEPT those whose name contains 'show_notification' (so direct tests of the helper still verify subprocess.Popen calls). * chore(gitignore): ignore .sisyphus directory * fix(install): improve openSUSE dependency handling with fallbacks (#418) The suse case in install_system_dependencies() previously installed all packages blindly, causing the entire install to fail if a single package was unavailable (e.g., glslang on some openSUSE repos). Changes: - Check each package individually with rpm -q before installing - Add graceful fallback for glslang -> glslang-devel - Add graceful fallback for libappindicator-gtk3 -> typelib-1_0-AppIndicator3-0_1 -> libayatana-appindicator3-1 - Continue installation with warning instead of hard failure Fixes #407 * fix(remote_api): re-init engine on resume from suspend Add a remote_api branch to reinitialize_after_resume mirroring the dispatch chain in __init__, so the engine is recreated after a suspend/resume cycle. Null self._http_session alongside the existing self.model and self.recognizer resets so a stale pooled-connection session is not reused after resume. * perf(remote_api): pool HTTP connections via requests.Session Introduces a manager-owned requests.Session (_http_session) so all remote_api engine HTTP traffic reuses a single connection pool instead of opening a new connection for every request. Lifecycle: - __init__: self._http_session = None (attribute always present) - _init_remote_api: close prior session if it exists, then create a new requests.Session(); use session.get for the connection test. Prevents connection leaks on back-to-back inits (re-init guard). - _try_openai_api / _try_whispercpp_server_api: replaced requests.post with self._http_session.post; local 'import requests' retained for the requests.exceptions.ConnectionError catch. - reconfigure: captures old engine before self.engine is updated; closes and nulls _http_session when switching away from remote_api. - reinitialize_after_resume: nulls _http_session before _init_remote_api recreates it. Tests: added TestHTTPSession class (4 cases) in tests/test_recognition_manager_remote_api.py; all 40 tests pass. Updated _setup_requests_get_*/post_* helpers to configure the session instance (mock_requests.Session.return_value) instead of the module-level mock, keeping all 36 pre-existing tests green. * docs: add remote HTTP transcription setup guide * fix(install): avoid asking for remote server URL twice run_interactive_install collects the URL upfront in Step 3, but install_python_package re-declared REMOTE_API_URL as local and prompted again, ignoring the wizard value. Inherit the wizard's value instead. * ui(settings): hide remote API behind an Advanced tab Addresses upstream PR #335 review feedback from @jatinkrmalik that remote API is not plug-and-play and should not be a peer of the local engines for casual users. - Remove 'Remote API' from the Speech Engine tab's engine combo; the combo now shows local engines only (vosk / whisper / whisper_cpp). - Add a new 'Advanced' notebook tab with a 'Remote Server' group containing a 'Use remote server' switch, the existing URL/Key/ Endpoint fields, and the Test Connection button + status label. - The switch overrides the local engine selection. get_selected_settings returns engine='remote_api' iff the switch is on, otherwise the combo selection. URL/Key/Endpoint persist independently of the toggle state. - _load_and_apply_settings: if config has engine='remote_api', the switch is turned on and the combo is set to a sensible local fallback (whisper_cpp, or first available local engine) so toggling off has somewhere to land. - _update_engine_specific_ui no longer special-cases the combo for remote_api (the combo never has it); it hides the model row, info card and legend whenever the remote switch is on. - Update HTTP_REMOTE.md to reflect the new tab location. * ui(remote-api): mark remote API as advanced and default toggle off - install.sh: relabel option 4 as 'REMOTE API (ADVANCED)' to signal it as a non-default, advanced engine choice. - settings_dialog.py: explicitly initialise the 'Use remote server' switch to inactive at widget creation, making the default state unambiguous (saved-config load logic still toggles it on for existing remote_api installs). * fix(remote-api): preserve trailing newline from transcription responses The remote-API transcription path stripped all whitespace from the upstream response before injection, silently discarding a trailing newline that some pipelines (post-processing proxies, fixup layers) emit to signal "submit" / Enter. Whisper itself doesn't emit trailing newlines, so for the common case this is a no-op; for users with a post-processing layer it stops their submit signal from being eaten. _filter_non_speech now uses a whitespace-stripped probe for its non-speech pattern checks but returns probe + "\n" when the original input ended with "\n" or "\r" (CRLF normalises to LF). The redundant .strip() at the call site is removed so the function receives the raw API text. Existing filter behaviour (BLANK_AUDIO, music notes, low speech-content ratio, etc.) is unchanged. Four new unit tests cover the trailing-newline cases. * test(remote-api): cover transcribe exception handler and unknown-engine reinit branch Adds two unit tests buttoning up previously-uncovered defensive branches: - _transcribe_with_remote_api swallows unexpected exceptions and returns "" — verified by raising RuntimeError from _try_whispercpp_server_api. - reinitialize_after_resume short-circuits without dispatching when the engine attribute is unknown — verified by mutating engine to a bogus value and asserting no _init_* method is called. * feat(settings): add Advanced tab with whisper.cpp anti-hallucination parameters (#415) * feat(settings): add Advanced tab with whisper.cpp anti-hallucination parameters Add a new Advanced settings tab that exposes whisper.cpp decoding parameters to help users reduce hallucinations and tune recognition behavior. Parameters exposed: - No Timestamps (default: on) - Suppress Non-Speech Tokens (default: on) - No Context (default: on) - Initial Prompt (default: empty) - Temperature (default: 0.0) - Temperature Increment (default: -1.0, disables fallback) - Entropy Threshold (default: 2.4) - Logprob Threshold (default: -1.0) - No-Speech Threshold (default: 0.6) All parameters are saved under the advanced config section and are only active when the whisper.cpp engine is selected. The UI grays out these settings and shows an explanatory note when another engine is active. Changes: - config_manager.py: add whisper.cpp keys to DEFAULT_CONFIG[advanced] - recognition_manager.py: read params from kwargs/config, pass to Model() - main.py: pass advanced settings from config to SpeechRecognitionManager - settings_dialog.py: new Advanced tab with switches, spin buttons, entry - tests: update test_main.py assertions, add whispercpp_advanced_defaults and whispercpp_advanced_persistence tests Fixes #363 * fix(whispercpp): gracefully filter unsupported params for pywhispercpp 1.4.1 pywhispercpp 1.4.1 does not expose no_timestamps or suppress_non_speech_tokens to Python even though they exist in the underlying whisper.cpp C struct. This causes AttributeError on startup. Add _load_model_with_compatible_params() which catches AttributeError, identifies the unsupported param from the error message, removes it, and retries until the model loads successfully or a non-AttributeError occurs. Also refactor _build_whispercpp_model_kwargs() from _load_whispercpp_model() for testability. Fixes local startup crash reported during PR testing. * fix(whispercpp): persist advanced settings correctly * chore(config): add advanced power-user default Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) * feat(settings): gate advanced controls behind power-user opt-in Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) * fix(whispercpp): prefilter unsupported native params * fix(settings): polish advanced whispercpp controls * fix(installer): reuse existing whispercpp builds (#421) * docs: clarify PyPI installation requirements (#423) * chore(deps): bump next (#429) Bumps the npm_and_yarn group with 1 update in the /web directory: [next](https://github.com/vercel/next.js). Updates `next` from 15.5.15 to 15.5.18 - [Release notes](https://github.com/vercel/next.js/releases) - [Changelog](https://github.com/vercel/next.js/blob/canary/release.js) - [Commits](https://github.com/vercel/next.js/compare/v15.5.15...v15.5.18) --- updated-dependencies: - dependency-name: next dependency-version: 15.5.18 dependency-type: direct:production dependency-group: npm_and_yarn ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * fix(installer): repair pywhispercpp library loading (#433) * fix: play stop sound immediately on release (#426) * fix(recognition): play stop sound after audio thread joins to prevent capture on short recordings (#436) Moves play_stop_sound() to after audio_thread.join() but before the final buffer processing. This closes the race where the audio thread's in-flight chunk could capture the stop cue for short (≤ ~192 ms) recordings, while still feeling immediate compared to the original placement after all recognition cleanup. * fix(install): correct openSUSE Tumbleweed dependencies (#420) * fix(install): correct openSUSE Tumbleweed dependencies * style: format openSUSE installer changes --------- Co-authored-by: Jatin K Malik <jatinkrmalik@gmail.com> * fix(installer): harden Debian compatibility layer (#437) Add missing pywhispercpp build prerequisites (libssl-dev, autoconf, automake, libtool, patchelf) to both Debian and Ubuntu apt package lists. These are not pulled in transitively on clean Debian installs and cause CMake bootstrap failures during source compilation. Add runtime smoke test for pywhispercpp to verify_installation() so "installs but does not run" failures surface at install time with actionable diagnostic steps instead of silently at first launch. Treat Debian as a first-class target — remove the "primarily designed for Ubuntu" warning for Debian systems. Remain conservative for non-Debian, non-Ubuntu distros. Add DEBIAN_FRONTEND=noninteractive to all apt install commands to prevent interactive debconf prompts from blocking unattended installs. Improve ydotool failure messaging on Debian with compile-from-source instructions since ydotool is not packaged in Debian standard repos. Add Debian-specific troubleshooting section to DISTRO_COMPATIBILITY.md covering build deps, ydotool compilation, scoped source builds, and libwhisper.so resolution diagnostics. References: https://medium.com/@cslev/talking-to-my-linux-box-without-talking-to-the-cloud-vocalinux-on-debian-without-the-tears-10bf053ea21b * fix(install): refresh ldconfig after openSUSE typelib install, clarify python3XY placeholder convention (#438) - Add 'sudo ldconfig' after zypper install in suse_install_appindicator_runtime() to ensure GI typelib cache is refreshed so newly installed typelib packages are immediately discoverable by GObject introspection - Add section-level comment in distro-package-map.yaml explaining that python3XY is a placeholder convention resolved at install time by suse_python_package_prefix() / suse_python_package_candidates() Follow-up polish from review of #420. * fix(whispercpp): reduce CPU threads and ensure GPU backend builds in dev mode (#439) * fix(whispercpp): reduce CPU threads and ensure GPU backend builds in dev mode - Reduce default n_threads from cpu_count() (16) to max 8 (CPU) / max 4 (GPU) - Add runtime pywhispercpp GPU capability detection (_detect_pywhispercpp_gpu_backend) - Make n_threads configurable via whispercpp_n_threads config option - Fix install.sh regression: GPU source build was skipped in --dev mode - Extract GPU install logic into install_whispercpp_with_gpu_support() - Add is_pywhispercpp_gpu_capable() to detect GPU libs in installed pywhispercpp - Force rebuild pywhispercpp when GPU is detected but lacks GPU support Fixes CPU spiking to 100% on GPU systems due to excessive thread count and ensures dev installs get GPU acceleration. * fix(audio): skip output-only HDMI devices when selecting input device Add _resolve_valid_input_device() that filters devices by maxInputChannels > 0, falling back preferred → default → first valid. Previously, HDMI output devices (0 input channels) could be selected as the default audio input, causing 'Invalid number of channels' errors. Now both _record_audio() and _attempt_audio_reconnection() validate the device has input channels before probing or opening the stream. * fix(whispercpp): treat n_threads=0 as auto-detect, not literal zero Config default whispercpp_n_threads=0 was being passed directly to whisper.cpp as n_threads=0, causing 'cannot create std::vector larger than max_size()' transcription errors. Now 0 and negative values are treated as auto-detect, falling through to the smart thread count logic (max 4 with GPU, max 8 on CPU). * test: add whispercpp_n_threads to mock assertions and config defaults Update test_main.py mock to include the new whispercpp_n_threads=0 kwarg and test_config_manager.py to assert the default config value. * fix(audio): harden device enumeration against test mocks The new HDMI-filter helper and GPU-backend detector crashed unit tests that pass MagicMock() for the PyAudio/pathlib surfaces: - _find_pywhispercpp_shared_library_dirs hit TypeError when tests monkey-patch os.stat (Path.is_dir internally calls S_ISDIR on a non-int st_mode). Widen the catch to OSError/TypeError/ValueError so unconfigured stat results just skip the candidate dir. - _resolve_valid_input_device crashed with 'MagicMock > int' on audio.get_device_count() / info.get('maxInputChannels'). Coerce the count, treat zero/unknown as 'use system default', and include non-dict info objects rather than filtering everything out. No production behavior changes: real PyAudio always returns int counts and dict device info, so the HDMI filter still works as designed. * test(recognition): cover new HDMI filter and GPU backend detector - TestResolveValidInputDevice: 9 cases for the HDMI-filter resolver (preferred passthrough, HDMI fallback, no-default fallback, no-input None return, and three mock-tolerance escape hatches). - TestDetectPywhispercppGpuBackend: 4 cases for runtime GPU library detection (cpu/vulkan/cuda/empty dirs). - TestFindPywhispercppSharedLibraryDirsResilience: proves the widened exception catch survives a TypeError from pathlib.Path.is_dir. * test(text-injection): wait for IBus init thread to avoid race on slow runners The IBus environment promotion runs in a background daemon thread spawned by TextInjector.__init__. The two TestIBusRuntimeFallback cases asserted on injector.environment immediately after construction, which raced the thread on Python 3.10 GHA runners and produced intermittent failures. Join the init thread (with a generous timeout) before asserting, so the test exercises the post-warmup state deterministically. * chore(release): prepare v0.11.0-beta (#440) - Bump version from 0.10.2-beta to 0.11.0-beta - Update all version references across codebase - Add What's New section for v0.11.0-beta in README and docs/UPDATE.md - Add v0.11.0-beta entry to website changelog - Update SECURITY.md supported versions to 0.11.x - Update website schema, badge, and package version - Update RELEASE_PROCESS.md version history table * fix(startup): allow launch without pynput backend (#448) * feat(vad): add Silero VAD and drop silence-only buffers (#447) * feat(vad): replace amplitude-based VAD with Silero neural network Replace the simple amplitude threshold VAD with Silero VAD, a lightweight neural network model that classifies audio as speech/non-speech. This addresses the root cause of #302 (silence not stopping recording) and - Add silero_vad.py wrapper using ONNX Runtime with bundled Silero v5 model - Accumulate 512-sample chunks with 64-sample context window per official API - Map existing vad_sensitivity (1-5) to Silero thresholds (0.8-0.3) - Use max() across sub-chunks to avoid missing speech onsets - Fall back to amplitude VAD when onnxruntime is unavailable - Add onnxruntime as optional dependency (pip install vocalinux[vad]) Closes #344 * address review: add tests, LICENSE, logging, validation fixes - Add unit tests for SileroVAD (process, reset, fallback, sensitivity mapping) - Add MIT LICENSE for bundled Silero VAD model in data/ - Log which VAD backend is active on init - Validate chunk length in SileroVAD.process() - Skip is_speech check until a full Silero chunk is processed - Add thread safety note to SileroVAD docstring - Document resampling rate effect on VAD resolution - Comment explaining why load_silero_vad() catches broad Exception * address review: bundle license, document [vad] extra, surface backend in UI - Pack data/LICENSE into the wheel (pyproject package-data) so attribution travels with the model - README: short note on enabling neural VAD via vocalinux[vad] - Settings: VAD Sensitivity row subtitle shows the active backend; if onnxruntime is missing, an info-row beneath the group spells out the install command and the restart requirement - silero_vad.is_silero_available() probes onnxruntime + model presence w/o instantiating an InferenceSession - CI installs .[dev,vad] so Silero paths get exercised - Integration test for _record_audio Silero branch (mocked pyaudio + scripted SileroVAD), covering silence flush, speech reset, and sensitivity threshold mapping at sens=1 vs sens=5 - Tests no longer skip when other modules globally mock numpy: pop the top-level numpy ref so a fresh import gives the real package, and swap sys.modules["numpy"] inside the _record_audio call so the lazy import inside the function picks up real numpy too * fix(vad): drop silence-only recording buffers * test(vad): align stop buffer expectations * test(vad): preserve real numpy module * fix(install): install vad support opportunistically * test(vad): cover recognition manager vad edges --------- Co-authored-by: Sbenazar <127324689+Sbenazar@users.noreply.github.com> * fix(web): clarify speech demo browser support (#449) * fix black format check --------- Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: jay <sevenjay@users.noreply.github.com> Co-authored-by: Jatin K Malik <jatinkrmalik@gmail.com> Co-authored-by: Fakhareddine <fe.sabbari@claranet.com> Co-authored-by: Sbenazar <127324689+Sbenazar@users.noreply.github.com> Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: parko636 <alex@alexparkinson.net> Co-authored-by: juanfradb <94815906+juanfradb@users.noreply.github.com> Co-authored-by: Pep Locanto <peplocanto@gmail.com> |
||
|
|
3f5a5effff |
feat(vad): add Silero VAD and drop silence-only buffers (#447)
* feat(vad): replace amplitude-based VAD with Silero neural network Replace the simple amplitude threshold VAD with Silero VAD, a lightweight neural network model that classifies audio as speech/non-speech. This addresses the root cause of #302 (silence not stopping recording) and - Add silero_vad.py wrapper using ONNX Runtime with bundled Silero v5 model - Accumulate 512-sample chunks with 64-sample context window per official API - Map existing vad_sensitivity (1-5) to Silero thresholds (0.8-0.3) - Use max() across sub-chunks to avoid missing speech onsets - Fall back to amplitude VAD when onnxruntime is unavailable - Add onnxruntime as optional dependency (pip install vocalinux[vad]) Closes #344 * address review: add tests, LICENSE, logging, validation fixes - Add unit tests for SileroVAD (process, reset, fallback, sensitivity mapping) - Add MIT LICENSE for bundled Silero VAD model in data/ - Log which VAD backend is active on init - Validate chunk length in SileroVAD.process() - Skip is_speech check until a full Silero chunk is processed - Add thread safety note to SileroVAD docstring - Document resampling rate effect on VAD resolution - Comment explaining why load_silero_vad() catches broad Exception * address review: bundle license, document [vad] extra, surface backend in UI - Pack data/LICENSE into the wheel (pyproject package-data) so attribution travels with the model - README: short note on enabling neural VAD via vocalinux[vad] - Settings: VAD Sensitivity row subtitle shows the active backend; if onnxruntime is missing, an info-row beneath the group spells out the install command and the restart requirement - silero_vad.is_silero_available() probes onnxruntime + model presence w/o instantiating an InferenceSession - CI installs .[dev,vad] so Silero paths get exercised - Integration test for _record_audio Silero branch (mocked pyaudio + scripted SileroVAD), covering silence flush, speech reset, and sensitivity threshold mapping at sens=1 vs sens=5 - Tests no longer skip when other modules globally mock numpy: pop the top-level numpy ref so a fresh import gives the real package, and swap sys.modules["numpy"] inside the _record_audio call so the lazy import inside the function picks up real numpy too * fix(vad): drop silence-only recording buffers * test(vad): align stop buffer expectations * test(vad): preserve real numpy module * fix(install): install vad support opportunistically * test(vad): cover recognition manager vad edges --------- Co-authored-by: Sbenazar <127324689+Sbenazar@users.noreply.github.com> |
||
|
|
b3c73dd872 |
chore(release): prepare v0.11.0-beta (#440)
- Bump version from 0.10.2-beta to 0.11.0-beta - Update all version references across codebase - Add What's New section for v0.11.0-beta in README and docs/UPDATE.md - Add v0.11.0-beta entry to website changelog - Update SECURITY.md supported versions to 0.11.x - Update website schema, badge, and package version - Update RELEASE_PROCESS.md version history table |
||
|
|
59ab9fe587 |
fix(installer): harden Debian compatibility layer (#437)
Add missing pywhispercpp build prerequisites (libssl-dev, autoconf, automake, libtool, patchelf) to both Debian and Ubuntu apt package lists. These are not pulled in transitively on clean Debian installs and cause CMake bootstrap failures during source compilation. Add runtime smoke test for pywhispercpp to verify_installation() so "installs but does not run" failures surface at install time with actionable diagnostic steps instead of silently at first launch. Treat Debian as a first-class target — remove the "primarily designed for Ubuntu" warning for Debian systems. Remain conservative for non-Debian, non-Ubuntu distros. Add DEBIAN_FRONTEND=noninteractive to all apt install commands to prevent interactive debconf prompts from blocking unattended installs. Improve ydotool failure messaging on Debian with compile-from-source instructions since ydotool is not packaged in Debian standard repos. Add Debian-specific troubleshooting section to DISTRO_COMPATIBILITY.md covering build deps, ydotool compilation, scoped source builds, and libwhisper.so resolution diagnostics. References: https://medium.com/@cslev/talking-to-my-linux-box-without-talking-to-the-cloud-vocalinux-on-debian-without-the-tears-10bf053ea21b |
||
|
|
a47747d6a4 |
fix(install): correct openSUSE Tumbleweed dependencies (#420)
* fix(install): correct openSUSE Tumbleweed dependencies * style: format openSUSE installer changes --------- Co-authored-by: Jatin K Malik <jatinkrmalik@gmail.com> |
||
|
|
c0246d5fc8 | docs: clarify PyPI installation requirements (#423) | ||
|
|
ff9fd0a961 | fix(installer): reuse existing whispercpp builds (#421) |