feat(identity): rename user-visible identity to voculinux with one-time legacy import
The fork takes its own name everywhere a user can see it: binaries (voculinux, voculinux-gui), desktop entry, autostart, tray, D-Bus namespace (com.voculinux.Voculinux, no alias for the old name), config/data/cache dirs, single-instance lock, IBus engine identity, tray icons, resource prefixes, VOCULINUX_* environment names, and the update-checker user agent. The internal Python module stays vocalinux and the distribution metadata keeps the upstream URLs as provenance. On first run a previous Vocalinux install's config.json, custom dictionary, and portal restore token are copied once into the new locations (new config always wins, no merge, no write-back); models re-resolve through the pinned-checksum download path. An offline naming-sweep test proves no old-name strings remain on user-visible surfaces outside the allowlist. Refs #18
@@ -1,5 +1,5 @@
|
||||
"""
|
||||
Vocalinux - A seamless voice dictation system for Linux
|
||||
Voculinux - A seamless voice dictation system for Linux
|
||||
|
||||
Note: GTK-dependent modules are imported lazily in main.py to allow
|
||||
the package to be imported even when system GTK typelibs are not available.
|
||||
|
||||
@@ -1,68 +1,74 @@
|
||||
"""Central user-visible application identity for Vocalinux.
|
||||
"""Central user-visible application identity for Voculinux.
|
||||
|
||||
Ticket #17 prefactor: every user-visible identity string that is hardcoded in
|
||||
more than one place is driven from this single module. No value changes here;
|
||||
the rename ticket (#18) flips them in one place instead of a scavenger hunt.
|
||||
Ticket #18 rename: every user-visible identity string that is hardcoded in
|
||||
more than one place is driven from this single module. The fork takes its
|
||||
own name everywhere a user can see it; the internal ``voculinux`` Python
|
||||
package name is retained (see :data:`RETAINED_SURFACES`) and unrelated to
|
||||
these values.
|
||||
|
||||
Retained internal names (the ``vocalinux`` Python package, upstream URLs, and
|
||||
changelog history) are recorded in :data:`RETAINED_SURFACES`, not renamed.
|
||||
One-time legacy import of a previous Voculinux install lives in
|
||||
:mod:`vocalinux.legacy_import` and keys off :data:`LEGACY_APP_DIR_NAME`.
|
||||
"""
|
||||
|
||||
APP_NAME: str = "Vocalinux"
|
||||
APP_ID: str = "vocalinux"
|
||||
APP_DIR_NAME: str = "vocalinux"
|
||||
CLI_PROG: str = "vocalinux"
|
||||
APP_NAME: str = "Voculinux"
|
||||
APP_ID: str = "voculinux"
|
||||
APP_DIR_NAME: str = "voculinux"
|
||||
CLI_PROG: str = "voculinux"
|
||||
|
||||
BUS_NAME: str = "com.vocalinux.Vocalinux"
|
||||
OBJECT_PATH: str = "/com/vocalinux/Vocalinux"
|
||||
INTERFACE_NAME: str = "com.vocalinux.Vocalinux"
|
||||
BUS_NAME: str = "com.voculinux.Voculinux"
|
||||
OBJECT_PATH: str = "/com/voculinux/Voculinux"
|
||||
INTERFACE_NAME: str = "com.voculinux.Voculinux"
|
||||
|
||||
DESKTOP_FILENAME: str = "vocalinux.desktop"
|
||||
DESKTOP_ICON_NAME: str = "vocalinux"
|
||||
DESKTOP_FILENAME: str = "voculinux.desktop"
|
||||
DESKTOP_ICON_NAME: str = "voculinux"
|
||||
|
||||
TRAY_TITLE: str = APP_NAME
|
||||
ICON_DEFAULT: str = "vocalinux-microphone-off"
|
||||
ICON_ACTIVE: str = "vocalinux-microphone"
|
||||
ICON_PROCESSING: str = "vocalinux-microphone-process"
|
||||
ICON_DEFAULT: str = "voculinux-microphone-off"
|
||||
ICON_ACTIVE: str = "voculinux-microphone"
|
||||
ICON_PROCESSING: str = "voculinux-microphone-process"
|
||||
ICON_BASE_NAMES: tuple[str, ...] = (
|
||||
"vocalinux",
|
||||
"voculinux",
|
||||
ICON_ACTIVE,
|
||||
ICON_DEFAULT,
|
||||
ICON_PROCESSING,
|
||||
)
|
||||
|
||||
ENGINE_NAME: str = "vocalinux"
|
||||
ENGINE_LONGNAME: str = "Vocalinux"
|
||||
ENGINE_DESCRIPTION: str = "Vocalinux voice dictation (use as default input method)"
|
||||
COMPONENT_NAME: str = "org.freedesktop.IBus.Vocalinux"
|
||||
IBUS_DIR_NAME: str = "vocalinux-ibus"
|
||||
IBUS_TEXTDOMAIN: str = "vocalinux"
|
||||
ENGINE_NAME: str = "voculinux"
|
||||
ENGINE_LONGNAME: str = "Voculinux"
|
||||
ENGINE_DESCRIPTION: str = "Voculinux voice dictation (use as default input method)"
|
||||
COMPONENT_NAME: str = "org.freedesktop.IBus.Voculinux"
|
||||
IBUS_DIR_NAME: str = "voculinux-ibus"
|
||||
IBUS_TEXTDOMAIN: str = "voculinux"
|
||||
|
||||
UPDATE_CHECKER_USER_AGENT: str = "Vocalinux-UpdateChecker"
|
||||
UPDATE_CHECKER_USER_AGENT: str = "Voculinux-UpdateChecker"
|
||||
|
||||
RESOURCE_DIR_NAME: str = "vocalinux"
|
||||
RESOURCE_DIR_NAME: str = "voculinux"
|
||||
|
||||
VERIFICATION_STAMP_NAME: str = ".vocalinux_verified"
|
||||
LOG_FILE_PREFIX: str = "vocalinux_logs_"
|
||||
PORTAL_TOKEN_PREFIX: str = "vocalinux_"
|
||||
VERIFICATION_STAMP_NAME: str = ".voculinux_verified"
|
||||
LOG_FILE_PREFIX: str = "voculinux_logs_"
|
||||
PORTAL_TOKEN_PREFIX: str = "voculinux_"
|
||||
|
||||
ENV_FORCE_BACKEND: str = "VOCALINUX_FORCE_BACKEND"
|
||||
ENV_INJECT_MODIFIER_WAIT: str = "VOCALINUX_INJECT_MODIFIER_WAIT"
|
||||
ENV_YDOTOOL_KEY_DELAY: str = "VOCALINUX_YDOTOOL_KEY_DELAY"
|
||||
ENV_FORCE_BACKEND: str = "VOCULINUX_FORCE_BACKEND"
|
||||
ENV_INJECT_MODIFIER_WAIT: str = "VOCULINUX_INJECT_MODIFIER_WAIT"
|
||||
ENV_YDOTOOL_KEY_DELAY: str = "VOCULINUX_YDOTOOL_KEY_DELAY"
|
||||
|
||||
SITE_URL: str = "https://vocalinux.com"
|
||||
SITE_URL: str = "https://voculinux.com"
|
||||
|
||||
#: Files the rename ticket (#18) must flip. Checked by
|
||||
#: Previous install locations read once by the legacy importer. Never written.
|
||||
LEGACY_APP_DIR_NAME: str = "vocalinux"
|
||||
LEGACY_BUS_NAME: str = "com.vocalinux.Vocalinux"
|
||||
|
||||
#: Files the rename ticket (#18) flipped. Checked by
|
||||
#: ``tests/test_app_identity.py`` so the list cannot drift silently.
|
||||
RENAME_SURFACES: tuple[str, ...] = (
|
||||
"pyproject.toml",
|
||||
"vocalinux.desktop",
|
||||
"snap/gui/vocalinux.desktop",
|
||||
"voculinux.desktop",
|
||||
"snap/gui/voculinux.desktop",
|
||||
"snap/snapcraft.yaml",
|
||||
"packaging/flatpak/com.vocalinux.Vocalinux.desktop",
|
||||
"packaging/flatpak/com.vocalinux.Vocalinux.metainfo.xml",
|
||||
"packaging/flatpak/com.vocalinux.Vocalinux.yml",
|
||||
"packaging/aur/vocalinux-bin/vocalinux.desktop",
|
||||
"packaging/flatpak/com.voculinux.Voculinux.desktop",
|
||||
"packaging/flatpak/com.voculinux.Voculinux.metainfo.xml",
|
||||
"packaging/flatpak/com.voculinux.Voculinux.yml",
|
||||
"packaging/aur/voculinux-bin/voculinux.desktop",
|
||||
"web/src/app/page.tsx",
|
||||
"web/package.json",
|
||||
"README.md",
|
||||
|
||||
@@ -494,7 +494,7 @@ def _open_capture_stream(audio: Any, device_index: Optional[int] = None) -> tupl
|
||||
"""
|
||||
Negotiate a working (channels, sample_rate) and return the opened stream.
|
||||
|
||||
Historically Vocalinux probed channels and sample rates separately, each
|
||||
Historically Voculinux probed channels and sample rates separately, each
|
||||
opening and closing PortAudio streams, then opened the real capture stream
|
||||
on top — three open/close cycles in under a second. Bluetooth headset
|
||||
capture (SCO/HFP / PipeWire "Bluetooth internal capture stream") is
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
"""
|
||||
Session-bus D-Bus service for Vocalinux.
|
||||
Session-bus D-Bus service for Voculinux.
|
||||
|
||||
Exposes ``com.vocalinux.Vocalinux`` with ``Toggle``/``Start``/``Stop`` methods
|
||||
Exposes ``com.voculinux.Voculinux`` with ``Toggle``/``Start``/``Stop`` methods
|
||||
so an external trigger (e.g. a KDE Plasma global shortcut running
|
||||
``vocalinux --toggle``) can control a running instance without the internal
|
||||
``voculinux --toggle``) can control a running instance without the internal
|
||||
evdev/pynput hotkey listener reading ``/dev/input``.
|
||||
|
||||
Mirrors the GDBus usage already present in ``suspend_handler.py``.
|
||||
@@ -37,7 +37,7 @@ _INTROSPECTION_XML = f"""
|
||||
|
||||
|
||||
class VocalinuxDBusService:
|
||||
"""Owns the Vocalinux session-bus name and dispatches control methods.
|
||||
"""Owns the Voculinux session-bus name and dispatches control methods.
|
||||
|
||||
Method calls are marshalled onto the GLib/GTK main thread via
|
||||
:func:`GLib.idle_add` before the callbacks run, so recognition start/stop
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
"""Optional local VocaGateway lifecycle helpers for Vocalinux.
|
||||
"""Optional local VocaGateway lifecycle helpers for Voculinux.
|
||||
|
||||
This is an opt-in path for power users who want to run
|
||||
https://github.com/VocaHQ/vocagateway on the same Linux box via podman
|
||||
|
||||
@@ -143,7 +143,7 @@ class GatewayEmbedManager:
|
||||
return
|
||||
threading.Thread(
|
||||
target=_worker,
|
||||
name="vocalinux-gateway-runtime",
|
||||
name="voculinux-gateway-runtime",
|
||||
daemon=True,
|
||||
).start()
|
||||
|
||||
@@ -163,7 +163,7 @@ class GatewayEmbedManager:
|
||||
self.lan_publish = lan_publish
|
||||
thread = threading.Thread(
|
||||
target=self._start_worker,
|
||||
name="vocalinux-gateway-start",
|
||||
name="voculinux-gateway-start",
|
||||
daemon=True,
|
||||
)
|
||||
thread.start()
|
||||
@@ -171,7 +171,7 @@ class GatewayEmbedManager:
|
||||
def stop_async(self) -> None:
|
||||
thread = threading.Thread(
|
||||
target=self._stop_worker,
|
||||
name="vocalinux-gateway-stop",
|
||||
name="voculinux-gateway-stop",
|
||||
daemon=True,
|
||||
)
|
||||
thread.start()
|
||||
@@ -201,7 +201,7 @@ class GatewayEmbedManager:
|
||||
self._republish_started = True
|
||||
thread = threading.Thread(
|
||||
target=self._republish_worker,
|
||||
name="vocalinux-gateway-republish",
|
||||
name="voculinux-gateway-republish",
|
||||
daemon=True,
|
||||
)
|
||||
thread.start()
|
||||
@@ -303,7 +303,7 @@ class GatewayEmbedManager:
|
||||
logger.exception("gateway poll failed")
|
||||
|
||||
self._poll_thread = threading.Thread(
|
||||
target=_loop, name="vocalinux-gateway-poll", daemon=True
|
||||
target=_loop, name="voculinux-gateway-poll", daemon=True
|
||||
)
|
||||
self._poll_thread.start()
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@ def xdg_cache_home() -> str:
|
||||
|
||||
|
||||
def gateway_cache_dir() -> str:
|
||||
return os.path.join(xdg_cache_home(), "vocalinux", f"vocagateway-{GATEWAY_RELEASE_TAG}")
|
||||
return os.path.join(xdg_cache_home(), "voculinux", f"vocagateway-{GATEWAY_RELEASE_TAG}")
|
||||
|
||||
|
||||
def gateway_embed_config_dir() -> str:
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
"""Map a paired local gateway onto Vocalinux remote_api settings."""
|
||||
"""Map a paired local gateway onto Voculinux remote_api settings."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
|
||||
@@ -132,7 +132,7 @@ def detect_container_runtime(
|
||||
binary=None,
|
||||
hint=(
|
||||
"Install podman (preferred) or docker to run a local VocaGateway. "
|
||||
"Vocalinux does not bundle a container engine in the AppImage."
|
||||
"Voculinux does not bundle a container engine in the AppImage."
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
@@ -34,8 +34,8 @@ def detect_sandbox(
|
||||
blocked=True,
|
||||
kind="flatpak",
|
||||
hint=(
|
||||
"Vocalinux is running as a Flatpak and cannot reach the host "
|
||||
"podman/docker socket. Run Vocalinux from the native package or "
|
||||
"Voculinux is running as a Flatpak and cannot reach the host "
|
||||
"podman/docker socket. Run Voculinux from the native package or "
|
||||
"AppImage, or start VocaGateway on the host and point Remote "
|
||||
"Server at it. See docs/GATEWAY_EMBED.md."
|
||||
),
|
||||
|
||||
@@ -0,0 +1,120 @@
|
||||
"""One-time legacy Vocalinux import for the Voculinux fork.
|
||||
|
||||
Ticket #18: on first run, a previous Vocalinux install's user data is copied
|
||||
once into the new ``voculinux`` locations — ``config.json``, the custom
|
||||
dictionary (``dictionary.txt`` plus ``custom-dictionary-corrections.json``),
|
||||
and the RemoteDesktop portal restore token. After that the new configuration
|
||||
always wins: no merging, no write-back to the legacy location.
|
||||
|
||||
Exactly-once falls out of existence checks — a copied file is never copied
|
||||
again, and a run that already has new locations imports nothing.
|
||||
"""
|
||||
|
||||
import logging
|
||||
import os
|
||||
import shutil
|
||||
|
||||
from .app_identity import APP_DIR_NAME, LEGACY_APP_DIR_NAME
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
#: Directory basenames, deliberately literal: the legacy name must keep
|
||||
#: resolving to the old install even after the rename.
|
||||
LEGACY_CONFIG_DIRNAME: str = LEGACY_APP_DIR_NAME
|
||||
NEW_CONFIG_DIRNAME: str = APP_DIR_NAME
|
||||
|
||||
CONFIG_FILENAME: str = "config.json"
|
||||
TERMS_FILENAME: str = "dictionary.txt"
|
||||
CORRECTIONS_FILENAME: str = "custom-dictionary-corrections.json"
|
||||
RESTORE_TOKEN_FILENAME: str = "remote_desktop_restore_token"
|
||||
|
||||
#: ``(subdirectory, filename)`` pairs copied ``config -> config`` or
|
||||
#: ``data -> data``. Models are deliberately absent: they re-resolve through
|
||||
#: the pinned-checksum download path instead of being migrated.
|
||||
CONFIG_PAYLOAD: tuple[tuple[str, str], ...] = (
|
||||
("", CONFIG_FILENAME),
|
||||
("", TERMS_FILENAME),
|
||||
("", CORRECTIONS_FILENAME),
|
||||
)
|
||||
DATA_PAYLOAD: tuple[tuple[str, str], ...] = (("", RESTORE_TOKEN_FILENAME),)
|
||||
|
||||
|
||||
def _xdg_dir(env_var: str, fallback: str) -> str:
|
||||
"""Return an XDG base directory (empty values treated as unset)."""
|
||||
return os.environ.get(env_var) or os.path.expanduser(fallback)
|
||||
|
||||
|
||||
def legacy_config_dir() -> str:
|
||||
"""Return the previous Vocalinux configuration directory."""
|
||||
return os.path.join(_xdg_dir("XDG_CONFIG_HOME", "~/.config"), LEGACY_CONFIG_DIRNAME)
|
||||
|
||||
|
||||
def new_config_dir() -> str:
|
||||
"""Return the Voculinux configuration directory."""
|
||||
return os.path.join(_xdg_dir("XDG_CONFIG_HOME", "~/.config"), NEW_CONFIG_DIRNAME)
|
||||
|
||||
|
||||
def legacy_data_dir() -> str:
|
||||
"""Return the previous Vocalinux data directory."""
|
||||
return os.path.join(_xdg_dir("XDG_DATA_HOME", "~/.local/share"), LEGACY_CONFIG_DIRNAME)
|
||||
|
||||
|
||||
def new_data_dir() -> str:
|
||||
"""Return the Voculinux data directory."""
|
||||
return os.path.join(_xdg_dir("XDG_DATA_HOME", "~/.local/share"), NEW_CONFIG_DIRNAME)
|
||||
|
||||
|
||||
def _copy_once(src: str, dst: str) -> bool:
|
||||
"""Copy ``src`` to ``dst`` when the destination does not exist yet.
|
||||
|
||||
Returns True when a copy happened. Missing sources, existing
|
||||
destinations, and I/O errors copy nothing and return False.
|
||||
"""
|
||||
if os.path.exists(dst):
|
||||
return False
|
||||
if not os.path.isfile(src):
|
||||
return False
|
||||
try:
|
||||
os.makedirs(os.path.dirname(dst) or ".", exist_ok=True)
|
||||
shutil.copy2(src, dst)
|
||||
except OSError as error:
|
||||
logger.warning("Legacy import skipped %s: %s", src, error)
|
||||
return False
|
||||
logger.info("Imported legacy file %s to %s", src, dst)
|
||||
return True
|
||||
|
||||
|
||||
def import_legacy_once() -> list[str]:
|
||||
"""Copy legacy Vocalinux user data into the new locations, once.
|
||||
|
||||
Preconditions (all must hold, otherwise nothing is imported):
|
||||
- the new configuration directory has no ``config.json`` yet (an
|
||||
existing new configuration always wins, with no merge and no
|
||||
write-back to the legacy location);
|
||||
- at least one legacy payload file exists.
|
||||
|
||||
Returns the list of destination paths that were created.
|
||||
"""
|
||||
new_config = os.path.join(new_config_dir(), CONFIG_FILENAME)
|
||||
if os.path.exists(new_config):
|
||||
return []
|
||||
legacy_config = legacy_config_dir()
|
||||
if not os.path.isdir(legacy_config):
|
||||
return []
|
||||
|
||||
imported: list[str] = []
|
||||
for _subdir, filename in CONFIG_PAYLOAD:
|
||||
src = os.path.join(legacy_config, filename)
|
||||
dst = os.path.join(new_config_dir(), filename)
|
||||
if _copy_once(src, dst):
|
||||
imported.append(dst)
|
||||
legacy_data = legacy_data_dir()
|
||||
if os.path.isdir(legacy_data):
|
||||
for _subdir, filename in DATA_PAYLOAD:
|
||||
src = os.path.join(legacy_data, filename)
|
||||
dst = os.path.join(new_data_dir(), filename)
|
||||
if _copy_once(src, dst):
|
||||
imported.append(dst)
|
||||
if imported:
|
||||
logger.info("Legacy Vocalinux import complete: %d file(s)", len(imported))
|
||||
return imported
|
||||
@@ -1,6 +1,6 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Main entry point for Vocalinux application.
|
||||
Main entry point for Voculinux application.
|
||||
"""
|
||||
|
||||
import argparse
|
||||
@@ -157,11 +157,11 @@ def _dispatch_trigger(command: str) -> int:
|
||||
from .dbus_service import send_command
|
||||
|
||||
if send_command(command):
|
||||
logger.info("Sent '%s' command to running Vocalinux instance", command)
|
||||
logger.info("Sent '%s' command to running Voculinux instance", command)
|
||||
return 0
|
||||
|
||||
logger.error(
|
||||
"Could not reach a running Vocalinux instance to '%s'. Is Vocalinux running?",
|
||||
"Could not reach a running Voculinux instance to '%s'. Is Voculinux running?",
|
||||
command,
|
||||
)
|
||||
return 1
|
||||
@@ -257,7 +257,7 @@ def check_dependencies():
|
||||
)
|
||||
logger.error("")
|
||||
logger.error(
|
||||
"For pipx users: Install system packages BEFORE running 'pipx install vocalinux'"
|
||||
"For pipx users: Install system packages BEFORE running 'pipx install voculinux'"
|
||||
)
|
||||
logger.error("")
|
||||
logger.error("For the best experience, use the recommended installer:")
|
||||
@@ -304,12 +304,12 @@ def check_display_available():
|
||||
|
||||
display = Gdk.Display.get_default()
|
||||
if display is None:
|
||||
logger.error("No display available. Vocalinux requires a graphical environment.")
|
||||
logger.error("No display available. Voculinux requires a graphical environment.")
|
||||
logger.error("")
|
||||
logger.error("If running remotely, ensure DISPLAY is set:")
|
||||
logger.error(" export DISPLAY=:0")
|
||||
logger.error("")
|
||||
logger.error("If running in a headless environment, Vocalinux cannot run.")
|
||||
logger.error("If running in a headless environment, Voculinux cannot run.")
|
||||
return False
|
||||
return True
|
||||
except Exception as e:
|
||||
@@ -394,9 +394,19 @@ def main():
|
||||
# Register cleanup to release lock on exit
|
||||
atexit.register(single_instance.release_lock)
|
||||
|
||||
# One-time legacy Voculinux import (ticket #18): copies a previous
|
||||
# install's config, dictionary, and portal token into the new locations
|
||||
# before anything reads them. No-op on every later run.
|
||||
try:
|
||||
from .legacy_import import import_legacy_once
|
||||
|
||||
import_legacy_once()
|
||||
except Exception as error:
|
||||
logger.debug("Legacy import skipped: %s", error)
|
||||
|
||||
# Check dependencies first (before importing GTK-dependent modules)
|
||||
if not check_dependencies():
|
||||
logger.error("Cannot start Vocalinux due to missing dependencies")
|
||||
logger.error("Cannot start Voculinux due to missing dependencies")
|
||||
sys.exit(1)
|
||||
|
||||
# Identity for WM class / AppIndicator title (otherwise shows as main.py)
|
||||
@@ -574,7 +584,7 @@ def main():
|
||||
)
|
||||
|
||||
# Initialize main components
|
||||
logger.info("Initializing Vocalinux...")
|
||||
logger.info("Initializing Voculinux...")
|
||||
|
||||
try:
|
||||
# Initialize speech recognition engine with saved/configured settings
|
||||
@@ -940,7 +950,7 @@ def main():
|
||||
# script path mid-queue can never let a later segment overtake an
|
||||
# earlier one still waiting behind a running script.
|
||||
post_processing_executor = ThreadPoolExecutor(
|
||||
max_workers=1, thread_name_prefix="vocalinux-post-processing"
|
||||
max_workers=1, thread_name_prefix="voculinux-post-processing"
|
||||
)
|
||||
# Cleared by the quit hook so a queued or in-flight job drops its
|
||||
# segment instead of injecting while the injector is being stopped.
|
||||
@@ -996,7 +1006,7 @@ def main():
|
||||
logger.exception("Focus probe crashed unexpectedly: %s", exc)
|
||||
probe_result.put(None)
|
||||
|
||||
threading.Thread(target=probe, daemon=True, name="vocalinux-focus-probe").start()
|
||||
threading.Thread(target=probe, daemon=True, name="voculinux-focus-probe").start()
|
||||
return probe_result
|
||||
|
||||
def _probe_result(
|
||||
@@ -1374,7 +1384,7 @@ def main():
|
||||
indicator.run()
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to initialize Vocalinux: {e}")
|
||||
logger.error(f"Failed to initialize Voculinux: {e}")
|
||||
logger.error("Please check the logs above for more details")
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
@@ -1 +1 @@
|
||||
"""Bundled resources (icons, sounds) for Vocalinux."""
|
||||
"""Bundled resources (icons, sounds) for Voculinux."""
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="128" height="128" viewBox="0 0 1024 1024" role="img" aria-label="Vocalinux idle">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="128" height="128" viewBox="0 0 1024 1024" role="img" aria-label="Voculinux idle">
|
||||
<!-- Opaque disc for StatusNotifier trays (~22px) -->
|
||||
<circle cx="512" cy="512" r="480" fill="#0B1A15"/>
|
||||
<g transform="translate(28.386 32.619) scale(2.116474)">
|
||||
|
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 1.0 KiB |
@@ -1,4 +1,4 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="128" height="128" viewBox="0 0 1024 1024" role="img" aria-label="Vocalinux processing">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="128" height="128" viewBox="0 0 1024 1024" role="img" aria-label="Voculinux processing">
|
||||
<!-- Opaque disc for StatusNotifier trays (~22px) -->
|
||||
<circle cx="512" cy="512" r="480" fill="#A86108"/>
|
||||
<g transform="translate(28.386 32.619) scale(2.116474)">
|
||||
|
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 1.0 KiB |
@@ -1,4 +1,4 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="128" height="128" viewBox="0 0 1024 1024" role="img" aria-label="Vocalinux recording">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="128" height="128" viewBox="0 0 1024 1024" role="img" aria-label="Voculinux recording">
|
||||
<!-- Opaque disc for StatusNotifier trays (~22px) -->
|
||||
<circle cx="512" cy="512" r="480" fill="#C73D38"/>
|
||||
<g transform="translate(28.386 32.619) scale(2.116474)">
|
||||
|
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 1.0 KiB |
@@ -1,4 +1,4 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 460 460" width="460" height="460" role="img" aria-label="Vocalinux">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 460 460" width="460" height="460" role="img" aria-label="Voculinux">
|
||||
<circle cx="229" cy="231.5" r="221.5" fill="#0F6B57"/>
|
||||
<path d="M 164 127 A 65 65 0 0 1 294 127 L 294 224 A 65 65 0 0 1 164 224 Z" fill="#F2F6F2"/>
|
||||
<path d="M 144.87 202.02 A 87 87 0 1 0 312.13 202.02" fill="none" stroke="#F2F6F2" stroke-width="18" stroke-linecap="round"/>
|
||||
|
Before Width: | Height: | Size: 887 B After Width: | Height: | Size: 887 B |
@@ -1,5 +1,5 @@
|
||||
"""
|
||||
Single instance enforcement for Vocalinux.
|
||||
Single instance enforcement for Voculinux.
|
||||
|
||||
Uses file locking to ensure only one instance runs at a time.
|
||||
Works across sessions and handles stale locks from crashed processes.
|
||||
@@ -16,7 +16,7 @@ from .utils.paths import data_dir
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# Lock file location: $XDG_DATA_HOME/vocalinux/ (defaults to ~/.local/share/vocalinux/)
|
||||
# Lock file location: $XDG_DATA_HOME/voculinux/ (defaults to ~/.local/share/voculinux/)
|
||||
LOCK_FILE_DIR = Path(data_dir())
|
||||
LOCK_FILE_PATH = LOCK_FILE_DIR / "instance.lock"
|
||||
|
||||
@@ -61,8 +61,8 @@ def acquire_lock() -> bool:
|
||||
fcntl.flock(fd, fcntl.LOCK_EX | fcntl.LOCK_NB)
|
||||
except (OSError, IOError):
|
||||
# Lock is held by another process
|
||||
logger.warning("Another instance of Vocalinux is already running. Exiting.")
|
||||
print("Another instance of Vocalinux is already running.", file=sys.stderr)
|
||||
logger.warning("Another instance of Voculinux is already running. Exiting.")
|
||||
print("Another instance of Voculinux is already running.", file=sys.stderr)
|
||||
print(
|
||||
f"If you're sure no other instance is running, delete: {LOCK_FILE_PATH}",
|
||||
file=sys.stderr,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
"""
|
||||
Speech recognition module for Vocalinux
|
||||
Speech recognition module for Voculinux
|
||||
"""
|
||||
|
||||
from . import command_processor, recognition_manager # noqa: F401
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
"""
|
||||
Command processor for Vocalinux.
|
||||
Command processor for Voculinux.
|
||||
|
||||
This module processes text commands from speech recognition, such as
|
||||
"new line", "period", etc.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
"""Speech recognition engine registry for Vocalinux.
|
||||
"""Speech recognition engine registry for Voculinux.
|
||||
|
||||
New engine implementations are registered here so the recognition manager can
|
||||
discover them without hard-coding every backend.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
"""Faster-Whisper speech recognition engine for Vocalinux.
|
||||
"""Faster-Whisper speech recognition engine for Voculinux.
|
||||
|
||||
This module wraps `faster_whisper.WhisperModel` behind the Engine Protocol. It
|
||||
reuses the same audio preprocessing used by the OpenAI Whisper backend and is
|
||||
@@ -123,7 +123,7 @@ class FasterWhisperEngine:
|
||||
self._model_initialized = False
|
||||
|
||||
def _normalize_language(self, language: Optional[str] = None) -> Optional[str]:
|
||||
"""Map Vocalinux language codes to faster-whisper language codes."""
|
||||
"""Map Voculinux language codes to faster-whisper language codes."""
|
||||
language = self.language if language is None else language
|
||||
if language == "auto":
|
||||
return None
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
"""
|
||||
Speech recognition manager module for Vocalinux.
|
||||
Speech recognition manager module for Voculinux.
|
||||
|
||||
This module provides a unified interface to different speech recognition engines,
|
||||
currently supporting VOSK, Whisper, and whisper.cpp.
|
||||
@@ -179,7 +179,7 @@ def _pywhispercpp_distribution_version() -> Optional[tuple[int, ...]]:
|
||||
AUR ``python-pywhispercpp-{cpu,cuda,rocm}`` are still 1.4.x. Those wheels
|
||||
advertise ``context_params`` on ``Model.__init__`` then setattr it onto
|
||||
``whisper_full_params``, which raises AttributeError (AUR comments on
|
||||
vocalinux, GitHub #625).
|
||||
voculinux, GitHub #625).
|
||||
"""
|
||||
try:
|
||||
from importlib.metadata import version as pkg_version
|
||||
@@ -585,7 +585,7 @@ class SpeechRecognitionManager:
|
||||
if self._silero_vad is not None:
|
||||
logger.info("Using Silero neural VAD")
|
||||
else:
|
||||
logger.info("Using amplitude-based VAD (install vocalinux[vad] for neural VAD)")
|
||||
logger.info("Using amplitude-based VAD (install voculinux[vad] for neural VAD)")
|
||||
|
||||
# Audio device selection (None means use system default)
|
||||
self.audio_device_index = kwargs.get("audio_device_index", None)
|
||||
@@ -951,7 +951,7 @@ class SpeechRecognitionManager:
|
||||
play_error_sound()
|
||||
_show_notification(
|
||||
"Language Shortcut Unavailable",
|
||||
f"Vocalinux cannot dictate in {language} with the loaded model. "
|
||||
f"Voculinux cannot dictate in {language} with the loaded model. "
|
||||
"Choose a multilingual model in Settings to use this shortcut.",
|
||||
"dialog-warning",
|
||||
)
|
||||
@@ -1078,7 +1078,7 @@ class SpeechRecognitionManager:
|
||||
# Check if this is a pre-installed model
|
||||
if any(self.vosk_model_path.startswith(sys_dir) for sys_dir in SYSTEM_MODELS_DIRS):
|
||||
logger.info(f"Using pre-installed VOSK model from {self.vosk_model_path}")
|
||||
elif os.path.exists(os.path.join(self.vosk_model_path, ".vocalinux_preinstalled")):
|
||||
elif os.path.exists(os.path.join(self.vosk_model_path, ".voculinux_preinstalled")):
|
||||
logger.info(f"Using installer-provided VOSK model from {self.vosk_model_path}")
|
||||
else:
|
||||
logger.info(f"Using existing VOSK model from {self.vosk_model_path}")
|
||||
@@ -1136,7 +1136,7 @@ class SpeechRecognitionManager:
|
||||
available here is comparing the model's on-disk footprint against
|
||||
currently available memory before calling into the native loader
|
||||
(issue #676: this spike killed the process via the kernel OOM
|
||||
killer on a 16GB machine, with no exception Vocalinux could catch).
|
||||
killer on a 16GB machine, with no exception Voculinux could catch).
|
||||
"""
|
||||
import psutil
|
||||
|
||||
@@ -1203,7 +1203,7 @@ class SpeechRecognitionManager:
|
||||
self.model = None
|
||||
|
||||
# load_model() verifies the checkpoint against the sha256 in its URL,
|
||||
# for a file already on disk as well as a fresh download, so Vocalinux
|
||||
# for a file already on disk as well as a fresh download, so Voculinux
|
||||
# never hashes Whisper checkpoints itself.
|
||||
self.model = whisper.load_model(
|
||||
self.model_size, device=device, download_root=whisper_cache_dir
|
||||
@@ -1858,7 +1858,7 @@ class SpeechRecognitionManager:
|
||||
else:
|
||||
logger.warning(
|
||||
"pywhispercpp is Vulkan-backed but vulkaninfo found no devices. "
|
||||
"Install vulkan-tools so Vocalinux can pick a discrete GPU. "
|
||||
"Install vulkan-tools so Voculinux can pick a discrete GPU. "
|
||||
"whisper.cpp will default to GPU 0, which may be an iGPU on hybrid laptops."
|
||||
)
|
||||
elif host_backend != ComputeBackend.CPU:
|
||||
@@ -1986,7 +1986,7 @@ class SpeechRecognitionManager:
|
||||
|
||||
logger.warning(f"Vulkan GPU initialization failed: {error}. Falling back to CPU backend.")
|
||||
_show_notification(
|
||||
"Vocalinux: GPU Fallback",
|
||||
"Voculinux: GPU Fallback",
|
||||
"Your GPU doesn't support whisper.cpp Vulkan.\n" "Switched to CPU mode - still fast!",
|
||||
"dialog-information",
|
||||
)
|
||||
@@ -2573,7 +2573,7 @@ class SpeechRecognitionManager:
|
||||
"""Extract transcript text from OpenAI-compatible ASR responses.
|
||||
|
||||
FunASR/SenseVoice wrappers may include extra metadata or segment arrays.
|
||||
Vocalinux only injects the transcript text and ignores the metadata.
|
||||
Voculinux only injects the transcript text and ignores the metadata.
|
||||
"""
|
||||
if payload is None:
|
||||
return ""
|
||||
@@ -2686,7 +2686,7 @@ class SpeechRecognitionManager:
|
||||
url,
|
||||
stream=True,
|
||||
timeout=self._MODEL_DOWNLOAD_TIMEOUT,
|
||||
headers={"User-Agent": f"vocalinux/{__version__}"},
|
||||
headers={"User-Agent": f"voculinux/{__version__}"},
|
||||
proxies=requests_proxies(),
|
||||
)
|
||||
except requests.exceptions.RequestException as e:
|
||||
@@ -3441,7 +3441,7 @@ class SpeechRecognitionManager:
|
||||
play_error_sound()
|
||||
_show_notification(
|
||||
"Dictation Paused",
|
||||
"Vocalinux is paused because a listed game or app is running. "
|
||||
"Voculinux is paused because a listed game or app is running. "
|
||||
"Close that app or remove it from Auto-Pause settings to resume.",
|
||||
"dialog-information",
|
||||
)
|
||||
@@ -3457,7 +3457,7 @@ class SpeechRecognitionManager:
|
||||
play_error_sound()
|
||||
_show_notification(
|
||||
"Model Reload Failed",
|
||||
"Vocalinux could not reload the speech model. "
|
||||
"Voculinux could not reload the speech model. "
|
||||
"Open Settings to check your engine and try again.",
|
||||
"dialog-warning",
|
||||
)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
"""
|
||||
Silero VAD wrapper for Vocalinux.
|
||||
Silero VAD wrapper for Voculinux.
|
||||
|
||||
Provides neural-network-based Voice Activity Detection using the Silero VAD
|
||||
ONNX model. Falls back gracefully when onnxruntime is unavailable.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
"""
|
||||
Suspend/resume handler for Vocalinux.
|
||||
Suspend/resume handler for Voculinux.
|
||||
|
||||
Monitors systemd-logind via D-Bus for PrepareForSleep signals and
|
||||
automatically reinitializes the audio pipeline and speech model
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
"""
|
||||
Text injection module for Vocalinux
|
||||
Text injection module for Voculinux
|
||||
"""
|
||||
|
||||
from . import text_injector # noqa: F401
|
||||
|
||||
@@ -330,9 +330,9 @@ def focused_xwayland_client_without_ibus() -> bool:
|
||||
An X11 client only talks to IBus through an input method module that it
|
||||
loads at startup from GTK_IM_MODULE (GTK), QT_IM_MODULE (Qt) or XMODIFIERS
|
||||
(XIM). Its toolkit is read from the libraries it has mapped. Without
|
||||
one, IBus has no input context for it: the Vocalinux engine still commits
|
||||
one, IBus has no input context for it: the Voculinux engine still commits
|
||||
and reports success, and the text goes nowhere (#972). The client's own
|
||||
environment is read because it can differ from the one Vocalinux runs in.
|
||||
environment is read because it can differ from the one Voculinux runs in.
|
||||
|
||||
While a native Wayland client holds focus, XWayland's active window is
|
||||
unset or a placeholder with no pid and no WM_CLASS, so this returns False.
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
"""
|
||||
IBus engine for Vocalinux text injection.
|
||||
IBus engine for Voculinux text injection.
|
||||
|
||||
This module provides an IBus input method engine that acts as a transparent
|
||||
proxy - it passes all keystrokes through normally while allowing Vocalinux
|
||||
proxy - it passes all keystrokes through normally while allowing Voculinux
|
||||
to inject text directly via commit_text().
|
||||
|
||||
The engine should be set as the user's default input method. It will:
|
||||
@@ -28,7 +28,7 @@ from pathlib import Path
|
||||
from typing import Any, Callable, Mapping, Optional
|
||||
|
||||
# Package import when loaded normally; absolute / inline fallbacks when this
|
||||
# file is executed by path (Vocalinux start_engine_process / IBus component exec).
|
||||
# file is executed by path (Voculinux start_engine_process / IBus component exec).
|
||||
try:
|
||||
from ..app_identity import (
|
||||
APP_NAME,
|
||||
@@ -57,14 +57,14 @@ except ImportError: # pragma: no cover - exercised via script-path subprocess
|
||||
except ImportError:
|
||||
# Last resort: same semantics as the vocalinux.utils originals, except
|
||||
# that host_env cannot strip a bundle it can no longer import.
|
||||
# Identity literals below keep in step with vocalinux/app_identity.py.
|
||||
APP_NAME = "Vocalinux"
|
||||
COMPONENT_NAME = "org.freedesktop.IBus.Vocalinux"
|
||||
ENGINE_DESCRIPTION = "Vocalinux voice dictation (use as default input method)"
|
||||
ENGINE_LONGNAME = "Vocalinux"
|
||||
ENGINE_NAME = "vocalinux"
|
||||
IBUS_DIR_NAME = "vocalinux-ibus"
|
||||
IBUS_TEXTDOMAIN = "vocalinux"
|
||||
# Identity literals below keep in step with voculinux/app_identity.py.
|
||||
APP_NAME = "Voculinux"
|
||||
COMPONENT_NAME = "org.freedesktop.IBus.Voculinux"
|
||||
ENGINE_DESCRIPTION = "Voculinux voice dictation (use as default input method)"
|
||||
ENGINE_LONGNAME = "Voculinux"
|
||||
ENGINE_NAME = "voculinux"
|
||||
IBUS_DIR_NAME = "voculinux-ibus"
|
||||
IBUS_TEXTDOMAIN = "voculinux"
|
||||
|
||||
def host_env(base: Optional[Mapping[str, str]] = None) -> dict[str, str]:
|
||||
return dict(os.environ if base is None else base)
|
||||
@@ -107,9 +107,9 @@ except (ImportError, ValueError) as e:
|
||||
# File paths for communication ($XDG_DATA_HOME, defaults to ~/.local/share).
|
||||
# Identity strings come from vocalinux/app_identity.py (see the import above);
|
||||
# the homepage below is the retained upstream URL, never renamed.
|
||||
VOCALINUX_IBUS_DIR = Path(xdg_data_home()) / IBUS_DIR_NAME
|
||||
SOCKET_PATH = VOCALINUX_IBUS_DIR / "inject.sock"
|
||||
PID_FILE = VOCALINUX_IBUS_DIR / "engine.pid"
|
||||
VOCULINUX_IBUS_DIR = Path(xdg_data_home()) / IBUS_DIR_NAME
|
||||
SOCKET_PATH = VOCULINUX_IBUS_DIR / "inject.sock"
|
||||
PID_FILE = VOCULINUX_IBUS_DIR / "engine.pid"
|
||||
|
||||
# Shared metadata used by component XML, --xml output, and runtime registration.
|
||||
# Kept in one place to prevent drift between the three consumers.
|
||||
@@ -132,9 +132,9 @@ _COMPONENT_META = {
|
||||
|
||||
def ensure_ibus_dir() -> None:
|
||||
"""Ensure the IBus data directory exists with secure permissions."""
|
||||
VOCALINUX_IBUS_DIR.mkdir(parents=True, exist_ok=True)
|
||||
VOCULINUX_IBUS_DIR.mkdir(parents=True, exist_ok=True)
|
||||
# Secure the directory - only owner can access (prevents socket hijacking)
|
||||
VOCALINUX_IBUS_DIR.chmod(0o700)
|
||||
VOCULINUX_IBUS_DIR.chmod(0o700)
|
||||
|
||||
|
||||
def verify_peer_credentials(conn: socket.socket) -> bool:
|
||||
@@ -365,7 +365,7 @@ def _get_exec_command() -> str:
|
||||
|
||||
|
||||
def is_engine_active() -> bool:
|
||||
"""Check if the Vocalinux IBus engine is currently active."""
|
||||
"""Check if the Voculinux IBus engine is currently active."""
|
||||
try:
|
||||
import subprocess
|
||||
|
||||
@@ -494,7 +494,7 @@ def get_current_engine_gnome_fallback() -> Optional[str]:
|
||||
|
||||
Explicit IBus source IDs are returned directly. XKB source IDs are matched
|
||||
against registered IBus engines; names such as ``xkb:cn::`` are never
|
||||
fabricated because a failed restoration can leave Vocalinux active.
|
||||
fabricated because a failed restoration can leave Voculinux active.
|
||||
|
||||
Returns:
|
||||
A verified IBus engine ID, or None if the current source cannot be
|
||||
@@ -811,7 +811,7 @@ def sync_xwayland_layout_from_gnome() -> bool:
|
||||
|
||||
On GNOME Wayland, Mutter hands XWayland one keymap holding every
|
||||
configured layout as a group plus all ``xkb-options``; input-source
|
||||
switches only move the active group inside it. The Vocalinux engine
|
||||
switches only move the active group inside it. The Voculinux engine
|
||||
round-trip (``ibus engine xkb:*`` applies a single-layout map) — and any
|
||||
plain ``setxkbmap -layout X`` — leaves XWayland with a one-group map:
|
||||
XKB group toggles like ``grp:alt_shift_toggle`` have no second group to
|
||||
@@ -987,7 +987,7 @@ def switch_engine(engine_name: str) -> bool:
|
||||
|
||||
|
||||
def is_engine_process_running() -> bool:
|
||||
"""Check if the Vocalinux IBus engine process is running."""
|
||||
"""Check if the Voculinux IBus engine process is running."""
|
||||
try:
|
||||
if not PID_FILE.exists():
|
||||
return False
|
||||
@@ -999,7 +999,7 @@ def is_engine_process_running() -> bool:
|
||||
cmdline_path = Path(f"/proc/{pid}/cmdline")
|
||||
if cmdline_path.exists():
|
||||
cmdline = cmdline_path.read_text()
|
||||
return "ibus_engine.py" in cmdline and "vocalinux" in cmdline
|
||||
return "ibus_engine.py" in cmdline and "voculinux" in cmdline
|
||||
return True # Process exists but can't verify cmdline
|
||||
except (OSError, ValueError, FileNotFoundError):
|
||||
# Process doesn't exist or PID file is invalid
|
||||
@@ -1012,7 +1012,7 @@ def start_engine_process() -> bool:
|
||||
"""
|
||||
Start the IBus engine process in the background.
|
||||
|
||||
This should be called by Vocalinux on startup to ensure the engine
|
||||
This should be called by Voculinux on startup to ensure the engine
|
||||
is running before attempting to switch to it.
|
||||
|
||||
Returns:
|
||||
@@ -1042,7 +1042,7 @@ def start_engine_process() -> bool:
|
||||
# keeps early import/startup failures visible without SIGPIPE risk if the
|
||||
# engine keeps running and continues to log after we return.
|
||||
ensure_ibus_dir()
|
||||
stderr_log = VOCALINUX_IBUS_DIR / "engine.stderr.log"
|
||||
stderr_log = VOCULINUX_IBUS_DIR / "engine.stderr.log"
|
||||
with open(stderr_log, "w", encoding="utf-8") as stderr_fh:
|
||||
process = subprocess.Popen(
|
||||
[sys.executable, str(engine_script)],
|
||||
@@ -1121,7 +1121,7 @@ def stop_engine_process(wait_timeout: float = 2.0) -> None:
|
||||
cmdline_path = Path(f"/proc/{pid}/cmdline")
|
||||
if cmdline_path.exists():
|
||||
cmdline = cmdline_path.read_text()
|
||||
if "ibus_engine.py" not in cmdline or "vocalinux" not in cmdline:
|
||||
if "ibus_engine.py" not in cmdline or "voculinux" not in cmdline:
|
||||
logger.warning(f"PID {pid} is not our engine process, skipping kill")
|
||||
PID_FILE.unlink()
|
||||
return
|
||||
@@ -1163,7 +1163,7 @@ IBusEngineBase: Any = IBus.Engine if IBUS_AVAILABLE else object
|
||||
|
||||
class VocalinuxEngine(IBusEngineBase):
|
||||
"""
|
||||
IBus proxy engine for Vocalinux text injection.
|
||||
IBus proxy engine for Voculinux text injection.
|
||||
|
||||
This engine acts as a transparent proxy:
|
||||
- All keyboard input passes through unchanged
|
||||
@@ -1189,7 +1189,7 @@ class VocalinuxEngine(IBusEngineBase):
|
||||
_FOCUS_WAIT_TIMEOUT_S: float = 1.0
|
||||
|
||||
def __init__(self):
|
||||
"""Initialize the Vocalinux IBus engine."""
|
||||
"""Initialize the Voculinux IBus engine."""
|
||||
if IBUS_AVAILABLE:
|
||||
super().__init__()
|
||||
logger.debug("VocalinuxEngine instance created")
|
||||
@@ -1382,7 +1382,7 @@ class VocalinuxEngine(IBusEngineBase):
|
||||
|
||||
class VocalinuxEngineApplication:
|
||||
"""
|
||||
Application wrapper for running the Vocalinux IBus engine.
|
||||
Application wrapper for running the Voculinux IBus engine.
|
||||
|
||||
This is used when the engine is launched by IBus as a separate process.
|
||||
"""
|
||||
@@ -1392,7 +1392,7 @@ class VocalinuxEngineApplication:
|
||||
|
||||
Args:
|
||||
exec_by_ibus: True when IBus launched this process (via --ibus flag).
|
||||
False when Vocalinux launched it directly.
|
||||
False when Voculinux launched it directly.
|
||||
"""
|
||||
if not IBUS_AVAILABLE:
|
||||
raise RuntimeError("IBus is not available")
|
||||
@@ -1423,7 +1423,7 @@ class VocalinuxEngineApplication:
|
||||
logger.error("bus.request_name() failed")
|
||||
raise RuntimeError("Failed to acquire IBus D-Bus name")
|
||||
else:
|
||||
# Launched by Vocalinux directly — register the full component
|
||||
# Launched by Voculinux directly — register the full component
|
||||
# so IBus discovers our engine without having launched us.
|
||||
component = IBus.Component(
|
||||
name=COMPONENT_NAME,
|
||||
@@ -1444,10 +1444,10 @@ class VocalinuxEngineApplication:
|
||||
raise RuntimeError("Failed to register IBus component")
|
||||
logger.info("Registered component with IBus (standalone mode)")
|
||||
|
||||
logger.info("Vocalinux IBus engine started")
|
||||
logger.info("Voculinux IBus engine started")
|
||||
|
||||
# Start the socket server immediately so it's ready for connections
|
||||
# even before the engine is activated via `ibus engine vocalinux`
|
||||
# even before the engine is activated via `ibus engine voculinux`
|
||||
VocalinuxEngine._start_socket_server()
|
||||
|
||||
def _on_disconnected(self, bus: "IBus.Bus") -> None:
|
||||
@@ -1465,10 +1465,10 @@ class IBusTextInjector:
|
||||
"""
|
||||
Text injector that uses IBus for text injection.
|
||||
|
||||
This class connects to the Vocalinux IBus engine via Unix socket
|
||||
This class connects to the Voculinux IBus engine via Unix socket
|
||||
and sends text to be injected. The normal TextInjector warmup path starts
|
||||
the engine process without selecting it as the user's active IBus engine.
|
||||
Each injection temporarily switches to the Vocalinux engine, commits the
|
||||
Each injection temporarily switches to the Voculinux engine, commits the
|
||||
text, and restores the user's previous engine.
|
||||
|
||||
The auto_activate=True path still performs the older eager activation flow
|
||||
@@ -1499,14 +1499,14 @@ class IBusTextInjector:
|
||||
"""
|
||||
Start the IBus engine process without selecting it as the active engine.
|
||||
|
||||
The Vocalinux engine is only needed while committing dictated text. Keeping
|
||||
The Voculinux engine is only needed while committing dictated text. Keeping
|
||||
the user's normal IBus/XKB engine active preserves dead-key composition and
|
||||
layout-specific behavior during ordinary typing.
|
||||
|
||||
Captures the current engine for shutdown restore only. Injection still
|
||||
resolves the restore target at commit time so a stale warmup value is not
|
||||
used mid-session (see #623); shutdown needs a fallback when quit leaves
|
||||
Vocalinux selected after a failed inject restore (#558).
|
||||
Voculinux selected after a failed inject restore (#558).
|
||||
"""
|
||||
if not is_engine_active():
|
||||
captured = get_current_engine()
|
||||
@@ -1538,28 +1538,28 @@ class IBusTextInjector:
|
||||
f"variant: {self._previous_xkb_layout[1]}, option: {self._previous_xkb_layout[2]}"
|
||||
)
|
||||
|
||||
# Save current engine and switch to Vocalinux
|
||||
# Save current engine and switch to Voculinux
|
||||
if not is_engine_active():
|
||||
self._previous_engine = get_current_engine()
|
||||
if self._previous_engine:
|
||||
logger.info(f"Saving current engine: {self._previous_engine}")
|
||||
|
||||
logger.info("Activating Vocalinux IBus engine...")
|
||||
logger.info("Activating Voculinux IBus engine...")
|
||||
if switch_engine(ENGINE_NAME):
|
||||
logger.info("Vocalinux IBus engine activated")
|
||||
logger.info("Voculinux IBus engine activated")
|
||||
else:
|
||||
raise IBusSetupError(
|
||||
"Failed to activate Vocalinux IBus engine. "
|
||||
"Try manually: ibus engine vocalinux"
|
||||
"Failed to activate Voculinux IBus engine. "
|
||||
"Try manually: ibus engine voculinux"
|
||||
)
|
||||
|
||||
self._wait_for_engine_ready()
|
||||
|
||||
# Restore the user's XKB layout immediately after engine activation.
|
||||
# Switching to the Vocalinux IBus engine can override the system
|
||||
# Switching to the Voculinux IBus engine can override the system
|
||||
# keyboard layout (e.g. Spanish, French AZERTY) with the engine's
|
||||
# default layout. Re-applying the captured XKB layout ensures the
|
||||
# user's keyboard keeps working correctly while Vocalinux is active.
|
||||
# user's keyboard keeps working correctly while Voculinux is active.
|
||||
# See issue #292.
|
||||
if self._previous_xkb_layout:
|
||||
layout, variant, option = self._previous_xkb_layout
|
||||
@@ -1598,15 +1598,15 @@ class IBusTextInjector:
|
||||
delay = min(delay * 2, 2.0)
|
||||
|
||||
raise IBusSetupError(
|
||||
"Vocalinux IBus engine did not become ready after startup. "
|
||||
"Try manually: ibus engine vocalinux"
|
||||
"Voculinux IBus engine did not become ready after startup. "
|
||||
"Try manually: ibus engine voculinux"
|
||||
)
|
||||
|
||||
def stop(self) -> None:
|
||||
"""
|
||||
Stop the IBus text injector and restore previous engine and XKB layout.
|
||||
|
||||
Call this when Vocalinux is shutting down. If no valid engine was
|
||||
Call this when Voculinux is shutting down. If no valid engine was
|
||||
captured before injection (e.g. on GNOME/Wayland with no global IBus
|
||||
engine), restoration is skipped to avoid switching the user to a
|
||||
wrong layout (see issue #497).
|
||||
@@ -1616,12 +1616,12 @@ class IBusTextInjector:
|
||||
``register_component`` makes IBus run ``check_global_engine()``, which
|
||||
only searches ``register_engine_list`` and treats XML engines such as
|
||||
``xkb:es::spa`` as missing, clearing the global engine even when
|
||||
Vocalinux was never selected (issue #558).
|
||||
Voculinux was never selected (issue #558).
|
||||
|
||||
Prefer the *live* engine at quit when it is a real non-Vocalinux
|
||||
Prefer the *live* engine at quit when it is a real non-Voculinux
|
||||
engine, so a user who changed input source after warmup is not
|
||||
switched back to a stale cache. Fall back to ``_previous_engine`` only
|
||||
when the live engine is Vocalinux or unavailable (failed inject restore).
|
||||
when the live engine is Voculinux or unavailable (failed inject restore).
|
||||
"""
|
||||
current = get_current_engine()
|
||||
restore_engine: Optional[str]
|
||||
@@ -1698,7 +1698,7 @@ class IBusTextInjector:
|
||||
logger.info(f"Starting IBus text injection: '{text[:20]}...' (length: {len(text)})")
|
||||
|
||||
restore_engine: Optional[str] = None
|
||||
# Capture before any engine switch. Switching to Vocalinux can override
|
||||
# Capture before any engine switch. Switching to Voculinux can override
|
||||
# the system XKB map to us; scoped injection must restore it after
|
||||
# switching back (#292, #664). Empty on Wayland (no-op restore).
|
||||
captured_xkb = get_current_xkb_layout()
|
||||
@@ -1716,10 +1716,10 @@ class IBusTextInjector:
|
||||
# quit does not restore a stale warmup value (Bugbot on #643 / #558).
|
||||
self._previous_engine = current_engine
|
||||
logger.debug(
|
||||
f"Temporarily activating Vocalinux IBus engine (will restore {current_engine})"
|
||||
f"Temporarily activating Voculinux IBus engine (will restore {current_engine})"
|
||||
)
|
||||
if not switch_engine(ENGINE_NAME):
|
||||
logger.error("Failed to activate Vocalinux IBus engine for injection")
|
||||
logger.error("Failed to activate Voculinux IBus engine for injection")
|
||||
return False
|
||||
|
||||
# Try injection with bounded retries for transient socket/engine races.
|
||||
@@ -1752,7 +1752,7 @@ class IBusTextInjector:
|
||||
|
||||
logger.error(
|
||||
"IBus engine socket not found. "
|
||||
"Make sure Vocalinux IBus engine is running."
|
||||
"Make sure Voculinux IBus engine is running."
|
||||
)
|
||||
return False
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
"""
|
||||
RemoteDesktop portal text injection for Vocalinux.
|
||||
RemoteDesktop portal text injection for Voculinux.
|
||||
|
||||
``org.freedesktop.portal.RemoteDesktop`` is the only key-injection path
|
||||
Wayland itself sanctions: the compositor's portal backend prompts the user
|
||||
@@ -325,7 +325,7 @@ class RemoteDesktopPortal:
|
||||
self._ready.clear()
|
||||
self._connect_error = None
|
||||
self._worker = threading.Thread(
|
||||
target=self._worker_main, name="vocalinux-portal", daemon=True
|
||||
target=self._worker_main, name="voculinux-portal", daemon=True
|
||||
)
|
||||
self._worker.start()
|
||||
if not self._ready.wait(10):
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
"""
|
||||
Text injection module for Vocalinux.
|
||||
Text injection module for Voculinux.
|
||||
|
||||
This module is responsible for injecting recognized text into the active
|
||||
application, supporting both X11 and Wayland environments.
|
||||
@@ -104,7 +104,7 @@ def _kde_wayland_ibus_hint() -> str:
|
||||
"""Return the KDE Plasma Wayland IBus setup hint."""
|
||||
return (
|
||||
"Open System Settings -> Keyboard -> Virtual Keyboard, select "
|
||||
"'IBus Wayland', then restart Vocalinux or log out and back in."
|
||||
"'IBus Wayland', then restart Voculinux or log out and back in."
|
||||
)
|
||||
|
||||
|
||||
@@ -245,7 +245,7 @@ class TextInjector:
|
||||
"""
|
||||
Clean up resources and restore previous state.
|
||||
|
||||
Call this when shutting down Vocalinux.
|
||||
Call this when shutting down Voculinux.
|
||||
"""
|
||||
with self._state_lock:
|
||||
if self._ibus_injector:
|
||||
@@ -318,7 +318,7 @@ class TextInjector:
|
||||
"weston",
|
||||
)
|
||||
|
||||
# Backends a user may pin explicitly, via VOCALINUX_FORCE_BACKEND or the
|
||||
# Backends a user may pin explicitly, via VOCULINUX_FORCE_BACKEND or the
|
||||
# text_injection.backend setting. Named once so the two readers and the two
|
||||
# "expected ..." messages cannot drift apart as values are added.
|
||||
_SELECTABLE_BACKENDS = ("ibus", "portal", "wtype", "ydotool", "xdotool")
|
||||
@@ -534,7 +534,7 @@ class TextInjector:
|
||||
Host ydotool 0.1.x often works without a daemon; starting one is still
|
||||
safe when ydotoold is installed. The CLI still needs a writable
|
||||
/dev/uinput (the v0.18.x GitHub sideload Snap declares the plug:
|
||||
``snap connect vocalinux:uinput``; the Store snap ships neither plug
|
||||
``snap connect voculinux:uinput``; the Store snap ships neither plug
|
||||
nor ydotool).
|
||||
"""
|
||||
if self._is_ydotoold_running():
|
||||
@@ -576,7 +576,7 @@ class TextInjector:
|
||||
|
||||
@staticmethod
|
||||
def _forced_backend_setting() -> Optional[str]:
|
||||
"""``VOCALINUX_FORCE_BACKEND`` as three distinct states.
|
||||
"""``VOCULINUX_FORCE_BACKEND`` as three distinct states.
|
||||
|
||||
``None`` when the variable is not set, ``"auto"`` when it is explicitly
|
||||
set to auto, otherwise the backend name. The distinction matters:
|
||||
@@ -599,7 +599,7 @@ class TextInjector:
|
||||
if value in TextInjector._SELECTABLE_BACKENDS:
|
||||
return value
|
||||
logger.warning(
|
||||
"Ignoring unknown VOCALINUX_FORCE_BACKEND=%r (expected %s)",
|
||||
"Ignoring unknown VOCULINUX_FORCE_BACKEND=%r (expected %s)",
|
||||
value,
|
||||
TextInjector._accepted_backends_help(),
|
||||
)
|
||||
@@ -624,7 +624,7 @@ class TextInjector:
|
||||
return (
|
||||
"ydotool cannot open /dev/uinput. The Store snap ships no "
|
||||
"uinput plug; only the v0.18.x GitHub sideload declares it: "
|
||||
"sudo snap connect vocalinux:uinput"
|
||||
"sudo snap connect voculinux:uinput"
|
||||
)
|
||||
return (
|
||||
"ydotool cannot open /dev/uinput. "
|
||||
@@ -645,7 +645,7 @@ class TextInjector:
|
||||
|
||||
@staticmethod
|
||||
def _forced_backend() -> str:
|
||||
"""Backend pinned via ``VOCALINUX_FORCE_BACKEND``, or ``"auto"``.
|
||||
"""Backend pinned via ``VOCULINUX_FORCE_BACKEND``, or ``"auto"``.
|
||||
|
||||
Autodetection has to infer whether IBus commits actually reach the
|
||||
focused app, and it cannot verify that: ``commit_text()`` reports
|
||||
@@ -780,7 +780,7 @@ class TextInjector:
|
||||
used -- and so the environment is parsed once per construction, which
|
||||
also means a malformed value is reported once rather than per caller.
|
||||
|
||||
``VOCALINUX_FORCE_BACKEND`` wins so a backend can still be A/B-tested
|
||||
``VOCULINUX_FORCE_BACKEND`` wins so a backend can still be A/B-tested
|
||||
for one run without editing (or permanently changing) the user's config.
|
||||
That includes an explicit ``auto``, which asks for autodetection this run
|
||||
and so must short-circuit here rather than fall through to the saved pin.
|
||||
@@ -918,7 +918,7 @@ class TextInjector:
|
||||
)
|
||||
)
|
||||
# Bridging Wayland DEs (GNOME): inject_text() switches to the
|
||||
# real vocalinux engine for each commit, so a bare xkb:* baseline is
|
||||
# real voculinux engine for each commit, so a bare xkb:* baseline is
|
||||
# fine (#501, #504). KDE joins that set only when KWin's Virtual
|
||||
# Keyboard is confirmed on: a confirmed VK bridge is what makes the
|
||||
# commits reach apps, and it is also what keeps the #752
|
||||
@@ -934,7 +934,7 @@ class TextInjector:
|
||||
# Check if IBus is the active input method (not just installed)
|
||||
# This is important because IBus may be installed but not being used,
|
||||
# e.g., when the user has configured ydotool or Fcitx instead.
|
||||
# VOCALINUX_FORCE_BACKEND=ibus bypasses the reachability guards below
|
||||
# VOCULINUX_FORCE_BACKEND=ibus bypasses the reachability guards below
|
||||
# and goes straight to setup.
|
||||
force_ibus = forced == "ibus"
|
||||
if not force_ibus and not ibus_active and not wayland_scoped_ibus:
|
||||
@@ -1278,7 +1278,7 @@ class TextInjector:
|
||||
return
|
||||
|
||||
try:
|
||||
# Keep the Vocalinux IBus process warm without selecting it as the
|
||||
# Keep the Voculinux IBus process warm without selecting it as the
|
||||
# user's active keyboard engine. Activation is scoped to each text
|
||||
# commit so normal typing keeps layout-specific compose/dead keys.
|
||||
self._ibus_injector.prepare_engine()
|
||||
@@ -2778,7 +2778,7 @@ class TextInjector:
|
||||
def _injection_modifier_wait_seconds(self) -> float:
|
||||
"""Return the sanitized max seconds to wait for modifiers to release.
|
||||
|
||||
Reads VOCALINUX_INJECT_MODIFIER_WAIT and falls back to the default for
|
||||
Reads VOCULINUX_INJECT_MODIFIER_WAIT and falls back to the default for
|
||||
anything unusable: a non-numeric value, or a non-finite one. In
|
||||
particular ``inf`` parses fine and is positive, so without this guard it
|
||||
would make the wait deadline infinite and block injection forever while a
|
||||
@@ -2793,7 +2793,7 @@ class TextInjector:
|
||||
return self._DEFAULT_INJECT_MODIFIER_WAIT
|
||||
if not math.isfinite(value):
|
||||
logger.warning(
|
||||
"VOCALINUX_INJECT_MODIFIER_WAIT=%r is not a finite number; " "using default %.1fs",
|
||||
"VOCULINUX_INJECT_MODIFIER_WAIT=%r is not a finite number; " "using default %.1fs",
|
||||
raw,
|
||||
self._DEFAULT_INJECT_MODIFIER_WAIT,
|
||||
)
|
||||
@@ -2805,7 +2805,7 @@ class TextInjector:
|
||||
|
||||
Returns immediately if no modifier is held (the common case), so this
|
||||
adds no latency unless the user is still holding their toggle/PTT
|
||||
shortcut. Bounded by VOCALINUX_INJECT_MODIFIER_WAIT seconds (default 1.0;
|
||||
shortcut. Bounded by VOCULINUX_INJECT_MODIFIER_WAIT seconds (default 1.0;
|
||||
set to 0 to disable). Best-effort: if evdev/permissions are unavailable
|
||||
it simply proceeds.
|
||||
"""
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
"""
|
||||
User interface module for Vocalinux
|
||||
User interface module for Voculinux
|
||||
|
||||
Note: GTK-dependent modules (tray_indicator) are imported lazily to allow
|
||||
the package to be imported even when GTK/AppIndicator3 typelibs are not available.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
"""
|
||||
Action handler for Vocalinux voice commands.
|
||||
Action handler for Voculinux voice commands.
|
||||
|
||||
This module handles special voice commands like "delete that", "undo", etc.
|
||||
"""
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
"""
|
||||
Audio feedback module for Vocalinux.
|
||||
Audio feedback module for Voculinux.
|
||||
|
||||
This module provides audio feedback for various recognition states.
|
||||
"""
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
"""
|
||||
Autostart manager for Vocalinux.
|
||||
Autostart manager for Voculinux.
|
||||
|
||||
This module handles the creation and removal of the XDG autostart desktop entry
|
||||
for automatically starting Vocalinux on login.
|
||||
for automatically starting Voculinux on login.
|
||||
"""
|
||||
|
||||
import logging
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
"""
|
||||
Configuration manager for Vocalinux.
|
||||
Configuration manager for Voculinux.
|
||||
|
||||
This module handles loading, saving, and accessing user preferences.
|
||||
"""
|
||||
@@ -152,7 +152,7 @@ DEFAULT_CONFIG = {
|
||||
"mode": "push_to_talk", # "toggle" or "push_to_talk"
|
||||
# When True, the internal evdev/pynput listener is not started; activation
|
||||
# comes in over D-Bus instead (e.g. a KDE Plasma global shortcut running
|
||||
# `vocalinux --toggle`). Avoids /dev/input access and the input group.
|
||||
# `voculinux --toggle`). Avoids /dev/input access and the input group.
|
||||
"disable_internal_hotkey": False,
|
||||
# Opt-in: forward shortcut key events while still detecting gestures.
|
||||
"pass_shortcut_to_apps": False,
|
||||
@@ -201,7 +201,7 @@ DEFAULT_CONFIG = {
|
||||
"paste_long_text": False, # Faster clipboard paste for long X11 dictations
|
||||
# Text-injection backend: "auto" autodetects, or pin "ibus"/"wtype"/
|
||||
# "ydotool"/"xdotool" when autodetection is wrong (#476).
|
||||
# VOCALINUX_FORCE_BACKEND overrides this for a single run.
|
||||
# VOCULINUX_FORCE_BACKEND overrides this for a single run.
|
||||
"backend": "auto",
|
||||
# Route dictation into the in-app Dictation Pad window instead of
|
||||
# injecting into other apps — the Wayland-safe fallback (#726).
|
||||
@@ -210,7 +210,7 @@ DEFAULT_CONFIG = {
|
||||
"history": {
|
||||
"enabled": True, # Keep recent dictation snippets in the tray menu
|
||||
"max_items": 10, # How many snippets to retain
|
||||
# Also save snippets to $XDG_DATA_HOME/vocalinux/history.jsonl so they
|
||||
# Also save snippets to $XDG_DATA_HOME/voculinux/history.jsonl so they
|
||||
# survive restarts. Off by default: dictated text must not silently
|
||||
# accumulate on disk unless the user asks for it (#758).
|
||||
"persist": False,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
"""
|
||||
Floating on-screen dictation overlay for Vocalinux.
|
||||
Floating on-screen dictation overlay for Voculinux.
|
||||
|
||||
Shows a compact glowing indicator while recognition is active so users get a
|
||||
clear visual cue beyond the system tray icon (similar to SuperWhisper-style
|
||||
@@ -286,7 +286,7 @@ class DictationOverlay:
|
||||
Settings unmaps it instead.
|
||||
|
||||
The window stays, but the request is passed to ``on_close_request``
|
||||
so "Quit" from the dock quits Vocalinux like the tray's Quit (#1028).
|
||||
so "Quit" from the dock quits Voculinux like the tray's Quit (#1028).
|
||||
"""
|
||||
if self._on_close_request is None:
|
||||
logger.info(
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
"""
|
||||
In-app dictation pad for Vocalinux.
|
||||
In-app dictation pad for Voculinux.
|
||||
|
||||
A small persistent window with an editable text view that receives dictated
|
||||
text directly, bypassing cross-application text injection entirely. It is the
|
||||
@@ -252,7 +252,7 @@ class DictationPad:
|
||||
self._Gtk = Gtk
|
||||
|
||||
window = Gtk.Window(type=Gtk.WindowType.TOPLEVEL)
|
||||
window.set_title("Vocalinux Dictation Pad")
|
||||
window.set_title("Voculinux Dictation Pad")
|
||||
window.set_default_size(_PAD_WIDTH, _PAD_HEIGHT)
|
||||
# Closing only hides: the pad keeps its buffer for the whole session
|
||||
# and must not take the app down with it.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
"""
|
||||
First-run dialog for Vocalinux.
|
||||
First-run dialog for Voculinux.
|
||||
|
||||
This module shows a dialog on first run to ask the user about autostart preferences.
|
||||
"""
|
||||
@@ -25,7 +25,7 @@ class FirstRunDialog(Gtk.Dialog):
|
||||
|
||||
def __init__(self, parent: Optional[Gtk.Window] = None):
|
||||
super().__init__(
|
||||
title="Welcome to Vocalinux",
|
||||
title="Welcome to Voculinux",
|
||||
transient_for=parent,
|
||||
flags=Gtk.DialogFlags.MODAL,
|
||||
)
|
||||
@@ -52,12 +52,12 @@ class FirstRunDialog(Gtk.Dialog):
|
||||
box.set_margin_top(24)
|
||||
box.set_margin_bottom(20)
|
||||
|
||||
title_label = Gtk.Label(label="<b>Welcome to Vocalinux!</b>", use_markup=True)
|
||||
title_label = Gtk.Label(label="<b>Welcome to Voculinux!</b>", use_markup=True)
|
||||
title_label.set_justify(Gtk.Justification.CENTER)
|
||||
box.pack_start(title_label, False, False, 0)
|
||||
|
||||
description_label = Gtk.Label(
|
||||
label="Vocalinux runs from your system tray so you can start and stop voice typing anytime.",
|
||||
label="Voculinux runs from your system tray so you can start and stop voice typing anytime.",
|
||||
wrap=True,
|
||||
justify=Gtk.Justification.CENTER,
|
||||
)
|
||||
@@ -77,7 +77,7 @@ class FirstRunDialog(Gtk.Dialog):
|
||||
box.pack_start(quick_guide_label, False, False, 0)
|
||||
|
||||
subtitle_label = Gtk.Label(
|
||||
label="Would you like Vocalinux to start automatically when you log in?",
|
||||
label="Would you like Voculinux to start automatically when you log in?",
|
||||
wrap=True,
|
||||
justify=Gtk.Justification.CENTER,
|
||||
)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
"""
|
||||
Keyboard backend system for Vocalinux.
|
||||
Keyboard backend system for Voculinux.
|
||||
|
||||
This package provides a modular backend system for global keyboard shortcuts,
|
||||
supporting multiple platforms and display servers (X11, Wayland).
|
||||
|
||||
@@ -95,9 +95,9 @@ else:
|
||||
_FORWARDED_EVENT_TYPES = frozenset()
|
||||
|
||||
# Suffix on every uinput clone name. ``_open_keyboard_device`` uses it to
|
||||
# skip clones (also ones created by other Vocalinux processes), so it must
|
||||
# skip clones (also ones created by other Voculinux processes), so it must
|
||||
# survive the kernel's uinput name-length truncation.
|
||||
_CLONE_NAME_SUFFIX = " (vocalinux)"
|
||||
_CLONE_NAME_SUFFIX = " (voculinux)"
|
||||
|
||||
# Name fragments remapper-created keyboards are known to use. Only a
|
||||
# fallback for systems where sysfs is unreadable (snap confinement): the
|
||||
@@ -1234,16 +1234,16 @@ class EvdevDeviceHub:
|
||||
logger.warning(f"Cannot open {device_path}: {e}")
|
||||
return False
|
||||
|
||||
# Only Vocalinux's own uinput clones are never monitored — reading
|
||||
# Only Voculinux's own uinput clones are never monitored — reading
|
||||
# them would loop the events we forwarded straight back in. Clones
|
||||
# are matched by their registered device path; the " (vocalinux)"
|
||||
# name suffix also covers clones of other Vocalinux processes whose
|
||||
# are matched by their registered device path; the " (voculinux)"
|
||||
# name suffix also covers clones of other Voculinux processes whose
|
||||
# paths we never registered. Other virtual devices (key remappers,
|
||||
# accessibility keyboards) ARE monitored: users can bind shortcuts
|
||||
# to them.
|
||||
device_name = str(getattr(device, "name", "") or "")
|
||||
if device_path in self._clone_paths or device_name.endswith(_CLONE_NAME_SUFFIX):
|
||||
logger.debug(f"Skipping Vocalinux clone device: {device_path} ({device_name})")
|
||||
logger.debug(f"Skipping Voculinux clone device: {device_path} ({device_name})")
|
||||
try:
|
||||
device.close()
|
||||
except (OSError, IOError, RuntimeError) as e:
|
||||
@@ -1953,8 +1953,8 @@ class EvdevKeyboardBackend(KeyboardBackend):
|
||||
return (
|
||||
"Snap is missing input-device access. "
|
||||
"Connect once, then restart:\n"
|
||||
"sudo snap connect vocalinux:raw-input\n"
|
||||
"sudo snap connect vocalinux:hardware-observe"
|
||||
"sudo snap connect voculinux:raw-input\n"
|
||||
"sudo snap connect voculinux:hardware-observe"
|
||||
)
|
||||
return (
|
||||
"Add your user to the 'input' group and log out/in:\n"
|
||||
@@ -1967,8 +1967,8 @@ class EvdevKeyboardBackend(KeyboardBackend):
|
||||
if in_snap:
|
||||
return (
|
||||
"Snap is missing input-device access. Connect once, then restart:\n"
|
||||
"sudo snap connect vocalinux:raw-input\n"
|
||||
"sudo snap connect vocalinux:hardware-observe"
|
||||
"sudo snap connect voculinux:raw-input\n"
|
||||
"sudo snap connect voculinux:hardware-observe"
|
||||
)
|
||||
|
||||
try:
|
||||
@@ -2034,7 +2034,7 @@ class EvdevKeyboardBackend(KeyboardBackend):
|
||||
|
||||
- Pure-modifier gestures (``right_alt+right_alt``, ``ctrl+ctrl``):
|
||||
every event of the configured modifier side(s) is consumed — the
|
||||
modifier is reserved for Vocalinux while the listener runs.
|
||||
modifier is reserved for Voculinux while the listener runs.
|
||||
- Bare function-key shortcuts (``f11``): every event of that key.
|
||||
- Modifier+key combos (``alt+r``): the main key is consumed only
|
||||
while the combo owns it (press with all modifiers held, plus the
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
"""
|
||||
Keyboard shortcut manager for Vocalinux.
|
||||
Keyboard shortcut manager for Voculinux.
|
||||
|
||||
This module provides global keyboard shortcut functionality to
|
||||
start/stop speech recognition with configurable shortcuts.
|
||||
@@ -101,9 +101,9 @@ class KeyboardShortcutManager:
|
||||
logger.warning("Keyboard shortcuts need Snap input access")
|
||||
logger.warning("=" * 60)
|
||||
logger.warning("Global hotkeys read /dev/input (evdev). Connect once:")
|
||||
logger.warning(" sudo snap connect vocalinux:raw-input")
|
||||
logger.warning(" sudo snap connect vocalinux:hardware-observe")
|
||||
logger.warning("Then restart Vocalinux. Until then, use the system tray.")
|
||||
logger.warning(" sudo snap connect voculinux:raw-input")
|
||||
logger.warning(" sudo snap connect voculinux:hardware-observe")
|
||||
logger.warning("Then restart Voculinux. Until then, use the system tray.")
|
||||
logger.warning("=" * 60)
|
||||
return
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
"""
|
||||
Logging viewer dialog for VocaLinux.
|
||||
Logging viewer dialog for Voculinux.
|
||||
|
||||
This module provides a GTK dialog for viewing, filtering, and exporting
|
||||
application logs for debugging purposes.
|
||||
@@ -722,7 +722,7 @@ class LoggingDialog(Gtk.Dialog):
|
||||
clipboard = Gtk.Clipboard.get(Gdk.SELECTION_CLIPBOARD)
|
||||
|
||||
# Create header for clipboard content
|
||||
header = f"VocaLinux Logs - Copied at {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n"
|
||||
header = f"Voculinux Logs - Copied at {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n"
|
||||
header += "=" * 80 + "\n\n"
|
||||
|
||||
# Combine header and content
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
"""
|
||||
Logging manager for VocaLinux.
|
||||
Logging manager for Voculinux.
|
||||
|
||||
This module provides centralized logging functionality with UI integration,
|
||||
allowing users to view, filter, and export application logs for debugging.
|
||||
@@ -46,7 +46,7 @@ class LogRecord:
|
||||
|
||||
class LoggingManager:
|
||||
"""
|
||||
Centralized logging manager for VocaLinux.
|
||||
Centralized logging manager for Voculinux.
|
||||
|
||||
This class captures all application logs, stores them in memory,
|
||||
and provides functionality for viewing, filtering, and exporting logs.
|
||||
@@ -64,7 +64,7 @@ class LoggingManager:
|
||||
self.log_callbacks: list[Callable[[LogRecord], None]] = []
|
||||
self.lock = threading.Lock()
|
||||
|
||||
# Create logs directory ($XDG_DATA_HOME/vocalinux/logs; Flatpak-safe)
|
||||
# Create logs directory ($XDG_DATA_HOME/voculinux/logs; Flatpak-safe)
|
||||
self.logs_dir = Path(data_dir()) / "logs"
|
||||
self.logs_dir.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
@@ -178,7 +178,7 @@ class LoggingManager:
|
||||
records = self.get_logs(level_filter, module_filter)
|
||||
|
||||
with open(filepath, "w", encoding="utf-8") as f:
|
||||
f.write("VocaLinux Log Export\n")
|
||||
f.write("Voculinux Log Export\n")
|
||||
f.write(f"Generated: {datetime.now().isoformat()}\n")
|
||||
f.write(f"Total Records: {len(records)}\n")
|
||||
if level_filter:
|
||||
|
||||
@@ -18,7 +18,7 @@ from ..version import __copyright__, __url__, __version__
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
VOCALINUX_SITE_URL = "https://vocalinux.com"
|
||||
VOCULINUX_SITE_URL = "https://voculinux.com"
|
||||
VOCAHQ_SITE_URL = "https://vocahq.com"
|
||||
VOCAMAC_SITE_URL = "https://vocamac.com"
|
||||
VOCAWIN_SITE_URL = "https://vocawin.com"
|
||||
@@ -33,7 +33,7 @@ VOCAHQ_MAILTO_URL = "mailto:hello@vocahq.com"
|
||||
# About page links that are not GitHub release URLs.
|
||||
_ABOUT_OPEN_URLS = frozenset(
|
||||
{
|
||||
VOCALINUX_SITE_URL,
|
||||
VOCULINUX_SITE_URL,
|
||||
VOCAHQ_SITE_URL,
|
||||
VOCAMAC_SITE_URL,
|
||||
VOCAWIN_SITE_URL,
|
||||
@@ -130,11 +130,11 @@ _VOCAHQ_FAMILY_LINKS = (
|
||||
"Open vocahq.com",
|
||||
),
|
||||
(
|
||||
VOCALINUX_SITE_URL,
|
||||
"Vocalinux",
|
||||
VOCULINUX_SITE_URL,
|
||||
"Voculinux",
|
||||
"Linux, available now",
|
||||
("platform-linux",),
|
||||
"Open vocalinux.com",
|
||||
"Open voculinux.com",
|
||||
),
|
||||
(
|
||||
VOCAMAC_SITE_URL,
|
||||
@@ -306,7 +306,7 @@ class AboutSectionMixin(Gtk.Dialog):
|
||||
|
||||
dark = _about_surface_is_dark()
|
||||
about_icon = None
|
||||
logo_path = ResourceManager().get_icon_path("vocalinux")
|
||||
logo_path = ResourceManager().get_icon_path("voculinux")
|
||||
if os.path.exists(logo_path):
|
||||
try:
|
||||
pixbuf = GdkPixbuf.Pixbuf.new_from_file(logo_path)
|
||||
@@ -317,7 +317,7 @@ class AboutSectionMixin(Gtk.Dialog):
|
||||
logger.warning("Failed to load About icon: %s", exc)
|
||||
|
||||
app_group = PreferencesGroup(
|
||||
title="Vocalinux",
|
||||
title="Voculinux",
|
||||
description=(
|
||||
"Available now on Linux (X11 and Wayland). After a model is downloaded, "
|
||||
"speech is processed on this PC."
|
||||
@@ -331,7 +331,7 @@ class AboutSectionMixin(Gtk.Dialog):
|
||||
self.about_version_label.get_style_context().add_class("preference-row-subtitle")
|
||||
version_row = PreferenceRow(
|
||||
title="Version",
|
||||
subtitle="Currently installed Vocalinux build",
|
||||
subtitle="Currently installed Voculinux build",
|
||||
widget=self.about_version_label,
|
||||
keywords=("version", "build"),
|
||||
)
|
||||
@@ -340,8 +340,8 @@ class AboutSectionMixin(Gtk.Dialog):
|
||||
link_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=8)
|
||||
link_box.pack_start(
|
||||
self._about_open_button(
|
||||
VOCALINUX_SITE_URL,
|
||||
"Open vocalinux.com website",
|
||||
VOCULINUX_SITE_URL,
|
||||
"Open voculinux.com website",
|
||||
label="Website",
|
||||
width=110,
|
||||
dark=dark,
|
||||
@@ -353,7 +353,7 @@ class AboutSectionMixin(Gtk.Dialog):
|
||||
link_box.pack_start(
|
||||
self._about_open_button(
|
||||
GITHUB_REPO_URL,
|
||||
"Open the Vocalinux GitHub repository",
|
||||
"Open the Voculinux GitHub repository",
|
||||
label="Source code",
|
||||
width=120,
|
||||
dark=dark,
|
||||
@@ -364,9 +364,9 @@ class AboutSectionMixin(Gtk.Dialog):
|
||||
)
|
||||
links_row = PreferenceRow(
|
||||
title="Open source",
|
||||
subtitle="Product site at vocalinux.com, source on GitHub.",
|
||||
subtitle="Product site at voculinux.com, source on GitHub.",
|
||||
widget=link_box,
|
||||
keywords=("website", "vocalinux.com", "github", "source", "repo", "code", "agpl"),
|
||||
keywords=("website", "voculinux.com", "github", "source", "repo", "code", "agpl"),
|
||||
)
|
||||
app_group.add_row(links_row)
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
"""
|
||||
Settings Dialog for Vocalinux.
|
||||
Settings Dialog for Voculinux.
|
||||
|
||||
Allows users to configure speech recognition engine, model size,
|
||||
and other relevant parameters.
|
||||
@@ -156,10 +156,10 @@ from .settings_about import ( # noqa: F401
|
||||
VOCAHQ_MAILTO_URL,
|
||||
VOCAHQ_SITE_URL,
|
||||
VOCAHQ_X_URL,
|
||||
VOCALINUX_SITE_URL,
|
||||
VOCAMAC_SITE_URL,
|
||||
VOCAPHONE_SITE_URL,
|
||||
VOCAWIN_SITE_URL,
|
||||
VOCULINUX_SITE_URL,
|
||||
AboutSectionMixin,
|
||||
_about_ink_hex,
|
||||
_about_surface_is_dark,
|
||||
@@ -2515,7 +2515,7 @@ def _populate_gpu_device_choices(combo: Any, saved_device: Optional[int]) -> Non
|
||||
|
||||
|
||||
class SettingsDialog(AboutSectionMixin):
|
||||
"""Modern GTK Dialog for configuring Vocalinux settings."""
|
||||
"""Modern GTK Dialog for configuring Voculinux settings."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
@@ -3281,7 +3281,7 @@ class SettingsDialog(AboutSectionMixin):
|
||||
self.autostart_switch = _add_switch_row(
|
||||
group,
|
||||
title="Start on Login",
|
||||
subtitle="Automatically start Vocalinux when you log in",
|
||||
subtitle="Automatically start Voculinux when you log in",
|
||||
keywords=("autostart", "boot", "startup"),
|
||||
)
|
||||
|
||||
@@ -3295,7 +3295,7 @@ class SettingsDialog(AboutSectionMixin):
|
||||
self.missing_tray_warning_switch = Gtk.Switch()
|
||||
missing_tray_warning_row = PreferenceRow(
|
||||
title="Warn if tray support is not detected",
|
||||
subtitle="Show a warning when Vocalinux cannot detect AppIndicator support",
|
||||
subtitle="Show a warning when Voculinux cannot detect AppIndicator support",
|
||||
widget=self.missing_tray_warning_switch,
|
||||
keywords=("tray", "appindicator", "warning"),
|
||||
)
|
||||
@@ -3315,7 +3315,7 @@ class SettingsDialog(AboutSectionMixin):
|
||||
title="Transcription History",
|
||||
description="Recent dictation snippets are kept in memory and shown "
|
||||
"in the tray menu. History clears on quit unless Keep on Disk is on, "
|
||||
"which saves them to history.jsonl under the Vocalinux data directory.",
|
||||
"which saves them to history.jsonl under the Voculinux data directory.",
|
||||
)
|
||||
|
||||
self.history_enabled_switch = Gtk.Switch()
|
||||
@@ -3425,7 +3425,7 @@ class SettingsDialog(AboutSectionMixin):
|
||||
terms_path_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=6)
|
||||
self.dictionary_terms_path_entry = Gtk.Entry()
|
||||
self.dictionary_terms_path_entry.set_hexpand(True)
|
||||
self.dictionary_terms_path_entry.set_placeholder_text("~/.config/vocalinux/dictionary.txt")
|
||||
self.dictionary_terms_path_entry.set_placeholder_text("~/.config/voculinux/dictionary.txt")
|
||||
self.dictionary_terms_path_entry.set_tooltip_text(
|
||||
"UTF-8 line file used for custom terms and the accessibility scanner"
|
||||
)
|
||||
@@ -5080,8 +5080,8 @@ class SettingsDialog(AboutSectionMixin):
|
||||
vad_info_label = Gtk.Label(
|
||||
label=(
|
||||
"Neural VAD is more accurate at separating speech from background "
|
||||
"noise. To enable it, install onnxruntime and restart Vocalinux:\n"
|
||||
' pip install "vocalinux[vad]"'
|
||||
"noise. To enable it, install onnxruntime and restart Voculinux:\n"
|
||||
' pip install "voculinux[vad]"'
|
||||
),
|
||||
xalign=0,
|
||||
wrap=True,
|
||||
@@ -5106,12 +5106,12 @@ class SettingsDialog(AboutSectionMixin):
|
||||
)
|
||||
|
||||
# External activation: drive start/stop from a desktop/compositor global
|
||||
# shortcut (bound to `vocalinux --toggle`) instead of the built-in key
|
||||
# shortcut (bound to `voculinux --toggle`) instead of the built-in key
|
||||
# listener. Avoids reading /dev/input (no keylogging, no `input` group).
|
||||
self.disable_internal_hotkey_switch = Gtk.Switch()
|
||||
self.disable_internal_hotkey_switch.set_tooltip_text(
|
||||
"Turn off the built-in key listener and trigger voice typing from a "
|
||||
"desktop global shortcut bound to 'vocalinux --toggle'. Avoids reading "
|
||||
"desktop global shortcut bound to 'voculinux --toggle'. Avoids reading "
|
||||
"/dev/input (no keylogging, no 'input' group needed)."
|
||||
)
|
||||
external_row = PreferenceRow(
|
||||
@@ -5287,7 +5287,7 @@ class SettingsDialog(AboutSectionMixin):
|
||||
if disabled:
|
||||
self.shortcut_info_label.set_text(
|
||||
"External activation is on: the built-in key listener is off. "
|
||||
"Bind a desktop global shortcut to 'vocalinux --toggle' to start/stop."
|
||||
"Bind a desktop global shortcut to 'voculinux --toggle' to start/stop."
|
||||
)
|
||||
else:
|
||||
# Restore the mode-appropriate hint.
|
||||
@@ -6258,7 +6258,7 @@ class SettingsDialog(AboutSectionMixin):
|
||||
_style_combo(self.proxy_mode_combo)
|
||||
_combo_chrome(
|
||||
self.proxy_mode_combo,
|
||||
tooltip="How Vocalinux connects to model hosts and update checks",
|
||||
tooltip="How Voculinux connects to model hosts and update checks",
|
||||
)
|
||||
self.proxy_mode_combo.append("off", "Off")
|
||||
self.proxy_mode_combo.append("system", "System")
|
||||
@@ -6611,7 +6611,7 @@ class SettingsDialog(AboutSectionMixin):
|
||||
self.latest_release_row.set_title("Release notes")
|
||||
self.latest_release_row.set_subtitle("Unavailable")
|
||||
self.open_release_btn.set_sensitive(True)
|
||||
self.open_release_btn.set_tooltip_text("Open the Vocalinux project page")
|
||||
self.open_release_btn.set_tooltip_text("Open the Voculinux project page")
|
||||
self.open_release_btn.get_style_context().remove_class("suggested-action")
|
||||
self.release_notes_group.hide()
|
||||
# Clear dialog pending state so search restore cannot revive the New
|
||||
@@ -6972,7 +6972,7 @@ class SettingsDialog(AboutSectionMixin):
|
||||
self.gateway_embed_group.add_row(
|
||||
PreferenceRow(
|
||||
title="Remote API preset",
|
||||
subtitle="Points Vocalinux at the local gateway",
|
||||
subtitle="Points Voculinux at the local gateway",
|
||||
widget=self.gateway_use_btn,
|
||||
)
|
||||
)
|
||||
@@ -9390,7 +9390,7 @@ class SettingsDialog(AboutSectionMixin):
|
||||
threading.Thread(
|
||||
target=cancel_test,
|
||||
daemon=True,
|
||||
name="vocalinux-test-dictation-cancel",
|
||||
name="voculinux-test-dictation-cancel",
|
||||
).start()
|
||||
|
||||
def _finish_test_after_user_cancel(self) -> bool:
|
||||
@@ -9464,7 +9464,7 @@ class SettingsDialog(AboutSectionMixin):
|
||||
threading.Thread(
|
||||
target=stop_test,
|
||||
daemon=True,
|
||||
name="vocalinux-test-dictation-stop",
|
||||
name="voculinux-test-dictation-stop",
|
||||
).start()
|
||||
|
||||
self._test_idle_wait_ticks = 0
|
||||
@@ -9526,7 +9526,7 @@ class SettingsDialog(AboutSectionMixin):
|
||||
threading.Thread(
|
||||
target=self._cancel_buffered_reload_then_restore,
|
||||
daemon=True,
|
||||
name="vocalinux-test-dictation-cancel",
|
||||
name="voculinux-test-dictation-cancel",
|
||||
).start()
|
||||
return False
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
"""
|
||||
Diarized transcript dialog for VocaLinux.
|
||||
Diarized transcript dialog for Voculinux.
|
||||
|
||||
Shows the speaker-attributed transcript produced by the "Transcribe audio
|
||||
file" flow (TinyDiarize tdrz output via diarization.py) with copy and
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
"""Recent-transcription history for Vocalinux.
|
||||
"""Recent-transcription history for Voculinux.
|
||||
|
||||
Keeps a bounded, newest-first list of recent dictation snippets so the user
|
||||
can review and re-copy recent voice input from the tray menu.
|
||||
@@ -11,7 +11,7 @@ in a file.
|
||||
|
||||
Users who want transcripts to survive restarts can opt in via
|
||||
``history.persist`` in config.json. Each snippet is then stored as one JSON
|
||||
object per line in a ``history.jsonl`` file under the Vocalinux data
|
||||
object per line in a ``history.jsonl`` file under the Voculinux data
|
||||
directory — timestamped, with the session's duration, language, and model
|
||||
recorded when the pipeline reported them — and the file is rewritten on
|
||||
every change so it always mirrors the in-memory list. Turning persistence
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
"""
|
||||
System tray indicator module for Vocalinux.
|
||||
System tray indicator module for Voculinux.
|
||||
|
||||
This module provides a system tray indicator for controlling the speech
|
||||
recognition process and displaying its status.
|
||||
@@ -158,7 +158,7 @@ _FALLBACK_KEYBOARD_RESTART_SECONDS = 6
|
||||
|
||||
class TrayIndicator:
|
||||
"""
|
||||
System tray indicator for Vocalinux.
|
||||
System tray indicator for Voculinux.
|
||||
|
||||
This class provides a system tray icon with a menu for controlling
|
||||
the speech recognition process.
|
||||
@@ -258,7 +258,7 @@ class TrayIndicator:
|
||||
|
||||
# Prefer absolute icon paths outside Flatpak. StatusNotifier hosts often
|
||||
# resolve IconName via the system hicolor theme and ignore IconThemePath,
|
||||
# so a stale ~/.local/share/icons copy of vocalinux-microphone-off.svg
|
||||
# so a stale ~/.local/share/icons copy of voculinux-microphone-off.svg
|
||||
# (the old red placeholder) would win over the AppImage's bundled icons.
|
||||
self._icon_keys = {
|
||||
"default": (self.icon_names["default"] if FLATPAK_ID else self.icon_paths["default"]),
|
||||
@@ -340,7 +340,7 @@ class TrayIndicator:
|
||||
self._setup_keyboard_shortcuts()
|
||||
|
||||
# Register the session-bus service so external triggers (e.g. a KDE
|
||||
# Plasma global shortcut running `vocalinux --toggle`) can control this
|
||||
# Plasma global shortcut running `voculinux --toggle`) can control this
|
||||
# running instance. Handlers marshal onto the GTK main thread.
|
||||
self._dbus_service = VocalinuxDBusService(
|
||||
on_toggle=self._toggle_recognition,
|
||||
@@ -379,7 +379,7 @@ class TrayIndicator:
|
||||
)
|
||||
notifications.notify(
|
||||
"External activation unavailable",
|
||||
"The D-Bus service could not start, so Vocalinux is using the "
|
||||
"The D-Bus service could not start, so Voculinux is using the "
|
||||
"internal keyboard shortcut instead. Check Settings -> Shortcuts.",
|
||||
"dialog-warning",
|
||||
)
|
||||
@@ -721,7 +721,7 @@ class TrayIndicator:
|
||||
text="System tray icon may not appear",
|
||||
)
|
||||
dialog.format_secondary_text(
|
||||
"Vocalinux could not detect AppIndicator support in your desktop environment.\n"
|
||||
"Voculinux could not detect AppIndicator support in your desktop environment.\n"
|
||||
"\n"
|
||||
"If you are using GNOME Shell, install the AppIndicator extension:\n"
|
||||
" sudo apt install gnome-shell-extension-appindicator\n"
|
||||
@@ -861,7 +861,7 @@ class TrayIndicator:
|
||||
"""Start recognition for an external (D-Bus) trigger.
|
||||
|
||||
Uses normal start semantics — not push-to-talk — so a single
|
||||
`vocalinux --start` transcribes immediately/with silence detection
|
||||
`voculinux --start` transcribes immediately/with silence detection
|
||||
rather than deferring until a Stop.
|
||||
"""
|
||||
with self._ptt_lock:
|
||||
@@ -1071,7 +1071,7 @@ class TrayIndicator:
|
||||
title = "Speech model downloaded"
|
||||
body = (
|
||||
f"{recommendation.display_name} is installed. "
|
||||
"Dictation will be available when Vocalinux resumes."
|
||||
"Dictation will be available when Voculinux resumes."
|
||||
)
|
||||
else:
|
||||
shortcut = self.config_manager.get_str(
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
"""
|
||||
Utility modules for Vocalinux.
|
||||
Utility modules for Voculinux.
|
||||
"""
|
||||
|
||||
from .resource_manager import ResourceManager
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
"""Faster-Whisper model information for Vocalinux.
|
||||
"""Faster-Whisper model information for Voculinux.
|
||||
|
||||
Metadata and checksum-gated download URLs for the optional faster-whisper
|
||||
backend (CTranslate2). Bundles are stored under ``models_dir()/faster_whisper/``
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
"""Environment for the host binaries Vocalinux shells out to.
|
||||
"""Environment for the host binaries Voculinux shells out to.
|
||||
|
||||
An AppImage puts its own libraries first on ``LD_LIBRARY_PATH``, and every child
|
||||
process inherits that. A host tool linked against a newer GLib than the bundle
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
"""Integrity verification for downloaded speech models.
|
||||
|
||||
Vocalinux pulls 40MB-2GB model files from Hugging Face, Alphacephei and
|
||||
Voculinux pulls 40MB-2GB model files from Hugging Face, Alphacephei and
|
||||
openaipublic.azureedge.net, then feeds them to native code -- whisper.cpp maps
|
||||
its ggml files through ``ctypes.cdll``. A tampered or truncated model is
|
||||
therefore not merely a bad transcription, so every download is checked against a
|
||||
@@ -16,7 +16,7 @@ pins nothing they could not change alongside it). Regenerate it with
|
||||
|
||||
OpenAI Whisper ``.pt`` checkpoints are absent from it: ``openai-whisper``
|
||||
downloads and verifies those itself, against the sha256 it reads out of the URL
|
||||
path, so Vocalinux neither fetches nor hashes them. ``install.sh`` does preinstall
|
||||
path, so Voculinux neither fetches nor hashes them. ``install.sh`` does preinstall
|
||||
the tiny checkpoint, and verifies it there with its own bash implementation.
|
||||
|
||||
Verification fails closed: a file with no pinned digest is an error, not a
|
||||
@@ -44,7 +44,7 @@ _MANIFEST_NAME = "model_checksums.txt"
|
||||
# came out of. A directory has no hash of its own and the zip is deleted after
|
||||
# unpacking, so this file is the only evidence the tree was ever verified.
|
||||
# install.sh reads it under the same name; keep the two in step.
|
||||
# (Name itself lives in vocalinux/app_identity.py so the rename flips it once.)
|
||||
# (Name itself lives in voculinux/app_identity.py so the rename flips it once.)
|
||||
|
||||
# Hash of a 2GB model read in one go would sit in RAM; stream it instead.
|
||||
_CHUNK_SIZE = 1024 * 1024
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Checksums for every speech model Vocalinux downloads.
|
||||
# Checksums for every speech model Voculinux downloads.
|
||||
#
|
||||
# Generated by scripts/generate-model-checksums.py -- do not edit by hand.
|
||||
# Regenerate with `just model-checksums` after adding a model to
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
"""
|
||||
Parakeet model information for Vocalinux.
|
||||
Parakeet model information for Voculinux.
|
||||
|
||||
This module provides model metadata and download URLs for the Parakeet
|
||||
engine, which runs NVIDIA NeMo ASR models through sherpa-onnx.
|
||||
|
||||
@@ -16,12 +16,12 @@ def xdg_data_home() -> str:
|
||||
|
||||
|
||||
def config_dir() -> str:
|
||||
"""Return the Vocalinux configuration directory."""
|
||||
"""Return the Voculinux configuration directory."""
|
||||
return os.path.join(xdg_config_home(), APP_DIR_NAME)
|
||||
|
||||
|
||||
def data_dir() -> str:
|
||||
"""Return the Vocalinux data directory."""
|
||||
"""Return the Voculinux data directory."""
|
||||
return os.path.join(xdg_data_home(), APP_DIR_NAME)
|
||||
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
"""Outbound-proxy configuration shared by Vocalinux network calls.
|
||||
"""Outbound-proxy configuration shared by Voculinux network calls.
|
||||
|
||||
Model downloads and update checks read the ``proxy`` section of config.json
|
||||
through this module so every outbound path honors the same setting:
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
"""
|
||||
Centralized resource manager for Vocalinux.
|
||||
Centralized resource manager for Voculinux.
|
||||
|
||||
This module provides a unified way to locate and access application resources
|
||||
like icons, sounds, and other assets regardless of how the application is installed.
|
||||
|
||||
@@ -17,6 +17,6 @@ SNAP_GPU_NOTE = (
|
||||
|
||||
|
||||
def running_in_snap(environ: Optional[Mapping[str, str]] = None) -> bool:
|
||||
"""Whether this process runs inside the Vocalinux snap."""
|
||||
"""Whether this process runs inside the Voculinux snap."""
|
||||
env = os.environ if environ is None else environ
|
||||
return bool(env.get("SNAP_NAME") or env.get("SNAP"))
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
"""Check GitHub Releases for newer Vocalinux versions."""
|
||||
"""Check GitHub Releases for newer Voculinux versions."""
|
||||
|
||||
import logging
|
||||
import re
|
||||
@@ -140,16 +140,16 @@ def is_update_available(
|
||||
|
||||
|
||||
# github.com/jatinkrmalik/vocalinux redirects here after the org transfer.
|
||||
_VOCALINUX_GITHUB_OWNERS = frozenset({"VocaHQ", "jatinkrmalik"})
|
||||
_VOCULINUX_GITHUB_OWNERS = frozenset({"VocaHQ", "jatinkrmalik"})
|
||||
|
||||
|
||||
def _trusted_path_prefixes(repo_url: str = __url__) -> tuple[str, ...]:
|
||||
"""Return GitHub path prefixes that belong to this project."""
|
||||
owner, repo = _repo_parts(repo_url)
|
||||
prefixes = [f"/{owner}/{repo}"]
|
||||
known_owners = {name.casefold() for name in _VOCALINUX_GITHUB_OWNERS}
|
||||
known_owners = {name.casefold() for name in _VOCULINUX_GITHUB_OWNERS}
|
||||
if repo.casefold() == "vocalinux" and owner.casefold() in known_owners:
|
||||
prefixes.extend(f"/{name}/vocalinux" for name in sorted(_VOCALINUX_GITHUB_OWNERS))
|
||||
prefixes.extend(f"/{name}/vocalinux" for name in sorted(_VOCULINUX_GITHUB_OWNERS))
|
||||
seen: set[str] = set()
|
||||
unique: list[str] = []
|
||||
for prefix in prefixes:
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
"""Background GitHub release checks for Vocalinux.
|
||||
"""Background GitHub release checks for Voculinux.
|
||||
|
||||
Schedules a delayed first check, then repeats on an interval. Networking runs on
|
||||
a worker thread; the result callback is delivered on the GLib main loop.
|
||||
@@ -132,7 +132,7 @@ class UpdateMonitor:
|
||||
target=self._worker,
|
||||
args=(channel, __version__, generation),
|
||||
daemon=True,
|
||||
name="vocalinux-update-check",
|
||||
name="voculinux-update-check",
|
||||
).start()
|
||||
|
||||
def _worker(self, channel: str, current: str, generation: int) -> None:
|
||||
|
||||
@@ -44,7 +44,7 @@ def whisper_model_file(model_size: str) -> str:
|
||||
def migrate_legacy_checkpoint_names(cache_dir: str) -> list[str]:
|
||||
"""Rename checkpoints saved under the old f"{size}.pt" name. Returns the new names.
|
||||
|
||||
Vocalinux's own downloader used to name every checkpoint after the catalog
|
||||
Voculinux's own downloader used to name every checkpoint after the catalog
|
||||
entry, which is wrong wherever upstream publishes a versioned file: "large"
|
||||
is stored as large-v3.pt. A file left under the old name is invisible three
|
||||
times over. load_model() looks for the upstream name and refetches 2.9GB,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
"""
|
||||
Whisper.cpp model information and hardware detection for Vocalinux.
|
||||
Whisper.cpp model information and hardware detection for Voculinux.
|
||||
|
||||
This module provides model metadata and hardware acceleration detection
|
||||
for whisper.cpp, supporting Vulkan, CUDA, and CPU backends.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
"""
|
||||
Version information for Vocalinux.
|
||||
Version information for Voculinux.
|
||||
"""
|
||||
|
||||
__version__ = "0.18.5"
|
||||
|
||||