cb90e849eeebf804f0ba144fd2e8e1f26c49df03
73
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
ddf073711b |
fix(ui): respect advanced toggle for Remote Server section (#454)
* fix(ui): respect advanced toggle for Remote Server section The Remote Server section was packed directly into advanced_tab, bypassing the advanced_revealer controlled by the 'Unlock Advanced Settings' toggle. This made it always visible regardless of toggle state. Changes: - Move remote_api_group and remote_status_label packing from advanced_tab to controls_box (inside the revealer) - Add visual separator between whisper.cpp controls and Remote Server - Increase scrolled min_content_height from 350 to 500 for taller content - Update power_user_switch tooltip/subtitle to mention remote server - Update info label to clarify whisper.cpp vs remote server scope - Align connection test to use requests.Session (consistent with transcription path) - Add empty-URL warning when toggling remote server ON - Add source-assertion tests for UI structure Fixes: Remote Server now hidden when 'Unlock Advanced Settings' is OFF * refactor(ui): move remote server to Speech Engine tab as peer engine Remote server is now a first-class engine option in the Speech Engine tab dropdown, alongside Vosk, Whisper, and whisper.cpp. The 'Use remote server' override toggle has been removed from the Advanced tab. Changes: - Add 'Remote API' to engine combo dropdown (removed skip logic) - Move remote server UI fields (URL, API key, endpoint, test button) from Advanced tab to Speech Engine tab - Show/hide remote server fields based on engine selection - Remove 'Use remote server' toggle and override logic - Simplify get_selected_settings (engine comes from combo directly) - Simplify _update_engine_specific_ui (check combo value) - Remove _on_use_remote_toggled method - Update _load_and_apply_settings (no remote switch logic) - Update power_user tooltip/subtitle (no longer mentions remote server) - Update tests for new behavior Backend requires zero changes - recognition_manager.py already treats remote_api as a first-class engine. User flow: 1. Select 'Remote API' from engine dropdown 2. Model selection hides automatically 3. Remote server config fields appear (URL, API key, endpoint) 4. Enter server details and test connection 5. Done - clear and intuitive |
||
|
+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> |
||
|
|
c65cc14a47 |
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.
|
||
|
|
86766d0ea0 |
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. |
||
|
|
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> |
||
|
|
722fc23403 | fix(installer): repair pywhispercpp library loading (#433) | ||
|
|
ff9fd0a961 | fix(installer): reuse existing whispercpp builds (#421) | ||
|
|
492f64ad8d |
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 |
||
|
|
79ae8e45d9 |
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 |
||
|
|
1c2fb4dadf |
fix(install): add missing dependencies for Pop!_OS and Ubuntu 24.04+ (#379)
Add cmake, libcairo2-dev, and dynamic libgirepository version detection to fix installation failures on Pop!_OS (Cosmic DE) and newer Ubuntu. Three issues addressed: 1. cmake: Required by pywhispercpp to build whisper.cpp from source. Added to all distro package lists (apt, dnf, pacman, zypper, emerge, apk, xbps, eopkg). 2. libcairo2-dev: Required to build pycairo. Was present in the Debian package list but missing from the Ubuntu list, which Pop!_OS uses. 3. libgirepository version: Ubuntu 24.04+ and Pop!_OS Cosmic+ ship libgirepository-2.0-dev instead of libgirepository1.0-dev. Added runtime detection to select the correct package, mirroring the existing version-aware handling already in place for Debian. Reported-by: DavidHoenisch Ref: https://github.com/jatinkrmalik/vocalinux/discussions/378 |
||
|
|
c1f12db845 |
chore(release): prepare v0.10.1-beta (#358)
* chore(release): prepare v0.10.1-beta * chore(release): update release notes with new fixes from main |
||
|
|
860a9f9ae6 |
fix: Bundle resources in package to fix missing system tray icons (fixes #349) (#354)
* fix: bundle resources in package to fix missing icons in system tray (fixes #349) The resources directory (icons and sounds) was not included in pip-installed packages because it lived outside the src/ package directory. This caused the resource_manager to find an empty icon directory at runtime, resulting in the ellipsis (three dots) icon in the GNOME system tray. Changes: - Copy resources into src/vocalinux/resources/ so setuptools bundles them - Add 'vocalinux.resources/**/*' to package-data in pyproject.toml - Add MANIFEST.in for sdist builds - Update resource_manager to check package-bundled resources first (module_dir.parent / 'resources') before checking external paths - Add install_resources_to_venv() to install.sh for curl-based installs, copying resources to /share/vocalinux/resources This fixes both pip installs (resources bundled in the wheel) and curl-based installs (resources copied to venv share directory). * fix: ensure bundled resources are included in sdist and pip installs - Add 'vocalinux.resources' to setuptools packages list so the subpackage (and its __init__.py) is recognized by setuptools - Add __init__.py to src/vocalinux/resources/ to make it a proper Python subpackage for reliable discovery - Add 'recursive-include src/vocalinux/resources *' to MANIFEST.in so sdist archives include the bundled icons and sounds |
||
|
|
84ec79c769 |
chore(release): prepare v0.10.0-beta (#345)
* chore(release): prepare v0.10.0-beta Bump versions and release references across app, docs, installer, and website for the next beta release. Add v0.10.0-beta release notes entries covering reliability fixes and CI/test improvements since v0.9.0-beta. Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai> * docs(release): align Python baseline references to 3.9+ Synchronize stale Python 3.8 references in agent guidance and distro dependency docs/scripts with the current 3.9+ minimum used by project metadata and release docs. Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai> --------- Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai> |
||
|
|
fa17b88f74 |
chore: bump minimum Python from 3.8 to 3.9 (#336)
* chore: bump minimum Python from 3.8 to 3.9 and adopt PEP 585 builtins - Update requires-python, classifiers, mypy target, and black target in pyproject.toml - Replace Python 3.8 with 3.9 in CI matrix and nightly workflow - Replace typing.List/Dict/Tuple/Set with builtin list/dict/tuple/set (PEP 585) - Update all 3.8 version references in README, docs, website, and install.sh - Fix test_wayland_no_tools to mock IBus availability - Remove List from common_types re-exports and update test * style: reformat test files for Black 26.x and pin version in CI Black 26.x introduced parenthesized context manager formatting. Reformat 7 test files and pin Black>=26,<27 in CI to prevent future drift from unpinned version upgrades. |
||
|
|
6bdbb3deb4 |
fix(install): handle piped bash by redirecting stdin to /dev/tty (#315)
When running via `curl | bash`, stdin (fd 0) is the pipe — not the terminal. The script was setting INTERACTIVE_MODE='ask' and then trying to `read` from the pipe, which returned EOF/garbage, defaulting to interactive mode, which then immediately errored because there was no TTY. Fix: if stdin is a pipe but /dev/tty is accessible, do `exec < /dev/tty` to redirect stdin to the real terminal so user prompts work correctly. If there is truly no terminal (headless/CI), fall back to automatic mode without erroring. Also fix the misleading error message which suggested running the exact same curl|bash command that just failed. |
||
|
|
81623ca6d3 | fix(install): dynamically resolve latest release tag via GitHub API (#313) | ||
|
|
e2fdf5ddf2 |
fix(clipboard): copy to clipboard after IBus text injection (#311)
The IBus injection path had an early return that skipped the clipboard copy logic. Store the result, copy to clipboard if enabled, then return. Also add xclip and wl-clipboard to install.sh for all supported distros so clipboard tools are available out of the box. |
||
|
|
0469defae6 |
chore(release): prepare v0.8.0-beta (#280)
* chore(release): prepare v0.8.0-beta * docs(web): align release messaging with v0.8.0-beta features * docs(web): tighten homepage messaging for v0.8.0-beta |
||
|
|
6e02e928ae |
chore(release): prepare v0.7.0-beta (#255)
* chore(release): prepare v0.7.0-beta ## Version Bump (v0.6.3-beta → v0.7.0-beta) This is a major beta release with significant new features including autostart support, tabbed settings, and improved GPU detection. ### Core Version Files - Updated src/vocalinux/version.py to 0.7.0-beta - Updated pyproject.toml classifiers (already correct - Beta) ### Documentation - Updated README.md with new features, bug fixes, and version references - Updated docs/INSTALL.md with v0.7.0-beta install commands - Updated docs/UPDATE.md with comprehensive 'What's New' section - Updated SECURITY.md supported versions table (0.7.x now supported) ### Website - Updated web/src/app/page.tsx version badge (v0.7.0 Beta) - Updated web/src/app/page.tsx softwareVersion schema - Updated web/package.json version to 0.7.0 - Updated web/src/app/changelog/page.tsx with v0.7.0-beta release entry ### Other Updates - Updated install.sh default tag to v0.7.0-beta - Updated src/vocalinux/main.py error message install command - Updated AGENTS.md branch naming example ## Changes Since v0.6.3-beta ### New Features (13 commits) - Autostart on login support via XDG autostart - Tabbed settings dialog with 5 organized tabs - Intel GPU compatibility detection with automatic CPU fallback - Single instance prevention - Evdev device management to prevent CPU spin - Improved GPU detection avoiding false positives - IBus fallback when daemon not running - Fedora dnf check-update fix - Web SEO - 7 new optimized pages ### Contributors - Jatin K Malik (maintainer) - Antoine Monnet (contributor) - jatinkrmalik (contributor) ### Bug Fixes - #254: Improved GPU detection to avoid false positives - #251: Skip IBus setup when daemon not running - #243: Prevent dnf check-update from exiting script on Fedora - #240: Raise exception on IBus setup failure - #234: Removed leading space from first speech transcription - #242/#253: Removed disconnected evdev devices to prevent CPU spin ## Verification - [x] Lint passed - [x] Black formatting passed - [x] isort check passed - [x] Version imports correctly as 0.7.0-beta * docs: update version history in RELEASE_PROCESS.md Add entries for 0.6.0-beta through 0.7.0-beta to the version history table. |
||
|
|
8d898de5e8 |
fix(installer): improve GPU detection to avoid false positives (#254)
* fix(installer): improve GPU detection to avoid false positives - Filter out software renderers (llvmpipe, swiftshader, lavapipe, etc.) when checking Vulkan compatibility - these are CPU implementations and should not affect hardware GPU detection - Skip Vulkan compatibility check for NVIDIA GPUs since they use CUDA instead of Vulkan for acceleration - Reorder backend priority: CUDA > Vulkan > CPU - Only show incompatible GPU warning for actual Vulkan-based GPUs (AMD/Intel) that lack VK_KHR_16bit_storage support Fixes issue where RTX 5080 + llvmpipe combination was incorrectly flagged as incompatible due to llvmpipe lacking 16-bit storage support. * fix(installer): recommend GPU even without dev libraries installed The installer installs dev libraries (libvulkan-dev, glslc) in install_system_dependencies(), which runs AFTER the backend selection. So we should recommend GPU if there's a compatible GPU regardless of whether dev libraries are currently installed. Changes: - NVIDIA GPU detected → recommend CUDA (even without nvcc) - Vulkan-compatible GPU → recommend Vulkan (even without libvulkan-dev) - Only fall back to CPU if no compatible GPU detected or GPU is genuinely incompatible (old Intel Gen7) |
||
|
|
58b635b8b9 |
feat: detect incompatible Intel GPUs and fallback to CPU (#239)
* feat: detect incompatible Intel GPUs and fallback to CPU Implements two-layer protection for users with older Intel GPUs that lack VK_KHR_16bit_storage support required by whisper.cpp Vulkan backend. Affected GPUs: Intel Gen7 and older (Ivy Bridge, Haswell, Sandy Bridge) - Intel HD Graphics 2500/4000 (Ivy Bridge) - Intel HD Graphics 4400/4600 (Haswell) Layer 1: Install-Time Detection (Prevention) - New check_vulkan_gpu_compatibility() function in install.sh - Parses GPU name from vulkaninfo output - Checks against known incompatible patterns - Shows clear warning and recommends CPU backend - Updates detect_whispercpp_backends() to skip Vulkan for incompatible GPUs Layer 2: Runtime Fallback (Safety Net) - Added error handling in recognition_manager.py _init_whispercpp() - Catches RuntimeError with "16-bit storage" or "Unsupported device" - Automatically disables Vulkan/CUDA and retries with CPU - Shows desktop notification explaining the fallback Fixes #238 * fix(gpu): improve compatibility detection and add tests |
||
|
|
9886e8cb9d |
feat(ui): add auto-start on boot support (#207)
* feat(ui): add auto-start on boot support - Add --start-minimized CLI flag for autostart entry - Create autostart_manager.py to handle XDG autostart desktop entry - Add autostart config option in config_manager.py - Add Start on Login toggle in Settings dialog - Add Start Minimized toggle in Settings dialog - Add Start on Login checkbox in system tray menu - Add first-run dialog to ask about autostart preference Closes #184 * fix(test): add first_run: False to mock config settings The tests were failing with StopIteration because main() now checks for first_run and shows a dialog if True. By setting first_run: False in the mock config, we skip the first-run dialog code path. * fix(ui): harden autostart behavior across Linux sessions Use XDG-compliant autostart paths and robust Exec command resolution, keep first-run prompt semantics correct, and fix GTK toggle handling so state changes are applied reliably. * docs(autostart): document XDG startup model and limits Clarify that Vocalinux uses desktop-session XDG autostart, not systemd services, and explain compatibility expectations for mainstream DEs versus minimal/custom WM sessions. * feat(ui): add first-run quick-start guidance text Expand the welcome dialog with concise first-time usage guidance and clear direction to the tray menu Settings entry while preserving the existing autostart decision flow. * fix(uninstall): remove XDG autostart desktop entry Ensure uninstall removes ~/.config autostart registration and reports leftover autostart entries during verification. * fix(install): ignore defunct processes in running-check Filter installer process detection to exclude defunct entries and installer/uninstaller shells so stale zombie rows do not block reinstall. * fix(uninstall): ignore defunct processes in running-check Use filtered process detection so zombie entries and installer shells do not trigger false running-process warnings during uninstall. |
||
|
|
f3bcffad60 |
fix(install): prevent dnf check-update from exiting script on Fedora (#243)
dnf check-update returns exit code 100 when system updates are available, which causes the script to exit immediately due to set -e. Added '|| true' to prevent this non-error condition from stopping the installer. Fixes #34 (Fedora installation failure) Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai> |
||
|
|
d7d91a7db2 |
chore(release): prepare v0.6.3-beta (#235)
* chore(release): prepare v0.6.3-beta - Fix #220: Installer default tag now points to correct version - Update version from 0.6.2-beta to 0.6.3-beta - Update documentation version references - Update website schema and badges - Add v0.6.3-beta changelog entry The v0.6.2-beta installer was defaulting to v0.6.1-beta which is missing the psutil dependency, causing fresh installs to fail. * docs(update): simplify update guide, remove duplicate version history |
||
|
|
ca6ba56318 |
feat(scripts): add process check and interactive prompts (#230)
install.sh: - Add check_running_processes() function to detect running Vocalinux - Prompt user to kill processes before proceeding with installation - Auto-kill in non-interactive mode uninstall.sh: - Add confirmation prompt before killing processes - Add interactive prompt for keeping data (models, config) - Simplified --keep-data flag behavior (also keeps config) This prevents issues during install/upgrade when Vocalinux is still running, and gives users more control over data preservation. |
||
|
|
68062713cf |
fix(install): remove premature pkg-config check (#228)
pkg-config is included in the package list to install, so the script should not exit with an error if it's not already installed. The check was preventing installation on fresh systems like WSL Ubuntu 24.04. The script already has proper fallbacks for when pkg-config is not available (detect_vulkan and detect_typelib_path both check before calling pkg-config). |
||
|
|
a1725f7cfc |
feat(text-injection): add IBus engine for Wayland text injection (#211)
* feat(text-injection): add IBus engine for Wayland text injection Add a custom IBus input method engine for reliable text injection on Wayland, solving keyboard layout issues with xdotool/wtype. Key features: - IBus proxy engine that passes through normal keyboard input - Text injection via Unix socket IPC - User-level component installation (no sudo required) - Auto-reactivation when user switches keyboard layouts - Graceful fallback when IBus is unavailable Changes: - New ibus_engine.py with VocalinuxEngine and IBusTextInjector - TextInjector now prefers IBus on Wayland for best compatibility - Add IBus GObject bindings to system dependencies in install.sh - Add comprehensive tests for IBus engine 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * security: secure IBus socket with permissions and peer verification - Set directory to 0o700 (owner-only access) - Set socket to 0o600 (owner-only read/write) - Add SO_PEERCRED verification to reject connections from other users This provides defense-in-depth: even if directory permissions are bypassed, the socket server verifies the connecting process UID. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix: address PR review feedback for IBus text injection - Fix infinite loop in socket server by adding _server_running flag - Use sys.executable instead of hardcoded python3 for venv compatibility - Also inherit environment (env=os.environ.copy()) for venv deps - Fix component XML to use sys.executable in <exec> tag - Replace unsafe pgrep with PID file tracking and /proc verification - Integrate TextInjector.stop() in TrayIndicator._quit() for cleanup - Remove unused is_engine_registered import - Fix redundant hasattr check to use proper None comparison - Auto-detect and update stale IBus component XML on startup 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> --------- Co-authored-by: AntoineMontane <monnet.antoine@gmail.com> Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com> |
||
|
|
f3fc8d6de6 |
fix(install): correct package detection and package names for multiple distros (#214)
* fix(install): correct package detection for Void and Solus, fix git fetch syntax - Fix git fetch syntax: remove incorrect quoting around 'tag' argument - Fix Void Linux: use 'xbps-query' instead of 'xbps-query -S' which checks remote repos instead of installed packages - Fix Solus: use 'eopkg list-installed | grep' instead of 'eopkg info' which queries repository info, not installed status These bugs caused the installer to always attempt reinstalling packages on Void Linux and Solus, even when they were already installed. * fix(install): correct package names for Arch, Void, and Gentoo - Arch Linux: libappindicator-gtk3 -> libappindicator (package was renamed) - Void Linux: libappindicator -> libappindicator-gtk3 (correct package name) - Gentoo: dev-libs/libappindicator:3 -> dev-libs/libayatana-appindicator (libappindicator is deprecated, use ayatana fork) |
||
|
|
5fb7c40db7 |
fix(install): add Vulkan shader packages to all supported distributions
Added glslang and vulkan-tools to distributions that were missing them: - Gentoo: dev-util/glslang - Alpine: glslang vulkan-tools - Void: glslang Vulkan-Tools - Solus: glslang vulkan-tools Validated package names against official repositories for each distro. |
||
|
|
bc5ceda546 |
fix(install): use glslc with glslang-tools fallback for Vulkan shader support
- Try glslc first (available on Ubuntu 24.04+) - Fall back to glslang-tools if glslc not available (Ubuntu 22.04) - Update user-facing messages to mention both packages - Fixes #212 while maintaining compatibility with newer Ubuntu versions |
||
|
|
844d2fb104 |
fix(install): ensure whisper module is properly installed with --auto flag (#205)
When installing with --engine=whisper, the installer would continue even if the whisper package installation failed, creating a config file with engine=whisper. This caused the app to crash on startup with 'No module named whisper'. Changes: - Add proper verification of whisper installation by importing the module - Only create whisper config if installation succeeds - Fall back to whisper.cpp if whisper installation fails - Provide clear error messages during installation Fixes #190 |
||
|
|
1e0a2a6cee |
chore(release): prepare v0.6.1-beta (#201)
Bump version from 0.6.0-beta to 0.6.1-beta with installation improvements: Version Updates: - src/vocalinux/version.py: 0.6.0-beta → 0.6.1-beta - install.sh: Default tag and help text updated - web/package.json: 0.6.0 → 0.6.1 - web/src/app/layout.tsx: schema.org softwareVersion updated - web/src/app/page.tsx: version badge updated Documentation Updates: - README.md: New 'What's New' section for v0.6.1-beta - docs/UPDATE.md: Added v0.6.1-beta release notes with new features - docs/INSTALL.md: Version references updated - docs/RELEASE_PROCESS.md: Example commands updated - tests/distro-testing-checklist.md: Version references updated - src/vocalinux/main.py: Error message updated New Features in v0.6.1-beta: - Interactive backend selection for whisper.cpp (GPU vs CPU) - Enhanced welcome message with testing instructions - Simplified install commands (tag-based URLs) - Better GPU support with proper library detection Bug Fixes: - Fixed bash arithmetic with set -e - Fixed TTY detection for interactive mode - Auto-install git if missing - Fixed GPU installation commands |
||
|
|
86ee79cc0d |
chore: update install commands to use tag-based URLs (#200)
Changed install commands to download install.sh directly from the release tag instead of from main branch with --tag parameter. This ensures: 1. The install script version matches the release being installed 2. No version mismatch between script and code 3. Simpler, cleaner install commands Changes: - install.sh: Updated error message to use v0.6.0-beta URL - README.md: All install commands now use v0.6.0-beta/install.sh - docs/INSTALL.md: Updated quick install command - docs/UPDATE.md: Updated update command examples - docs/RELEASE_PROCESS.md: Updated example command - src/vocalinux/main.py: Updated error message install command - tests/distro-testing-checklist.md: Updated download URL - web/src/app/page.tsx: Now uses dynamic tag in URL (not main) - .github/workflows/release.yml: Release notes use tag-based URL Before: curl .../main/install.sh | bash -s -- --tag=v0.6.0-beta After: curl .../v0.6.0-beta/install.sh | bash Fixes version consistency between install script and installed code. |
||
|
|
091731e7ca |
feat(install): add backend selection and enhanced welcome message (#197)
Add interactive whisper.cpp backend selection: - New Step 3: Choose between GPU (Vulkan/CUDA) or CPU backend - Auto-detects available GPU capabilities and recommends best option - Shows helpful tips when GPU libraries are missing - Respects user's backend choice during installation - Installation summary now shows backend selection Enhanced welcome message at end: - Shows what was installed (engine, backend, locations) - Clear testing instructions with example - Voice command examples - Management commands (start, debug, GUI) - Better formatting with visual separators Updated system dependencies: - Added glslc package for Vulkan shader compilation - Fixed package name from glslang-tools to glslc - Updated all distro package lists (Ubuntu, Debian, Fedora, Arch, openSUSE) - Improved error messages when GPU builds fail Fixed bash arithmetic issues: - Replaced ((var++)) with 1 for set -e compatibility PR: https://github.com/jatinkrmalik/vocalinux/pull/197 |
||
|
|
1a166eff56 |
fix(install): execute GPU pip command directly, not via variable (#196)
The previous fix stored the pip command in a variable:
PYWHISPERCPP_INSTALL_CMD="GGML_CUDA=1 pip install ..."
$PYWHISPERCPP_INSTALL_CMD
This doesn't work because bash interprets "GGML_CUDA=1" as a
command, not as an environment variable prefix.
Fix: Use conditional logic to run the commands directly:
if GGML_CUDA=1 pip install ...; then
GPU_INSTALL_SUCCESS=true
fi
|
||
|
|
0241b0cb12 |
fix(install): install pywhispercpp with GPU support when available (#195)
The basic `pip install pywhispercpp` only includes CPU support. For GPU acceleration, pywhispercpp must be installed with special environment flags: - NVIDIA GPU: GGML_CUDA=1 pip install git+... - Vulkan (AMD/Intel): GGML_VULKAN=1 pip install git+... This fix: - Detects GPU type during whisper.cpp installation - Installs pywhispercpp with appropriate GPU support flag - Falls back to CPU version if GPU install fails Fixes #193 |
||
|
|
17bf62be64 |
fix(install): auto-install git if not present before cloning (#192)
Users running the install script via curl | bash may not have git installed. This adds a check before attempting to clone the repo and auto-installs git using the appropriate package manager for the detected distribution. Fixes issue reported on Reddit where install failed on Linux Mint with "git command not found" error at line 170. |
||
|
|
57a0ddd0b8 |
feat(speech): add whisper.cpp with Vulkan support for GPU acceleration (#179)
* feat(speech): add whisper.cpp with Vulkan support for GPU acceleration Add whisper.cpp as a new speech recognition engine with the following features: - Vulkan GPU acceleration support (AMD, Intel, NVIDIA) - Fallback to CUDA for NVIDIA GPUs - CPU-only mode with automatic thread optimization - Support for all model sizes: tiny, base, small, medium, large - Hardware detection to recommend best model/backend - Models downloaded from Hugging Face (ggml format) - Updated UI to show compute backend info in settings - pywhispercpp as the Python binding library - whisper_cpp as the new default engine Changes: - New whispercpp_model_info.py with model metadata and hardware detection - Updated recognition_manager.py with whisper.cpp init and transcription - Updated settings_dialog.py with whisper.cpp UI support - Updated config_manager.py with whisper_cpp_model_size config - Updated main.py with whisper_cpp CLI option - Updated pyproject.toml with pywhispercpp dependency - Updated install.sh with vulkan-tools for GPU detection Closes #104 * style: fix black and isort formatting issues * test: update config manager test for whisper_cpp default engine * feat(install): interactive mode with 3 engine choices and whisper.cpp as default Major changes to install.sh: - Interactive mode is now the DEFAULT (not --auto) - Added --auto flag for non-interactive automatic installation - New --engine flag to specify engine directly - Interactive mode shows 3 engine options with visual boxes: 1. whisper.cpp (recommended) - Vulkan/CUDA/CPU support 2. whisper (OpenAI) - PyTorch-based, NVIDIA-only 3. VOSK - lightweight option - Hardware detection with recommendations: - Detects Vulkan support (AMD/Intel/NVIDIA) - Detects NVIDIA CUDA - Checks available RAM - Recommends best engine based on hardware - Updated help text with new options and examples - Removed old --with-whisper, --whisper-cpu, --no-whisper flags - whisper.cpp is now the default engine for automatic mode * feat(uninstall): kill running vocalinux processes before uninstall * fix(whisper.cpp): auto-download model, use all CPU cores, hide unavailable engines Three fixes for whisper.cpp implementation: 1. Auto-download model during install - Added install_whispercpp_model() function to install.sh - Downloads ggml-tiny.bin (~39MB) from Hugging Face - Runs automatically when whisper.cpp engine is selected 2. Use all CPU cores for transcription - Added n_threads parameter to Model initialization - Uses multiprocessing.cpu_count() to get all cores - Significantly improves transcription speed 3. Hide unavailable engines from dropdown - Added get_available_engines() function to detect installed engines - Settings dialog now only shows engines that are actually installed - Falls back to first available engine if current not available Fixes user-reported issues with whisper.cpp integration. * ui(settings): display whisper.cpp correctly and increase dialog height - Changed engine display name from 'whisper_cpp' to 'whisper.cpp' - Added format_engine_display_name() helper function - Dialog now uses 75% of screen height for better visibility - More options visible by default without scrolling * revert(settings): remove display name change, keep dialog height increase - Reverted engine display name change (was breaking engine selection) - Kept dialog height at 75% of screen for better visibility * style: fix black and isort formatting for whisper.cpp logging * style: fix black formatting in settings_dialog.py * ci: fix Fedora 39 container package installation Add dnf cache update and fallback with --nogpgcheck for Fedora containers that may have stale or missing package metadata. |
||
|
|
5e8336239f | fix(install): add python3-dev dependency for evdev build (#177) | ||
|
|
ff994a3733 | fix(install): prevent silent exit on systems without NVIDIA GPU (#175) | ||
|
|
04eada775b |
feat: Phase 5 & 6 - Complete cross-distro compatibility fixes (#168)
Phase 5: Fix remaining hardcoded GI_TYPELIB_PATH values - install.sh: Use dynamic GI_TYPELIB_DETECTED in package verification (line 1208) - install.sh: Use dynamic path in desktop entry Exec line (line 1651) - CI workflow: Use pkg-config for dynamic GI_TYPELIB_PATH detection (line 151) Phase 6: Testing & validation improvements - Add comprehensive test_cross_distro_compatibility.py test suite - Tests verify no hardcoded paths, dynamic detection works, wrapper scripts correct - Update DISTRO_COMPATIBILITY.md with implementation status and technical details Fixes #157 (remaining issues) Related to #48 (Snap packaging) |
||
|
|
6b0e81c117 |
feat: Phase 4 - Testing & Polish improvements (#161)
Add testing infrastructure and documentation improvements. **Task 12: Add pkg-config to Core Dependencies** - Added pkg-config check at the start of install_system_dependencies() - Added pkg-config to all distribution package lists - Provides distro-specific install instructions if pkg-config is missing - Critical for library detection (GI_TYPELIB_PATH, PortAudio, etc.) **Task 13: Update README with Distro Info** - Updated Requirements section with tested distributions - Added link to DISTRO_COMPATIBILITY.md - Listed officially supported distros: Ubuntu 22.04+, Debian 11+, Fedora 39+, Arch, openSUSE - Mentioned experimental support for Gentoo, Alpine, Void, Solus **Task 14: Create Test Matrix for CI** - New workflow: .github/workflows/distro-test-matrix.yml - Tests on multiple container images: Ubuntu 22.04, 24.04, Debian 12, Fedora 39 - Runs dependency checker on each distro - Tests Python imports and bash syntax validation - Uses continue-on-error for experimental distro support **Task 15: Integration Testing Checklist** - New file: tests/distro-testing-checklist.md - Comprehensive checklist for testing on new distributions - Covers: Pre-install, Installation, Post-install, Functionality, Performance, Cleanup - Includes quick reference commands - Desktop environment and application-specific testing sections - Template for submitting test results Part of cross-distribution compatibility improvements (Phase 4 of 5). Related to #157 |
||
|
|
461400b3bc |
feat: Phase 3 - Better error handling and documentation (#160)
Add better error messages and documentation for cross-distribution compatibility. **Task 9: System Dependency Checker (scripts/check-system-deps.sh)** - New script to check system dependencies regardless of distribution - Detects Python 3.8+, pkg-config, GTK3, PyGObject, PortAudio - Checks for text injection tools (xdotool, wtype, ydotool) - Provides distribution-specific installation instructions - Supports all 10+ distribution families (Ubuntu, Debian, Fedora, Arch, openSUSE, Gentoo, Alpine, Void, Solus, Mageia) **Task 10: Better Unsupported Distro Message (install.sh)** - Enhanced error message for unsupported distributions - References the new dependency checker script - Provides manual installation options (skip-system-deps, venv install) - Includes link to project wiki - Interactive prompt to continue or cancel **Task 11: Distro Compatibility Documentation (docs/DISTRO_COMPATIBILITY.md)** - Comprehensive compatibility matrix for supported distros - Experimental support section with known issues - Requirements by distribution with exact commands - Desktop environment compatibility (GNOME, KDE, Xfce, sway, i3, etc.) - Display server support (X11, Wayland, XWayland) - Architecture support (x86_64, arm64, armhf, riscv64) - Testing checklist for new distributions - Issue reporting guidelines - Contributing guidelines for compatibility fixes Part of cross-distribution compatibility improvements (Phase 3 of 5). Related to #157 |
||
|
|
f2e13bbcba |
feat: Phase 2 - Enhanced distro detection and package manager support (#159)
Add support for 5 additional Linux distribution families: **New Distribution Detection:** - Gentoo (Portage package manager) - Alpine Linux (apk package manager) - Void Linux (xbps package manager) - Solus (eopkg package manager) - Mageia (dnf/urpmi package managers) **Changes:** - Enhanced `detect_distro()` to detect Gentoo, Alpine, Void, Solus, Mageia - Added package definitions for each new distribution in `install_system_dependencies()` - Added full installation case statements with package verification - Updated `install_python_dependencies()` to handle virtual environment creation for new distros - Updated `install_text_input_tools()` for both Wayland and X11 sessions - Updated unknown session type fallback with all new distributions **Package Manager Support:** - emerge: Gentoo's Portage (compiles from source) - apk: Alpine's package manager (musl libc) - xbps: Void Linux's package manager - eopkg: Solus's package manager - dnf/urpmi: Mageia's package managers Part of cross-distribution compatibility improvements (Phase 2 of 5). Related to #157 |
||
|
|
2b498f0888 |
fix: Phase 1 - Critical cross-distribution compatibility fixes (#158)
* fix: Phase 1 - Critical cross-distribution compatibility fixes This commit implements the first phase of cross-distribution compatibility improvements for Vocalinux, addressing the most critical issues that prevent the application from working on non-Ubuntu distributions. ## Changes ### install.sh - Add detect_typelib_path() function that dynamically discovers GI_TYPELIB_PATH using pkg-config with fallback to distribution-specific paths - Update wrapper scripts (vocalinux, vocalinux-gui) to use detected path - Support for: x86_64, arm64, armhf, riscv64, powerpc64le, s390x architectures - Specific paths for: Ubuntu multiarch, Fedora/RHEL (lib64), Arch (lib) ### src/vocalinux/speech_recognition/recognition_manager.py - Add _get_system_model_paths() function for dynamic model path discovery - Enhance ALSA library detection with multiple library name fallbacks: - libasound.so.2, libasound.so, libasound.so.0, asound - Support XDG_DATA_DIRS environment variable for model paths - Distribution-specific paths for Fedora/RHEL/CentOS/Rocky/AlmaLinux - Arch-specific handling (excludes /usr/local) ### src/vocalinux/utils/resource_manager.py - Enhanced _find_resources_dir() with more candidate paths - Support for XDG_DATA_DIRS environment variable - Add lib64 and lib paths for Fedora/RHEL distributions - Add Flatpak-specific path (/app/share/vocalinux/resources) ## Testing All 462 existing tests pass. ## Related Issues Fixes #157 (partial - Phase 1 of 5) * chore: Remove analysis docs from PR, add to .gitignore * style: Apply black formatting to pass lint checks |
||
|
|
2becfb5a98 |
fix(install): fix model download and VOSK-only option in interactive mode (#132)
* fix(install): fix model download and VOSK-only option in interactive mode - Add missing option 3 handling in interactive prompt to create VOSK config when user selects VOSK-only mode (mirrors --no-whisper flag) - Set WITH_WHISPER and WHISPER_CPU flags correctly for options 1 and 2 - Fix Whisper model download logic to check actual installation status using venv python instead of relying on unreliable flag combinations - Always download Whisper tiny model when Whisper is installed since it's the default engine in config_manager.py - Add informative messages about model download status Fixes issues where: 1. Selecting option 2 or 3 didn't properly configure the system 2. Whisper tiny model wasn't downloaded even when Whisper was selected 3. App defaulted to Whisper engine but model wasn't available * fix(install): use explicit venv python path and simplify condition - Use $VENV_DIR/bin/python instead of bare 'python' in run_tests() and verify_installation() for consistency and reliability - Simplify Whisper install condition since WITH_WHISPER is already set by auto-detection in non-interactive mode (lines 412-424) - Update comments to better explain the logic flow * style(install): restore ASCII art logo and improve formatting - Restore beautiful VOCALINUX ASCII art banner in interactive installer - Restore ASCII art banner in installation complete message - Use Unicode box-drawing characters (━) for section separators - Use bullet points (•) instead of dashes for lists - Add colored terminal output for commands - Add star emoji for GitHub link * style(install): show ASCII art banner in all installation modes The ASCII art logo is now displayed at the start of installation regardless of mode (interactive, curl|bash, or with flags). * fix(install): remove duplicate ASCII art in interactive mode The ASCII art banner is now shown once at script start for all modes. Remove the duplicate from run_interactive_install() to avoid showing it twice when running with -i/--interactive flag. * docs(install): rename options to 'Full install' and 'Minimal install' Clarify that VOSK is always installed as a fallback engine: - 'Full install' = Whisper + VOSK (recommended, highest accuracy) - 'Minimal install' = VOSK only (lightweight, ~40MB) Updates both the -i interactive installer and the fallback prompt. Also improves the installation summary display. * fix(install): update desktop database so app appears in menu immediately Previously only the icon cache was updated after installation, but not the desktop database. This caused the app to not appear in application menus until the user logged out and back in (or manually ran update-desktop-database). Now the installer runs update-desktop-database after installing the .desktop file, so Vocalinux appears in the app launcher immediately. |
||
|
|
ee494f921c |
fix(keyboard): verify Ctrl key capability in evdev backend availability check (#128)
* fix(keyboard): verify Ctrl key capability in evdev backend availability check The evdev backend's is_available() method was incorrectly returning True when keyboard devices were found, even if the user lacked permission to access them or if the accessible devices didn't have Ctrl key capability. This caused keyboard shortcuts to fail silently on Wayland when the user wasn't in the 'input' group, as the backend was selected but couldn't actually monitor keyboard events. Changes: - Add device_has_ctrl_key() helper to verify a device supports Ctrl keys - Update is_available() to check both device accessibility AND Ctrl key capability - Now correctly returns False when no usable keyboard device is found, triggering proper error messages about permissions This ensures users get clear feedback about missing permissions instead of silently non-working keyboard shortcuts. * fix(install): use sg command to apply input group without logout The install.sh script adds the user to the 'input' group for keyboard shortcuts on Wayland, but this change doesn't take effect until logout. Updated wrapper scripts to use 'sg input -c ...' to run vocalinux with the input group applied, even if the user hasn't logged out yet. This provides a much better user experience as keyboard shortcuts work immediately after installation. The wrapper detects if: 1. User is in /etc/group for 'input' group 2. Current session doesn't have the group active If both conditions are true, it uses 'sg' to apply the group. |
||
|
|
6f54dd80e0 |
Fix Python 3.13 and Wayland compatibility (#125)
* Fix Python 3.13 and Wayland compatibility This commit addresses installation failures on modern Linux distributions (Ubuntu 25.10+) with Python 3.13 and Wayland-only sessions. Changes: 1. **Add Python 3.13 support** (pyproject.toml) - Add Python 3.13 to classifiers - Update black target versions to include py312, py313 - No code changes needed - already compatible 2. **Use system site-packages for PyGObject** (install.sh) - Change venv creation to use --system-site-packages flag - Avoids PyGObject build failures with Python 3.13 - System PyGObject (python3-gi) is already installed as dependency - Building from source is unnecessary and fragile with new Python versions 3. **Improve Wayland text injection support** (install.sh) - Install ydotool as fallback (wtype doesn't work with GNOME Wayland) - Add user to 'input' group for uinput device access - Install udev rule for /dev/uinput permissions - Provide clear warning about logout requirement Technical details: - wtype uses Wayland virtual keyboard protocol (not supported by GNOME) - ydotool uses Linux uinput kernel module (works with all compositors) - System PyGObject 3.50.0+ supports Python 3.13 - Building PyGObject 3.54.5 from source fails with Python 3.13 build tools Fixes installation on: - Ubuntu 25.10 (Questing Quokka) - Wayland-only, Python 3.13 - Arch Linux with Python 3.13 - Fedora 41+ with Python 3.13 Tested on: Ubuntu 25.10 with Python 3.13.7, GNOME 49 Wayland Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com> * Prioritize ydotool over wtype for universal Wayland compatibility wtype uses the virtual-keyboard Wayland protocol which is not supported by GNOME/Mutter compositors. ydotool uses the Linux uinput kernel module which works with all Wayland compositors including GNOME. This change swaps the priority order to check ydotool first, ensuring text injection works on GNOME Wayland (Ubuntu 25.10+) while still falling back to wtype for compositors that support it. Tested on Ubuntu 25.10 with GNOME 49 Wayland - text injection now works perfectly with ydotool. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com> * ci: trigger workflow run * ci: run lint/tests on python 3.13 --------- Co-authored-by: ACT900 <179795730+ACT900@users.noreply.github.com> Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com> |