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
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
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
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
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.
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
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
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
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.
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.
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)
* 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).
* 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
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.
* 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>
* 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>
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.
* 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.
* 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.
* 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.
* 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>
* 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>
* 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>
* 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>
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>
* fix(aur): gate source publication on attested sdists
Prepare and verify the canonical release source, build the exact PKGBUILD on Arch, and publish the gated artifact without further rewrites. Part of #701.
* fix(aur): isolate local gate and binary publication
Keep local gate distributions and diagnostics in a private workspace, preserving other build outputs and failed diagnostics. Publish vocalinux-bin after its own AppImage inputs without depending on the source gate or unrelated release jobs. Add regression coverage for both review findings.
* fix(clipboard): keep paste-injection copies out of clipboard history
The clipboard-paste injection path writes the dictated text to the
clipboard only to feed the paste chord, then restores the previous
content (#588, #646). Clipboard managers such as Klipper and cliphist
still record that transient copy, so every dictation lands in history.
Mark the injection copy with `wl-copy --sensitive`, which clipboard
managers honour by skipping the entry. The flag is probed once via
`wl-copy --help` (added in wl-clipboard 2.2) so older builds keep
working. It is only set when Copy to Clipboard is off: with that
setting on, the dictated text is meant to stay, so it remains an
ordinary copy. Restoring the user's previous content is never marked
sensitive. xclip/xsel have no equivalent and are unchanged.
* test(clipboard): address review on sensitive paste copies
- Move the unittest main guard below TestSensitivePasteCopy so running
the file directly also runs the new tests.
- Add -> None return hints to the new test methods.
- Cover _copy_to_clipboard forwarding the sensitive hint when a tool
declines and the next one is tried.
- Docs: history suppression needs wl-copy 2.2+ and a clipboard manager
that honours the hint; older wl-copy and the XWayland xclip/xsel path
still produce ordinary copies.
* fix(clipboard): type the wl-copy probe cache and adapt the X11 paste test
- Declare _wl_copy_sensitive_supported as a typed class default instead
of reading it through getattr(), which mypy (now in CI since #968)
flags as returning Any.
- test_plain_x11_clipboard_paste_uses_xdotool_and_x11_clipboard
asserted the exact _copy_to_clipboard kwargs. Check the text and the
X11 tools instead, so it does not depend on the sensitive hint (which
xclip ignores) or on the copy_to_clipboard setting.
* test(clipboard): wait for the restore thread instead of a fixed sleep
TestSensitivePasteCopy slept 0.5 s and hoped the background restore had
run. On a slow runner the patches could end first. Wait on an event set
by the restore copy, inside the patches, with a generous timeout.
* fix(recognition): never lose a stop that races start, and read capture outside the buffer lock
start_recognition() published LISTENING (and played the start cue) before setting should_record. A stop arriving in that window, e.g. a push-to-talk release handled on another thread, was undone by the later should_record = True, leaving the capture thread recording with no way to stop it. The session is now armed under _buffer_lock before LISTENING is visible.
The capture loop held _buffer_lock across source.read_chunk(). When a read blocks (the audio server reconfigures the device, a USB/Bluetooth mic stalls), stop_recognition() gives up its bounded join and then blocks forever on that lock, stuck in PROCESSING. The read now happens outside the lock and the session is re-checked before the chunk is appended.
* test(recognition): wait for the stalled read before stopping
Signal from inside the blocking read instead of sleeping after the first, non-blocking one, so the test always exercises the stalled-read path.
* test(recognition): run the stalled-read test against the real NumPy
Older test modules leave sys.modules["numpy"] as a MagicMock. Under that leak the capture loop's first chunk raised before the stalled read was reached, so the test failed in the full-suite order on CI. Restore the real module for these tests, as test_playback_ducker.py already does.
---------
Co-authored-by: AAG Projects Team <293887937+aagprojectsteam-max@users.noreply.github.com>
Use the maintained AyatanaAppIndicatorGlib 2.0 indicator when it is installed, mirroring the existing Gtk.Menu into a Gio.Menu whose actions keep stable, never-reused names and fire the original items. Fall back to the GTK 3 indicators otherwise; the AppImage stays on its bundled GTK 3 indicator, and the .deb recommends the glib typelib.
Fixes#1013
Inside the snap, list the sound server's capture sources with pactl and record from the chosen one by scoping PULSE_SOURCE to each PortAudio open (serialized behind one lock). Settings saves the stable source name, keeps each dropdown entry's server name so selection never queries the server from the GTK thread, and the snap reports whisper.cpp as CPU-only instead of probing host GPU tools.
Fixes#1011
Refuse close requests on the pre-mapped Wayland overlay so closing its GNOME dock entry no longer destroys it and brings back the first-recording focus loss, and unmap the overlay when it is turned off so the dock entry goes away immediately.
Fixes#1010
* chore(deps): allow uv versions below 0.14
* fix(ci): align uv version and generated exports
* fix(dev): check Fedora build headers and AppImage GI bundle
Fail early with distro-specific native dependency hints before dev sync. Verify the AppImage's PyGObject installation and Python path after linuxdeploy, and report isolated GI import failures with actionable path diagnostics. Guard the CI uv pin and cover both preflights with tests.
* feat(build): add locked reproducible wheel gate
Build independently in clean source trees and virtualenvs, compare wheel bytes, and stage only verified canonical distributions. Safely clean recognized prior outputs on repeat runs.
Part of #701
* ci(release): verify wheel reproducibility before publishing
Run the shared gate in PR and tag workflows, preserve failure diagnostics, and keep verification artifacts out of release checksums. Make Snap Store publication wait for the gate and reuse the canonical distributions for PyPI.
Part of #701
* docs(release): document reproducible wheel validation
Describe the locked toolchain, canonical publication boundary, same-platform scope, diagnostic outputs, and safe automatic cleanup.
* chore(scripts): update executable permissions
* fix(build): isolate release tooling and preserve cleanup retries
Exclude host site-packages from build-only virtualenvs so unlocked setuptools plugins cannot affect verified distributions. Delete staged artifacts before canonical ownership evidence to recover safely from interrupted cleanup.
Add regression tests and document the build-only environment exception.
* fix(release): make reproducible builds retryable and refresh tools
Regenerate release build exports from uv.lock. Hold a checkout lock through cleanup and staging, and atomically rename complete archives into dist. Cover interrupted staging, competing processes, and lock release with regression tests.
---------
Co-authored-by: Kanishk Pachauri <itskanishkp.py@gmail.com>
Co-authored-by: sesav <1636488+sesav@users.noreply.github.com>
- Update version from 0.18.3 to 0.18.4
- Add v0.18.4 sections to CHANGELOG, README, docs/UPDATE.md, metainfo.xml and the website changelog
- Bump website package metadata and softwareVersion schema
Co-authored-by: Jatin K Malik <jatinkrmalik@gmail.com>
* feat(recognition): cancel in-progress transcription with hotkey
Adds per-session cancellation across the manager, tray, and Test Dictation.
Fixes#991
* fix(settings): keep Test Dictation cancellation responsive
Fixes#991
* fix(recognition): keep late workers cancellable (#991)
* test(tray): avoid early tray import in cancellation tests
* fix(recognition): keep a cancelled capture thread out of the next session
A cancel returns to IDLE without joining the capture thread, so a thread
still in its reconnection backoff could wake inside a newer recording,
reopen that session's device and write into its buffer. Tie the capture
loop and the reconnection to the session's cancel token: a cancelled or
replaced session stops reading, skips the reopen and exits without
raising an error.