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
This commit is contained in:
soubarnak
2026-11-06 01:29:39 +05:30
parent c565e6d946
commit ea7be5f111
251 changed files with 2449 additions and 1991 deletions
+3 -3
View File
@@ -1,6 +1,6 @@
--- ---
name: Bug Report name: Bug Report
about: Report a bug to help improve Vocalinux about: Report a bug to help improve Voculinux
title: "[BUG] " title: "[BUG] "
labels: bug labels: bug
assignees: '' assignees: ''
@@ -32,7 +32,7 @@ If applicable, attach screenshots.
- **OS / distro**: (e.g. Ubuntu 24.04) - **OS / distro**: (e.g. Ubuntu 24.04)
- **Python version**: (e.g. 3.12; 3.11+ required) - **Python version**: (e.g. 3.12; 3.11+ required)
- **Vocalinux version**: (`vocalinux --version`) - **Voculinux version**: (`voculinux --version`)
- **Install method**: (installer / AppImage / AUR / Flatpak / Snap / source / PyPI) - **Install method**: (installer / AppImage / AUR / Flatpak / Snap / source / PyPI)
- **Display server**: (X11 / Wayland) - **Display server**: (X11 / Wayland)
- **Desktop environment**: (e.g. GNOME, KDE Plasma) - **Desktop environment**: (e.g. GNOME, KDE Plasma)
@@ -41,7 +41,7 @@ If applicable, attach screenshots.
## Debug logs ## Debug logs
Run `vocalinux --debug` and paste the relevant output: Run `voculinux --debug` and paste the relevant output:
``` ```
[paste logs here] [paste logs here]
+2 -2
View File
@@ -1,6 +1,6 @@
--- ---
name: Feature Request name: Feature Request
about: Suggest an idea for Vocalinux about: Suggest an idea for Voculinux
title: "[FEATURE] " title: "[FEATURE] "
labels: enhancement labels: enhancement
assignees: '' assignees: ''
@@ -8,7 +8,7 @@ assignees: ''
## Feature description ## Feature description
What you would like Vocalinux to do. What you would like Voculinux to do.
## Problem ## Problem
+1 -1
View File
@@ -8,7 +8,7 @@ app:
- "src/**/*" - "src/**/*"
- "pyproject.toml" - "pyproject.toml"
- "MANIFEST.in" - "MANIFEST.in"
- "vocalinux.desktop" - "voculinux.desktop"
# Next.js website # Next.js website
web: web:
+1 -1
View File
@@ -41,7 +41,7 @@ jobs:
export GIT_SSH_COMMAND="ssh -i ~/.ssh/codeberg_mirror -o IdentitiesOnly=yes" export GIT_SSH_COMMAND="ssh -i ~/.ssh/codeberg_mirror -o IdentitiesOnly=yes"
git remote add codeberg git@codeberg.org:jatinkrmalik/vocalinux.git git remote add codeberg git@codeberg.org:jatinkrmalik/voculinux.git
git push codeberg --all git push codeberg --all
# Force tags so the mirror stays aligned with GitHub when a tag # Force tags so the mirror stays aligned with GitHub when a tag
# already exists on Codeberg (retagged release or prior partial sync). # already exists on Codeberg (retagged release or prior partial sync).
+2 -2
View File
@@ -11,7 +11,7 @@ on:
- 'uv.lock' - 'uv.lock'
- 'requirements/**' - 'requirements/**'
- 'src/vocalinux/version.py' - 'src/vocalinux/version.py'
- 'vocalinux.desktop' - 'voculinux.desktop'
- '.github/workflows/distro-test-matrix.yml' - '.github/workflows/distro-test-matrix.yml'
pull_request: pull_request:
branches: [ main, master ] branches: [ main, master ]
@@ -23,7 +23,7 @@ on:
- 'uv.lock' - 'uv.lock'
- 'requirements/**' - 'requirements/**'
- 'src/vocalinux/version.py' - 'src/vocalinux/version.py'
- 'vocalinux.desktop' - 'voculinux.desktop'
- '.github/workflows/distro-test-matrix.yml' - '.github/workflows/distro-test-matrix.yml'
workflow_dispatch: workflow_dispatch:
+3 -3
View File
@@ -71,8 +71,8 @@ jobs:
- name: Build and bundle - name: Build and bundle
uses: flatpak/flatpak-github-actions/flatpak-builder@79327416609af08178ad73b352877e51450790b3 # v6 uses: flatpak/flatpak-github-actions/flatpak-builder@79327416609af08178ad73b352877e51450790b3 # v6
with: with:
bundle: vocalinux-${{ matrix.arch }}.flatpak bundle: voculinux-${{ matrix.arch }}.flatpak
manifest-path: packaging/flatpak/com.vocalinux.Vocalinux.yml manifest-path: packaging/flatpak/com.voculinux.Voculinux.yml
# The action appends -${arch} and restore-keys is # The action appends -${arch} and restore-keys is
# `flatpak-builder-${arch}`, so the key has to start with that # `flatpak-builder-${arch}`, so the key has to start with that
# prefix. Putting the packaging hash first made every lock # prefix. Putting the packaging hash first made every lock
@@ -80,7 +80,7 @@ jobs:
# github.sha in the key forced a full rebuild every run. # github.sha in the key forced a full rebuild every run.
cache-key: flatpak-builder-${{ matrix.arch }}-${{ hashFiles('packaging/flatpak/**') }} cache-key: flatpak-builder-${{ matrix.arch }}-${{ hashFiles('packaging/flatpak/**') }}
arch: ${{ matrix.arch }} arch: ${{ matrix.arch }}
artifact-name: vocalinux-flatpak-${{ matrix.arch }} artifact-name: voculinux-flatpak-${{ matrix.arch }}
# Flathub mirrors screenshots at submission time; mirror locally so the # Flathub mirrors screenshots at submission time; mirror locally so the
# builddir linter does not flag the external screenshot URL. # builddir linter does not flag the external screenshot URL.
mirror-screenshots-url: https://dl.flathub.org/media mirror-screenshots-url: https://dl.flathub.org/media
+14 -14
View File
@@ -77,12 +77,12 @@ jobs:
- name: Cache AppImage build tools - name: Cache AppImage build tools
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with: with:
path: ~/.cache/vocalinux-appimage path: ~/.cache/voculinux-appimage
key: appimage-native-${{ runner.arch }}-${{ steps.native-cache.outputs.id }} key: appimage-native-${{ runner.arch }}-${{ steps.native-cache.outputs.id }}
- name: Build AppImage (x86_64) - name: Build AppImage (x86_64)
env: env:
VOCALINUX_APPIMAGE_REQUIRE_VULKAN: "1" VOCULINUX_APPIMAGE_REQUIRE_VULKAN: "1"
run: | run: |
bash packaging/appimage/docker-build.sh dist/*.whl "${{ steps.version.outputs.version }}" dist bash packaging/appimage/docker-build.sh dist/*.whl "${{ steps.version.outputs.version }}" dist
ls -la dist/*.AppImage ls -la dist/*.AppImage
@@ -159,13 +159,13 @@ jobs:
#### Quick Install (Recommended) #### Quick Install (Recommended)
Download the wheel file below and install: Download the wheel file below and install:
\`\`\`bash \`\`\`bash
pip install vocalinux-${{ steps.version.outputs.version }}-py3-none-any.whl pip install voculinux-${{ steps.version.outputs.version }}-py3-none-any.whl
\`\`\` \`\`\`
#### From PyPI (Stable) #### From PyPI (Stable)
For the latest stable release: For the latest stable release:
\`\`\`bash \`\`\`bash
pip install vocalinux pip install voculinux
\`\`\` \`\`\`
#### Using Install Script #### Using Install Script
@@ -176,8 +176,8 @@ jobs:
#### AppImage (no install, no root) #### AppImage (no install, no root)
Download the \`.AppImage\` matching your CPU (\`x86_64\` or \`aarch64\`) below: Download the \`.AppImage\` matching your CPU (\`x86_64\` or \`aarch64\`) below:
\`\`\`bash \`\`\`bash
chmod +x Vocalinux-${{ steps.version.outputs.version }}-x86_64.AppImage chmod +x Voculinux-${{ steps.version.outputs.version }}-x86_64.AppImage
./Vocalinux-${{ steps.version.outputs.version }}-x86_64.AppImage ./Voculinux-${{ steps.version.outputs.version }}-x86_64.AppImage
\`\`\` \`\`\`
Requires \`xdotool\`/\`wtype\`/\`ydotool\` on the host for text injection, same as the PyPI install path. Requires \`xdotool\`/\`wtype\`/\`ydotool\` on the host for text injection, same as the PyPI install path.
@@ -197,16 +197,16 @@ jobs:
### Files ### Files
- **\`vocalinux-${{ steps.version.outputs.version }}-py3-none-any.whl\`** - Python wheel (recommended) - **\`voculinux-${{ steps.version.outputs.version }}-py3-none-any.whl\`** - Python wheel (recommended)
- **\`vocalinux-${{ steps.version.outputs.version }}.tar.gz\`** - Source distribution - **\`voculinux-${{ steps.version.outputs.version }}.tar.gz\`** - Source distribution
- **\`Vocalinux-${{ steps.version.outputs.version }}-x86_64.AppImage\`** - Self-contained, no install (Intel/AMD) - **\`Voculinux-${{ steps.version.outputs.version }}-x86_64.AppImage\`** - Self-contained, no install (Intel/AMD)
- **\`Vocalinux-${{ steps.version.outputs.version }}-aarch64.AppImage\`** - Self-contained, no install (ARM64, added shortly after this release is published) - **\`Voculinux-${{ steps.version.outputs.version }}-aarch64.AppImage\`** - Self-contained, no install (ARM64, added shortly after this release is published)
--- ---
### Links ### Links
- [Documentation](https://vocalinux.com) - [Documentation](https://voculinux.com)
- [Report Issues](https://github.com/VocaHQ/vocalinux/issues) - [Report Issues](https://github.com/VocaHQ/vocalinux/issues)
- [Other Releases](https://github.com/VocaHQ/vocalinux/releases) - [Other Releases](https://github.com/VocaHQ/vocalinux/releases)
@@ -242,7 +242,7 @@ jobs:
- name: Upload artifacts (backup) - name: Upload artifacts (backup)
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2 uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
with: with:
name: vocalinux-nightly-${{ steps.version.outputs.version }} name: voculinux-nightly-${{ steps.version.outputs.version }}
path: dist/ path: dist/
retention-days: 7 retention-days: 7
@@ -296,12 +296,12 @@ jobs:
- name: Cache AppImage build tools - name: Cache AppImage build tools
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with: with:
path: ~/.cache/vocalinux-appimage path: ~/.cache/voculinux-appimage
key: appimage-native-${{ runner.arch }}-${{ steps.native-cache.outputs.id }} key: appimage-native-${{ runner.arch }}-${{ steps.native-cache.outputs.id }}
- name: Build AppImage (aarch64) - name: Build AppImage (aarch64)
env: env:
VOCALINUX_APPIMAGE_REQUIRE_VULKAN: "1" VOCULINUX_APPIMAGE_REQUIRE_VULKAN: "1"
run: | run: |
bash packaging/appimage/docker-build.sh dist/*.whl "${{ needs.nightly.outputs.version }}" dist bash packaging/appimage/docker-build.sh dist/*.whl "${{ needs.nightly.outputs.version }}" dist
ls -la dist/*.AppImage ls -la dist/*.AppImage
+54 -54
View File
@@ -15,11 +15,11 @@ env:
# Build against the supported floor (requires-python in pyproject.toml). # Build against the supported floor (requires-python in pyproject.toml).
PYTHON_VERSION: '3.11' PYTHON_VERSION: '3.11'
# The dedicated "tap" repo and the URL Pages serves it at. A custom # The dedicated "tap" repo and the URL Pages serves it at. A custom
# domain (e.g. flatpak.vocalinux.com) overrides both via the vars. # domain (e.g. flatpak.voculinux.com) overrides both via the vars.
FLATPAK_REMOTE_REPO: ${{ vars.FLATPAK_REMOTE_REPO || 'VocaHQ/vocalinux-flatpak' }} FLATPAK_REMOTE_REPO: ${{ vars.FLATPAK_REMOTE_REPO || 'VocaHQ/vocalinux-flatpak' }}
FLATPAK_REMOTE_URL: ${{ vars.FLATPAK_REMOTE_URL || 'https://vocahq.github.io/vocalinux-flatpak/repo' }} FLATPAK_REMOTE_URL: ${{ vars.FLATPAK_REMOTE_URL || 'https://vocahq.github.io/voculinux-flatpak/repo' }}
FLATPAKREPO_URL: ${{ vars.FLATPAKREPO_URL || 'https://vocahq.github.io/vocalinux-flatpak/vocahq.flatpakrepo' }} FLATPAKREPO_URL: ${{ vars.FLATPAKREPO_URL || 'https://vocahq.github.io/voculinux-flatpak/vocahq.flatpakrepo' }}
FLATPAKREF_URL: ${{ vars.FLATPAKREF_URL || 'https://vocahq.github.io/vocalinux-flatpak/com.vocalinux.Vocalinux.flatpakref' }} FLATPAKREF_URL: ${{ vars.FLATPAKREF_URL || 'https://vocahq.github.io/voculinux-flatpak/com.voculinux.Voculinux.flatpakref' }}
jobs: jobs:
build-and-release: build-and-release:
@@ -101,12 +101,12 @@ jobs:
- name: Cache AppImage build tools - name: Cache AppImage build tools
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with: with:
path: ~/.cache/vocalinux-appimage path: ~/.cache/voculinux-appimage
key: appimage-native-${{ runner.arch }}-${{ steps.native-cache.outputs.id }} key: appimage-native-${{ runner.arch }}-${{ steps.native-cache.outputs.id }}
- name: Build AppImage (x86_64) - name: Build AppImage (x86_64)
env: env:
VOCALINUX_APPIMAGE_REQUIRE_VULKAN: "1" VOCULINUX_APPIMAGE_REQUIRE_VULKAN: "1"
run: | run: |
bash packaging/appimage/docker-build.sh dist/*.whl "${{ steps.get_version.outputs.VERSION }}" dist bash packaging/appimage/docker-build.sh dist/*.whl "${{ steps.get_version.outputs.VERSION }}" dist
ls -la dist/*.AppImage ls -la dist/*.AppImage
@@ -151,7 +151,7 @@ jobs:
```bash ```bash
flatpak remote-add --if-not-exists flathub https://dl.flathub.org/repo/flathub.flatpakrepo flatpak remote-add --if-not-exists flathub https://dl.flathub.org/repo/flathub.flatpakrepo
flatpak remote-add --if-not-exists vocahq __FLATPAKREPO_URL__ flatpak remote-add --if-not-exists vocahq __FLATPAKREPO_URL__
flatpak install vocahq com.vocalinux.Vocalinux flatpak install vocahq com.voculinux.Voculinux
``` ```
Or download the `.flatpak` matching your CPU (`x86_64` or `aarch64`, attached Or download the `.flatpak` matching your CPU (`x86_64` or `aarch64`, attached
@@ -161,8 +161,8 @@ jobs:
flatpak remote-add --if-not-exists flathub https://dl.flathub.org/repo/flathub.flatpakrepo flatpak remote-add --if-not-exists flathub https://dl.flathub.org/repo/flathub.flatpakrepo
flatpak install flathub org.gnome.Platform//50 flatpak install flathub org.gnome.Platform//50
flatpak install --user ./Vocalinux-__VERSION__-x86_64.flatpak flatpak install --user ./Voculinux-__VERSION__-x86_64.flatpak
flatpak run com.vocalinux.Vocalinux flatpak run com.voculinux.Voculinux
``` ```
EOF EOF
else else
@@ -175,8 +175,8 @@ jobs:
flatpak remote-add --if-not-exists flathub https://dl.flathub.org/repo/flathub.flatpakrepo flatpak remote-add --if-not-exists flathub https://dl.flathub.org/repo/flathub.flatpakrepo
flatpak install flathub org.gnome.Platform//50 flatpak install flathub org.gnome.Platform//50
flatpak install --user ./Vocalinux-__VERSION__-x86_64.flatpak flatpak install --user ./Voculinux-__VERSION__-x86_64.flatpak
flatpak run com.vocalinux.Vocalinux flatpak run com.voculinux.Voculinux
``` ```
EOF EOF
if [ "$prerelease" = true ]; then if [ "$prerelease" = true ]; then
@@ -223,7 +223,7 @@ jobs:
### From Source ### From Source
```bash ```bash
git clone https://github.com/VocaHQ/vocalinux.git git clone https://github.com/VocaHQ/vocalinux.git
cd vocalinux cd voculinux
./install.sh ./install.sh
``` ```
@@ -231,10 +231,10 @@ jobs:
```bash ```bash
# Install the Linux system packages first: # Install the Linux system packages first:
# GTK/AppIndicator, PortAudio, and xdotool/wtype are required. # GTK/AppIndicator, PortAudio, and xdotool/wtype are required.
python3 -m venv ~/.local/share/vocalinux-pypi/venv --system-site-packages python3 -m venv ~/.local/share/voculinux-pypi/venv --system-site-packages
source ~/.local/share/vocalinux-pypi/venv/bin/activate source ~/.local/share/voculinux-pypi/venv/bin/activate
pip install vocalinux pip install voculinux
vocalinux voculinux
``` ```
PyPI installs the Python package only. For automatic system dependency, PyPI installs the Python package only. For automatic system dependency,
@@ -243,15 +243,15 @@ jobs:
### Arch Linux (AUR) ### Arch Linux (AUR)
```bash ```bash
yay -S vocalinux yay -S voculinux
``` ```
### AppImage (no install, no root) ### AppImage (no install, no root)
Download the `.AppImage` matching your CPU (`x86_64` or `aarch64`, the latter attached Download the `.AppImage` matching your CPU (`x86_64` or `aarch64`, the latter attached
shortly after this release is published) below: shortly after this release is published) below:
```bash ```bash
chmod +x Vocalinux-${{ steps.get_version.outputs.VERSION }}-x86_64.AppImage chmod +x Voculinux-${{ steps.get_version.outputs.VERSION }}-x86_64.AppImage
./Vocalinux-${{ steps.get_version.outputs.VERSION }}-x86_64.AppImage ./Voculinux-${{ steps.get_version.outputs.VERSION }}-x86_64.AppImage
``` ```
Still requires `xdotool`/`wtype`/`ydotool` on the host for text injection, same as the PyPI path. Still requires `xdotool`/`wtype`/`ydotool` on the host for text injection, same as the PyPI path.
@@ -260,10 +260,10 @@ jobs:
attached shortly after this release is published): attached shortly after this release is published):
```bash ```bash
# Debian / Ubuntu # Debian / Ubuntu
sudo apt install ./vocalinux_*_amd64.deb sudo apt install ./voculinux_*_amd64.deb
# Fedora # Fedora
sudo dnf install ./vocalinux-*.x86_64.rpm sudo dnf install ./voculinux-*.x86_64.rpm
``` ```
Thin packages: Python, GTK, PyGObject and the AppIndicator typelib come from the Thin packages: Python, GTK, PyGObject and the AppIndicator typelib come from the
distro, and `apt`/`dnf` resolves them. Only `pywhispercpp` and `pynput` are distro, and `apt`/`dnf` resolves them. Only `pywhispercpp` and `pynput` are
@@ -278,13 +278,13 @@ jobs:
${{ steps.flatpak_notes.outputs.flatpak }} ${{ steps.flatpak_notes.outputs.flatpak }}
### Snap ### Snap
Tagged releases also attach `vocalinux_${{ steps.get_version.outputs.VERSION }}_amd64.snap` Tagged releases also attach `voculinux_${{ steps.get_version.outputs.VERSION }}_amd64.snap`
and try the Snap Store (`edge` and `candidate`). `stable` is promoted and try the Snap Store (`edge` and `candidate`). `stable` is promoted
from candidate after QA by the `snap-promote.yml` dispatch workflow. from candidate after QA by the `snap-promote.yml` dispatch workflow.
Wayland typing runs through the RemoteDesktop portal; the snap ships no `uinput` plug. Wayland typing runs through the RemoteDesktop portal; the snap ships no `uinput` plug.
The GitHub `.snap` is also the sideload while a Store revision lags: The GitHub `.snap` is also the sideload while a Store revision lags:
```bash ```bash
sudo snap install --dangerous ./vocalinux_${{ steps.get_version.outputs.VERSION }}_amd64.snap sudo snap install --dangerous ./voculinux_${{ steps.get_version.outputs.VERSION }}_amd64.snap
``` ```
`--dangerous` is required because this file is not a Store revision. It will not `--dangerous` is required because this file is not a Store revision. It will not
refresh from the Store. amd64 only. refresh from the Store. amd64 only.
@@ -298,7 +298,7 @@ jobs:
``` ```
Each artifact also carries GitHub build provenance, which ties it to this workflow run: Each artifact also carries GitHub build provenance, which ties it to this workflow run:
```bash ```bash
gh attestation verify Vocalinux-${{ steps.get_version.outputs.VERSION }}-x86_64.AppImage \ gh attestation verify Voculinux-${{ steps.get_version.outputs.VERSION }}-x86_64.AppImage \
--repo ${{ github.repository }} --repo ${{ github.repository }}
``` ```
@@ -343,12 +343,12 @@ jobs:
- name: Cache AppImage build tools - name: Cache AppImage build tools
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with: with:
path: ~/.cache/vocalinux-appimage path: ~/.cache/voculinux-appimage
key: appimage-native-${{ runner.arch }}-${{ steps.native-cache.outputs.id }} key: appimage-native-${{ runner.arch }}-${{ steps.native-cache.outputs.id }}
- name: Build AppImage (aarch64) - name: Build AppImage (aarch64)
env: env:
VOCALINUX_APPIMAGE_REQUIRE_VULKAN: "1" VOCULINUX_APPIMAGE_REQUIRE_VULKAN: "1"
run: | run: |
bash packaging/appimage/docker-build.sh dist/*.whl "${{ steps.get_version.outputs.VERSION }}" dist bash packaging/appimage/docker-build.sh dist/*.whl "${{ steps.get_version.outputs.VERSION }}" dist
ls -la dist/*.AppImage ls -la dist/*.AppImage
@@ -492,8 +492,8 @@ jobs:
- name: Build and bundle - name: Build and bundle
uses: flatpak/flatpak-github-actions/flatpak-builder@79327416609af08178ad73b352877e51450790b3 # v6 uses: flatpak/flatpak-github-actions/flatpak-builder@79327416609af08178ad73b352877e51450790b3 # v6
with: with:
bundle: Vocalinux-${{ steps.get_version.outputs.VERSION }}-x86_64.flatpak bundle: Voculinux-${{ steps.get_version.outputs.VERSION }}-x86_64.flatpak
manifest-path: packaging/flatpak/com.vocalinux.Vocalinux.yml manifest-path: packaging/flatpak/com.voculinux.Voculinux.yml
# Same prefix rule as flatpak.yml: restore-keys is # Same prefix rule as flatpak.yml: restore-keys is
# `flatpak-builder-${arch}`. The action appends -${arch} again. # `flatpak-builder-${arch}`. The action appends -${arch} again.
cache-key: flatpak-builder-x86_64-${{ hashFiles('packaging/flatpak/**') }} cache-key: flatpak-builder-x86_64-${{ hashFiles('packaging/flatpak/**') }}
@@ -506,7 +506,7 @@ jobs:
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2 uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
with: with:
name: flatpak-x86_64 name: flatpak-x86_64
path: Vocalinux-${{ steps.get_version.outputs.VERSION }}-x86_64.flatpak path: Voculinux-${{ steps.get_version.outputs.VERSION }}-x86_64.flatpak
if-no-files-found: error if-no-files-found: error
retention-days: 7 retention-days: 7
@@ -529,8 +529,8 @@ jobs:
- name: Build and bundle - name: Build and bundle
uses: flatpak/flatpak-github-actions/flatpak-builder@79327416609af08178ad73b352877e51450790b3 # v6 uses: flatpak/flatpak-github-actions/flatpak-builder@79327416609af08178ad73b352877e51450790b3 # v6
with: with:
bundle: Vocalinux-${{ steps.get_version.outputs.VERSION }}-aarch64.flatpak bundle: Voculinux-${{ steps.get_version.outputs.VERSION }}-aarch64.flatpak
manifest-path: packaging/flatpak/com.vocalinux.Vocalinux.yml manifest-path: packaging/flatpak/com.voculinux.Voculinux.yml
cache-key: flatpak-builder-aarch64-${{ hashFiles('packaging/flatpak/**') }} cache-key: flatpak-builder-aarch64-${{ hashFiles('packaging/flatpak/**') }}
arch: aarch64 arch: aarch64
upload-artifact: false upload-artifact: false
@@ -540,7 +540,7 @@ jobs:
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2 uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
with: with:
name: flatpak-aarch64 name: flatpak-aarch64
path: Vocalinux-${{ steps.get_version.outputs.VERSION }}-aarch64.flatpak path: Voculinux-${{ steps.get_version.outputs.VERSION }}-aarch64.flatpak
if-no-files-found: error if-no-files-found: error
retention-days: 7 retention-days: 7
@@ -715,27 +715,27 @@ jobs:
echo '[Flatpak Repo]' echo '[Flatpak Repo]'
echo 'Title=VocaHQ' echo 'Title=VocaHQ'
echo "Url=${FLATPAK_REMOTE_URL}" echo "Url=${FLATPAK_REMOTE_URL}"
echo 'Homepage=https://vocalinux.com' echo 'Homepage=https://voculinux.com'
echo 'Comment=Official Vocalinux Flatpak remote' echo 'Comment=Official Voculinux Flatpak remote'
echo 'Description=Self-hosted Vocalinux Flatpak repository' echo 'Description=Self-hosted Voculinux Flatpak repository'
echo 'Icon=https://vocalinux.com/icon-512x512.png' echo 'Icon=https://voculinux.com/icon-512x512.png'
echo 'DefaultBranch=stable' echo 'DefaultBranch=stable'
echo "GPGKey=$(base64 -w0 "$RUNNER_TEMP/pubkey.gpg")" echo "GPGKey=$(base64 -w0 "$RUNNER_TEMP/pubkey.gpg")"
} > publish/vocahq.flatpakrepo } > publish/vocahq.flatpakrepo
{ {
echo '[Flatpak Ref]' echo '[Flatpak Ref]'
echo 'Version=1' echo 'Version=1'
echo 'Title=Vocalinux' echo 'Title=Voculinux'
echo 'Name=com.vocalinux.Vocalinux' echo 'Name=com.voculinux.Voculinux'
echo 'Branch=stable' echo 'Branch=stable'
echo "Url=${FLATPAK_REMOTE_URL}" echo "Url=${FLATPAK_REMOTE_URL}"
echo 'SuggestRemoteName=vocahq' echo 'SuggestRemoteName=vocahq'
echo 'RuntimeRepo=https://dl.flathub.org/repo/flathub.flatpakrepo' echo 'RuntimeRepo=https://dl.flathub.org/repo/flathub.flatpakrepo'
echo 'IsRuntime=false' echo 'IsRuntime=false'
echo 'Homepage=https://vocalinux.com' echo 'Homepage=https://voculinux.com'
echo 'Icon=https://vocalinux.com/icon-512x512.png' echo 'Icon=https://voculinux.com/icon-512x512.png'
echo "GPGKey=$(base64 -w0 "$RUNNER_TEMP/pubkey.gpg")" echo "GPGKey=$(base64 -w0 "$RUNNER_TEMP/pubkey.gpg")"
} > publish/com.vocalinux.Vocalinux.flatpakref } > publish/com.voculinux.Voculinux.flatpakref
cp "$RUNNER_TEMP/pubkey.gpg" publish/vocahq.gpg cp "$RUNNER_TEMP/pubkey.gpg" publish/vocahq.gpg
grep -v GPGKey publish/vocahq.flatpakrepo grep -v GPGKey publish/vocahq.flatpakrepo
sed -e "s|__FLATPAKREPO_URL__|${FLATPAKREPO_URL}|g" \ sed -e "s|__FLATPAKREPO_URL__|${FLATPAKREPO_URL}|g" \
@@ -751,7 +751,7 @@ jobs:
external_repository: ${{ env.FLATPAK_REMOTE_REPO }} external_repository: ${{ env.FLATPAK_REMOTE_REPO }}
publish_dir: ./publish publish_dir: ./publish
publish_branch: gh-pages publish_branch: gh-pages
commit_message: 'Vocalinux ${{ github.ref_name }}' commit_message: 'Voculinux ${{ github.ref_name }}'
attach-flatpak: attach-flatpak:
name: Attach Flatpak bundles name: Attach Flatpak bundles
@@ -854,7 +854,7 @@ jobs:
id-token: write # PyPI trusted publishing; there is no API token to leak id-token: write # PyPI trusted publishing; there is no API token to leak
environment: environment:
name: pypi name: pypi
url: https://pypi.org/project/vocalinux/ url: https://pypi.org/project/voculinux/
steps: steps:
# Not a rebuild: the artifact attached to the GitHub release, so one # Not a rebuild: the artifact attached to the GitHub release, so one
# checksum describes both. # checksum describes both.
@@ -892,14 +892,14 @@ jobs:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: | run: |
gh release download "$GITHUB_REF_NAME" --repo "$GITHUB_REPOSITORY" \ gh release download "$GITHUB_REF_NAME" --repo "$GITHUB_REPOSITORY" \
--pattern 'vocalinux-*.tar.gz' --pattern SHA256SUMS --dir release-source --pattern 'voculinux-*.tar.gz' --pattern SHA256SUMS --dir release-source
- name: Verify provenance and prepare the exact source PKGBUILD - name: Verify provenance and prepare the exact source PKGBUILD
env: env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: | run: |
uv run --python "$(command -v python)" --locked --only-group release-build \ uv run --python "$(command -v python)" --locked --only-group release-build \
python scripts/prepare_aur_source.py \ python scripts/prepare_aur_source.py \
--sdist release-source/vocalinux-*.tar.gz --manifest release-source/SHA256SUMS \ --sdist release-source/voculinux-*.tar.gz --manifest release-source/SHA256SUMS \
--tag "$GITHUB_REF_NAME" --source-commit "$GITHUB_SHA" --output build/aur-source --tag "$GITHUB_REF_NAME" --source-commit "$GITHUB_SHA" --output build/aur-source
- name: Build and install the exact source PKGBUILD on Arch - name: Build and install the exact source PKGBUILD on Arch
run: | run: |
@@ -944,7 +944,7 @@ jobs:
if: steps.aur.outputs.configured == 'true' if: steps.aur.outputs.configured == 'true'
uses: KSXGitHub/github-actions-deploy-aur@084b0d9b15415bf9cdb65d44dad1efe37a354050 # v4.2.0 uses: KSXGitHub/github-actions-deploy-aur@084b0d9b15415bf9cdb65d44dad1efe37a354050 # v4.2.0
with: with:
pkgname: vocalinux pkgname: voculinux
pkgbuild: aur-source/PKGBUILD pkgbuild: aur-source/PKGBUILD
updpkgsums: 'false' updpkgsums: 'false'
commit_username: ${{ secrets.AUR_USERNAME }} commit_username: ${{ secrets.AUR_USERNAME }}
@@ -985,7 +985,7 @@ jobs:
run: | run: |
set -euo pipefail set -euo pipefail
TAG="${GITHUB_REF#refs/tags/v}" TAG="${GITHUB_REF#refs/tags/v}"
PKGBUILD=packaging/aur/vocalinux-bin/PKGBUILD PKGBUILD=packaging/aur/voculinux-bin/PKGBUILD
sed -i "s/^pkgver=.*/pkgver=${TAG//-/}/; s/^_tag=.*/_tag=${TAG}/; s/^pkgrel=.*/pkgrel=1/" "$PKGBUILD" sed -i "s/^pkgver=.*/pkgver=${TAG//-/}/; s/^_tag=.*/_tag=${TAG}/; s/^pkgrel=.*/pkgrel=1/" "$PKGBUILD"
grep -E '^(pkgver|_tag|pkgrel)=' "$PKGBUILD" grep -E '^(pkgver|_tag|pkgrel)=' "$PKGBUILD"
@@ -998,21 +998,21 @@ jobs:
set -euo pipefail set -euo pipefail
tag="${GITHUB_REF_NAME#v}" tag="${GITHUB_REF_NAME#v}"
for arch in x86_64 aarch64; do for arch in x86_64 aarch64; do
url="https://github.com/VocaHQ/vocalinux/releases/download/${GITHUB_REF_NAME}/Vocalinux-${tag}-${arch}.AppImage" url="https://github.com/VocaHQ/vocalinux/releases/download/${GITHUB_REF_NAME}/Voculinux-${tag}-${arch}.AppImage"
sum="$(curl -fsSL "$url" | sha256sum | cut -d' ' -f1)" sum="$(curl -fsSL "$url" | sha256sum | cut -d' ' -f1)"
sed -i "s/^sha256sums_${arch}=('[0-9a-f]*')/sha256sums_${arch}=('${sum}')/" \ sed -i "s/^sha256sums_${arch}=('[0-9a-f]*')/sha256sums_${arch}=('${sum}')/" \
packaging/aur/vocalinux-bin/PKGBUILD packaging/aur/voculinux-bin/PKGBUILD
done done
- name: Publish AUR -bin package - name: Publish AUR -bin package
if: steps.aur.outputs.configured == 'true' if: steps.aur.outputs.configured == 'true'
uses: KSXGitHub/github-actions-deploy-aur@084b0d9b15415bf9cdb65d44dad1efe37a354050 # v4.2.0 uses: KSXGitHub/github-actions-deploy-aur@084b0d9b15415bf9cdb65d44dad1efe37a354050 # v4.2.0
with: with:
pkgname: vocalinux-bin pkgname: voculinux-bin
# The -bin PKGBUILD names local sources (vocalinux.desktop, # The -bin PKGBUILD names local sources (voculinux.desktop,
# vocalinux.svg, LICENSE); publishing the whole directory mirrors # voculinux.svg, LICENSE); publishing the whole directory mirrors
# them into the AUR repo instead of shipping a lone PKGBUILD. # them into the AUR repo instead of shipping a lone PKGBUILD.
asset_dir: packaging/aur/vocalinux-bin asset_dir: packaging/aur/voculinux-bin
# Sums are pinned in the step above for BOTH arches; regenerating # Sums are pinned in the step above for BOTH arches; regenerating
# here would only refresh the x86_64 array. # here would only refresh the x86_64 array.
updpkgsums: 'false' updpkgsums: 'false'
@@ -1195,5 +1195,5 @@ jobs:
github_token: ${{ secrets.GITHUB_TOKEN }} github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: ./web/out publish_dir: ./web/out
publish_branch: gh-pages publish_branch: gh-pages
cname: vocalinux.com cname: voculinux.com
force_orphan: true force_orphan: true
+4 -4
View File
@@ -77,7 +77,7 @@ jobs:
run: | run: |
set -euo pipefail set -euo pipefail
VERSION="${TAG#v}" VERSION="${TAG#v}"
/snap/bin/snapcraft status vocalinux | tee /tmp/snap-status.txt /snap/bin/snapcraft status voculinux | tee /tmp/snap-status.txt
# POSIX: mawk has no \S — use a literal non-space class. # POSIX: mawk has no \S — use a literal non-space class.
row=$(awk '/^[^ \t]/ { track = $1 } row=$(awk '/^[^ \t]/ { track = $1 }
track == "latest" && $1 == "candidate" { print $2, $3; exit } track == "latest" && $1 == "candidate" { print $2, $3; exit }
@@ -110,7 +110,7 @@ jobs:
row=$(awk '/^[^ \t]/ { track = $1 } row=$(awk '/^[^ \t]/ { track = $1 }
track == "latest" && $1 == "candidate" { print $2, $3; exit } track == "latest" && $1 == "candidate" { print $2, $3; exit }
track == "latest" && $3 == "candidate" { print $4, $5; exit }' \ track == "latest" && $3 == "candidate" { print $4, $5; exit }' \
<(/snap/bin/snapcraft status vocalinux)) <(/snap/bin/snapcraft status voculinux))
read -r seen_version revision <<<"$row" read -r seen_version revision <<<"$row"
if [ "${revision:-}" != "$REVISION" ] || [ "$seen_version" != "$VERSION" ]; then if [ "${revision:-}" != "$REVISION" ] || [ "$seen_version" != "$VERSION" ]; then
echo "latest/candidate moved on (now version=${seen_version:-none} revision=${revision:-none}); aborting instead of promoting the stale revision" >&2 echo "latest/candidate moved on (now version=${seen_version:-none} revision=${revision:-none}); aborting instead of promoting the stale revision" >&2
@@ -123,5 +123,5 @@ jobs:
REVISION: ${{ steps.candidate.outputs.revision }} REVISION: ${{ steps.candidate.outputs.revision }}
run: | run: |
set -euo pipefail set -euo pipefail
/snap/bin/snapcraft release vocalinux "$REVISION" latest/stable /snap/bin/snapcraft release voculinux "$REVISION" latest/stable
/snap/bin/snapcraft status vocalinux /snap/bin/snapcraft status voculinux
+11 -11
View File
@@ -1,4 +1,4 @@
name: Vocalinux CI name: Voculinux CI
on: on:
push: push:
@@ -51,7 +51,7 @@ jobs:
# entry and the icon into the AppDir. # entry and the icon into the AppDir.
- 'install.sh' - 'install.sh'
- 'install.d/**' - 'install.d/**'
- 'vocalinux.desktop' - 'voculinux.desktop'
- 'resources/**' - 'resources/**'
- 'justfile' - 'justfile'
# scripts/ is guarded by tests/ and one of them is executed by the # scripts/ is guarded by tests/ and one of them is executed by the
@@ -65,7 +65,7 @@ jobs:
- '.pre-commit-config.yaml' - '.pre-commit-config.yaml'
- '.github/workflows/unified-pipeline.yml' - '.github/workflows/unified-pipeline.yml'
- '.github/workflows/release.yml' - '.github/workflows/release.yml'
# The AUR gate builds packaging/aur/vocalinux/PKGBUILD from this # The AUR gate builds packaging/aur/voculinux/PKGBUILD from this
# tree. Its depends= mirror pyproject.toml, so every python change # tree. Its depends= mirror pyproject.toml, so every python change
# reaches the gate through the python filter above; this filter # reaches the gate through the python filter above; this filter
# exists so a PKGBUILD-only change runs the gate without dragging # exists so a PKGBUILD-only change runs the gate without dragging
@@ -350,12 +350,12 @@ jobs:
- name: Cache AppImage build tools - name: Cache AppImage build tools
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with: with:
path: ~/.cache/vocalinux-appimage path: ~/.cache/voculinux-appimage
key: appimage-native-${{ runner.arch }}-${{ steps.native-cache.outputs.id }} key: appimage-native-${{ runner.arch }}-${{ steps.native-cache.outputs.id }}
- name: Build AppImage - name: Build AppImage
env: env:
VOCALINUX_APPIMAGE_REQUIRE_VULKAN: "1" VOCULINUX_APPIMAGE_REQUIRE_VULKAN: "1"
run: | run: |
VERSION=$(grep -oP '__version__\s*=\s*"\K[^"]+' src/vocalinux/version.py) VERSION=$(grep -oP '__version__\s*=\s*"\K[^"]+' src/vocalinux/version.py)
WHEEL=$(ls dist/*.whl) WHEEL=$(ls dist/*.whl)
@@ -365,7 +365,7 @@ jobs:
- name: Upload AppImage artifact - name: Upload AppImage artifact
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2 uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
with: with:
name: vocalinux-appimage-${{ matrix.arch }} name: voculinux-appimage-${{ matrix.arch }}
path: dist/*.AppImage path: dist/*.AppImage
retention-days: 7 retention-days: 7
@@ -394,7 +394,7 @@ jobs:
- name: Download the AppImage built above - name: Download the AppImage built above
uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2 uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
with: with:
name: vocalinux-appimage-x86_64 name: voculinux-appimage-x86_64
path: dist path: dist
- name: Boot it on ${{ matrix.distro }} - name: Boot it on ${{ matrix.distro }}
@@ -434,7 +434,7 @@ jobs:
run: | run: |
uv run --python "$(command -v python)" --locked --only-group release-build \ uv run --python "$(command -v python)" --locked --only-group release-build \
python scripts/prepare_aur_source.py \ python scripts/prepare_aur_source.py \
--local-unattested --sdist dist/vocalinux-*.tar.gz --output build/aur-source --local-unattested --sdist dist/voculinux-*.tar.gz --output build/aur-source
- name: Build the PKGBUILD on Arch - name: Build the PKGBUILD on Arch
run: | run: |
@@ -445,7 +445,7 @@ jobs:
# The .deb and .rpm exist to be installed and run, so the gate does exactly # The .deb and .rpm exist to be installed and run, so the gate does exactly
# that: build both in the pinned Debian 12 image release.yml uses, then # that: build both in the pinned Debian 12 image release.yml uses, then
# install them in distro containers and run vocalinux --version plus an # install them in distro containers and run voculinux --version plus an
# import smoke. debian:12 and ubuntu:24.04 exercise the .deb's dependency # import smoke. debian:12 and ubuntu:24.04 exercise the .deb's dependency
# list (and the vendored cp311/cp312 wheels), fedora:42 the .rpm's. # list (and the vendored cp311/cp312 wheels), fedora:42 the .rpm's.
native-build: native-build:
@@ -535,7 +535,7 @@ jobs:
- name: Upload the verified snap - name: Upload the verified snap
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2 uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
with: with:
name: vocalinux-snap-amd64 name: voculinux-snap-amd64
path: ${{ steps.build.outputs.snap }} path: ${{ steps.build.outputs.snap }}
if-no-files-found: error if-no-files-found: error
retention-days: 7 retention-days: 7
@@ -628,5 +628,5 @@ jobs:
github_token: ${{ secrets.GITHUB_TOKEN }} github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: ./web/out publish_dir: ./web/out
publish_branch: gh-pages publish_branch: gh-pages
cname: vocalinux.com cname: voculinux.com
force_orphan: true force_orphan: true
+1 -1
View File
@@ -24,7 +24,7 @@ env/
ENV/ ENV/
.env .env
.venv/ .venv/
activate-vocalinux.sh activate-voculinux.sh
# Local development settings # Local development settings
.vscode/ .vscode/
+1 -1
View File
@@ -23,7 +23,7 @@ ENV UV_PYTHON_INSTALL_DIR=/opt/uv-python \
UV_LINK_MODE=copy UV_LINK_MODE=copy
RUN uv sync --locked --extra dev --extra vad RUN uv sync --locked --extra dev --extra vad
# Make the project venv the default interpreter, so `python`, `pytest` and `vocalinux` work offline. # Make the project venv the default interpreter, so `python`, `pytest` and `voculinux` work offline.
ENV VIRTUAL_ENV=/src/.venv \ ENV VIRTUAL_ENV=/src/.venv \
PATH=/src/.venv/bin:$PATH \ PATH=/src/.venv/bin:$PATH \
PYTHONPATH=/src \ PYTHONPATH=/src \
+4 -4
View File
@@ -1,6 +1,6 @@
# Threat model # Threat model
Vocalinux is a desktop voice-dictation app for Linux (X11 and Wayland). It records the Voculinux is a desktop voice-dictation app for Linux (X11 and Wayland). It records the
microphone, runs speech recognition, and types the result into whichever window has focus. microphone, runs speech recognition, and types the result into whichever window has focus.
It runs as the logged-in user, never as root, and has no server of its own. Python It runs as the logged-in user, never as root, and has no server of its own. Python
sources are under `src/vocalinux/`; the tests are under `tests/`. sources are under `src/vocalinux/`; the tests are under `tests/`.
@@ -26,9 +26,9 @@ has the user's privileges. Input from these places is untrusted:
(`docs/HTTP_REMOTE.md`), audio goes to a user-configured OpenAI-compatible or (`docs/HTTP_REMOTE.md`), audio goes to a user-configured OpenAI-compatible or
whisper.cpp HTTP endpoint, and the JSON response is parsed and *typed into the whisper.cpp HTTP endpoint, and the JSON response is parsed and *typed into the
focused window*. Treat the server as hostile. It may return oversized, malformed, focused window*. Treat the server as hostile. It may return oversized, malformed,
or control-character-laden responses, try to make Vocalinux type key sequences, or control-character-laden responses, try to make Voculinux type key sequences,
shell commands or terminal escapes, or redirect requests elsewhere. shell commands or terminal escapes, or redirect requests elsewhere.
4. **Local VocaGateway embed** (`gateway_embed/`). Vocalinux can start a 4. **Local VocaGateway embed** (`gateway_embed/`). Voculinux can start a
[VocaGateway](https://github.com/VocaHQ/vocagateway) container via podman or docker [VocaGateway](https://github.com/VocaHQ/vocagateway) container via podman or docker
compose. It writes the compose `.env` (`runner.py`, with `_IMAGE_RE` guarding image compose. It writes the compose `.env` (`runner.py`, with `_IMAGE_RE` guarding image
refs) and fetches pairing JSON and QR SVG from the gateway over HTTP (`pairing.py`, refs) and fetches pairing JSON and QR SVG from the gateway over HTTP (`pairing.py`,
@@ -66,7 +66,7 @@ has the user's privileges. Input from these places is untrusted:
- Out of scope: `web/` (the marketing website), `docs/`, `packaging/` (AppImage, - Out of scope: `web/` (the marketing website), `docs/`, `packaging/` (AppImage,
Flatpak, AUR, Snap recipes), `snap/`, `.github/` workflows, and bugs that live Flatpak, AUR, Snap recipes), `snap/`, `.github/` workflows, and bugs that live
in third-party engines (whisper.cpp, VOSK, sherpa-onnx, onnxruntime, torch) in third-party engines (whisper.cpp, VOSK, sherpa-onnx, onnxruntime, torch)
themselves. Report those upstream, unless Vocalinux calls them unsafely. themselves. Report those upstream, unless Voculinux calls them unsafely.
## How to exercise it ## How to exercise it
- `python -m pytest -q tests/` runs the full suite offline. Hardware and display tests - `python -m pytest -q tests/` runs the full suite offline. Hardware and display tests
+1 -1
View File
@@ -1,4 +1,4 @@
# Pre-commit hooks for Vocalinux # Pre-commit hooks for Voculinux
# Run: pre-commit run --all-files # Run: pre-commit run --all-files
repos: repos:
+11 -11
View File
@@ -1,4 +1,4 @@
# AGENTS.md — Vocalinux # AGENTS.md — Voculinux
Voice dictation for Linux: GTK 3 tray app (Python) plus a Next.js marketing site in `web/`. Default speech engine is **whisper.cpp** (`pywhispercpp`); OpenAI Whisper, Vosk, and a user-configured remote API are optional. Do not invent features, user counts, or privacy claims. Voice dictation for Linux: GTK 3 tray app (Python) plus a Next.js marketing site in `web/`. Default speech engine is **whisper.cpp** (`pywhispercpp`); OpenAI Whisper, Vosk, and a user-configured remote API are optional. Do not invent features, user counts, or privacy claims.
@@ -13,18 +13,18 @@ Never create a branch, commit, or open a pull request in the primary checkout. A
```bash ```bash
git fetch origin git fetch origin
git worktree add /tmp/vocalinux-<task> -b <type>/<short-name> origin/main git worktree add /tmp/voculinux-<task> -b <type>/<short-name> origin/main
# All edits, commits, and `gh pr create` happen inside that worktree. # All edits, commits, and `gh pr create` happen inside that worktree.
git worktree remove /tmp/vocalinux-<task> git worktree remove /tmp/voculinux-<task>
git worktree prune git worktree prune
``` ```
Rules: Rules:
- One worktree per branch, one branch per PR - One worktree per branch, one branch per PR
- Place worktrees **outside** the primary working tree (`/tmp/vocalinux-<task>` or a sibling directory such as `../.worktrees/vocalinux-<task>`) - Place worktrees **outside** the primary working tree (`/tmp/voculinux-<task>` or a sibling directory such as `../.worktrees/voculinux-<task>`)
- Never run two tasks in the same worktree - Never run two tasks in the same worktree
- Never commit directly to `main` - Never commit directly to `main`
- Clean up the worktree after the PR is pushed - Clean up the worktree after the PR is pushed
@@ -97,7 +97,7 @@ just remote-install-gate debian:12 # remote curl|bash path end to end against H
just verify-release # check a published release as published (needs gh) just verify-release # check a published release as published (needs gh)
just reproducible-wheel # independent locked wheel builds; stages verified dist/ (needs uv) just reproducible-wheel # independent locked wheel builds; stages verified dist/ (needs uv)
just pre-commit # pre-commit run --all-files just pre-commit # pre-commit run --all-files
just run-debug # vocalinux --debug just run-debug # voculinux --debug
just run-source-debug just run-source-debug
``` ```
@@ -127,7 +127,7 @@ Use **uv 0.13.3** for `just lock` and `just export-check`, matching CI. Other ve
| pywhispercpp | Pinned in `install.sh` as `PYWHISPERCPP_VERSION` (keep in sync with `uv.lock`) | | pywhispercpp | Pinned in `install.sh` as `PYWHISPERCPP_VERSION` (keep in sync with `uv.lock`) |
| Installer build tools | `[dependency-groups].installer-build` exports to `requirements/installer-build.txt`. Bootstrap uses hashed wheels on new and reused venvs; source builds use these tools without build isolation. Its setuptools `>=83` floor keeps the pinned installer tools patched; do not add it to `[build-system]` (Arch). Backend rebuilds select pywhispercpp's complete hash block from `runtime.txt` via `scripts/installer_requirements.py`, with `--no-deps` so they cannot replace the runtime | | Installer build tools | `[dependency-groups].installer-build` exports to `requirements/installer-build.txt`. Bootstrap uses hashed wheels on new and reused venvs; source builds use these tools without build isolation. Its setuptools `>=83` floor keeps the pinned installer tools patched; do not add it to `[build-system]` (Arch). Backend rebuilds select pywhispercpp's complete hash block from `runtime.txt` via `scripts/installer_requirements.py`, with `--no-deps` so they cannot replace the runtime |
| Release build tools | `[dependency-groups].release-build` exports to `requirements/release-build.txt`. `scripts/build_reproducible_wheel.py` builds in two fresh source trees/venvs with hashed tools and `--no-isolation`, compares wheel bytes, and stages only canonical distributions in `dist/`. These build-only venvs exclude system site-packages: host setuptools plugins must not influence locked builds, and packaging does not import GTK. Repeat runs clean recognized diagnostics and staged archives matching retained canonical digests; staged copies are deleted first so interrupted cleanup remains retryable. Unrelated/modified files are preserved and block cleanup. Never wipe all of `build/`. PR and release CI use Python 3.11 and pinned uv. Failed build outputs/logs stay under `build/reproducible-wheel/`, excluded from release artifacts | | Release build tools | `[dependency-groups].release-build` exports to `requirements/release-build.txt`. `scripts/build_reproducible_wheel.py` builds in two fresh source trees/venvs with hashed tools and `--no-isolation`, compares wheel bytes, and stages only canonical distributions in `dist/`. These build-only venvs exclude system site-packages: host setuptools plugins must not influence locked builds, and packaging does not import GTK. Repeat runs clean recognized diagnostics and staged archives matching retained canonical digests; staged copies are deleted first so interrupted cleanup remains retryable. Unrelated/modified files are preserved and block cleanup. Never wipe all of `build/`. PR and release CI use Python 3.11 and pinned uv. Failed build outputs/logs stay under `build/reproducible-wheel/`, excluded from release artifacts |
| AUR source publication | Use the canonical release sdist, not a GitHub tag archive. `scripts/prepare_aur_source.py` (run with the locked `release-build` group) requires its published checksum and provenance from the exact release workflow/tag/commit before preparing a pinned PKGBUILD. Release CI builds/installs that unchanged recipe on current Arch; publication consumes only the gated artifact with `updpkgsums: false`. PR/local fixtures must explicitly use `--local-unattested` and are never publication inputs. `just aur-gate` keeps distributions, diagnostics and tooling in its own temporary directory, leaving shared packaging outputs and dev environments untouched; failures retain that directory and print its path. `vocalinux-bin` publication depends only on release creation and both AppImages, not the source gate or global checksums. Existing AUR provider stubs/pinned import smoke remain limited as documented in `docs/AUR.md` | | AUR source publication | Use the canonical release sdist, not a GitHub tag archive. `scripts/prepare_aur_source.py` (run with the locked `release-build` group) requires its published checksum and provenance from the exact release workflow/tag/commit before preparing a pinned PKGBUILD. Release CI builds/installs that unchanged recipe on current Arch; publication consumes only the gated artifact with `updpkgsums: false`. PR/local fixtures must explicitly use `--local-unattested` and are never publication inputs. `just aur-gate` keeps distributions, diagnostics and tooling in its own temporary directory, leaving shared packaging outputs and dev environments untouched; failures retain that directory and print its path. `voculinux-bin` publication depends only on release creation and both AppImages, not the source gate or global checksums. Existing AUR provider stubs/pinned import smoke remain limited as documented in `docs/AUR.md` |
| Installer Whisper export | Compiled universally from Python 3.11, constrained by `runtime.txt` and `installer-build.txt` to keep shared pins consistent. Uses a `+cpu` torch pin and best-match index resolution so the CPU index's older copies of shared packages do not downgrade the runtime | | Installer Whisper export | Compiled universally from Python 3.11, constrained by `runtime.txt` and `installer-build.txt` to keep shared pins consistent. Uses a `+cpu` torch pin and best-match index resolution so the CPU index's older copies of shared packages do not downgrade the runtime |
| Remote installer | After cloning, hand off to the tagged `install.sh` with the original arguments and remote venv path. The installer and its exports must come from the same revision, including when the tag predates the pinned installer | | Remote installer | After cloning, hand off to the tagged `install.sh` with the original arguments and remote venv path. The installer and its exports must come from the same revision, including when the tag predates the pinned installer |
| `[vad]` extra | `onnxruntime` for Silero VAD | | `[vad]` extra | `onnxruntime` for Silero VAD |
@@ -136,7 +136,7 @@ Use **uv 0.13.3** for `just lock` and `just export-check`, matching CI. Other ve
| AppImage PyGObject | Pinned separately in `requirements/appimage.in`, and below 3.52: the AppImage bundles its own interpreter and builds PyGObject against the base image's girepository-1.0, while uv.lock's 3.56 needs girepository-2.0 (glib 2.80+) | | AppImage PyGObject | Pinned separately in `requirements/appimage.in`, and below 3.52: the AppImage bundles its own interpreter and builds PyGObject against the base image's girepository-1.0, while uv.lock's 3.56 needs girepository-2.0 (glib 2.80+) |
| AppImage build inputs | Base image, tooling, interpreter, shaderc and the Vulkan headers are pinned in `packaging/appimage/tool_checksums.txt`. Build with `just appimage` (docker) — building on the host ships the host's glibc, which is what kept the AppImage off Debian 12 | | AppImage build inputs | Base image, tooling, interpreter, shaderc and the Vulkan headers are pinned in `packaging/appimage/tool_checksums.txt`. Build with `just appimage` (docker) — building on the host ships the host's glibc, which is what kept the AppImage off Debian 12 |
| AppImage boot matrix | `packaging/appimage/boot-test.sh` runs the finished AppImage in distro containers, and the matrix in `unified-pipeline.yml` must keep distros both older and newer than the build image: the old ones prove the glibc floor, the new ones catch a bundle that breaks the host binaries it spawns. A distro added there needs its package recipe in the script — `tests/test_appimage_packaging.py` checks both | | AppImage boot matrix | `packaging/appimage/boot-test.sh` runs the finished AppImage in distro containers, and the matrix in `unified-pipeline.yml` must keep distros both older and newer than the build image: the old ones prove the glibc floor, the new ones catch a bundle that breaks the host binaries it spawns. A distro added there needs its package recipe in the script — `tests/test_appimage_packaging.py` checks both |
| Native packages (.deb / .rpm) | Thin packages built by nfpm in the pinned Debian 12 image (`packaging/native/tool_checksums.txt` pins the image and the nfpm releases). Python, GTK and PyGObject come from the distro; only `pywhispercpp` and `pynput` are vendored under `/usr/lib/vocalinux/vendor` (unpackaged on Debian/Fedora). `packaging/native/smoke-test.sh` installs the package per-distro and runs `vocalinux --version` plus an import smoke — `tests/test_native_packaging.py` keeps the deps lists, vendored set and CI matrix consistent offline | | Native packages (.deb / .rpm) | Thin packages built by nfpm in the pinned Debian 12 image (`packaging/native/tool_checksums.txt` pins the image and the nfpm releases). Python, GTK and PyGObject come from the distro; only `pywhispercpp` and `pynput` are vendored under `/usr/lib/voculinux/vendor` (unpackaged on Debian/Fedora). `packaging/native/smoke-test.sh` installs the package per-distro and runs `voculinux --version` plus an import smoke — `tests/test_native_packaging.py` keeps the deps lists, vendored set and CI matrix consistent offline |
| Snap Python inputs | `snap/local/build.py` uses `requirements/installer-build.txt` in an isolated, build-only venv and installs `requirements/runtime.txt` with hashes, no dependency resolution or build isolation. The project installs without dependencies or index access; never strip PyGObject from `pyproject.toml`. GI comes from the GNOME extension. Keep portable CPU flags and force the pinned pywhispercpp sdist without a wheel cache. PR/release CI installs the real artifact via `snap/local/smoke-test.sh` before publication. Native Ubuntu/GNOME/Snapcraft inputs remain channel-controlled, not byte-for-byte reproducible — see `docs/SNAP.md` | | Snap Python inputs | `snap/local/build.py` uses `requirements/installer-build.txt` in an isolated, build-only venv and installs `requirements/runtime.txt` with hashes, no dependency resolution or build isolation. The project installs without dependencies or index access; never strip PyGObject from `pyproject.toml`. GI comes from the GNOME extension. Keep portable CPU flags and force the pinned pywhispercpp sdist without a wheel cache. PR/release CI installs the real artifact via `snap/local/smoke-test.sh` before publication. Native Ubuntu/GNOME/Snapcraft inputs remain channel-controlled, not byte-for-byte reproducible — see `docs/SNAP.md` |
| Flatpak deps | `packaging/flatpak/python3-dependencies.yaml` is generated from `requirements/vad.txt` (runtime dependencies plus the `[vad]` extra) by `scripts/sync_flatpak_deps.py` (`just flatpak-deps`, which `just lock` runs itself). It resolves each artifact by the digest uv already recorded, so the Flatpak downloads the bytes in `uv.lock`; `onnxruntime` is pinned to CPython wheels for each Flatpak architecture and the GNOME 50 Python version. The project installs there with `pip3 install --no-deps`, so nothing inside the build evaluates `pyproject.toml` — `tests/test_flatpak_packaging.py` checks the invariant offline | | Flatpak deps | `packaging/flatpak/python3-dependencies.yaml` is generated from `requirements/vad.txt` (runtime dependencies plus the `[vad]` extra) by `scripts/sync_flatpak_deps.py` (`just flatpak-deps`, which `just lock` runs itself). It resolves each artifact by the digest uv already recorded, so the Flatpak downloads the bytes in `uv.lock`; `onnxruntime` is pinned to CPython wheels for each Flatpak architecture and the GNOME 50 Python version. The project installs there with `pip3 install --no-deps`, so nothing inside the build evaluates `pyproject.toml` — `tests/test_flatpak_packaging.py` checks the invariant offline |
| Speech models | Verified against digests pinned in `src/vocalinux/utils/model_checksums.txt` before install, by both `install.sh` and the runtime downloaders. **Fails closed** — an unpinned model is refused. Regenerate with `just model-checksums` (never by hand); `tests/test_model_checksums.py` fails if it falls behind. whisper.cpp URLs use a pinned Hugging Face commit, never `main` | | Speech models | Verified against digests pinned in `src/vocalinux/utils/model_checksums.txt` before install, by both `install.sh` and the runtime downloaders. **Fails closed** — an unpinned model is refused. Regenerate with `just model-checksums` (never by hand); `tests/test_model_checksums.py` fails if it falls behind. whisper.cpp URLs use a pinned Hugging Face commit, never `main` |
@@ -152,7 +152,7 @@ src/vocalinux/
│ ├── capture.py # PortAudio capture sources and device handling │ ├── capture.py # PortAudio capture sources and device handling
│ ├── pipewire.py # PipeWire system-audio capture │ ├── pipewire.py # PipeWire system-audio capture
│ └── playback_ducker.py # lower default sink while dictating │ └── playback_ducker.py # lower default sink while dictating
├── single_instance.py # $XDG_DATA_HOME/vocalinux/instance.lock ├── single_instance.py # $XDG_DATA_HOME/voculinux/instance.lock
├── auto_pause_monitor.py # unload model while configured apps run ├── auto_pause_monitor.py # unload model while configured apps run
├── model_keepalive.py # idle unload ├── model_keepalive.py # idle unload
├── suspend_handler.py # logind PrepareForSleep ├── suspend_handler.py # logind PrepareForSleep
@@ -243,9 +243,9 @@ Site-only work: `web/AGENTS.md` (commands, layout map). Product claims: `web/PRO
## Runtime (any Linux dev machine) ## Runtime (any Linux dev machine)
- Create runtime/dev venvs with `--system-site-packages`. Build-only venvs for `scripts/build_reproducible_wheel.py`, `just aur-gate` preparation and `snap/local/build.py` are the exception: exclude host packages to keep the locked tools isolated; the Snap runtime gets GI from its GNOME command chain. Never prune the developer environment to run a packaging gate - Create runtime/dev venvs with `--system-site-packages`. Build-only venvs for `scripts/build_reproducible_wheel.py`, `just aur-gate` preparation and `snap/local/build.py` are the exception: exclude host packages to keep the locked tools isolated; the Snap runtime gets GI from its GNOME command chain. Never prune the developer environment to run a packaging gate
- Kill the app by PID, never `pkill -f vocalinux` (matches editors, shells, and cwd paths) - Kill the app by PID, never `pkill -f voculinux` (matches editors, shells, and cwd paths)
- Stale instance: `$XDG_DATA_HOME/vocalinux/instance.lock` (default `~/.local/share/vocalinux/instance.lock`) - Stale instance: `$XDG_DATA_HOME/voculinux/instance.lock` (default `~/.local/share/voculinux/instance.lock`)
- Default dictation shortcut: hold Right Alt (push-to-talk). Existing `~/.config/vocalinux/config.json` wins - Default dictation shortcut: hold Right Alt (push-to-talk). Existing `~/.config/voculinux/config.json` wins
- Headless speech smoke: `pywhispercpp.model.Model` + `CommandProcessor` (first run may download the tiny model) - Headless speech smoke: `pywhispercpp.model.Model` + `CommandProcessor` (first run may download the tiny model)
Update this file when commands, layout, or agent rules change. Update this file when commands, layout, or agent rules change.
+1 -1
View File
@@ -1 +1 @@
vocalinux.com voculinux.com
+4 -4
View File
@@ -1,4 +1,4 @@
# Contributing to Vocalinux # Contributing to Voculinux
Thanks for your interest in contributing. This guide covers setup, style, testing, and pull requests. Thanks for your interest in contributing. This guide covers setup, style, testing, and pull requests.
@@ -18,8 +18,8 @@ Participation is covered by our [Code of Conduct](CODE_OF_CONDUCT.md). Be respec
### Automated (recommended) ### Automated (recommended)
```bash ```bash
git clone https://github.com/YOUR-USERNAME/vocalinux.git git clone https://github.com/YOUR-USERNAME/voculinux.git
cd vocalinux cd voculinux
./install.sh --dev ./install.sh --dev
``` ```
@@ -152,7 +152,7 @@ just typecheck # mypy src/
### Project structure ### Project structure
``` ```
vocalinux/ voculinux/
├── src/vocalinux/ # Application ├── src/vocalinux/ # Application
│ ├── main.py │ ├── main.py
│ ├── speech_recognition/ # Engines + command processor │ ├── speech_recognition/ # Engines + command processor
+1 -1
View File
@@ -1,4 +1,4 @@
include vocalinux.desktop include voculinux.desktop
include resources/icons/scalable/*.svg include resources/icons/scalable/*.svg
include resources/sounds/*.wav include resources/sounds/*.wav
recursive-include src/vocalinux/resources * recursive-include src/vocalinux/resources *
+52 -52
View File
@@ -1,8 +1,8 @@
<div align="center"> <div align="center">
<img src="https://vocalinux.com/brand/vocalinux-mark-circle.svg" width="50" height="50" alt="Vocalinux"> <img src="https://voculinux.com/brand/voculinux-mark-circle.svg" width="50" height="50" alt="Voculinux">
# Vocalinux # Voculinux
**Voice dictation for Linux** **Voice dictation for Linux**
@@ -22,20 +22,20 @@
[![.deb](https://img.shields.io/badge/.deb-Debian%20%7C%20Ubuntu-A81D33?logo=debian&logoColor=white)](https://github.com/VocaHQ/vocalinux/releases/latest) [![.deb](https://img.shields.io/badge/.deb-Debian%20%7C%20Ubuntu-A81D33?logo=debian&logoColor=white)](https://github.com/VocaHQ/vocalinux/releases/latest)
[![.rpm](https://img.shields.io/badge/.rpm-Fedora-EE0000?logo=fedora&logoColor=white)](https://github.com/VocaHQ/vocalinux/releases/latest) [![.rpm](https://img.shields.io/badge/.rpm-Fedora-EE0000?logo=fedora&logoColor=white)](https://github.com/VocaHQ/vocalinux/releases/latest)
[![Flatpak](https://img.shields.io/badge/Flatpak-vocahq%20remote-4A90D9?logo=flatpak&logoColor=white)](#flatpak-any-distro) [![Flatpak](https://img.shields.io/badge/Flatpak-vocahq%20remote-4A90D9?logo=flatpak&logoColor=white)](#flatpak-any-distro)
[![Snap](https://img.shields.io/badge/Snap-edge-82BEA0?logo=snapcraft&logoColor=white)](https://snapcraft.io/vocalinux) [![Snap](https://img.shields.io/badge/Snap-edge-82BEA0?logo=snapcraft&logoColor=white)](https://snapcraft.io/voculinux)
[![AUR](https://img.shields.io/aur/version/vocalinux)](https://aur.archlinux.org/packages/vocalinux) [![AUR](https://img.shields.io/aur/version/voculinux)](https://aur.archlinux.org/packages/voculinux)
[![PyPI](https://img.shields.io/pypi/v/vocalinux)](https://pypi.org/project/vocalinux/) [![PyPI](https://img.shields.io/pypi/v/voculinux)](https://pypi.org/project/voculinux/)
[![Nix flake](https://img.shields.io/badge/Nix-flake-5277C3?logo=nixos&logoColor=white)](packaging/nix/README.md) [![Nix flake](https://img.shields.io/badge/Nix-flake-5277C3?logo=nixos&logoColor=white)](packaging/nix/README.md)
[![Discord](https://img.shields.io/discord/1538633755877580810?logo=discord&logoColor=white&label=Discord)](https://discord.gg/t6muquAJbm) [![Discord](https://img.shields.io/discord/1538633755877580810?logo=discord&logoColor=white&label=Discord)](https://discord.gg/t6muquAJbm)
[![VocaHQ](https://img.shields.io/badge/VocaHQ-vocahq.com-1a7f4e)](https://vocahq.com) [![VocaHQ](https://img.shields.io/badge/VocaHQ-vocahq.com-1a7f4e)](https://vocahq.com)
[![Follow on X](https://img.shields.io/badge/Follow%20%40vocahq-000000?style=flat&logo=x&logoColor=white)](https://x.com/vocahq) [![Follow on X](https://img.shields.io/badge/Follow%20%40vocahq-000000?style=flat&logo=x&logoColor=white)](https://x.com/vocahq)
[Website](https://vocalinux.com) · [Install](#install) · [Docs](#documentation) · [Releases](https://github.com/VocaHQ/vocalinux/releases) [Website](https://voculinux.com) · [Install](#install) · [Docs](#documentation) · [Releases](https://github.com/VocaHQ/vocalinux/releases)
</div> </div>
Vocalinux turns speech into typed text in whatever app has focus. It is a free, AGPL-3.0-licensed desktop app for X11 and Wayland. After you download a model, local engines (whisper.cpp by default, plus OpenAI Whisper, Faster Whisper, VOSK, and Parakeet) run speech-to-text on your machine. An optional remote HTTP API is off unless you configure it. Voculinux turns speech into typed text in whatever app has focus. It is a free, AGPL-3.0-licensed desktop app for X11 and Wayland. After you download a model, local engines (whisper.cpp by default, plus OpenAI Whisper, Faster Whisper, VOSK, and Parakeet) run speech-to-text on your machine. An optional remote HTTP API is off unless you configure it.
No Voca account is required. Models download once. After that, speech-to-text stays on your machine. No Voca account is required. Models download once. After that, speech-to-text stays on your machine.
@@ -54,7 +54,7 @@ No Voca account is required. Models download once. After that, speech-to-text st
## Screenshots ## Screenshots
Vocalinux in action. Full gallery on the [website screenshots page](https://vocalinux.com/screenshots/). Voculinux in action. Full gallery on the [website screenshots page](https://voculinux.com/screenshots/).
### Product ### Product
@@ -161,7 +161,7 @@ For a specific release tag, see [GitHub Releases](https://github.com/VocaHQ/voca
### Arch Linux (AUR) ### Arch Linux (AUR)
```bash ```bash
yay -S vocalinux yay -S voculinux
``` ```
See [docs/AUR.md](docs/AUR.md). See [docs/AUR.md](docs/AUR.md).
@@ -172,28 +172,28 @@ Download the `x86_64` or `aarch64` AppImage from [Releases](https://github.com/V
### Distro packages (.deb / .rpm) ### Distro packages (.deb / .rpm)
Download the `.deb` (Debian 12+, Ubuntu 24.04+) or `.rpm` (Fedora) matching your CPU from [Releases](https://github.com/VocaHQ/vocalinux/releases) and install with `sudo apt install ./vocalinux_*.deb` or `sudo dnf install ./vocalinux-*.rpm`. Thin packages: Python and GTK come from the distro. No auto-update; CPU whisper.cpp. Details in [docs/INSTALL.md](docs/INSTALL.md). Download the `.deb` (Debian 12+, Ubuntu 24.04+) or `.rpm` (Fedora) matching your CPU from [Releases](https://github.com/VocaHQ/vocalinux/releases) and install with `sudo apt install ./voculinux_*.deb` or `sudo dnf install ./voculinux-*.rpm`. Thin packages: Python and GTK come from the distro. No auto-update; CPU whisper.cpp. Details in [docs/INSTALL.md](docs/INSTALL.md).
### Flatpak (any distro) ### Flatpak (any distro)
Install and auto-update via the self-hosted VocaHQ remote (`flatpak update` picks up each release): Install and auto-update via the self-hosted VocaHQ remote (`flatpak update` picks up each release):
```bash ```bash
flatpak install https://vocahq.github.io/vocalinux-flatpak/com.vocalinux.Vocalinux.flatpakref flatpak install https://vocahq.github.io/voculinux-flatpak/com.voculinux.Voculinux.flatpakref
``` ```
The `.flatpakref` adds the `vocahq` remote and resolves the GNOME runtime through Flathub. Manually instead: The `.flatpakref` adds the `vocahq` remote and resolves the GNOME runtime through Flathub. Manually instead:
```bash ```bash
flatpak remote-add --if-not-exists flathub https://dl.flathub.org/repo/flathub.flatpakrepo flatpak remote-add --if-not-exists flathub https://dl.flathub.org/repo/flathub.flatpakrepo
flatpak remote-add --if-not-exists vocahq https://vocahq.github.io/vocalinux-flatpak/vocahq.flatpakrepo flatpak remote-add --if-not-exists vocahq https://vocahq.github.io/voculinux-flatpak/vocahq.flatpakrepo
flatpak install vocahq com.vocalinux.Vocalinux flatpak install vocahq com.voculinux.Voculinux
``` ```
Or sideload a GitHub Release bundle (`Vocalinux-<version>-x86_64.flatpak` or `-aarch64.flatpak`) after the Flathub GNOME runtime is present — bundles do not auto-update: Or sideload a GitHub Release bundle (`Voculinux-<version>-x86_64.flatpak` or `-aarch64.flatpak`) after the Flathub GNOME runtime is present — bundles do not auto-update:
```bash ```bash
flatpak install --user ./Vocalinux-<version>-x86_64.flatpak flatpak install --user ./Voculinux-<version>-x86_64.flatpak
``` ```
Local build: Local build:
@@ -201,21 +201,21 @@ Local build:
```bash ```bash
flatpak install flathub org.gnome.Platform//50 org.gnome.Sdk//50 flatpak install flathub org.gnome.Platform//50 org.gnome.Sdk//50
flatpak-builder --user --install --force-clean build-dir \ flatpak-builder --user --install --force-clean build-dir \
packaging/flatpak/com.vocalinux.Vocalinux.yml packaging/flatpak/com.voculinux.Voculinux.yml
flatpak run com.vocalinux.Vocalinux flatpak run com.voculinux.Voculinux
``` ```
Ships whisper.cpp with Vulkan. It is **not on Flathub** (submission [flathub#9368](https://github.com/flathub/flathub/pull/9368) closed 2026-07-23 on policy grounds). Details: [packaging/flatpak/README.md](packaging/flatpak/README.md). Ships whisper.cpp with Vulkan. It is **not on Flathub** (submission [flathub#9368](https://github.com/flathub/flathub/pull/9368) closed 2026-07-23 on policy grounds). Details: [packaging/flatpak/README.md](packaging/flatpak/README.md).
### Snap (Ubuntu Snap Store) ### Snap (Ubuntu Snap Store)
Listing: [snapcraft.io/vocalinux](https://snapcraft.io/vocalinux). `stable` is promoted from `candidate` after QA. Tagged releases also attach `vocalinux_<version>_amd64.snap` on GitHub. Listing: [snapcraft.io/voculinux](https://snapcraft.io/voculinux). `stable` is promoted from `candidate` after QA. Tagged releases also attach `voculinux_<version>_amd64.snap` on GitHub.
```bash ```bash
sudo snap install vocalinux --edge sudo snap install voculinux --edge
sudo snap connect vocalinux:audio-record # if mic is not auto-connected sudo snap connect voculinux:audio-record # if mic is not auto-connected
sudo snap connect vocalinux:raw-input # global keyboard shortcuts (evdev) sudo snap connect voculinux:raw-input # global keyboard shortcuts (evdev)
sudo snap connect vocalinux:hardware-observe # list keyboards (/proc/bus/input/devices) sudo snap connect voculinux:hardware-observe # list keyboards (/proc/bus/input/devices)
``` ```
Native Wayland typing uses the RemoteDesktop portal: the compositor asks once on first dictation, nothing to connect. The recipe ships **no `uinput` plug** and no ydotool; the Store denied the super-privileged grant, which held the 0.17.0+ revisions and left **v0.16.2 (rev 7)** on edge. On KDE Plasma, KWin's portal can mistype letter case ([KDE bug 489021](https://bugs.kde.org/show_bug.cgi?id=489021)); set Virtual Keyboard to "IBus Wayland" so dictation types correctly. Native Wayland typing uses the RemoteDesktop portal: the compositor asks once on first dictation, nothing to connect. The recipe ships **no `uinput` plug** and no ydotool; the Store denied the super-privileged grant, which held the 0.17.0+ revisions and left **v0.16.2 (rev 7)** on edge. On KDE Plasma, KWin's portal can mistype letter case ([KDE bug 489021](https://bugs.kde.org/show_bug.cgi?id=489021)); set Virtual Keyboard to "IBus Wayland" so dictation types correctly.
@@ -223,8 +223,8 @@ Native Wayland typing uses the RemoteDesktop portal: the compositor asks once on
GitHub `.snap` files through v0.18.2 declare `uinput`, so KDE users who want ydotool can sideload that build and connect the plug manually: GitHub `.snap` files through v0.18.2 declare `uinput`, so KDE users who want ydotool can sideload that build and connect the plug manually:
```bash ```bash
sudo snap install --dangerous ./vocalinux_0.18.2_amd64.snap sudo snap install --dangerous ./voculinux_0.18.2_amd64.snap
sudo snap connect vocalinux:uinput # only in the v0.18.x sideload; the recipe no longer ships it sudo snap connect voculinux:uinput # only in the v0.18.x sideload; the recipe no longer ships it
``` ```
### Nix / NixOS (flake) ### Nix / NixOS (flake)
@@ -234,13 +234,13 @@ nix run github:VocaHQ/vocalinux # try it
nix profile install github:VocaHQ/vocalinux nix profile install github:VocaHQ/vocalinux
``` ```
The flake also ships `nixosModules.vocalinux` and `homeManagerModules.vocalinux` for declarative installs (`programs.vocalinux.enable = true`). The bundled pywhispercpp builds with Vulkan GPU inference. Details: [packaging/nix/README.md](packaging/nix/README.md). The flake also ships `nixosModules.voculinux` and `homeManagerModules.voculinux` for declarative installs (`programs.voculinux.enable = true`). The bundled pywhispercpp builds with Vulkan GPU inference. Details: [packaging/nix/README.md](packaging/nix/README.md).
### From source ### From source
```bash ```bash
git clone https://github.com/VocaHQ/vocalinux.git git clone https://github.com/VocaHQ/vocalinux.git
cd vocalinux cd voculinux
./install.sh ./install.sh
# or pick the engine up front # or pick the engine up front
./install.sh --engine=whisper_cpp ./install.sh --engine=whisper_cpp
@@ -251,12 +251,12 @@ cd vocalinux
### After installation ### After installation
```bash ```bash
vocalinux # if ~/.local/bin is on PATH voculinux # if ~/.local/bin is on PATH
# or # or
~/.local/share/vocalinux/venv/bin/vocalinux ~/.local/share/voculinux/venv/bin/voculinux
``` ```
You can also launch Vocalinux from your application menu. You can also launch Voculinux from your application menu.
### Nightly builds ### Nightly builds
@@ -271,7 +271,7 @@ Daily builds from `main` appear on [Releases](https://github.com/VocaHQ/vocalinu
| **Display** | X11 or Wayland | | **Display** | X11 or Wayland |
| **Hardware** | Microphone; GPU optional (Vulkan) | | **Hardware** | Microphone; GPU optional (Vulkan) |
The distro must ship Python 3.11+ because Vocalinux uses distro PyGObject (`python3-gi`). Ubuntu 22.04 (Python 3.10) and Debian 11 (3.9) are below that floor. Distribution notes: [docs/DISTRO_COMPATIBILITY.md](docs/DISTRO_COMPATIBILITY.md). The distro must ship Python 3.11+ because Voculinux uses distro PyGObject (`python3-gi`). Ubuntu 22.04 (Python 3.10) and Debian 11 (3.9) are below that floor. Distribution notes: [docs/DISTRO_COMPATIBILITY.md](docs/DISTRO_COMPATIBILITY.md).
## Usage ## Usage
@@ -302,20 +302,20 @@ punctuation / line-break phrases in that language are also recognized
### CLI ### CLI
```bash ```bash
vocalinux --help voculinux --help
vocalinux --version voculinux --version
vocalinux --debug voculinux --debug
vocalinux --engine whisper_cpp # default voculinux --engine whisper_cpp # default
vocalinux --engine whisper voculinux --engine whisper
vocalinux --engine faster_whisper voculinux --engine faster_whisper
vocalinux --engine vosk voculinux --engine vosk
vocalinux --engine parakeet voculinux --engine parakeet
vocalinux --engine remote_api voculinux --engine remote_api
vocalinux --model medium voculinux --model medium
vocalinux --model medium.en-q5_0 # exact whisper.cpp variant voculinux --model medium.en-q5_0 # exact whisper.cpp variant
vocalinux --model large-v3-turbo voculinux --model large-v3-turbo
vocalinux --wayland voculinux --wayland
vocalinux --start-minimized voculinux --start-minimized
``` ```
### Autostart ### Autostart
@@ -324,9 +324,9 @@ vocalinux --start-minimized
### Configuration ### Configuration
Stored at `~/.config/vocalinux/config.json`. Prefer the Settings dialog for day-to-day changes. **Settings → Speech Model** starts with a simple setup; expand **Advanced** for engine, size, and specialization. Set `post_processing.script_path` to an executable to transform each transcription result (stdin → stdout) before it is injected; leave empty to disable. Stored at `~/.config/voculinux/config.json`. Prefer the Settings dialog for day-to-day changes. **Settings → Speech Model** starts with a simple setup; expand **Advanced** for engine, size, and specialization. Set `post_processing.script_path` to an executable to transform each transcription result (stdin → stdout) before it is injected; leave empty to disable.
Neural VAD (Silero) is used when `onnxruntime` is available; install via `pip install "vocalinux[vad]"` for manual/PyPI installs. The installer attempts this automatically. Neural VAD (Silero) is used when `onnxruntime` is available; install via `pip install "voculinux[vad]"` for manual/PyPI installs. The installer attempts this automatically.
## Documentation ## Documentation
@@ -358,7 +358,7 @@ Report vulnerabilities privately per [SECURITY.md](SECURITY.md).
```bash ```bash
git clone https://github.com/VocaHQ/vocalinux.git git clone https://github.com/VocaHQ/vocalinux.git
cd vocalinux cd voculinux
./install.sh --dev ./install.sh --dev
source venv/bin/activate source venv/bin/activate
pytest pytest
@@ -379,11 +379,11 @@ Planned:
## Voca ecosystem ## Voca ecosystem
Vocalinux is part of [VocaHQ](https://vocahq.com). On-device speech-to-text first, one app per platform. Optional [VocaGateway](https://vocagateway.vocahq.com) is self-hosted and not on-device. Voculinux is part of [VocaHQ](https://vocahq.com). On-device speech-to-text first, one app per platform. Optional [VocaGateway](https://vocagateway.vocahq.com) is self-hosted and not on-device.
| Platform | Project | Website | GitHub | | Platform | Project | Website | GitHub |
|----------|---------|---------|--------| |----------|---------|---------|--------|
| Linux | **VocaLinux** | [vocalinux.com](https://vocalinux.com) | [VocaHQ/vocalinux](https://github.com/VocaHQ/vocalinux) | | Linux | **Voculinux** | [voculinux.com](https://voculinux.com) | [VocaHQ/vocalinux](https://github.com/VocaHQ/vocalinux) |
| macOS | **VocaMac** | [vocamac.com](https://vocamac.com) | [VocaHQ/vocamac](https://github.com/VocaHQ/vocamac) | | macOS | **VocaMac** | [vocamac.com](https://vocamac.com) | [VocaHQ/vocamac](https://github.com/VocaHQ/vocamac) |
| Windows | **VocaWin** | [vocawin.com](https://vocawin.com) | [VocaHQ/vocawin](https://github.com/VocaHQ/vocawin) | | Windows | **VocaWin** | [vocawin.com](https://vocawin.com) | [VocaHQ/vocawin](https://github.com/VocaHQ/vocawin) |
| Phone | **VocaPhone** | [vocaphone.vocahq.com](https://vocaphone.vocahq.com) | [VocaHQ/vocaphone](https://github.com/VocaHQ/vocaphone) | | Phone | **VocaPhone** | [vocaphone.vocahq.com](https://vocaphone.vocahq.com) | [VocaHQ/vocaphone](https://github.com/VocaHQ/vocaphone) |
@@ -404,7 +404,7 @@ Bug reports, docs, and code are welcome. Start with [CONTRIBUTING.md](CONTRIBUTI
Thanks to everyone who has contributed code, docs, or fixes: Thanks to everyone who has contributed code, docs, or fixes:
<a href="https://github.com/VocaHQ/vocalinux/graphs/contributors"> <a href="https://github.com/VocaHQ/vocalinux/graphs/contributors">
<img src="https://contrib.rocks/image?repo=VocaHQ/vocalinux" alt="Vocalinux contributors" /> <img src="https://contrib.rocks/image?repo=VocaHQ/vocalinux" alt="Voculinux contributors" />
</a> </a>
## Repository mirrors ## Repository mirrors
@@ -414,7 +414,7 @@ GitHub is the primary forge for issues, PRs, CI, and releases.
| Role | URL | | Role | URL |
|------|-----| |------|-----|
| Primary | https://github.com/VocaHQ/vocalinux | | Primary | https://github.com/VocaHQ/vocalinux |
| Read-only mirror (Codeberg) | https://codeberg.org/jatinkrmalik/vocalinux | | Read-only mirror (Codeberg) | https://codeberg.org/jatinkrmalik/voculinux |
Open issues and pull requests on GitHub only. Open issues and pull requests on GitHub only.
@@ -440,7 +440,7 @@ offers `orukeet-v0.1.0`. It uses the existing local sherpa-onnx recognizer for
engine stay unchanged. engine stay unchanged.
The download comes from a pinned revision of The download comes from a pinned revision of
[oruk/orukeet on Hugging Face](https://huggingface.co/oruk/orukeet). Vocalinux [oruk/orukeet on Hugging Face](https://huggingface.co/oruk/orukeet). Voculinux
checks every file against its bundled SHA-256 pins and checks the publisher's checks every file against its bundled SHA-256 pins and checks the publisher's
release manifest against those same pins. The manifest download participates release manifest against those same pins. The manifest download participates
in Hugging Face's normal model-download accounting. Cached recognition works in Hugging Face's normal model-download accounting. Cached recognition works
+4 -4
View File
@@ -20,7 +20,7 @@ Do not open a public GitHub issue for security vulnerabilities.
**Preferred:** email the maintainer at **jatinkrmalik@gmail.com** with subject: **Preferred:** email the maintainer at **jatinkrmalik@gmail.com** with subject:
`Vocalinux Security Vulnerability Report` `Voculinux Security Vulnerability Report`
Include: Include:
@@ -35,7 +35,7 @@ If email is unavailable, use [GitHub private vulnerability reporting](https://gi
## Privacy design ## Privacy design
Vocalinux is built for local-first dictation: Voculinux is built for local-first dictation:
- Local engines process audio on-device after you download a model - Local engines process audio on-device after you download a model
- No Voca account is required for local engines - No Voca account is required for local engines
@@ -62,8 +62,8 @@ When the user enables **Remote API**, audio is uploaded to the configured server
| Path | Purpose | Permissions (typical) | | Path | Purpose | Permissions (typical) |
|------|---------|------------------------| |------|---------|------------------------|
| `~/.config/vocalinux/` | Configuration | User-only (mode 700) | | `~/.config/voculinux/` | Configuration | User-only (mode 700) |
| `~/.local/share/vocalinux/` | Data and models | User-only (mode 700) | | `~/.local/share/voculinux/` | Data and models | User-only (mode 700) |
### Known limitations ### Known limitations
+3 -3
View File
@@ -1,6 +1,6 @@
# Support # Support
Where to get help with Vocalinux. Where to get help with Voculinux.
## Documentation ## Documentation
@@ -14,7 +14,7 @@ Where to get help with Vocalinux.
| Distros and sessions | [docs/DISTRO_COMPATIBILITY.md](docs/DISTRO_COMPATIBILITY.md) | | Distros and sessions | [docs/DISTRO_COMPATIBILITY.md](docs/DISTRO_COMPATIBILITY.md) |
| Remote transcription API | [docs/HTTP_REMOTE.md](docs/HTTP_REMOTE.md) | | Remote transcription API | [docs/HTTP_REMOTE.md](docs/HTTP_REMOTE.md) |
| Full index | [docs/README.md](docs/README.md) | | Full index | [docs/README.md](docs/README.md) |
| Website | [vocalinux.com](https://vocalinux.com) | | Website | [voculinux.com](https://voculinux.com) |
## Community ## Community
@@ -25,7 +25,7 @@ Where to get help with Vocalinux.
| [Bug report](https://github.com/VocaHQ/vocalinux/issues/new?template=bug_report.md) | Something is broken | | [Bug report](https://github.com/VocaHQ/vocalinux/issues/new?template=bug_report.md) | Something is broken |
| [Feature request](https://github.com/VocaHQ/vocalinux/issues/new?template=feature_request.md) | New capability proposals | | [Feature request](https://github.com/VocaHQ/vocalinux/issues/new?template=feature_request.md) | New capability proposals |
When filing a bug, include OS/distro, `vocalinux --version`, install method, display server (X11/Wayland), engine, and a `vocalinux --debug` snippet. When filing a bug, include OS/distro, `voculinux --version`, install method, display server (X11/Wayland), engine, and a `voculinux --debug` snippet.
## Security ## Security
+2 -2
View File
@@ -1,6 +1,6 @@
# Architecture # Architecture
Vocalinux is a GTK 3 desktop application. `main.py` wires together the Voculinux is a GTK 3 desktop application. `main.py` wires together the
configuration manager, recognition manager, tray UI, D-Bus service, and text configuration manager, recognition manager, tray UI, D-Bus service, and text
injector. Audio capture and recognition run outside the GTK event loop. injector. Audio capture and recognition run outside the GTK event loop.
@@ -104,7 +104,7 @@ as `tray_indicator.py`, `dictation_overlay.py`, `transcription_history.py`,
## Services and optional integrations ## Services and optional integrations
`dbus_service.py` owns the `com.vocalinux.Vocalinux` session-bus name and `dbus_service.py` owns the `com.voculinux.Voculinux` session-bus name and
provides Toggle, Start, and Stop methods for external controls. provides Toggle, Start, and Stop methods for external controls.
`gateway_embed/` manages the optional local VocaGateway container lifecycle, `gateway_embed/` manages the optional local VocaGateway container lifecycle,
status, pairing, and configuration. It is separate from the default status, pairing, and configuration. It is separate from the default
+10 -10
View File
@@ -3,13 +3,13 @@
## Users ## Users
```bash ```bash
yay -S vocalinux # canonical release sdist, built against Arch dependencies yay -S voculinux # canonical release sdist, built against Arch dependencies
yay -S vocalinux-git # community package; tracks main yay -S voculinux-git # community package; tracks main
``` ```
`vocalinux`, `vocalinux-bin` and `vocalinux-git` conflict; install only one. `voculinux`, `voculinux-bin` and `voculinux-git` conflict; install only one.
`packaging/aur/vocalinux-bin/` builds a `-bin` variant that installs the `packaging/aur/voculinux-bin/` builds a `-bin` variant that installs the
prebuilt AppImage from the release — no Python, engine, or GPU dependency prebuilt AppImage from the release — no Python, engine, or GPU dependency
builds, since the bundle carries them (#817). Its publication waits only for builds, since the bundle carries them (#817). Its publication waits only for
release creation and both AppImages, not the source-package gate or other release creation and both AppImages, not the source-package gate or other
@@ -19,7 +19,7 @@ then pushes to AUR in the `Publish AUR -bin package` step.
## Maintainers (one-time setup) ## Maintainers (one-time setup)
Release tags prepare and test the source PKGBUILD from Release tags prepare and test the source PKGBUILD from
`packaging/aur/vocalinux/PKGBUILD`, then auto-push the verified file when these `packaging/aur/voculinux/PKGBUILD`, then auto-push the verified file when these
secrets exist: secrets exist:
| Secret | Value | | Secret | Value |
@@ -30,16 +30,16 @@ secrets exist:
```bash ```bash
# 1. AUR account + SSH pubkey: https://aur.archlinux.org/register # 1. AUR account + SSH pubkey: https://aur.archlinux.org/register
ssh-keygen -t ed25519 -C "vocalinux-aur" -f ./aur_ed25519 -N "" ssh-keygen -t ed25519 -C "voculinux-aur" -f ./aur_ed25519 -N ""
# upload aur_ed25519.pub on AUR; put aur_ed25519 in AUR_SSH_PRIVATE_KEY # upload aur_ed25519.pub on AUR; put aur_ed25519 in AUR_SSH_PRIVATE_KEY
# 2. Create empty package once # 2. Create empty package once
git clone ssh://aur@aur.archlinux.org/vocalinux.git git clone ssh://aur@aur.archlinux.org/voculinux.git
# 3. Next v* tag runs Publish to AUR (skipped if secret missing) # 3. Next v* tag runs Publish to AUR (skipped if secret missing)
``` ```
Sources: `packaging/aur/vocalinux/PKGBUILD`. Community git package: https://aur.archlinux.org/packages/vocalinux-git Sources: `packaging/aur/voculinux/PKGBUILD`. Community git package: https://aur.archlinux.org/packages/voculinux-git
## Source archive and trust model ## Source archive and trust model
@@ -91,7 +91,7 @@ Only after this succeeds is `aur-source-pkgbuild` uploaded for `publish-aur`.
Publication downloads that exact file; there is no version rewrite or Publication downloads that exact file; there is no version rewrite or
`updpkgsums` afterwards. The gate runs even without AUR credentials; only the `updpkgsums` afterwards. The gate runs even without AUR credentials; only the
final push is secret-gated. This intentionally makes source publication wait final push is secret-gated. This intentionally makes source publication wait
for the release's checksums and attestations. `vocalinux-bin` uses a separate for the release's checksums and attestations. `voculinux-bin` uses a separate
AppImage publication path independent of this source gate and the global AppImage publication path independent of this source gate and the global
checksum job; its PR smoke still uses a stub AppImage. checksum job; its PR smoke still uses a stub AppImage.
@@ -107,7 +107,7 @@ What those providers actually ship remains a separate monitoring question.
**`ERROR Missing dependencies: setuptools<82`** **`ERROR Missing dependencies: setuptools<82`**
The AUR package builds with `python -m build --wheel --no-isolation`, so it uses Arch extra `python-setuptools` (currently 84). Vocalinux 0.16.1 drops the `<82` cap in `pyproject.toml`. Rebuild 0.16.1 or later. The AUR package builds with `python -m build --wheel --no-isolation`, so it uses Arch extra `python-setuptools` (currently 84). Voculinux 0.16.1 drops the `<82` cap in `pyproject.toml`. Rebuild 0.16.1 or later.
**`'_pywhispercpp.whisper_full_params' object has no attribute 'context_params'`** **`'_pywhispercpp.whisper_full_params' object has no attribute 'context_params'`**
+9 -9
View File
@@ -16,13 +16,13 @@ would make the external terms contract surprising.
## File contracts ## File contracts
Both files live in VocaLinux's XDG-aware configuration directory: Both files live in Voculinux's XDG-aware configuration directory:
`$XDG_CONFIG_HOME/vocalinux` (or `~/.config/vocalinux` when unset). `$XDG_CONFIG_HOME/voculinux` (or `~/.config/voculinux` when unset).
| File | Contract | Consumer | | File | Contract | Consumer |
| --- | --- | --- | | --- | --- | --- |
| `dictionary.txt` | UTF-8 (BOM accepted), one term per line. Blank lines and lines beginning with `#` are ignored. Terms are de-duplicated case-insensitively while retaining the first spelling. | VocaLinux and an external accessibility scanner. | | `dictionary.txt` | UTF-8 (BOM accepted), one term per line. Blank lines and lines beginning with `#` are ignored. Terms are de-duplicated case-insensitively while retaining the first spelling. | Voculinux and an external accessibility scanner. |
| `custom-dictionary-corrections.json` | UTF-8 JSON object: `{"version": 1, "corrections": [{"heard": "super base", "replacement": "Supabase"}]}`. | VocaLinux only. | | `custom-dictionary-corrections.json` | UTF-8 JSON object: `{"version": 1, "corrections": [{"heard": "super base", "replacement": "Supabase"}]}`. | Voculinux only. |
The terms file remains deliberately simple and scanner-friendly. Corrections The terms file remains deliberately simple and scanner-friendly. Corrections
need two unambiguous fields, a version, and exact replacement casing, so JSON is need two unambiguous fields, a version, and exact replacement casing, so JSON is
@@ -35,7 +35,7 @@ edited file therefore takes effect on the next dictation segment without an app
restart. Missing, unreadable, malformed, or unsupported correction files are restart. Missing, unreadable, malformed, or unsupported correction files are
ignored safely; dictation continues without their entries. ignored safely; dictation continues without their entries.
The terms file may contain any number of lines, but VocaLinux limits a single The terms file may contain any number of lines, but Voculinux limits a single
prompt to the first 200 valid terms and 2,000 characters. This bounds decoding prompt to the first 200 valid terms and 2,000 characters. This bounds decoding
work while leaving the scanner's complete line-file contract intact. The Custom work while leaving the scanner's complete line-file contract intact. The Custom
Dictionary page keeps both an add/remove editor and a validated file chooser Dictionary page keeps both an add/remove editor and a validated file chooser
@@ -43,7 +43,7 @@ for the terms file.
## Processing and engine behavior ## Processing and engine behavior
For every completed audio segment, VocaLinux applies this order: For every completed audio segment, Voculinux applies this order:
1. The selected engine transcribes audio. 1. The selected engine transcribes audio.
2. Live phrase corrections run against the raw transcript. 2. Live phrase corrections run against the raw transcript.
@@ -78,7 +78,7 @@ the JSON corrections file exists; it is never silently deleted or migrated.
The #767 persisted configuration keys are retained unchanged: The #767 persisted configuration keys are retained unchanged:
`dictionary.enabled`, `dictionary.file_path` (default `dictionary.enabled`, `dictionary.file_path` (default
`~/.config/vocalinux/dictionary.txt`), and `dictionary.max_words`. Existing `~/.config/voculinux/dictionary.txt`), and `dictionary.max_words`. Existing
Peony settings therefore continue to select the scanner's `dictionary.txt` Peony settings therefore continue to select the scanner's `dictionary.txt`
file. New configured paths are accepted only when they can be expanded and are file. New configured paths are accepted only when they can be expanded and are
either absent (so the UI can create them) or readable regular files; a failed either absent (so the UI can create them) or readable regular files; a failed
@@ -99,7 +99,7 @@ paths are safely ignored and shown as unavailable rather than raising.
## Human test checklist ## Human test checklist
1. Add `VocaLinux`, `PyGObject`, and a non-ASCII term in **Custom terms**; 1. Add `Voculinux`, `PyGObject`, and a non-ASCII term in **Custom terms**;
confirm `dictionary.txt` is UTF-8, one line per term, and an external confirm `dictionary.txt` is UTF-8, one line per term, and an external
edit changes the next Whisper, whisper.cpp, and Faster Whisper dictation segment. edit changes the next Whisper, whisper.cpp, and Faster Whisper dictation segment.
2. Add `super base` → `Supabase`; test lowercase, uppercase, punctuation, an 2. Add `super base` → `Supabase`; test lowercase, uppercase, punctuation, an
@@ -108,7 +108,7 @@ paths are safely ignored and shown as unavailable rather than raising.
3. With voice commands enabled, add a correction that changes a command-like 3. With voice commands enabled, add a correction that changes a command-like
misrecognition into ordinary text. Confirm it is injected rather than acted misrecognition into ordinary text. Confirm it is injected rather than acted
on. Also verify an intentionally command-like replacement is avoided. on. Also verify an intentionally command-like replacement is avoided.
4. Edit each file externally while VocaLinux is running. Test an invalid JSON 4. Edit each file externally while Voculinux is running. Test an invalid JSON
correction file and invalid UTF-8 terms file; confirm dictation continues and correction file and invalid UTF-8 terms file; confirm dictation continues and
the Settings status/save feedback is clear. the Settings status/save feedback is clear.
5. Test the UI with keyboard only: Add buttons via Enter, empty validation, 5. Test the UI with keyboard only: Add buttons via Enter, empty validation,
+42 -42
View File
@@ -1,8 +1,8 @@
# Linux distribution compatibility # Linux distribution compatibility
How Vocalinux behaves across distributions, what is tested, and what to install manually when needed. How Voculinux behaves across distributions, what is tested, and what to install manually when needed.
For install steps, prefer [INSTALL.md](INSTALL.md). Flatpak packaging (useful on immutable or non-standard layouts): [packaging/flatpak/README.md](../packaging/flatpak/README.md). Snap Store listing: [snapcraft.io/vocalinux](https://snapcraft.io/vocalinux) (`--edge`; pack/upload steps in [INSTALL.md](INSTALL.md)). Flatpak is not on Flathub: submission [flathub#9368](https://github.com/flathub/flathub/pull/9368) closed 2026-07-23 on policy grounds; channel tracked in [#167](https://github.com/VocaHQ/vocalinux/issues/167). For install steps, prefer [INSTALL.md](INSTALL.md). Flatpak packaging (useful on immutable or non-standard layouts): [packaging/flatpak/README.md](../packaging/flatpak/README.md). Snap Store listing: [snapcraft.io/voculinux](https://snapcraft.io/voculinux) (`--edge`; pack/upload steps in [INSTALL.md](INSTALL.md)). Flatpak is not on Flathub: submission [flathub#9368](https://github.com/flathub/flathub/pull/9368) closed 2026-07-23 on policy grounds; channel tracked in [#167](https://github.com/VocaHQ/vocalinux/issues/167).
## Cross-distro behavior ## Cross-distro behavior
@@ -24,7 +24,7 @@ The installer uses a multi-step approach to detect the correct GI_TYPELIB_PATH:
### Wrapper Scripts ### Wrapper Scripts
The installer creates wrapper scripts (`~/.local/bin/vocalinux` and `~/.local/bin/vocalinux-gui`) that: The installer creates wrapper scripts (`~/.local/bin/voculinux` and `~/.local/bin/voculinux-gui`) that:
- Set the correct `GI_TYPELIB_PATH` environment variable - Set the correct `GI_TYPELIB_PATH` environment variable
- Handle input group permissions for Wayland keyboard shortcuts - Handle input group permissions for Wayland keyboard shortcuts
- Provide seamless cross-distro compatibility - Provide seamless cross-distro compatibility
@@ -35,7 +35,7 @@ The `.desktop` entry is automatically configured with the detected `GI_TYPELIB_P
## Officially Supported Distributions ## Officially Supported Distributions
These distributions are tested and known to work well with Vocalinux. A These distributions are tested and known to work well with Voculinux. A
derivative is judged by the Python and package repositories it ships rather derivative is judged by the Python and package repositories it ships rather
than by its own version number, which rarely tracks the base release. In than by its own version number, which rarely tracks the base release. In
particular, `install.sh` does not interpret a Debian-based derivative's product particular, `install.sh` does not interpret a Debian-based derivative's product
@@ -94,7 +94,7 @@ sudo apt install -y python3-gi gir1.2-gtk-3.0 gir1.2-ibus-1.0 gir1.2-gdkpixbuf-2
### Debian-specific notes ### Debian-specific notes
Debian's standard repositories differ from Ubuntu in a few ways that matter for Vocalinux: Debian's standard repositories differ from Ubuntu in a few ways that matter for Voculinux:
**pywhispercpp build prerequisites.** On a clean Debian install the following packages are not **pywhispercpp build prerequisites.** On a clean Debian install the following packages are not
pulled in transitively (unlike Ubuntu) but are required when `pywhispercpp` must be compiled pulled in transitively (unlike Ubuntu) but are required when `pywhispercpp` must be compiled
@@ -125,16 +125,16 @@ package-scoped flag to avoid compiling unrelated deps like NumPy from source (wh
long time and can fail): long time and can fail):
```bash ```bash
source ~/.local/share/vocalinux/venv/bin/activate source ~/.local/share/voculinux/venv/bin/activate
PYWHISPERCPP_CLEAN=1 pip install --force-reinstall --no-binary=pywhispercpp pywhispercpp PYWHISPERCPP_CLEAN=1 pip install --force-reinstall --no-binary=pywhispercpp pywhispercpp
deactivate deactivate
``` ```
**Verifying libwhisper.so resolution.** If Vocalinux starts with **Verifying libwhisper.so resolution.** If Voculinux starts with
`libwhisper.so.1: cannot open shared object file`, check for unresolved symbols: `libwhisper.so.1: cannot open shared object file`, check for unresolved symbols:
```bash ```bash
ldd $(find ~/.local/share/vocalinux/venv -name '*.so' -path '*/pywhispercpp*' 2>/dev/null | head -1) | grep 'not found' ldd $(find ~/.local/share/voculinux/venv -name '*.so' -path '*/pywhispercpp*' 2>/dev/null | head -1) | grep 'not found'
``` ```
If any libraries are listed, re-run the installer with `--rebuild-whispercpp` or switch to If any libraries are listed, re-run the installer with `--rebuild-whispercpp` or switch to
@@ -215,11 +215,11 @@ sudo urpmi -y python3-gobject gtk3-devel gobject-introspection-devel \
## PyPI, pip, and pipx Installation ## PyPI, pip, and pipx Installation
The PyPI package is useful when you want a standard Python package install, or The PyPI package is useful when you want a standard Python package install, or
when you want to manage Vocalinux with `pipx`. However, **Vocalinux requires when you want to manage Voculinux with `pipx`. However, **Voculinux requires
system desktop packages** that cannot be installed by pip or pipx. system desktop packages** that cannot be installed by pip or pipx.
Use the official installer when possible. It installs the required system Use the official installer when possible. It installs the required system
packages, creates the virtual environment, installs Vocalinux, sets up desktop packages, creates the virtual environment, installs Voculinux, sets up desktop
integration, and downloads the default speech model: integration, and downloads the default speech model:
```bash ```bash
@@ -229,8 +229,8 @@ bash /tmp/vl.sh
### Important: Install System Packages First ### Important: Install System Packages First
Before running `pip install vocalinux`, `pip install "vocalinux[vosk]"`, or Before running `pip install voculinux`, `pip install "voculinux[vosk]"`, or
`pipx install vocalinux`, you **must** install the required system packages: `pipx install voculinux`, you **must** install the required system packages:
#### Ubuntu/Debian #### Ubuntu/Debian
```bash ```bash
@@ -272,8 +272,8 @@ sudo zypper install "${PYVER}-gobject" "${PYVER}-gobject-cairo" gtk3 ibus typeli
2. **Create a virtual environment with system site-packages enabled**: 2. **Create a virtual environment with system site-packages enabled**:
```bash ```bash
python3 -m venv ~/.local/share/vocalinux-pypi/venv --system-site-packages python3 -m venv ~/.local/share/voculinux-pypi/venv --system-site-packages
source ~/.local/share/vocalinux-pypi/venv/bin/activate source ~/.local/share/voculinux-pypi/venv/bin/activate
pip install --upgrade pip setuptools wheel pip install --upgrade pip setuptools wheel
``` ```
@@ -281,15 +281,15 @@ sudo zypper install "${PYVER}-gobject" "${PYVER}-gobject-cairo" gtk3 ibus typeli
as `python3-gi`, which are often more reliable than building PyGObject from as `python3-gi`, which are often more reliable than building PyGObject from
source in a venv. source in a venv.
3. **Install Vocalinux from PyPI**: 3. **Install Voculinux from PyPI**:
```bash ```bash
pip install vocalinux pip install voculinux
# vosk engine: pip install "vocalinux[vosk]" # vosk engine: pip install "voculinux[vosk]"
``` ```
4. **Run Vocalinux**: 4. **Run Voculinux**:
```bash ```bash
vocalinux voculinux
``` ```
### pipx Installation Steps ### pipx Installation Steps
@@ -311,20 +311,20 @@ sudo zypper install "${PYVER}-gobject" "${PYVER}-gobject-cairo" gtk3 ibus typeli
pipx ensurepath pipx ensurepath
``` ```
3. **Install Vocalinux**: 3. **Install Voculinux**:
```bash ```bash
pipx install vocalinux pipx install voculinux
# vosk engine: pipx install "vocalinux[vosk]" # vosk engine: pipx install "voculinux[vosk]"
``` ```
4. **Run Vocalinux**: 4. **Run Voculinux**:
```bash ```bash
vocalinux voculinux
``` ```
### Why System Packages Are Required ### Why System Packages Are Required
Vocalinux uses GTK3 for its GUI, AppIndicator/Ayatana for the system tray icon, Voculinux uses GTK3 for its GUI, AppIndicator/Ayatana for the system tray icon,
PortAudio for microphone capture, and tools such as `xdotool`, `wtype`, and clipboard utilities (`wl-clipboard`, `xclip`, `xsel`) for PortAudio for microphone capture, and tools such as `xdotool`, `wtype`, and clipboard utilities (`wl-clipboard`, `xclip`, `xsel`) for
text injection and layout-independent Wayland paste fallback. These are system libraries and desktop tools that must be text injection and layout-independent Wayland paste fallback. These are system libraries and desktop tools that must be
installed with your distribution's package manager. Python packages from PyPI installed with your distribution's package manager. Python packages from PyPI
@@ -333,20 +333,20 @@ typelibs, tray support, audio development libraries, or text injection binaries.
### Troubleshooting pip/pipx Installation ### Troubleshooting pip/pipx Installation
If `vocalinux` fails to start: If `voculinux` fails to start:
1. **Check for missing system packages** - The error message will indicate which packages are missing 1. **Check for missing system packages** - The error message will indicate which packages are missing
2. **Verify GI_TYPELIB_PATH** - In rare cases, you may need to set this environment variable: 2. **Verify GI_TYPELIB_PATH** - In rare cases, you may need to set this environment variable:
```bash ```bash
export GI_TYPELIB_PATH=/usr/lib/x86_64-linux-gnu/girepository-1.0 export GI_TYPELIB_PATH=/usr/lib/x86_64-linux-gnu/girepository-1.0
vocalinux voculinux
``` ```
3. **Check package metadata** - Run `pip show vocalinux` or 3. **Check package metadata** - Run `pip show voculinux` or
`pipx runpip vocalinux show vocalinux` to confirm which version was installed. `pipx runpip voculinux show voculinux` to confirm which version was installed.
4. **Check Python dependencies** - Run `pip check` inside the venv, or 4. **Check Python dependencies** - Run `pip check` inside the venv, or
`pipx runpip vocalinux check` for pipx. `pipx runpip voculinux check` for pipx.
If Vocalinux reports that a speech recognition model is missing, open Settings If Voculinux reports that a speech recognition model is missing, open Settings
and download a model, or use the official installer to download the default model and download a model, or use the official installer to download the default model
during setup. during setup.
@@ -362,7 +362,7 @@ This installer automatically detects your distribution and installs all required
## Text Injection Tools ## Text Injection Tools
Vocalinux requires text injection tools to work with X11 or Wayland: Voculinux requires text injection tools to work with X11 or Wayland:
### For X11 ### For X11
- **xdotool**: Required for X11 sessions - **xdotool**: Required for X11 sessions
@@ -370,7 +370,7 @@ Vocalinux requires text injection tools to work with X11 or Wayland:
### For Wayland ### For Wayland
- **IBus**: Recommended for KDE Plasma Wayland and generally the most reliable direct text input path - **IBus**: Recommended for KDE Plasma Wayland and generally the most reliable direct text input path
- **wtype**: Recommended for wlroots-style Wayland compositors such as sway - **wtype**: Recommended for wlroots-style Wayland compositors such as sway
- **ydotool**: Universal alternative that works with both X11 and Wayland. Current distro packages use the per-user `ydotool.service`; source builds can install the system-level `ydotoold.service`. Vocalinux uses clipboard paste with `wl-clipboard`, `xclip`, or `xsel` to avoid non-US layout scrambling. - **ydotool**: Universal alternative that works with both X11 and Wayland. Current distro packages use the per-user `ydotool.service`; source builds can install the system-level `ydotoold.service`. Voculinux uses clipboard paste with `wl-clipboard`, `xclip`, or `xsel` to avoid non-US layout scrambling.
- **xdotool**: May work via XWayland fallback - **xdotool**: May work via XWayland fallback
### Installation by Distribution ### Installation by Distribution
@@ -383,7 +383,7 @@ sudo zypper install xdotool wtype wl-clipboard xclip xsel # openSUSE
``` ```
Ubuntu releases which package `ydotool` install a **user** unit named Ubuntu releases which package `ydotool` install a **user** unit named
`ydotool.service`. Enable it only for the account running Vocalinux: `ydotool.service`. Enable it only for the account running Voculinux:
```bash ```bash
sudo apt install ydotool sudo apt install ydotool
@@ -443,13 +443,13 @@ For unsupported or experimental distributions:
### Autostart Compatibility ### Autostart Compatibility
Vocalinux uses **XDG autostart desktop entries** for "Start on Login". Voculinux uses **XDG autostart desktop entries** for "Start on Login".
- Desktop-session autostart file location: - Desktop-session autostart file location:
- `$XDG_CONFIG_HOME/autostart/vocalinux.desktop`, or - `$XDG_CONFIG_HOME/autostart/voculinux.desktop`, or
- `~/.config/autostart/vocalinux.desktop` (fallback) - `~/.config/autostart/voculinux.desktop` (fallback)
- This launches Vocalinux as a user GUI app after login. - This launches Voculinux as a user GUI app after login.
- Vocalinux does **not** create a `systemd` service/unit for this feature. - Voculinux does **not** create a `systemd` service/unit for this feature.
Practical caveat: Practical caveat:
@@ -500,7 +500,7 @@ When reporting issues, please include:
5. **Error messages or logs:** 5. **Error messages or logs:**
- Any error messages from the installer - Any error messages from the installer
- Output from running `vocalinux-gui` from terminal - Output from running `voculinux-gui` from terminal
- Relevant journal logs if applicable - Relevant journal logs if applicable
6. **Steps to reproduce:** 6. **Steps to reproduce:**
@@ -525,7 +525,7 @@ Use this checklist when testing on a new distribution:
- [ ] Check wrapper scripts exist - [ ] Check wrapper scripts exist
### Post-Install ### Post-Install
- [ ] Launch from terminal: `~/.local/bin/vocalinux-gui` - [ ] Launch from terminal: `~/.local/bin/voculinux-gui`
- [ ] Launch from application menu - [ ] Launch from application menu
- [ ] Verify tray icon appears - [ ] Verify tray icon appears
- [ ] Test keyboard shortcuts (Hold Right Alt by default) - [ ] Test keyboard shortcuts (Hold Right Alt by default)
@@ -543,7 +543,7 @@ Use this checklist when testing on a new distribution:
## Contributing Compatibility Fixes ## Contributing Compatibility Fixes
If you successfully get Vocalinux working on an unsupported or experimental distribution: If you successfully get Voculinux working on an unsupported or experimental distribution:
1. Test thoroughly using the checklist above 1. Test thoroughly using the checklist above
2. Document any distribution-specific requirements 2. Document any distribution-specific requirements
+17 -17
View File
@@ -1,13 +1,13 @@
# Self-hosted Flatpak remote # Self-hosted Flatpak remote
Vocalinux publishes a signed OSTree Flatpak remote on every stable `v*` tag — Voculinux publishes a signed OSTree Flatpak remote on every stable `v*` tag —
the Homebrew-tap analogue to `VocaHQ/homebrew-vocamac` the Homebrew-tap analogue to `VocaHQ/homebrew-vocamac`
([issue #785](https://github.com/VocaHQ/vocalinux/issues/785)). The remote ([issue #785](https://github.com/VocaHQ/vocalinux/issues/785)). The remote
lives in the dedicated repo `VocaHQ/vocalinux-flatpak`, served by GitHub lives in the dedicated repo `VocaHQ/vocalinux-flatpak`, served by GitHub
Pages: Pages:
```bash ```bash
flatpak install https://vocahq.github.io/vocalinux-flatpak/com.vocalinux.Vocalinux.flatpakref flatpak install https://vocahq.github.io/voculinux-flatpak/com.voculinux.Voculinux.flatpakref
flatpak update # picks up each new release flatpak update # picks up each new release
``` ```
@@ -18,8 +18,8 @@ steps:
```bash ```bash
flatpak remote-add --if-not-exists flathub https://dl.flathub.org/repo/flathub.flatpakrepo flatpak remote-add --if-not-exists flathub https://dl.flathub.org/repo/flathub.flatpakrepo
flatpak remote-add --if-not-exists vocahq https://vocahq.github.io/vocalinux-flatpak/vocahq.flatpakrepo flatpak remote-add --if-not-exists vocahq https://vocahq.github.io/voculinux-flatpak/vocahq.flatpakrepo
flatpak install vocahq com.vocalinux.Vocalinux flatpak install vocahq com.voculinux.Voculinux
``` ```
Only the app is ours; the GNOME runtime still comes from Flathub — Only the app is ours; the GNOME runtime still comes from Flathub —
@@ -38,7 +38,7 @@ prerelease tags, which never reach the remote.
3. **Signing key** — OSTree remotes sign the summary and commits with GPG: 3. **Signing key** — OSTree remotes sign the summary and commits with GPG:
```bash ```bash
gpg --quick-gen-key "Vocalinux Flatpak <flatpak@vocahq.com>" ed25519 sign never gpg --quick-gen-key "Voculinux Flatpak <flatpak@vocahq.com>" ed25519 sign never
gpg --list-secret-keys # note the KEYID gpg --list-secret-keys # note the KEYID
gpg --export-secret-keys --armor KEYID # -> FLATPAK_GPG_PRIVATE_KEY gpg --export-secret-keys --armor KEYID # -> FLATPAK_GPG_PRIVATE_KEY
gpg --gen-revoke KEYID > flatpak-remote-revoke.asc # store it somewhere safe gpg --gen-revoke KEYID > flatpak-remote-revoke.asc # store it somewhere safe
@@ -59,12 +59,12 @@ prerelease tags, which never reach the remote.
- `FLATPAK_REMOTE_REPO` — publish target (default - `FLATPAK_REMOTE_REPO` — publish target (default
`VocaHQ/vocalinux-flatpak`) `VocaHQ/vocalinux-flatpak`)
- `FLATPAK_REMOTE_URL` — remote URL baked into the `.flatpakrepo` - `FLATPAK_REMOTE_URL` — remote URL baked into the `.flatpakrepo`
(default `https://vocahq.github.io/vocalinux-flatpak/repo`; set (default `https://vocahq.github.io/voculinux-flatpak/repo`; set
e.g. `https://flatpak.vocalinux.com/repo` for a custom domain) e.g. `https://flatpak.voculinux.com/repo` for a custom domain)
- `FLATPAKREPO_URL` — where users fetch `vocahq.flatpakrepo` (default - `FLATPAKREPO_URL` — where users fetch `vocahq.flatpakrepo` (default
`https://vocahq.github.io/vocalinux-flatpak/vocahq.flatpakrepo`) `https://vocahq.github.io/voculinux-flatpak/vocahq.flatpakrepo`)
- `FLATPAKREF_URL` — where users fetch the app `.flatpakref` (default - `FLATPAKREF_URL` — where users fetch the app `.flatpakref` (default
`https://vocahq.github.io/vocalinux-flatpak/com.vocalinux.Vocalinux.flatpakref`) `https://vocahq.github.io/voculinux-flatpak/com.voculinux.Voculinux.flatpakref`)
6. **Trigger** — push a stable `v*` tag (alpha/beta/rc tags do not publish to 6. **Trigger** — push a stable `v*` tag (alpha/beta/rc tags do not publish to
the remote), or re-run `publish-flatpak-remote` on an existing release run the remote), or re-run `publish-flatpak-remote` on an existing release run
after configuring the secrets. after configuring the secrets.
@@ -77,7 +77,7 @@ an unconfigured remote never fails a release.
`release.yml → publish-flatpak-remote` runs on stable tags only, after the `release.yml → publish-flatpak-remote` runs on stable tags only, after the
GitHub Release exists and the two arch builds finish: GitHub Release exists and the two arch builds finish:
1. Downloads `Vocalinux-<version>-{x86_64,aarch64}.flatpak` from the build 1. Downloads `Voculinux-<version>-{x86_64,aarch64}.flatpak` from the build
jobs. jobs.
2. Probes the remote repo for an existing `gh-pages` branch; only a confirmed 2. Probes the remote repo for an existing `gh-pages` branch; only a confirmed
first publish may skip the history checkout — a transient failure aborts first publish may skip the history checkout — a transient failure aborts
@@ -90,7 +90,7 @@ GitHub Release exists and the two arch builds finish:
4. `flatpak build-update-repo --generate-static-deltas --gpg-sign 4. `flatpak build-update-repo --generate-static-deltas --gpg-sign
--gpg-import` rebuilds the summary, signs it, and generates static deltas. --gpg-import` rebuilds the summary, signs it, and generates static deltas.
5. Regenerates `vocahq.flatpakrepo` (URL + base64 public key), 5. Regenerates `vocahq.flatpakrepo` (URL + base64 public key),
`com.vocalinux.Vocalinux.flatpakref` (the one-command app ref carrying the `com.voculinux.Voculinux.flatpakref` (the one-command app ref carrying the
`RuntimeRepo=` pointer to Flathub), `vocahq.gpg` (the bare public key, for `RuntimeRepo=` pointer to Flathub), `vocahq.gpg` (the bare public key, for
the key-rotation path below), and the index page from the key-rotation path below), and the index page from
`packaging/flatpak/index.html`. `packaging/flatpak/index.html`.
@@ -101,10 +101,10 @@ GitHub Release exists and the two arch builds finish:
```bash ```bash
flatpak remote-add --if-not-exists flathub https://dl.flathub.org/repo/flathub.flatpakrepo flatpak remote-add --if-not-exists flathub https://dl.flathub.org/repo/flathub.flatpakrepo
flatpak remote-add --if-not-exists vocahq https://vocahq.github.io/vocalinux-flatpak/vocahq.flatpakrepo flatpak remote-add --if-not-exists vocahq https://vocahq.github.io/voculinux-flatpak/vocahq.flatpakrepo
flatpak remote-info vocahq com.vocalinux.Vocalinux # signed commit, both arches flatpak remote-info vocahq com.voculinux.Voculinux # signed commit, both arches
flatpak install vocahq com.vocalinux.Vocalinux flatpak install vocahq com.voculinux.Voculinux
flatpak run com.vocalinux.Vocalinux flatpak run com.voculinux.Voculinux
# after the next tag: flatpak update pulls the delta # after the next tag: flatpak update pulls the delta
``` ```
@@ -121,7 +121,7 @@ does nothing at all), and `flatpak update` fails verification until the key
is updated. Point users at the public key published beside the repo: is updated. Point users at the public key published beside the repo:
```bash ```bash
curl -fsSL https://vocahq.github.io/vocalinux-flatpak/vocahq.gpg -o /tmp/vocahq.gpg curl -fsSL https://vocahq.github.io/voculinux-flatpak/vocahq.gpg -o /tmp/vocahq.gpg
flatpak remote-modify --gpg-import=/tmp/vocahq.gpg vocahq flatpak remote-modify --gpg-import=/tmp/vocahq.gpg vocahq
flatpak update flatpak update
``` ```
@@ -133,7 +133,7 @@ their `vocahq` origin and update normally afterwards):
```bash ```bash
flatpak remote-delete --force vocahq # --force: the remote is still in use flatpak remote-delete --force vocahq # --force: the remote is still in use
flatpak remote-add vocahq https://vocahq.github.io/vocalinux-flatpak/vocahq.flatpakrepo flatpak remote-add vocahq https://vocahq.github.io/voculinux-flatpak/vocahq.flatpakrepo
flatpak update flatpak update
``` ```
+9 -9
View File
@@ -1,6 +1,6 @@
# Local VocaGateway from Vocalinux (optional) # Local VocaGateway from Voculinux (optional)
Vocalinux can start a **local** [VocaGateway](https://github.com/VocaHQ/vocagateway) Voculinux can start a **local** [VocaGateway](https://github.com/VocaHQ/vocagateway)
container from **Settings → Speech Model → Advanced**, next to Remote Server. This is an container from **Settings → Speech Model → Advanced**, next to Remote Server. This is an
optional power-user path. It is **not** on-device processing: microphone audio optional power-user path. It is **not** on-device processing: microphone audio
still leaves the desktop client and is transcribed by the gateway process on still leaves the desktop client and is transcribed by the gateway process on
@@ -8,23 +8,23 @@ your machine (or LAN).
The default speech engine remains **whisper.cpp** on-device. Enabling a local The default speech engine remains **whisper.cpp** on-device. Enabling a local
gateway never changes that default by itself. Use **Use this Gateway** only when gateway never changes that default by itself. Use **Use this Gateway** only when
you want Vocalinux to talk to the gateway over the existing Remote API settings you want Voculinux to talk to the gateway over the existing Remote API settings
(`remote_api_*`, endpoint `/v1/audio/transcriptions`). See [HTTP_REMOTE.md](HTTP_REMOTE.md). (`remote_api_*`, endpoint `/v1/audio/transcriptions`). See [HTTP_REMOTE.md](HTTP_REMOTE.md).
## Requirements (Linux MVP) ## Requirements (Linux MVP)
- **podman** (preferred) or **docker**, with a working `compose` plugin (or - **podman** (preferred) or **docker**, with a working `compose` plugin (or
`podman-compose` / `docker-compose`). `podman-compose` / `docker-compose`).
- Vocalinux does **not** bundle a container engine inside the AppImage. - Voculinux does **not** bundle a container engine inside the AppImage.
- **Flatpak**: the sandbox cannot reach the host container socket. Run the - **Flatpak**: the sandbox cannot reach the host container socket. Run the
native package or AppImage, or start VocaGateway on the host yourself and native package or AppImage, or start VocaGateway on the host yourself and
point Remote Server at it. point Remote Server at it.
## What Vocalinux does ## What Voculinux does
1. Pins upstream **VocaGateway `v0.1.0`** (git tag) under your XDG cache and uses 1. Pins upstream **VocaGateway `v0.1.0`** (git tag) under your XDG cache and uses
that checkout's `compose.yaml` with Compose project name `vocagateway`. that checkout's `compose.yaml` with Compose project name `vocagateway`.
2. Writes a private bootstrap token under `~/.config/vocalinux/gateway_embed/` 2. Writes a private bootstrap token under `~/.config/voculinux/gateway_embed/`
(mode `600`) and a matching Compose `.env`. (mode `600`) and a matching Compose `.env`.
3. Runs `compose -f compose.yaml --profile cpu up -d` for the `gateway` service. 3. Runs `compose -f compose.yaml --profile cpu up -d` for the `gateway` service.
The v0.1.0 GitHub release has no published container image assets, so the first The v0.1.0 GitHub release has no published container image assets, so the first
@@ -55,18 +55,18 @@ By default the published host is `127.0.0.1` (desktop-only). Turn on **Allow LAN
access for Phone** to set `VOCAGATEWAY_PUBLISH_HOST=0.0.0.0` and to advertise a access for Phone** to set `VOCAGATEWAY_PUBLISH_HOST=0.0.0.0` and to advertise a
LAN URL in pairing. Open port `8765` in your firewall only on trusted networks. LAN URL in pairing. Open port `8765` in your firewall only on trusted networks.
Never put `127.0.0.1` / `localhost` in the phone QR. If a LAN toggle fails, Never put `127.0.0.1` / `localhost` in the phone QR. If a LAN toggle fails,
compose keeps the previous bind, Vocalinux restores the embed `.env`, and the compose keeps the previous bind, Voculinux restores the embed `.env`, and the
switch snaps back with an error (Stop and Run again, or retry). switch snaps back with an error (Stop and Run again, or retry).
## Tray and Quit ## Tray and Quit
The tray menu offers **Stop local Gateway** whenever compose is actually running The tray menu offers **Stop local Gateway** whenever compose is actually running
(Starting, Live, Pairable, Ready, or Error), including a leftover from a previous (Starting, Live, Pairable, Ready, or Error), including a leftover from a previous
Vocalinux session. Vocalinux does not auto-start the gateway on login in v1. Voculinux session. Voculinux does not auto-start the gateway on login in v1.
**Quit does not stop compose.** The pinned compose file uses `restart: **Quit does not stop compose.** The pinned compose file uses `restart:
unless-stopped`, so a LAN-published gateway on `0.0.0.0:8765` keeps running after unless-stopped`, so a LAN-published gateway on `0.0.0.0:8765` keeps running after
you quit Vocalinux. The next launch restores the Allow LAN switch from the running you quit Voculinux. The next launch restores the Allow LAN switch from the running
compose publish host. Use **Stop local Gateway** in Settings or the tray when you compose publish host. Use **Stop local Gateway** in Settings or the tray when you
want it gone. Do not assume Quit tears it down. want it gone. Do not assume Quit tears it down.
+19 -19
View File
@@ -1,8 +1,8 @@
# Remote HTTP transcription # Remote HTTP transcription
Vocalinux can offload speech recognition to a remote HTTP server instead of running a local model. Use this when you want faster transcription on a more powerful machine, to share one model across clients, or to keep a laptop free of GPU/model overhead. Voculinux can offload speech recognition to a remote HTTP server instead of running a local model. Use this when you want faster transcription on a more powerful machine, to share one model across clients, or to keep a laptop free of GPU/model overhead.
Vocalinux speaks two wire formats out of the box: Voculinux speaks two wire formats out of the box:
- **OpenAI-compatible**: `POST /v1/audio/transcriptions` (e.g. OpenAI, [Speaches](https://github.com/speaches-ai/speaches), LocalAI, FunASR/SenseVoice) - **OpenAI-compatible**: `POST /v1/audio/transcriptions` (e.g. OpenAI, [Speaches](https://github.com/speaches-ai/speaches), LocalAI, FunASR/SenseVoice)
- **whisper.cpp server**: `POST /inference` (the binary shipped with [whisper.cpp](https://github.com/ggerganov/whisper.cpp)) - **whisper.cpp server**: `POST /inference` (the binary shipped with [whisper.cpp](https://github.com/ggerganov/whisper.cpp))
@@ -17,7 +17,7 @@ On Linux you can start an optional local [VocaGateway](https://github.com/VocaHQ
## How it works ## How it works
When the **Remote API** engine is active, Vocalinux: When the **Remote API** engine is active, Voculinux:
1. Captures microphone audio locally (16 kHz, mono, 16-bit PCM). 1. Captures microphone audio locally (16 kHz, mono, 16-bit PCM).
2. On end-of-utterance, packages the buffer as a WAV file in memory. 2. On end-of-utterance, packages the buffer as a WAV file in memory.
@@ -61,7 +61,7 @@ The endpoint is `/v1/audio/transcriptions`.
[FunASR](https://github.com/modelscope/FunASR) can run a local [FunASR](https://github.com/modelscope/FunASR) can run a local
OpenAI-compatible transcription service, which keeps audio on your own machine OpenAI-compatible transcription service, which keeps audio on your own machine
or LAN while letting Vocalinux use SenseVoice or Paraformer-family models: or LAN while letting Voculinux use SenseVoice or Paraformer-family models:
```bash ```bash
pip install 'torch>=2.13.0' torchaudio pip install 'torch>=2.13.0' torchaudio
@@ -70,7 +70,7 @@ funasr-server --device cuda
``` ```
The server listens on `http://localhost:8000` by default and exposes The server listens on `http://localhost:8000` by default and exposes
`POST /v1/audio/transcriptions`. In Vocalinux, set: `POST /v1/audio/transcriptions`. In Voculinux, set:
- **Server URL**: `http://localhost:8000` - **Server URL**: `http://localhost:8000`
- **API Endpoint**: `OpenAI/FunASR (/v1/audio/transcriptions)` - **API Endpoint**: `OpenAI/FunASR (/v1/audio/transcriptions)`
@@ -78,7 +78,7 @@ The server listens on `http://localhost:8000` by default and exposes
Use `--device cpu` for CPU-only testing if your FunASR install supports it. A Use `--device cpu` for CPU-only testing if your FunASR install supports it. A
remote GPU host works too: bind the server to the LAN interface, open the port, remote GPU host works too: bind the server to the LAN interface, open the port,
and point Vocalinux at that host. and point Voculinux at that host.
### Network checklist ### Network checklist
@@ -87,26 +87,26 @@ and point Vocalinux at that host.
- For anything outside a trusted LAN, terminate TLS in front of the server (Caddy, nginx, Traefik) and require an API key. The audio you upload is private speech. - For anything outside a trusted LAN, terminate TLS in front of the server (Caddy, nginx, Traefik) and require an API key. The audio you upload is private speech.
- If you also use the server from a mobile dictation app, terminate TLS even on a LAN. Most Android and iOS apps refuse cleartext HTTP. - If you also use the server from a mobile dictation app, terminate TLS even on a LAN. Most Android and iOS apps refuse cleartext HTTP.
## Client-side setup (Vocalinux) ## Client-side setup (Voculinux)
### Via the Settings dialog ### Via the Settings dialog
The remote engine is a power-user option in the **Speech Engine** settings. The remote engine is a power-user option in the **Speech Engine** settings.
1. Launch Vocalinux and open **Settings → Speech Engine → Remote Server**. 1. Launch Voculinux and open **Settings → Speech Engine → Remote Server**.
2. Toggle **Use remote server** on. This overrides whatever local engine is selected above it. 2. Toggle **Use remote server** on. This overrides whatever local engine is selected above it.
3. Fill in the fields: 3. Fill in the fields:
- **Server URL**: base URL of the server, e.g. `http://192.168.1.100:8080` (no trailing slash needed; one is stripped automatically). - **Server URL**: base URL of the server, e.g. `http://192.168.1.100:8080` (no trailing slash needed; one is stripped automatically).
- **API Key** (optional): sent as `Authorization: Bearer <key>`. Leave blank if your server doesn't require auth. - **API Key** (optional): sent as `Authorization: Bearer <key>`. Leave blank if your server doesn't require auth.
- **API Endpoint**: pick **Whisper.cpp (`/inference`)** or **OpenAI/FunASR (`/v1/audio/transcriptions`)** to match your server. - **API Endpoint**: pick **Whisper.cpp (`/inference`)** or **OpenAI/FunASR (`/v1/audio/transcriptions`)** to match your server.
- **Model**: model identifier sent to OpenAI-compatible servers. Use `whisper-1` for classic Whisper servers, or `sensevoice` for FunASR/SenseVoice. - **Model**: model identifier sent to OpenAI-compatible servers. Use `whisper-1` for classic Whisper servers, or `sensevoice` for FunASR/SenseVoice.
4. Click **Test Connection**. Success means the URL is reachable and credentials (if any) are accepted. A failure here is only a warning; Vocalinux will try again on the first transcription. 4. Click **Test Connection**. Success means the URL is reachable and credentials (if any) are accepted. A failure here is only a warning; Voculinux will try again on the first transcription.
Settings auto-save and re-initialise the engine immediately, so you can start dictating as soon as the test passes. Toggling the switch off restores the local engine selected on the Speech Engine page in Settings. Settings auto-save and re-initialise the engine immediately, so you can start dictating as soon as the test passes. Toggling the switch off restores the local engine selected on the Speech Engine page in Settings.
### Via the config file ### Via the config file
The same options live in `~/.config/vocalinux/config.json`: The same options live in `~/.config/voculinux/config.json`:
```json ```json
{ {
@@ -120,7 +120,7 @@ The same options live in `~/.config/vocalinux/config.json`:
} }
``` ```
Restart Vocalinux after editing the file by hand. Restart Voculinux after editing the file by hand.
FunASR/SenseVoice example: FunASR/SenseVoice example:
@@ -201,7 +201,7 @@ en
``` ```
For OpenAI-compatible FunASR/SenseVoice wrappers, extra metadata is allowed and For OpenAI-compatible FunASR/SenseVoice wrappers, extra metadata is allowed and
ignored by Vocalinux: ignored by Voculinux:
```json ```json
{ {
@@ -214,16 +214,16 @@ ignored by Vocalinux:
} }
``` ```
If `text` is not present, Vocalinux also accepts `transcript`, `transcription`, If `text` is not present, Voculinux also accepts `transcript`, `transcription`,
or a `segments` list with per-segment `text` fields. If a SenseVoice wrapper or a `segments` list with per-segment `text` fields. If a SenseVoice wrapper
returns rich labels inline, for example `<|en|><|NEUTRAL|><|Speech|>hello`, returns rich labels inline, for example `<|en|><|NEUTRAL|><|Speech|>hello`,
Vocalinux strips the leading labels before text injection. Voculinux strips the leading labels before text injection.
Empty utterances should return `{"text": ""}`. HTTP 4xx/5xx responses are surfaced as transcription errors in the Vocalinux log. Empty utterances should return `{"text": ""}`. HTTP 4xx/5xx responses are surfaced as transcription errors in the Voculinux log.
### Language handling ### Language handling
- Vocalinux's UI language `auto` is sent as **no `language` field** (server auto-detects). - Voculinux's UI language `auto` is sent as **no `language` field** (server auto-detects).
- `en-us` is normalised to `en`. - `en-us` is normalised to `en`.
- Anything else is forwarded verbatim. - Anything else is forwarded verbatim.
@@ -273,10 +273,10 @@ compatible response shapes above.
| First transcription is slow | Cold model load on the server | Warm the server before dictating, or increase the client timeout server-side | | First transcription is slow | Cold model load on the server | Warm the server before dictating, or increase the client timeout server-side |
| Garbled text in foreign language | Wrong `language` setting | Set the language explicitly in Settings instead of `auto` | | Garbled text in foreign language | Wrong `language` setting | Set the language explicitly in Settings instead of `auto` |
Logs live at `~/.local/share/vocalinux/vocalinux.log` (or wherever your `XDG_DATA_HOME` points). Look for lines tagged `Remote API` for client-side detail. Logs live at `~/.local/share/voculinux/voculinux.log` (or wherever your `XDG_DATA_HOME` points). Look for lines tagged `Remote API` for client-side detail.
## Security notes ## Security notes
- The API key is stored in plain JSON in `~/.config/vocalinux/config.json`. Treat that file the same way you treat any other secret on disk. - The API key is stored in plain JSON in `~/.config/voculinux/config.json`. Treat that file the same way you treat any other secret on disk.
- Audio is sent as raw WAV. Anyone on the wire can hear it, so use HTTPS outside a fully trusted network. - Audio is sent as raw WAV. Anyone on the wire can hear it, so use HTTPS outside a fully trusted network.
- Vocalinux does **not** validate TLS certificate pins; standard system trust is used. Self-signed certs require importing the CA into your system trust store. - Voculinux does **not** validate TLS certificate pins; standard system trust is used. Self-signed certs require importing the CA into your system trust store.
+55 -55
View File
@@ -1,6 +1,6 @@
# Installation guide # Installation guide
How to install Vocalinux on Linux. Short overview: [project README](../README.md). How to install Voculinux on Linux. Short overview: [project README](../README.md).
| Path | When to use | | Path | When to use |
|------|-------------| |------|-------------|
@@ -75,7 +75,7 @@ Useful flags: `--tag=TAG`, `--skip-models`, `--rebuild-whispercpp`, `--no-rebuil
1. Detects the distribution and installs system packages 1. Detects the distribution and installs system packages
2. Creates a Python venv from the system interpreter with `--system-site-packages` (for distro GTK / PyGObject) 2. Creates a Python venv from the system interpreter with `--system-site-packages` (for distro GTK / PyGObject)
3. Installs Vocalinux and the chosen speech engine 3. Installs Voculinux and the chosen speech engine
4. Optionally installs neural VAD (`onnxruntime`) 4. Optionally installs neural VAD (`onnxruntime`)
5. Downloads a default speech model (checksum-verified) 5. Downloads a default speech model (checksum-verified)
6. Installs icons, desktop entry, and `~/.local/bin` wrappers 6. Installs icons, desktop entry, and `~/.local/bin` wrappers
@@ -85,8 +85,8 @@ Useful flags: `--tag=TAG`, `--skip-models`, `--rebuild-whispercpp`, `--no-rebuil
From [GitHub Releases](https://github.com/VocaHQ/vocalinux/releases): From [GitHub Releases](https://github.com/VocaHQ/vocalinux/releases):
```bash ```bash
chmod +x Vocalinux-*-x86_64.AppImage # or aarch64 chmod +x Voculinux-*-x86_64.AppImage # or aarch64
./Vocalinux-*-x86_64.AppImage ./Voculinux-*-x86_64.AppImage
``` ```
Built against glibc 2.35, so it starts on Debian 12+, Ubuntu 22.04+, Fedora 36+, Arch, and Tumbleweed. Older bases (RHEL 9, Debian 11, Ubuntu 20.04) are below that AppImage floor. The installer and PyPI package still require a distro that ships Python 3.11+, so they are not a workaround for Debian 11 or Ubuntu 20.04. Built against glibc 2.35, so it starts on Debian 12+, Ubuntu 22.04+, Fedora 36+, Arch, and Tumbleweed. Older bases (RHEL 9, Debian 11, Ubuntu 20.04) are below that AppImage floor. The installer and PyPI package still require a distro that ships Python 3.11+, so they are not a workaround for Debian 11 or Ubuntu 20.04.
@@ -99,13 +99,13 @@ From [GitHub Releases](https://github.com/VocaHQ/vocalinux/releases), pick the f
```bash ```bash
# Debian / Ubuntu (amd64; arm64 files are attached too) # Debian / Ubuntu (amd64; arm64 files are attached too)
sudo apt install ./vocalinux_*_amd64.deb sudo apt install ./voculinux_*_amd64.deb
# Fedora (x86_64; aarch64 files are attached too) # Fedora (x86_64; aarch64 files are attached too)
sudo dnf install ./vocalinux-*.x86_64.rpm sudo dnf install ./voculinux-*.x86_64.rpm
``` ```
These are thin packages: Python, GTK, PyGObject and the AppIndicator typelib come from the distribution, and `apt`/`dnf` resolves them. `xdotool`, `wtype`, `ydotool`, the clipboard tools and IBus are `Recommends`, matching the optional feature set of the other paths. Only `pywhispercpp` and `pynput` are vendored inside the package — `pywhispercpp` is packaged by neither distro and `pynput` is missing on Fedora. They land under `/usr/lib/vocalinux/vendor`, on `sys.path` via the `vocalinux` launcher. These are thin packages: Python, GTK, PyGObject and the AppIndicator typelib come from the distribution, and `apt`/`dnf` resolves them. `xdotool`, `wtype`, `ydotool`, the clipboard tools and IBus are `Recommends`, matching the optional feature set of the other paths. Only `pywhispercpp` and `pynput` are vendored inside the package — `pywhispercpp` is packaged by neither distro and `pynput` is missing on Fedora. They land under `/usr/lib/voculinux/vendor`, on `sys.path` via the `voculinux` launcher.
The `.deb` needs a distro that ships Python 3.11+: Debian 12+ and Ubuntu 24.04+ (Ubuntu 22.04 ships 3.10). whisper.cpp runs on CPU in these packages — the PyPI wheel, not the Vulkan build the AppImage carries. There is no auto-update: a new release is a new package install. A PPA and a COPR are not published yet. The `.deb` needs a distro that ships Python 3.11+: Debian 12+ and Ubuntu 24.04+ (Ubuntu 22.04 ships 3.10). whisper.cpp runs on CPU in these packages — the PyPI wheel, not the Vulkan build the AppImage carries. There is no auto-update: a new release is a new package install. A PPA and a COPR are not published yet.
@@ -120,7 +120,7 @@ Joining `input` grants read access to every keyboard and pointer device — X11
## Arch Linux (AUR) ## Arch Linux (AUR)
```bash ```bash
yay -S vocalinux yay -S voculinux
``` ```
See [AUR.md](AUR.md). See [AUR.md](AUR.md).
@@ -130,21 +130,21 @@ See [AUR.md](AUR.md).
Install and auto-update via the self-hosted VocaHQ remote (`flatpak update` picks up each release): Install and auto-update via the self-hosted VocaHQ remote (`flatpak update` picks up each release):
```bash ```bash
flatpak install https://vocahq.github.io/vocalinux-flatpak/com.vocalinux.Vocalinux.flatpakref flatpak install https://vocahq.github.io/voculinux-flatpak/com.voculinux.Voculinux.flatpakref
``` ```
The `.flatpakref` adds the `vocahq` remote and resolves the GNOME runtime through Flathub. Manually instead: The `.flatpakref` adds the `vocahq` remote and resolves the GNOME runtime through Flathub. Manually instead:
```bash ```bash
flatpak remote-add --if-not-exists flathub https://dl.flathub.org/repo/flathub.flatpakrepo flatpak remote-add --if-not-exists flathub https://dl.flathub.org/repo/flathub.flatpakrepo
flatpak remote-add --if-not-exists vocahq https://vocahq.github.io/vocalinux-flatpak/vocahq.flatpakrepo flatpak remote-add --if-not-exists vocahq https://vocahq.github.io/voculinux-flatpak/vocahq.flatpakrepo
flatpak install vocahq com.vocalinux.Vocalinux flatpak install vocahq com.voculinux.Voculinux
``` ```
Or sideload a GitHub Release bundle (`Vocalinux-<version>-x86_64.flatpak` or `-aarch64.flatpak`) after the Flathub GNOME runtime is present — bundles do not auto-update: Or sideload a GitHub Release bundle (`Voculinux-<version>-x86_64.flatpak` or `-aarch64.flatpak`) after the Flathub GNOME runtime is present — bundles do not auto-update:
```bash ```bash
flatpak install --user ./Vocalinux-<version>-x86_64.flatpak flatpak install --user ./Voculinux-<version>-x86_64.flatpak
``` ```
Local build (contributors): Local build (contributors):
@@ -152,8 +152,8 @@ Local build (contributors):
```bash ```bash
flatpak install flathub org.gnome.Platform//50 org.gnome.Sdk//50 flatpak install flathub org.gnome.Platform//50 org.gnome.Sdk//50
flatpak-builder --user --install --force-clean build-dir \ flatpak-builder --user --install --force-clean build-dir \
packaging/flatpak/com.vocalinux.Vocalinux.yml packaging/flatpak/com.voculinux.Voculinux.yml
flatpak run com.vocalinux.Vocalinux flatpak run com.voculinux.Voculinux
``` ```
Whisper.cpp + Vulkan. It is **not on Flathub** (submission [flathub#9368](https://github.com/flathub/flathub/pull/9368) closed 2026-07-23 on policy grounds). Details: [packaging/flatpak/README.md](../packaging/flatpak/README.md). Whisper.cpp + Vulkan. It is **not on Flathub** (submission [flathub#9368](https://github.com/flathub/flathub/pull/9368) closed 2026-07-23 on policy grounds). Details: [packaging/flatpak/README.md](../packaging/flatpak/README.md).
@@ -168,7 +168,7 @@ Desktop shortcuts only toggle dictation, so they cannot support push-to-talk.
## Snap (Ubuntu Snap Store) ## Snap (Ubuntu Snap Store)
Listing: [snapcraft.io/vocalinux](https://snapcraft.io/vocalinux) (issue [#48](https://github.com/VocaHQ/vocalinux/issues/48)). Recipe: `snap/snapcraft.yaml`. Tagged `v*` releases attach `vocalinux_<version>_amd64.snap` to the GitHub Release and try Snap Store `edge` and `candidate` when credentials are set. `stable` is promoted from `candidate` after QA. Listing: [snapcraft.io/voculinux](https://snapcraft.io/voculinux) (issue [#48](https://github.com/VocaHQ/vocalinux/issues/48)). Recipe: `snap/snapcraft.yaml`. Tagged `v*` releases attach `voculinux_<version>_amd64.snap` to the GitHub Release and try Snap Store `edge` and `candidate` when credentials are set. `stable` is promoted from `candidate` after QA.
Build inputs, the PR/release artifact gate, and its verification limits are Build inputs, the PR/release artifact gate, and its verification limits are
documented in [SNAP.md](SNAP.md). documented in [SNAP.md](SNAP.md).
@@ -176,10 +176,10 @@ documented in [SNAP.md](SNAP.md).
Store install, once Canonical lists the revision: Store install, once Canonical lists the revision:
```bash ```bash
sudo snap install vocalinux --edge sudo snap install voculinux --edge
sudo snap connect vocalinux:audio-record # if mic is not auto-connected sudo snap connect voculinux:audio-record # if mic is not auto-connected
sudo snap connect vocalinux:raw-input # global keyboard shortcuts (evdev) sudo snap connect voculinux:raw-input # global keyboard shortcuts (evdev)
sudo snap connect vocalinux:hardware-observe # list keyboards (/proc/bus/input/devices) sudo snap connect voculinux:hardware-observe # list keyboards (/proc/bus/input/devices)
``` ```
Native Wayland typing uses the RemoteDesktop portal: the compositor asks once on first dictation, nothing to connect. The recipe ships **no `uinput` plug** and no ydotool; the Store denied the super-privileged grant ([the request thread](https://forum.snapcraft.io/t/53528)), which held the 0.17.0+ revisions and left **v0.16.2 (rev 7)** on edge. On KDE Plasma, KWin's portal can mistype letter case ([KDE bug 489021](https://bugs.kde.org/show_bug.cgi?id=489021)); set Virtual Keyboard to "IBus Wayland" so dictation types correctly. Native Wayland typing uses the RemoteDesktop portal: the compositor asks once on first dictation, nothing to connect. The recipe ships **no `uinput` plug** and no ydotool; the Store denied the super-privileged grant ([the request thread](https://forum.snapcraft.io/t/53528)), which held the 0.17.0+ revisions and left **v0.16.2 (rev 7)** on edge. On KDE Plasma, KWin's portal can mistype letter case ([KDE bug 489021](https://bugs.kde.org/show_bug.cgi?id=489021)); set Virtual Keyboard to "IBus Wayland" so dictation types correctly.
@@ -189,14 +189,14 @@ Under strict confinement the snap reaches audio through the PulseAudio/PipeWire
GitHub `.snap` files through v0.18.2 declare `uinput`, so KDE users who want ydotool can sideload that build and connect the plug manually: GitHub `.snap` files through v0.18.2 declare `uinput`, so KDE users who want ydotool can sideload that build and connect the plug manually:
```bash ```bash
sudo snap install --dangerous ./vocalinux_0.18.2_amd64.snap sudo snap install --dangerous ./voculinux_0.18.2_amd64.snap
sudo snap connect vocalinux:audio-record sudo snap connect voculinux:audio-record
sudo snap connect vocalinux:raw-input sudo snap connect voculinux:raw-input
sudo snap connect vocalinux:hardware-observe sudo snap connect voculinux:hardware-observe
sudo snap connect vocalinux:uinput # only in the v0.18.x sideload; the recipe no longer ships it sudo snap connect voculinux:uinput # only in the v0.18.x sideload; the recipe no longer ships it
``` ```
`--dangerous` is required because this file is not a Store revision and will not refresh from the Store; once the Store carries a newer revision, `sudo snap install vocalinux --edge` (or `snap refresh`) replaces it. `--dangerous` is required because this file is not a Store revision and will not refresh from the Store; once the Store carries a newer revision, `sudo snap install voculinux --edge` (or `snap refresh`) replaces it.
## Nix / NixOS ## Nix / NixOS
@@ -207,13 +207,13 @@ nix run github:VocaHQ/vocalinux # try it
nix profile install github:VocaHQ/vocalinux nix profile install github:VocaHQ/vocalinux
``` ```
On NixOS, `vocalinux.nixosModules.vocalinux` provides `programs.vocalinux.enable`, which installs the package and enables `hardware.uinput`. Add each dictating user to `input` and `uinput` for Wayland shortcuts and ydotool typing. Home Manager users get `vocalinux.homeManagerModules.vocalinux` with the same `programs.vocalinux` options — device permissions stay on the system side. Usage snippets and scope notes: [packaging/nix/README.md](../packaging/nix/README.md). On NixOS, `voculinux.nixosModules.voculinux` provides `programs.voculinux.enable`, which installs the package and enables `hardware.uinput`. Add each dictating user to `input` and `uinput` for Wayland shortcuts and ydotool typing. Home Manager users get `voculinux.homeManagerModules.voculinux` with the same `programs.voculinux` options — device permissions stay on the system side. Usage snippets and scope notes: [packaging/nix/README.md](../packaging/nix/README.md).
## From source ## From source
```bash ```bash
git clone https://github.com/VocaHQ/vocalinux.git git clone https://github.com/VocaHQ/vocalinux.git
cd vocalinux cd voculinux
./install.sh ./install.sh
``` ```
@@ -228,18 +228,18 @@ cd vocalinux
| **Disk** | ~200MB with default whisper.cpp model | | **Disk** | ~200MB with default whisper.cpp model |
| **RAM** | 4GB minimum; 8GB comfortable | | **RAM** | 4GB minimum; 8GB comfortable |
The distro must ship Python 3.11+ because Vocalinux uses distro PyGObject (`python3-gi`). Ubuntu 22.04 (Python 3.10) and Debian 11 (3.9) are below that floor. Distro matrix: [DISTRO_COMPATIBILITY.md](DISTRO_COMPATIBILITY.md). The distro must ship Python 3.11+ because Voculinux uses distro PyGObject (`python3-gi`). Ubuntu 22.04 (Python 3.10) and Debian 11 (3.9) are below that floor. Distro matrix: [DISTRO_COMPATIBILITY.md](DISTRO_COMPATIBILITY.md).
### GPU (optional) ### GPU (optional)
whisper.cpp uses Vulkan when available (AMD, Intel, NVIDIA). Check with `vulkaninfo --summary`. whisper.cpp uses Vulkan when available (AMD, Intel, NVIDIA). Check with `vulkaninfo --summary`.
## Running Vocalinux ## Running Voculinux
```bash ```bash
vocalinux # if ~/.local/bin is on PATH voculinux # if ~/.local/bin is on PATH
~/.local/share/vocalinux/venv/bin/vocalinux # direct ~/.local/share/voculinux/venv/bin/voculinux # direct
source venv/bin/activate && vocalinux # source checkout source venv/bin/activate && voculinux # source checkout
``` ```
Or launch from the application menu. Or launch from the application menu.
@@ -247,19 +247,19 @@ Or launch from the application menu.
### CLI ### CLI
```bash ```bash
vocalinux --help voculinux --help
vocalinux --version voculinux --version
vocalinux --debug voculinux --debug
vocalinux --engine whisper_cpp voculinux --engine whisper_cpp
vocalinux --engine whisper voculinux --engine whisper
vocalinux --engine vosk voculinux --engine vosk
vocalinux --engine parakeet voculinux --engine parakeet
vocalinux --engine remote_api voculinux --engine remote_api
vocalinux --model tiny voculinux --model tiny
vocalinux --model medium.en-q5_0 voculinux --model medium.en-q5_0
vocalinux --model large-v3-turbo voculinux --model large-v3-turbo
vocalinux --wayland voculinux --wayland
vocalinux --start-minimized voculinux --start-minimized
``` ```
## Autostart ## Autostart
@@ -270,8 +270,8 @@ vocalinux --start-minimized
| Directory | Purpose | | Directory | Purpose |
|-----------|---------| |-----------|---------|
| `~/.config/vocalinux/` | Configuration | | `~/.config/voculinux/` | Configuration |
| `~/.local/share/vocalinux/` | Data and speech models | | `~/.local/share/voculinux/` | Data and speech models |
| `~/.local/share/applications/` | Desktop entry | | `~/.local/share/applications/` | Desktop entry |
| `~/.local/share/icons/hicolor/scalable/apps/` | Icons | | `~/.local/share/icons/hicolor/scalable/apps/` | Icons |
@@ -296,11 +296,11 @@ Reuse an existing `pywhispercpp` build:
./install.sh --auto --rebuild-whispercpp # force rebuild ./install.sh --auto --rebuild-whispercpp # force rebuild
``` ```
Switch engines in Settings → Speech Model (Advanced), or edit `~/.config/vocalinux/config.json` (`engine`: `whisper_cpp` | `whisper` | `vosk` | `parakeet` | `remote_api`). Switch engines in Settings → Speech Model (Advanced), or edit `~/.config/voculinux/config.json` (`engine`: `whisper_cpp` | `whisper` | `vosk` | `parakeet` | `remote_api`).
## Uninstall ## Uninstall
From a source checkout, `./uninstall.sh` removes the install and that checkout's `venv/`, `build/`, `dist/`, and Python bytecode. `--keep-config` and `--keep-data` leave `~/.config/vocalinux` and `~/.local/share/vocalinux` in place. From a source checkout, `./uninstall.sh` removes the install and that checkout's `venv/`, `build/`, `dist/`, and Python bytecode. `--keep-config` and `--keep-data` leave `~/.config/voculinux` and `~/.local/share/voculinux` in place.
```bash ```bash
./uninstall.sh ./uninstall.sh
@@ -315,15 +315,15 @@ curl -fsSL https://raw.githubusercontent.com/VocaHQ/vocalinux/main/uninstall.sh
bash /tmp/vul.sh bash /tmp/vul.sh
``` ```
That removes Vocalinux from your home directory (config, data, launchers, desktop entry, icons). It does not delete `venv/`, `build/`, `dist/`, or `.pyc` files in the directory where you ran it. That removes Voculinux from your home directory (config, data, launchers, desktop entry, icons). It does not delete `venv/`, `build/`, `dist/`, or `.pyc` files in the directory where you ran it.
From the source checkout, the equivalent by hand: From the source checkout, the equivalent by hand:
```bash ```bash
rm -rf venv ~/.config/vocalinux ~/.local/share/vocalinux rm -rf venv ~/.config/voculinux ~/.local/share/voculinux
rm -f activate-vocalinux.sh rm -f activate-voculinux.sh
rm -f ~/.local/share/applications/vocalinux.desktop rm -f ~/.local/share/applications/voculinux.desktop
rm -f ~/.local/share/icons/hicolor/scalable/apps/vocalinux*.svg rm -f ~/.local/share/icons/hicolor/scalable/apps/voculinux*.svg
gtk-update-icon-cache -f -t ~/.local/share/icons/hicolor gtk-update-icon-cache -f -t ~/.local/share/icons/hicolor
``` ```
+19 -19
View File
@@ -12,7 +12,7 @@ Related: [DISTRO_COMPATIBILITY.md](DISTRO_COMPATIBILITY.md), [TROUBLESHOOTING.md
## System packages by distribution ## System packages by distribution
Install desktop/system dependencies **before** creating a venv or running `pip install vocalinux`. Pip cannot install GTK typelibs, AppIndicator, PortAudio, or text-injection tools. Install desktop/system dependencies **before** creating a venv or running `pip install voculinux`. Pip cannot install GTK typelibs, AppIndicator, PortAudio, or text-injection tools.
Debian 11 and earlier are unsupported; the Debian instructions start at Debian Debian 11 and earlier are unsupported; the Debian instructions start at Debian
12, which is the oldest release with the required Python 3.11. 12, which is the oldest release with the required Python 3.11.
@@ -84,7 +84,7 @@ sudo dnf install -y \
### Arch Linux ### Arch Linux
Prefer the AUR package (`yay -S vocalinux`). Manual packages: Prefer the AUR package (`yay -S voculinux`). Manual packages:
```bash ```bash
sudo pacman -S --needed \ sudo pacman -S --needed \
@@ -125,7 +125,7 @@ Gentoo, Alpine, Void, Solus, Mageia: [DISTRO_COMPATIBILITY.md](DISTRO_COMPATIBIL
Use the **system** Python (`/usr/bin/python3`). Distro PyGObject (`gi`) is compiled for that interpreter only, so a venv from pyenv/conda/uv cannot import it even with `--system-site-packages`. Use the **system** Python (`/usr/bin/python3`). Distro PyGObject (`gi`) is compiled for that interpreter only, so a venv from pyenv/conda/uv cannot import it even with `--system-site-packages`.
```bash ```bash
cd vocalinux cd voculinux
/usr/bin/python3 -m venv venv --system-site-packages /usr/bin/python3 -m venv venv --system-site-packages
source venv/bin/activate source venv/bin/activate
pip install --upgrade pip setuptools wheel pip install --upgrade pip setuptools wheel
@@ -141,14 +141,14 @@ pip install -e ".[dev,vad]" # development
### Desktop integration ### Desktop integration
```bash ```bash
mkdir -p ~/.config/vocalinux \ mkdir -p ~/.config/voculinux \
~/.local/share/vocalinux/models \ ~/.local/share/voculinux/models \
~/.local/share/applications \ ~/.local/share/applications \
~/.local/share/icons/hicolor/scalable/apps ~/.local/share/icons/hicolor/scalable/apps
cp vocalinux.desktop ~/.local/share/applications/ cp voculinux.desktop ~/.local/share/applications/
VENV_PATH=$(realpath venv/bin/vocalinux) VENV_PATH=$(realpath venv/bin/voculinux)
sed -i "s|^Exec=vocalinux|Exec=$VENV_PATH|" ~/.local/share/applications/vocalinux.desktop sed -i "s|^Exec=voculinux|Exec=$VENV_PATH|" ~/.local/share/applications/voculinux.desktop
cp resources/icons/scalable/*.svg ~/.local/share/icons/hicolor/scalable/apps/ cp resources/icons/scalable/*.svg ~/.local/share/icons/hicolor/scalable/apps/
gtk-update-icon-cache -f -t ~/.local/share/icons/hicolor gtk-update-icon-cache -f -t ~/.local/share/icons/hicolor
@@ -159,14 +159,14 @@ gtk-update-icon-cache -f -t ~/.local/share/icons/hicolor
Same system packages as above, then: Same system packages as above, then:
```bash ```bash
python3 -m venv ~/.local/share/vocalinux-pypi/venv --system-site-packages python3 -m venv ~/.local/share/voculinux-pypi/venv --system-site-packages
source ~/.local/share/vocalinux-pypi/venv/bin/activate source ~/.local/share/voculinux-pypi/venv/bin/activate
pip install --upgrade pip setuptools wheel pip install --upgrade pip setuptools wheel
pip install vocalinux pip install voculinux
# vosk engine: pip install "vocalinux[vosk]" # vosk engine: pip install "voculinux[vosk]"
# faster-whisper: pip install "vocalinux[faster_whisper]" # faster-whisper: pip install "voculinux[faster_whisper]"
# parakeet engine: pip install "vocalinux[parakeet]" # parakeet engine: pip install "voculinux[parakeet]"
vocalinux voculinux
``` ```
`--system-site-packages` lets the venv use distro GTK bindings (`python3-gi`). Building PyGObject from pip is often painful on Ubuntu/Debian. `--system-site-packages` lets the venv use distro GTK bindings (`python3-gi`). Building PyGObject from pip is often painful on Ubuntu/Debian.
@@ -177,18 +177,18 @@ If the app starts but no speech model is present, open Settings and download a m
```bash ```bash
# After system packages are installed: # After system packages are installed:
pipx install vocalinux pipx install voculinux
vocalinux voculinux
``` ```
pipx still cannot install system desktop libraries. Install those first with your package manager. pipx still cannot install system desktop libraries. Install those first with your package manager.
## Why system packages are required ## Why system packages are required
Vocalinux uses GTK3, AppIndicator/Ayatana, PortAudio, and tools such as `xdotool`, `wtype`, and clipboard utilities. Those come from the distro. PyPI only supplies Python packages and wheels. Voculinux uses GTK3, AppIndicator/Ayatana, PortAudio, and tools such as `xdotool`, `wtype`, and clipboard utilities. Those come from the distro. PyPI only supplies Python packages and wheels.
## After install ## After install
- Run: [INSTALL.md](INSTALL.md#running-vocalinux) - Run: [INSTALL.md](INSTALL.md#running-voculinux)
- Engines and models: [USER_GUIDE.md](USER_GUIDE.md) - Engines and models: [USER_GUIDE.md](USER_GUIDE.md)
- Failures: [TROUBLESHOOTING.md](TROUBLESHOOTING.md) - Failures: [TROUBLESHOOTING.md](TROUBLESHOOTING.md)
+4 -4
View File
@@ -1,6 +1,6 @@
# Choosing a speech model # Choosing a speech model
Vocalinux never downloads a speech model on its own. Changing the model pickers in Voculinux never downloads a speech model on its own. Changing the model pickers in
Settings → Speech Model only selects a model. If that model is not on disk yet, the Settings → Speech Model only selects a model. If that model is not on disk yet, the
info card shows its download size and a **Download** button, and the transfer starts info card shows its download size and a **Download** button, and the transfer starts
only after you confirm it. Cancelling the download, or declining the confirmation, only after you confirm it. Cancelling the download, or declining the confirmation,
@@ -43,7 +43,7 @@ more accurate for English.
| Large v3 Turbo | 1.6 GB | 874 MB | 574 MB (q5_0) | 809M | | Large v3 Turbo | 1.6 GB | 874 MB | 574 MB (q5_0) | 809M |
| Large (v3) | 2.9 GB | | 1.1 GB (q5_0) | 1550M | | Large (v3) | 2.9 GB | | 1.1 GB (q5_0) | 1550M |
Sizes are the catalog values Vocalinux shows before a download; the file on disk can Sizes are the catalog values Voculinux shows before a download; the file on disk can
differ slightly. differ slightly.
If you already have a quantized model of the size you want (for example Small Q8_0), If you already have a quantized model of the size you want (for example Small Q8_0),
@@ -57,5 +57,5 @@ there is no reason to download the full model of the same size.
- **Parakeet**: one model of about 640 MB. - **Parakeet**: one model of about 640 MB.
- **Remote API**: transcription runs on your server, nothing is downloaded locally. - **Remote API**: transcription runs on your server, nothing is downloaded locally.
Models are stored under `~/.local/share/vocalinux/models/` by default. Deleting a file there Models are stored under `~/.local/share/voculinux/models/` by default. Deleting a file there
removes the model; Vocalinux will show it as not downloaded again. removes the model; Voculinux will show it as not downloaded again.
+3 -3
View File
@@ -19,7 +19,7 @@ no CUDA toolkit installed (Vulkan then CPU backend order).
`small.en` (floor, by language). `small.en` (floor, by language).
- Isolated XDG directories so the pass never touches a live install: - Isolated XDG directories so the pass never touches a live install:
point `XDG_CONFIG_HOME`, `XDG_DATA_HOME`, `XDG_STATE_HOME` at scratch point `XDG_CONFIG_HOME`, `XDG_DATA_HOME`, `XDG_STATE_HOME` at scratch
roots and link the verified models under `<data>/vocalinux/models/`. roots and link the verified models under `<data>/voculinux/models/`.
- Seed `config.json` from the app's own `DEFAULT_CONFIG`: engine - Seed `config.json` from the app's own `DEFAULT_CONFIG`: engine
`whisper_cpp`, the rung under test, keep-alive enabled with the `whisper_cpp`, the rung under test, keep-alive enabled with the
minimum 60 s timeout, `first_run` false, update checks off. minimum 60 s timeout, `first_run` false, update checks off.
@@ -36,8 +36,8 @@ with the installed build's interpreter.
`reason=idle_keepalive`). RSS does not reflect the unload (freed `reason=idle_keepalive`). RSS does not reflect the unload (freed
native pages stay mapped); VRAM-used is the state signal. native pages stay mapped); VRAM-used is the state signal.
2. **Wake latency, toggle to capture start.** Drive starts over D-Bus 2. **Wake latency, toggle to capture start.** Drive starts over D-Bus
(`busctl --user call com.vocalinux.Vocalinux (`busctl --user call com.voculinux.Voculinux
/com/vocalinux/Vocalinux com.vocalinux.Vocalinux Toggle`; the /com/voculinux/Voculinux com.voculinux.Voculinux Toggle`; the
`--toggle` client also works but spawns the full app stack). `--toggle` client also works but spawns the full app stack).
Parse repeated runs with `parse-log --log`: each `Toggle requested Parse repeated runs with `parse-log --log`: each `Toggle requested
in state IDLE` pairs with the next `Opened capture stream`. Report in state IDLE` pairs with the next `Opened capture stream`. Report
+2 -2
View File
@@ -1,6 +1,6 @@
# Documentation # Documentation
User and maintainer documentation for Vocalinux. User and maintainer documentation for Voculinux.
| Document | Audience | Description | | Document | Audience | Description |
|----------|----------|-------------| |----------|----------|-------------|
@@ -32,4 +32,4 @@ Project root:
Website product and design notes live under [`web/`](../web/) (`PRODUCT.md`, `DESIGN.md`, `README.md`). Flatpak packaging: [`packaging/flatpak/README.md`](../packaging/flatpak/README.md). Website product and design notes live under [`web/`](../web/) (`PRODUCT.md`, `DESIGN.md`, `README.md`). Flatpak packaging: [`packaging/flatpak/README.md`](../packaging/flatpak/README.md).
Website: [vocalinux.com](https://vocalinux.com) Website: [voculinux.com](https://voculinux.com)
+17 -17
View File
@@ -1,10 +1,10 @@
# Vocalinux Release Process # Voculinux Release Process
This document outlines the step-by-step process for creating a new release of Vocalinux. This document outlines the step-by-step process for creating a new release of Voculinux.
## Overview ## Overview
Vocalinux uses [Semantic Versioning](https://semver.org/) with pre-release suffixes: Voculinux uses [Semantic Versioning](https://semver.org/) with pre-release suffixes:
- **Alpha**: Early testing phase (e.g., `0.4.1-alpha`) - **Alpha**: Early testing phase (e.g., `0.4.1-alpha`)
- **Beta**: Feature-complete, testing phase (e.g., `0.5.0-beta`) - **Beta**: Feature-complete, testing phase (e.g., `0.5.0-beta`)
- **RC** (Release Candidate): Final testing before stable (e.g., `0.5.0-rc1`) - **RC** (Release Candidate): Final testing before stable (e.g., `0.5.0-rc1`)
@@ -19,13 +19,13 @@ in the corresponding job.
`publish-pypi` authenticates with a short-lived OIDC token instead of a stored API `publish-pypi` authenticates with a short-lived OIDC token instead of a stored API
token, so there is no `PYPI_API_TOKEN` secret to leak or rotate. It only works once token, so there is no `PYPI_API_TOKEN` secret to leak or rotate. It only works once
PyPI knows about this workflow. On https://pypi.org/manage/project/vocalinux/settings/publishing/ PyPI knows about this workflow. On https://pypi.org/manage/project/voculinux/settings/publishing/
add a GitHub publisher: add a GitHub publisher:
| field | value | | field | value |
|---|---| |---|---|
| Owner | `VocaHQ` | | Owner | `VocaHQ` |
| Repository | `vocalinux` | | Repository | `voculinux` |
| Workflow name | `release.yml` | | Workflow name | `release.yml` |
| Environment | `pypi` | | Environment | `pypi` |
@@ -144,7 +144,7 @@ AUR push, but does not unpublish GitHub/PyPI artifacts already released. It is
separate from PR/local `--local-unattested` fixtures, which exercise the same separate from PR/local `--local-unattested` fixtures, which exercise the same
preparation/build path but are never used for publication. See [AUR.md](AUR.md) preparation/build path but are never used for publication. See [AUR.md](AUR.md)
for the trust model and the existing AUR-provider smoke-test limitations. for the trust model and the existing AUR-provider smoke-test limitations.
`vocalinux-bin` publication waits only for release creation and both AppImages; `voculinux-bin` publication waits only for release creation and both AppImages;
source-gate failures or unrelated checksum producers do not block it. source-gate failures or unrelated checksum producers do not block it.
## Detailed Release Steps ## Detailed Release Steps
@@ -199,9 +199,9 @@ When shipping bug fixes only (PATCH bump, same MINOR):
- **README / `docs/UPDATE.md` "What's New"** keeps the **minor-series feature list** (everything introduced in that 0.Y line) and adds a short **Bug fixes in this patch** subsection for the delta only. Do not make the marketing highlight table look featureless because this tag only contains fixes. - **README / `docs/UPDATE.md` "What's New"** keeps the **minor-series feature list** (everything introduced in that 0.Y line) and adds a short **Bug fixes in this patch** subsection for the delta only. Do not make the marketing highlight table look featureless because this tag only contains fixes.
- **Website changelog** (`web/src/app/changelog/page.tsx`) may list only the delta for the new entry; older entries stay historical. - **Website changelog** (`web/src/app/changelog/page.tsx`) may list only the delta for the new entry; older entries stay historical.
- **AppStream** (`packaging/flatpak/com.vocalinux.Vocalinux.metainfo.xml`): add a short `<release>` note for the patch delta only; leave the long product feature `<ul>` as series/product-level. - **AppStream** (`packaging/flatpak/com.voculinux.Voculinux.metainfo.xml`): add a short `<release>` note for the patch delta only; leave the long product feature `<ul>` as series/product-level.
- **SECURITY.md** supported versions table usually stays on the minor line (`0.14.x`) without a row per patch. - **SECURITY.md** supported versions table usually stays on the minor line (`0.14.x`) without a row per patch.
- **AUR source PKGBUILD**: the release gate prepares `pkgver`, `_tag`, the normalized `_version`, and the canonical sdist digest, then tests that exact file. Do not introduce `SKIP` or rehash it during publication. The separate `vocalinux-bin` path still pins both AppImage digests before its own publication. - **AUR source PKGBUILD**: the release gate prepares `pkgver`, `_tag`, the normalized `_version`, and the canonical sdist digest, then tests that exact file. Do not introduce `SKIP` or rehash it during publication. The separate `voculinux-bin` path still pins both AppImage digests before its own publication.
- **Dates**: patch release dates must not predate the previous release in changelog / AppStream / version history. - **Dates**: patch release dates must not predate the previous release in changelog / AppStream / version history.
### GitHub Release Notes Writing Rules ### GitHub Release Notes Writing Rules
@@ -216,7 +216,7 @@ Use these rules for every GitHub Release body (and for the draft pasted into the
#### Required structure #### Required structure
1. `# Vocalinux vX.Y.Z` title 1. `# Voculinux vX.Y.Z` title
2. One to three plain sentences: what this release is for (no hype) 2. One to three plain sentences: what this release is for (no hype)
3. `## Highlights` - markdown table, about 4-8 rows 3. `## Highlights` - markdown table, about 4-8 rows
4. `## New Features` - bullets with PR + author; include issue closes when real 4. `## New Features` - bullets with PR + author; include issue closes when real
@@ -334,7 +334,7 @@ Add new release entry to the `releases` array at the top of the file.
```json ```json
{ {
"name": "vocalinux-website", "name": "voculinux-website",
"version": "0.5.0" "version": "0.5.0"
} }
``` ```
@@ -369,7 +369,7 @@ bash -n install.sh
### Upstream Fix Intake (Cherry-Pick Policy) ### Upstream Fix Intake (Cherry-Pick Policy)
Upstream Vocalinux fixes enter this fork only as hand cherry-picks: taken at Upstream Voculinux fixes enter this fork only as hand cherry-picks: taken at
defined sync points, never as continuous tracking and never as wholesale defined sync points, never as continuous tracking and never as wholesale
merges. Like every other change, each port goes through the pull request flow merges. Like every other change, each port goes through the pull request flow
with the standard test, lint, and type gates. Never land one as a direct or with the standard test, lint, and type gates. Never land one as a direct or
@@ -390,7 +390,7 @@ worktree per branch, every change via a pull request, never commit on `main`
renames on top. Conflicts concentrate at the renamed user-visible surfaces renames on top. Conflicts concentrate at the renamed user-visible surfaces
(application name, entry points, desktop and D-Bus identity, configuration (application name, entry points, desktop and D-Bus identity, configuration
and data directories, package, website) precisely because the internal and data directories, package, website) precisely because the internal
Python module name stays `vocalinux` as a retained internal name — that Python module name stays `voculinux` as a retained internal name — that
split is what keeps hand cherry-picks nearly conflict-free everywhere else. split is what keeps hand cherry-picks nearly conflict-free everywhere else.
Upstream URLs and changelog history stay untouched for provenance. Upstream URLs and changelog history stay untouched for provenance.
- **What qualifies.** Fixes and documentation that changes user instructions - **What qualifies.** Fixes and documentation that changes user instructions
@@ -448,7 +448,7 @@ git checkout main
git pull origin main git pull origin main
# Create annotated tag # Create annotated tag
git tag -a v0.5.0-beta -m "Vocalinux Beta Release 0.5.0 git tag -a v0.5.0-beta -m "Voculinux Beta Release 0.5.0
Key changes since 0.4.1-alpha: Key changes since 0.4.1-alpha:
- Feature 1 - Feature 1
@@ -481,7 +481,7 @@ After pushing the tag, the GitHub Actions workflow will automatically:
(stable tags only, once the GitHub Release exists, when (stable tags only, once the GitHub Release exists, when
`FLATPAK_GPG_PRIVATE_KEY` + `FLATPAK_REPO_TOKEN` are configured — see `FLATPAK_GPG_PRIVATE_KEY` + `FLATPAK_REPO_TOKEN` are configured — see
docs/FLATPAK_REMOTE.md) docs/FLATPAK_REMOTE.md)
8. Deploy the website to vocalinux.com 8. Deploy the website to voculinux.com
9. Build the amd64 snap from locked Python exports and run the installed-artifact 9. Build the amd64 snap from locked Python exports and run the installed-artifact
gate described in [SNAP.md](SNAP.md), then attach it to the GitHub Release and gate described in [SNAP.md](SNAP.md), then attach it to the GitHub Release and
try Snap Store try Snap Store
@@ -511,9 +511,9 @@ Monitor at: https://github.com/VocaHQ/vocalinux/actions
- [ ] Verify provenance: `gh attestation verify <artifact> --repo VocaHQ/vocalinux` - [ ] Verify provenance: `gh attestation verify <artifact> --repo VocaHQ/vocalinux`
- [ ] Verify PyPI package was published (if applicable), and that its wheel sha256 - [ ] Verify PyPI package was published (if applicable), and that its wheel sha256
matches the line for that wheel in `SHA256SUMS` matches the line for that wheel in `SHA256SUMS`
- [ ] Verify website was deployed (check vocalinux.com) - [ ] Verify website was deployed (check voculinux.com)
- [ ] If the Flatpak remote is configured, verify it picked up the tag: - [ ] If the Flatpak remote is configured, verify it picked up the tag:
`flatpak remote-info vocahq com.vocalinux.Vocalinux` (docs/FLATPAK_REMOTE.md) `flatpak remote-info vocahq com.voculinux.Voculinux` (docs/FLATPAK_REMOTE.md)
- [ ] Announce on social media/communities - [ ] Announce on social media/communities
- [ ] Update any pinned issues or discussions - [ ] Update any pinned issues or discussions
@@ -562,7 +562,7 @@ git push origin v0.5.1-beta
| 0.14.1 | 2026-07-17 | Stable | Flatpak packaging, AUR package, layout-aware hotkeys, installer/text-injection fixes | | 0.14.1 | 2026-07-17 | Stable | Flatpak packaging, AUR package, layout-aware hotkeys, installer/text-injection fixes |
| 0.14.2 | 2026-07-17 | Stable | IBus engine launch + FocusIn gate; settings tabs scroll to fit monitor | | 0.14.2 | 2026-07-17 | Stable | IBus engine launch + FocusIn gate; settings tabs scroll to fit monitor |
| 0.15.0 | 2026-07-28 | Stable | Searchable settings + sidebar dictation footer, AppImage, expanded languages, dictation polish, auto-pause/keepalive, Vulkan device selection, ibus-wayland, Bluetooth mic + shortcut UI fixes | | 0.15.0 | 2026-07-28 | Stable | Searchable settings + sidebar dictation footer, AppImage, expanded languages, dictation polish, auto-pause/keepalive, Vulkan device selection, ibus-wayland, Bluetooth mic + shortcut UI fixes |
| 0.16.0 | 2026-08-23 | Stable | Update checker + tray notify, Right Alt PTT default (new installs), searchable language list, delete unused models, Voca tone picker (family preview WAVs), About page, AGPL-3.0, family mic icons, installer/just/uv pinning, Test Dictation missing-model message, IBus/audio/clipboard/GPU/AppImage reliability, vocalinux.com family workbench restyle | | 0.16.0 | 2026-08-23 | Stable | Update checker + tray notify, Right Alt PTT default (new installs), searchable language list, delete unused models, Voca tone picker (family preview WAVs), About page, AGPL-3.0, family mic icons, installer/just/uv pinning, Test Dictation missing-model message, IBus/audio/clipboard/GPU/AppImage reliability, voculinux.com family workbench restyle |
| 0.17.0 | 2026-09-16 | Stable | Faster Whisper + Parakeet engines, Speech Model simple setup, first-run system language, Snap with ydotool/uinput, Flatpak bundles on the GitHub Release, XWayland/layout paste and settings/audio fixes | | 0.17.0 | 2026-09-16 | Stable | Faster Whisper + Parakeet engines, Speech Model simple setup, first-run system language, Snap with ydotool/uinput, Flatpak bundles on the GitHub Release, XWayland/layout paste and settings/audio fixes |
| 0.18.0 | 2026-10-02 | Stable | Dictation Pad, evdev hotkey suppression, PipeWire capture + system audio, RemoteDesktop portal injection, per-language shortcuts + tray history, TinyDiarize file transcription, D-Bus activation, custom dictionary, postprocessing hook, floating overlay, audio ducking, AUR -bin + self-hosted Flatpak remote + snap-promote | | 0.18.0 | 2026-10-02 | Stable | Dictation Pad, evdev hotkey suppression, PipeWire capture + system audio, RemoteDesktop portal injection, per-language shortcuts + tray history, TinyDiarize file transcription, D-Bus activation, custom dictionary, postprocessing hook, floating overlay, audio ducking, AUR -bin + self-hosted Flatpak remote + snap-promote |
| 0.18.1 | 2026-10-08 | Stable | Settings Proxy page, opt-in JSONL transcript persistence, .deb/.rpm release packages, slimmer tray menu, dictation-pad Wayland ghost fix, evdev keyboard-discovery tightening, KWin 6 case fix, staged model picker | | 0.18.1 | 2026-10-08 | Stable | Settings Proxy page, opt-in JSONL transcript persistence, .deb/.rpm release packages, slimmer tray menu, dictation-pad Wayland ghost fix, evdev keyboard-discovery tightening, KWin 6 case fix, staged model picker |
+4 -4
View File
@@ -30,7 +30,7 @@ packages over incidental copies in the GNOME platform.
The build audits build-tool and installed runtime versions against the active The build audits build-tool and installed runtime versions against the active
export pins and records expected runtime versions and both export digests in export pins and records expected runtime versions and both export digests in
`usr/share/vocalinux/python-inputs.json` inside the snap. Missing or wrong-version `usr/share/voculinux/python-inputs.json` inside the snap. Missing or wrong-version
build tools fail the build. For the packaged runtime, extra and duplicate build tools fail the build. For the packaged runtime, extra and duplicate
Python distributions are rejected as well. Python distributions are rejected as well.
@@ -49,15 +49,15 @@ snapcraft pack
On a **disposable Ubuntu host with snapd**, inspect the resulting artifact: On a **disposable Ubuntu host with snapd**, inspect the resulting artifact:
```bash ```bash
bash snap/local/smoke-test.sh ./vocalinux_<version>_amd64.snap bash snap/local/smoke-test.sh ./voculinux_<version>_amd64.snap
``` ```
Replace `<version>` with the version that was built. The script refuses to overwrite an Replace `<version>` with the version that was built. The script refuses to overwrite an
existing Vocalinux snap. It installs the supplied file with `--dangerous`, existing Voculinux snap. It installs the supplied file with `--dangerous`,
runs the actual application launcher with `--version`, then enters the GNOME runs the actual application launcher with `--version`, then enters the GNOME
command-chain environment to audit Python versions and import the native, command-chain environment to audit Python versions and import the native,
GTK, recognition and text-injection modules. It leaves the installation in GTK, recognition and text-injection modules. It leaves the installation in
place for inspection; use a disposable host, not your normal Vocalinux install. place for inspection; use a disposable host, not your normal Voculinux install.
`unified-pipeline.yml` builds and runs this gate on relevant Python/Snap PRs and `unified-pipeline.yml` builds and runs this gate on relevant Python/Snap PRs and
main pushes, including recipe-only changes. `release.yml` runs it before the main pushes, including recipe-only changes. `release.yml` runs it before the
+18 -18
View File
@@ -5,36 +5,36 @@ Common install and runtime problems. Install overview: [INSTALL.md](INSTALL.md).
Always try once with debug logs: Always try once with debug logs:
```bash ```bash
vocalinux --debug voculinux --debug
``` ```
## Command not found: `vocalinux` ## Command not found: `voculinux`
```bash ```bash
# Official install wrappers # Official install wrappers
export PATH="$HOME/.local/bin:$PATH" export PATH="$HOME/.local/bin:$PATH"
vocalinux voculinux
# Or run the venv binary # Or run the venv binary
~/.local/share/vocalinux/venv/bin/vocalinux ~/.local/share/voculinux/venv/bin/voculinux
# Source checkout # Source checkout
source venv/bin/activate source venv/bin/activate
vocalinux voculinux
``` ```
## No audio / microphone not working ## No audio / microphone not working
1. Check system sound settings and mute state 1. Check system sound settings and mute state
2. List capture devices: `arecord -l` 2. List capture devices: `arecord -l`
3. Run `vocalinux --debug` and confirm the selected device 3. Run `voculinux --debug` and confirm the selected device
4. Reset a bad device index: set `audio.device_index` to `null` in `~/.config/vocalinux/config.json` 4. Reset a bad device index: set `audio.device_index` to `null` in `~/.config/voculinux/config.json`
5. Bluetooth mics: try a wired device to isolate SCO capture issues 5. Bluetooth mics: try a wired device to isolate SCO capture issues
### Microphone Stalled ### Microphone Stalled
A **Microphone Stalled** notification means the selected input stopped delivering A **Microphone Stalled** notification means the selected input stopped delivering
audio. Vocalinux stops the failed dictation attempt. audio. Voculinux stops the failed dictation attempt.
Open **Settings → Audio**. Use **Microphone Test → Test** to record two seconds and Open **Settings → Audio**. Use **Microphone Test → Test** to record two seconds and
check the audio level, or select a different **Input Device**, then retry check the audio level, or select a different **Input Device**, then retry
@@ -75,7 +75,7 @@ Refresh icons:
```bash ```bash
gtk-update-icon-cache -f -t ~/.local/share/icons/hicolor gtk-update-icon-cache -f -t ~/.local/share/icons/hicolor
vocalinux --debug voculinux --debug
``` ```
## Text not injected into apps ## Text not injected into apps
@@ -95,7 +95,7 @@ wtype "hello"
printf "bonjour" | xsel --clipboard --input printf "bonjour" | xsel --clipboard --input
``` ```
**KDE Plasma Wayland:** `wtype` may fail if the compositor does not expose a virtual keyboard to normal clients. Prefer **System Settings → Keyboard → Virtual Keyboard → IBus Wayland**, then restart Vocalinux (or log out/in). **KDE Plasma Wayland:** `wtype` may fail if the compositor does not expose a virtual keyboard to normal clients. Prefer **System Settings → Keyboard → Virtual Keyboard → IBus Wayland**, then restart Voculinux (or log out/in).
Install IBus if needed: Install IBus if needed:
@@ -115,8 +115,8 @@ Compositor-specific notes: [DISTRO_COMPATIBILITY.md](DISTRO_COMPATIBILITY.md).
Models download on first use. Manual VOSK example: Models download on first use. Manual VOSK example:
```bash ```bash
mkdir -p ~/.local/share/vocalinux/models mkdir -p ~/.local/share/voculinux/models
cd ~/.local/share/vocalinux/models cd ~/.local/share/voculinux/models
wget https://alphacephei.com/vosk/models/vosk-model-small-en-us-0.15.zip wget https://alphacephei.com/vosk/models/vosk-model-small-en-us-0.15.zip
unzip vosk-model-small-en-us-0.15.zip unzip vosk-model-small-en-us-0.15.zip
``` ```
@@ -128,23 +128,23 @@ whisper.cpp models are managed from Settings or first-run download.
```bash ```bash
# Unresolved libs # Unresolved libs
ldd $(find ~/.local/share/vocalinux/venv -name '*.so' -path '*/pywhispercpp*' 2>/dev/null | head -1) | grep 'not found' ldd $(find ~/.local/share/voculinux/venv -name '*.so' -path '*/pywhispercpp*' 2>/dev/null | head -1) | grep 'not found'
# Rebuild whisper.cpp bindings via installer # Rebuild whisper.cpp bindings via installer
./install.sh --auto --rebuild-whispercpp ./install.sh --auto --rebuild-whispercpp
# Or switch to VOSK temporarily # Or switch to VOSK temporarily
vocalinux --engine vosk voculinux --engine vosk
``` ```
## Old process still running after update ## Old process still running after update
Stop via the tray, or use the lock-file PID (do **not** use `pkill -f vocalinux`; it can kill editors and shells): Stop via the tray, or use the lock-file PID (do **not** use `pkill -f voculinux`; it can kill editors and shells):
```bash ```bash
kill "$(tr -d '[:space:]' < "${XDG_DATA_HOME:-$HOME/.local/share}/vocalinux/instance.lock")" kill "$(tr -d '[:space:]' < "${XDG_DATA_HOME:-$HOME/.local/share}/voculinux/instance.lock")"
kill "$(tr -d '[:space:]' < "${XDG_DATA_HOME:-$HOME/.local/share}/vocalinux-ibus/engine.pid")" kill "$(tr -d '[:space:]' < "${XDG_DATA_HOME:-$HOME/.local/share}/voculinux-ibus/engine.pid")"
vocalinux voculinux
``` ```
## Clean reinstall ## Clean reinstall
+16 -16
View File
@@ -1,10 +1,10 @@
# Updating Vocalinux # Updating Voculinux
How to upgrade an existing install, plus release notes for the current series. How to upgrade an existing install, plus release notes for the current series.
## Quick update ## Quick update
Vocalinux checks GitHub Releases in the background about every six hours (and shortly after startup). When a newer build is on your selected channel, the tray menu gains an **Update Available...** entry and Settings -> About shows a green **New** badge; open that page for release notes and download links. Updates are not installed automatically. Re-run the installer (or your package manager) as below. Voculinux checks GitHub Releases in the background about every six hours (and shortly after startup). When a newer build is on your selected channel, the tray menu gains an **Update Available...** entry and Settings -> About shows a green **New** badge; open that page for release notes and download links. Updates are not installed automatically. Re-run the installer (or your package manager) as below.
### Official installer ### Official installer
@@ -15,7 +15,7 @@ bash /tmp/vl.sh
The installer detects a running instance, updates in place, preserves configuration and models, and pulls new dependencies (including neural VAD when available). The installer detects a running instance, updates in place, preserves configuration and models, and pulls new dependencies (including neural VAD when available).
On an existing install (a virtual environment plus `~/.config/vocalinux/config.json`), the installer reports the saved configuration and defaults to **Upgrade (keep current settings)**: the configured engine stays selected, `config.json` is never rewritten, the virtual environment is reused, downloaded models are kept, and pywhispercpp is rebuilt only when this release pins a new version or GPU support is missing. Pick the interactive or automatic modes instead if you want to reconfigure the install. On an existing install (a virtual environment plus `~/.config/voculinux/config.json`), the installer reports the saved configuration and defaults to **Upgrade (keep current settings)**: the configured engine stays selected, `config.json` is never rewritten, the virtual environment is reused, downloaded models are kept, and pywhispercpp is rebuilt only when this release pins a new version or GPU support is missing. Pick the interactive or automatic modes instead if you want to reconfigure the install.
For a scripted, non-interactive upgrade: For a scripted, non-interactive upgrade:
@@ -28,7 +28,7 @@ bash /tmp/vl.sh --upgrade
### Installed from source ### Installed from source
```bash ```bash
cd vocalinux cd voculinux
git fetch origin git fetch origin
git checkout v0.18.5 git checkout v0.18.5
./install.sh ./install.sh
@@ -37,7 +37,7 @@ git checkout v0.18.5
Latest development tree: Latest development tree:
```bash ```bash
cd vocalinux cd voculinux
git pull origin main git pull origin main
./install.sh ./install.sh
``` ```
@@ -46,16 +46,16 @@ git pull origin main
| Method | Upgrade | | Method | Upgrade |
|--------|---------| |--------|---------|
| AUR | `yay -S vocalinux` (or your AUR helper) | | AUR | `yay -S voculinux` (or your AUR helper) |
| AppImage | Download the new file from [Releases](https://github.com/VocaHQ/vocalinux/releases) | | AppImage | Download the new file from [Releases](https://github.com/VocaHQ/vocalinux/releases) |
| Snap | Store: `sudo snap refresh vocalinux` (`--edge` until stable is promoted). The recipe ships no `uinput` plug (the Store denied the grant); Wayland typing uses the RemoteDesktop portal. The GitHub `vocalinux_0.18.2_amd64.snap` still declares `uinput` for KDE users who want ydotool (`sudo snap install --dangerous`, then `sudo snap connect vocalinux:uinput`). | | Snap | Store: `sudo snap refresh voculinux` (`--edge` until stable is promoted). The recipe ships no `uinput` plug (the Store denied the grant); Wayland typing uses the RemoteDesktop portal. The GitHub `voculinux_0.18.2_amd64.snap` still declares `uinput` for KDE users who want ydotool (`sudo snap install --dangerous`, then `sudo snap connect voculinux:uinput`). |
| Flatpak (release bundle) | Install the new `.flatpak` from Releases; bundles do not auto-update | | Flatpak (release bundle) | Install the new `.flatpak` from Releases; bundles do not auto-update |
| PyPI | Reinstall in the same venv after system packages are current | | PyPI | Reinstall in the same venv after system packages are current |
### Check your version ### Check your version
```bash ```bash
vocalinux --version voculinux --version
# or # or
python3 -c "import vocalinux; print(vocalinux.version.__version__)" python3 -c "import vocalinux; print(vocalinux.version.__version__)"
``` ```
@@ -76,13 +76,13 @@ curl -fsSL https://raw.githubusercontent.com/VocaHQ/vocalinux/main/install.sh -o
bash /tmp/vl.sh bash /tmp/vl.sh
``` ```
If an old process is stuck, stop via the tray or the PID in the instance lock file (do not use `pkill -f vocalinux`; it can kill unrelated processes): If an old process is stuck, stop via the tray or the PID in the instance lock file (do not use `pkill -f voculinux`; it can kill unrelated processes):
```bash ```bash
kill "$(tr -d '[:space:]' < "${XDG_DATA_HOME:-$HOME/.local/share}/vocalinux/instance.lock")" kill "$(tr -d '[:space:]' < "${XDG_DATA_HOME:-$HOME/.local/share}/voculinux/instance.lock")"
# If you use IBus injection: # If you use IBus injection:
kill "$(tr -d '[:space:]' < "${XDG_DATA_HOME:-$HOME/.local/share}/vocalinux-ibus/engine.pid")" kill "$(tr -d '[:space:]' < "${XDG_DATA_HOME:-$HOME/.local/share}/voculinux-ibus/engine.pid")"
vocalinux voculinux
``` ```
Missing system packages: see [INSTALL.md](INSTALL.md) or [DISTRO_COMPATIBILITY.md](DISTRO_COMPATIBILITY.md). Missing system packages: see [INSTALL.md](INSTALL.md) or [DISTRO_COMPATIBILITY.md](DISTRO_COMPATIBILITY.md).
@@ -196,7 +196,7 @@ See the [full changelog](https://github.com/VocaHQ/vocalinux/releases/tag/v0.18.
- Installer package map covers cairo headers on openSUSE, Void, Solus and Alpine (#937) - Installer package map covers cairo headers on openSUSE, Void, Solus and Alpine (#937)
- evdev monitors remapper virtual keyboards ungrabbed so remappers like keyd keep working (#932) - evdev monitors remapper virtual keyboards ungrabbed so remappers like keyd keep working (#932)
- The install section on vocalinux.com showcases the .deb, .rpm, Snap, Flatpak and AUR packages (#929) - The install section on voculinux.com showcases the .deb, .rpm, Snap, Flatpak and AUR packages (#929)
- The remote `curl | bash` install path is gated end to end in CI (#898) - The remote `curl | bash` install path is gated end to end in CI (#898)
AppImage, Flatpak, and Snap still ship whisper.cpp only. AppImage, Flatpak, and Snap still ship whisper.cpp only.
@@ -273,7 +273,7 @@ See the [full changelog](https://github.com/VocaHQ/vocalinux/releases/tag/v0.18.
- Audio: stop passing an explicit index when zero devices enumerate; View Logs closes via the titlebar X (#891, #892) - Audio: stop passing an explicit index when zero devices enumerate; View Logs closes via the titlebar X (#891, #892)
- Snap gains `hardware-observe` so hotkeys can read input devices (#858, fixes #857 reported by @RhysU) - Snap gains `hardware-observe` so hotkeys can read input devices (#858, fixes #857 reported by @RhysU)
- Installer installs hash-pinned dependencies and build tools, and is split into sourced modules with a generated distro package map (#856, #862, #872 by @sesav) - Installer installs hash-pinned dependencies and build tools, and is split into sourced modules with a generated distro package map (#856, #862, #872 by @sesav)
- AUR `vocalinux-bin` ships the AppImage with per-arch digests; releases publish a signed self-hosted Flatpak OSTree remote; a snap-promote workflow gates stable promotion (#879, #875, #881, fixes #817, #785, #783) - AUR `voculinux-bin` ships the AppImage with per-arch digests; releases publish a signed self-hosted Flatpak OSTree remote; a snap-promote workflow gates stable promotion (#879, #875, #881, fixes #817, #785, #783)
- VocaGateway can run locally from Settings → Advanced (podman-first) (#774) - VocaGateway can run locally from Settings → Advanced (podman-first) (#774)
- Dependency advisories cleared and workflow tokens scoped to repository reads (#869, #865, #866 by @Mr-Sunglasses, #870) - Dependency advisories cleared and workflow tokens scoped to repository reads (#869, #865, #866 by @Mr-Sunglasses, #870)
- Settings dialog consolidated: guard flags and widget construction in one place each (#878, fixes #793) - Settings dialog consolidated: guard flags and widget construction in one place each (#878, fixes #793)
@@ -359,7 +359,7 @@ See the [full changelog](https://github.com/VocaHQ/vocalinux/releases/tag/v0.17.
- **CI**: gate AUR PKGBUILD builds on every PR (#772 by @sesav) - **CI**: gate AUR PKGBUILD builds on every PR (#772 by @sesav)
- **CI / docs**: test the distros the docs promise and fix docs drift (#773 by @sesav) - **CI / docs**: test the distros the docs promise and fix docs drift (#773 by @sesav)
- **Site**: VocaGateway family card is Beta; README logo, badges, and privacy copy (#765 by @jatinkrmalik, #764) - **Site**: VocaGateway family card is Beta; README logo, badges, and privacy copy (#765 by @jatinkrmalik, #764)
- **Snap**: v0.16.2 `latest/edge` (rev 7) has no `uinput` plug. `sudo snap connect vocalinux:uinput` errors with `snap "vocalinux" has no plug named "uinput"`. Dictation only reaches XWayland apps. (v0.17.0+ added the plug, but the Store denied it, so later revisions drop it again and type via the RemoteDesktop portal.) (#823) - **Snap**: v0.16.2 `latest/edge` (rev 7) has no `uinput` plug. `sudo snap connect voculinux:uinput` errors with `snap "voculinux" has no plug named "uinput"`. Dictation only reaches XWayland apps. (v0.17.0+ added the plug, but the Store denied it, so later revisions drop it again and type via the RemoteDesktop portal.) (#823)
See the [full changelog](https://github.com/VocaHQ/vocalinux/releases/tag/v0.16.2). See the [full changelog](https://github.com/VocaHQ/vocalinux/releases/tag/v0.16.2).
@@ -453,7 +453,7 @@ See the [full changelog](https://github.com/VocaHQ/vocalinux/releases/tag/v0.16.
- CUDA device 0 note for dual NVIDIA (#644) - CUDA device 0 note for dual NVIDIA (#644)
- Discord and VocaHQ README shields; VocaHQ URL migration; VocaGateway rename (#695, #696, #697) - Discord and VocaHQ README shields; VocaHQ URL migration; VocaGateway rename (#695, #696, #697)
- Discord invite and X handle point at VocaHQ (#722) - Discord invite and X handle point at VocaHQ (#722)
- vocalinux.com restyled to the Voca family workbench; Open Graph card uses the flat Tux (#728, #729) - voculinux.com restyled to the Voca family workbench; Open Graph card uses the flat Tux (#728, #729)
- Website copy drops the stale 100% offline claim and marks VocaWin as alpha on the site (#717) - Website copy drops the stale 100% offline claim and marks VocaWin as alpha on the site (#717)
- Codeberg mirror tag force-push (#633) - Codeberg mirror tag force-push (#633)
- Installer hardening, Justfile in place of Makefile, and uv lockfiles with pinned build inputs. Epic #701 remains open (#700, #705 by @sesav) - Installer hardening, Justfile in place of Makefile, and uv lockfiles with pinned build inputs. Epic #701 remains open (#700, #705 by @sesav)
+49 -49
View File
@@ -1,10 +1,10 @@
# User guide # User guide
How to use Vocalinux day to day. Install first: [INSTALL.md](INSTALL.md). How to use Voculinux day to day. Install first: [INSTALL.md](INSTALL.md).
## Getting started ## Getting started
1. Launch Vocalinux (`vocalinux` or the application menu) 1. Launch Voculinux (`voculinux` or the application menu)
2. Find the microphone icon in the system tray 2. Find the microphone icon in the system tray
3. Start dictation with the tray menu or your keyboard shortcut 3. Start dictation with the tray menu or your keyboard shortcut
4. Speak into the focused application; text is injected when an utterance completes 4. Speak into the focused application; text is injected when an utterance completes
@@ -12,7 +12,7 @@ How to use Vocalinux day to day. Install first: [INSTALL.md](INSTALL.md).
### Start on login ### Start on login
Enable **Start on Login** from the first-run dialog, tray menu, or Settings. Vocalinux writes an XDG autostart entry (`~/.config/autostart/vocalinux.desktop`) and starts as a normal user app (`--start-minimized`). It does not create a systemd service. Enable **Start on Login** from the first-run dialog, tray menu, or Settings. Voculinux writes an XDG autostart entry (`~/.config/autostart/voculinux.desktop`) and starts as a normal user app (`--start-minimized`). It does not create a systemd service.
Works on common desktop environments (GNOME, KDE, Xfce, Cinnamon, MATE, LXQt). Minimal window-manager sessions may need their own autostart helper. Works on common desktop environments (GNOME, KDE, Xfce, Cinnamon, MATE, LXQt). Minimal window-manager sessions may need their own autostart helper.
@@ -26,15 +26,15 @@ Works on common desktop environments (GNOME, KDE, Xfce, Cinnamon, MATE, LXQt). M
### Dictation formatting ### Dictation formatting
Vocalinux capitalizes the start of dictation and letters after `.`, `!`, or `?`. Each completed utterance leaves a trailing space so the next session does not glue onto the previous sentence. Voculinux capitalizes the start of dictation and letters after `.`, `!`, or `?`. Each completed utterance leaves a trailing space so the next session does not glue onto the previous sentence.
### Dictating into terminals ### Dictating into terminals
When Vocalinux injects through the clipboard (the usual Wayland / ydotool path), it sends **Ctrl+V** in ordinary text fields and **Ctrl+Shift+V** in terminal emulator windows. Auto-detect works on X11 and on Hyprland, Sway, and niri. On GNOME or KDE Wayland, set **Settings → Dictation → Clipboard Paste Shortcut** to **Ctrl+Shift+V**. When Voculinux injects through the clipboard (the usual Wayland / ydotool path), it sends **Ctrl+V** in ordinary text fields and **Ctrl+Shift+V** in terminal emulator windows. Auto-detect works on X11 and on Hyprland, Sway, and niri. On GNOME or KDE Wayland, set **Settings → Dictation → Clipboard Paste Shortcut** to **Ctrl+Shift+V**.
On non-US layouts such as German Neo, that chord uses the key that types **v** on the active layout (not physical KEY_V). Nested terminal panels inside an IDE are often invisible to window-class detection. If paste lands as a literal `^V` or does nothing, open **Settings → Dictation → Clipboard Paste Shortcut** and choose **Ctrl+Shift+V**. On non-US layouts such as German Neo, that chord uses the key that types **v** on the active layout (not physical KEY_V). Nested terminal panels inside an IDE are often invisible to window-class detection. If paste lands as a literal `^V` or does nothing, open **Settings → Dictation → Clipboard Paste Shortcut** and choose **Ctrl+Shift+V**.
On X11, **Paste long dictations** opts in to clipboard-pasting dictations of 30 or more characters instead of typing them key by key. It is off by default. Vocalinux restores the previous clipboard contents after pasting unless **Copy to Clipboard** is enabled. If the clipboard contains an image or other non-text content, Vocalinux types the dictation and leaves that content alone. On X11, **Paste long dictations** opts in to clipboard-pasting dictations of 30 or more characters instead of typing them key by key. It is off by default. Voculinux restores the previous clipboard contents after pasting unless **Copy to Clipboard** is enabled. If the clipboard contains an image or other non-text content, Voculinux types the dictation and leaves that content alone.
The clipboard copy behind these pastes is temporary: your previous clipboard comes back right after. When the copy goes through `wl-copy` 2.2 or newer, it is also marked sensitive (`wl-copy --sensitive`), and clipboard managers that honor that hint, such as Klipper or cliphist, keep it out of their history. Older `wl-copy` builds and X11 or XWayland pastes (`xclip`/`xsel`) have no such hint, so there the dictated text can still show up in history. With **Settings → Dictation → Copy to Clipboard** on, the dictated text is an ordinary copy and stays in history. The clipboard copy behind these pastes is temporary: your previous clipboard comes back right after. When the copy goes through `wl-copy` 2.2 or newer, it is also marked sensitive (`wl-copy --sensitive`), and clipboard managers that honor that hint, such as Klipper or cliphist, keep it out of their history. Older `wl-copy` builds and X11 or XWayland pastes (`xclip`/`xsel`) have no such hint, so there the dictated text can still show up in history. With **Settings → Dictation → Copy to Clipboard** on, the dictated text is an ordinary copy and stays in history.
@@ -46,11 +46,11 @@ Open **Settings → Audio → Input Device** to select a specific microphone or
**System Default**. A valid explicit microphone selection uses that device and **System Default**. A valid explicit microphone selection uses that device and
bypasses the System Default preference below. bypasses the System Default preference below.
With **System Default**, Vocalinux leaves valid default or virtual inputs under With **System Default**, Voculinux leaves valid default or virtual inputs under
the audio system's control rather than pinning a hardware device. the audio system's control rather than pinning a hardware device.
On ALSA only, if PortAudio substitutes arbitrary hardware for the default input, On ALSA only, if PortAudio substitutes arbitrary hardware for the default input,
Vocalinux prefers an input-capable `pulse` PCM on the same host API to follow the Voculinux prefers an input-capable `pulse` PCM on the same host API to follow the
microphone selected in desktop sound settings. If no suitable `pulse` PCM exists, microphone selected in desktop sound settings. If no suitable `pulse` PCM exists,
it keeps PortAudio's fallback device and logs a warning. it keeps PortAudio's fallback device and logs a warning.
@@ -112,7 +112,7 @@ Parakeet runs NVIDIA NeMo ASR models through sherpa-onnx. The default bundle is
Pick the language you dictate in from **Language**, or leave it on auto-detect. Pick the language you dictate in from **Language**, or leave it on auto-detect.
If you work in more than one language and switch keyboard layouts to do it, turn If you work in more than one language and switch keyboard layouts to do it, turn
on **Follow keyboard layout** instead. Vocalinux then reads your active layout at on **Follow keyboard layout** instead. Voculinux then reads your active layout at
the start of every dictation and uses the matching language, so switching layout the start of every dictation and uses the matching language, so switching layout
switches dictation language with it. The Language picker greys out while this is switches dictation language with it. The Language picker greys out while this is
on and shows what your current layout resolves to; turning it off pins that on and shows what your current layout resolves to; turning it off pins that
@@ -127,31 +127,31 @@ the configured primary layout.
### Activation via a KDE Plasma global shortcut ### Activation via a KDE Plasma global shortcut
Instead of the built-in hotkey listener, you can let your desktop's global Instead of the built-in hotkey listener, you can let your desktop's global
shortcut system trigger Vocalinux. On Wayland the built-in listener reads shortcut system trigger Voculinux. On Wayland the built-in listener reads
`/dev/input` (requiring membership in the `input` group and effectively acting `/dev/input` (requiring membership in the `input` group and effectively acting
as a system-wide key reader). Delegating activation to the compositor avoids as a system-wide key reader). Delegating activation to the compositor avoids
this entirely: no `/dev/input` access and no `input` group needed just to this entirely: no `/dev/input` access and no `input` group needed just to
start/stop dictation. Text injection is unaffected. start/stop dictation. Text injection is unaffected.
Enable it in Vocalinux: Enable it in Voculinux:
1. Open **Settings → Shortcuts** (the Keyboard Shortcuts group on the Dictation page) 1. Open **Settings → Shortcuts** (the Keyboard Shortcuts group on the Dictation page)
2. Turn on **External Activation (Desktop Shortcut)** 2. Turn on **External Activation (Desktop Shortcut)**
The change applies immediately; you do not need to restart. The built-in key The change applies immediately; you do not need to restart. The built-in key
listener stops, and a running instance exposes a D-Bus service on the session listener stops, and a running instance exposes a D-Bus service on the session
bus (`com.vocalinux.Vocalinux`). The CLI can forward commands to it: bus (`com.voculinux.Voculinux`). The CLI can forward commands to it:
```bash ```bash
vocalinux --toggle # start if idle, stop if active voculinux --toggle # start if idle, stop if active
vocalinux --start # start voice typing voculinux --start # start voice typing
vocalinux --stop # stop voice typing voculinux --stop # stop voice typing
``` ```
Then bind a compositor shortcut to `vocalinux --toggle` on a native install. Then bind a compositor shortcut to `voculinux --toggle` on a native install.
For Flatpak, bind the `gdbus` command in For Flatpak, bind the `gdbus` command in
[Running without device access](../packaging/flatpak/README.md#running-without-device-access) [Running without device access](../packaging/flatpak/README.md#running-without-device-access)
instead, because `vocalinux --toggle` is not on the host PATH. A desktop instead, because `voculinux --toggle` is not on the host PATH. A desktop
shortcut only sends the key press, so it can toggle dictation but cannot shortcut only sends the key press, so it can toggle dictation but cannot
support push-to-talk because it never sees the release. On KDE Plasma: support push-to-talk because it never sees the release. On KDE Plasma:
@@ -159,16 +159,16 @@ support push-to-talk because it never sees the release. On KDE Plasma:
(older Plasma: **System Settings -> Shortcuts -> Custom Shortcuts -> Edit -> (older Plasma: **System Settings -> Shortcuts -> Custom Shortcuts -> Edit ->
New -> Global Shortcut -> Command/URL**). New -> Global Shortcut -> Command/URL**).
2. Bind a key combination of your choice to the command `vocalinux --toggle` 2. Bind a key combination of your choice to the command `voculinux --toggle`
for a native install. For Flatpak, use the D-Bus command linked above. for a native install. For Flatpak, use the D-Bus command linked above.
Now your chosen key combination toggles dictation, handled by the compositor Now your chosen key combination toggles dictation, handled by the compositor
rather than by Vocalinux reading the keyboard directly. This works the same way rather than by Voculinux reading the keyboard directly. This works the same way
on other compositors that support binding a key to a command (e.g. GNOME custom on other compositors that support binding a key to a command (e.g. GNOME custom
shortcuts, Sway/Hyprland `bindsym`/`bind`). shortcuts, Sway/Hyprland `bindsym`/`bind`).
As an advanced alternative, you can set the same option in As an advanced alternative, you can set the same option in
`~/.config/vocalinux/config.json` under `shortcuts`: `~/.config/voculinux/config.json` under `shortcuts`:
```json ```json
"shortcuts": { "shortcuts": {
@@ -176,8 +176,8 @@ As an advanced alternative, you can set the same option in
} }
``` ```
Quit Vocalinux before editing that file so the running app cannot overwrite Quit Voculinux before editing that file so the running app cannot overwrite
the edit from its cached config. Start Vocalinux again after saving. the edit from its cached config. Start Voculinux again after saving.
### Model size (whisper.cpp / Whisper) ### Model size (whisper.cpp / Whisper)
@@ -193,13 +193,13 @@ For whisper.cpp, also pick a **Specialization**: standard multilingual, English-
### Removing unused models ### Removing unused models
If leftover files are on disk that are not the model currently selected, **Unused downloads** appears under the model info card. Expand it to delete leftovers one at a time. Confirming removes those files from `~/.local/share/vocalinux`. Packaged system-wide VOSK models are left alone. If leftover files are on disk that are not the model currently selected, **Unused downloads** appears under the model info card. Expand it to delete leftovers one at a time. Confirming removes those files from `~/.local/share/voculinux`. Packaged system-wide VOSK models are left alone.
### GPU ### GPU
whisper.cpp prefers Vulkan when the bundled pywhispercpp libraries include it, then CUDA, then CPU. Host tools such as `vulkaninfo` only describe the machine. The engine follows the libraries actually loaded. On multi-GPU machines a discrete Vulkan device is preferred; override under **Settings → Performance → Vulkan GPU**. Check logs with `vocalinux --debug`. whisper.cpp prefers Vulkan when the bundled pywhispercpp libraries include it, then CUDA, then CPU. Host tools such as `vulkaninfo` only describe the machine. The engine follows the libraries actually loaded. On multi-GPU machines a discrete Vulkan device is preferred; override under **Settings → Performance → Vulkan GPU**. Check logs with `voculinux --debug`.
Pip wheels of pywhispercpp are often CUDA builds. In that case Vocalinux uses CUDA device 0. `install.sh` rebuilds pywhispercpp with Vulkan or CUDA when it can. Pip wheels of pywhispercpp are often CUDA builds. In that case Voculinux uses CUDA device 0. `install.sh` rebuilds pywhispercpp with Vulkan or CUDA when it can.
### Auto-pause and keep-alive ### Auto-pause and keep-alive
@@ -226,16 +226,16 @@ recording. This option is off by default.
## CLI ## CLI
```bash ```bash
vocalinux --help voculinux --help
vocalinux --version voculinux --version
vocalinux --debug voculinux --debug
vocalinux --engine whisper_cpp voculinux --engine whisper_cpp
vocalinux --engine faster_whisper voculinux --engine faster_whisper
vocalinux --engine parakeet voculinux --engine parakeet
vocalinux --model medium.en-q5_0 voculinux --model medium.en-q5_0
vocalinux --wayland voculinux --wayland
vocalinux --start-minimized voculinux --start-minimized
vocalinux --transcribe-file meeting.wav # diarized transcript on stdout, then exits voculinux --transcribe-file meeting.wav # diarized transcript on stdout, then exits
``` ```
## Custom Dictionary Support ## Custom Dictionary Support
@@ -243,15 +243,15 @@ vocalinux --transcribe-file meeting.wav # diarized transcript on stdout, then
Open **Settings → Custom Dictionary** to configure two separate capabilities: Open **Settings → Custom Dictionary** to configure two separate capabilities:
- **Custom terms** bias recognition toward product names, people, and jargon. - **Custom terms** bias recognition toward product names, people, and jargon.
They are read from `~/.config/vocalinux/dictionary.txt` by default, as UTF-8 They are read from `~/.config/voculinux/dictionary.txt` by default, as UTF-8
with one term per line. Blank lines and `#` comments are allowed, so the same with one term per line. Blank lines and `#` comments are allowed, so the same
file remains friendly to an accessibility scanner. Use the add/remove editor file remains friendly to an accessibility scanner. Use the add/remove editor
or choose a different readable terms file with the file picker. or choose a different readable terms file with the file picker.
- **Transcript corrections** replace a known misheard word or phrase after - **Transcript corrections** replace a known misheard word or phrase after
transcription, for example `super base` with `Supabase`. Corrections are transcription, for example `super base` with `Supabase`. Corrections are
stored separately in `~/.config/vocalinux/custom-dictionary-corrections.json`. stored separately in `~/.config/voculinux/custom-dictionary-corrections.json`.
VocaLinux re-reads both files before each completed dictation segment, so an Voculinux re-reads both files before each completed dictation segment, so an
external edit applies to the next segment without restarting the app. Vocabulary external edit applies to the next segment without restarting the app. Vocabulary
bias works with Whisper, whisper.cpp, and Faster Whisper. Corrections work with bias works with Whisper, whisper.cpp, and Faster Whisper. Corrections work with
every engine, including VOSK, Parakeet, and the configured remote API. every engine, including VOSK, Parakeet, and the configured remote API.
@@ -261,10 +261,10 @@ command-like misrecognition from acting, but avoid replacements that create a
voice command unless that is intentional. Correction replacements are not voice command unless that is intentional. Correction replacements are not
automatically added to recognition bias. automatically added to recognition bias.
For a one-session terms override, start Vocalinux with: For a one-session terms override, start Voculinux with:
```bash ```bash
vocalinux --dictionary-file /path/to/dictionary.txt voculinux --dictionary-file /path/to/dictionary.txt
``` ```
This temporarily enables terms from that file without changing saved settings; This temporarily enables terms from that file without changing saved settings;
@@ -275,7 +275,7 @@ transcript corrections.
### Post-Processing ### Post-Processing
Vocalinux can pipe each transcription result through a user-defined script before injecting it into your application. This lets you apply custom transformations — for example, grammar correction, abbreviation expansion, or domain-specific formatting. Voculinux can pipe each transcription result through a user-defined script before injecting it into your application. This lets you apply custom transformations — for example, grammar correction, abbreviation expansion, or domain-specific formatting.
**To configure:** **To configure:**
1. Open Settings from the tray icon menu (right-click) 1. Open Settings from the tray icon menu (right-click)
@@ -295,19 +295,19 @@ Vocalinux can pipe each transcription result through a user-defined script befor
#!/bin/bash #!/bin/bash
tr '[:lower:]' '[:upper:]' tr '[:lower:]' '[:upper:]'
``` ```
Make the script executable (`chmod +x`) before setting the path in Vocalinux. Make the script executable (`chmod +x`) before setting the path in Voculinux.
## Troubleshooting ## Troubleshooting
```bash ```bash
vocalinux --debug voculinux --debug
``` ```
See [TROUBLESHOOTING.md](TROUBLESHOOTING.md) for tray, audio, injection, and model issues. Distro notes: [DISTRO_COMPATIBILITY.md](DISTRO_COMPATIBILITY.md). Updates: [UPDATE.md](UPDATE.md). Help channels: [SUPPORT.md](../SUPPORT.md). See [TROUBLESHOOTING.md](TROUBLESHOOTING.md) for tray, audio, injection, and model issues. Distro notes: [DISTRO_COMPATIBILITY.md](DISTRO_COMPATIBILITY.md). Updates: [UPDATE.md](UPDATE.md). Help channels: [SUPPORT.md](../SUPPORT.md).
### Text injection backend ### Text injection backend
Vocalinux types your dictated text using one of several backends. It picks one Voculinux types your dictated text using one of several backends. It picks one
automatically, and on most desktops the automatic choice is correct. automatically, and on most desktops the automatic choice is correct.
Autodetection can be wrong, though, and it fails in a way that is easy to Autodetection can be wrong, though, and it fails in a way that is easy to
@@ -316,7 +316,7 @@ applications, IBus reports the text as delivered while nothing appears. If
dictation works in some windows (typically XWayland ones, like a browser) but dictation works in some windows (typically XWayland ones, like a browser) but
silently does nothing in others, that is the symptom. silently does nothing in others, that is the symptom.
Pin the backend explicitly in `~/.config/vocalinux/config.json`: Pin the backend explicitly in `~/.config/voculinux/config.json`:
```json ```json
{ {
@@ -350,19 +350,19 @@ particular application. Pinning `ibus` keeps the IBus path; pinning anything
else turns it off. else turns it off.
To try a backend for a single run without changing the saved setting, set To try a backend for a single run without changing the saved setting, set
`VOCALINUX_FORCE_BACKEND`, which overrides the config value. Set it to `auto` `VOCULINUX_FORCE_BACKEND`, which overrides the config value. Set it to `auto`
to ignore a saved pin for that run: to ignore a saved pin for that run:
```bash ```bash
VOCALINUX_FORCE_BACKEND=wtype vocalinux --debug VOCULINUX_FORCE_BACKEND=wtype voculinux --debug
``` ```
The setting and environment override are read at startup, so restart Vocalinux The setting and environment override are read at startup, so restart Voculinux
after editing `config.json`. The startup log first records a backend pin request; after editing `config.json`. The startup log first records a backend pin request;
it does not prove the backend was available or that text reached the focused it does not prove the backend was available or that text reached the focused
application. Later logs identify a fallback when a pin was not applied. application. Later logs identify a fallback when a pin was not applied.
If a pinned tool is unavailable, Vocalinux warns and continues with its normal If a pinned tool is unavailable, Voculinux warns and continues with its normal
fallback selection. `portal` needs a desktop implementing the fallback selection. `portal` needs a desktop implementing the
`org.freedesktop.portal.RemoteDesktop` interface (GNOME and KDE Plasma do; `org.freedesktop.portal.RemoteDesktop` interface (GNOME and KDE Plasma do;
most wlroots compositors do not). `ydotool` also needs a usable `/dev/uinput` most wlroots compositors do not). `ydotool` also needs a usable `/dev/uinput`
+13 -13
View File
@@ -1,5 +1,5 @@
{ {
description = "Vocalinux — free, offline voice dictation for Linux"; description = "Voculinux — free, offline voice dictation for Linux";
inputs = { inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
@@ -21,40 +21,40 @@
pkgs = nixpkgs.legacyPackages.${system}; pkgs = nixpkgs.legacyPackages.${system};
in in
{ {
vocalinux = pkgs.python3Packages.callPackage ./packaging/nix/package.nix { voculinux = pkgs.python3Packages.callPackage ./packaging/nix/package.nix {
pywhispercpp = pkgs.python3Packages.callPackage ./packaging/nix/pywhispercpp.nix { }; pywhispercpp = pkgs.python3Packages.callPackage ./packaging/nix/pywhispercpp.nix { };
src = self; src = self;
}; };
default = self.packages.${system}.vocalinux; default = self.packages.${system}.voculinux;
} }
); );
apps = forAllSystems (system: { apps = forAllSystems (system: {
vocalinux = { voculinux = {
type = "app"; type = "app";
program = "${self.packages.${system}.vocalinux}/bin/vocalinux"; program = "${self.packages.${system}.voculinux}/bin/voculinux";
}; };
default = self.apps.${system}.vocalinux; default = self.apps.${system}.voculinux;
}); });
overlays.default = final: prev: { overlays.default = final: prev: {
vocalinux = self.packages.${prev.stdenv.hostPlatform.system}.vocalinux; voculinux = self.packages.${prev.stdenv.hostPlatform.system}.voculinux;
}; };
nixosModules.vocalinux = nixosModules.voculinux =
{ pkgs, lib, ... }: { pkgs, lib, ... }:
{ {
imports = [ ./packaging/nix/module.nix ]; imports = [ ./packaging/nix/module.nix ];
programs.vocalinux.package = lib.mkDefault self.packages.${pkgs.stdenv.hostPlatform.system}.vocalinux; programs.voculinux.package = lib.mkDefault self.packages.${pkgs.stdenv.hostPlatform.system}.voculinux;
}; };
nixosModules.default = self.nixosModules.vocalinux; nixosModules.default = self.nixosModules.voculinux;
homeManagerModules.vocalinux = homeManagerModules.voculinux =
{ pkgs, lib, ... }: { pkgs, lib, ... }:
{ {
imports = [ ./packaging/nix/home-manager.nix ]; imports = [ ./packaging/nix/home-manager.nix ];
programs.vocalinux.package = lib.mkDefault self.packages.${pkgs.stdenv.hostPlatform.system}.vocalinux; programs.voculinux.package = lib.mkDefault self.packages.${pkgs.stdenv.hostPlatform.system}.voculinux;
}; };
homeManagerModules.default = self.homeManagerModules.vocalinux; homeManagerModules.default = self.homeManagerModules.voculinux;
}; };
} }
+14 -14
View File
@@ -7,8 +7,8 @@ install_desktop_entry() {
print_info "Installing desktop entry..." print_info "Installing desktop entry..."
# Check if desktop entry file exists # Check if desktop entry file exists
if [ ! -f "vocalinux.desktop" ]; then if [ ! -f "voculinux.desktop" ]; then
print_error "Desktop entry file not found: vocalinux.desktop" print_error "Desktop entry file not found: voculinux.desktop"
return 1 return 1
fi fi
@@ -19,27 +19,27 @@ install_desktop_entry() {
} }
# Copy desktop entry # Copy desktop entry
cp vocalinux.desktop "$DESKTOP_DIR/" || { cp voculinux.desktop "$DESKTOP_DIR/" || {
print_error "Failed to copy desktop entry to $DESKTOP_DIR" print_error "Failed to copy desktop entry to $DESKTOP_DIR"
return 1 return 1
} }
# Update the desktop entry to use the wrapper script with GI_TYPELIB_PATH # Update the desktop entry to use the wrapper script with GI_TYPELIB_PATH
WRAPPER_SCRIPT="$HOME/.local/bin/vocalinux-gui" WRAPPER_SCRIPT="$HOME/.local/bin/voculinux-gui"
if [ ! -f "$WRAPPER_SCRIPT" ]; then if [ ! -f "$WRAPPER_SCRIPT" ]; then
print_warning "Wrapper script not found at $WRAPPER_SCRIPT" print_warning "Wrapper script not found at $WRAPPER_SCRIPT"
print_warning "Desktop entry may not work correctly" print_warning "Desktop entry may not work correctly"
else else
# Update Exec line to include GI_TYPELIB_PATH for PyGObject # Update Exec line to include GI_TYPELIB_PATH for PyGObject
# Use the detected path for cross-distro compatibility # Use the detected path for cross-distro compatibility
sed -i "s|^Exec=vocalinux|Exec=env GI_TYPELIB_PATH=$GI_TYPELIB_DETECTED $WRAPPER_SCRIPT|" "$DESKTOP_DIR/vocalinux.desktop" || { sed -i "s|^Exec=voculinux|Exec=env GI_TYPELIB_PATH=$GI_TYPELIB_DETECTED $WRAPPER_SCRIPT|" "$DESKTOP_DIR/voculinux.desktop" || {
print_warning "Failed to update desktop entry path" print_warning "Failed to update desktop entry path"
} }
print_info "Updated desktop entry to use wrapper script with GI_TYPELIB_PATH" print_info "Updated desktop entry to use wrapper script with GI_TYPELIB_PATH"
fi fi
# Make desktop entry executable # Make desktop entry executable
chmod +x "$DESKTOP_DIR/vocalinux.desktop" || { chmod +x "$DESKTOP_DIR/voculinux.desktop" || {
print_warning "Failed to make desktop entry executable" print_warning "Failed to make desktop entry executable"
} }
@@ -64,10 +64,10 @@ install_icons() {
# List of icons to install # List of icons to install
local ICONS=( local ICONS=(
"vocalinux.svg" "voculinux.svg"
"vocalinux-microphone.svg" "voculinux-microphone.svg"
"vocalinux-microphone-off.svg" "voculinux-microphone-off.svg"
"vocalinux-microphone-process.svg" "voculinux-microphone-process.svg"
) )
# Install each icon # Install each icon
@@ -85,10 +85,10 @@ install_icons() {
done done
if [ "$INSTALLED_COUNT" -eq "${#ICONS[@]}" ]; then if [ "$INSTALLED_COUNT" -eq "${#ICONS[@]}" ]; then
print_success "Installed all custom Vocalinux icons" print_success "Installed all custom Voculinux icons"
return 0 return 0
elif [ "$INSTALLED_COUNT" -gt 0 ]; then elif [ "$INSTALLED_COUNT" -gt 0 ]; then
print_warning "Installed $INSTALLED_COUNT/${#ICONS[@]} custom Vocalinux icons" print_warning "Installed $INSTALLED_COUNT/${#ICONS[@]} custom Voculinux icons"
return 0 return 0
else else
print_error "Failed to install any icons" print_error "Failed to install any icons"
@@ -125,8 +125,8 @@ update_icon_cache() {
install_resources_to_venv() { install_resources_to_venv() {
print_info "Installing resources to virtual environment..." print_info "Installing resources to virtual environment..."
# Target directory: $VENV_DIR/share/vocalinux/resources # Target directory: $VENV_DIR/share/voculinux/resources
local VENV_RESOURCES_DIR="$VENV_DIR/share/vocalinux/resources" local VENV_RESOURCES_DIR="$VENV_DIR/share/voculinux/resources"
# Create directories # Create directories
mkdir -p "$VENV_RESOURCES_DIR/icons/scalable" || { mkdir -p "$VENV_RESOURCES_DIR/icons/scalable" || {
+4 -4
View File
@@ -27,7 +27,7 @@ run_interactive_install() {
EOF EOF
echo "Welcome! This guided installation will help you set up Vocalinux" echo "Welcome! This guided installation will help you set up Voculinux"
echo "with the best options for your system." echo "with the best options for your system."
echo "" echo ""
echo "Local engines (whisper.cpp, Whisper, Faster Whisper, VOSK) process audio on-device after you download a model." echo "Local engines (whisper.cpp, Whisper, Faster Whisper, VOSK) process audio on-device after you download a model."
@@ -38,11 +38,11 @@ EOF
# and update in place instead of redoing first-time setup. Choosing the full # and update in place instead of redoing first-time setup. Choosing the full
# guided setup continues below and can re-point the engine deliberately. # guided setup continues below and can re-point the engine deliberately.
if [[ "${EXISTING_INSTALL:-no}" == "yes" ]]; then if [[ "${EXISTING_INSTALL:-no}" == "yes" ]]; then
echo "An existing Vocalinux installation was detected:" echo "An existing Voculinux installation was detected:"
echo " Configuration: $CONFIG_DIR/config.json (kept unchanged by an upgrade)" echo " Configuration: $CONFIG_DIR/config.json (kept unchanged by an upgrade)"
echo " Virtual env: $VENV_DIR (reused by an upgrade)" echo " Virtual env: $VENV_DIR (reused by an upgrade)"
echo "" echo ""
echo " 1. Upgrade (keep current settings) - update Vocalinux only" echo " 1. Upgrade (keep current settings) - update Voculinux only"
echo " 2. Full guided setup - choose engine, backend, and model options" echo " 2. Full guided setup - choose engine, backend, and model options"
echo "" echo ""
@@ -330,7 +330,7 @@ EOF
echo " Remote Server: $REMOTE_DISPLAY" echo " Remote Server: $REMOTE_DISPLAY"
fi fi
echo " Models: $MODELS_DISPLAY" echo " Models: $MODELS_DISPLAY"
echo " Install Location: ${INSTALL_DIR:-\$HOME/.local/share/vocalinux}" echo " Install Location: ${INSTALL_DIR:-\$HOME/.local/share/voculinux}"
echo "" echo ""
read_prompt -p "Press Enter to continue with installation, or Ctrl+C to cancel..." read_prompt -p "Press Enter to continue with installation, or Ctrl+C to cancel..."
echo "" echo ""
+8 -8
View File
@@ -191,7 +191,7 @@ install_whisper_model() {
print_info "Downloading Whisper tiny model..." print_info "Downloading Whisper tiny model..."
print_info "This may take a few minutes depending on your internet connection." print_info "This may take a few minutes depending on your internet connection."
local TEMP_FILE="$VOCALINUX_TMP_DIR/tiny.pt" local TEMP_FILE="$VOCULINUX_TMP_DIR/tiny.pt"
if ! download_model_file "$TINY_MODEL_URL" "$TEMP_FILE" "Whisper model"; then if ! download_model_file "$TINY_MODEL_URL" "$TEMP_FILE" "Whisper model"; then
return 1 return 1
@@ -214,7 +214,7 @@ install_whisper_model() {
print_success "Whisper tiny model installed successfully ($MODEL_SIZE)" print_success "Whisper tiny model installed successfully ($MODEL_SIZE)"
# Create a marker file to indicate this model was pre-installed # Create a marker file to indicate this model was pre-installed
echo "$(date)" > "$WHISPER_DIR/.vocalinux_preinstalled" echo "$(date)" > "$WHISPER_DIR/.voculinux_preinstalled"
return 0 return 0
else else
@@ -241,7 +241,7 @@ install_vosk_models() {
# is deleted after unpacking. So whoever unpacks it records the verified zip # is deleted after unpacking. So whoever unpacks it records the verified zip
# digest in a stamp (here, and in the app's own downloader), and the directory # digest in a stamp (here, and in the app's own downloader), and the directory
# is trusted only while that stamp matches what we pin today. # is trusted only while that stamp matches what we pin today.
local SMALL_MODEL_STAMP="$SMALL_MODEL_PATH/.vocalinux_verified" local SMALL_MODEL_STAMP="$SMALL_MODEL_PATH/.voculinux_verified"
local EXPECTED_ZIP_DIGEST local EXPECTED_ZIP_DIGEST
EXPECTED_ZIP_DIGEST=$(pinned_digest_for "$SMALL_MODEL_ARCHIVE" || true) EXPECTED_ZIP_DIGEST=$(pinned_digest_for "$SMALL_MODEL_ARCHIVE" || true)
if [ -d "$SMALL_MODEL_PATH" ]; then if [ -d "$SMALL_MODEL_PATH" ]; then
@@ -282,7 +282,7 @@ install_vosk_models() {
print_info "Downloading small VOSK model (approximately 40MB)..." print_info "Downloading small VOSK model (approximately 40MB)..."
print_info "This may take a few minutes depending on your internet connection." print_info "This may take a few minutes depending on your internet connection."
local TEMP_ZIP="$VOCALINUX_TMP_DIR/$(basename $SMALL_MODEL_URL)" local TEMP_ZIP="$VOCULINUX_TMP_DIR/$(basename $SMALL_MODEL_URL)"
if ! download_model_file "$SMALL_MODEL_URL" "$TEMP_ZIP" "VOSK model"; then if ! download_model_file "$SMALL_MODEL_URL" "$TEMP_ZIP" "VOSK model"; then
return 1 return 1
@@ -334,13 +334,13 @@ install_vosk_models() {
fi fi
chmod -R 755 "$STAGING_DIR/$SMALL_MODEL_NAME" chmod -R 755 "$STAGING_DIR/$SMALL_MODEL_NAME"
echo "$(date)" > "$STAGING_DIR/$SMALL_MODEL_NAME/.vocalinux_preinstalled" echo "$(date)" > "$STAGING_DIR/$SMALL_MODEL_NAME/.voculinux_preinstalled"
# Record the digest this tree was extracted from, so a later run can tell a # Record the digest this tree was extracted from, so a later run can tell a
# verified model from one that merely exists. Written before the swap: an # verified model from one that merely exists. Written before the swap: an
# unstamped tree is one this function would download all over again. # unstamped tree is one this function would download all over again.
if [ -z "$EXPECTED_ZIP_DIGEST" ] || if [ -z "$EXPECTED_ZIP_DIGEST" ] ||
! printf '%s\n' "$EXPECTED_ZIP_DIGEST" > "$STAGING_DIR/$SMALL_MODEL_NAME/.vocalinux_verified"; then ! printf '%s\n' "$EXPECTED_ZIP_DIGEST" > "$STAGING_DIR/$SMALL_MODEL_NAME/.voculinux_verified"; then
print_error "Could not record the verified digest for $SMALL_MODEL_NAME" print_error "Could not record the verified digest for $SMALL_MODEL_NAME"
rm -rf "$STAGING_DIR" rm -rf "$STAGING_DIR"
return 1 return 1
@@ -418,7 +418,7 @@ install_whispercpp_model() {
print_info "Downloading whisper.cpp tiny model..." print_info "Downloading whisper.cpp tiny model..."
print_info "This may take a few minutes depending on your internet connection." print_info "This may take a few minutes depending on your internet connection."
local TEMP_FILE="$VOCALINUX_TMP_DIR/ggml-tiny.bin" local TEMP_FILE="$VOCULINUX_TMP_DIR/ggml-tiny.bin"
if ! download_model_file "$TINY_MODEL_URL" "$TEMP_FILE" "whisper.cpp model"; then if ! download_model_file "$TINY_MODEL_URL" "$TEMP_FILE" "whisper.cpp model"; then
return 1 return 1
@@ -441,7 +441,7 @@ install_whispercpp_model() {
print_success "whisper.cpp tiny model installed successfully ($MODEL_SIZE)" print_success "whisper.cpp tiny model installed successfully ($MODEL_SIZE)"
# Create a marker file to indicate this model was pre-installed # Create a marker file to indicate this model was pre-installed
echo "$(date)" > "$WHISPERCPP_DIR/.vocalinux_preinstalled" echo "$(date)" > "$WHISPERCPP_DIR/.voculinux_preinstalled"
return 0 return 0
else else
+1 -1
View File
@@ -565,7 +565,7 @@ install_system_dependencies() {
print_error "Unsupported distribution family: $DISTRO_FAMILY" print_error "Unsupported distribution family: $DISTRO_FAMILY"
print_info "" print_info ""
print_info "Your distribution ($DISTRO_NAME) is not officially supported." print_info "Your distribution ($DISTRO_NAME) is not officially supported."
print_info "However, you can still install Vocalinux manually:" print_info "However, you can still install Voculinux manually:"
print_info "" print_info ""
print_info "1. Run the dependency checker:" print_info "1. Run the dependency checker:"
print_info " bash scripts/check-system-deps.sh" print_info " bash scripts/check-system-deps.sh"
+122 -122
View File
@@ -1,6 +1,6 @@
#!/bin/bash #!/bin/bash
# Vocalinux Installer # Voculinux Installer
# This script installs the Vocalinux application and its dependencies # This script installs the Voculinux application and its dependencies
# -E: ERR trap propagates into functions/subshells # -E: ERR trap propagates into functions/subshells
# -e: exit on unhandled command failure -u: error on unset variables # -e: exit on unhandled command failure -u: error on unset variables
@@ -118,7 +118,7 @@ is_kde_plasma_session() {
print_kde_wayland_ibus_hint() { print_kde_wayland_ibus_hint() {
print_warning "KDE Plasma Wayland detected." print_warning "KDE Plasma Wayland detected."
print_info "For direct dictation into apps, open System Settings -> Keyboard -> Virtual Keyboard and select 'IBus Wayland'." print_info "For direct dictation into apps, open System Settings -> Keyboard -> Virtual Keyboard and select 'IBus Wayland'."
print_info "After changing it, restart Vocalinux or log out and back in." print_info "After changing it, restart Voculinux or log out and back in."
} }
# Read a numeric PID from a file (instance.lock / engine.pid). Empty if missing/invalid. # Read a numeric PID from a file (instance.lock / engine.pid). Empty if missing/invalid.
@@ -133,9 +133,9 @@ read_pid_file() {
fi fi
} }
# True only for the Vocalinux app / IBus engine — not editors, shells, or tests # True only for the Voculinux app / IBus engine — not editors, shells, or tests
# whose argv merely contains the string "vocalinux" (e.g. cwd under the repo). # whose argv merely contains the string "voculinux" (e.g. cwd under the repo).
is_vocalinux_process() { is_voculinux_process() {
local pid="$1" local pid="$1"
if [ "$pid" = "$$" ] || [ "$pid" = "$PPID" ]; then if [ "$pid" = "$$" ] || [ "$pid" = "$PPID" ]; then
@@ -163,18 +163,18 @@ is_vocalinux_process() {
return 0 return 0
fi fi
# IBus engine: .../vocalinux/.../ibus_engine.py # IBus engine: .../voculinux/.../ibus_engine.py
if [[ "$cmdline" == *"ibus_engine.py"* && "$cmdline" == *"vocalinux"* ]]; then if [[ "$cmdline" == *"ibus_engine.py"* && "$cmdline" == *"voculinux"* ]]; then
return 0 return 0
fi fi
# Console-script entrypoints. Setuptools scripts are executed as # Console-script entrypoints. Setuptools scripts are executed as
# `python …/bin/vocalinux`, so argv0 is python — scan every arg for the # `python …/bin/voculinux`, so argv0 is python — scan every arg for the
# script path (require …/bin/vocalinux to avoid matching a repo directory). # script path (require …/bin/voculinux to avoid matching a repo directory).
local arg local arg
while IFS= read -r -d '' arg; do while IFS= read -r -d '' arg; do
case "$arg" in case "$arg" in
vocalinux|vocalinux-gui|*/bin/vocalinux|*/bin/vocalinux-gui) voculinux|voculinux-gui|*/bin/voculinux|*/bin/voculinux-gui)
return 0 return 0
;; ;;
esac esac
@@ -184,16 +184,16 @@ is_vocalinux_process() {
} }
# Prefer the PIDs the app writes; fall back to narrow entrypoint patterns only. # Prefer the PIDs the app writes; fall back to narrow entrypoint patterns only.
# Deliberately does NOT use `pgrep -f vocalinux` (matches editors/shells/cwd paths). # Deliberately does NOT use `pgrep -f voculinux` (matches editors/shells/cwd paths).
get_vocalinux_pids() { get_voculinux_pids() {
local data_home="${XDG_DATA_HOME:-$HOME/.local/share}" local data_home="${XDG_DATA_HOME:-$HOME/.local/share}"
local -A seen=() local -A seen=()
local pid candidate local pid candidate
local -a candidates=() local -a candidates=()
for candidate in \ for candidate in \
"$(read_pid_file "$data_home/vocalinux/instance.lock")" \ "$(read_pid_file "$data_home/voculinux/instance.lock")" \
"$(read_pid_file "$data_home/vocalinux-ibus/engine.pid")" "$(read_pid_file "$data_home/voculinux-ibus/engine.pid")"
do do
[ -n "$candidate" ] && candidates+=("$candidate") [ -n "$candidate" ] && candidates+=("$candidate")
done done
@@ -202,16 +202,16 @@ get_vocalinux_pids() {
while read -r candidate; do while read -r candidate; do
[ -n "$candidate" ] && candidates+=("$candidate") [ -n "$candidate" ] && candidates+=("$candidate")
done < <( done < <(
pgrep -u "$(id -u)" -x vocalinux 2>/dev/null || true pgrep -u "$(id -u)" -x voculinux 2>/dev/null || true
pgrep -u "$(id -u)" -x vocalinux-gui 2>/dev/null || true pgrep -u "$(id -u)" -x voculinux-gui 2>/dev/null || true
pgrep -u "$(id -u)" -f -- '-m vocalinux\.main' 2>/dev/null || true pgrep -u "$(id -u)" -f -- '-m voculinux\.main' 2>/dev/null || true
pgrep -u "$(id -u)" -f -- 'vocalinux/.*/ibus_engine\.py' 2>/dev/null || true pgrep -u "$(id -u)" -f -- 'voculinux/.*/ibus_engine\.py' 2>/dev/null || true
) )
for pid in "${candidates[@]}"; do for pid in "${candidates[@]}"; do
[ -n "${seen[$pid]:-}" ] && continue [ -n "${seen[$pid]:-}" ] && continue
seen[$pid]=1 seen[$pid]=1
if is_vocalinux_process "$pid"; then if is_voculinux_process "$pid"; then
echo "$pid" echo "$pid"
fi fi
done done
@@ -219,10 +219,10 @@ get_vocalinux_pids() {
check_running_processes() { check_running_processes() {
local PIDS local PIDS
PIDS=$(get_vocalinux_pids || true) PIDS=$(get_voculinux_pids || true)
if [ -n "$PIDS" ]; then if [ -n "$PIDS" ]; then
print_warning "Found running Vocalinux process(es):" print_warning "Found running Voculinux process(es):"
local pid local pid
for pid in $PIDS; do for pid in $PIDS; do
local cmd local cmd
@@ -232,24 +232,24 @@ check_running_processes() {
echo "" echo ""
if [[ "$NON_INTERACTIVE" == "yes" ]]; then if [[ "$NON_INTERACTIVE" == "yes" ]]; then
print_info "Non-interactive mode: stopping Vocalinux automatically..." print_info "Non-interactive mode: stopping Voculinux automatically..."
else else
read_prompt -p "Vocalinux must be stopped before installation. Kill running process(es)? (Y/n) " -n 1 -r read_prompt -p "Voculinux must be stopped before installation. Kill running process(es)? (Y/n) " -n 1 -r
echo echo
if [[ $REPLY =~ ^[Nn]$ ]]; then if [[ $REPLY =~ ^[Nn]$ ]]; then
print_error "Cannot proceed with installation while Vocalinux is running." print_error "Cannot proceed with installation while Voculinux is running."
print_info "Please stop Vocalinux manually and run the installer again." print_info "Please stop Voculinux manually and run the installer again."
exit "$EXIT_USER_ABORT" exit "$EXIT_USER_ABORT"
fi fi
fi fi
print_info "Stopping Vocalinux..." print_info "Stopping Voculinux..."
# shellcheck disable=SC2086 # shellcheck disable=SC2086
echo $PIDS | xargs -r kill -TERM 2>/dev/null || true echo $PIDS | xargs -r kill -TERM 2>/dev/null || true
sleep 2 sleep 2
local REMAINING_PIDS local REMAINING_PIDS
REMAINING_PIDS=$(get_vocalinux_pids || true) REMAINING_PIDS=$(get_voculinux_pids || true)
if [ -n "$REMAINING_PIDS" ]; then if [ -n "$REMAINING_PIDS" ]; then
print_warning "Some processes still running, forcing termination..." print_warning "Some processes still running, forcing termination..."
@@ -259,13 +259,13 @@ check_running_processes() {
fi fi
local FINAL_PIDS local FINAL_PIDS
FINAL_PIDS=$(get_vocalinux_pids || true) FINAL_PIDS=$(get_voculinux_pids || true)
if [ -n "$FINAL_PIDS" ]; then if [ -n "$FINAL_PIDS" ]; then
print_error "Could not terminate all Vocalinux processes: $FINAL_PIDS" print_error "Could not terminate all Voculinux processes: $FINAL_PIDS"
print_error "Please manually kill these processes and run the installer again." print_error "Please manually kill these processes and run the installer again."
exit "$EXIT_USER_ABORT" exit "$EXIT_USER_ABORT"
else else
print_success "All Vocalinux processes stopped" print_success "All Voculinux processes stopped"
fi fi
fi fi
} }
@@ -373,7 +373,7 @@ while [[ $# -gt 0 ]]; do
shift shift
;; ;;
--help) --help)
echo "Vocalinux Installer" echo "Voculinux Installer"
echo "" echo ""
echo "Usage: $0 [options]" echo "Usage: $0 [options]"
echo "" echo ""
@@ -410,7 +410,7 @@ while [[ $# -gt 0 ]]; do
echo " $0 --dev --test # Dev mode with tests" echo " $0 --dev --test # Dev mode with tests"
echo "" echo ""
echo "During installation a full transcript is saved to" echo "During installation a full transcript is saved to"
echo " ~/.local/state/vocalinux/install-<timestamp>.log" echo " ~/.local/state/voculinux/install-<timestamp>.log"
echo "" echo ""
echo "Exit codes:" echo "Exit codes:"
echo " 0 success" echo " 0 success"
@@ -437,7 +437,7 @@ fi
# Global install log: everything (stdout + stderr) is teed to this file so # Global install log: everything (stdout + stderr) is teed to this file so
# failures can be debugged after the fact. The path is printed at exit. # failures can be debugged after the fact. The path is printed at exit.
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
INSTALL_LOG_DIR="${XDG_STATE_HOME:-$HOME/.local/state}/vocalinux" INSTALL_LOG_DIR="${XDG_STATE_HOME:-$HOME/.local/state}/voculinux"
if ! mkdir -p "$INSTALL_LOG_DIR" 2>/dev/null; then if ! mkdir -p "$INSTALL_LOG_DIR" 2>/dev/null; then
INSTALL_LOG_DIR="${TMPDIR:-/tmp}" INSTALL_LOG_DIR="${TMPDIR:-/tmp}"
fi fi
@@ -447,18 +447,18 @@ exec > >(tee -a "$INSTALL_LOG_FILE") 2>&1
# Scratch directory for pip logs, model downloads, and other temps. # Scratch directory for pip logs, model downloads, and other temps.
# Removed on success; kept for inspection (with a notice) when the install fails. # Removed on success; kept for inspection (with a notice) when the install fails.
# Exporting TMPDIR routes pip/wget/curl scratch files into this directory. # Exporting TMPDIR routes pip/wget/curl scratch files into this directory.
VOCALINUX_TMP_DIR="$(mktemp -d "${TMPDIR:-/tmp}/vocalinux-install.XXXXXXXX")" VOCULINUX_TMP_DIR="$(mktemp -d "${TMPDIR:-/tmp}/voculinux-install.XXXXXXXX")"
export TMPDIR="$VOCALINUX_TMP_DIR" export TMPDIR="$VOCULINUX_TMP_DIR"
cleanup_on_exit() { cleanup_on_exit() {
local rc=$? local rc=$?
if [ "$rc" -eq "$EXIT_OK" ]; then if [ "$rc" -eq "$EXIT_OK" ]; then
rm -rf "$VOCALINUX_TMP_DIR" rm -rf "$VOCULINUX_TMP_DIR"
else else
print_error "" print_error ""
print_error "Installation did not complete (exit code $rc)." print_error "Installation did not complete (exit code $rc)."
print_error "Full install log: $INSTALL_LOG_FILE" print_error "Full install log: $INSTALL_LOG_FILE"
print_error "Scratch files kept for inspection: $VOCALINUX_TMP_DIR" print_error "Scratch files kept for inspection: $VOCULINUX_TMP_DIR"
fi fi
} }
trap cleanup_on_exit EXIT trap cleanup_on_exit EXIT
@@ -478,7 +478,7 @@ cat << "EOF"
EOF EOF
print_info "Vocalinux Installer" print_info "Voculinux Installer"
print_info "==============================" print_info "=============================="
echo "" echo ""
@@ -509,21 +509,21 @@ resolve_install_tag() {
resolve_install_tag resolve_install_tag
# Check if running from within the vocalinux repo or remotely (via curl) # Check if running from within the voculinux repo or remotely (via curl)
# VOCALINUX_REPO_URL overrides the clone source so the remote-install gate can # VOCULINUX_REPO_URL overrides the clone source so the remote-install gate can
# serve a tag from a local mirror; public installs use the repository below. # serve a tag from a local mirror; public installs use the repository below.
REPO_URL="${VOCALINUX_REPO_URL:-https://github.com/VocaHQ/vocalinux.git}" REPO_URL="${VOCULINUX_REPO_URL:-https://github.com/VocaHQ/vocalinux.git}"
INSTALL_DIR="" INSTALL_DIR=""
CLEANUP_ON_EXIT="${VOCALINUX_REMOTE_INSTALL:-no}" CLEANUP_ON_EXIT="${VOCULINUX_REMOTE_INSTALL:-no}"
handoff_to_tagged_installer() { handoff_to_tagged_installer() {
local tagged_installer="$INSTALL_DIR/install.sh" local tagged_installer="$INSTALL_DIR/install.sh"
local remote_venv="$HOME/.local/share/vocalinux/venv" local remote_venv="$HOME/.local/share/voculinux/venv"
if grep -q 'CLEANUP_ON_EXIT="${VOCALINUX_REMOTE_INSTALL:-no}"' "$tagged_installer"; then if grep -q 'CLEANUP_ON_EXIT="${VOCULINUX_REMOTE_INSTALL:-no}"' "$tagged_installer"; then
export VOCALINUX_REMOTE_INSTALL=yes export VOCULINUX_REMOTE_INSTALL=yes
export TMPDIR="$(dirname "$VOCALINUX_TMP_DIR")" export TMPDIR="$(dirname "$VOCULINUX_TMP_DIR")"
rmdir "$VOCALINUX_TMP_DIR" rmdir "$VOCULINUX_TMP_DIR"
# A bootstrap read from a pipe (curl | bash) leaves its unread tail on # A bootstrap read from a pipe (curl | bash) leaves its unread tail on
# stdin: bash hands the pipe to the tagged installer, which reads its # stdin: bash hands the pipe to the tagged installer, which reads its
# commands from a file instead, so nothing drains it. A producer still # commands from a file instead, so nothing drains it. A producer still
@@ -537,12 +537,12 @@ handoff_to_tagged_installer() {
fi fi
bash "$tagged_installer" "${INSTALLER_ARGS[@]}" "--venv-dir=$remote_venv" || return $? bash "$tagged_installer" "${INSTALLER_ARGS[@]}" "--venv-dir=$remote_venv" || return $?
if [ ! -f "$INSTALL_DIR/activate-vocalinux.sh" ]; then if [ ! -f "$INSTALL_DIR/activate-voculinux.sh" ]; then
print_error "The tagged installer did not create activate-vocalinux.sh." print_error "The tagged installer did not create activate-voculinux.sh."
return 1 return 1
fi fi
mkdir -p "$HOME/.local/bin" mkdir -p "$HOME/.local/bin"
mv "$INSTALL_DIR/activate-vocalinux.sh" "$HOME/.local/bin/activate-vocalinux.sh" mv "$INSTALL_DIR/activate-voculinux.sh" "$HOME/.local/bin/activate-voculinux.sh"
} }
# Function to check and install git if needed # Function to check and install git if needed
@@ -664,17 +664,17 @@ ensure_git_installed() {
print_success "git installed successfully!" print_success "git installed successfully!"
} }
# Check if running from within the vocalinux repository # Check if running from within the voculinux repository
# Validate that pyproject.toml/setup.py actually belongs to vocalinux before entering local repo mode # Validate that pyproject.toml/setup.py actually belongs to voculinux before entering local repo mode
IS_VOCALINUX_LOCAL=false IS_VOCULINUX_LOCAL=false
if [ -f "pyproject.toml" ] && grep -q 'name = "vocalinux"' "pyproject.toml" 2>/dev/null; then if [ -f "pyproject.toml" ] && grep -q 'name = "voculinux"' "pyproject.toml" 2>/dev/null; then
IS_VOCALINUX_LOCAL=true IS_VOCULINUX_LOCAL=true
elif [ -f "setup.py" ] && grep -q "vocalinux" "setup.py" 2>/dev/null; then elif [ -f "setup.py" ] && grep -q "voculinux" "setup.py" 2>/dev/null; then
IS_VOCALINUX_LOCAL=true IS_VOCULINUX_LOCAL=true
fi fi
if [ "$IS_VOCALINUX_LOCAL" = true ]; then if [ "$IS_VOCULINUX_LOCAL" = true ]; then
# Running from within the vocalinux repo # Running from within the voculinux repo
INSTALL_DIR="$(pwd)" INSTALL_DIR="$(pwd)"
print_info "Running from local repository: $INSTALL_DIR" print_info "Running from local repository: $INSTALL_DIR"
# Convert relative VENV_DIR to absolute for wrapper scripts. # Convert relative VENV_DIR to absolute for wrapper scripts.
@@ -690,26 +690,26 @@ else
print_error "Re-run with an explicit release tag, e.g.: --tag=<release>" print_error "Re-run with an explicit release tag, e.g.: --tag=<release>"
exit "$EXIT_NETWORK" exit "$EXIT_NETWORK"
fi fi
print_info "Installing Vocalinux version: ${INSTALL_TAG}" print_info "Installing Voculinux version: ${INSTALL_TAG}"
# Ensure git is installed before attempting to clone # Ensure git is installed before attempting to clone
ensure_git_installed ensure_git_installed
INSTALL_DIR="$HOME/.local/share/vocalinux-install" INSTALL_DIR="$HOME/.local/share/voculinux-install"
mkdir -p "$INSTALL_DIR" mkdir -p "$INSTALL_DIR"
if [ -d "$INSTALL_DIR/.git" ]; then if [ -d "$INSTALL_DIR/.git" ]; then
print_info "Updating existing clone..." print_info "Updating existing clone..."
cd "$INSTALL_DIR" cd "$INSTALL_DIR"
if ! git fetch origin tag "$INSTALL_TAG" || ! git reset --hard "$INSTALL_TAG"; then if ! git fetch origin tag "$INSTALL_TAG" || ! git reset --hard "$INSTALL_TAG"; then
print_error "Failed to update the Vocalinux clone to $INSTALL_TAG." print_error "Failed to update the Voculinux clone to $INSTALL_TAG."
print_error "Check the install log and your network connection: $INSTALL_LOG_FILE" print_error "Check the install log and your network connection: $INSTALL_LOG_FILE"
exit "$EXIT_NETWORK" exit "$EXIT_NETWORK"
fi fi
else else
rm -rf "$INSTALL_DIR" rm -rf "$INSTALL_DIR"
git clone --depth 1 --branch "$INSTALL_TAG" "$REPO_URL" "$INSTALL_DIR" || { git clone --depth 1 --branch "$INSTALL_TAG" "$REPO_URL" "$INSTALL_DIR" || {
print_error "Failed to clone Vocalinux repository" print_error "Failed to clone Voculinux repository"
exit "$EXIT_NETWORK" exit "$EXIT_NETWORK"
} }
cd "$INSTALL_DIR" cd "$INSTALL_DIR"
@@ -1270,7 +1270,7 @@ detect_distro
# Check compatibility. These tiers mirror docs/DISTRO_COMPATIBILITY.md, and the # Check compatibility. These tiers mirror docs/DISTRO_COMPATIBILITY.md, and the
# distro matrix builds on ubuntu, debian and fedora. Nothing here decides whether # distro matrix builds on ubuntu, debian and fedora. Nothing here decides whether
# the install can proceed: what Vocalinux needs is an interpreter at the floor # the install can proceed: what Voculinux needs is an interpreter at the floor
# and that interpreter's distro PyGObject, checked by check_python_version() # and that interpreter's distro PyGObject, checked by check_python_version()
# (fatal) and require_distro_gi() further down. This block only says how much # (fatal) and require_distro_gi() further down. This block only says how much
# help the package step is likely to be, so it must not turn away a distro the # help the package step is likely to be, so it must not turn away a distro the
@@ -1286,7 +1286,7 @@ case "$DISTRO_FAMILY" in
;; ;;
*) *)
print_warning "This installer has not been tested on $DISTRO_NAME; you may need to install dependencies manually." print_warning "This installer has not been tested on $DISTRO_NAME; you may need to install dependencies manually."
print_warning "Vocalinux itself does not care which distribution it runs on, only that Python and PyGObject are new enough." print_warning "Voculinux itself does not care which distribution it runs on, only that Python and PyGObject are new enough."
if [[ "$NON_INTERACTIVE" == "yes" ]]; then if [[ "$NON_INTERACTIVE" == "yes" ]]; then
print_info "Non-interactive mode: continuing anyway..." print_info "Non-interactive mode: continuing anyway..."
else else
@@ -1301,8 +1301,8 @@ esac
# Define XDG directories early: existing-install detection and the mode menu # Define XDG directories early: existing-install detection and the mode menu
# need CONFIG_DIR before system dependency installation asks anything. # need CONFIG_DIR before system dependency installation asks anything.
CONFIG_DIR="${XDG_CONFIG_HOME:-$HOME/.config}/vocalinux" CONFIG_DIR="${XDG_CONFIG_HOME:-$HOME/.config}/voculinux"
DATA_DIR="${XDG_DATA_HOME:-$HOME/.local/share}/vocalinux" DATA_DIR="${XDG_DATA_HOME:-$HOME/.local/share}/voculinux"
DESKTOP_DIR="${XDG_DATA_HOME:-$HOME/.local/share}/applications" DESKTOP_DIR="${XDG_DATA_HOME:-$HOME/.local/share}/applications"
ICON_DIR="${XDG_DATA_HOME:-$HOME/.local/share}/icons/hicolor/scalable/apps" ICON_DIR="${XDG_DATA_HOME:-$HOME/.local/share}/icons/hicolor/scalable/apps"
@@ -1322,7 +1322,7 @@ choose_installation_mode() {
echo "" echo ""
echo "Installation Mode:" echo "Installation Mode:"
if [[ "${EXISTING_INSTALL:-no}" == "yes" ]]; then if [[ "${EXISTING_INSTALL:-no}" == "yes" ]]; then
echo " 1. Upgrade (keep current settings) - update Vocalinux, keep your setup" echo " 1. Upgrade (keep current settings) - update Voculinux, keep your setup"
echo " 2. Interactive - guided setup with recommendations" echo " 2. Interactive - guided setup with recommendations"
echo " 3. Automatic - quick install with defaults (whisper.cpp)" echo " 3. Automatic - quick install with defaults (whisper.cpp)"
echo "" echo ""
@@ -1653,7 +1653,7 @@ install_pinned_build_tools() {
print_info "Installing pinned pip and source-build tools..." print_info "Installing pinned pip and source-build tools..."
"$VENV_DIR/bin/python" -m pip install --require-hashes --no-deps \ "$VENV_DIR/bin/python" -m pip install --require-hashes --no-deps \
--only-binary=:all: --ignore-installed \ --only-binary=:all: --ignore-installed \
-r "$reqs_file" --log "$VOCALINUX_TMP_DIR/bootstrap.log" -r "$reqs_file" --log "$VOCULINUX_TMP_DIR/bootstrap.log"
} }
install_pinned_build_tools || { install_pinned_build_tools || {
print_error "Failed to install the pinned build tools. Check requirements/installer-build.txt." print_error "Failed to install the pinned build tools. Check requirements/installer-build.txt."
@@ -1668,15 +1668,15 @@ if [[ "$CLEANUP_ON_EXIT" == "yes" ]]; then
else else
ACTIVATION_SCRIPT_DIR="." ACTIVATION_SCRIPT_DIR="."
fi fi
ACTIVATION_SCRIPT="$ACTIVATION_SCRIPT_DIR/activate-vocalinux.sh" ACTIVATION_SCRIPT="$ACTIVATION_SCRIPT_DIR/activate-voculinux.sh"
cat > "$ACTIVATION_SCRIPT" << EOF cat > "$ACTIVATION_SCRIPT" << EOF
#!/bin/bash #!/bin/bash
# This script activates the Vocalinux virtual environment # This script activates the Voculinux virtual environment
export PYTHONNOUSERSITE=1 export PYTHONNOUSERSITE=1
source "$VENV_DIR/bin/activate" source "$VENV_DIR/bin/activate"
echo "Vocalinux virtual environment activated." echo "Voculinux virtual environment activated."
echo "To start the application, run: vocalinux" echo "To start the application, run: voculinux"
EOF EOF
chmod +x "$ACTIVATION_SCRIPT" chmod +x "$ACTIVATION_SCRIPT"
print_info "Created activation script: $ACTIVATION_SCRIPT" print_info "Created activation script: $ACTIVATION_SCRIPT"
@@ -1897,7 +1897,7 @@ install_cpu_pywhispercpp() {
pip_reinstall_pywhispercpp() { pip_reinstall_pywhispercpp() {
local pip_log="$1" local pip_log="$1"
shift shift
local reqs_file="$VOCALINUX_TMP_DIR/pywhispercpp.txt" local reqs_file="$VOCULINUX_TMP_DIR/pywhispercpp.txt"
"$VENV_DIR/bin/python" "$INSTALL_DIR/scripts/installer_requirements.py" \ "$VENV_DIR/bin/python" "$INSTALL_DIR/scripts/installer_requirements.py" \
"$INSTALL_DIR/requirements/runtime.txt" "$reqs_file" --package pywhispercpp || return 1 "$INSTALL_DIR/requirements/runtime.txt" "$reqs_file" --package pywhispercpp || return 1
# --no-deps preserves the locked runtime and avoids reinstalling numpy # --no-deps preserves the locked runtime and avoids reinstalling numpy
@@ -2190,7 +2190,7 @@ install_whispercpp_with_gpu_support() {
fi fi
if ! is_pywhispercpp_installed; then if ! is_pywhispercpp_installed; then
print_warning "pywhispercpp is still unavailable; setting VOSK as the default engine so Vocalinux can start." print_warning "pywhispercpp is still unavailable; setting VOSK as the default engine so Voculinux can start."
print_warning "You can switch back to whisper.cpp from Settings after reinstalling pywhispercpp." print_warning "You can switch back to whisper.cpp from Settings after reinstalling pywhispercpp."
SELECTED_ENGINE="vosk" SELECTED_ENGINE="vosk"
@@ -2304,7 +2304,7 @@ pip_install_extras_skip_pygobject() {
# Function to install Python package with error handling and verification # Function to install Python package with error handling and verification
install_python_package() { install_python_package() {
# Pip logs live in the install scratch dir so a failed run can keep them. # Pip logs live in the install scratch dir so a failed run can keep them.
local PIP_LOG_DIR="$VOCALINUX_TMP_DIR/pip" local PIP_LOG_DIR="$VOCULINUX_TMP_DIR/pip"
mkdir -p "$PIP_LOG_DIR" mkdir -p "$PIP_LOG_DIR"
local PIP_LOG_FILE="$PIP_LOG_DIR/pip_log.txt" local PIP_LOG_FILE="$PIP_LOG_DIR/pip_log.txt"
@@ -2351,21 +2351,21 @@ PY
print_success "Neural VAD support installed and verified successfully." print_success "Neural VAD support installed and verified successfully."
else else
print_warning "Neural VAD dependencies installed, but Silero VAD could not be verified." print_warning "Neural VAD dependencies installed, but Silero VAD could not be verified."
print_warning "Vocalinux will use amplitude-based VAD until this is resolved." print_warning "Voculinux will use amplitude-based VAD until this is resolved."
fi fi
else else
print_warning "Failed to install neural VAD support." print_warning "Failed to install neural VAD support."
print_warning "Vocalinux will still work using amplitude-based VAD." print_warning "Voculinux will still work using amplitude-based VAD."
print_warning "Check the pip log for details: $PIP_LOG_FILE" print_warning "Check the pip log for details: $PIP_LOG_FILE"
fi fi
} }
if [[ "$DEV_MODE" == "yes" ]]; then if [[ "$DEV_MODE" == "yes" ]]; then
print_info "Installing Vocalinux in development mode..." print_info "Installing Voculinux in development mode..."
# Install in development mode with logging # Install in development mode with logging
pip_install_project_skip_pygobject "$PIP_LOG_FILE" -e . || { pip_install_project_skip_pygobject "$PIP_LOG_FILE" -e . || {
print_error "Failed to install Vocalinux in development mode." print_error "Failed to install Voculinux in development mode."
print_error "Check the pip log for details: $PIP_LOG_FILE" print_error "Check the pip log for details: $PIP_LOG_FILE"
return 1 return 1
} }
@@ -2389,11 +2389,11 @@ PY
install_whispercpp_with_gpu_support "$PIP_LOG_FILE" install_whispercpp_with_gpu_support "$PIP_LOG_FILE"
fi fi
else else
print_info "Installing Vocalinux..." print_info "Installing Voculinux..."
# Install the package with logging (includes pywhispercpp by default) # Install the package with logging (includes pywhispercpp by default)
pip_install_project_skip_pygobject "$PIP_LOG_FILE" . || { pip_install_project_skip_pygobject "$PIP_LOG_FILE" . || {
print_error "Failed to install Vocalinux." print_error "Failed to install Voculinux."
print_error "Check the pip log for details: $PIP_LOG_FILE" print_error "Check the pip log for details: $PIP_LOG_FILE"
return 1 return 1
} }
@@ -2701,8 +2701,8 @@ FASTER_WHISPER_CONFIG
# Verify installation # Verify installation
if verify_package_installed; then if verify_package_installed; then
print_success "Vocalinux package installed successfully!" print_success "Voculinux package installed successfully!"
# Pip logs stay under VOCALINUX_TMP_DIR; the EXIT trap removes that # Pip logs stay under VOCULINUX_TMP_DIR; the EXIT trap removes that
# directory on success and keeps it when a later step fails. # directory on success and keeps it when a later step fails.
# GI_TYPELIB_PATH was already detected at the start of install_python_package # GI_TYPELIB_PATH was already detected at the start of install_python_package
@@ -2720,10 +2720,10 @@ else
exec $EXEC_CMD exec $EXEC_CMD
fi' fi'
# Create vocalinux wrapper script # Create voculinux wrapper script
cat > "$HOME/.local/bin/vocalinux" << WRAPPER_EOF cat > "$HOME/.local/bin/voculinux" << WRAPPER_EOF
#!/bin/bash #!/bin/bash
# Wrapper script for Vocalinux that sets required environment variables # Wrapper script for Voculinux that sets required environment variables
# and applies the 'input' group for keyboard shortcuts on Wayland # and applies the 'input' group for keyboard shortcuts on Wayland
export PYTHONNOUSERSITE=1 export PYTHONNOUSERSITE=1
export GI_TYPELIB_PATH=$GI_TYPELIB_DETECTED export GI_TYPELIB_PATH=$GI_TYPELIB_DETECTED
@@ -2754,16 +2754,16 @@ if [ -n "\$PYWHISPERCPP_LIBRARY_PATH" ]; then
export LD_LIBRARY_PATH="\$PYWHISPERCPP_LIBRARY_PATH\${LD_LIBRARY_PATH:+:\$LD_LIBRARY_PATH}" export LD_LIBRARY_PATH="\$PYWHISPERCPP_LIBRARY_PATH\${LD_LIBRARY_PATH:+:\$LD_LIBRARY_PATH}"
fi fi
EXEC_CMD="$VENV_DIR/bin/vocalinux \$*" EXEC_CMD="$VENV_DIR/bin/voculinux \$*"
$SG_CHECK $SG_CHECK
WRAPPER_EOF WRAPPER_EOF
chmod +x "$HOME/.local/bin/vocalinux" chmod +x "$HOME/.local/bin/voculinux"
print_info "Created wrapper: ~/.local/bin/vocalinux" print_info "Created wrapper: ~/.local/bin/voculinux"
# Create vocalinux-gui wrapper script # Create voculinux-gui wrapper script
cat > "$HOME/.local/bin/vocalinux-gui" << WRAPPER_EOF cat > "$HOME/.local/bin/voculinux-gui" << WRAPPER_EOF
#!/bin/bash #!/bin/bash
# Wrapper script for Vocalinux GUI that sets required environment variables # Wrapper script for Voculinux GUI that sets required environment variables
# and applies the 'input' group for keyboard shortcuts on Wayland # and applies the 'input' group for keyboard shortcuts on Wayland
export PYTHONNOUSERSITE=1 export PYTHONNOUSERSITE=1
export GI_TYPELIB_PATH=$GI_TYPELIB_DETECTED export GI_TYPELIB_PATH=$GI_TYPELIB_DETECTED
@@ -2794,11 +2794,11 @@ if [ -n "\$PYWHISPERCPP_LIBRARY_PATH" ]; then
export LD_LIBRARY_PATH="\$PYWHISPERCPP_LIBRARY_PATH\${LD_LIBRARY_PATH:+:\$LD_LIBRARY_PATH}" export LD_LIBRARY_PATH="\$PYWHISPERCPP_LIBRARY_PATH\${LD_LIBRARY_PATH:+:\$LD_LIBRARY_PATH}"
fi fi
EXEC_CMD="$VENV_DIR/bin/vocalinux-gui \$*" EXEC_CMD="$VENV_DIR/bin/voculinux-gui \$*"
$SG_CHECK $SG_CHECK
WRAPPER_EOF WRAPPER_EOF
chmod +x "$HOME/.local/bin/vocalinux-gui" chmod +x "$HOME/.local/bin/voculinux-gui"
print_info "Created wrapper: ~/.local/bin/vocalinux-gui" print_info "Created wrapper: ~/.local/bin/voculinux-gui"
# Check if ~/.local/bin is in PATH # Check if ~/.local/bin is in PATH
if [[ ":$PATH:" != *":$HOME/.local/bin:"* ]]; then if [[ ":$PATH:" != *":$HOME/.local/bin:"* ]]; then
@@ -2809,7 +2809,7 @@ WRAPPER_EOF
return 0 return 0
else else
print_error "Vocalinux package installation verification failed." print_error "Voculinux package installation verification failed."
print_error "Check the pip log for details: $PIP_LOG_FILE" print_error "Check the pip log for details: $PIP_LOG_FILE"
return 1 return 1
fi fi
@@ -2817,7 +2817,7 @@ WRAPPER_EOF
# Install Python package # Install Python package
if ! install_python_package; then if ! install_python_package; then
print_error "Failed to install Vocalinux package. Installation cannot continue." print_error "Failed to install Voculinux package. Installation cannot continue."
exit "$EXIT_NETWORK" exit "$EXIT_NETWORK"
fi fi
@@ -2907,7 +2907,7 @@ PY
if [[ "${UPGRADE_MODE:-no}" == "yes" ]]; then if [[ "${UPGRADE_MODE:-no}" == "yes" ]]; then
# Upgrade mode never rewrites config.json: report instead of repairing. # Upgrade mode never rewrites config.json: report instead of repairing.
print_error "Vocalinux cannot start with this configuration and the engine is not installed." print_error "Voculinux cannot start with this configuration and the engine is not installed."
print_error "Install the engine: $VENV_DIR/bin/pip install $(engine_pip_name "$CONFIGURED_ENGINE")" print_error "Install the engine: $VENV_DIR/bin/pip install $(engine_pip_name "$CONFIGURED_ENGINE")"
print_error "or edit $CONFIG_FILE and set speech_recognition.engine to an installed engine." print_error "or edit $CONFIG_FILE and set speech_recognition.engine to an installed engine."
return 1 return 1
@@ -2934,7 +2934,7 @@ PY
fi fi
done done
print_error "Vocalinux cannot start with this configuration and no working engine is available." print_error "Voculinux cannot start with this configuration and no working engine is available."
print_error "Install the engine: $VENV_DIR/bin/pip install $(engine_pip_name "$CONFIGURED_ENGINE")" print_error "Install the engine: $VENV_DIR/bin/pip install $(engine_pip_name "$CONFIGURED_ENGINE")"
print_error "or edit $CONFIG_FILE and set speech_recognition.engine to an installed engine." print_error "or edit $CONFIG_FILE and set speech_recognition.engine to an installed engine."
return 1 return 1
@@ -2954,7 +2954,7 @@ run_tests() {
# Check if pytest is installed in the virtual environment # Check if pytest is installed in the virtual environment
if ! "$VENV_DIR/bin/python" -c "import pytest" &>/dev/null; then if ! "$VENV_DIR/bin/python" -c "import pytest" &>/dev/null; then
print_info "Installing pytest and related packages..." print_info "Installing pytest and related packages..."
local PIP_LOG_FILE="$VOCALINUX_TMP_DIR/test-deps.log" local PIP_LOG_FILE="$VOCULINUX_TMP_DIR/test-deps.log"
pip_install_extras_skip_pygobject "$PIP_LOG_FILE" dev || { pip_install_extras_skip_pygobject "$PIP_LOG_FILE" dev || {
print_error "Failed to install pytest. Cannot run tests." print_error "Failed to install pytest. Cannot run tests."
return 1 return 1
@@ -2962,7 +2962,7 @@ run_tests() {
fi fi
# Create a directory for test results # Create a directory for test results
local TEST_RESULTS_DIR="${XDG_DATA_HOME:-$HOME/.local/share}/vocalinux/test_results" local TEST_RESULTS_DIR="${XDG_DATA_HOME:-$HOME/.local/share}/voculinux/test_results"
mkdir -p "$TEST_RESULTS_DIR" mkdir -p "$TEST_RESULTS_DIR"
local TEST_RESULTS_FILE="$TEST_RESULTS_DIR/pytest_$(date +%Y%m%d_%H%M%S).xml" local TEST_RESULTS_FILE="$TEST_RESULTS_DIR/pytest_$(date +%Y%m%d_%H%M%S).xml"
@@ -3007,21 +3007,21 @@ verify_installation() {
ISSUES=$((ISSUES + 1)) ISSUES=$((ISSUES + 1))
fi fi
# Check if vocalinux command is available # Check if voculinux command is available
if ! command -v vocalinux &>/dev/null && [ ! -f "$VENV_DIR/bin/vocalinux" ]; then if ! command -v voculinux &>/dev/null && [ ! -f "$VENV_DIR/bin/voculinux" ]; then
print_error "Vocalinux command not found." print_error "Voculinux command not found."
ISSUES=$((ISSUES + 1)) ISSUES=$((ISSUES + 1))
fi fi
# Check if desktop entry is installed # Check if desktop entry is installed
if [ ! -f "$DESKTOP_DIR/vocalinux.desktop" ]; then if [ ! -f "$DESKTOP_DIR/voculinux.desktop" ]; then
print_warning "Desktop entry not found. Application may not appear in application menu." print_warning "Desktop entry not found. Application may not appear in application menu."
ISSUES=$((ISSUES + 1)) ISSUES=$((ISSUES + 1))
fi fi
# Check if icons are installed # Check if icons are installed
local ICON_COUNT=0 local ICON_COUNT=0
for icon in vocalinux.svg vocalinux-microphone.svg vocalinux-microphone-off.svg vocalinux-microphone-process.svg; do for icon in voculinux.svg voculinux-microphone.svg voculinux-microphone-off.svg voculinux-microphone-process.svg; do
if [ -f "$ICON_DIR/$icon" ]; then if [ -f "$ICON_DIR/$icon" ]; then
ICON_COUNT=$((ICON_COUNT + 1)) ICON_COUNT=$((ICON_COUNT + 1))
fi fi
@@ -3034,7 +3034,7 @@ verify_installation() {
# Check if Python package is importable using venv python # Check if Python package is importable using venv python
if ! "$VENV_DIR/bin/python" -c "import vocalinux" &>/dev/null; then if ! "$VENV_DIR/bin/python" -c "import vocalinux" &>/dev/null; then
print_error "Vocalinux Python package cannot be imported." print_error "Voculinux Python package cannot be imported."
ISSUES=$((ISSUES + 1)) ISSUES=$((ISSUES + 1))
fi fi
@@ -3094,7 +3094,7 @@ EOF
# Success or warning message # Success or warning message
if [ "$ISSUES" -eq 0 ]; then if [ "$ISSUES" -eq 0 ]; then
print_success "Vocalinux has been installed successfully!" print_success "Voculinux has been installed successfully!"
else else
print_warning "Installation complete with $ISSUES minor issue(s)" print_warning "Installation complete with $ISSUES minor issue(s)"
print_warning "The application should still work normally." print_warning "The application should still work normally."
@@ -3141,12 +3141,12 @@ EOF
echo " 📦 What Was Installed" echo " 📦 What Was Installed"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "" echo ""
echo " Application: Vocalinux (voice dictation for Linux)" echo " Application: Voculinux (voice dictation for Linux)"
echo " Engine: $ENGINE_DISPLAY_NAME" echo " Engine: $ENGINE_DISPLAY_NAME"
if [[ -n "$BACKEND_INFO" ]]; then if [[ -n "$BACKEND_INFO" ]]; then
echo " Backend: $BACKEND_INFO" echo " Backend: $BACKEND_INFO"
fi fi
echo " Location: ${INSTALL_DIR:-\$HOME/.local/share/vocalinux}" echo " Location: ${INSTALL_DIR:-\$HOME/.local/share/voculinux}"
echo " Virtual Env: $VENV_DIR" echo " Virtual Env: $VENV_DIR"
echo " Config: $CONFIG_DIR" echo " Config: $CONFIG_DIR"
echo "" echo ""
@@ -3155,9 +3155,9 @@ EOF
echo " 🚀 Getting Started" echo " 🚀 Getting Started"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "" echo ""
echo "1. Launch Vocalinux" echo "1. Launch Voculinux"
echo " • From app menu: Look for 'Vocalinux'" echo " • From app menu: Look for 'Voculinux'"
echo " • From terminal: Run 'vocalinux' command" echo " • From terminal: Run 'voculinux' command"
echo "" echo ""
echo "2. Find the icon in your system tray (top bar)" echo "2. Find the icon in your system tray (top bar)"
echo " • Click for settings and status" echo " • Click for settings and status"
@@ -3180,16 +3180,16 @@ EOF
echo "" echo ""
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo " 🔧 Managing Vocalinux" echo " 🔧 Managing Voculinux"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "" echo ""
echo "Commands:" echo "Commands:"
echo " vocalinux Start the application" echo " voculinux Start the application"
echo " vocalinux --debug Start with debug logging" echo " voculinux --debug Start with debug logging"
echo " vocalinux-gui Open settings GUI" echo " voculinux-gui Open settings GUI"
echo "" echo ""
echo "To activate the virtual environment:" echo "To activate the virtual environment:"
echo " source ${ACTIVATION_SCRIPT:-activate-vocalinux.sh}" echo " source ${ACTIVATION_SCRIPT:-activate-voculinux.sh}"
echo "" echo ""
echo "To uninstall:" echo "To uninstall:"
echo " ./uninstall.sh" echo " ./uninstall.sh"
@@ -3215,10 +3215,10 @@ EOF
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "" echo ""
echo "Virtual environment: $VENV_DIR" echo "Virtual environment: $VENV_DIR"
echo "Desktop entry: $DESKTOP_DIR/vocalinux.desktop" echo "Desktop entry: $DESKTOP_DIR/voculinux.desktop"
echo "Configuration: $CONFIG_DIR" echo "Configuration: $CONFIG_DIR"
echo "Data directory: $DATA_DIR" echo "Data directory: $DATA_DIR"
echo "Wrapper script: $HOME/.local/bin/vocalinux" echo "Wrapper script: $HOME/.local/bin/voculinux"
echo "" echo ""
fi fi
} }
+6 -6
View File
@@ -1,4 +1,4 @@
# Vocalinux justfile # Voculinux justfile
# Convenient commands for development # Convenient commands for development
# Run `just` to list all recipes. Python tooling runs through `uv run` against # Run `just` to list all recipes. Python tooling runs through `uv run` against
# .venv/ — run `just deps` after cloning (requires uv). # .venv/ — run `just deps` after cloning (requires uv).
@@ -47,7 +47,7 @@ _check_build_deps:
_tooling: _check_build_deps _tooling: _check_build_deps
uv sync --inexact {{DEV_EXTRAS}} uv sync --inexact {{DEV_EXTRAS}}
# Install Vocalinux # Install Voculinux
install: install:
./install.sh ./install.sh
@@ -149,7 +149,7 @@ aur-gate:
uv pip install --no-config --python "$WORK/venv/bin/python" \ uv pip install --no-config --python "$WORK/venv/bin/python" \
--require-hashes --no-deps --only-binary=:all: -r requirements/release-build.txt --require-hashes --no-deps --only-binary=:all: -r requirements/release-build.txt
"$WORK/venv/bin/python" scripts/prepare_aur_source.py \ "$WORK/venv/bin/python" scripts/prepare_aur_source.py \
--local-unattested --sdist "$WORK"/dist/vocalinux-*.tar.gz --output "$WORK/source" --local-unattested --sdist "$WORK"/dist/voculinux-*.tar.gz --output "$WORK/source"
docker run --rm -v "$PWD:/repo:ro" -v "$WORK/source:/aur-source:ro" \ docker run --rm -v "$PWD:/repo:ro" -v "$WORK/source:/aur-source:ro" \
-e AUR_SOURCE=/aur-source archlinux:latest bash /repo/packaging/aur/build-test.sh -e AUR_SOURCE=/aur-source archlinux:latest bash /repo/packaging/aur/build-test.sh
@@ -294,11 +294,11 @@ clean:
# Run the installed application # Run the installed application
run: run:
vocalinux voculinux
# Run the installed application with debug logging # Run the installed application with debug logging
run-debug: run-debug:
vocalinux --debug voculinux --debug
# Run from source # Run from source
run-source: _tooling run-source: _tooling
@@ -314,4 +314,4 @@ pre-commit: _tooling
# Print the current version # Print the current version
version: version:
@uv run --no-sync python -c "from src.vocalinux.version import __version__; print(__version__)" @uv run --no-sync python -c "from src.voculinux.version import __version__; print(__version__)"
+1 -1
View File
@@ -130,7 +130,7 @@ if [ "$status" -ne 124 ] && [ "$status" -ne 0 ] && [ "$status" -ne 143 ]; then
fail "the app exited with $status instead of running" fail "the app exited with $status instead of running"
fi fi
grep -q "Initializing Vocalinux" "$LOG" || { grep -q "Initializing Voculinux" "$LOG" || {
tail -n 30 "$LOG" >&2 tail -n 30 "$LOG" >&2
fail "startup never reached component initialisation" fail "startup never reached component initialisation"
} }
+25 -25
View File
@@ -1,5 +1,5 @@
#!/usr/bin/env bash #!/usr/bin/env bash
# Build a relocatable AppImage for Vocalinux, natively for whichever # Build a relocatable AppImage for Voculinux, natively for whichever
# architecture this script runs on (x86_64 or aarch64) - no cross-compiling. # architecture this script runs on (x86_64 or aarch64) - no cross-compiling.
# #
# Usage: build.sh <path-to-wheel> <version> [output-dir] # Usage: build.sh <path-to-wheel> <version> [output-dir]
@@ -26,10 +26,10 @@
# pywhispercpp from source with GGML_VULKAN=1 when Vulkan headers and a # pywhispercpp from source with GGML_VULKAN=1 when Vulkan headers and a
# shader compiler are on the build host. The AppImage uses the host Vulkan # shader compiler are on the build host. The AppImage uses the host Vulkan
# loader/ICDs at runtime (do not bundle NVIDIA/AMD driver ICDs). # loader/ICDs at runtime (do not bundle NVIDIA/AMD driver ICDs).
# Set VOCALINUX_APPIMAGE_REQUIRE_VULKAN=1 in CI so a failed rebuild fails # Set VOCULINUX_APPIMAGE_REQUIRE_VULKAN=1 in CI so a failed rebuild fails
# the job instead of shipping a CPU-only image. Set # the job instead of shipping a CPU-only image. Set
# VOCALINUX_APPIMAGE_SKIP_VULKAN=1 to force the CPU wheel. # VOCULINUX_APPIMAGE_SKIP_VULKAN=1 to force the CPU wheel.
# A build that produced libggml-vulkan.so is kept in VOCALINUX_APPIMAGE_CACHE # A build that produced libggml-vulkan.so is kept in VOCULINUX_APPIMAGE_CACHE
# and reused while the base image, tool_checksums.txt, and the pywhispercpp # and reused while the base image, tool_checksums.txt, and the pywhispercpp
# pin are unchanged. A CPU-only build is not cached. # pin are unchanged. A CPU-only build is not cached.
set -euo pipefail set -euo pipefail
@@ -113,7 +113,7 @@ checkout_pinned() {
git -C "$dest" checkout -q FETCH_HEAD git -C "$dest" checkout -q FETCH_HEAD
} }
# Seed typelibs Vocalinux imports directly, plus transitive GIR deps that Gtk # Seed typelibs Voculinux imports directly, plus transitive GIR deps that Gtk
# and AppIndicator require (xlib, Dbusmenu, …). Without those, GI falls back to # and AppIndicator require (xlib, Dbusmenu, …). Without those, GI falls back to
# the builder's baked-in path (/usr/lib/x86_64-linux-gnu/girepository-1.0), which # the builder's baked-in path (/usr/lib/x86_64-linux-gnu/girepository-1.0), which
# does not exist on Fedora/openSUSE/Arch — startup then reports "missing GTK3". # does not exist on Fedora/openSUSE/Arch — startup then reports "missing GTK3".
@@ -241,7 +241,7 @@ uv_install() {
"$UV" pip install --python "$PY_BIN" --prefix "$APPDIR/usr" "$@" "$UV" pip install --python "$PY_BIN" --prefix "$APPDIR/usr" "$@"
} }
echo "== Installing Vocalinux + runtime deps into the bundle ==" echo "== Installing Voculinux + runtime deps into the bundle =="
# From the lock, not from whatever PyPI serves today. vad.txt is a superset of # From the lock, not from whatever PyPI serves today. vad.txt is a superset of
# runtime.txt (a test keeps it that way, so this is one resolution rather than # runtime.txt (a test keeps it that way, so this is one resolution rather than
# two); appimage.txt adds the PyGObject the exports leave out. The wheel is last # two); appimage.txt adds the PyGObject the exports leave out. The wheel is last
@@ -253,15 +253,15 @@ uv_install --require-hashes --no-deps -r "$REPO_ROOT/requirements/appimage.txt"
uv_install --no-deps "$WHEEL" uv_install --no-deps "$WHEEL"
echo "== Adding desktop entry + icon ==" echo "== Adding desktop entry + icon =="
install -Dm644 "$REPO_ROOT/vocalinux.desktop" "$APPDIR/usr/share/applications/vocalinux.desktop" install -Dm644 "$REPO_ROOT/voculinux.desktop" "$APPDIR/usr/share/applications/voculinux.desktop"
# AppImage desktop integration expects Exec=AppRun; set WM class for the tray app. # AppImage desktop integration expects Exec=AppRun; set WM class for the tray app.
sed -i \ sed -i \
-e 's|^Exec=.*|Exec=AppRun|' \ -e 's|^Exec=.*|Exec=AppRun|' \
-e '/^StartupWMClass=/d' \ -e '/^StartupWMClass=/d' \
"$APPDIR/usr/share/applications/vocalinux.desktop" "$APPDIR/usr/share/applications/voculinux.desktop"
printf 'StartupWMClass=vocalinux\n' >> "$APPDIR/usr/share/applications/vocalinux.desktop" printf 'StartupWMClass=voculinux\n' >> "$APPDIR/usr/share/applications/voculinux.desktop"
install -Dm644 "$REPO_ROOT/resources/icons/scalable/vocalinux.svg" \ install -Dm644 "$REPO_ROOT/resources/icons/scalable/voculinux.svg" \
"$APPDIR/usr/share/icons/hicolor/scalable/apps/vocalinux.svg" "$APPDIR/usr/share/icons/hicolor/scalable/apps/voculinux.svg"
copy_typelibs() { copy_typelibs() {
local dest="$1" local dest="$1"
@@ -363,7 +363,7 @@ ensure_build_tools() {
# ggml demands glslc (`find_package(Vulkan COMPONENTS glslc REQUIRED)`) and the # ggml demands glslc (`find_package(Vulkan COMPONENTS glslc REQUIRED)`) and the
# base image packages none, so build it from the pinned shaderc commit — ~4 min # base image packages none, so build it from the pinned shaderc commit — ~4 min
# on a runner, and CI caches it in VOCALINUX_APPIMAGE_CACHE since the commit is # on a runner, and CI caches it in VOCULINUX_APPIMAGE_CACHE since the commit is
# its only input. # its only input.
ensure_glslc() { ensure_glslc() {
if command -v glslc >/dev/null 2>&1; then if command -v glslc >/dev/null 2>&1; then
@@ -372,7 +372,7 @@ ensure_glslc() {
fi fi
local commit cache_dir src local commit cache_dir src
commit="$(pin_field shaderc 3)" commit="$(pin_field shaderc 3)"
cache_dir="${VOCALINUX_APPIMAGE_CACHE:-${XDG_CACHE_HOME:-$HOME/.cache}/vocalinux-appimage}/glslc-${commit}-${ARCH}" cache_dir="${VOCULINUX_APPIMAGE_CACHE:-${XDG_CACHE_HOME:-$HOME/.cache}/voculinux-appimage}/glslc-${commit}-${ARCH}"
if [ ! -x "$cache_dir/glslc" ]; then if [ ! -x "$cache_dir/glslc" ]; then
echo "== Building glslc from shaderc ${commit:0:12} (this image packages none) ==" echo "== Building glslc from shaderc ${commit:0:12} (this image packages none) =="
ensure_build_tools ensure_build_tools
@@ -486,7 +486,7 @@ _compile_pywhispercpp_vulkan() {
if ! has_vulkan_build_deps; then if ! has_vulkan_build_deps; then
echo "Vulkan build deps missing (libvulkan-dev plus a C++ compiler)." >&2 echo "Vulkan build deps missing (libvulkan-dev plus a C++ compiler)." >&2
if [ "$require_vulkan" = "1" ]; then if [ "$require_vulkan" = "1" ]; then
echo "VOCALINUX_APPIMAGE_REQUIRE_VULKAN=1: refusing to ship a CPU-only AppImage." >&2 echo "VOCULINUX_APPIMAGE_REQUIRE_VULKAN=1: refusing to ship a CPU-only AppImage." >&2
exit 1 exit 1
fi fi
echo "Warning: AppImage will use a CPU-only baseline pywhispercpp build." >&2 echo "Warning: AppImage will use a CPU-only baseline pywhispercpp build." >&2
@@ -547,13 +547,13 @@ _compile_pywhispercpp_vulkan() {
} }
rebuild_pywhispercpp_vulkan() { rebuild_pywhispercpp_vulkan() {
local require_vulkan="${VOCALINUX_APPIMAGE_REQUIRE_VULKAN:-0}" local require_vulkan="${VOCULINUX_APPIMAGE_REQUIRE_VULKAN:-0}"
if [ "${VOCALINUX_APPIMAGE_SKIP_VULKAN:-0}" = "1" ]; then if [ "${VOCULINUX_APPIMAGE_SKIP_VULKAN:-0}" = "1" ]; then
echo "== Skipping pywhispercpp Vulkan rebuild (VOCALINUX_APPIMAGE_SKIP_VULKAN=1) ==" echo "== Skipping pywhispercpp Vulkan rebuild (VOCULINUX_APPIMAGE_SKIP_VULKAN=1) =="
return 0 return 0
fi fi
# The Vocalinux wheel was installed above and is not part of this cache. # The Voculinux wheel was installed above and is not part of this cache.
# A hit still replaces the CPU pywhispercpp wheel the lock just installed, # A hit still replaces the CPU pywhispercpp wheel the lock just installed,
# and it does not run _compile_pywhispercpp_vulkan. # and it does not run _compile_pywhispercpp_vulkan.
local cache_dir site local cache_dir site
@@ -593,8 +593,8 @@ export DEPLOY_GTK_VERSION=3
"$TOOLDIR/linuxdeploy" --appdir "$APPDIR" \ "$TOOLDIR/linuxdeploy" --appdir "$APPDIR" \
--plugin gtk \ --plugin gtk \
-e "$APPDIR/usr/bin/python3" \ -e "$APPDIR/usr/bin/python3" \
-d "$APPDIR/usr/share/applications/vocalinux.desktop" \ -d "$APPDIR/usr/share/applications/voculinux.desktop" \
-i "$APPDIR/usr/share/icons/hicolor/scalable/apps/vocalinux.svg" -i "$APPDIR/usr/share/icons/hicolor/scalable/apps/voculinux.svg"
# The plugin can change the bundle after PyGObject was installed. Check the # The plugin can change the bundle after PyGObject was installed. Check the
# package and the version-agnostic Python path that AppRun and the GI smoke use # package and the version-agnostic Python path that AppRun and the GI smoke use
@@ -689,14 +689,14 @@ GTK_HOOK="$APPDIR/apprun-hooks/linuxdeploy-plugin-gtk.sh"
if [ -f "$GTK_HOOK" ]; then if [ -f "$GTK_HOOK" ]; then
# Upstream forces GDK_BACKEND=x11 even on Wayland, which breaks hover/focus # Upstream forces GDK_BACKEND=x11 even on Wayland, which breaks hover/focus
# for GTK widgets under Plasma (XWayland). Prefer native Wayland unless the # for GTK widgets under Plasma (XWayland). Prefer native Wayland unless the
# user opts into X11 via VOCALINUX_FORCE_X11=1. # user opts into X11 via VOCULINUX_FORCE_X11=1.
"$APPDIR/usr/bin/python3" - "$GTK_HOOK" <<'PY' "$APPDIR/usr/bin/python3" - "$GTK_HOOK" <<'PY'
import pathlib, re, sys import pathlib, re, sys
path = pathlib.Path(sys.argv[1]) path = pathlib.Path(sys.argv[1])
text = path.read_text() text = path.read_text()
replacement = ( replacement = (
'# Prefer Wayland when available; force X11 only when requested.\n' '# Prefer Wayland when available; force X11 only when requested.\n'
'if [ "${VOCALINUX_FORCE_X11:-0}" = "1" ]; then\n' 'if [ "${VOCULINUX_FORCE_X11:-0}" = "1" ]; then\n'
' export GDK_BACKEND=x11\n' ' export GDK_BACKEND=x11\n'
'elif [ -n "${WAYLAND_DISPLAY:-}" ] || [ "${XDG_SESSION_TYPE:-}" = "wayland" ]; then\n' 'elif [ -n "${WAYLAND_DISPLAY:-}" ] || [ "${XDG_SESSION_TYPE:-}" = "wayland" ]; then\n'
' export GDK_BACKEND=wayland\n' ' export GDK_BACKEND=wayland\n'
@@ -727,13 +727,13 @@ smoke_gi_imports() {
local appdir="$1" local appdir="$1"
unshare --user --mount --map-root-user bash -c " unshare --user --mount --map-root-user bash -c "
set -euo pipefail set -euo pipefail
mkdir -p /tmp/vocalinux-empty-gi mkdir -p /tmp/voculinux-empty-gi
for d in /usr/lib/x86_64-linux-gnu/girepository-1.0 \ for d in /usr/lib/x86_64-linux-gnu/girepository-1.0 \
/usr/lib/aarch64-linux-gnu/girepository-1.0 \ /usr/lib/aarch64-linux-gnu/girepository-1.0 \
/usr/lib/girepository-1.0 \ /usr/lib/girepository-1.0 \
/usr/lib64/girepository-1.0; do /usr/lib64/girepository-1.0; do
if [ -d \"\$d\" ]; then if [ -d \"\$d\" ]; then
mount --bind /tmp/vocalinux-empty-gi \"\$d\" mount --bind /tmp/voculinux-empty-gi \"\$d\"
fi fi
done done
export PYTHONHOME='$appdir/usr' export PYTHONHOME='$appdir/usr'
@@ -797,6 +797,6 @@ else
fi fi
echo "== Packaging AppImage ==" echo "== Packaging AppImage =="
OUTPUT="$OUTDIR/Vocalinux-${VERSION}-${ARCH}.AppImage" OUTPUT="$OUTDIR/Voculinux-${VERSION}-${ARCH}.AppImage"
ARCH="$ARCH" "$TOOLDIR/appimagetool" "$APPDIR" "$OUTPUT" ARCH="$ARCH" "$TOOLDIR/appimagetool" "$APPDIR" "$OUTPUT"
echo "Built $OUTPUT" echo "Built $OUTPUT"
+2 -2
View File
@@ -6,7 +6,7 @@
# tool_checksums.txt. This script is the part that runs inside that image; the # tool_checksums.txt. This script is the part that runs inside that image; the
# way in is docker-build.sh, which both CI and `just appimage` call: # way in is docker-build.sh, which both CI and `just appimage` call:
# #
# bash packaging/appimage/docker-build.sh dist/vocalinux-0.16.0-py3-none-any.whl 0.16.0 dist # bash packaging/appimage/docker-build.sh dist/voculinux-0.16.0-py3-none-any.whl 0.16.0 dist
# #
# Only the interpreter and the Python packages are pinned by digest; these apt # Only the interpreter and the Python packages are pinned by digest; these apt
# packages are whatever the pinned image resolves to. That is the trade the base # packages are whatever the pinned image resolves to. That is the trade the base
@@ -34,5 +34,5 @@ bash "$(dirname "$(readlink -f "${BASH_SOURCE[0]}")")/build.sh" "$@"
# results back to whoever started it (docker-build.sh passes its own ids). # results back to whoever started it (docker-build.sh passes its own ids).
if [ -n "${HOST_UID:-}" ]; then if [ -n "${HOST_UID:-}" ]; then
chown -R "${HOST_UID}:${HOST_GID:-$HOST_UID}" "${3:-dist}" \ chown -R "${HOST_UID}:${HOST_GID:-$HOST_UID}" "${3:-dist}" \
"${VOCALINUX_APPIMAGE_CACHE:-/cache}" 2>/dev/null || true "${VOCULINUX_APPIMAGE_CACHE:-/cache}" 2>/dev/null || true
fi fi
+7 -7
View File
@@ -8,9 +8,9 @@
# the AppImage links against, and building on the host instead means shipping # the AppImage links against, and building on the host instead means shipping
# whatever that host happens to run. # whatever that host happens to run.
# #
# Honours VOCALINUX_APPIMAGE_REQUIRE_VULKAN / _SKIP_VULKAN like build.sh, and # Honours VOCULINUX_APPIMAGE_REQUIRE_VULKAN / _SKIP_VULKAN like build.sh, and
# keeps glslc and the pywhispercpp Vulkan build in VOCALINUX_APPIMAGE_CACHE # keeps glslc and the pywhispercpp Vulkan build in VOCULINUX_APPIMAGE_CACHE
# (~/.cache/vocalinux-appimage by default) so the next build reuses them. # (~/.cache/voculinux-appimage by default) so the next build reuses them.
set -euo pipefail set -euo pipefail
REPO_ROOT="$(cd "$(dirname "$(readlink -f "${BASH_SOURCE[0]}")")/../.." && pwd)" REPO_ROOT="$(cd "$(dirname "$(readlink -f "${BASH_SOURCE[0]}")")/../.." && pwd)"
@@ -20,7 +20,7 @@ if [ -z "$IMAGE" ]; then
exit 1 exit 1
fi fi
CACHE="${VOCALINUX_APPIMAGE_CACHE:-${XDG_CACHE_HOME:-$HOME/.cache}/vocalinux-appimage}" CACHE="${VOCULINUX_APPIMAGE_CACHE:-${XDG_CACHE_HOME:-$HOME/.cache}/voculinux-appimage}"
mkdir -p "$CACHE" mkdir -p "$CACHE"
# The wheel and output dir are given as host paths; the repo is mounted at the # The wheel and output dir are given as host paths; the repo is mounted at the
@@ -31,9 +31,9 @@ exec docker run --rm \
--security-opt seccomp=unconfined \ --security-opt seccomp=unconfined \
-v "$REPO_ROOT:$REPO_ROOT" -w "$REPO_ROOT" \ -v "$REPO_ROOT:$REPO_ROOT" -w "$REPO_ROOT" \
-v "$CACHE:/cache" \ -v "$CACHE:/cache" \
-e VOCALINUX_APPIMAGE_CACHE=/cache \ -e VOCULINUX_APPIMAGE_CACHE=/cache \
-e VOCALINUX_APPIMAGE_REQUIRE_VULKAN \ -e VOCULINUX_APPIMAGE_REQUIRE_VULKAN \
-e VOCALINUX_APPIMAGE_SKIP_VULKAN \ -e VOCULINUX_APPIMAGE_SKIP_VULKAN \
-e CMAKE_BUILD_PARALLEL_LEVEL \ -e CMAKE_BUILD_PARALLEL_LEVEL \
-e HOST_UID="$(id -u)" \ -e HOST_UID="$(id -u)" \
-e HOST_GID="$(id -g)" \ -e HOST_GID="$(id -g)" \
+5 -5
View File
@@ -5,16 +5,16 @@
# from it), every pin in tool_checksums.txt, the cmake/ninja pins in # from it), every pin in tool_checksums.txt, the cmake/ninja pins in
# requirements/appimage-tools.txt, the Vulkan flags in build.sh, and the # requirements/appimage-tools.txt, the Vulkan flags in build.sh, and the
# pywhispercpp sdist install.sh names. It does not depend on the rest of the # pywhispercpp sdist install.sh names. It does not depend on the rest of the
# Vocalinux sources, so this id ignores the git commit and the Actions run id # Voculinux sources, so this id ignores the git commit and the Actions run id
# on purpose: putting either in the cache key would rebuild on every push. # on purpose: putting either in the cache key would rebuild on every push.
set -euo pipefail set -euo pipefail
here="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" here="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
repo="$(cd "$here/../.." && pwd)" repo="$(cd "$here/../.." && pwd)"
pins="${VOCALINUX_PINS:-$repo/packaging/appimage/tool_checksums.txt}" pins="${VOCULINUX_PINS:-$repo/packaging/appimage/tool_checksums.txt}"
install_sh="${VOCALINUX_INSTALL_SH:-$repo/install.sh}" install_sh="${VOCULINUX_INSTALL_SH:-$repo/install.sh}"
tools="${VOCALINUX_APPIMAGE_TOOLS:-$repo/requirements/appimage-tools.txt}" tools="${VOCULINUX_APPIMAGE_TOOLS:-$repo/requirements/appimage-tools.txt}"
build_sh="${VOCALINUX_BUILD_SH:-$repo/packaging/appimage/build.sh}" build_sh="${VOCULINUX_BUILD_SH:-$repo/packaging/appimage/build.sh}"
base="$(awk '$1=="base-image" {print $3; exit}' "$pins")" base="$(awk '$1=="base-image" {print $3; exit}' "$pins")"
if [ -z "$base" ]; then if [ -z "$base" ]; then
+2 -2
View File
@@ -12,7 +12,7 @@ _NATIVE_CACHE_HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
pywhispercpp_native_cache_dir() { pywhispercpp_native_cache_dir() {
local arch="$1" local arch="$1"
local root id local root id
root="${VOCALINUX_APPIMAGE_CACHE:-${XDG_CACHE_HOME:-$HOME/.cache}/vocalinux-appimage}" root="${VOCULINUX_APPIMAGE_CACHE:-${XDG_CACHE_HOME:-$HOME/.cache}/voculinux-appimage}"
id="$(bash "$_NATIVE_CACHE_HERE/native-cache-id.sh")" id="$(bash "$_NATIVE_CACHE_HERE/native-cache-id.sh")"
printf '%s\n' "$root/pywhispercpp-vulkan-${arch}-${id}" printf '%s\n' "$root/pywhispercpp-vulkan-${arch}-${id}"
} }
@@ -26,7 +26,7 @@ pywhispercpp_cache_has_vulkan() {
} }
# Copy the pywhispercpp install out of a site-packages directory. # Copy the pywhispercpp install out of a site-packages directory.
# Leaves the rest of the prefix (Vocalinux, numpy, …) where it is. # Leaves the rest of the prefix (Voculinux, numpy, …) where it is.
pywhispercpp_cache_publish() { pywhispercpp_cache_publish() {
local site="$1" dest="$2" local site="$1" dest="$2"
local parent staging pattern backup local parent staging pattern backup
+9 -9
View File
@@ -108,10 +108,10 @@ echo "== Satisfy the AUR-resolved depends for dependency resolution =="
STUB="$WORKDIR/stub" STUB="$WORKDIR/stub"
mkdir -p "$STUB" mkdir -p "$STUB"
cat >"$STUB/PKGBUILD" <<EOF cat >"$STUB/PKGBUILD" <<EOF
pkgname=vocalinux-gate-stub pkgname=voculinux-gate-stub
pkgver=1 pkgver=1
pkgrel=1 pkgrel=1
pkgdesc="Satisfies the AUR-resolved depends of the vocalinux build gate" pkgdesc="Satisfies the AUR-resolved depends of the voculinux build gate"
arch=('any') arch=('any')
provides=('python-pywhispercpp' 'python-pynput') provides=('python-pywhispercpp' 'python-pynput')
EOF EOF
@@ -169,12 +169,12 @@ pacman -U --noconfirm "$BUILD"/*.pkg.tar.* >/dev/null
# The console script proves the wheel's entry points landed; the imports walk # The console script proves the wheel's entry points landed; the imports walk
# every depends entry the package declares (gi/Gtk/AppIndicator, IBus, cairo, # every depends entry the package declares (gi/Gtk/AppIndicator, IBus, cairo,
# pyaudio, numpy, requests, tqdm, psutil, evdev, xlib, pynput). # pyaudio, numpy, requests, tqdm, psutil, evdev, xlib, pynput).
vocalinux --version voculinux --version
python - "$_version" <<'PY' || fail "the installed package does not import or has the wrong version" python - "$_version" <<'PY' || fail "the installed package does not import or has the wrong version"
import sys import sys
from importlib.metadata import version from importlib.metadata import version
assert version("vocalinux") == sys.argv[1], "installed distribution version differs from the sdist" assert version("voculinux") == sys.argv[1], "installed distribution version differs from the sdist"
import vocalinux.main import vocalinux.main
import vocalinux.ui.tray_indicator import vocalinux.ui.tray_indicator
@@ -196,13 +196,13 @@ if [ "${AUR_SOURCE_ONLY:-0}" = 1 ]; then
exit 0 exit 0
fi fi
echo "== Build vocalinux-bin against a stub AppImage ==" echo "== Build voculinux-bin against a stub AppImage =="
# The -bin package's arch sources are the published release assets, which do # The -bin package's arch sources are the published release assets, which do
# not exist for an untagged HEAD. A placeholder exercises the same package() # not exist for an untagged HEAD. A placeholder exercises the same package()
# install path on this arch; the pinned sums still describe the real assets, # install path on this arch; the pinned sums still describe the real assets,
# so this stage skips checksum verification — release.yml's updpkgsums keeps # so this stage skips checksum verification — release.yml's updpkgsums keeps
# the published PKGBUILD pinned to the actual files. # the published PKGBUILD pinned to the actual files.
BINPKGDIR="$REPO/packaging/aur/vocalinux-bin" BINPKGDIR="$REPO/packaging/aur/voculinux-bin"
[ -f "$BINPKGDIR/PKGBUILD" ] || fail "$BINPKGDIR/PKGBUILD not found" [ -f "$BINPKGDIR/PKGBUILD" ] || fail "$BINPKGDIR/PKGBUILD not found"
# shellcheck source=/dev/null # shellcheck source=/dev/null
source "$BINPKGDIR/PKGBUILD" source "$BINPKGDIR/PKGBUILD"
@@ -214,10 +214,10 @@ pacman_retry -S --needed --noconfirm --asdeps "${depends[@]}" >/dev/null
# directory as that user — makepkg writes into it as `builder`. # directory as that user — makepkg writes into it as `builder`.
BINBUILD="$WORKDIR/binpkg" BINBUILD="$WORKDIR/binpkg"
runuser -u builder -- mkdir -p "$BINBUILD" runuser -u builder -- mkdir -p "$BINBUILD"
cp "$BINPKGDIR/PKGBUILD" "$BINPKGDIR/vocalinux.desktop" \ cp "$BINPKGDIR/PKGBUILD" "$BINPKGDIR/voculinux.desktop" \
"$BINPKGDIR/vocalinux.svg" "$BINPKGDIR/LICENSE" "$BINBUILD/" "$BINPKGDIR/voculinux.svg" "$BINPKGDIR/LICENSE" "$BINBUILD/"
CARCH="$(uname -m)" CARCH="$(uname -m)"
printf 'aur gate stub\n' >"$BINBUILD/Vocalinux-${_tag}-${CARCH}.AppImage" printf 'aur gate stub\n' >"$BINBUILD/Voculinux-${_tag}-${CARCH}.AppImage"
( (
cd "$BINBUILD" cd "$BINBUILD"
@@ -1,7 +1,7 @@
# Maintainer: Jatin K Malik <jatinkrmalik@gmail.com> # Maintainer: Jatin K Malik <jatinkrmalik@gmail.com>
# Updated by .github/workflows/release.yml on v* tags. Setup: docs/AUR.md # Updated by .github/workflows/release.yml on v* tags. Setup: docs/AUR.md
pkgname=vocalinux-bin pkgname=voculinux-bin
# AUR pkgver cannot contain hyphens (v0.14.0-beta -> 0.14.0beta). # AUR pkgver cannot contain hyphens (v0.14.0-beta -> 0.14.0beta).
pkgver=0.17.0 pkgver=0.17.0
_tag=0.17.0 _tag=0.17.0
@@ -28,17 +28,17 @@ optdepends=(
'wl-clipboard: Wayland clipboard (wl-copy/wl-paste) injection fallbacks' 'wl-clipboard: Wayland clipboard (wl-copy/wl-paste) injection fallbacks'
'ydotool: uinput keystroke injection fallback' 'ydotool: uinput keystroke injection fallback'
) )
provides=('vocalinux') provides=('voculinux')
conflicts=('vocalinux' 'vocalinux-git') conflicts=('voculinux' 'voculinux-git')
# Stripping would rewrite the runtime header of a self-mounting ELF. # Stripping would rewrite the runtime header of a self-mounting ELF.
options=('!strip') options=('!strip')
source=( source=(
'vocalinux.desktop' 'voculinux.desktop'
'vocalinux.svg' 'voculinux.svg'
'LICENSE' 'LICENSE'
) )
source_x86_64=("Vocalinux-${_tag}-x86_64.AppImage::https://github.com/VocaHQ/vocalinux/releases/download/v${_tag}/Vocalinux-${_tag}-x86_64.AppImage") source_x86_64=("Voculinux-${_tag}-x86_64.AppImage::https://github.com/VocaHQ/vocalinux/releases/download/v${_tag}/Voculinux-${_tag}-x86_64.AppImage")
source_aarch64=("Vocalinux-${_tag}-aarch64.AppImage::https://github.com/VocaHQ/vocalinux/releases/download/v${_tag}/Vocalinux-${_tag}-aarch64.AppImage") source_aarch64=("Voculinux-${_tag}-aarch64.AppImage::https://github.com/VocaHQ/vocalinux/releases/download/v${_tag}/Voculinux-${_tag}-aarch64.AppImage")
sha256sums=('837006a9c689146308eae8c57cba1cdcc06673a108e72f39231837c6f63ae487' sha256sums=('837006a9c689146308eae8c57cba1cdcc06673a108e72f39231837c6f63ae487'
'128b8939ffc00314a55d0600ab29c6a92dfb116138e088c59579a086616ec8ec' '128b8939ffc00314a55d0600ab29c6a92dfb116138e088c59579a086616ec8ec'
'8486a10c4393cee1c25392769ddd3b2d6c242d6ec7928e1414efff7dfb2f07ef') '8486a10c4393cee1c25392769ddd3b2d6c242d6ec7928e1414efff7dfb2f07ef')
@@ -46,16 +46,16 @@ sha256sums_x86_64=('1a87408bf5bd8f9176c395a4da9ce28c68a1a7585bd67f125ff6037c4328
sha256sums_aarch64=('b256fe8577e2733a4da3ae4f9c2b9717f10017ad8328b832070ce04edc996a7d') sha256sums_aarch64=('b256fe8577e2733a4da3ae4f9c2b9717f10017ad8328b832070ce04edc996a7d')
package() { package() {
install -Dm755 "Vocalinux-${_tag}-${CARCH}.AppImage" \ install -Dm755 "Voculinux-${_tag}-${CARCH}.AppImage" \
"${pkgdir}/opt/${pkgname}/Vocalinux-${_tag}-${CARCH}.AppImage" "${pkgdir}/opt/${pkgname}/Voculinux-${_tag}-${CARCH}.AppImage"
install -Dm644 vocalinux.desktop \ install -Dm644 voculinux.desktop \
"${pkgdir}/usr/share/applications/vocalinux.desktop" "${pkgdir}/usr/share/applications/voculinux.desktop"
install -Dm644 vocalinux.svg \ install -Dm644 voculinux.svg \
"${pkgdir}/usr/share/icons/hicolor/scalable/apps/vocalinux.svg" "${pkgdir}/usr/share/icons/hicolor/scalable/apps/voculinux.svg"
install -Dm644 LICENSE "${pkgdir}/usr/share/licenses/${pkgname}/LICENSE" install -Dm644 LICENSE "${pkgdir}/usr/share/licenses/${pkgname}/LICENSE"
install -dm755 "${pkgdir}/usr/bin" install -dm755 "${pkgdir}/usr/bin"
ln -s "/opt/${pkgname}/Vocalinux-${_tag}-${CARCH}.AppImage" \ ln -s "/opt/${pkgname}/Voculinux-${_tag}-${CARCH}.AppImage" \
"${pkgdir}/usr/bin/vocalinux" "${pkgdir}/usr/bin/voculinux"
} }
@@ -1,11 +1,11 @@
[Desktop Entry] [Desktop Entry]
Version=1.0 Version=1.0
Type=Application Type=Application
Name=Vocalinux Name=Voculinux
Comment=Seamless voice dictation system for Linux Comment=Seamless voice dictation system for Linux
GenericName=Voice Typing GenericName=Voice Typing
Exec=vocalinux Exec=voculinux
Icon=vocalinux Icon=voculinux
Terminal=false Terminal=false
Categories=Utility;Accessibility;AudioVideo; Categories=Utility;Accessibility;AudioVideo;
Keywords=voice;typing;dictation;speech;recognition;transcription;accessibility; Keywords=voice;typing;dictation;speech;recognition;transcription;accessibility;
@@ -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"/> <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 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"/> <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,7 +1,7 @@
# Maintainer: Jatin K Malik <jatinkrmalik@gmail.com> # Maintainer: Jatin K Malik <jatinkrmalik@gmail.com>
# Updated by .github/workflows/release.yml on v* tags. Setup: docs/AUR.md # Updated by .github/workflows/release.yml on v* tags. Setup: docs/AUR.md
pkgname=vocalinux pkgname=voculinux
# AUR pkgver cannot contain hyphens (v0.14.0-beta -> 0.14.0beta). # AUR pkgver cannot contain hyphens (v0.14.0-beta -> 0.14.0beta).
pkgver=0.18.4 pkgver=0.18.4
_tag=0.18.4 _tag=0.18.4
@@ -58,7 +58,7 @@ optdepends=(
'wtype: Wayland keystroke injection fallback' 'wtype: Wayland keystroke injection fallback'
'gtk-layer-shell: dictation overlay as a layer surface on KDE and wlroots Wayland' 'gtk-layer-shell: dictation overlay as a layer surface on KDE and wlroots Wayland'
) )
conflicts=('vocalinux-git') conflicts=('voculinux-git')
source=("${pkgname}-${_version}.tar.gz::https://github.com/VocaHQ/vocalinux/releases/download/v${_tag}/${pkgname}-${_version}.tar.gz") source=("${pkgname}-${_version}.tar.gz::https://github.com/VocaHQ/vocalinux/releases/download/v${_tag}/${pkgname}-${_version}.tar.gz")
# Canonical release sdist, also published on PyPI. prepare_aur_source.py verifies # Canonical release sdist, also published on PyPI. prepare_aur_source.py verifies
# its checksum/provenance before pinning the next version; publication never rehashes. # its checksum/provenance before pinning the next version; publication never rehashes.
@@ -74,7 +74,7 @@ package() {
python -m installer --destdir="${pkgdir}" dist/*.whl python -m installer --destdir="${pkgdir}" dist/*.whl
install -Dm644 LICENSE "${pkgdir}/usr/share/licenses/${pkgname}/LICENSE" install -Dm644 LICENSE "${pkgdir}/usr/share/licenses/${pkgname}/LICENSE"
install -Dm644 vocalinux.desktop "${pkgdir}/usr/share/applications/vocalinux.desktop" install -Dm644 voculinux.desktop "${pkgdir}/usr/share/applications/voculinux.desktop"
for icon in resources/icons/scalable/*.svg; do for icon in resources/icons/scalable/*.svg; do
install -Dm644 "$icon" "${pkgdir}/usr/share/icons/hicolor/scalable/apps/$(basename "$icon")" install -Dm644 "$icon" "${pkgdir}/usr/share/icons/hicolor/scalable/apps/$(basename "$icon")"
+19 -19
View File
@@ -1,6 +1,6 @@
# Vocalinux Flatpak Packaging # Voculinux Flatpak Packaging
Manifest and AppStream metadata for building Vocalinux as a Flatpak. Manifest and AppStream metadata for building Voculinux as a Flatpak.
## VocaHQ remote (auto-update) ## VocaHQ remote (auto-update)
@@ -9,33 +9,33 @@ Homebrew-tap analogue — which `release.yml` signs and publishes to
`VocaHQ/vocalinux-flatpak` (GitHub Pages) on every stable `v*` tag: `VocaHQ/vocalinux-flatpak` (GitHub Pages) on every stable `v*` tag:
```bash ```bash
flatpak install https://vocahq.github.io/vocalinux-flatpak/com.vocalinux.Vocalinux.flatpakref flatpak install https://vocahq.github.io/voculinux-flatpak/com.voculinux.Voculinux.flatpakref
flatpak update # each new release arrives here flatpak update # each new release arrives here
``` ```
The `.flatpakref` adds the `vocahq` remote and resolves the GNOME runtime The `.flatpakref` adds the `vocahq` remote and resolves the GNOME runtime
through Flathub via `RuntimeRepo=` — the manual equivalent is `remote-add` through Flathub via `RuntimeRepo=` — the manual equivalent is `remote-add`
for `flathub` then `vocahq`, then `flatpak install vocahq for `flathub` then `vocahq`, then `flatpak install vocahq
com.vocalinux.Vocalinux`. Only the app is ours; `org.gnome.Platform//50` com.voculinux.Voculinux`. Only the app is ours; `org.gnome.Platform//50`
still comes from Flathub. One-time repo/key setup: still comes from Flathub. One-time repo/key setup:
[docs/FLATPAK_REMOTE.md](../../docs/FLATPAK_REMOTE.md). [docs/FLATPAK_REMOTE.md](../../docs/FLATPAK_REMOTE.md).
## GitHub Release bundles ## GitHub Release bundles
Each `v*` GitHub Release attaches `Vocalinux-<version>-x86_64.flatpak` and Each `v*` GitHub Release attaches `Voculinux-<version>-x86_64.flatpak` and
`Vocalinux-<version>-aarch64.flatpak` as a no-remote fallback: `Voculinux-<version>-aarch64.flatpak` as a no-remote fallback:
```bash ```bash
# once: Flathub remote + GNOME runtime (the app itself is not on Flathub) # once: Flathub remote + GNOME runtime (the app itself is not on Flathub)
flatpak remote-add --if-not-exists flathub https://dl.flathub.org/repo/flathub.flatpakrepo flatpak remote-add --if-not-exists flathub https://dl.flathub.org/repo/flathub.flatpakrepo
flatpak install flathub org.gnome.Platform//50 flatpak install flathub org.gnome.Platform//50
flatpak install --user ./Vocalinux-<version>-x86_64.flatpak flatpak install --user ./Voculinux-<version>-x86_64.flatpak
flatpak run com.vocalinux.Vocalinux flatpak run com.voculinux.Voculinux
``` ```
Bundles do **not** auto-update — the VocaHQ remote above is the path for Bundles do **not** auto-update — the VocaHQ remote above is the path for
that. Vocalinux is **not on Flathub** (submission that. Voculinux is **not on Flathub** (submission
[flathub/flathub#9368](https://github.com/flathub/flathub/pull/9368) closed on [flathub/flathub#9368](https://github.com/flathub/flathub/pull/9368) closed on
policy grounds; we are not re-submitting). Local `flatpak-builder` remains for policy grounds; we are not re-submitting). Local `flatpak-builder` remains for
contributors (below). contributors (below).
@@ -67,7 +67,7 @@ no narrower uinput flag).
To disable device access while keeping GPU support for Vulkan whisper.cpp, run: To disable device access while keeping GPU support for Vulkan whisper.cpp, run:
```bash ```bash
flatpak override --user --nodevice=all --device=dri com.vocalinux.Vocalinux flatpak override --user --nodevice=all --device=dri com.voculinux.Voculinux
``` ```
On native X11, hotkeys still work through the X11 listener, except on GNOME X11, On native X11, hotkeys still work through the X11 listener, except on GNOME X11,
@@ -78,12 +78,12 @@ enable **Settings → Shortcuts → External Activation (Desktop Shortcut)** and
bind a desktop shortcut to: bind a desktop shortcut to:
```bash ```bash
gdbus call --session --dest com.vocalinux.Vocalinux --object-path /com/vocalinux/Vocalinux --method com.vocalinux.Vocalinux.Toggle gdbus call --session --dest com.voculinux.Voculinux --object-path /com/voculinux/Voculinux --method com.voculinux.Voculinux.Toggle
``` ```
Use toggle mode for this shortcut. A desktop shortcut only sends a key press, Use toggle mode for this shortcut. A desktop shortcut only sends a key press,
so it cannot support push-to-talk. Running so it cannot support push-to-talk. Running
`flatpak run com.vocalinux.Vocalinux --toggle` also works, but starts a sandbox `flatpak run com.voculinux.Voculinux --toggle` also works, but starts a sandbox
on each press. The host `gdbus` command is immediate and does not start another on each press. The host `gdbus` command is immediate and does not start another
sandbox. sandbox.
@@ -100,19 +100,19 @@ flatpak install flathub org.gnome.Platform//50 org.gnome.Sdk//50
# --user --install install for the current user # --user --install install for the current user
# --repo=repo export a local repo (then bundle or lint) # --repo=repo export a local repo (then bundle or lint)
# --compose-url-policy=full --mirror-screenshots-url=https://dl.flathub.org/media # --compose-url-policy=full --mirror-screenshots-url=https://dl.flathub.org/media
flatpak-builder --force-clean build-dir packaging/flatpak/com.vocalinux.Vocalinux.yml flatpak-builder --force-clean build-dir packaging/flatpak/com.voculinux.Voculinux.yml
flatpak run com.vocalinux.Vocalinux --debug flatpak run com.voculinux.Voculinux --debug
# single-file bundle (after a --repo=repo build) # single-file bundle (after a --repo=repo build)
flatpak build-bundle repo vocalinux.flatpak com.vocalinux.Vocalinux flatpak build-bundle repo voculinux.flatpak com.voculinux.Voculinux
# elsewhere: flatpak install --user vocalinux.flatpak # elsewhere: flatpak install --user voculinux.flatpak
``` ```
Lint (needs `org.flatpak.Builder`): Lint (needs `org.flatpak.Builder`):
```bash ```bash
flatpak run --command=flatpak-builder-lint org.flatpak.Builder manifest packaging/flatpak/com.vocalinux.Vocalinux.yml flatpak run --command=flatpak-builder-lint org.flatpak.Builder manifest packaging/flatpak/com.voculinux.Voculinux.yml
flatpak run --command=flatpak-builder-lint org.flatpak.Builder builddir build-dir flatpak run --command=flatpak-builder-lint org.flatpak.Builder builddir build-dir
flatpak run --command=flatpak-builder-lint org.flatpak.Builder repo repo # if you used --repo=repo flatpak run --command=flatpak-builder-lint org.flatpak.Builder repo repo # if you used --repo=repo
``` ```
@@ -122,7 +122,7 @@ glycin loaders, `flatpak-builder --run` can fail to load themed SVG icons for an
uninstalled stable app ID; it is still fine for non-GUI smokes: uninstalled stable app ID; it is still fine for non-GUI smokes:
```bash ```bash
flatpak-builder --run build-dir packaging/flatpak/com.vocalinux.Vocalinux.yml \ flatpak-builder --run build-dir packaging/flatpak/com.voculinux.Voculinux.yml \
python3 -c 'from pywhispercpp.model import Model; print("pywhispercpp ok")' python3 -c 'from pywhispercpp.model import Model; print("pywhispercpp ok")'
``` ```
@@ -187,4 +187,4 @@ Release `.flatpak` assets are the supported paths.
- IBus: `--talk-name=org.freedesktop.IBus` - IBus: `--talk-name=org.freedesktop.IBus`
- Tray: `--talk-name=org.kde.StatusNotifierWatcher` - Tray: `--talk-name=org.kde.StatusNotifierWatcher`
Config and models use Flatpak XDG dirs under `~/.var/app/com.vocalinux.Vocalinux/`. Config and models use Flatpak XDG dirs under `~/.var/app/com.voculinux.Voculinux/`.
@@ -1,14 +1,14 @@
[Desktop Entry] [Desktop Entry]
Version=1.0 Version=1.0
Type=Application Type=Application
Name=Vocalinux Name=Voculinux
Comment=Free, offline voice dictation for Linux Comment=Free, offline voice dictation for Linux
GenericName=Voice Typing GenericName=Voice Typing
Exec=vocalinux Exec=voculinux
Icon=com.vocalinux.Vocalinux Icon=com.voculinux.Voculinux
Terminal=false Terminal=false
Categories=Utility;Accessibility;AudioVideo; Categories=Utility;Accessibility;AudioVideo;
Keywords=voice;typing;dictation;speech;recognition;transcription;accessibility; Keywords=voice;typing;dictation;speech;recognition;transcription;accessibility;
StartupNotify=true StartupNotify=true
StartupWMClass=com.vocalinux.Vocalinux StartupWMClass=com.voculinux.Voculinux
X-GNOME-UsesNotifications=true X-GNOME-UsesNotifications=true
@@ -1,10 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<component type="desktop-application"> <component type="desktop-application">
<id>com.vocalinux.Vocalinux</id> <id>com.voculinux.Voculinux</id>
<name>Vocalinux</name> <name>Voculinux</name>
<summary>Free, offline voice dictation for Linux</summary> <summary>Free, offline voice dictation for Linux</summary>
<developer id="com.vocalinux"> <developer id="com.voculinux">
<name>Jatin K Malik</name> <name>Jatin K Malik</name>
</developer> </developer>
@@ -13,7 +13,7 @@
<description> <description>
<p> <p>
Vocalinux is a privacy-first, fully offline voice dictation system for Linux. Voculinux is a privacy-first, fully offline voice dictation system for Linux.
Speak naturally and your words appear in any application — on X11 or Wayland — Speak naturally and your words appear in any application — on X11 or Wayland —
using local speech recognition. No cloud, no data leaves your machine. using local speech recognition. No cloud, no data leaves your machine.
Speech recognition models are downloaded on first run. Speech recognition models are downloaded on first run.
@@ -37,7 +37,7 @@
</p> </p>
</description> </description>
<launchable type="desktop-id">com.vocalinux.Vocalinux.desktop</launchable> <launchable type="desktop-id">com.voculinux.Voculinux.desktop</launchable>
<categories> <categories>
<category>Utility</category> <category>Utility</category>
@@ -58,19 +58,19 @@
<screenshots> <screenshots>
<screenshot type="default"> <screenshot type="default">
<caption>Real-time voice-to-text transcription</caption> <caption>Real-time voice-to-text transcription</caption>
<image>https://vocalinux.com/screenshots/00-transcription.png</image> <image>https://voculinux.com/screenshots/00-transcription.png</image>
</screenshot> </screenshot>
<screenshot> <screenshot>
<caption>Speech engine settings</caption> <caption>Speech engine settings</caption>
<image>https://vocalinux.com/screenshots/settings-speech-engine.png</image> <image>https://voculinux.com/screenshots/settings-speech-engine.png</image>
</screenshot> </screenshot>
<screenshot> <screenshot>
<caption>System tray indicator</caption> <caption>System tray indicator</caption>
<image>https://vocalinux.com/screenshots/02-system-tray.png</image> <image>https://voculinux.com/screenshots/02-system-tray.png</image>
</screenshot> </screenshot>
</screenshots> </screenshots>
<url type="homepage">https://vocalinux.com</url> <url type="homepage">https://voculinux.com</url>
<url type="bugtracker">https://github.com/VocaHQ/vocalinux/issues</url> <url type="bugtracker">https://github.com/VocaHQ/vocalinux/issues</url>
<url type="vcs-browser">https://github.com/VocaHQ/vocalinux</url> <url type="vcs-browser">https://github.com/VocaHQ/vocalinux</url>
@@ -172,7 +172,7 @@
</release> </release>
<release version="0.14.0-beta" date="2026-07-14"> <release version="0.14.0-beta" date="2026-07-14">
<description> <description>
<p>Vocalinux 0.14.0-beta as Flatpak: whisper.cpp with Vulkan, XDG config/models/logs, XWayland text injection, and system tray integration.</p> <p>Voculinux 0.14.0-beta as Flatpak: whisper.cpp with Vulkan, XDG config/models/logs, XWayland text injection, and system tray integration.</p>
</description> </description>
</release> </release>
<release version="0.12.0-beta" date="2026-06-07"> <release version="0.12.0-beta" date="2026-06-07">
@@ -1,9 +1,9 @@
app-id: com.vocalinux.Vocalinux app-id: com.voculinux.Voculinux
branch: stable branch: stable
runtime: org.gnome.Platform runtime: org.gnome.Platform
runtime-version: "50" runtime-version: "50"
sdk: org.gnome.Sdk sdk: org.gnome.Sdk
command: vocalinux command: voculinux
build-options: build-options:
env: env:
@@ -97,7 +97,7 @@ modules:
# uinput-based typing / paste chords for native Wayland clients. # uinput-based typing / paste chords for native Wayland clients.
# Pinned to v1.0.4 (keycode:value key API). Distro 0.1.x uses "ctrl+v" instead; # Pinned to v1.0.4 (keycode:value key API). Distro 0.1.x uses "ctrl+v" instead;
# vocalinux detects the installed CLI at runtime (see TextInjector). # voculinux detects the installed CLI at runtime (see TextInjector).
- name: ydotool - name: ydotool
buildsystem: cmake-ninja buildsystem: cmake-ninja
config-opts: config-opts:
@@ -124,16 +124,16 @@ modules:
- python3-build-dependencies.yaml - python3-build-dependencies.yaml
- python3-dependencies.yaml - python3-dependencies.yaml
- name: vocalinux - name: voculinux
buildsystem: simple buildsystem: simple
build-commands: build-commands:
- pip3 install --no-deps --no-build-isolation --prefix=/app . - pip3 install --no-deps --no-build-isolation --prefix=/app .
- install -Dm644 packaging/flatpak/com.vocalinux.Vocalinux.desktop -t /app/share/applications/ - install -Dm644 packaging/flatpak/com.voculinux.Voculinux.desktop -t /app/share/applications/
- install -Dm644 resources/icons/scalable/vocalinux.svg /app/share/icons/hicolor/scalable/apps/com.vocalinux.Vocalinux.svg - install -Dm644 resources/icons/scalable/voculinux.svg /app/share/icons/hicolor/scalable/apps/com.voculinux.Voculinux.svg
- install -Dm644 resources/icons/scalable/vocalinux-microphone-off.svg /app/share/icons/hicolor/scalable/apps/com.vocalinux.Vocalinux-microphone-off.svg - install -Dm644 resources/icons/scalable/voculinux-microphone-off.svg /app/share/icons/hicolor/scalable/apps/com.voculinux.Voculinux-microphone-off.svg
- install -Dm644 resources/icons/scalable/vocalinux-microphone.svg /app/share/icons/hicolor/scalable/apps/com.vocalinux.Vocalinux-microphone.svg - install -Dm644 resources/icons/scalable/voculinux-microphone.svg /app/share/icons/hicolor/scalable/apps/com.voculinux.Voculinux-microphone.svg
- install -Dm644 resources/icons/scalable/vocalinux-microphone-process.svg /app/share/icons/hicolor/scalable/apps/com.vocalinux.Vocalinux-microphone-process.svg - install -Dm644 resources/icons/scalable/voculinux-microphone-process.svg /app/share/icons/hicolor/scalable/apps/com.voculinux.Voculinux-microphone-process.svg
- install -Dm644 packaging/flatpak/com.vocalinux.Vocalinux.metainfo.xml -t /app/share/metainfo/ - install -Dm644 packaging/flatpak/com.voculinux.Voculinux.metainfo.xml -t /app/share/metainfo/
sources: sources:
- type: dir - type: dir
path: ../.. path: ../..
+3 -3
View File
@@ -27,7 +27,7 @@
<h1>VocaHQ Flatpak remote</h1> <h1>VocaHQ Flatpak remote</h1>
<p> <p>
Self-hosted, signed OSTree repository for Self-hosted, signed OSTree repository for
<a href="https://vocalinux.com">Vocalinux</a> — published on every stable <a href="https://voculinux.com">Voculinux</a> — published on every stable
tagged release, so installs auto-update via <code>flatpak update</code>. tagged release, so installs auto-update via <code>flatpak update</code>.
</p> </p>
<pre><code>flatpak install __FLATPAKREF_URL__</code></pre> <pre><code>flatpak install __FLATPAKREF_URL__</code></pre>
@@ -38,11 +38,11 @@
</p> </p>
<pre><code>flatpak remote-add --if-not-exists flathub https://dl.flathub.org/repo/flathub.flatpakrepo <pre><code>flatpak remote-add --if-not-exists flathub https://dl.flathub.org/repo/flathub.flatpakrepo
flatpak remote-add --if-not-exists vocahq __FLATPAKREPO_URL__ flatpak remote-add --if-not-exists vocahq __FLATPAKREPO_URL__
flatpak install vocahq com.vocalinux.Vocalinux</code></pre> flatpak install vocahq com.voculinux.Voculinux</code></pre>
<p> <p>
The GNOME runtime still comes from Flathub — the The GNOME runtime still comes from Flathub — the
<code>.flatpakref</code> declares <code>RuntimeRepo</code> so flatpak <code>.flatpakref</code> declares <code>RuntimeRepo</code> so flatpak
finds it there. Vocalinux itself is not on Flathub. finds it there. Voculinux itself is not on Flathub.
</p> </p>
<p> <p>
<a href="https://github.com/VocaHQ/vocalinux">Source code</a> · <a href="https://github.com/VocaHQ/vocalinux">Source code</a> ·
+22 -22
View File
@@ -1,7 +1,7 @@
#!/usr/bin/env bash #!/usr/bin/env bash
# Build vocalinux_<version>_<arch>.deb and vocalinux-<version>.<arch>.rpm. # Build voculinux_<version>_<arch>.deb and voculinux-<version>.<arch>.rpm.
# #
# Usage: build.sh <path-to-vocalinux-wheel> <version> [output-dir] # Usage: build.sh <path-to-voculinux-wheel> <version> [output-dir]
# version release version, e.g. 0.18.0 or 0.19.0-beta # version release version, e.g. 0.18.0 or 0.19.0-beta
# #
# CI and releases run this inside docker-build.sh's pinned Debian 12 image; # CI and releases run this inside docker-build.sh's pinned Debian 12 image;
@@ -63,18 +63,18 @@ NFPM="$TOOLS/nfpm"
PAYLOAD="$TOOLS/payload" PAYLOAD="$TOOLS/payload"
DOC="$TOOLS/pkg-doc" DOC="$TOOLS/pkg-doc"
install -d \ install -d \
"$PAYLOAD/usr/lib/vocalinux/app" \ "$PAYLOAD/usr/lib/voculinux/app" \
"$PAYLOAD/usr/lib/vocalinux/vendor" \ "$PAYLOAD/usr/lib/voculinux/vendor" \
"$PAYLOAD/usr/bin" \ "$PAYLOAD/usr/bin" \
"$PAYLOAD/usr/share/applications" \ "$PAYLOAD/usr/share/applications" \
"$PAYLOAD/usr/share/icons/hicolor/scalable/apps" \ "$PAYLOAD/usr/share/icons/hicolor/scalable/apps" \
"$DOC" "$DOC"
# --- application payload ------------------------------------------------ # --- application payload ------------------------------------------------
# The wheel is a zip; -m zipfile lays out vocalinux/ and its dist-info # The wheel is a zip; -m zipfile lays out voculinux/ and its dist-info
# exactly where pip --target would, without pip's EXTERNALLY-MANAGED refusal # exactly where pip --target would, without pip's EXTERNALLY-MANAGED refusal
# on Debian 12. # on Debian 12.
python3 -m zipfile -e "$WHEEL" "$PAYLOAD/usr/lib/vocalinux/app/" python3 -m zipfile -e "$WHEEL" "$PAYLOAD/usr/lib/voculinux/app/"
# --- vendored wheels ----------------------------------------------------- # --- vendored wheels -----------------------------------------------------
# Download each supported CPython's pywhispercpp wheel — it has no abi3 tag, # Download each supported CPython's pywhispercpp wheel — it has no abi3 tag,
@@ -99,7 +99,7 @@ for PYV in $VENDOR_PYS; do
--python-version "$PYV" -r "$PINS_VENDOR" -d "$TOOLS/wheels/$PYV" >/dev/null --python-version "$PYV" -r "$PINS_VENDOR" -d "$TOOLS/wheels/$PYV" >/dev/null
done done
for VENDOR_WHEEL in "$TOOLS"/wheels/*/*.whl; do for VENDOR_WHEEL in "$TOOLS"/wheels/*/*.whl; do
python3 -m zipfile -e "$VENDOR_WHEEL" "$PAYLOAD/usr/lib/vocalinux/vendor/" python3 -m zipfile -e "$VENDOR_WHEEL" "$PAYLOAD/usr/lib/voculinux/vendor/"
done done
# --- portable libggml-cpu ------------------------------------------------- # --- portable libggml-cpu -------------------------------------------------
@@ -144,7 +144,7 @@ swapped=0
while IFS= read -r vendored; do while IFS= read -r vendored; do
cp "$BASELINE_CPU" "$vendored" cp "$BASELINE_CPU" "$vendored"
swapped=$((swapped + 1)) swapped=$((swapped + 1))
done < <(find "$PAYLOAD/usr/lib/vocalinux/vendor" -name 'libggml-cpu*.so*') done < <(find "$PAYLOAD/usr/lib/voculinux/vendor" -name 'libggml-cpu*.so*')
[ "$swapped" -gt 0 ] || fail "found no vendored libggml-cpu to replace" [ "$swapped" -gt 0 ] || fail "found no vendored libggml-cpu to replace"
# The baseline library's own NEEDED entries use the unmangled sonames # The baseline library's own NEEDED entries use the unmangled sonames
# (libggml-base.so.0, libgomp.so.1), so alias the vendored copies — the same # (libggml-base.so.0, libgomp.so.1), so alias the vendored copies — the same
@@ -157,30 +157,30 @@ while IFS= read -r libs_dir; do
vendored_gomp="$(find "$libs_dir" -name 'libgomp*.so*' | head -1)" vendored_gomp="$(find "$libs_dir" -name 'libgomp*.so*' | head -1)"
[ -n "$vendored_gomp" ] || fail "no vendored libgomp in $libs_dir" [ -n "$vendored_gomp" ] || fail "no vendored libgomp in $libs_dir"
cp "$vendored_gomp" "$libs_dir/libgomp.so.1" cp "$vendored_gomp" "$libs_dir/libgomp.so.1"
done < <(find "$PAYLOAD/usr/lib/vocalinux/vendor" -type d -name 'pywhispercpp.libs') done < <(find "$PAYLOAD/usr/lib/voculinux/vendor" -type d -name 'pywhispercpp.libs')
# --- launchers ----------------------------------------------------------- # --- launchers -----------------------------------------------------------
# Same environment install.sh's wrapper establishes: the vendored # Same environment install.sh's wrapper establishes: the vendored
# pywhispercpp.libs on LD_LIBRARY_PATH, user site-packages off so they cannot # pywhispercpp.libs on LD_LIBRARY_PATH, user site-packages off so they cannot
# shadow the distro modules, and sg input so evdev hotkeys work on Wayland # shadow the distro modules, and sg input so evdev hotkeys work on Wayland
# when the user is already in the input group but the session has not picked # when the user is already in the input group but the session has not picked
# up the membership yet. vocalinux-gui exists because the wheel declares a # up the membership yet. voculinux-gui exists because the wheel declares a
# gui-script of that name; on Linux it runs the same entry. # gui-script of that name; on Linux it runs the same entry.
write_launcher() { write_launcher() {
local path="$1" local path="$1"
cat > "$path" <<'LAUNCHER_EOF' cat > "$path" <<'LAUNCHER_EOF'
#!/bin/sh #!/bin/sh
# Launcher for the distro-packaged Vocalinux. The vendored wheels (the # Launcher for the distro-packaged Voculinux. The vendored wheels (the
# pywhispercpp/pynput copies Debian and Fedora do not ship) live under # pywhispercpp/pynput copies Debian and Fedora do not ship) live under
# /usr/lib/vocalinux/vendor with the app itself under .../app; the loader in # /usr/lib/voculinux/vendor with the app itself under .../app; the loader in
# utils/pywhispercpp_loader.py finds the bundled .so files from sys.path. # utils/pywhispercpp_loader.py finds the bundled .so files from sys.path.
PYTHONNOUSERSITE=1 PYTHONNOUSERSITE=1
VOCALINUX_VENDOR="/usr/lib/vocalinux/vendor" VOCULINUX_VENDOR="/usr/lib/voculinux/vendor"
if [ -d "$VOCALINUX_VENDOR/pywhispercpp.libs" ]; then if [ -d "$VOCULINUX_VENDOR/pywhispercpp.libs" ]; then
LD_LIBRARY_PATH="$VOCALINUX_VENDOR/pywhispercpp.libs${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}" LD_LIBRARY_PATH="$VOCULINUX_VENDOR/pywhispercpp.libs${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}"
export LD_LIBRARY_PATH export LD_LIBRARY_PATH
fi fi
export PYTHONNOUSERSITE PYTHONPATH="/usr/lib/vocalinux/app:$VOCALINUX_VENDOR${PYTHONPATH:+:$PYTHONPATH}" export PYTHONNOUSERSITE PYTHONPATH="/usr/lib/voculinux/app:$VOCULINUX_VENDOR${PYTHONPATH:+:$PYTHONPATH}"
if grep -q "^input:.*\b$(whoami)\b" /etc/group 2>/dev/null \ if grep -q "^input:.*\b$(whoami)\b" /etc/group 2>/dev/null \
&& ! groups | grep -q "\binput\b" && command -v sg >/dev/null 2>&1; then && ! groups | grep -q "\binput\b" && command -v sg >/dev/null 2>&1; then
# sg(1) takes a single command string, so each argument is re-quoted for # sg(1) takes a single command string, so each argument is re-quoted for
@@ -196,14 +196,14 @@ exec /usr/bin/python3 -m vocalinux.main "$@"
LAUNCHER_EOF LAUNCHER_EOF
chmod 755 "$path" chmod 755 "$path"
} }
write_launcher "$PAYLOAD/usr/bin/vocalinux" write_launcher "$PAYLOAD/usr/bin/voculinux"
write_launcher "$PAYLOAD/usr/bin/vocalinux-gui" write_launcher "$PAYLOAD/usr/bin/voculinux-gui"
# --- desktop entry, icon, license ---------------------------------------- # --- desktop entry, icon, license ----------------------------------------
install -Dm644 "$REPO_ROOT/vocalinux.desktop" \ install -Dm644 "$REPO_ROOT/voculinux.desktop" \
"$PAYLOAD/usr/share/applications/vocalinux.desktop" "$PAYLOAD/usr/share/applications/voculinux.desktop"
install -Dm644 "$REPO_ROOT/resources/icons/scalable/vocalinux.svg" \ install -Dm644 "$REPO_ROOT/resources/icons/scalable/voculinux.svg" \
"$PAYLOAD/usr/share/icons/hicolor/scalable/apps/vocalinux.svg" "$PAYLOAD/usr/share/icons/hicolor/scalable/apps/voculinux.svg"
install -Dm644 "$REPO_ROOT/LICENSE" "$DOC/LICENSE" install -Dm644 "$REPO_ROOT/LICENSE" "$DOC/LICENSE"
# --- package ------------------------------------------------------------- # --- package -------------------------------------------------------------
+7 -7
View File
@@ -8,8 +8,8 @@
# the gi typelibs come from the distribution, and so do every Python # the gi typelibs come from the distribution, and so do every Python
# dependency the distros package. Only pywhispercpp and pynput are vendored — # dependency the distros package. Only pywhispercpp and pynput are vendored —
# pywhispercpp is not packaged by Debian or Fedora at all, and pynput is # pywhispercpp is not packaged by Debian or Fedora at all, and pynput is
# missing from Fedora — both under /usr/lib/vocalinux/vendor/, on sys.path via # missing from Fedora — both under /usr/lib/voculinux/vendor/, on sys.path via
# the launcher in /usr/bin/vocalinux. Vendoring the pywhispercpp wheel per # the launcher in /usr/bin/voculinux. Vendoring the pywhispercpp wheel per
# supported CPython is what keeps the package free of a bundled interpreter. # supported CPython is what keeps the package free of a bundled interpreter.
# #
# Version: VOCA_VERSION is the tag without its leading v and without a # Version: VOCA_VERSION is the tag without its leading v and without a
@@ -17,7 +17,7 @@
# nfpm maps a prerelease to 0.19.0~beta-1, which orders before the stable # nfpm maps a prerelease to 0.19.0~beta-1, which orders before the stable
# 0.19.0-1 in both formats. # 0.19.0-1 in both formats.
name: vocalinux name: voculinux
arch: ${VOCA_ARCH} arch: ${VOCA_ARCH}
platform: linux platform: linux
version: ${VOCA_VERSION} version: ${VOCA_VERSION}
@@ -31,12 +31,12 @@ description: |
transcribe speech locally (whisper.cpp by default), and type into the transcribe speech locally (whisper.cpp by default), and type into the
focused window. GTK, PyGObject and the interpreter come from the focused window. GTK, PyGObject and the interpreter come from the
distribution; pywhispercpp and pynput are vendored under distribution; pywhispercpp and pynput are vendored under
/usr/lib/vocalinux/vendor because neither distro packages them. /usr/lib/voculinux/vendor because neither distro packages them.
homepage: https://github.com/VocaHQ/vocalinux homepage: https://github.com/VocaHQ/vocalinux
license: AGPL-3.0-only license: AGPL-3.0-only
contents: contents:
# The staged payload tree: /usr/lib/vocalinux/{app,vendor}, the launchers in # The staged payload tree: /usr/lib/voculinux/{app,vendor}, the launchers in
# /usr/bin, the desktop file and the hicolor icon. build.sh writes it. # /usr/bin, the desktop file and the hicolor icon. build.sh writes it.
- src: ./payload/ - src: ./payload/
dst: / dst: /
@@ -45,10 +45,10 @@ contents:
# License placement differs between the formats: Debian policy wants the # License placement differs between the formats: Debian policy wants the
# copyright file under /usr/share/doc, Fedora marks /usr/share/licenses. # copyright file under /usr/share/doc, Fedora marks /usr/share/licenses.
- src: ./pkg-doc/LICENSE - src: ./pkg-doc/LICENSE
dst: /usr/share/doc/vocalinux/copyright dst: /usr/share/doc/voculinux/copyright
packager: deb packager: deb
- src: ./pkg-doc/LICENSE - src: ./pkg-doc/LICENSE
dst: /usr/share/licenses/vocalinux/LICENSE dst: /usr/share/licenses/voculinux/LICENSE
packager: rpm packager: rpm
overrides: overrides:
+6 -6
View File
@@ -52,17 +52,17 @@ echo "== installed: $PKG"
# before check_dependencies() touches GTK, so it proves the wrapper, the # before check_dependencies() touches GTK, so it proves the wrapper, the
# PYTHONPATH layout and the entry module's import chain — the dependency # PYTHONPATH layout and the entry module's import chain — the dependency
# typelibs are exercised by the import smoke below. # typelibs are exercised by the import smoke below.
vocalinux --version voculinux --version
vocalinux-gui --version voculinux-gui --version
# Import smoke in the shape of the AUR gate: the app modules, the vendored # Import smoke in the shape of the AUR gate: the app modules, the vendored
# pywhispercpp wheel, and the distro gi/GTK typelibs, all through the same # pywhispercpp wheel, and the distro gi/GTK typelibs, all through the same
# interpreter and environment the launcher sets up. pynput is only checked # interpreter and environment the launcher sets up. pynput is only checked
# for presence: its __init__ selects a display backend and raises ImportError # for presence: its __init__ selects a display backend and raises ImportError
# with no DISPLAY, which is why the app guards it with PYNPUT_AVAILABLE. # with no DISPLAY, which is why the app guards it with PYNPUT_AVAILABLE.
PYTHONPATH="/usr/lib/vocalinux/app:/usr/lib/vocalinux/vendor" \ PYTHONPATH="/usr/lib/voculinux/app:/usr/lib/voculinux/vendor" \
PYTHONNOUSERSITE=1 \ PYTHONNOUSERSITE=1 \
LD_LIBRARY_PATH="/usr/lib/vocalinux/vendor/pywhispercpp.libs" \ LD_LIBRARY_PATH="/usr/lib/voculinux/vendor/pywhispercpp.libs" \
/usr/bin/python3 - <<'PY' /usr/bin/python3 - <<'PY'
import importlib import importlib
import importlib.util import importlib.util
@@ -83,11 +83,11 @@ for module in MODULES:
importlib.import_module(module) importlib.import_module(module)
spec = importlib.util.find_spec("pynput") spec = importlib.util.find_spec("pynput")
assert spec and spec.origin and spec.origin.startswith("/usr/lib/vocalinux/vendor/"), \ assert spec and spec.origin and spec.origin.startswith("/usr/lib/voculinux/vendor/"), \
f"vendored pynput not found on sys.path: {spec}" f"vendored pynput not found on sys.path: {spec}"
tag = f"cpython-{sys.version_info.major}{sys.version_info.minor}-" tag = f"cpython-{sys.version_info.major}{sys.version_info.minor}-"
exts = [p.name for p in Path("/usr/lib/vocalinux/vendor").glob("_pywhispercpp.*.so")] exts = [p.name for p in Path("/usr/lib/voculinux/vendor").glob("_pywhispercpp.*.so")]
assert any(tag in name for name in exts), \ assert any(tag in name for name in exts), \
f"no _pywhispercpp extension for {tag[:-1]} among {exts}" f"no _pywhispercpp extension for {tag[:-1]} among {exts}"
+10 -10
View File
@@ -1,6 +1,6 @@
# Vocalinux for Nix # Voculinux for Nix
A flake that builds Vocalinux against nixpkgs: GTK 3, PyGObject and the A flake that builds Voculinux against nixpkgs: GTK 3, PyGObject and the
AppIndicator typelib come from nixpkgs, and `pywhispercpp` — which nixpkgs AppIndicator typelib come from nixpkgs, and `pywhispercpp` — which nixpkgs
does not package — is built from its PyPI sdist (it vendors whisper.cpp and does not package — is built from its PyPI sdist (it vendors whisper.cpp and
pybind11). Every host tool the app probes for (`xdotool`, `wtype`, `ydotool`, pybind11). Every host tool the app probes for (`xdotool`, `wtype`, `ydotool`,
@@ -24,14 +24,14 @@ nix profile install github:VocaHQ/vocalinux
```nix ```nix
{ {
inputs.vocalinux.url = "github:VocaHQ/vocalinux"; inputs.voculinux.url = "github:VocaHQ/vocalinux";
outputs = { nixpkgs, vocalinux, ... }: { outputs = { nixpkgs, voculinux, ... }: {
nixosConfigurations.host = nixpkgs.lib.nixosSystem { nixosConfigurations.host = nixpkgs.lib.nixosSystem {
modules = [ modules = [
vocalinux.nixosModules.vocalinux voculinux.nixosModules.voculinux
{ {
programs.vocalinux.enable = true; programs.voculinux.enable = true;
users.users.you.extraGroups = [ "input" "uinput" ]; users.users.you.extraGroups = [ "input" "uinput" ];
} }
]; ];
@@ -53,14 +53,14 @@ and can skip the group lines entirely.
inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
inputs.home-manager.url = "github:nix-community/home-manager"; inputs.home-manager.url = "github:nix-community/home-manager";
inputs.home-manager.inputs.nixpkgs.follows = "nixpkgs"; inputs.home-manager.inputs.nixpkgs.follows = "nixpkgs";
inputs.vocalinux.url = "github:VocaHQ/vocalinux"; inputs.voculinux.url = "github:VocaHQ/vocalinux";
outputs = { nixpkgs, home-manager, vocalinux, ... }: { outputs = { nixpkgs, home-manager, voculinux, ... }: {
homeConfigurations.you = home-manager.lib.homeManagerConfiguration { homeConfigurations.you = home-manager.lib.homeManagerConfiguration {
pkgs = nixpkgs.legacyPackages.x86_64-linux; pkgs = nixpkgs.legacyPackages.x86_64-linux;
modules = [ modules = [
vocalinux.homeManagerModules.vocalinux voculinux.homeManagerModules.voculinux
{ programs.vocalinux.enable = true; } { programs.voculinux.enable = true; }
]; ];
}; };
}; };
+6 -6
View File
@@ -1,4 +1,4 @@
# Home Manager module for Vocalinux. # Home Manager module for Voculinux.
# #
# Installs the package into the user profile; device permissions are a # Installs the package into the user profile; device permissions are a
# system concern, so pair it with the NixOS module (or the manual group # system concern, so pair it with the NixOS module (or the manual group
@@ -6,16 +6,16 @@
{ config, lib, pkgs, ... }: { config, lib, pkgs, ... }:
let let
cfg = config.programs.vocalinux; cfg = config.programs.voculinux;
in in
{ {
options.programs.vocalinux = { options.programs.voculinux = {
enable = lib.mkEnableOption "Vocalinux voice dictation"; enable = lib.mkEnableOption "Voculinux voice dictation";
package = lib.mkOption { package = lib.mkOption {
type = lib.types.package; type = lib.types.package;
defaultText = "the package built by the vocalinux flake"; defaultText = "the package built by the voculinux flake";
description = "The vocalinux package to install."; description = "The voculinux package to install.";
}; };
}; };
+9 -9
View File
@@ -1,9 +1,9 @@
# NixOS module for Vocalinux. # NixOS module for Voculinux.
# #
# The flake wraps this file and injects the flake-built package as the # The flake wraps this file and injects the flake-built package as the
# default (programs.vocalinux.package), so importing the module through # default (programs.voculinux.package), so importing the module through
# nixosModules.vocalinux needs no extra wiring. Imported on its own, set # nixosModules.voculinux needs no extra wiring. Imported on its own, set
# programs.vocalinux.package to a vocalinux derivation. # programs.voculinux.package to a voculinux derivation.
# #
# What it cannot do for you: membership in the "input" group is per-user, # What it cannot do for you: membership in the "input" group is per-user,
# so the module documents it and each user opts in: # so the module documents it and each user opts in:
@@ -16,16 +16,16 @@
{ config, lib, pkgs, ... }: { config, lib, pkgs, ... }:
let let
cfg = config.programs.vocalinux; cfg = config.programs.voculinux;
in in
{ {
options.programs.vocalinux = { options.programs.voculinux = {
enable = lib.mkEnableOption "Vocalinux voice dictation"; enable = lib.mkEnableOption "Voculinux voice dictation";
package = lib.mkOption { package = lib.mkOption {
type = lib.types.package; type = lib.types.package;
defaultText = "the package built by the vocalinux flake"; defaultText = "the package built by the voculinux flake";
description = "The vocalinux package to install."; description = "The voculinux package to install.";
}; };
}; };
+10 -10
View File
@@ -1,6 +1,6 @@
# Vocalinux for Nix. # Voculinux for Nix.
# #
# Consumed by the repo flake (packages.<system>.vocalinux) and buildable with # Consumed by the repo flake (packages.<system>.voculinux) and buildable with
# plain callPackage: # plain callPackage:
# #
# pkgs.python3Packages.callPackage ./packaging/nix/package.nix { # pkgs.python3Packages.callPackage ./packaging/nix/package.nix {
@@ -75,7 +75,7 @@ let
]; ];
in in
buildPythonApplication { buildPythonApplication {
pname = "vocalinux"; pname = "voculinux";
# Track src/vocalinux/version.py so release bumps need no edit here. # Track src/vocalinux/version.py so release bumps need no edit here.
# builtins.match is POSIX ERE (no \s class), so flatten newlines first. # builtins.match is POSIX ERE (no \s class), so flatten newlines first.
@@ -136,15 +136,15 @@ buildPythonApplication {
''; '';
postInstall = '' postInstall = ''
install -Dm644 vocalinux.desktop \ install -Dm644 voculinux.desktop \
$out/share/applications/vocalinux.desktop $out/share/applications/voculinux.desktop
install -Dm644 resources/icons/scalable/vocalinux.svg \ install -Dm644 resources/icons/scalable/voculinux.svg \
$out/share/icons/hicolor/scalable/apps/vocalinux.svg $out/share/icons/hicolor/scalable/apps/voculinux.svg
''; '';
dontCheck = true; # test suite needs a session bus and X display dontCheck = true; # test suite needs a session bus and X display
pythonImportsCheck = [ "vocalinux" ]; pythonImportsCheck = [ "voculinux" ];
meta = { meta = {
description = "Free, offline voice dictation for Linux"; description = "Free, offline voice dictation for Linux";
@@ -155,11 +155,11 @@ buildPythonApplication {
Wayland/evdev shortcuts and ydotool injection need device access: on Wayland/evdev shortcuts and ydotool injection need device access: on
NixOS enable hardware.uinput and add your user to the "input" and NixOS enable hardware.uinput and add your user to the "input" and
"uinput" groups; the programs.vocalinux NixOS module does the first part. "uinput" groups; the programs.voculinux NixOS module does the first part.
''; '';
homepage = "https://github.com/VocaHQ/vocalinux"; homepage = "https://github.com/VocaHQ/vocalinux";
license = lib.licenses.agpl3Only; license = lib.licenses.agpl3Only;
mainProgram = "vocalinux"; mainProgram = "voculinux";
platforms = lib.platforms.linux; platforms = lib.platforms.linux;
}; };
} }
+1 -1
View File
@@ -52,7 +52,7 @@ buildPythonPackage rec {
env = { env = {
# The sdist ships no version.txt; setup.py falls back to this env var. # The sdist ships no version.txt; setup.py falls back to this env var.
PYWHISPERCPP_VERSION = version; PYWHISPERCPP_VERSION = version;
# Build Vulkan as a shared backend: Vocalinux's GPU detector looks for # Build Vulkan as a shared backend: Voculinux's GPU detector looks for
# libggml-vulkan.so. CMake's build-tree RPATH can leak /build into the # libggml-vulkan.so. CMake's build-tree RPATH can leak /build into the
# wheel; disable CMake RPATHs and let autoPatchelfHook set Nix runtime paths. # wheel; disable CMake RPATHs and let autoPatchelfHook set Nix runtime paths.
CMAKE_ARGS = "-DBUILD_SHARED_LIBS=ON -DGGML_NATIVE=OFF -DGGML_VULKAN=ON -DCMAKE_POSITION_INDEPENDENT_CODE=ON -DCMAKE_SKIP_RPATH=ON"; CMAKE_ARGS = "-DBUILD_SHARED_LIBS=ON -DGGML_NATIVE=OFF -DGGML_VULKAN=ON -DCMAKE_POSITION_INDEPENDENT_CODE=ON -DCMAKE_SKIP_RPATH=ON";
+3 -3
View File
@@ -5,7 +5,7 @@ requires = ["setuptools>=77"]
build-backend = "setuptools.build_meta" build-backend = "setuptools.build_meta"
[project] [project]
name = "vocalinux" name = "voculinux"
dynamic = ["version"] dynamic = ["version"]
description = "A seamless voice dictation system for Linux" description = "A seamless voice dictation system for Linux"
readme = "README.md" readme = "README.md"
@@ -117,10 +117,10 @@ lint = [
] ]
[project.scripts] [project.scripts]
vocalinux = "vocalinux.main:main" voculinux = "vocalinux.main:main"
[project.gui-scripts] [project.gui-scripts]
vocalinux-gui = "vocalinux.main:main" voculinux-gui = "vocalinux.main:main"
[project.urls] [project.urls]
Homepage = "https://github.com/VocaHQ/vocalinux" Homepage = "https://github.com/VocaHQ/vocalinux"
+16 -16
View File
@@ -360,7 +360,7 @@ evdev==1.9.3 ; sys_platform == 'linux' or 'linux' in sys_platform \
--hash=sha256:2c140e01ac8437758fa23fe5c871397412461f42d421aa20241dc8fe8cfccbc9 --hash=sha256:2c140e01ac8437758fa23fe5c871397412461f42d421aa20241dc8fe8cfccbc9
# via # via
# pynput # pynput
# vocalinux # voculinux
filelock==4.2.1 \ filelock==4.2.1 \
--hash=sha256:7be2662e9e826b9bba28afc0f843a123280d799a2ea44bb5d35ccec67905ae44 \ --hash=sha256:7be2662e9e826b9bba28afc0f843a123280d799a2ea44bb5d35ccec67905ae44 \
--hash=sha256:c4c937ccac2a813cee448e662a56366cfb374c0f8702e75f295c412cc1c5d5a3 --hash=sha256:c4c937ccac2a813cee448e662a56366cfb374c0f8702e75f295c412cc1c5d5a3
@@ -547,7 +547,7 @@ mypy==2.3.1 \
--hash=sha256:ef0c6335cda9d807f8193d8ff6204a72bc909fa9882aacbca14f43cdb7188306 \ --hash=sha256:ef0c6335cda9d807f8193d8ff6204a72bc909fa9882aacbca14f43cdb7188306 \
--hash=sha256:f5d716048611e85ca9eefb2e1baa5d73ede389b5820ded260ea27c757d667af8 \ --hash=sha256:f5d716048611e85ca9eefb2e1baa5d73ede389b5820ded260ea27c757d667af8 \
--hash=sha256:ff715e45b2231a8e85de1d163d1b42791e4d7aab8f5145f85fee1b710b735aff --hash=sha256:ff715e45b2231a8e85de1d163d1b42791e4d7aab8f5145f85fee1b710b735aff
# via vocalinux # via voculinux
mypy-extensions==1.1.0 \ mypy-extensions==1.1.0 \
--hash=sha256:1be4cccdb0f2482337c4743e60421de3a356cd97508abadd57d47403e94f5505 \ --hash=sha256:1be4cccdb0f2482337c4743e60421de3a356cd97508abadd57d47403e94f5505 \
--hash=sha256:52e68efc3284861e772bbcd66823fde5ae21fd2fdb51c62a211403730b916558 --hash=sha256:52e68efc3284861e772bbcd66823fde5ae21fd2fdb51c62a211403730b916558
@@ -633,7 +633,7 @@ numpy==2.4.6 ; python_full_version < '3.12' \
--hash=sha256:f74a575920ab21fe304421a3fc28793d82e299cae9eccb37084e9fc7f3617c20 --hash=sha256:f74a575920ab21fe304421a3fc28793d82e299cae9eccb37084e9fc7f3617c20
# via # via
# pywhispercpp # pywhispercpp
# vocalinux # voculinux
numpy==2.5.2 ; python_full_version >= '3.12' \ numpy==2.5.2 ; python_full_version >= '3.12' \
--hash=sha256:0090ccdd57ec2703e9b49d0bf554767370581c1dd0a6b2bb2b2d9def317d042a \ --hash=sha256:0090ccdd57ec2703e9b49d0bf554767370581c1dd0a6b2bb2b2d9def317d042a \
--hash=sha256:078f9b027b478c9379b9677babbf0f8b8f1ecfada27636d7b9a93990c638739f \ --hash=sha256:078f9b027b478c9379b9677babbf0f8b8f1ecfada27636d7b9a93990c638739f \
@@ -703,7 +703,7 @@ numpy==2.5.2 ; python_full_version >= '3.12' \
--hash=sha256:fd0d703772bba096843785bd38371e31bb4a0c1151497ad5739d182114a73f7f --hash=sha256:fd0d703772bba096843785bd38371e31bb4a0c1151497ad5739d182114a73f7f
# via # via
# pywhispercpp # pywhispercpp
# vocalinux # voculinux
packaging==26.3 \ packaging==26.3 \
--hash=sha256:94edc256424af38762eb31306eed28beb9f0efc50a8837492c9d6fd6004aed79 \ --hash=sha256:94edc256424af38762eb31306eed28beb9f0efc50a8837492c9d6fd6004aed79 \
--hash=sha256:d7193f7c8e4e93f444fde0262bf90af30e16fa0ad0ad44cb553c87339b23cd1c --hash=sha256:d7193f7c8e4e93f444fde0262bf90af30e16fa0ad0ad44cb553c87339b23cd1c
@@ -733,7 +733,7 @@ pluggy==1.6.0 \
pre-commit==4.6.2 \ pre-commit==4.6.2 \
--hash=sha256:8f5d7bfb021ecdbcd9d49d89847082dd24172ccde534390081a679ad046e2441 \ --hash=sha256:8f5d7bfb021ecdbcd9d49d89847082dd24172ccde534390081a679ad046e2441 \
--hash=sha256:e2dde9a75d3bce11bd3831c26d134df00a2803c1d818be6a0383c3dcda25dc4e --hash=sha256:e2dde9a75d3bce11bd3831c26d134df00a2803c1d818be6a0383c3dcda25dc4e
# via vocalinux # via voculinux
psutil==7.2.2 \ psutil==7.2.2 \
--hash=sha256:0746f5f8d406af344fd547f1c8daa5f5c33dbc293bb8d6a16d80b4bb88f59372 \ --hash=sha256:0746f5f8d406af344fd547f1c8daa5f5c33dbc293bb8d6a16d80b4bb88f59372 \
--hash=sha256:076a2d2f923fd4821644f5ba89f059523da90dc9014e85f8e45a5774ca5bc6f9 \ --hash=sha256:076a2d2f923fd4821644f5ba89f059523da90dc9014e85f8e45a5774ca5bc6f9 \
@@ -756,7 +756,7 @@ psutil==7.2.2 \
--hash=sha256:ed0cace939114f62738d808fdcecd4c869222507e266e574799e9c0faa17d486 \ --hash=sha256:ed0cace939114f62738d808fdcecd4c869222507e266e574799e9c0faa17d486 \
--hash=sha256:eed63d3b4d62449571547b60578c5b2c4bcccc5387148db46e0c2313dad0ee00 \ --hash=sha256:eed63d3b4d62449571547b60578c5b2c4bcccc5387148db46e0c2313dad0ee00 \
--hash=sha256:fd04ef36b4a6d599bbdb225dd1d3f51e00105f6d48a28f006da7f9822f2606d8 --hash=sha256:fd04ef36b4a6d599bbdb225dd1d3f51e00105f6d48a28f006da7f9822f2606d8
# via vocalinux # via voculinux
pyaudio==0.2.14 \ pyaudio==0.2.14 \
--hash=sha256:12f2f1ba04e06ff95d80700a78967897a489c05e093e3bffa05a84ed9c0a7fa3 \ --hash=sha256:12f2f1ba04e06ff95d80700a78967897a489c05e093e3bffa05a84ed9c0a7fa3 \
--hash=sha256:506b32a595f8693811682ab4b127602d404df7dfc453b499c91a80d0f7bad289 \ --hash=sha256:506b32a595f8693811682ab4b127602d404df7dfc453b499c91a80d0f7bad289 \
@@ -765,7 +765,7 @@ pyaudio==0.2.14 \
--hash=sha256:78dfff3879b4994d1f4fc6485646a57755c6ee3c19647a491f790a0895bd2f87 \ --hash=sha256:78dfff3879b4994d1f4fc6485646a57755c6ee3c19647a491f790a0895bd2f87 \
--hash=sha256:95328285b4dab57ea8c52a4a996cb52be6d629353315be5bfda403d15932a497 \ --hash=sha256:95328285b4dab57ea8c52a4a996cb52be6d629353315be5bfda403d15932a497 \
--hash=sha256:bbeb01d36a2f472ae5ee5e1451cacc42112986abe622f735bb870a5db77cf903 --hash=sha256:bbeb01d36a2f472ae5ee5e1451cacc42112986abe622f735bb870a5db77cf903
# via vocalinux # via voculinux
pycairo==1.29.1 ; sys_platform == 'linux' \ pycairo==1.29.1 ; sys_platform == 'linux' \
--hash=sha256:4fbd26b4af24c9787d84cf5448e34eb8dca064b732479aaecd03109520eebd5f --hash=sha256:4fbd26b4af24c9787d84cf5448e34eb8dca064b732479aaecd03109520eebd5f
# via pygobject # via pygobject
@@ -784,7 +784,7 @@ pygments==2.21.0 \
pynput==1.8.2 \ pynput==1.8.2 \
--hash=sha256:8cc38cf13a6ab2749cb375678be8a0fd705d7ce49c8001ff5db4007a723bbef1 \ --hash=sha256:8cc38cf13a6ab2749cb375678be8a0fd705d7ce49c8001ff5db4007a723bbef1 \
--hash=sha256:f493c87157cd3861b4468f7f896857051762f44ed26f1b641e7cc5840a457087 --hash=sha256:f493c87157cd3861b4468f7f896857051762f44ed26f1b641e7cc5840a457087
# via vocalinux # via voculinux
pyobjc-core==12.2.2 ; sys_platform == 'darwin' \ pyobjc-core==12.2.2 ; sys_platform == 'darwin' \
--hash=sha256:122e6ad302a2abf5d4d4adb0156db751600ddf2768441696cba17b31323085e7 \ --hash=sha256:122e6ad302a2abf5d4d4adb0156db751600ddf2768441696cba17b31323085e7 \
--hash=sha256:2062e8ad30a310441cd022544a897553408bebeaa7820d5edba3c96fd7fd693b \ --hash=sha256:2062e8ad30a310441cd022544a897553408bebeaa7820d5edba3c96fd7fd693b \
@@ -853,7 +853,7 @@ pyobjc-framework-quartz==12.2.2 ; sys_platform == 'darwin' \
pysocks==1.7.1 \ pysocks==1.7.1 \
--hash=sha256:2725bd0a9925919b9b51739eea5f9e2bae91e83288108a9ad338b2e3a4435ee5 \ --hash=sha256:2725bd0a9925919b9b51739eea5f9e2bae91e83288108a9ad338b2e3a4435ee5 \
--hash=sha256:3f8804571ebe159c380ac6de37643bb4685970655d3bba243530d6558b799aa0 --hash=sha256:3f8804571ebe159c380ac6de37643bb4685970655d3bba243530d6558b799aa0
# via vocalinux # via voculinux
pytest==9.1.1 \ pytest==9.1.1 \
--hash=sha256:1088fbde8f2b49d95a549a195707afa7a76a3ce9bcadc26b6d71f0ffda5fe313 \ --hash=sha256:1088fbde8f2b49d95a549a195707afa7a76a3ce9bcadc26b6d71f0ffda5fe313 \
--hash=sha256:37a86b45efb9a47a61a36449063e8e18d0cab3161329fc099eb21783169c4f0c --hash=sha256:37a86b45efb9a47a61a36449063e8e18d0cab3161329fc099eb21783169c4f0c
@@ -861,19 +861,19 @@ pytest==9.1.1 \
# pytest-cov # pytest-cov
# pytest-mock # pytest-mock
# pytest-timeout # pytest-timeout
# vocalinux # voculinux
pytest-cov==7.1.0 \ pytest-cov==7.1.0 \
--hash=sha256:30674f2b5f6351aa09702a9c8c364f6a01c27aae0c1366ae8016160d1efc56b2 \ --hash=sha256:30674f2b5f6351aa09702a9c8c364f6a01c27aae0c1366ae8016160d1efc56b2 \
--hash=sha256:a0461110b7865f9a271aa1b51e516c9a95de9d696734a2f71e3e78f46e1d4678 --hash=sha256:a0461110b7865f9a271aa1b51e516c9a95de9d696734a2f71e3e78f46e1d4678
# via vocalinux # via voculinux
pytest-mock==3.15.1 \ pytest-mock==3.15.1 \
--hash=sha256:0a25e2eb88fe5168d535041d09a4529a188176ae608a6d249ee65abc0949630d \ --hash=sha256:0a25e2eb88fe5168d535041d09a4529a188176ae608a6d249ee65abc0949630d \
--hash=sha256:1849a238f6f396da19762269de72cb1814ab44416fa73a8686deac10b0d87a0f --hash=sha256:1849a238f6f396da19762269de72cb1814ab44416fa73a8686deac10b0d87a0f
# via vocalinux # via voculinux
pytest-timeout==2.4.0 \ pytest-timeout==2.4.0 \
--hash=sha256:7e68e90b01f9eff71332b25001f85c75495fc4e3a836701876183c4bcfd0540a \ --hash=sha256:7e68e90b01f9eff71332b25001f85c75495fc4e3a836701876183c4bcfd0540a \
--hash=sha256:c42667e5cdadb151aeb5b26d114aff6bdf5a907f176a007a30b940d3d865b5c2 --hash=sha256:c42667e5cdadb151aeb5b26d114aff6bdf5a907f176a007a30b940d3d865b5c2
# via vocalinux # via voculinux
python-discovery==1.6.4 \ python-discovery==1.6.4 \
--hash=sha256:530179cc51a1816d512b15635b3dc931295ae12efb183783ad31190a5d425d2b \ --hash=sha256:530179cc51a1816d512b15635b3dc931295ae12efb183783ad31190a5d425d2b \
--hash=sha256:bc70d31f0ec5de11eda13d4d64c16e24b3506a65984684f2b6c98cb83b2ea9df --hash=sha256:bc70d31f0ec5de11eda13d4d64c16e24b3506a65984684f2b6c98cb83b2ea9df
@@ -948,7 +948,7 @@ pywhispercpp==1.5.0 \
--hash=sha256:f5d13b4a7e34b9f4eebf70bff150ec36f4145c580bd5d812b9a3139611e7433c \ --hash=sha256:f5d13b4a7e34b9f4eebf70bff150ec36f4145c580bd5d812b9a3139611e7433c \
--hash=sha256:f9b1cd9dd4c3eb7fb2b777529eb62618e58a6bb05be20f82b7b1afe3d0b12274 \ --hash=sha256:f9b1cd9dd4c3eb7fb2b777529eb62618e58a6bb05be20f82b7b1afe3d0b12274 \
--hash=sha256:fbdbb1bb236f4481ff6ea475aed9ed091393ddb02271df309645d71de207778d --hash=sha256:fbdbb1bb236f4481ff6ea475aed9ed091393ddb02271df309645d71de207778d
# via vocalinux # via voculinux
pyyaml==6.0.3 \ pyyaml==6.0.3 \
--hash=sha256:00c4bdeba853cc34e7dd471f16b4114f4162dc03e6b7afcc2128711f0eca823c \ --hash=sha256:00c4bdeba853cc34e7dd471f16b4114f4162dc03e6b7afcc2128711f0eca823c \
--hash=sha256:02893d100e99e03eda1c8fd5c441d8c60103fd175728e23e431db1b589cf5ab3 \ --hash=sha256:02893d100e99e03eda1c8fd5c441d8c60103fd175728e23e431db1b589cf5ab3 \
@@ -1000,13 +1000,13 @@ pyyaml==6.0.3 \
--hash=sha256:fc09d0aa354569bc501d4e787133afc08552722d3ab34836a80547331bb5d4a0 --hash=sha256:fc09d0aa354569bc501d4e787133afc08552722d3ab34836a80547331bb5d4a0
# via # via
# pre-commit # pre-commit
# vocalinux # voculinux
requests==2.34.2 \ requests==2.34.2 \
--hash=sha256:2a0d60c172f83ac6ab31e4554906c0f3b3588d37b5cb939b1c061f4907e278e0 \ --hash=sha256:2a0d60c172f83ac6ab31e4554906c0f3b3588d37b5cb939b1c061f4907e278e0 \
--hash=sha256:f288924cae4e29463698d6d60bc6a4da69c89185ad1e0bcc4104f584e960b9ed --hash=sha256:f288924cae4e29463698d6d60bc6a4da69c89185ad1e0bcc4104f584e960b9ed
# via # via
# pywhispercpp # pywhispercpp
# vocalinux # voculinux
six==1.17.0 \ six==1.17.0 \
--hash=sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274 \ --hash=sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274 \
--hash=sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81 --hash=sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81
+10 -10
View File
@@ -215,10 +215,10 @@ evdev==1.9.3 ; sys_platform == 'linux' or 'linux' in sys_platform \
--hash=sha256:2c140e01ac8437758fa23fe5c871397412461f42d421aa20241dc8fe8cfccbc9 --hash=sha256:2c140e01ac8437758fa23fe5c871397412461f42d421aa20241dc8fe8cfccbc9
# via # via
# pynput # pynput
# vocalinux # voculinux
faster-whisper==1.2.1 \ faster-whisper==1.2.1 \
--hash=sha256:79a66ad50688c0b794dd501dc340a736992a6342f7f95e5811be60b5224a26a7 --hash=sha256:79a66ad50688c0b794dd501dc340a736992a6342f7f95e5811be60b5224a26a7
# via vocalinux # via voculinux
filelock==4.2.1 \ filelock==4.2.1 \
--hash=sha256:7be2662e9e826b9bba28afc0f843a123280d799a2ea44bb5d35ccec67905ae44 \ --hash=sha256:7be2662e9e826b9bba28afc0f843a123280d799a2ea44bb5d35ccec67905ae44 \
--hash=sha256:c4c937ccac2a813cee448e662a56366cfb374c0f8702e75f295c412cc1c5d5a3 --hash=sha256:c4c937ccac2a813cee448e662a56366cfb374c0f8702e75f295c412cc1c5d5a3
@@ -351,7 +351,7 @@ numpy==2.4.6 ; python_full_version < '3.12' \
# ctranslate2 # ctranslate2
# onnxruntime # onnxruntime
# pywhispercpp # pywhispercpp
# vocalinux # voculinux
numpy==2.5.2 ; python_full_version >= '3.12' \ numpy==2.5.2 ; python_full_version >= '3.12' \
--hash=sha256:0090ccdd57ec2703e9b49d0bf554767370581c1dd0a6b2bb2b2d9def317d042a \ --hash=sha256:0090ccdd57ec2703e9b49d0bf554767370581c1dd0a6b2bb2b2d9def317d042a \
--hash=sha256:078f9b027b478c9379b9677babbf0f8b8f1ecfada27636d7b9a93990c638739f \ --hash=sha256:078f9b027b478c9379b9677babbf0f8b8f1ecfada27636d7b9a93990c638739f \
@@ -423,7 +423,7 @@ numpy==2.5.2 ; python_full_version >= '3.12' \
# ctranslate2 # ctranslate2
# onnxruntime # onnxruntime
# pywhispercpp # pywhispercpp
# vocalinux # voculinux
onnxruntime==1.29.0 \ onnxruntime==1.29.0 \
--hash=sha256:07c5907474dec4a2792fd7626b753dc66707808385a6d9eecf993db0066a9d0f \ --hash=sha256:07c5907474dec4a2792fd7626b753dc66707808385a6d9eecf993db0066a9d0f \
--hash=sha256:0d4f427afac434b0070fe992b540ddf20a7aff2265f760f314d91331935b6b98 \ --hash=sha256:0d4f427afac434b0070fe992b540ddf20a7aff2265f760f314d91331935b6b98 \
@@ -492,7 +492,7 @@ psutil==7.2.2 \
--hash=sha256:ed0cace939114f62738d808fdcecd4c869222507e266e574799e9c0faa17d486 \ --hash=sha256:ed0cace939114f62738d808fdcecd4c869222507e266e574799e9c0faa17d486 \
--hash=sha256:eed63d3b4d62449571547b60578c5b2c4bcccc5387148db46e0c2313dad0ee00 \ --hash=sha256:eed63d3b4d62449571547b60578c5b2c4bcccc5387148db46e0c2313dad0ee00 \
--hash=sha256:fd04ef36b4a6d599bbdb225dd1d3f51e00105f6d48a28f006da7f9822f2606d8 --hash=sha256:fd04ef36b4a6d599bbdb225dd1d3f51e00105f6d48a28f006da7f9822f2606d8
# via vocalinux # via voculinux
pyaudio==0.2.14 \ pyaudio==0.2.14 \
--hash=sha256:12f2f1ba04e06ff95d80700a78967897a489c05e093e3bffa05a84ed9c0a7fa3 \ --hash=sha256:12f2f1ba04e06ff95d80700a78967897a489c05e093e3bffa05a84ed9c0a7fa3 \
--hash=sha256:506b32a595f8693811682ab4b127602d404df7dfc453b499c91a80d0f7bad289 \ --hash=sha256:506b32a595f8693811682ab4b127602d404df7dfc453b499c91a80d0f7bad289 \
@@ -501,14 +501,14 @@ pyaudio==0.2.14 \
--hash=sha256:78dfff3879b4994d1f4fc6485646a57755c6ee3c19647a491f790a0895bd2f87 \ --hash=sha256:78dfff3879b4994d1f4fc6485646a57755c6ee3c19647a491f790a0895bd2f87 \
--hash=sha256:95328285b4dab57ea8c52a4a996cb52be6d629353315be5bfda403d15932a497 \ --hash=sha256:95328285b4dab57ea8c52a4a996cb52be6d629353315be5bfda403d15932a497 \
--hash=sha256:bbeb01d36a2f472ae5ee5e1451cacc42112986abe622f735bb870a5db77cf903 --hash=sha256:bbeb01d36a2f472ae5ee5e1451cacc42112986abe622f735bb870a5db77cf903
# via vocalinux # via voculinux
pycairo==1.29.1 ; sys_platform == 'linux' \ pycairo==1.29.1 ; sys_platform == 'linux' \
--hash=sha256:4fbd26b4af24c9787d84cf5448e34eb8dca064b732479aaecd03109520eebd5f --hash=sha256:4fbd26b4af24c9787d84cf5448e34eb8dca064b732479aaecd03109520eebd5f
# via pygobject # via pygobject
pynput==1.8.2 \ pynput==1.8.2 \
--hash=sha256:8cc38cf13a6ab2749cb375678be8a0fd705d7ce49c8001ff5db4007a723bbef1 \ --hash=sha256:8cc38cf13a6ab2749cb375678be8a0fd705d7ce49c8001ff5db4007a723bbef1 \
--hash=sha256:f493c87157cd3861b4468f7f896857051762f44ed26f1b641e7cc5840a457087 --hash=sha256:f493c87157cd3861b4468f7f896857051762f44ed26f1b641e7cc5840a457087
# via vocalinux # via voculinux
pyobjc-core==12.2.2 ; sys_platform == 'darwin' \ pyobjc-core==12.2.2 ; sys_platform == 'darwin' \
--hash=sha256:122e6ad302a2abf5d4d4adb0156db751600ddf2768441696cba17b31323085e7 \ --hash=sha256:122e6ad302a2abf5d4d4adb0156db751600ddf2768441696cba17b31323085e7 \
--hash=sha256:2062e8ad30a310441cd022544a897553408bebeaa7820d5edba3c96fd7fd693b \ --hash=sha256:2062e8ad30a310441cd022544a897553408bebeaa7820d5edba3c96fd7fd693b \
@@ -577,7 +577,7 @@ pyobjc-framework-quartz==12.2.2 ; sys_platform == 'darwin' \
pysocks==1.7.1 \ pysocks==1.7.1 \
--hash=sha256:2725bd0a9925919b9b51739eea5f9e2bae91e83288108a9ad338b2e3a4435ee5 \ --hash=sha256:2725bd0a9925919b9b51739eea5f9e2bae91e83288108a9ad338b2e3a4435ee5 \
--hash=sha256:3f8804571ebe159c380ac6de37643bb4685970655d3bba243530d6558b799aa0 --hash=sha256:3f8804571ebe159c380ac6de37643bb4685970655d3bba243530d6558b799aa0
# via vocalinux # via voculinux
python-xlib==0.33 ; 'linux' in sys_platform \ python-xlib==0.33 ; 'linux' in sys_platform \
--hash=sha256:55af7906a2c75ce6cb280a584776080602444f75815a7aff4d287bb2d7018b32 \ --hash=sha256:55af7906a2c75ce6cb280a584776080602444f75815a7aff4d287bb2d7018b32 \
--hash=sha256:c3534038d42e0df2f1392a1b30a15a4ff5fdc2b86cfa94f072bf11b10a164398 --hash=sha256:c3534038d42e0df2f1392a1b30a15a4ff5fdc2b86cfa94f072bf11b10a164398
@@ -619,7 +619,7 @@ pywhispercpp==1.5.0 \
--hash=sha256:f5d13b4a7e34b9f4eebf70bff150ec36f4145c580bd5d812b9a3139611e7433c \ --hash=sha256:f5d13b4a7e34b9f4eebf70bff150ec36f4145c580bd5d812b9a3139611e7433c \
--hash=sha256:f9b1cd9dd4c3eb7fb2b777529eb62618e58a6bb05be20f82b7b1afe3d0b12274 \ --hash=sha256:f9b1cd9dd4c3eb7fb2b777529eb62618e58a6bb05be20f82b7b1afe3d0b12274 \
--hash=sha256:fbdbb1bb236f4481ff6ea475aed9ed091393ddb02271df309645d71de207778d --hash=sha256:fbdbb1bb236f4481ff6ea475aed9ed091393ddb02271df309645d71de207778d
# via vocalinux # via voculinux
pyyaml==6.0.3 \ pyyaml==6.0.3 \
--hash=sha256:00c4bdeba853cc34e7dd471f16b4114f4162dc03e6b7afcc2128711f0eca823c \ --hash=sha256:00c4bdeba853cc34e7dd471f16b4114f4162dc03e6b7afcc2128711f0eca823c \
--hash=sha256:02893d100e99e03eda1c8fd5c441d8c60103fd175728e23e431db1b589cf5ab3 \ --hash=sha256:02893d100e99e03eda1c8fd5c441d8c60103fd175728e23e431db1b589cf5ab3 \
@@ -677,7 +677,7 @@ requests==2.34.2 \
--hash=sha256:f288924cae4e29463698d6d60bc6a4da69c89185ad1e0bcc4104f584e960b9ed --hash=sha256:f288924cae4e29463698d6d60bc6a4da69c89185ad1e0bcc4104f584e960b9ed
# via # via
# pywhispercpp # pywhispercpp
# vocalinux # voculinux
six==1.17.0 \ six==1.17.0 \
--hash=sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274 \ --hash=sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274 \
--hash=sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81 --hash=sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81
+11 -11
View File
@@ -154,7 +154,7 @@ evdev==1.9.3 ; sys_platform == 'linux' or 'linux' in sys_platform \
--hash=sha256:2c140e01ac8437758fa23fe5c871397412461f42d421aa20241dc8fe8cfccbc9 --hash=sha256:2c140e01ac8437758fa23fe5c871397412461f42d421aa20241dc8fe8cfccbc9
# via # via
# pynput # pynput
# vocalinux # voculinux
idna==3.19 \ idna==3.19 \
--hash=sha256:5e0811a4383b21dc5838069f801c4fb62113b7447663d2530d2bd6e77b49bf15 \ --hash=sha256:5e0811a4383b21dc5838069f801c4fb62113b7447663d2530d2bd6e77b49bf15 \
--hash=sha256:815e7be7a7806d54abb586dc943addc79e8b2ee16915059658cbeff4b1b43bf4 --hash=sha256:815e7be7a7806d54abb586dc943addc79e8b2ee16915059658cbeff4b1b43bf4
@@ -234,7 +234,7 @@ numpy==2.4.6 ; python_full_version < '3.12' \
--hash=sha256:f74a575920ab21fe304421a3fc28793d82e299cae9eccb37084e9fc7f3617c20 --hash=sha256:f74a575920ab21fe304421a3fc28793d82e299cae9eccb37084e9fc7f3617c20
# via # via
# pywhispercpp # pywhispercpp
# vocalinux # voculinux
numpy==2.5.2 ; python_full_version >= '3.12' \ numpy==2.5.2 ; python_full_version >= '3.12' \
--hash=sha256:0090ccdd57ec2703e9b49d0bf554767370581c1dd0a6b2bb2b2d9def317d042a \ --hash=sha256:0090ccdd57ec2703e9b49d0bf554767370581c1dd0a6b2bb2b2d9def317d042a \
--hash=sha256:078f9b027b478c9379b9677babbf0f8b8f1ecfada27636d7b9a93990c638739f \ --hash=sha256:078f9b027b478c9379b9677babbf0f8b8f1ecfada27636d7b9a93990c638739f \
@@ -304,7 +304,7 @@ numpy==2.5.2 ; python_full_version >= '3.12' \
--hash=sha256:fd0d703772bba096843785bd38371e31bb4a0c1151497ad5739d182114a73f7f --hash=sha256:fd0d703772bba096843785bd38371e31bb4a0c1151497ad5739d182114a73f7f
# via # via
# pywhispercpp # pywhispercpp
# vocalinux # voculinux
platformdirs==4.11.3 \ platformdirs==4.11.3 \
--hash=sha256:5ed065d443751de711da036041a7a214122efc4a4de393b3f4137ba5576540e7 \ --hash=sha256:5ed065d443751de711da036041a7a214122efc4a4de393b3f4137ba5576540e7 \
--hash=sha256:66a73d38a849810252df809a3d8bcbda8e26f6c189920e7535ad608a48dbb5ab --hash=sha256:66a73d38a849810252df809a3d8bcbda8e26f6c189920e7535ad608a48dbb5ab
@@ -331,7 +331,7 @@ psutil==7.2.2 \
--hash=sha256:ed0cace939114f62738d808fdcecd4c869222507e266e574799e9c0faa17d486 \ --hash=sha256:ed0cace939114f62738d808fdcecd4c869222507e266e574799e9c0faa17d486 \
--hash=sha256:eed63d3b4d62449571547b60578c5b2c4bcccc5387148db46e0c2313dad0ee00 \ --hash=sha256:eed63d3b4d62449571547b60578c5b2c4bcccc5387148db46e0c2313dad0ee00 \
--hash=sha256:fd04ef36b4a6d599bbdb225dd1d3f51e00105f6d48a28f006da7f9822f2606d8 --hash=sha256:fd04ef36b4a6d599bbdb225dd1d3f51e00105f6d48a28f006da7f9822f2606d8
# via vocalinux # via voculinux
pyaudio==0.2.14 \ pyaudio==0.2.14 \
--hash=sha256:12f2f1ba04e06ff95d80700a78967897a489c05e093e3bffa05a84ed9c0a7fa3 \ --hash=sha256:12f2f1ba04e06ff95d80700a78967897a489c05e093e3bffa05a84ed9c0a7fa3 \
--hash=sha256:506b32a595f8693811682ab4b127602d404df7dfc453b499c91a80d0f7bad289 \ --hash=sha256:506b32a595f8693811682ab4b127602d404df7dfc453b499c91a80d0f7bad289 \
@@ -340,14 +340,14 @@ pyaudio==0.2.14 \
--hash=sha256:78dfff3879b4994d1f4fc6485646a57755c6ee3c19647a491f790a0895bd2f87 \ --hash=sha256:78dfff3879b4994d1f4fc6485646a57755c6ee3c19647a491f790a0895bd2f87 \
--hash=sha256:95328285b4dab57ea8c52a4a996cb52be6d629353315be5bfda403d15932a497 \ --hash=sha256:95328285b4dab57ea8c52a4a996cb52be6d629353315be5bfda403d15932a497 \
--hash=sha256:bbeb01d36a2f472ae5ee5e1451cacc42112986abe622f735bb870a5db77cf903 --hash=sha256:bbeb01d36a2f472ae5ee5e1451cacc42112986abe622f735bb870a5db77cf903
# via vocalinux # via voculinux
pycairo==1.29.1 ; sys_platform == 'linux' \ pycairo==1.29.1 ; sys_platform == 'linux' \
--hash=sha256:4fbd26b4af24c9787d84cf5448e34eb8dca064b732479aaecd03109520eebd5f --hash=sha256:4fbd26b4af24c9787d84cf5448e34eb8dca064b732479aaecd03109520eebd5f
# via pygobject # via pygobject
pynput==1.8.2 \ pynput==1.8.2 \
--hash=sha256:8cc38cf13a6ab2749cb375678be8a0fd705d7ce49c8001ff5db4007a723bbef1 \ --hash=sha256:8cc38cf13a6ab2749cb375678be8a0fd705d7ce49c8001ff5db4007a723bbef1 \
--hash=sha256:f493c87157cd3861b4468f7f896857051762f44ed26f1b641e7cc5840a457087 --hash=sha256:f493c87157cd3861b4468f7f896857051762f44ed26f1b641e7cc5840a457087
# via vocalinux # via voculinux
pyobjc-core==12.2.2 ; sys_platform == 'darwin' \ pyobjc-core==12.2.2 ; sys_platform == 'darwin' \
--hash=sha256:122e6ad302a2abf5d4d4adb0156db751600ddf2768441696cba17b31323085e7 \ --hash=sha256:122e6ad302a2abf5d4d4adb0156db751600ddf2768441696cba17b31323085e7 \
--hash=sha256:2062e8ad30a310441cd022544a897553408bebeaa7820d5edba3c96fd7fd693b \ --hash=sha256:2062e8ad30a310441cd022544a897553408bebeaa7820d5edba3c96fd7fd693b \
@@ -416,7 +416,7 @@ pyobjc-framework-quartz==12.2.2 ; sys_platform == 'darwin' \
pysocks==1.7.1 \ pysocks==1.7.1 \
--hash=sha256:2725bd0a9925919b9b51739eea5f9e2bae91e83288108a9ad338b2e3a4435ee5 \ --hash=sha256:2725bd0a9925919b9b51739eea5f9e2bae91e83288108a9ad338b2e3a4435ee5 \
--hash=sha256:3f8804571ebe159c380ac6de37643bb4685970655d3bba243530d6558b799aa0 --hash=sha256:3f8804571ebe159c380ac6de37643bb4685970655d3bba243530d6558b799aa0
# via vocalinux # via voculinux
python-xlib==0.33 ; 'linux' in sys_platform \ python-xlib==0.33 ; 'linux' in sys_platform \
--hash=sha256:55af7906a2c75ce6cb280a584776080602444f75815a7aff4d287bb2d7018b32 \ --hash=sha256:55af7906a2c75ce6cb280a584776080602444f75815a7aff4d287bb2d7018b32 \
--hash=sha256:c3534038d42e0df2f1392a1b30a15a4ff5fdc2b86cfa94f072bf11b10a164398 --hash=sha256:c3534038d42e0df2f1392a1b30a15a4ff5fdc2b86cfa94f072bf11b10a164398
@@ -458,13 +458,13 @@ pywhispercpp==1.5.0 \
--hash=sha256:f5d13b4a7e34b9f4eebf70bff150ec36f4145c580bd5d812b9a3139611e7433c \ --hash=sha256:f5d13b4a7e34b9f4eebf70bff150ec36f4145c580bd5d812b9a3139611e7433c \
--hash=sha256:f9b1cd9dd4c3eb7fb2b777529eb62618e58a6bb05be20f82b7b1afe3d0b12274 \ --hash=sha256:f9b1cd9dd4c3eb7fb2b777529eb62618e58a6bb05be20f82b7b1afe3d0b12274 \
--hash=sha256:fbdbb1bb236f4481ff6ea475aed9ed091393ddb02271df309645d71de207778d --hash=sha256:fbdbb1bb236f4481ff6ea475aed9ed091393ddb02271df309645d71de207778d
# via vocalinux # via voculinux
requests==2.34.2 \ requests==2.34.2 \
--hash=sha256:2a0d60c172f83ac6ab31e4554906c0f3b3588d37b5cb939b1c061f4907e278e0 \ --hash=sha256:2a0d60c172f83ac6ab31e4554906c0f3b3588d37b5cb939b1c061f4907e278e0 \
--hash=sha256:f288924cae4e29463698d6d60bc6a4da69c89185ad1e0bcc4104f584e960b9ed --hash=sha256:f288924cae4e29463698d6d60bc6a4da69c89185ad1e0bcc4104f584e960b9ed
# via # via
# pywhispercpp # pywhispercpp
# vocalinux # voculinux
sherpa-onnx==1.13.7 \ sherpa-onnx==1.13.7 \
--hash=sha256:01c4d001390f282bef653d229dce5d9da5009a804999449e33f890139f1f2da0 \ --hash=sha256:01c4d001390f282bef653d229dce5d9da5009a804999449e33f890139f1f2da0 \
--hash=sha256:026917d88fde36d5d86661e4a61c968dd741f43a96773ad742da13bffd030471 \ --hash=sha256:026917d88fde36d5d86661e4a61c968dd741f43a96773ad742da13bffd030471 \
@@ -507,7 +507,7 @@ sherpa-onnx==1.13.7 \
--hash=sha256:ed40aee149f9e2e47d0b5cf1be21b2d662e5816bebdabdcdbbdb98b8a9928cbe \ --hash=sha256:ed40aee149f9e2e47d0b5cf1be21b2d662e5816bebdabdcdbbdb98b8a9928cbe \
--hash=sha256:fce7cfab223d6bee0a58fa940b5057e2f3cf63613928b88e09ac99b477e40be0 \ --hash=sha256:fce7cfab223d6bee0a58fa940b5057e2f3cf63613928b88e09ac99b477e40be0 \
--hash=sha256:fed61cc23dba8f212eeb2723d2e90108d6de95734c5ae427aeafdb10f09ec184 --hash=sha256:fed61cc23dba8f212eeb2723d2e90108d6de95734c5ae427aeafdb10f09ec184
# via vocalinux # via voculinux
sherpa-onnx-core==1.13.7 ; platform_machine != 'armv7l' \ sherpa-onnx-core==1.13.7 ; platform_machine != 'armv7l' \
--hash=sha256:07de22f590299a68d9b5c985264f380579600a831ee95510ab12f4068800d08c \ --hash=sha256:07de22f590299a68d9b5c985264f380579600a831ee95510ab12f4068800d08c \
--hash=sha256:861c1e18fbf9bd2c725992c5a1797efe1b934b7df7bb18986d1fdc60e91290c9 \ --hash=sha256:861c1e18fbf9bd2c725992c5a1797efe1b934b7df7bb18986d1fdc60e91290c9 \
@@ -522,7 +522,7 @@ sherpa-onnx-core==1.13.7 ; platform_machine != 'armv7l' \
--hash=sha256:e1e6333ecde50c69b8a43feff6eedf1033ec2a3fe58009506572bd717fc78e42 \ --hash=sha256:e1e6333ecde50c69b8a43feff6eedf1033ec2a3fe58009506572bd717fc78e42 \
--hash=sha256:e3066e94c24c898c5794e08507f955e14bc904299bac80f357d0bcb7d93e3825 \ --hash=sha256:e3066e94c24c898c5794e08507f955e14bc904299bac80f357d0bcb7d93e3825 \
--hash=sha256:f48029e0c79b4cc654076806cbf62094a51dcd26c6bd1ce17db3bad03aff02b1 --hash=sha256:f48029e0c79b4cc654076806cbf62094a51dcd26c6bd1ce17db3bad03aff02b1
# via vocalinux # via voculinux
six==1.17.0 \ six==1.17.0 \
--hash=sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274 \ --hash=sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274 \
--hash=sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81 --hash=sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81
+9 -9
View File
@@ -154,7 +154,7 @@ evdev==1.9.3 ; sys_platform == 'linux' or 'linux' in sys_platform \
--hash=sha256:2c140e01ac8437758fa23fe5c871397412461f42d421aa20241dc8fe8cfccbc9 --hash=sha256:2c140e01ac8437758fa23fe5c871397412461f42d421aa20241dc8fe8cfccbc9
# via # via
# pynput # pynput
# vocalinux # voculinux
idna==3.19 \ idna==3.19 \
--hash=sha256:5e0811a4383b21dc5838069f801c4fb62113b7447663d2530d2bd6e77b49bf15 \ --hash=sha256:5e0811a4383b21dc5838069f801c4fb62113b7447663d2530d2bd6e77b49bf15 \
--hash=sha256:815e7be7a7806d54abb586dc943addc79e8b2ee16915059658cbeff4b1b43bf4 --hash=sha256:815e7be7a7806d54abb586dc943addc79e8b2ee16915059658cbeff4b1b43bf4
@@ -234,7 +234,7 @@ numpy==2.4.6 ; python_full_version < '3.12' \
--hash=sha256:f74a575920ab21fe304421a3fc28793d82e299cae9eccb37084e9fc7f3617c20 --hash=sha256:f74a575920ab21fe304421a3fc28793d82e299cae9eccb37084e9fc7f3617c20
# via # via
# pywhispercpp # pywhispercpp
# vocalinux # voculinux
numpy==2.5.2 ; python_full_version >= '3.12' \ numpy==2.5.2 ; python_full_version >= '3.12' \
--hash=sha256:0090ccdd57ec2703e9b49d0bf554767370581c1dd0a6b2bb2b2d9def317d042a \ --hash=sha256:0090ccdd57ec2703e9b49d0bf554767370581c1dd0a6b2bb2b2d9def317d042a \
--hash=sha256:078f9b027b478c9379b9677babbf0f8b8f1ecfada27636d7b9a93990c638739f \ --hash=sha256:078f9b027b478c9379b9677babbf0f8b8f1ecfada27636d7b9a93990c638739f \
@@ -304,7 +304,7 @@ numpy==2.5.2 ; python_full_version >= '3.12' \
--hash=sha256:fd0d703772bba096843785bd38371e31bb4a0c1151497ad5739d182114a73f7f --hash=sha256:fd0d703772bba096843785bd38371e31bb4a0c1151497ad5739d182114a73f7f
# via # via
# pywhispercpp # pywhispercpp
# vocalinux # voculinux
platformdirs==4.11.3 \ platformdirs==4.11.3 \
--hash=sha256:5ed065d443751de711da036041a7a214122efc4a4de393b3f4137ba5576540e7 \ --hash=sha256:5ed065d443751de711da036041a7a214122efc4a4de393b3f4137ba5576540e7 \
--hash=sha256:66a73d38a849810252df809a3d8bcbda8e26f6c189920e7535ad608a48dbb5ab --hash=sha256:66a73d38a849810252df809a3d8bcbda8e26f6c189920e7535ad608a48dbb5ab
@@ -331,7 +331,7 @@ psutil==7.2.2 \
--hash=sha256:ed0cace939114f62738d808fdcecd4c869222507e266e574799e9c0faa17d486 \ --hash=sha256:ed0cace939114f62738d808fdcecd4c869222507e266e574799e9c0faa17d486 \
--hash=sha256:eed63d3b4d62449571547b60578c5b2c4bcccc5387148db46e0c2313dad0ee00 \ --hash=sha256:eed63d3b4d62449571547b60578c5b2c4bcccc5387148db46e0c2313dad0ee00 \
--hash=sha256:fd04ef36b4a6d599bbdb225dd1d3f51e00105f6d48a28f006da7f9822f2606d8 --hash=sha256:fd04ef36b4a6d599bbdb225dd1d3f51e00105f6d48a28f006da7f9822f2606d8
# via vocalinux # via voculinux
pyaudio==0.2.14 \ pyaudio==0.2.14 \
--hash=sha256:12f2f1ba04e06ff95d80700a78967897a489c05e093e3bffa05a84ed9c0a7fa3 \ --hash=sha256:12f2f1ba04e06ff95d80700a78967897a489c05e093e3bffa05a84ed9c0a7fa3 \
--hash=sha256:506b32a595f8693811682ab4b127602d404df7dfc453b499c91a80d0f7bad289 \ --hash=sha256:506b32a595f8693811682ab4b127602d404df7dfc453b499c91a80d0f7bad289 \
@@ -340,14 +340,14 @@ pyaudio==0.2.14 \
--hash=sha256:78dfff3879b4994d1f4fc6485646a57755c6ee3c19647a491f790a0895bd2f87 \ --hash=sha256:78dfff3879b4994d1f4fc6485646a57755c6ee3c19647a491f790a0895bd2f87 \
--hash=sha256:95328285b4dab57ea8c52a4a996cb52be6d629353315be5bfda403d15932a497 \ --hash=sha256:95328285b4dab57ea8c52a4a996cb52be6d629353315be5bfda403d15932a497 \
--hash=sha256:bbeb01d36a2f472ae5ee5e1451cacc42112986abe622f735bb870a5db77cf903 --hash=sha256:bbeb01d36a2f472ae5ee5e1451cacc42112986abe622f735bb870a5db77cf903
# via vocalinux # via voculinux
pycairo==1.29.1 ; sys_platform == 'linux' \ pycairo==1.29.1 ; sys_platform == 'linux' \
--hash=sha256:4fbd26b4af24c9787d84cf5448e34eb8dca064b732479aaecd03109520eebd5f --hash=sha256:4fbd26b4af24c9787d84cf5448e34eb8dca064b732479aaecd03109520eebd5f
# via pygobject # via pygobject
pynput==1.8.2 \ pynput==1.8.2 \
--hash=sha256:8cc38cf13a6ab2749cb375678be8a0fd705d7ce49c8001ff5db4007a723bbef1 \ --hash=sha256:8cc38cf13a6ab2749cb375678be8a0fd705d7ce49c8001ff5db4007a723bbef1 \
--hash=sha256:f493c87157cd3861b4468f7f896857051762f44ed26f1b641e7cc5840a457087 --hash=sha256:f493c87157cd3861b4468f7f896857051762f44ed26f1b641e7cc5840a457087
# via vocalinux # via voculinux
pyobjc-core==12.2.2 ; sys_platform == 'darwin' \ pyobjc-core==12.2.2 ; sys_platform == 'darwin' \
--hash=sha256:122e6ad302a2abf5d4d4adb0156db751600ddf2768441696cba17b31323085e7 \ --hash=sha256:122e6ad302a2abf5d4d4adb0156db751600ddf2768441696cba17b31323085e7 \
--hash=sha256:2062e8ad30a310441cd022544a897553408bebeaa7820d5edba3c96fd7fd693b \ --hash=sha256:2062e8ad30a310441cd022544a897553408bebeaa7820d5edba3c96fd7fd693b \
@@ -416,7 +416,7 @@ pyobjc-framework-quartz==12.2.2 ; sys_platform == 'darwin' \
pysocks==1.7.1 \ pysocks==1.7.1 \
--hash=sha256:2725bd0a9925919b9b51739eea5f9e2bae91e83288108a9ad338b2e3a4435ee5 \ --hash=sha256:2725bd0a9925919b9b51739eea5f9e2bae91e83288108a9ad338b2e3a4435ee5 \
--hash=sha256:3f8804571ebe159c380ac6de37643bb4685970655d3bba243530d6558b799aa0 --hash=sha256:3f8804571ebe159c380ac6de37643bb4685970655d3bba243530d6558b799aa0
# via vocalinux # via voculinux
python-xlib==0.33 ; 'linux' in sys_platform \ python-xlib==0.33 ; 'linux' in sys_platform \
--hash=sha256:55af7906a2c75ce6cb280a584776080602444f75815a7aff4d287bb2d7018b32 \ --hash=sha256:55af7906a2c75ce6cb280a584776080602444f75815a7aff4d287bb2d7018b32 \
--hash=sha256:c3534038d42e0df2f1392a1b30a15a4ff5fdc2b86cfa94f072bf11b10a164398 --hash=sha256:c3534038d42e0df2f1392a1b30a15a4ff5fdc2b86cfa94f072bf11b10a164398
@@ -458,13 +458,13 @@ pywhispercpp==1.5.0 \
--hash=sha256:f5d13b4a7e34b9f4eebf70bff150ec36f4145c580bd5d812b9a3139611e7433c \ --hash=sha256:f5d13b4a7e34b9f4eebf70bff150ec36f4145c580bd5d812b9a3139611e7433c \
--hash=sha256:f9b1cd9dd4c3eb7fb2b777529eb62618e58a6bb05be20f82b7b1afe3d0b12274 \ --hash=sha256:f9b1cd9dd4c3eb7fb2b777529eb62618e58a6bb05be20f82b7b1afe3d0b12274 \
--hash=sha256:fbdbb1bb236f4481ff6ea475aed9ed091393ddb02271df309645d71de207778d --hash=sha256:fbdbb1bb236f4481ff6ea475aed9ed091393ddb02271df309645d71de207778d
# via vocalinux # via voculinux
requests==2.34.2 \ requests==2.34.2 \
--hash=sha256:2a0d60c172f83ac6ab31e4554906c0f3b3588d37b5cb939b1c061f4907e278e0 \ --hash=sha256:2a0d60c172f83ac6ab31e4554906c0f3b3588d37b5cb939b1c061f4907e278e0 \
--hash=sha256:f288924cae4e29463698d6d60bc6a4da69c89185ad1e0bcc4104f584e960b9ed --hash=sha256:f288924cae4e29463698d6d60bc6a4da69c89185ad1e0bcc4104f584e960b9ed
# via # via
# pywhispercpp # pywhispercpp
# vocalinux # voculinux
six==1.17.0 \ six==1.17.0 \
--hash=sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274 \ --hash=sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274 \
--hash=sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81 --hash=sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81
+10 -10
View File
@@ -154,7 +154,7 @@ evdev==1.9.3 ; sys_platform == 'linux' or 'linux' in sys_platform \
--hash=sha256:2c140e01ac8437758fa23fe5c871397412461f42d421aa20241dc8fe8cfccbc9 --hash=sha256:2c140e01ac8437758fa23fe5c871397412461f42d421aa20241dc8fe8cfccbc9
# via # via
# pynput # pynput
# vocalinux # voculinux
flatbuffers==25.12.19 \ flatbuffers==25.12.19 \
--hash=sha256:7634f50c427838bb021c2d66a3d1168e9d199b0607e6329399f04846d42e20b4 --hash=sha256:7634f50c427838bb021c2d66a3d1168e9d199b0607e6329399f04846d42e20b4
# via onnxruntime # via onnxruntime
@@ -238,7 +238,7 @@ numpy==2.4.6 ; python_full_version < '3.12' \
# via # via
# onnxruntime # onnxruntime
# pywhispercpp # pywhispercpp
# vocalinux # voculinux
numpy==2.5.2 ; python_full_version >= '3.12' \ numpy==2.5.2 ; python_full_version >= '3.12' \
--hash=sha256:0090ccdd57ec2703e9b49d0bf554767370581c1dd0a6b2bb2b2d9def317d042a \ --hash=sha256:0090ccdd57ec2703e9b49d0bf554767370581c1dd0a6b2bb2b2d9def317d042a \
--hash=sha256:078f9b027b478c9379b9677babbf0f8b8f1ecfada27636d7b9a93990c638739f \ --hash=sha256:078f9b027b478c9379b9677babbf0f8b8f1ecfada27636d7b9a93990c638739f \
@@ -309,7 +309,7 @@ numpy==2.5.2 ; python_full_version >= '3.12' \
# via # via
# onnxruntime # onnxruntime
# pywhispercpp # pywhispercpp
# vocalinux # voculinux
onnxruntime==1.29.0 \ onnxruntime==1.29.0 \
--hash=sha256:07c5907474dec4a2792fd7626b753dc66707808385a6d9eecf993db0066a9d0f \ --hash=sha256:07c5907474dec4a2792fd7626b753dc66707808385a6d9eecf993db0066a9d0f \
--hash=sha256:0d4f427afac434b0070fe992b540ddf20a7aff2265f760f314d91331935b6b98 \ --hash=sha256:0d4f427afac434b0070fe992b540ddf20a7aff2265f760f314d91331935b6b98 \
@@ -335,7 +335,7 @@ onnxruntime==1.29.0 \
--hash=sha256:e2128f31f449e922c62dbe5d8b6b7b079f0bcaf2d56a102fa203cb6e5bb5ab19 \ --hash=sha256:e2128f31f449e922c62dbe5d8b6b7b079f0bcaf2d56a102fa203cb6e5bb5ab19 \
--hash=sha256:e417ef8628dcce310d2d53023e750ea298ec14d4341ae6dc3a572bfd9bc7fa97 \ --hash=sha256:e417ef8628dcce310d2d53023e750ea298ec14d4341ae6dc3a572bfd9bc7fa97 \
--hash=sha256:e74b278af1d949876f5d91d1268fd6c680e79f2bac194967394eaba9fdf69e7e --hash=sha256:e74b278af1d949876f5d91d1268fd6c680e79f2bac194967394eaba9fdf69e7e
# via vocalinux # via voculinux
packaging==26.3 \ packaging==26.3 \
--hash=sha256:94edc256424af38762eb31306eed28beb9f0efc50a8837492c9d6fd6004aed79 \ --hash=sha256:94edc256424af38762eb31306eed28beb9f0efc50a8837492c9d6fd6004aed79 \
--hash=sha256:d7193f7c8e4e93f444fde0262bf90af30e16fa0ad0ad44cb553c87339b23cd1c --hash=sha256:d7193f7c8e4e93f444fde0262bf90af30e16fa0ad0ad44cb553c87339b23cd1c
@@ -376,7 +376,7 @@ psutil==7.2.2 \
--hash=sha256:ed0cace939114f62738d808fdcecd4c869222507e266e574799e9c0faa17d486 \ --hash=sha256:ed0cace939114f62738d808fdcecd4c869222507e266e574799e9c0faa17d486 \
--hash=sha256:eed63d3b4d62449571547b60578c5b2c4bcccc5387148db46e0c2313dad0ee00 \ --hash=sha256:eed63d3b4d62449571547b60578c5b2c4bcccc5387148db46e0c2313dad0ee00 \
--hash=sha256:fd04ef36b4a6d599bbdb225dd1d3f51e00105f6d48a28f006da7f9822f2606d8 --hash=sha256:fd04ef36b4a6d599bbdb225dd1d3f51e00105f6d48a28f006da7f9822f2606d8
# via vocalinux # via voculinux
pyaudio==0.2.14 \ pyaudio==0.2.14 \
--hash=sha256:12f2f1ba04e06ff95d80700a78967897a489c05e093e3bffa05a84ed9c0a7fa3 \ --hash=sha256:12f2f1ba04e06ff95d80700a78967897a489c05e093e3bffa05a84ed9c0a7fa3 \
--hash=sha256:506b32a595f8693811682ab4b127602d404df7dfc453b499c91a80d0f7bad289 \ --hash=sha256:506b32a595f8693811682ab4b127602d404df7dfc453b499c91a80d0f7bad289 \
@@ -385,14 +385,14 @@ pyaudio==0.2.14 \
--hash=sha256:78dfff3879b4994d1f4fc6485646a57755c6ee3c19647a491f790a0895bd2f87 \ --hash=sha256:78dfff3879b4994d1f4fc6485646a57755c6ee3c19647a491f790a0895bd2f87 \
--hash=sha256:95328285b4dab57ea8c52a4a996cb52be6d629353315be5bfda403d15932a497 \ --hash=sha256:95328285b4dab57ea8c52a4a996cb52be6d629353315be5bfda403d15932a497 \
--hash=sha256:bbeb01d36a2f472ae5ee5e1451cacc42112986abe622f735bb870a5db77cf903 --hash=sha256:bbeb01d36a2f472ae5ee5e1451cacc42112986abe622f735bb870a5db77cf903
# via vocalinux # via voculinux
pycairo==1.29.1 ; sys_platform == 'linux' \ pycairo==1.29.1 ; sys_platform == 'linux' \
--hash=sha256:4fbd26b4af24c9787d84cf5448e34eb8dca064b732479aaecd03109520eebd5f --hash=sha256:4fbd26b4af24c9787d84cf5448e34eb8dca064b732479aaecd03109520eebd5f
# via pygobject # via pygobject
pynput==1.8.2 \ pynput==1.8.2 \
--hash=sha256:8cc38cf13a6ab2749cb375678be8a0fd705d7ce49c8001ff5db4007a723bbef1 \ --hash=sha256:8cc38cf13a6ab2749cb375678be8a0fd705d7ce49c8001ff5db4007a723bbef1 \
--hash=sha256:f493c87157cd3861b4468f7f896857051762f44ed26f1b641e7cc5840a457087 --hash=sha256:f493c87157cd3861b4468f7f896857051762f44ed26f1b641e7cc5840a457087
# via vocalinux # via voculinux
pyobjc-core==12.2.2 ; sys_platform == 'darwin' \ pyobjc-core==12.2.2 ; sys_platform == 'darwin' \
--hash=sha256:122e6ad302a2abf5d4d4adb0156db751600ddf2768441696cba17b31323085e7 \ --hash=sha256:122e6ad302a2abf5d4d4adb0156db751600ddf2768441696cba17b31323085e7 \
--hash=sha256:2062e8ad30a310441cd022544a897553408bebeaa7820d5edba3c96fd7fd693b \ --hash=sha256:2062e8ad30a310441cd022544a897553408bebeaa7820d5edba3c96fd7fd693b \
@@ -461,7 +461,7 @@ pyobjc-framework-quartz==12.2.2 ; sys_platform == 'darwin' \
pysocks==1.7.1 \ pysocks==1.7.1 \
--hash=sha256:2725bd0a9925919b9b51739eea5f9e2bae91e83288108a9ad338b2e3a4435ee5 \ --hash=sha256:2725bd0a9925919b9b51739eea5f9e2bae91e83288108a9ad338b2e3a4435ee5 \
--hash=sha256:3f8804571ebe159c380ac6de37643bb4685970655d3bba243530d6558b799aa0 --hash=sha256:3f8804571ebe159c380ac6de37643bb4685970655d3bba243530d6558b799aa0
# via vocalinux # via voculinux
python-xlib==0.33 ; 'linux' in sys_platform \ python-xlib==0.33 ; 'linux' in sys_platform \
--hash=sha256:55af7906a2c75ce6cb280a584776080602444f75815a7aff4d287bb2d7018b32 \ --hash=sha256:55af7906a2c75ce6cb280a584776080602444f75815a7aff4d287bb2d7018b32 \
--hash=sha256:c3534038d42e0df2f1392a1b30a15a4ff5fdc2b86cfa94f072bf11b10a164398 --hash=sha256:c3534038d42e0df2f1392a1b30a15a4ff5fdc2b86cfa94f072bf11b10a164398
@@ -503,13 +503,13 @@ pywhispercpp==1.5.0 \
--hash=sha256:f5d13b4a7e34b9f4eebf70bff150ec36f4145c580bd5d812b9a3139611e7433c \ --hash=sha256:f5d13b4a7e34b9f4eebf70bff150ec36f4145c580bd5d812b9a3139611e7433c \
--hash=sha256:f9b1cd9dd4c3eb7fb2b777529eb62618e58a6bb05be20f82b7b1afe3d0b12274 \ --hash=sha256:f9b1cd9dd4c3eb7fb2b777529eb62618e58a6bb05be20f82b7b1afe3d0b12274 \
--hash=sha256:fbdbb1bb236f4481ff6ea475aed9ed091393ddb02271df309645d71de207778d --hash=sha256:fbdbb1bb236f4481ff6ea475aed9ed091393ddb02271df309645d71de207778d
# via vocalinux # via voculinux
requests==2.34.2 \ requests==2.34.2 \
--hash=sha256:2a0d60c172f83ac6ab31e4554906c0f3b3588d37b5cb939b1c061f4907e278e0 \ --hash=sha256:2a0d60c172f83ac6ab31e4554906c0f3b3588d37b5cb939b1c061f4907e278e0 \
--hash=sha256:f288924cae4e29463698d6d60bc6a4da69c89185ad1e0bcc4104f584e960b9ed --hash=sha256:f288924cae4e29463698d6d60bc6a4da69c89185ad1e0bcc4104f584e960b9ed
# via # via
# pywhispercpp # pywhispercpp
# vocalinux # voculinux
six==1.17.0 \ six==1.17.0 \
--hash=sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274 \ --hash=sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274 \
--hash=sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81 --hash=sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81
+10 -10
View File
@@ -244,7 +244,7 @@ evdev==1.9.3 ; sys_platform == 'linux' or 'linux' in sys_platform \
--hash=sha256:2c140e01ac8437758fa23fe5c871397412461f42d421aa20241dc8fe8cfccbc9 --hash=sha256:2c140e01ac8437758fa23fe5c871397412461f42d421aa20241dc8fe8cfccbc9
# via # via
# pynput # pynput
# vocalinux # voculinux
idna==3.19 \ idna==3.19 \
--hash=sha256:5e0811a4383b21dc5838069f801c4fb62113b7447663d2530d2bd6e77b49bf15 \ --hash=sha256:5e0811a4383b21dc5838069f801c4fb62113b7447663d2530d2bd6e77b49bf15 \
--hash=sha256:815e7be7a7806d54abb586dc943addc79e8b2ee16915059658cbeff4b1b43bf4 --hash=sha256:815e7be7a7806d54abb586dc943addc79e8b2ee16915059658cbeff4b1b43bf4
@@ -324,7 +324,7 @@ numpy==2.4.6 ; python_full_version < '3.12' \
--hash=sha256:f74a575920ab21fe304421a3fc28793d82e299cae9eccb37084e9fc7f3617c20 --hash=sha256:f74a575920ab21fe304421a3fc28793d82e299cae9eccb37084e9fc7f3617c20
# via # via
# pywhispercpp # pywhispercpp
# vocalinux # voculinux
numpy==2.5.2 ; python_full_version >= '3.12' \ numpy==2.5.2 ; python_full_version >= '3.12' \
--hash=sha256:0090ccdd57ec2703e9b49d0bf554767370581c1dd0a6b2bb2b2d9def317d042a \ --hash=sha256:0090ccdd57ec2703e9b49d0bf554767370581c1dd0a6b2bb2b2d9def317d042a \
--hash=sha256:078f9b027b478c9379b9677babbf0f8b8f1ecfada27636d7b9a93990c638739f \ --hash=sha256:078f9b027b478c9379b9677babbf0f8b8f1ecfada27636d7b9a93990c638739f \
@@ -394,7 +394,7 @@ numpy==2.5.2 ; python_full_version >= '3.12' \
--hash=sha256:fd0d703772bba096843785bd38371e31bb4a0c1151497ad5739d182114a73f7f --hash=sha256:fd0d703772bba096843785bd38371e31bb4a0c1151497ad5739d182114a73f7f
# via # via
# pywhispercpp # pywhispercpp
# vocalinux # voculinux
platformdirs==4.11.3 \ platformdirs==4.11.3 \
--hash=sha256:5ed065d443751de711da036041a7a214122efc4a4de393b3f4137ba5576540e7 \ --hash=sha256:5ed065d443751de711da036041a7a214122efc4a4de393b3f4137ba5576540e7 \
--hash=sha256:66a73d38a849810252df809a3d8bcbda8e26f6c189920e7535ad608a48dbb5ab --hash=sha256:66a73d38a849810252df809a3d8bcbda8e26f6c189920e7535ad608a48dbb5ab
@@ -421,7 +421,7 @@ psutil==7.2.2 \
--hash=sha256:ed0cace939114f62738d808fdcecd4c869222507e266e574799e9c0faa17d486 \ --hash=sha256:ed0cace939114f62738d808fdcecd4c869222507e266e574799e9c0faa17d486 \
--hash=sha256:eed63d3b4d62449571547b60578c5b2c4bcccc5387148db46e0c2313dad0ee00 \ --hash=sha256:eed63d3b4d62449571547b60578c5b2c4bcccc5387148db46e0c2313dad0ee00 \
--hash=sha256:fd04ef36b4a6d599bbdb225dd1d3f51e00105f6d48a28f006da7f9822f2606d8 --hash=sha256:fd04ef36b4a6d599bbdb225dd1d3f51e00105f6d48a28f006da7f9822f2606d8
# via vocalinux # via voculinux
pyaudio==0.2.14 \ pyaudio==0.2.14 \
--hash=sha256:12f2f1ba04e06ff95d80700a78967897a489c05e093e3bffa05a84ed9c0a7fa3 \ --hash=sha256:12f2f1ba04e06ff95d80700a78967897a489c05e093e3bffa05a84ed9c0a7fa3 \
--hash=sha256:506b32a595f8693811682ab4b127602d404df7dfc453b499c91a80d0f7bad289 \ --hash=sha256:506b32a595f8693811682ab4b127602d404df7dfc453b499c91a80d0f7bad289 \
@@ -430,7 +430,7 @@ pyaudio==0.2.14 \
--hash=sha256:78dfff3879b4994d1f4fc6485646a57755c6ee3c19647a491f790a0895bd2f87 \ --hash=sha256:78dfff3879b4994d1f4fc6485646a57755c6ee3c19647a491f790a0895bd2f87 \
--hash=sha256:95328285b4dab57ea8c52a4a996cb52be6d629353315be5bfda403d15932a497 \ --hash=sha256:95328285b4dab57ea8c52a4a996cb52be6d629353315be5bfda403d15932a497 \
--hash=sha256:bbeb01d36a2f472ae5ee5e1451cacc42112986abe622f735bb870a5db77cf903 --hash=sha256:bbeb01d36a2f472ae5ee5e1451cacc42112986abe622f735bb870a5db77cf903
# via vocalinux # via voculinux
pycairo==1.29.1 ; sys_platform == 'linux' \ pycairo==1.29.1 ; sys_platform == 'linux' \
--hash=sha256:4fbd26b4af24c9787d84cf5448e34eb8dca064b732479aaecd03109520eebd5f --hash=sha256:4fbd26b4af24c9787d84cf5448e34eb8dca064b732479aaecd03109520eebd5f
# via pygobject # via pygobject
@@ -441,7 +441,7 @@ pycparser==3.0 ; implementation_name != 'PyPy' \
pynput==1.8.2 \ pynput==1.8.2 \
--hash=sha256:8cc38cf13a6ab2749cb375678be8a0fd705d7ce49c8001ff5db4007a723bbef1 \ --hash=sha256:8cc38cf13a6ab2749cb375678be8a0fd705d7ce49c8001ff5db4007a723bbef1 \
--hash=sha256:f493c87157cd3861b4468f7f896857051762f44ed26f1b641e7cc5840a457087 --hash=sha256:f493c87157cd3861b4468f7f896857051762f44ed26f1b641e7cc5840a457087
# via vocalinux # via voculinux
pyobjc-core==12.2.2 ; sys_platform == 'darwin' \ pyobjc-core==12.2.2 ; sys_platform == 'darwin' \
--hash=sha256:122e6ad302a2abf5d4d4adb0156db751600ddf2768441696cba17b31323085e7 \ --hash=sha256:122e6ad302a2abf5d4d4adb0156db751600ddf2768441696cba17b31323085e7 \
--hash=sha256:2062e8ad30a310441cd022544a897553408bebeaa7820d5edba3c96fd7fd693b \ --hash=sha256:2062e8ad30a310441cd022544a897553408bebeaa7820d5edba3c96fd7fd693b \
@@ -510,7 +510,7 @@ pyobjc-framework-quartz==12.2.2 ; sys_platform == 'darwin' \
pysocks==1.7.1 \ pysocks==1.7.1 \
--hash=sha256:2725bd0a9925919b9b51739eea5f9e2bae91e83288108a9ad338b2e3a4435ee5 \ --hash=sha256:2725bd0a9925919b9b51739eea5f9e2bae91e83288108a9ad338b2e3a4435ee5 \
--hash=sha256:3f8804571ebe159c380ac6de37643bb4685970655d3bba243530d6558b799aa0 --hash=sha256:3f8804571ebe159c380ac6de37643bb4685970655d3bba243530d6558b799aa0
# via vocalinux # via voculinux
python-xlib==0.33 ; 'linux' in sys_platform \ python-xlib==0.33 ; 'linux' in sys_platform \
--hash=sha256:55af7906a2c75ce6cb280a584776080602444f75815a7aff4d287bb2d7018b32 \ --hash=sha256:55af7906a2c75ce6cb280a584776080602444f75815a7aff4d287bb2d7018b32 \
--hash=sha256:c3534038d42e0df2f1392a1b30a15a4ff5fdc2b86cfa94f072bf11b10a164398 --hash=sha256:c3534038d42e0df2f1392a1b30a15a4ff5fdc2b86cfa94f072bf11b10a164398
@@ -552,13 +552,13 @@ pywhispercpp==1.5.0 \
--hash=sha256:f5d13b4a7e34b9f4eebf70bff150ec36f4145c580bd5d812b9a3139611e7433c \ --hash=sha256:f5d13b4a7e34b9f4eebf70bff150ec36f4145c580bd5d812b9a3139611e7433c \
--hash=sha256:f9b1cd9dd4c3eb7fb2b777529eb62618e58a6bb05be20f82b7b1afe3d0b12274 \ --hash=sha256:f9b1cd9dd4c3eb7fb2b777529eb62618e58a6bb05be20f82b7b1afe3d0b12274 \
--hash=sha256:fbdbb1bb236f4481ff6ea475aed9ed091393ddb02271df309645d71de207778d --hash=sha256:fbdbb1bb236f4481ff6ea475aed9ed091393ddb02271df309645d71de207778d
# via vocalinux # via voculinux
requests==2.34.2 \ requests==2.34.2 \
--hash=sha256:2a0d60c172f83ac6ab31e4554906c0f3b3588d37b5cb939b1c061f4907e278e0 \ --hash=sha256:2a0d60c172f83ac6ab31e4554906c0f3b3588d37b5cb939b1c061f4907e278e0 \
--hash=sha256:f288924cae4e29463698d6d60bc6a4da69c89185ad1e0bcc4104f584e960b9ed --hash=sha256:f288924cae4e29463698d6d60bc6a4da69c89185ad1e0bcc4104f584e960b9ed
# via # via
# pywhispercpp # pywhispercpp
# vocalinux # voculinux
# vosk # vosk
six==1.17.0 \ six==1.17.0 \
--hash=sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274 \ --hash=sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274 \
@@ -584,7 +584,7 @@ vosk==0.3.45 \
--hash=sha256:4221f83287eefe5abbe54fc6f1da5774e9e3ffcbbdca1705a466b341093b072e \ --hash=sha256:4221f83287eefe5abbe54fc6f1da5774e9e3ffcbbdca1705a466b341093b072e \
--hash=sha256:54efb47dd890e544e9e20f0316413acec7f8680d04ec095c6140ab4e70262704 \ --hash=sha256:54efb47dd890e544e9e20f0316413acec7f8680d04ec095c6140ab4e70262704 \
--hash=sha256:6994ddc68556c7e5730c3b6f6bad13320e3519b13ce3ed2aa25a86724e7c10ac --hash=sha256:6994ddc68556c7e5730c3b6f6bad13320e3519b13ce3ed2aa25a86724e7c10ac
# via vocalinux # via voculinux
websockets==17.0.1 \ websockets==17.0.1 \
--hash=sha256:02ed63bf26dda9fa27df730a41f6664586c4ee05972c8fb667ce1725b3fd13d3 \ --hash=sha256:02ed63bf26dda9fa27df730a41f6664586c4ee05972c8fb667ce1725b3fd13d3 \
--hash=sha256:02f0b037a737d0cb0c33866c97bcd1a0b73170dfbf42d69d8fb86f51002fd5ae \ --hash=sha256:02f0b037a737d0cb0c33866c97bcd1a0b73170dfbf42d69d8fb86f51002fd5ae \
@@ -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) --> <!-- Opaque disc for StatusNotifier trays (~22px) -->
<circle cx="512" cy="512" r="480" fill="#0B1A15"/> <circle cx="512" cy="512" r="480" fill="#0B1A15"/>
<g transform="translate(28.386 32.619) scale(2.116474)"> <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) --> <!-- Opaque disc for StatusNotifier trays (~22px) -->
<circle cx="512" cy="512" r="480" fill="#A86108"/> <circle cx="512" cy="512" r="480" fill="#A86108"/>
<g transform="translate(28.386 32.619) scale(2.116474)"> <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) --> <!-- Opaque disc for StatusNotifier trays (~22px) -->
<circle cx="512" cy="512" r="480" fill="#C73D38"/> <circle cx="512" cy="512" r="480" fill="#C73D38"/>
<g transform="translate(28.386 32.619) scale(2.116474)"> <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"/> <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 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"/> <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

Before

Width:  |  Height:  |  Size: 6.5 MiB

After

Width:  |  Height:  |  Size: 6.5 MiB

+1 -1
View File
@@ -232,7 +232,7 @@ def _build(python: str, output: Path, diagnostics: Path) -> None:
env, env,
diagnostics / "inputs.log", diagnostics / "inputs.log",
) )
with tempfile.TemporaryDirectory(prefix="vocalinux-wheel-") as temporary: with tempfile.TemporaryDirectory(prefix="voculinux-wheel-") as temporary:
scratch = Path(temporary) scratch = Path(temporary)
source = scratch / "snapshot" source = scratch / "snapshot"
source.mkdir() source.mkdir()
+1 -1
View File
@@ -6,7 +6,7 @@ check_deps() {
MISSING=0 MISSING=0
WARNINGS=0 WARNINGS=0
echo "Checking Vocalinux system dependencies..." echo "Checking Voculinux system dependencies..."
echo "" echo ""
# Check for Python 3.11+ (requires-python in pyproject.toml) # Check for Python 3.11+ (requires-python in pyproject.toml)
+5 -5
View File
@@ -1,7 +1,7 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
"""Regenerate src/vocalinux/utils/model_checksums.txt from upstream metadata. """Regenerate src/vocalinux/utils/model_checksums.txt from upstream metadata.
Vocalinux downloads 40MB-2GB speech models at install time and on first run, Voculinux downloads 40MB-2GB speech models at install time and on first run,
then hands them to native code (whisper.cpp loads its ggml files through then hands them to native code (whisper.cpp loads its ggml files through
``ctypes.cdll``). Nothing about that is safe unless we know what we received, ``ctypes.cdll``). Nothing about that is safe unless we know what we received,
so every downloader checks the file against a hash pinned *in this repository* so every downloader checks the file against a hash pinned *in this repository*
@@ -46,7 +46,7 @@ from typing import Iterable, NamedTuple
REPO_ROOT = Path(__file__).resolve().parents[1] REPO_ROOT = Path(__file__).resolve().parents[1]
SRC = REPO_ROOT / "src" SRC = REPO_ROOT / "src"
MANIFEST = SRC / "vocalinux" / "utils" / "model_checksums.txt" MANIFEST = SRC / "voculinux" / "utils" / "model_checksums.txt"
HF_REPO = "ggerganov/whisper.cpp" HF_REPO = "ggerganov/whisper.cpp"
HF_API = f"https://huggingface.co/api/models/{HF_REPO}?blobs=true" HF_API = f"https://huggingface.co/api/models/{HF_REPO}?blobs=true"
@@ -65,7 +65,7 @@ class Entry(NamedTuple):
def _fetch_json(url: str): def _fetch_json(url: str):
request = urllib.request.Request(url, headers={"User-Agent": "vocalinux-checksum-generator"}) request = urllib.request.Request(url, headers={"User-Agent": "voculinux-checksum-generator"})
with urllib.request.urlopen(request, timeout=TIMEOUT) as response: # noqa: S310 with urllib.request.urlopen(request, timeout=TIMEOUT) as response: # noqa: S310
return json.load(response) return json.load(response)
@@ -93,7 +93,7 @@ def _download_and_hash(url: str, expected_md5: str) -> tuple[str, int]:
for rather than pinning either one. for rather than pinning either one.
""" """
sha256, md5, size = hashlib.sha256(), hashlib.md5(), 0 sha256, md5, size = hashlib.sha256(), hashlib.md5(), 0
request = urllib.request.Request(url, headers={"User-Agent": "vocalinux-checksum-generator"}) request = urllib.request.Request(url, headers={"User-Agent": "voculinux-checksum-generator"})
with urllib.request.urlopen(request, timeout=TIMEOUT) as response: # noqa: S310 with urllib.request.urlopen(request, timeout=TIMEOUT) as response: # noqa: S310
for chunk in iter(lambda: response.read(1024 * 1024), b""): for chunk in iter(lambda: response.read(1024 * 1024), b""):
sha256.update(chunk) sha256.update(chunk)
@@ -306,7 +306,7 @@ def _vosk_entries(wanted: Iterable[str], refresh: bool) -> list[Entry]:
def _render(entries: list[Entry], revision: str) -> str: def _render(entries: list[Entry], revision: str) -> str:
width = max(len(entry.filename) for entry in entries) width = max(len(entry.filename) for entry in entries)
lines = [ lines = [
"# 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.", "# Generated by scripts/generate-model-checksums.py -- do not edit by hand.",
"# Regenerate with `just model-checksums` after adding a model to", "# Regenerate with `just model-checksums` after adding a model to",
+1 -1
View File
@@ -1,5 +1,5 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
"""Generate Vocalinux notification WAVs. """Generate Voculinux notification WAVs.
Writes 16-bit PCM mono files at 44.1 kHz into resources/sounds/: Writes 16-bit PCM mono files at 44.1 kHz into resources/sounds/:
+20 -20
View File
@@ -14,7 +14,7 @@
# fetch+reset over the existing clone. # fetch+reset over the existing clone.
# #
# The remote is a local mirror so the gate answers for *this commit*: the tag # The remote is a local mirror so the gate answers for *this commit*: the tag
# is published from HEAD's tree and VOCALINUX_REPO_URL points the bootstrap at # is published from HEAD's tree and VOCULINUX_REPO_URL points the bootstrap at
# it. A gate that cloned the public repo would install the last release, not # it. A gate that cloned the public repo would install the last release, not
# the checkout under test. The tag's own install.sh is the bootstrap: the #701 # the checkout under test. The tag's own install.sh is the bootstrap: the #701
# guardrail allows the bootstrap to come from main, and this commit's copy is # guardrail allows the bootstrap to come from main, and this commit's copy is
@@ -37,10 +37,10 @@ INSTALL_HOME="/home/$INSTALL_USER"
# Opaque on purpose: a name the public repository cannot already carry, so a # Opaque on purpose: a name the public repository cannot already carry, so a
# silent fallback to GitHub fails loudly instead of installing the wrong ref. # silent fallback to GitHub fails loudly instead of installing the wrong ref.
GATE_TAG="${GATE_TAG:-v0.0.0-remote-gate}" GATE_TAG="${GATE_TAG:-v0.0.0-remote-gate}"
MIRROR="/srv/vocalinux-remote.git" MIRROR="/srv/voculinux-remote.git"
BOOTSTRAP="/tmp/vl.sh" BOOTSTRAP="/tmp/vl.sh"
CLONE_DIR="$INSTALL_HOME/.local/share/vocalinux-install" CLONE_DIR="$INSTALL_HOME/.local/share/voculinux-install"
REMOTE_VENV="$INSTALL_HOME/.local/share/vocalinux/venv" REMOTE_VENV="$INSTALL_HOME/.local/share/voculinux/venv"
fail() { fail() {
echo "FAIL: $*" >&2 echo "FAIL: $*" >&2
@@ -48,11 +48,11 @@ fail() {
exit 1 exit 1
} }
# install.sh writes a full transcript under ~/.local/state/vocalinux/. It is the # install.sh writes a full transcript under ~/.local/state/voculinux/. It is the
# only place the per-distro package manager output survives. # only place the per-distro package manager output survives.
dump_install_log() { dump_install_log() {
local log local log
log="$(ls -1t "$INSTALL_HOME"/.local/state/vocalinux/install-*.log 2>/dev/null | head -n1 || true)" log="$(ls -1t "$INSTALL_HOME"/.local/state/voculinux/install-*.log 2>/dev/null | head -n1 || true)"
if [ -n "$log" ]; then if [ -n "$log" ]; then
echo "== last 200 lines of $log ==" >&2 echo "== last 200 lines of $log ==" >&2
tail -n 200 "$log" >&2 tail -n 200 "$log" >&2
@@ -142,9 +142,9 @@ chmod 0644 "$BOOTSTRAP"
run_remote_install() { run_remote_install() {
# cd to $INSTALL_HOME so the bootstrap sees no checkout: local mode is chosen # cd to $INSTALL_HOME so the bootstrap sees no checkout: local mode is chosen
# by a pyproject.toml naming vocalinux in the working directory, and the # by a pyproject.toml naming voculinux in the working directory, and the
# remote path is the one under test. # remote path is the one under test.
su - "$INSTALL_USER" -c "cd '$INSTALL_HOME' && VOCALINUX_REPO_URL='file://$MIRROR' bash '$BOOTSTRAP' --auto --skip-models '--tag=$GATE_TAG'" su - "$INSTALL_USER" -c "cd '$INSTALL_HOME' && VOCULINUX_REPO_URL='file://$MIRROR' bash '$BOOTSTRAP' --auto --skip-models '--tag=$GATE_TAG'"
} }
# The user's literal `curl | bash`: installer code reaches bash on a pipe while # The user's literal `curl | bash`: installer code reaches bash on a pipe while
@@ -187,7 +187,7 @@ drip "$bootstrap" | bash -s -- "$@"
EOF EOF
chmod 0755 "$PIPED_FEED" chmod 0755 "$PIPED_FEED"
run_remote_install_piped() { run_remote_install_piped() {
su - "$INSTALL_USER" -c "cd '$INSTALL_HOME' && VOCALINUX_REPO_URL='file://$MIRROR' script -qec \"'$PIPED_FEED' '$BOOTSTRAP' --auto --skip-models '--tag=$GATE_TAG'\" /dev/null" su - "$INSTALL_USER" -c "cd '$INSTALL_HOME' && VOCULINUX_REPO_URL='file://$MIRROR' script -qec \"'$PIPED_FEED' '$BOOTSTRAP' --auto --skip-models '--tag=$GATE_TAG'\" /dev/null"
} }
assert_remote_install() { assert_remote_install() {
@@ -223,20 +223,20 @@ assert_remote_install() {
[ -x "$REMOTE_VENV/bin/python" ] \ [ -x "$REMOTE_VENV/bin/python" ] \
|| fail "no remote venv at $REMOTE_VENV; the handoff's --venv-dir did not reach the tagged installer" || fail "no remote venv at $REMOTE_VENV; the handoff's --venv-dir did not reach the tagged installer"
# The remote marker relocates the activation helper to ~/.local/bin only when # The remote marker relocates the activation helper to ~/.local/bin only when
# VOCALINUX_REMOTE_INSTALL survives the exec. # VOCULINUX_REMOTE_INSTALL survives the exec.
[ -f "$INSTALL_HOME/.local/bin/activate-vocalinux.sh" ] \ [ -f "$INSTALL_HOME/.local/bin/activate-voculinux.sh" ] \
|| fail "no ~/.local/bin/activate-vocalinux.sh; the remote marker did not survive the handoff" || fail "no ~/.local/bin/activate-voculinux.sh; the remote marker did not survive the handoff"
# --skip-models forwarded: a model in the data dir means the tagged installer # --skip-models forwarded: a model in the data dir means the tagged installer
# re-parsed arguments the handoff did not deliver. # re-parsed arguments the handoff did not deliver.
if [ -d "$INSTALL_HOME/.local/share/vocalinux/models" ]; then if [ -d "$INSTALL_HOME/.local/share/voculinux/models" ]; then
[ -z "$(find "$INSTALL_HOME/.local/share/vocalinux/models" -type f -print -quit)" ] \ [ -z "$(find "$INSTALL_HOME/.local/share/voculinux/models" -type f -print -quit)" ] \
|| fail "models were downloaded; --skip-models did not survive the handoff" || fail "models were downloaded; --skip-models did not survive the handoff"
fi fi
echo "== Assert: the installed app is there and answers ==" echo "== Assert: the installed app is there and answers =="
[ -x "$INSTALL_HOME/.local/bin/vocalinux" ] || fail "no wrapper at ~/.local/bin/vocalinux" [ -x "$INSTALL_HOME/.local/bin/voculinux" ] || fail "no wrapper at ~/.local/bin/voculinux"
[ -x "$INSTALL_HOME/.local/bin/vocalinux-gui" ] || fail "no wrapper at ~/.local/bin/vocalinux-gui" [ -x "$INSTALL_HOME/.local/bin/voculinux-gui" ] || fail "no wrapper at ~/.local/bin/voculinux-gui"
[ -f "$INSTALL_HOME/.local/share/applications/vocalinux.desktop" ] \ [ -f "$INSTALL_HOME/.local/share/applications/voculinux.desktop" ] \
|| fail "no desktop entry; install_desktop_entry did not run" || fail "no desktop entry; install_desktop_entry did not run"
# The version the wrapper reports must be the tag's own, not an assertion # The version the wrapper reports must be the tag's own, not an assertion
@@ -244,13 +244,13 @@ assert_remote_install() {
local expected_version version_output local expected_version version_output
expected_version="$(grep -oP '__version__\s*=\s*"\K[^"]+' "$CLONE_DIR/src/vocalinux/version.py")" \ expected_version="$(grep -oP '__version__\s*=\s*"\K[^"]+' "$CLONE_DIR/src/vocalinux/version.py")" \
|| fail "cannot read __version__ from the clone's version.py" || fail "cannot read __version__ from the clone's version.py"
version_output="$(su - "$INSTALL_USER" -c "'$INSTALL_HOME/.local/bin/vocalinux' --version")" \ version_output="$(su - "$INSTALL_USER" -c "'$INSTALL_HOME/.local/bin/voculinux' --version")" \
|| fail "the installed wrapper cannot report a version" || fail "the installed wrapper cannot report a version"
case "$version_output" in case "$version_output" in
*"$expected_version"*) ;; *"$expected_version"*) ;;
*) fail "wrapper reports '$version_output', expected the tag's $expected_version" ;; *) fail "wrapper reports '$version_output', expected the tag's $expected_version" ;;
esac esac
echo " vocalinux --version: $version_output" echo " voculinux --version: $version_output"
# Same blind spots as the local gate: the injection binaries are reached # Same blind spots as the local gate: the injection binaries are reached
# through subprocess, and the IBus runtime only where the package list # through subprocess, and the IBus runtime only where the package list
@@ -320,7 +320,7 @@ su - "$INSTALL_USER" -c "git -C '$CLONE_DIR' reset --hard '$AWAY_SHA' \
[ -z "$(git -C "$CLONE_DIR" tag -l "$GATE_TAG")" ] \ [ -z "$(git -C "$CLONE_DIR" tag -l "$GATE_TAG")" ] \
|| fail "the clone kept refs/tags/$GATE_TAG; the re-run proves nothing" || fail "the clone kept refs/tags/$GATE_TAG; the re-run proves nothing"
run_remote_install_piped || fail "the terminal-backed piped install exited non-zero over the existing clone" run_remote_install_piped || fail "the terminal-backed piped install exited non-zero over the existing clone"
grep -l "Updating existing clone" "$INSTALL_HOME"/.local/state/vocalinux/install-*.log >/dev/null 2>&1 \ grep -l "Updating existing clone" "$INSTALL_HOME"/.local/state/voculinux/install-*.log >/dev/null 2>&1 \
|| fail "no install log shows 'Updating existing clone'; the fetch arm did not run" || fail "no install log shows 'Updating existing clone'; the fetch arm did not run"
assert_remote_install assert_remote_install
+7 -7
View File
@@ -17,7 +17,7 @@
# exercise a path no user takes. # exercise a path no user takes.
# #
# Local mode only: install.sh chooses it by finding a pyproject.toml naming # Local mode only: install.sh chooses it by finding a pyproject.toml naming
# vocalinux in the working directory, so extracting the tree and running from it # voculinux in the working directory, so extracting the tree and running from it
# tests this commit. Remote mode (curl | bash, which clones the latest tag) is # tests this commit. Remote mode (curl | bash, which clones the latest tag) is
# not covered here. # not covered here.
# #
@@ -33,7 +33,7 @@ INSTALL_HOME="/home/$INSTALL_USER"
# Local mode puts the venv inside the tree, which is why the checkout is copied # Local mode puts the venv inside the tree, which is why the checkout is copied
# rather than written to: a bind-mounted host tree would come back owned by a # rather than written to: a bind-mounted host tree would come back owned by a
# container uid with a venv in it. # container uid with a venv in it.
TREE="$INSTALL_HOME/vocalinux" TREE="$INSTALL_HOME/voculinux"
fail() { fail() {
echo "FAIL: $*" >&2 echo "FAIL: $*" >&2
@@ -41,11 +41,11 @@ fail() {
exit 1 exit 1
} }
# install.sh writes a full transcript under ~/.local/state/vocalinux/. It is the # install.sh writes a full transcript under ~/.local/state/voculinux/. It is the
# only place the per-distro package manager output survives. # only place the per-distro package manager output survives.
dump_install_log() { dump_install_log() {
local log local log
log="$(ls -1t "$INSTALL_HOME"/.local/state/vocalinux/install-*.log 2>/dev/null | head -n1 || true)" log="$(ls -1t "$INSTALL_HOME"/.local/state/voculinux/install-*.log 2>/dev/null | head -n1 || true)"
if [ -n "$log" ]; then if [ -n "$log" ]; then
echo "== last 200 lines of $log ==" >&2 echo "== last 200 lines of $log ==" >&2
tail -n 200 "$log" >&2 tail -n 200 "$log" >&2
@@ -132,8 +132,8 @@ echo " install took $(( $(date +%s) - START ))s"
echo "== Smoke: is there an installation, and does it import ==" echo "== Smoke: is there an installation, and does it import =="
VENV="$TREE/venv" VENV="$TREE/venv"
[ -x "$VENV/bin/python" ] || fail "no venv interpreter at $VENV/bin/python" [ -x "$VENV/bin/python" ] || fail "no venv interpreter at $VENV/bin/python"
[ -x "$INSTALL_HOME/.local/bin/vocalinux" ] || fail "no wrapper at ~/.local/bin/vocalinux" [ -x "$INSTALL_HOME/.local/bin/voculinux" ] || fail "no wrapper at ~/.local/bin/voculinux"
[ -f "$INSTALL_HOME/.local/share/applications/vocalinux.desktop" ] \ [ -f "$INSTALL_HOME/.local/share/applications/voculinux.desktop" ] \
|| fail "no desktop entry; install_desktop_entry did not run" || fail "no desktop entry; install_desktop_entry did not run"
# The injection backends are reached through subprocess, not GI, so no typelib # The injection backends are reached through subprocess, not GI, so no typelib
@@ -159,7 +159,7 @@ esac
# Through the wrapper rather than the venv directly: the wrapper is what the # Through the wrapper rather than the venv directly: the wrapper is what the
# desktop entry and the user's PATH invoke, and it carries the GI_TYPELIB_PATH # desktop entry and the user's PATH invoke, and it carries the GI_TYPELIB_PATH
# and pywhispercpp library-path resolution the bare console script does not. # and pywhispercpp library-path resolution the bare console script does not.
su - "$INSTALL_USER" -c "'$INSTALL_HOME/.local/bin/vocalinux' --version" \ su - "$INSTALL_USER" -c "'$INSTALL_HOME/.local/bin/voculinux' --version" \
|| fail "the installed wrapper cannot report a version" || fail "the installed wrapper cannot report a version"
su - "$INSTALL_USER" -c "'$VENV/bin/python' - " <<'PY' || fail "the installation does not import" su - "$INSTALL_USER" -c "'$VENV/bin/python' - " <<'PY' || fail "the installation does not import"
+6 -6
View File
@@ -22,7 +22,7 @@ from pathlib import Path
from packaging.version import Version from packaging.version import Version
ROOT = Path(__file__).resolve().parents[1] ROOT = Path(__file__).resolve().parents[1]
TEMPLATE = ROOT / "packaging/aur/vocalinux/PKGBUILD" TEMPLATE = ROOT / "packaging/aur/voculinux/PKGBUILD"
REPOSITORY = "VocaHQ/vocalinux" REPOSITORY = "VocaHQ/vocalinux"
@@ -33,14 +33,14 @@ def digest(path: Path) -> str:
def sdist_version(sdist: Path) -> str: def sdist_version(sdist: Path) -> str:
"""Require one canonical Vocalinux sdist with matching top-level metadata.""" """Require one canonical Voculinux sdist with matching top-level metadata."""
match = re.fullmatch(r"vocalinux-([0-9][A-Za-z0-9.+!]*)\.tar\.gz", sdist.name) match = re.fullmatch(r"voculinux-([0-9][A-Za-z0-9.+!]*)\.tar\.gz", sdist.name)
if not match: if not match:
raise ValueError("expected a canonical vocalinux-VERSION.tar.gz sdist") raise ValueError("expected a canonical voculinux-VERSION.tar.gz sdist")
version = match[1] version = match[1]
if str(Version(version)) != version: if str(Version(version)) != version:
raise ValueError("sdist filename version is not canonical") raise ValueError("sdist filename version is not canonical")
root = f"vocalinux-{version}" root = f"voculinux-{version}"
with tarfile.open(sdist, "r:gz") as archive: with tarfile.open(sdist, "r:gz") as archive:
members = archive.getmembers() members = archive.getmembers()
if any( if any(
@@ -55,7 +55,7 @@ def sdist_version(sdist: Path) -> str:
assert stream is not None assert stream is not None
with stream: with stream:
info = BytesParser().parse(stream) info = BytesParser().parse(stream)
if info.get_all("Name") != ["vocalinux"] or info.get_all("Version") != [version]: if info.get_all("Name") != ["voculinux"] or info.get_all("Version") != [version]:
raise ValueError("sdist metadata does not match its filename") raise ValueError("sdist metadata does not match its filename")
return version return version
+1 -1
View File
@@ -47,7 +47,7 @@ from packaging.utils import canonicalize_name
REPO_ROOT = Path(__file__).resolve().parents[1] REPO_ROOT = Path(__file__).resolve().parents[1]
EXPORT = REPO_ROOT / "requirements" / "vad.txt" EXPORT = REPO_ROOT / "requirements" / "vad.txt"
DEPS_YAML = REPO_ROOT / "packaging" / "flatpak" / "python3-dependencies.yaml" DEPS_YAML = REPO_ROOT / "packaging" / "flatpak" / "python3-dependencies.yaml"
MANIFEST = REPO_ROOT / "packaging" / "flatpak" / "com.vocalinux.Vocalinux.yml" MANIFEST = REPO_ROOT / "packaging" / "flatpak" / "com.voculinux.Voculinux.yml"
#: The environment the Flatpak build resolves for. GNOME 50 ships Python 3.13. #: The environment the Flatpak build resolves for. GNOME 50 ships Python 3.13.
#: numpy's lock entry splits at `python_full_version >= '3.12'`; onnxruntime's #: numpy's lock entry splits at `python_full_version >= '3.12'`; onnxruntime's
+7 -7
View File
@@ -1,8 +1,8 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
""" """
Vocalinux Remote API Test Server Voculinux Remote API Test Server
A simple mock server for testing Vocalinux's remote API feature. A simple mock server for testing Voculinux's remote API feature.
Supports whisper.cpp (/inference) and OpenAI/FunASR-compatible Supports whisper.cpp (/inference) and OpenAI/FunASR-compatible
(/v1/audio/transcriptions) formats. (/v1/audio/transcriptions) formats.
@@ -22,7 +22,7 @@ from http.server import BaseHTTPRequestHandler, HTTPServer
class RemoteAPITestHandler(BaseHTTPRequestHandler): class RemoteAPITestHandler(BaseHTTPRequestHandler):
"""HTTP request handler for testing Vocalinux remote API.""" """HTTP request handler for testing Voculinux remote API."""
def log_message(self, format, *args): def log_message(self, format, *args):
"""Custom log format with timestamp.""" """Custom log format with timestamp."""
@@ -37,7 +37,7 @@ class RemoteAPITestHandler(BaseHTTPRequestHandler):
self.end_headers() self.end_headers()
response = { response = {
"status": "ok", "status": "ok",
"message": "Vocalinux Remote API Test Server", "message": "Voculinux Remote API Test Server",
"endpoints": [ "endpoints": [
"/inference (whisper.cpp format)", "/inference (whisper.cpp format)",
"/v1/audio/transcriptions (OpenAI/FunASR format)", "/v1/audio/transcriptions (OpenAI/FunASR format)",
@@ -138,7 +138,7 @@ class RemoteAPITestHandler(BaseHTTPRequestHandler):
if "test" in filename.lower(): if "test" in filename.lower():
return "This is a test transcription from the mock server." return "This is a test transcription from the mock server."
elif "hello" in filename.lower(): elif "hello" in filename.lower():
return "Hello, this is Vocalinux testing the remote API." return "Hello, this is Voculinux testing the remote API."
elif "long" in filename.lower(): elif "long" in filename.lower():
return ( return (
"This is a longer transcription to test how the system handles " "This is a longer transcription to test how the system handles "
@@ -159,7 +159,7 @@ class DelayedHTTPServer(HTTPServer):
def main(): def main():
parser = argparse.ArgumentParser( parser = argparse.ArgumentParser(
description="Vocalinux Remote API Test Server" description="Voculinux Remote API Test Server"
) )
parser.add_argument( parser.add_argument(
"--port", "--port",
@@ -183,7 +183,7 @@ def main():
args = parser.parse_args() args = parser.parse_args()
print("=" * 60) print("=" * 60)
print("Vocalinux Remote API Test Server") print("Voculinux Remote API Test Server")
print("=" * 60) print("=" * 60)
print(f"Listening on: http://{args.host}:{args.port}") print(f"Listening on: http://{args.host}:{args.port}")
print(f"Processing delay: {args.delay}s") print(f"Processing delay: {args.delay}s")
+1 -1
View File
@@ -26,7 +26,7 @@ import subprocess
import sys import sys
MANIFEST = "SHA256SUMS" MANIFEST = "SHA256SUMS"
PYPI_PROJECT = "vocalinux" PYPI_PROJECT = "voculinux"
PYPI_SUFFIXES = (".whl", ".tar.gz") PYPI_SUFFIXES = (".whl", ".tar.gz")
PYPI_TIMEOUT = 30 PYPI_TIMEOUT = 30
@@ -1,15 +1,15 @@
[Desktop Entry] [Desktop Entry]
Version=1.0 Version=1.0
Type=Application Type=Application
Name=Vocalinux Name=Voculinux
Comment=Free, offline voice dictation for Linux Comment=Free, offline voice dictation for Linux
GenericName=Voice Typing GenericName=Voice Typing
Exec=vocalinux Exec=voculinux
Icon=${SNAP}/usr/share/icons/hicolor/scalable/apps/vocalinux.svg Icon=${SNAP}/usr/share/icons/hicolor/scalable/apps/voculinux.svg
Terminal=false Terminal=false
Categories=Utility;Accessibility;AudioVideo; Categories=Utility;Accessibility;AudioVideo;
Keywords=voice;typing;dictation;speech;recognition;transcription;accessibility; Keywords=voice;typing;dictation;speech;recognition;transcription;accessibility;
StartupNotify=true StartupNotify=true
StartupWMClass=com.vocalinux.Vocalinux StartupWMClass=com.voculinux.Voculinux
X-GNOME-UsesNotifications=true X-GNOME-UsesNotifications=true
X-SnapInstanceName=vocalinux X-SnapInstanceName=voculinux

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 26 KiB

+1 -1
View File
@@ -54,7 +54,7 @@ def relocate_scripts(install: Path, tooling: Path) -> None:
"if __name__ == '__main__':\n" "if __name__ == '__main__':\n"
" sys.exit(main())\n" " sys.exit(main())\n"
) )
for name in ("vocalinux", "vocalinux-gui"): for name in ("voculinux", "voculinux-gui"):
path = install / "bin" / name path = install / "bin" / name
if path.is_symlink() or not path.is_file(): if path.is_symlink() or not path.is_file():
raise RuntimeError(f"Missing installed entry point: {path}") raise RuntimeError(f"Missing installed entry point: {path}")
+2 -2
View File
@@ -17,7 +17,7 @@ import runpy
import sys import sys
from pathlib import Path from pathlib import Path
MANIFEST = Path("usr/share/vocalinux/python-inputs.json") MANIFEST = Path("usr/share/voculinux/python-inputs.json")
def normalize(name: str) -> str: def normalize(name: str) -> str:
@@ -80,7 +80,7 @@ def record(root: Path, source: Path) -> None:
if importlib.metadata.version(name) != expected_version: if importlib.metadata.version(name) != expected_version:
raise RuntimeError(f"Build tool {name} differs from its locked version") raise RuntimeError(f"Build tool {name} differs from its locked version")
expected = locked_versions(source / "requirements/runtime.txt") expected = locked_versions(source / "requirements/runtime.txt")
expected["vocalinux"] = runpy.run_path(str(source / "src/vocalinux/version.py"))["__version__"] expected["voculinux"] = runpy.run_path(str(source / "src/vocalinux/version.py"))["__version__"]
audit(root, expected) audit(root, expected)
manifest = { manifest = {
"python": list(sys.version_info[:2]), "python": list(sys.version_info[:2]),
+5 -5
View File
@@ -7,16 +7,16 @@ if [ "$#" -ne 1 ] || [ ! -f "$1" ]; then
echo "Usage: bash snap/local/smoke-test.sh PATH.snap" >&2 echo "Usage: bash snap/local/smoke-test.sh PATH.snap" >&2
exit 1 exit 1
fi fi
if snap list vocalinux >/dev/null 2>&1; then if snap list voculinux >/dev/null 2>&1; then
echo "Refusing to replace an existing Vocalinux snap; use a disposable host" >&2 echo "Refusing to replace an existing Voculinux snap; use a disposable host" >&2
exit 1 exit 1
fi fi
sudo snap install --dangerous "$1" sudo snap install --dangerous "$1"
snap run vocalinux --version snap run voculinux --version
# --shell does not run the application's command chain. Explicitly enter the # --shell does not run the application's command chain. Explicitly enter the
# same GNOME desktop-launch environment before checking the installed payload. # same GNOME desktop-launch environment before checking the installed payload.
PYNPUT_BACKEND=dummy snap run --shell vocalinux -c ' PYNPUT_BACKEND=dummy snap run --shell voculinux -c '
exec "$SNAP/snap/command-chain/desktop-launch" "$SNAP/bin/python3" \ exec "$SNAP/snap/command-chain/desktop-launch" "$SNAP/bin/python3" \
"$SNAP/usr/lib/vocalinux/check_runtime.py" --root "$SNAP" "$SNAP/usr/lib/voculinux/check_runtime.py" --root "$SNAP"
' '
+24 -24
View File
@@ -1,4 +1,4 @@
# Vocalinux Snap packaging # Voculinux Snap packaging
# #
# First ship: strict confinement, default whisper.cpp engine path, GTK tray UI, # First ship: strict confinement, default whisper.cpp engine path, GTK tray UI,
# mic capture, model download, and text injection (RemoteDesktop portal, wtype, xdotool). # mic capture, model download, and text injection (RemoteDesktop portal, wtype, xdotool).
@@ -8,15 +8,15 @@
# snapcraft pack # snapcraft pack
# See docs/SNAP.md for controlled inputs, the build gate and verification limits. # See docs/SNAP.md for controlled inputs, the build gate and verification limits.
name: vocalinux name: voculinux
base: core24 # Ubuntu 24.04 / Python 3.12 (project requires-python >=3.11) base: core24 # Ubuntu 24.04 / Python 3.12 (project requires-python >=3.11)
# Fallback version; live version is set from src/vocalinux/version.py via # Fallback version; live version is set from src/vocalinux/version.py via
# adopt-info + craftctl set in the vocalinux part (required for craftctl). # adopt-info + craftctl set in the voculinux part (required for craftctl).
version: "0.17.0" version: "0.17.0"
adopt-info: vocalinux adopt-info: voculinux
summary: Free offline voice dictation for Linux summary: Free offline voice dictation for Linux
description: | description: |
Vocalinux is free voice typing for Linux. Hold a shortcut or toggle dictation, and text lands in almost any app on X11 or Wayland. Voculinux is free voice typing for Linux. Hold a shortcut or toggle dictation, and text lands in almost any app on X11 or Wayland.
Speech recognition runs on your machine after you download a model. No Voca account. No cloud transcription on the default path. Speech recognition runs on your machine after you download a model. No Voca account. No cloud transcription on the default path.
@@ -29,33 +29,33 @@ description: |
- Text injection via the Wayland RemoteDesktop portal, wtype, or xdotool - Text injection via the Wayland RemoteDesktop portal, wtype, or xdotool
Sandbox notes: Sandbox notes:
- Global hotkeys need: sudo snap connect vocalinux:raw-input - Global hotkeys need: sudo snap connect voculinux:raw-input
and sudo snap connect vocalinux:hardware-observe and sudo snap connect voculinux:hardware-observe
- Native Wayland typing uses the RemoteDesktop portal: the compositor - Native Wayland typing uses the RemoteDesktop portal: the compositor
asks once on first dictation. The snap ships no uinput plug and no asks once on first dictation. The snap ships no uinput plug and no
ydotool; the Store denied the super-privileged grant. ydotool; the Store denied the super-privileged grant.
- KDE Plasma: KWin's portal can mistype letter case (KDE bug 489021); - KDE Plasma: KWin's portal can mistype letter case (KDE bug 489021);
set "Virtual Keyboard = IBus Wayland" so the IBus path delivers text. set "Virtual Keyboard = IBus Wayland" so the IBus path delivers text.
- Microphone may need: sudo snap connect vocalinux:audio-record - Microphone may need: sudo snap connect voculinux:audio-record
The microphone list comes from the sound server, so pick a device The microphone list comes from the sound server, so pick a device
in Settings rather than relying on the system default. in Settings rather than relying on the system default.
- whisper.cpp runs on the CPU in this snap; it has no GPU backend. - whisper.cpp runs on the CPU in this snap; it has no GPU backend.
For Vulkan acceleration install the AppImage or use install.sh. For Vulkan acceleration install the AppImage or use install.sh.
- Config and models live under ~/snap/vocalinux/ - Config and models live under ~/snap/voculinux/
grade: stable grade: stable
confinement: strict confinement: strict
license: AGPL-3.0-only license: AGPL-3.0-only
icon: snap/gui/vocalinux.png icon: snap/gui/voculinux.png
contact: https://github.com/VocaHQ/vocalinux/issues contact: https://github.com/VocaHQ/vocalinux/issues
issues: https://github.com/VocaHQ/vocalinux/issues issues: https://github.com/VocaHQ/vocalinux/issues
website: https://vocalinux.com website: https://voculinux.com
source-code: https://github.com/VocaHQ/vocalinux source-code: https://github.com/VocaHQ/vocalinux
donation: https://github.com/sponsors/jatinkrmalik donation: https://github.com/sponsors/jatinkrmalik
apps: apps:
vocalinux: voculinux:
command: bin/vocalinux command: bin/voculinux
extensions: [gnome] extensions: [gnome]
plugs: plugs:
# gnome extension already grants desktop, desktop-legacy, gsettings, # gnome extension already grants desktop, desktop-legacy, gsettings,
@@ -67,17 +67,17 @@ apps:
- network-bind # optional remote_api engine - network-bind # optional remote_api engine
# Global hotkeys via python-evdev (/dev/input/event*). # Global hotkeys via python-evdev (/dev/input/event*).
# Super-privileged: not auto-connected; user must: # Super-privileged: not auto-connected; user must:
# sudo snap connect vocalinux:raw-input # sudo snap connect voculinux:raw-input
- raw-input - raw-input
# Needed for /proc/bus/input/devices; not auto-connected: # Needed for /proc/bus/input/devices; not auto-connected:
# sudo snap connect vocalinux:hardware-observe # sudo snap connect voculinux:hardware-observe
- hardware-observe - hardware-observe
# No uinput plug; the RemoteDesktop portal types on Wayland instead. # No uinput plug; the RemoteDesktop portal types on Wayland instead.
# The Store denied the super-privileged grant # The Store denied the super-privileged grant
# (https://forum.snapcraft.io/t/53528), and ydotool is not staged # (https://forum.snapcraft.io/t/53528), and ydotool is not staged
# because it cannot reach /dev/uinput under confinement. # because it cannot reach /dev/uinput under confinement.
common-id: com.vocalinux.Vocalinux common-id: com.voculinux.Voculinux
desktop: usr/share/applications/vocalinux.desktop desktop: usr/share/applications/voculinux.desktop
environment: environment:
# Prefer bundled CLI helpers (xdotool, xsel, xclip, wl-copy). # Prefer bundled CLI helpers (xdotool, xsel, xclip, wl-copy).
# Do NOT prepend $SNAP/usr/lib over the gnome extension platform path: # Do NOT prepend $SNAP/usr/lib over the gnome extension platform path:
@@ -103,7 +103,7 @@ layout:
bind: $SNAP/usr/lib/$CRAFT_ARCH_TRIPLET/alsa-lib bind: $SNAP/usr/lib/$CRAFT_ARCH_TRIPLET/alsa-lib
parts: parts:
vocalinux: voculinux:
# The Python plugin would resolve dependencies and build tools again. # The Python plugin would resolve dependencies and build tools again.
plugin: nil plugin: nil
source: . source: .
@@ -151,13 +151,13 @@ parts:
--source "${CRAFT_PART_BUILD}" --install "${CRAFT_PART_INSTALL}" \ --source "${CRAFT_PART_BUILD}" --install "${CRAFT_PART_INSTALL}" \
--work "${CRAFT_PART_BUILD}/.snap-python-build" --work "${CRAFT_PART_BUILD}/.snap-python-build"
install -Dm644 "${CRAFT_PART_SRC}/snap/local/check_runtime.py" \ install -Dm644 "${CRAFT_PART_SRC}/snap/local/check_runtime.py" \
"${CRAFT_PART_INSTALL}/usr/lib/vocalinux/check_runtime.py" "${CRAFT_PART_INSTALL}/usr/lib/voculinux/check_runtime.py"
install -Dm644 "${CRAFT_PROJECT_DIR}/snap/gui/vocalinux.desktop" \ install -Dm644 "${CRAFT_PROJECT_DIR}/snap/gui/voculinux.desktop" \
"${CRAFT_PART_INSTALL}/usr/share/applications/vocalinux.desktop" "${CRAFT_PART_INSTALL}/usr/share/applications/voculinux.desktop"
install -Dm644 "${CRAFT_PROJECT_DIR}/resources/icons/scalable/vocalinux.svg" \ install -Dm644 "${CRAFT_PROJECT_DIR}/resources/icons/scalable/voculinux.svg" \
"${CRAFT_PART_INSTALL}/usr/share/icons/hicolor/scalable/apps/vocalinux.svg" "${CRAFT_PART_INSTALL}/usr/share/icons/hicolor/scalable/apps/voculinux.svg"
# meta/gui is what snapd uses for the store/desktop icon when present. # meta/gui is what snapd uses for the store/desktop icon when present.
install -Dm644 "${CRAFT_PROJECT_DIR}/resources/icons/scalable/vocalinux.svg" \ install -Dm644 "${CRAFT_PROJECT_DIR}/resources/icons/scalable/voculinux.svg" \
"${CRAFT_PART_INSTALL}/meta/gui/icon.svg" "${CRAFT_PART_INSTALL}/meta/gui/icon.svg"
install -Dm644 "${CRAFT_PROJECT_DIR}/snap/local/alsa/asound.conf" \ install -Dm644 "${CRAFT_PROJECT_DIR}/snap/local/alsa/asound.conf" \
"${CRAFT_PART_INSTALL}/etc/asound.conf" "${CRAFT_PART_INSTALL}/etc/asound.conf"
+1 -1
View File
@@ -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 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. the package to be imported even when system GTK typelibs are not available.
+47 -41
View File
@@ -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 Ticket #18 rename: every user-visible identity string that is hardcoded in
more than one place is driven from this single module. No value changes here; more than one place is driven from this single module. The fork takes its
the rename ticket (#18) flips them in one place instead of a scavenger hunt. 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 One-time legacy import of a previous Voculinux install lives in
changelog history) are recorded in :data:`RETAINED_SURFACES`, not renamed. :mod:`vocalinux.legacy_import` and keys off :data:`LEGACY_APP_DIR_NAME`.
""" """
APP_NAME: str = "Vocalinux" APP_NAME: str = "Voculinux"
APP_ID: str = "vocalinux" APP_ID: str = "voculinux"
APP_DIR_NAME: str = "vocalinux" APP_DIR_NAME: str = "voculinux"
CLI_PROG: str = "vocalinux" CLI_PROG: str = "voculinux"
BUS_NAME: str = "com.vocalinux.Vocalinux" BUS_NAME: str = "com.voculinux.Voculinux"
OBJECT_PATH: str = "/com/vocalinux/Vocalinux" OBJECT_PATH: str = "/com/voculinux/Voculinux"
INTERFACE_NAME: str = "com.vocalinux.Vocalinux" INTERFACE_NAME: str = "com.voculinux.Voculinux"
DESKTOP_FILENAME: str = "vocalinux.desktop" DESKTOP_FILENAME: str = "voculinux.desktop"
DESKTOP_ICON_NAME: str = "vocalinux" DESKTOP_ICON_NAME: str = "voculinux"
TRAY_TITLE: str = APP_NAME TRAY_TITLE: str = APP_NAME
ICON_DEFAULT: str = "vocalinux-microphone-off" ICON_DEFAULT: str = "voculinux-microphone-off"
ICON_ACTIVE: str = "vocalinux-microphone" ICON_ACTIVE: str = "voculinux-microphone"
ICON_PROCESSING: str = "vocalinux-microphone-process" ICON_PROCESSING: str = "voculinux-microphone-process"
ICON_BASE_NAMES: tuple[str, ...] = ( ICON_BASE_NAMES: tuple[str, ...] = (
"vocalinux", "voculinux",
ICON_ACTIVE, ICON_ACTIVE,
ICON_DEFAULT, ICON_DEFAULT,
ICON_PROCESSING, ICON_PROCESSING,
) )
ENGINE_NAME: str = "vocalinux" ENGINE_NAME: str = "voculinux"
ENGINE_LONGNAME: str = "Vocalinux" ENGINE_LONGNAME: str = "Voculinux"
ENGINE_DESCRIPTION: str = "Vocalinux voice dictation (use as default input method)" ENGINE_DESCRIPTION: str = "Voculinux voice dictation (use as default input method)"
COMPONENT_NAME: str = "org.freedesktop.IBus.Vocalinux" COMPONENT_NAME: str = "org.freedesktop.IBus.Voculinux"
IBUS_DIR_NAME: str = "vocalinux-ibus" IBUS_DIR_NAME: str = "voculinux-ibus"
IBUS_TEXTDOMAIN: str = "vocalinux" 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" VERIFICATION_STAMP_NAME: str = ".voculinux_verified"
LOG_FILE_PREFIX: str = "vocalinux_logs_" LOG_FILE_PREFIX: str = "voculinux_logs_"
PORTAL_TOKEN_PREFIX: str = "vocalinux_" PORTAL_TOKEN_PREFIX: str = "voculinux_"
ENV_FORCE_BACKEND: str = "VOCALINUX_FORCE_BACKEND" ENV_FORCE_BACKEND: str = "VOCULINUX_FORCE_BACKEND"
ENV_INJECT_MODIFIER_WAIT: str = "VOCALINUX_INJECT_MODIFIER_WAIT" ENV_INJECT_MODIFIER_WAIT: str = "VOCULINUX_INJECT_MODIFIER_WAIT"
ENV_YDOTOOL_KEY_DELAY: str = "VOCALINUX_YDOTOOL_KEY_DELAY" 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. #: ``tests/test_app_identity.py`` so the list cannot drift silently.
RENAME_SURFACES: tuple[str, ...] = ( RENAME_SURFACES: tuple[str, ...] = (
"pyproject.toml", "pyproject.toml",
"vocalinux.desktop", "voculinux.desktop",
"snap/gui/vocalinux.desktop", "snap/gui/voculinux.desktop",
"snap/snapcraft.yaml", "snap/snapcraft.yaml",
"packaging/flatpak/com.vocalinux.Vocalinux.desktop", "packaging/flatpak/com.voculinux.Voculinux.desktop",
"packaging/flatpak/com.vocalinux.Vocalinux.metainfo.xml", "packaging/flatpak/com.voculinux.Voculinux.metainfo.xml",
"packaging/flatpak/com.vocalinux.Vocalinux.yml", "packaging/flatpak/com.voculinux.Voculinux.yml",
"packaging/aur/vocalinux-bin/vocalinux.desktop", "packaging/aur/voculinux-bin/voculinux.desktop",
"web/src/app/page.tsx", "web/src/app/page.tsx",
"web/package.json", "web/package.json",
"README.md", "README.md",

Some files were not shown because too many files have changed in this diff Show More