Commit Graph
683 Commits
Author SHA1 Message Date
soubarnak da97b676a7 Merge feature/29 into integration/spec16 (ticket #29: signed xbps pipeline) 2026-11-06 06:29:27 +05:30
soubarnak 9bc04936b6 feat(void): add signed xbps repository pipeline
Build every template in smoke-gate order, index the full repository and
sign packages plus metadata with the RSA4096 key, in the pinned Void
container via podman. Fail-closed without a key: no key file, no repo.

Ticket #29 (needs #27, #28).
2026-11-06 06:29:22 +05:30
soubarnak 1ae453e7d9 Merge feature/28 into integration/spec16 (ticket #28: Void package smoke gate) 2026-11-06 05:57:40 +05:30
soubarnak bd7b624f50 feat(void): add package smoke gate across all templates
Ticket xavierk/voculinux#28, needs #27. One documented command
(packaging/void/smoke.sh, just void-smoke) lints, builds, installs
and smoke-tests every template under packaging/void/srcpkgs in
dependency order in the pinned Void container via podman, failing
fast naming the package with the harness echoing each stage.

The gate caught three latent breaks in the per-package harness and
the app template, fixed here:
- container-main.sh scoped the libggml scan to python3-pywhispercpp
  and gave the leaf packages their own import smokes (pynput by
  presence: no display in the container), failing loudly for
  packages without a smoke recipe;
- voculinux template drops the deprecated wrksrc override (xbps-src
  renames the single-top-dir archive itself; xlint rejects wrksrc)
  and builds the PEP 517 wheel (the sdist ships no setup.py);
- post_install promotes the pre-rename sdist names (desktop entry
  content, icons, /usr/bin launchers) to the renamed identity, and
  the payload smoke rejects pre-rename leftovers in OS-visible paths.

Offline policy tests (tests/test_void_smoke_gate.py) pin the gate to
the template set, the app-last order, podman-only pinned inputs and
per-package failure identity; the #27 policy tests and the rename
sweep cover the harness and template contracts.
2026-11-06 05:57:37 +05:30
soubarnak edaa3a57d3 Merge feature/24 fix into integration/spec16 (ticket #24: fork-status install notes, xbps claim) 2026-11-06 03:17:08 +05:30
soubarnak fe209e70ad fix(web): honest fork-status notes on install paths, soften xbps claim
Install one-liners still fetch the upstream script, so each install
entry point now says so plainly (Nix-page precedent): upstream
installs Vocalinux, not Voculinux, and fork release artefacts are not
published yet. Homepage xbps card no longer claims signed repository
data ships with every release while the packaging pipeline (#29) is
open. Also 'VocaHQ remote' -> 'Flatpak remote' on the install page.

Refs #24
2026-11-06 03:16:39 +05:30
soubarnak f660471757 Merge feature/31 into integration/spec16 (ticket #31: injection ladder tests and first-dictation docs) 2026-11-06 03:04:14 +05:30
soubarnak d320cdae14 Merge feature/27 into integration/spec16 (ticket #27: Void xbps app template) 2026-11-06 03:04:11 +05:30
soubarnak fa14552a2a Merge feature/24 into integration/spec16 (ticket #24: web rebrand to Voculinux) 2026-11-06 03:04:09 +05:30
soubarnak c8cdb810a6 Merge feature/22 into integration/spec16 (ticket #22: niri keybind pack) 2026-11-06 03:04:06 +05:30
soubarnak d7a4b8c21e feat(void): add xbps template for the voculinux app
Ticket xavierk/voculinux#27, built on #18 (rename) plus the #25/#26
dependency templates. The template builds the app from the canonical
checksum-verified release sdist, depends on Void's interpreter, GTK,
desktop bindings and packaged Python libraries, and installs the
renamed desktop entry, icons and a packaged-install marker. No speech
models, no vendored wheels, no pip at build or install time.

Packaged installs keep background update checks as a signal, but every
update affordance presents the package-manager path
(src/vocalinux/utils/packaging.py). Also corrects the Void distro
inventory (Ayatana provider name; ydotool marked absent) and regens
the installer map.
2026-11-06 03:03:33 +05:30
soubarnak f26e0a9111 feat(web): rebrand marketing site to Voculinux
Names, wordmarks, package and install references move to voculinux;
site domain to voculinux.com with renamed public assets. Layout keeps
Carbon spacing/type/motion discipline remapped to the house palette
(teal interactive, warm paper, system stacks, red reserved). Header
and footer gain Bongbetic attribution chrome linking hosted brand
assets; no brand binaries vendored. Engine copy states whisper.cpp as
guaranteed default, others as best-effort extras, remote API as
user-configured with audio leaving the machine. Nix page carries a
fork-status note (upstream flake installs Vocalinux).

Refs #24
2026-11-06 02:30:01 +05:30
soubarnak e6f7938068 test(injection): pin fallback ladder per desktop class and niri first-run
Locks the ordered rungs without real compositors: portal first where a
backend implements it (GNOME), direct typing first on niri with IBus
excluded and no portal selected, deny demoting to direct typing with the
full dictation kept, terminal probe choosing Ctrl+Shift+V, held modifiers
delaying the XWayland paste, and clipboard manual paste as last resort.

USER_GUIDE states the niri first run (no prompt, typing goes direct,
never pin portal there) and the GNOME/KDE grant, persistence and deny
fallback under the renamed backend setting and override names.

Refs #31
2026-11-06 02:27:15 +05:30
soubarnak c27d204a00 feat(niri): add niri keybind pack and Dank Material Shell notes
Docs-only slice of ticket #22: include file, integration page, index
links. Tray-watcher observation stays open for hardware run (#32); no
probe code changed.

Refs #22
2026-11-06 02:23:40 +05:30
soubarnak 27f157355d style(models): fix isort import block in recognition_manager
Removes the blank line between the function-level imports so
isort --check-only passes on integration/spec16.
2026-11-06 01:58:07 +05:30
soubarnak 59086820a7 Merge feature/21 into integration/spec16 (ticket #21: D-Bus state signal) 2026-11-06 01:51:05 +05:30
soubarnak 74ea855220 feat(dbus): expose dictation state signal and property
Alongside Toggle/Start/Stop, the renamed interface now publishes
dictation state (idle/listening/processing) as a StateChanged signal
and a readable State property, so status bars and scripts can follow
dictation without scraping the tray. Signals fire only on change, so
double start/stop emits no duplicates; command-line triggers behave
exactly as before.

Refs #21
2026-11-06 01:51:05 +05:30
soubarnak 701266ac2e Merge feature/20 into integration/spec16 (ticket #20: memory-aware model ladder) 2026-11-06 01:48:24 +05:30
soubarnak df771d8fb1 feat(models): memory-aware whisper.cpp ladder with conditional backend order
Free memory picks the load-time rung (large-v3-turbo-q8_0 default,
q5_0 under 1086 MiB free, small/small.en floor) for unpinned default
configs; thresholds derive from catalog sizes plus the protocol's
512 MiB headroom. A manual pin is never overridden, and a failed load
steps one rung down and retries exactly once before surfacing.

CUDA ranks before Vulkan only with a complete architecture-matching
toolkit (nvidia-smi GPU, nvcc, compute capability >= 7.0); otherwise
Vulkan, then CPU. The driver-only CUDA recommendation now demotes to
CPU logic instead of naming an unreachable VRAM ladder.

Refs #20
2026-11-06 01:48:20 +05:30
soubarnak 426f524145 Merge feature/18 into integration/spec16 (ticket #18: rename to voculinux) 2026-11-06 01:29:42 +05:30
soubarnak ea7be5f111 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
2026-11-06 01:29:39 +05:30
soubarnak c565e6d946 Merge feature/30 into integration/spec16 (ticket #30: upstream intake policy) 2026-11-06 00:06:02 +05:30
soubarnak d4be3caf7a Merge feature/26 into integration/spec16 (ticket #26: void pywhispercpp template)
Conflict in packaging/void/tool_checksums.txt (add/add with #25):
kept #26 rows (base-image digest + void-packages pin the kept
build.sh reads) with the #25 header reference folded in. #25's
base-image digest differed; flagged for re-pin on next bump.
2026-11-06 00:06:02 +05:30
soubarnak cc9db31450 Merge feature/25 into integration/spec16 (ticket #25: void pynput/pyaudio templates) 2026-11-06 00:05:40 +05:30
soubarnak 9d09319ba1 Merge feature/23 into integration/spec16 (ticket #23: brand accent hook) 2026-11-06 00:05:37 +05:30
soubarnak c5c42fedee Merge feature/19 into integration/spec16 (ticket #19: perf baseline protocol) 2026-11-06 00:05:35 +05:30
soubarnak 0b3f01227f Merge feature/17 into integration/spec16 (ticket #17: centralise app identity) 2026-11-06 00:05:32 +05:30
soubarnak 203b51ab78 feat(ui): add brand accent hook to GTK colour scheme
BRAND_ACCENT single source with shade()-derived states; accent CSS
covers suggested-action buttons and the listening chip, installed at
USER priority from main().

Refs #23
2026-11-06 00:05:29 +05:30
soubarnak d7a0273da1 refactor(identity): centralise user-visible app identity
Single app_identity module owns APP_NAME/APP_ID, D-Bus names,
desktop/icon/IBus names and related surfaces; 16 consumers import
from it with zero value changes. Deliberately excludes
settings_about and dictation_pad literals pinned by existing tests.

Refs #17
2026-11-06 00:05:26 +05:30
soubarnak 1d8b07727e feat(void): add xbps template for python3-pywhispercpp
Spec #16 ticket #26: Void template for the whisper.cpp Python binding,
built from the pinned 1.5.0 sdist with the Vulkan backend (default-on
option), shared libraries, and portable CPU flags. Includes the podman
build/smoke harness, pinned base-image pins, and offline policy tests.

Refs: #26
2026-11-06 00:04:47 +05:30
soubarnak 6713722daf feat(perf): lock installed-build baseline protocol and take target-machine numbers
Ticket #19 (spec #16): fixed three-metric protocol as a runnable script
(idle memory both keep-alive states, wake latency from debug logs,
real-time factor on the fixed 30 s sample per backend) plus per-rung
load footprints, portal/direct injection probe, and threshold
derivation for the model-ladder ticket (#20). Offline unit tests for
the parsing/statistics/report helpers. Evidence recorded on the ticket.

One debug-only marker (Toggle requested in state ...) makes wake
latency derivable from logs alone.
2026-11-05 23:11:28 +05:30
soubarnak c462ffb4b2 feat(void): add xbps templates for pynput and updated PyAudio
Ticket: #25

Two in-repo void-packages-layout templates under packaging/void/srcpkgs:
python3-pynput 1.8.2 (new; Void does not ship it) and python3-PyAudio
0.2.14 (updated local template; Void ships 0.2.12, below the project's
pyaudio>=0.2.13 floor). Versions and checksums track uv.lock and
requirements/runtime.txt; pynput post_patch strips setup_requires so the
build never fetches helpers over the network (setuptools-lint and twine
are not in void-packages). No noverifypydeps/nopyprovides anywhere.

Adds tests/test_void_small_deps.py (offline policy test): template
layout and metadata shape, lock/export/floor pins, checksum pins,
no verification-disabling options, linux dep coverage, verified-only
dependency names, and the digest-pinned base image in
packaging/void/tool_checksums.txt.

Verified: xlint clean on both templates; both packages build with
xbps-src in the pinned ghcr.io/void-linux/void-glibc-full container
(podman) and install in a fresh container with pyaudio 0.2.14
importing against PortAudio V19.6.0.
2026-11-05 23:09:16 +05:30
soubarnak de548d6a56 docs(release): write upstream cherry-pick policy into release process
Intake at release-preparation sync points plus on-demand critical
fixes, freeze once the release branch opens to release-blockers only,
one upstream fix per fork pull request through the standard gates,
conflicts resolved by keeping upstream logic and re-applying fork
renames, and the qualification rule (fixes and user-instruction docs
port; features re-decided; dependency/CI churn only on a security
advisory). Names the retained internal vocalinux module as the reason
conflicts concentrate at renamed user-visible surfaces.

Tests: tests/test_upstream_intake_policy.py (7 tests)
2026-11-05 22:46:41 +05:30
Jatin K MalikandGitHub 0a4d637f5f fix(overlay): detect Wayland by GType so the fallback overlay stays compact (#1030) (#1035)
* fix(overlay): detect Wayland by GType so the fallback overlay stays compact (#1030)

_is_wayland() checked isinstance(display, Gdk.WaylandDisplay), but GTK3's
Gdk typelib has no WaylandDisplay class, so it always returned False. On
Wayland without gtk-layer-shell, _layout_bottom_strip() therefore sized the
prewarmed fallback window to the full monitor width (the X11 strip) instead
of the compact centered window meant for Wayland, which on KDE showed up as
a wide empty "Vocalinux Dictation" window listed in the taskbar.

Drop _is_wayland() and use _display_is_wayland(), which checks the GType
name and already gates prewarm, so both decisions agree.

Also list gtk-layer-shell as an optdepend of the source AUR package: with
it KWin and wlroots compositors get a layer surface that is never listed
in the taskbar and never needs the prewarmed toplevel. The AppImage (and
so vocalinux-bin) already bundles it.

* docs(overlay): describe the compact Wayland fallback in the overlay docstrings

The class docstring still said the overlay is always a full-width strip, which no longer holds on Wayland without layer shell (#1030).

* fix(overlay): keep the full-width strip on GNOME Wayland

Mutter ignores CENTER_ALWAYS for a plain Wayland toplevel, so the compact
fallback window landed in the top-left corner on GNOME (tested on GNOME
Shell 46 with the Ubuntu Dock). The full-width strip at least keeps the
orb horizontally centered there. Use the compact window only on KDE,
where KWin listed the strip in the taskbar as a wide empty window (#1030).
2026-11-02 19:17:27 -08:00
Jatin K MalikandGitHub f70357f2b8 fix(overlay): quit Vocalinux when the dock closes the overlay window (#1028) (#1036)
* fix(overlay): quit Vocalinux when the dock closes the overlay window (#1028)

On GNOME Wayland without layer shell, the prewarmed dictation overlay is a
regular toplevel that the dock lists as the app's window. Since #1017 the
overlay refuses close requests so it is never destroyed (#1010), which made
"Quit" from the dock silently do nothing.

The overlay still keeps its window, but now forwards the close request to
an optional on_close_request callback, scheduled once from an idle source.
The tray passes its _quit(), so the dock's Quit behaves like the tray's Quit.

* fix(overlay): drop a queued dock close once the app is quitting

A dock close request is forwarded through an idle source. If the tray's
Quit or SIGTERM tears the overlay down before that idle runs, the
callback would run _quit() a second time. destroy() now drops the
callback, and the pending flag is cleared when the request runs so a
later close can retry if quitting failed.

Refs #1028
2026-11-02 19:17:21 -08:00
Jatin K MalikandGitHub 61bfb1e58e fix(ui): escape device names and test errors in the audio status label (#1043)
The Settings audio status label is set with Pango markup. A microphone
named with '&' or '<' (or an audio test error carrying them) made the
markup invalid, so GTK dropped the text. Escape both with
GLib.markup_escape_text.

Also correct the v0.17.0 notes in docs/UPDATE.md: the Snap never
bundled VOSK; it installs the project without extras.
2026-11-02 18:46:31 -08:00
c461cf5147 fix(snap): build from locked python inputs and verify artifacts (#1041)
* fix(snap): build and verify payload from locked python inputs

* docs(snap): document locked inputs and artifact verification

* test(clipboard): isolate restore workers between tests

* test(packaging): tighten isolation and clarify snap audit guarantees

* fix(ci): run snap recipe tests on snap-only changes

A change to snap/snapcraft.yaml or snap/local/smoke-test.sh only matched
the snap filter, so the Python tests that guard the recipe (including the
portable whisper.cpp flags from #957) and the smoke script were skipped.
Match all of snap/ in the python filter, as scripts/ already is.

* docs(snap): use a version placeholder in the smoke-test example

---------

Co-authored-by: Jatin <jatinkrmalik@gmail.com>
2026-11-02 12:31:14 -08:00
80e3019a74 feat(parakeet): add optional Underdog Bark 3.0 models, int8 and 4-bit (#1031)
* feat(parakeet): add optional Underdog Bark 3.0 int8 model

Adds bark-3.0 to the Parakeet model picker: Underdog Bark 3.0, an
English fine-tune of Parakeet TDT 0.6B v3, as the int8 sherpa-onnx
conversion published at velddev/Underdog-Bark-3.0-sherpa-onnx. It runs
on the existing sherpa-onnx recognizer, pinned to a revision with every
file checked against model_checksums.txt, like Orukeet (#840).

The conversion writes the unknown-word token where a quote mark was, so
the Parakeet transcript drops <unk>; the other models never emit it.

* docs(parakeet): say when to pick Bark 3.0; loosen its catalog test

The README now says Bark suits English dictation of conversational
speech and v3-european stays the choice for read or multilingual
speech. The test checks that bark-3.0 is offered rather than that it is
the last catalog entry, so a later model does not break it.

* test(parakeet): add docstrings to the Bark tests

* feat(parakeet): add optional 4-bit Underdog Bark 3.0 build

Adds bark-3.0-int4: the same Bark 3.0 weights quantized to 4-bit with
onnxruntime's HQQ quantizer, for machines short on memory. It needs
about half the RAM of the int8 build (0.64 GB against 1.28 GB peak) and
a 379 MiB download, at two to four times the CPU time (about 2x on
Intel, 3.7x on AMD by the publisher's measurement). The files are
published in velddev/Underdog-Bark-3.0-sherpa-onnx (Hugging Face PR #1),
and both Bark entries now pin its revision 3ae3af1, which holds both
builds; the int8 files are unchanged there.

Catalog entries gain an optional "precision" key, so the loader opens
encoder.int4.onnx and friends instead of assuming int8; every existing
model keeps the int8 default. The README section now covers both
builds in a few lines.

* test(parakeet): type the Bark test signatures

* docs(parakeet): name the Bark sherpa-onnx source and its pins

The README now says where both Bark builds download from (a pinned revision of
velddev/Underdog-Bark-3.0-sherpa-onnx), that it is an unofficial conversion, and
that each file is checked against its bundled SHA-256 pin, as the Orukeet
section does.

---------

Co-authored-by: Jatin <jatinkrmalik@gmail.com>
2026-11-02 12:31:08 -08:00
Jatin K MalikandGitHub f3913fb6bf fix(ui): show the selected microphone's full name in the device tooltip (#1029) (#1037)
The Input Device dropdown has a fixed width and cuts labels off at about
18 characters. Raw ALSA names such as "sof-hda-dsp: - (hw:0,6)" therefore
all look like "sof-hda-dsp: - (h…", and the tooltip always showed the same
generic text, so there was no way to tell which entry was selected.

The tooltip now shows the full label of the selected device. It updates
when the list is filled and every time the selection changes, and goes
back to the generic hint for System Default.
2026-11-02 12:31:02 -08:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
d11af1c232 chore(deps): bump the github-actions group with 4 updates (#1042)
Bumps the github-actions group with 4 updates: [actions/setup-python](https://github.com/actions/setup-python), [actions/upload-artifact](https://github.com/actions/upload-artifact), [astral-sh/setup-uv](https://github.com/astral-sh/setup-uv) and [actions/download-artifact](https://github.com/actions/download-artifact).


Updates `actions/setup-python` from 7.0.0 to 7.1.0
- [Release notes](https://github.com/actions/setup-python/releases)
- [Commits](https://github.com/actions/setup-python/compare/v7...6d4fd22b20465ccc0f7354476588e8df7c9ca0e0)

Updates `actions/upload-artifact` from 7.0.1 to 7.0.2
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](https://github.com/actions/upload-artifact/compare/v7.0.1...cf430e030ddbb5b0abf93d22962f4752f3646cd9)

Updates `astral-sh/setup-uv` from 10.2.0 to 10.3.3
- [Release notes](https://github.com/astral-sh/setup-uv/releases)
- [Commits](https://github.com/astral-sh/setup-uv/compare/v10.2.0...68559e99cc807c1eb7e9238fd43a15bba460eec2)

Updates `actions/download-artifact` from 8.0.1 to 8.0.2
- [Release notes](https://github.com/actions/download-artifact/releases)
- [Commits](https://github.com/actions/download-artifact/compare/v8.0.1...9000827ccba6bdab643e8b6fd33ac0654aef8333)

---
updated-dependencies:
- dependency-name: actions/setup-python
  dependency-version: 7.1.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: github-actions
- dependency-name: actions/upload-artifact
  dependency-version: 7.0.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: github-actions
- dependency-name: astral-sh/setup-uv
  dependency-version: 10.3.3
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: github-actions
- dependency-name: actions/download-artifact
  dependency-version: 8.0.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: github-actions
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-11-02 12:30:53 -08:00
Jatin K MalikandGitHub 122a5e69e6 feat(web): Linux-flavoured 404 page that sends visitors back (#1040)
* feat(web): Linux-flavoured 404 page that sends visitors back

The static export served Next's default "404: This page could not be
found." with no way back into the site.

- "This page is not in your $PATH" heading, plus three true facts about
  404 (chmod 404, exit 404 -> $? = 148, 0x194 / 0o624 / 0b110010100).
- "Did you mean ...?" suggests the closest page the site links to, by edit
  distance after lowercasing. Catches typos and case slips, which matter
  because GitHub Pages paths are case-sensitive.
- Back to home and Install buttons, plus quick links to the install
  guides, FAQ, engine comparison, troubleshooting and changelog.
- Title "Page not found | Vocalinux", noindex.

* fix(web): keep the 404 page working on malformed URLs

decodeURIComponent throws on escapes like /%FF/, which replaced the 404
page with an error screen. Fall back to the raw path, and add mounted
tests for the suggestion and the malformed case.
2026-11-01 01:44:17 -07:00
Jatin K MalikandGitHub f7147bdb44 fix(web): SEO fixes from Search Console and Lighthouse audit (#1039)
* fix(web): SEO fixes from Search Console and Lighthouse audit

Technical:
- Emit BreadcrumbList JSON-LD from the same trail as the visual breadcrumbs
  on every SeoSubpageShell page.
- absoluteUrl() no longer appends a trailing slash to file paths. The
  Organization and publisher logo URLs pointed at /vocalinux.png/, a 404.
- Fetch the GitHub star count once at build time in next.config.js and
  drop the client-side api.github.com fetch and preconnect (Lighthouse
  "third-party code" on every page). Falls back to the "GitHub" label.
- Fix h1 -> h3 heading skips on 11 pages by promoting the leading card
  headings to h2; new .subpage-card-title keeps the old h3 sizing.
- Header and footer logo get alt="" next to the visible "Vocalinux" text.
- Footer links every guide, distro and feature page, so the pages added in
  #1038 and /vs-nerd-dictation/ (Discovered, not indexed) and /writers/
  (no inbound links) are linked sitewide.

Titles and descriptions:
- Rewrite titles/descriptions for pages with high impressions and low CTR
  in Search Console (home, FAQ, compare, languages, open-source, GPU,
  alternatives, offline, install) to lead with the queries they rank for.
- Distro pages use "<Distro> Speech to Text: Offline Voice Typing Setup"
  as the search title; the on-page h1 is unchanged.
- Remove brand repetition and shorten 75+ character titles.
- Bump sitemap lastmod for the rewritten pages.

* fix(web): keep home install card titles styled

The home page's install cards still use h3; only /install/ moved to h2.
Style both so the home cards keep their size and spacing.
2026-11-01 01:39:01 -07:00
Jatin K MalikandGitHub 6389e81d45 docs(web): 16 new SEO pages for v0.17–v0.18 features and more distros (#1038)
* fix(web): stop doubled title suffix and correct stale page claims

- buildPageMetadata now emits titles that already end in the brand
  (e.g. "... | Vocalinux") as absolute, so the layout template no longer
  renders "| Vocalinux | Vocalinux" on 15 existing pages
- autostart: Start on Login moved out of the tray menu in v0.18.1; point
  readers at Settings -> General and the first-run prompt
- wayland: align compositor support with docs/DISTRO_COMPATIBILITY.md
  (sway good, Hyprland experimental, river untested)

* docs(web): add guides for v0.17-v0.18 features and more distros

New pages for features that only had changelog one-liners:
/parakeet/, /faster-whisper/, /dictation-pad/, /transcribe-audio-files/,
/custom-dictionary/, /multilingual-dictation/, /pipewire/,
/global-shortcuts/, /hyprland-sway/, /local-gateway/, /flatpak/,
/install/nixos/, plus /install/debian/, /install/opensuse/,
/install/linux-mint/ and /install/pop-os/ via the distro route.

Each page is checked against src/ and docs/ at v0.18.5, carries
TechArticle/HowTo/FAQPage JSON-LD built from the visible content, and
links to existing guides. The distro route now lists support levels from
the compatibility matrix instead of unsourced "tested on" claims, uses
the documented install commands, adds AUR and native package options,
and drops its duplicate breadcrumb.

Registration: sitemap entries, breadcrumb titles, a Features footer
group, links from the install hub, homepage engine chips and the
reliability page, and a test that every static route is in the sitemap.
The homepage VocaPhone card now says Android is on Google Play.
2026-11-01 00:30:07 -07:00
dbf8039196 chore(release): v0.18.5 (#1027)
* chore(release): v0.18.5

- Update version from 0.18.4 to 0.18.5
- Add v0.18.5 sections to CHANGELOG, README, docs/UPDATE.md, metainfo.xml and the website changelog
- Bump website package metadata and softwareVersion schema
- Add the 0.18.5 row to the RELEASE_PROCESS version history

* docs(release): qualify the clipboard-history claim for wl-copy 2.2+

---------

Co-authored-by: Jatin K Malik <jatinkrmalik@gmail.com>
2026-10-26 21:40:44 -07:00
e66bd218d9 fix(audio): prevent dictation hangs on stalled microphones (#974)
* fix(audio): bound capture reads and support cancellation

* fix(recognition): bound and serialize capture shutdown

* fix(recognition): cancel retries and report stalled microphones

* fix(audio): prefer pulse when the ALSA default is unavailable

* fix(recognition): guard late callbacks and finalize capture tests

* docs(audio): explain input selection and stall recovery

* fix(speech): enforce auto-pause across buffered reload timeouts

* fix(speech): recover auto-resume after reload timeout

* test(speech): fix auto-pause follow-up test regressions

* fix(speech): retain auto-resume after reconfigure timeout

* fix(tray): preserve auto-pause recovery across suspend

* test(capture-shutdown): give the bare tray the suspend path's shortcut managers

_on_system_suspend now releases the main and per-language hotkey
listeners before sleep (#1025); the fixture builds TrayIndicator without
__init__, so it needs the two attributes present.

* fix(tray): release hotkey listeners on suspend even while auto-paused

Merging #1025's suspend cleanup with this branch's auto-pause handoff
left an early return that skipped shortcut_manager.stop() whenever a
configured game was active, keeping EVIOCGRABs held through sleep. The
paused branch now only owns the model-lifecycle work; listener release
and stopping active recognition run for every suspend.

* fix(tray): hold the apply lock only around the download's verify and save

Wrapping the whole recommended-model download in _apply_settings_lock
blocked every GTK-thread settings apply (e.g. Test Dictation) for the
length of the transfer. The engine already serializes reconfigures, so
the lock only needs to cover verifying the download still owns the
receipt and saving it; an apply that lands mid-download now proceeds
instead of freezing the app.

---------

Co-authored-by: Jatin K Malik <jatinkrmalik@gmail.com>
2026-10-26 21:25:50 -07:00
be690f0bd8 fix(installer): add upgrade mode that keeps the existing configuration (#1026)
* fix(installer): add upgrade mode that keeps the existing configuration

Re-running install.sh on an existing install replayed first-time setup:
the mode menu offered only interactive or automatic, automatic mode
forced whisper.cpp regardless of the configured engine, and nothing said
config.json would be kept, even though docs/UPDATE.md promised the
installer updates in place and preserves configuration.

Detect an existing install early (venv activate + config.json) and offer
an upgrade path:

- The curl|bash mode menu gains 'Upgrade (keep current settings)' as the
  default when an install is detected; the guided setup offers it first
  too.
- A --upgrade flag runs the same path non-interactively for scripts, and
  falls back to a normal automatic install when nothing is installed.
- Upgrade mode reads speech_recognition.engine from the existing
  config.json instead of forcing whisper.cpp, reuses the venv, updates
  that engine's dependencies, and rebuilds pywhispercpp only when the
  installed version drifts from the pinned release or GPU support is
  missing.
- config.json is never modified: every engine path that may rewrite it
  (parakeet, faster_whisper, the whisper.cpp -> vosk fallback) goes
  through a wrapper that is a logged no-op in upgrade mode, and
  verify_configured_engine reports an unimportable configured engine
  instead of repairing the file.
- The installer prints 'Existing configuration found at
  ~/.config/vocalinux/config.json - it will be kept.' when it detects
  one.

Update docs/UPDATE.md with the upgrade behavior and add
tests/test_installer_upgrade.py covering detection, the mode menu
default, engine resolution from config, config immutability, the guided
offer, --upgrade parsing, and an end-to-end pty run of the piped
installer.

Fixes #1009

* test(installer): isolate the upgrade pty test from host changes

Skip system-dependency and model steps in the end-to-end installer run
so the only host-mutating calls are gated out entirely; the timed
interrupt is no longer the only thing between the marker and sudo.

---------

Co-authored-by: Jatin K Malik <jatinkrmalik@gmail.com>
2026-10-26 20:55:39 -07:00
8f220c38f4 fix(evdev): release EVIOCGRAB when forwarding cannot run (#1025)
* fix(evdev): release EVIOCGRAB when forwarding cannot run (#1021)

With the internal evdev listener, Vocalinux holds EVIOCGRAB on physical
keyboards and re-emits through uinput clones. Two windows left the grab
held while nothing could forward:

- Suspend/resume: grabs persisted through sleep while the compositor's
  input stack was rebuilt; the listener was torn down and rebuilt ~6s
  after resume, and keystrokes typed at the GNOME lock screen in that
  window went to a stale forwarding path.
- A monitor thread wedged mid-iteration (fd, lock, or a clone write that
  never returns): grabs stayed held forever because the self-healing
  path only fired on write exceptions, and the unexpected-exit cleanup
  only ran when the thread died.

_on_system_suspend now tears the shortcut listeners down before the
machine sleeps, so no grab survives the suspend boundary; the existing
resume flow rebuilds them once /dev/input settles.

A forwarding watchdog now runs alongside the monitor. The monitor stamps
a heartbeat on every loop; if it goes stale while grab->clone pairs
exist, the watchdog runs the existing _release_failed_forwarder path on
each device so input reaches applications directly, and tracks them as
suspended. Once dispatching resumes, suspended devices are removed so
the rescan re-opens them through the standard open->clone->grab path.
Generation checks keep a recycled fd from ever being torn down under a
newer generation.

* fix(evdev): re-arm watchdog-released devices on the monitor thread

The watchdog closing a device fd races the monitor's in-flight select:
a dead descriptor in the select set trips the error handler, which
exits the loop and tears down every keyboard. Recovery now runs where
the close is safe — the monitor retires each suspended device at its
own loop top and the rescan re-opens it through the standard
open->clone->grab path. The watchdog only ungrabs (safe cross-thread:
that fd never sits in the select set) and marks the device suspended.

---------

Co-authored-by: Jatin K Malik <jatinkrmalik@gmail.com>
2026-10-26 20:42:59 -07:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>Jatin K Malik
4b0ec87f69 chore(deps): bump the pip group across 2 directories with 4 updates (#1015)
* chore(deps): bump the pip group across 2 directories with 4 updates

Bumps the pip group with 4 updates in the / directory: [setuptools](https://github.com/pypa/setuptools), [black](https://github.com/psf/black), [filelock](https://github.com/tox-dev/py-filelock) and [virtualenv](https://github.com/pypa/virtualenv).
Bumps the pip group with 4 updates in the /requirements directory: [setuptools](https://github.com/pypa/setuptools), [black](https://github.com/psf/black), [filelock](https://github.com/tox-dev/py-filelock) and [virtualenv](https://github.com/pypa/virtualenv).


Updates `setuptools` from 84.0.0 to 84.1.0
- [Release notes](https://github.com/pypa/setuptools/releases)
- [Changelog](https://github.com/pypa/setuptools/blob/main/NEWS.rst)
- [Commits](https://github.com/pypa/setuptools/compare/v84.0.0...v84.1.0)

Updates `black` from 26.5.1 to 26.10.1
- [Release notes](https://github.com/psf/black/releases)
- [Changelog](https://github.com/psf/black/blob/main/CHANGES.md)
- [Commits](https://github.com/psf/black/compare/26.5.1...26.10.1)

Updates `filelock` from 3.32.3 to 4.2.1
- [Release notes](https://github.com/tox-dev/py-filelock/releases)
- [Changelog](https://github.com/tox-dev/filelock/blob/main/docs/changelog.rst)
- [Commits](https://github.com/tox-dev/py-filelock/compare/3.32.3...4.2.1)

Updates `virtualenv` from 21.14.12 to 21.14.13
- [Release notes](https://github.com/pypa/virtualenv/releases)
- [Changelog](https://github.com/pypa/virtualenv/blob/main/docs/changelog.rst)
- [Commits](https://github.com/pypa/virtualenv/compare/21.14.12...21.14.13)

Updates `setuptools` from 84.0.0 to 84.1.0
- [Release notes](https://github.com/pypa/setuptools/releases)
- [Changelog](https://github.com/pypa/setuptools/blob/main/NEWS.rst)
- [Commits](https://github.com/pypa/setuptools/compare/v84.0.0...v84.1.0)

Updates `black` from 26.5.1 to 26.10.1
- [Release notes](https://github.com/psf/black/releases)
- [Changelog](https://github.com/psf/black/blob/main/CHANGES.md)
- [Commits](https://github.com/psf/black/compare/26.5.1...26.10.1)

Updates `filelock` from 3.32.3 to 4.2.1
- [Release notes](https://github.com/tox-dev/py-filelock/releases)
- [Changelog](https://github.com/tox-dev/filelock/blob/main/docs/changelog.rst)
- [Commits](https://github.com/tox-dev/py-filelock/compare/3.32.3...4.2.1)

Updates `virtualenv` from 21.14.12 to 21.14.13
- [Release notes](https://github.com/pypa/virtualenv/releases)
- [Changelog](https://github.com/pypa/virtualenv/blob/main/docs/changelog.rst)
- [Commits](https://github.com/pypa/virtualenv/compare/21.14.12...21.14.13)

---
updated-dependencies:
- dependency-name: setuptools
  dependency-version: 84.1.0
  dependency-type: direct:production
  dependency-group: pip
- dependency-name: black
  dependency-version: 26.10.1
  dependency-type: direct:development
  dependency-group: pip
- dependency-name: filelock
  dependency-version: 4.2.1
  dependency-type: direct:production
  dependency-group: pip
- dependency-name: virtualenv
  dependency-version: 21.14.13
  dependency-type: direct:development
  dependency-group: pip
- dependency-name: setuptools
  dependency-version: 84.1.0
  dependency-type: direct:production
  dependency-group: pip
- dependency-name: black
  dependency-version: 26.10.1
  dependency-type: direct:development
  dependency-group: pip
- dependency-name: filelock
  dependency-version: 4.2.1
  dependency-type: direct:production
  dependency-group: pip
- dependency-name: virtualenv
  dependency-version: 21.14.13
  dependency-type: direct:development
  dependency-group: pip
...

Signed-off-by: dependabot[bot] <support@github.com>

* chore(deps): sync uv.lock with dependabot pip bump

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Jatin K Malik <jatinkrmalik@gmail.com>
2026-10-26 20:40:49 -07:00
9374b86793 fix(ui): keep standalone dialogs from binding to other windows on Wayland (#1024)
On GTK's Wayland backend, a toplevel with the DIALOG type hint and no
transient parent becomes an orphan dialog: the next toplevel to receive
keyboard focus silently becomes its xdg_toplevel parent, and GNOME then
raises, minimizes, and workspace-moves the two windows as one.

Settings, the transcript viewer, and the log viewer are standalone
windows opened parentless, so the dictation pad (focused constantly)
adopted them, making Alt+Tab group them and minimize/workspace changes
apply to both. Force the NORMAL type hint on all three so they stay
independent.

Fixes #984

Co-authored-by: Jatin K Malik <jatinkrmalik@gmail.com>
2026-10-26 20:28:51 -07:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
ac783e53bd chore(deps): bump the github-actions group across 1 directory with 6 updates (#1014)
Bumps the github-actions group with 6 updates in the / directory:

| Package | From | To |
| --- | --- | --- |
| [actions/setup-python](https://github.com/actions/setup-python) | `7.0.0` | `7.1.0` |
| [actions/upload-artifact](https://github.com/actions/upload-artifact) | `7.0.1` | `7.0.2` |
| [astral-sh/setup-uv](https://github.com/astral-sh/setup-uv) | `10.2.0` | `10.3.2` |
| [actions/download-artifact](https://github.com/actions/download-artifact) | `8.0.1` | `8.0.2` |
| [pypa/gh-action-pypi-publish](https://github.com/pypa/gh-action-pypi-publish) | `1.14.2` | `1.14.3` |
| [actions/setup-node](https://github.com/actions/setup-node) | `7.0.0` | `7.1.0` |



Updates `actions/setup-python` from 7.0.0 to 7.1.0
- [Release notes](https://github.com/actions/setup-python/releases)
- [Commits](https://github.com/actions/setup-python/compare/5fda3b95a4ea91299a34e894583c3862153e4b97...6d4fd22b20465ccc0f7354476588e8df7c9ca0e0)

Updates `actions/upload-artifact` from 7.0.1 to 7.0.2
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](https://github.com/actions/upload-artifact/compare/043fb46d1a93c77aae656e7c1c64a875d1fc6a0a...cf430e030ddbb5b0abf93d22962f4752f3646cd9)

Updates `astral-sh/setup-uv` from 10.2.0 to 10.3.2
- [Release notes](https://github.com/astral-sh/setup-uv/releases)
- [Commits](https://github.com/astral-sh/setup-uv/compare/c18668ad3cf93ea998bef934396af7bb5c839dc7...07a047e608db84b3d8c06cd9a7c7dcc9c355c18e)

Updates `actions/download-artifact` from 8.0.1 to 8.0.2
- [Release notes](https://github.com/actions/download-artifact/releases)
- [Commits](https://github.com/actions/download-artifact/compare/3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c...9000827ccba6bdab643e8b6fd33ac0654aef8333)

Updates `pypa/gh-action-pypi-publish` from 1.14.2 to 1.14.3
- [Release notes](https://github.com/pypa/gh-action-pypi-publish/releases)
- [Commits](https://github.com/pypa/gh-action-pypi-publish/compare/dc37677b2e1c63e2034f94d8a5b11f265b73ba33...7245906fb61fa33a13554f9d5fb856fe61758086)

Updates `actions/setup-node` from 7.0.0 to 7.1.0
- [Release notes](https://github.com/actions/setup-node/releases)
- [Commits](https://github.com/actions/setup-node/compare/820762786026740c76f36085b0efc47a31fe5020...949feb2413d6458794dcd2491c4babbbce0c15c1)

---
updated-dependencies:
- dependency-name: actions/download-artifact
  dependency-version: 8.0.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: github-actions
- dependency-name: actions/setup-node
  dependency-version: 7.1.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: github-actions
- dependency-name: actions/setup-python
  dependency-version: 7.1.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: github-actions
- dependency-name: actions/upload-artifact
  dependency-version: 7.0.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: github-actions
- dependency-name: astral-sh/setup-uv
  dependency-version: 10.3.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: github-actions
- dependency-name: pypa/gh-action-pypi-publish
  dependency-version: 1.14.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: github-actions
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-10-26 20:26:08 -07:00