Skip to content

v1.5.0: UI redesign, issue #19 fixes, Linux audio fix - #20

Merged
infiniV merged 18 commits into
mainfrom
fix/issue-19-various-issues
Apr 29, 2026
Merged

infiniV merged 18 commits into
mainfrom
fix/issue-19-various-issues

Conversation

@infiniV

@infiniV infiniV commented Apr 29, 2026 •

Copy link
Copy Markdown
Owner

Summary

  • v1.5.0 UI redesign (manifest aesthetic, dark mode default)
  • Hotkey, link, and big-model download fixes for issue Various issues #19
  • GPU library preload (libcublas) fix for Linux
  • SEO/metadata refresh, Linux + website URLs added
  • Linux audio fix: only remove `_internal/libasound*` in CI; keep `av.libs/libasound-cfbebb71.so.2.0.0` (PyAV needs it). Removing the latter crashes startup with ImportError on libasound-cfbebb71.

Root cause of the recurring 'no mics in build' bug

Bundled libasound has its ALSA plugin search path hardcoded at build time to the build host's path (Ubuntu CI: `/usr/lib/x86_64-linux-gnu/alsa-lib`). On other distros (Arch: `/usr/lib/alsa-lib`) the plugin path doesn't exist, so libasound can't load `libasound_module_pcm_pipewire.so` or the pulse plugin → PortAudio reports zero PipeWire/Pulse-routed input devices. Removing `_internal/libasound*` makes the loader fall through to the user's system libasound, which knows where its own plugins live.

Test plan

  • CI green (linux + windows + release)
  • Verified extracted tarball ships only `av.libs/libasound-cfbebb71.so.2.0.0` (no `_internal/libasound*`, no `_internal/libportaudio*`)
  • Launched new binary on Arch + Hyprland + PipeWire; `/proc/PID/maps` shows `/usr/lib/libasound.so.2.0.0`, `/usr/lib/libportaudio.so.2.0.0`, and `/usr/lib/alsa-lib/libasound_module_pcm_pipewire.so` all loaded
  • Manual mic dropdown verified by maintainer

Summary by CodeRabbit

  • New Features

    • Manual recording toggle from the UI interface
    • Hotkey availability status notification banner
    • Live history updates while recording
    • Model cache directory browser and management
    • Recording state display and monitoring
    • Redesigned onboarding and guided setup experience
  • Enhancements

    • Dark mode now default theme
    • Revamped settings interface with streamlined model selection
    • Improved history page with search and date grouping
    • Enhanced Linux support for CUDA acceleration with CPU fallback
    • Wayland and X11 support on Linux
    • Updated popup interface with recording indicators
  • Version Update

    • Bumped to version 1.5.0

infiniV added 18 commits April 1, 2026 00:38
cpython-3.12.13 from python-build-standalone ships libpython with
GNU_STACK RWE flag, which Linux 6.8+ kernels reject. Python 3.13
builds do not have this flag.
python-build-standalone ships libpython with GNU_STACK RWE flag
which Linux 6.8+ kernels reject. Revert to Python 3.12 and use
execstack -c to clear the flag after PyInstaller bundles the app.
sounddevice loads libportaudio.so at runtime via ctypes. On Arch
this is pre-installed, but on Ubuntu 22.04 CI it's missing, so
PyInstaller never bundles it and audio devices fail to open.
Ubuntu 22.04's PortAudio only has the ALSA backend, so the bundled
library only sees raw hw: devices which don't support 16kHz. The
system's PortAudio on modern Linux has PipeWire/PulseAudio backends
that handle sample rate conversion and show all virtual devices.
Some audio devices (raw ALSA hw: devices, or when PipeWire ALSA
plugin isn't available) don't support 16kHz natively. Now tries
16kHz first, falls back to the device's default rate, and resamples
to 16kHz for Whisper using linear interpolation.
model_manager: fix DownloadProgressBar so byte progress reports during
multi-GB downloads. tqdm with disable=None auto-disables when stderr
isn't a TTY (every packaged GUI build), which silently drops self.n
increments and self.unit. That made big models look frozen at 0% for
minutes - the "won't download" complaint. Track bytes via _vf_n
counter and force disable=False. Real-network test confirms base
(145MB) now reports 9 smooth byte updates vs 5 file-count jumps.

model_manager: add get_cache_dir() so the UI can surface the resolved
HF cache path.

hotkey: add manual_start/manual_stop and get_status(). manual_start
reuses the toggle-mode bookkeeping so transcription, paste, and history
behave identically to a hotkey-driven session. get_status surfaces
evdev permission errors when the user isn't in the input group on
Wayland - silent failures previously made hotkeys appear broken.

app_controller: manual_toggle_recording wraps the hotkey activate/
deactivate flow.

server: open_external_url uses QDesktopServices.openUrl first, then
non-blocking subprocess.Popen. The previous subprocess.run with
check=True failed on KDE Plasma because xdg-open forks to kde-open5
and exits non-zero even on success, breaking "Report Issue" and
"Star on GitHub" links.

server: expose manual_toggle_recording, get_recording_state,
get_hotkey_status, get_model_cache_dir, open_model_cache_dir; add
api.ts wrappers.

tests: add real-network download tests gated on
VOICEFLOW_NETWORK_TESTS=1 (tiny + base, ~71s) and big-model tests on
VOICEFLOW_BIG_MODEL_TESTS=1 (turbo). White-box regression test
verifies _vf_n counter works when tqdm is disabled.

tests(audio): reference TARGET_SAMPLE_RATE - the attr was renamed in
d5ecc49 when the audio service gained the device-rate fallback.

pyproject: add pytest network/big_model markers.
Comprehensive UI overhaul aligning the app with an editorial "engineering
manifest" voice — flat surfaces, hairline dividers, mono accents, and
chapter-marker section eyebrows replace the previous bento-card grid.

Pages:
- SettingsTab: spec-sheet model picker (size/VRAM/speed/accuracy dots),
  segmented retention, 3-chip theme picker, GPU manifest panel, full
  danger-zone redesign
- HomePage: log-row layout (time + text + hover actions) replaces the
  card grid; StatsHeader rebuilt as 4-stat manifest with today vs
  lifetime cuts
- HistoryPage: full redesign matching HomePage's log aesthetic with a
  mono "searching..." indicator for server-side search
- Onboarding: 7 steps cleaned of glass-card/rounded-2xl/tinted-icon-square
  patterns; cross-platform copy ("Launch at login" replaces Windows-only
  text); cuDNN download flow refined
- Dashboard shell: mobile header brandmark aligned with desktop sidebar;
  HotkeyStatusBanner pinned (no longer scrolls with content)
- Sidebar: eyebrow tracking aligned to manifest convention
- ModelDownloadModal: shadcn defaults instead of glass-strong shim

Theme:
- Dark mode is now the default across index.html, settings.py defaults,
  and Onboarding initial state
- Removed legacy glass-card/glass-strong/rounded-xl-2xl-3xl chrome
- Standardized on rounded-md/lg radius tokens
- Mono accents use JetBrains Mono with tracking-[0.25em] eyebrows

Cleanup:
- Deleted dead HistoryTab.tsx
- Removed 6 outdated marketing screenshots; added 3 new (dashboard,
  onboarding, model-picker) showing the redesigned UI
- README hero now opens with the actual app dashboard

Bumps version to 1.5.0 across package.json, pyproject.toml,
installer/voiceflow.iss, src/lib/constants.ts, and the sidebar footer.
CI's stricter TypeScript caught a mismatch between my validateHotkey
callback (returning error?: string) and HotkeyCapture's onValidate prop
(expecting error: string | null). Add explicit return type and update
the HotkeysPanel/HotkeyMode prop signatures to match.
…oject metadata

- pyproject.toml: rewrite description, add keywords, classifiers, project URLs
- VoiceFlow.desktop: add GenericName, Keywords, expand Categories
- voiceflow.iss: replace placeholder URL with get-voice-flow.vercel.app, split support/updates URLs
- AppStream metainfo for Linux app store indexing (Flathub/GNOME Software/KDE Discover)
- README: new SEO opening line, promote website link, drop hardcoded v1.3.1 to /releases/latest
- site.webmanifest: add description, dark theme/background colors
- index.html: add description meta + theme-color
macOS still works unofficially but isn't an officially supported platform yet.
Removed macOS from public-facing descriptions across pyproject, .desktop,
AppStream metainfo, README, site.webmanifest, and bundled index.html.

Also rewrote descriptions to drop AI-flavored patterns (em-dash overuse,
rule-of-three chants, promotional triplets like "no cloud, no telemetry,
no subscription") in favor of plainer wording.
ctranslate2 4.x has no Linux init code that adds the nvidia-cublas-cu12
package's lib dir to the dynamic linker path, so libcublas.so.12 was
unloadable in both the venv and the AppImage — silently falling back to
CPU even when CUDA was technically available.

Add _preload_nvidia_libs() that imports the nvidia namespace package and
ctypes.CDLL each .so under nvidia/<pkg>/lib/ with RTLD_GLOBAL. This makes
the symbols globally visible so libctranslate2's dlopen() resolves them.

Also add --collect-all=nvidia to the Linux PyInstaller invocation so the
nvidia pip packages actually ship in the AppImage.
A prior edit inserted [project.urls] before requires-python and
dependencies, silently nesting them under [project.urls]. uv warned
'No requires-python value found in the workspace' and skipped installing
pyloid, breaking both Linux and Windows CI builds.

Restore correct TOML structure (all [project] fields first, then
[project.urls]) and regenerate uv.lock against the fixed manifest.
…ound

Bundled libasound has its ALSA plugin search path hardcoded to the build
host's path (Ubuntu CI: /usr/lib/x86_64-linux-gnu/alsa-lib). On other
distros (Arch: /usr/lib/alsa-lib) this path doesn't exist, so libasound
can't load libasound_module_pcm_pipewire.so / pulse plugin and PortAudio
silently reports zero virtual input devices — the recurring 'no mics in
build' bug.

Removing _internal/libasound* makes the loader fall through to the user's
system libasound, which knows its own plugin layout. Do NOT delete
av.libs/libasound-cfbebb71.so.2.0.0 — PyAV's libavdevice has that exact
name in its NEEDED list (different SONAME, doesn't conflict with
PortAudio's libasound.so.2 lookup). Removing it crashes startup with
ImportError on libasound-cfbebb71.
@infiniV
infiniV merged commit f907b32 into main Apr 29, 2026
3 of 4 checks passed
@coderabbitai

coderabbitai Bot commented Apr 29, 2026 •

Copy link
Copy Markdown

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 0fd67862-6102-4cc6-9ae5-ed1d503f697f

📥 Commits

Reviewing files that changed from the base of the PR and between 075a2f6 and b801e13.

⛔ Files ignored due to path filters (10)
  • media/app-dash.png is excluded by !**/*.png
  • media/comparison.png is excluded by !**/*.png
  • media/dashboard.png is excluded by !**/*.png
  • media/footer.png is excluded by !**/*.png
  • media/hero.png is excluded by !**/*.png
  • media/hotkeys.png is excluded by !**/*.png
  • media/how-it-works.png is excluded by !**/*.png
  • media/model-picker.png is excluded by !**/*.png
  • media/onboarding.png is excluded by !**/*.png
  • uv.lock is excluded by !**/*.lock
📒 Files selected for processing (33)
  • .github/workflows/release.yml
  • README.md
  • index.html
  • installer/VoiceFlow.desktop
  • installer/io.github.infiniv.VoiceFlow.metainfo.xml
  • installer/voiceflow.iss
  • package.json
  • public/site.webmanifest
  • pyproject.toml
  • src-pyloid/app_controller.py
  • src-pyloid/build/build.py
  • src-pyloid/server.py
  • src-pyloid/services/gpu.py
  • src-pyloid/services/hotkey.py
  • src-pyloid/services/model_manager.py
  • src-pyloid/services/settings.py
  • src-pyloid/tests/test_audio.py
  • src-pyloid/tests/test_model_manager.py
  • src/components/HistoryPage.tsx
  • src/components/HistoryTab.tsx
  • src/components/HomePage.tsx
  • src/components/HotkeyStatusBanner.tsx
  • src/components/ModelDownloadModal.tsx
  • src/components/SettingsTab.tsx
  • src/components/Sidebar.tsx
  • src/components/StatsHeader.tsx
  • src/index.css
  • src/lib/api.ts
  • src/lib/constants.ts
  • src/lib/utils.ts
  • src/pages/Dashboard.tsx
  • src/pages/Onboarding.tsx
  • src/pages/Popup.tsx

📝 Walkthrough

Walkthrough

Version 1.5.0 release: extends Linux support with ALSA bundling cleanup and NVIDIA CUDA preloading, introduces UI-driven recording control and hotkey status monitoring via new RPC endpoints, significantly refactors React components (history, home, settings, popup, onboarding) with new internal layouts, updates project metadata across multiple formats, and improves model download progress reporting and default theming.

Changes

Cohort / File(s) Summary
Linux Release & Build
.github/workflows/release.yml, src-pyloid/build/build.py, src-pyloid/services/gpu.py
Linux workflow cleanup now removes bundled ALSA (libasound*) in addition to PortAudio, with exception for PyAV's libasound-cfbebb71.so.2.0.0; build script collects nvidia-* packages; GPU module adds Linux-only NVIDIA CUDA library preloading from pip wheels.
Project Metadata & Versioning
package.json, pyproject.toml, src/lib/constants.ts, installer/voiceflow.iss, installer/VoiceFlow.desktop, installer/io.github.infiniv.VoiceFlow.metainfo.xml, public/site.webmanifest
Version bumped to 1.5.0 across all configs; desktop entry metadata enhanced with GenericName, description, keywords, and categories; new AppStream metainfo XML added; web manifest adds description and dark theme colors (#09090b); Windows installer updates publisher to infiniV and adds support/updates URLs.
README & Documentation
README.md
Positions VoiceFlow as offline dictation app for Windows/Linux, updates Linux section from experimental v1.3.2 to non-experimental with Wayland/X11/CUDA support, reorganizes download badges to "latest" releases with new "Visit Website" link, replaces illustrative screenshots with onboarding/setup content, expands Neural Engine section with model picker details.
Backend Recording & Hotkey Control
src-pyloid/app_controller.py, src-pyloid/server.py, src-pyloid/services/hotkey.py
New controller methods expose recording toggle and state polling; server RPC layer exposes manual recording control, hotkey status diagnostics, and model cache operations; hotkey service tracks availability via _status dict and adds manual_start() / manual_stop() methods for UI-driven control.
Model Management & Download
src-pyloid/services/model_manager.py
Adds get_cache_dir() for Hugging Face cache resolution; refactors progress reporting by clamping byte drift and reworking tqdm DownloadProgressBar to reliably emit byte-level progress in non-TTY/GUI environments via internal _vf_n tracking.
Settings & Defaults
src-pyloid/services/settings.py
Default theme changed from "system" to "dark" in both dataclass and fallback logic.
Frontend API Integration
src/lib/api.ts, src/lib/utils.ts
API layer extended with manualToggleRecording(), getRecordingState(), getHotkeyStatus(), getModelCacheDir(), openModelCacheDir() RPC methods; new formatHotkeyForDisplay() utility added.
Frontend HTML & Styling
index.html, src/index.css
HTML root defaults to dark mode (class="dark"); head metadata expanded with app name, description, theme-color, and color-scheme; CSS replaced with flat VoiceFlow brand aesthetic (new tokens, removed decorative effects/animations, added layout primitives, forced orb/glass classes to no-op, WebKit scrollbar styling).
Frontend Component: History
src/components/HistoryPage.tsx, src/components/HistoryTab.tsx
HistoryTab removed entirely; HistoryPage refactored with memoized grouping, dedicated search/loading states, new internal subcomponents (SearchBar, skeleton views, row/section renderers, AudioPlayerDialog), 24-hour time formatting, and lowercase date labels.
Frontend Component: Home Page
src/components/HomePage.tsx
Adds recording state polling (1s interval), centralizes toggle via api.manualToggleRecording(), refactors UI with memoization and new internal components for header/search/history/dialog; updates styling to custom elements and lowercase date labels.
Frontend Component: Sidebar
src/components/Sidebar.tsx
Fetches user hotkey settings on mount, computes and displays active hotkey in footer (with fallback), removes decorative orb and static "Pro Tip" card, updates logo/nav/version (v1.5.0) and styling.
Frontend Component: Hotkey Status Banner
src/components/HotkeyStatusBanner.tsx
New component that fetches hotkey status (10s poll), renders warning banner when unavailable, supports localStorage-based dismissal with status code tracking.
Frontend Component: Settings
src/components/SettingsTab.tsx
Major refactor: adds refs to preserve callback state, async cache/model info preloading, optimistic local updates with API persistence, new section-based layout replacing bento-grid, new helper components (model picker, compute panel, hotkeys panel, storage viewer, DangerZone dialog), preserves deletion operations.
Frontend Component: Stats Header
src/components/StatsHeader.tsx
Accepts optional todayStats props, renders compact grid with StatCells (words, entries, characters, streak) and deltas, removes old icon-based hero UI, updates config display to lowercase/formatted values.
Frontend Component: Model Download Modal
src/components/ModelDownloadModal.tsx
Simplifies DialogContent styling, restructures header with status label/title/description mentioning Hugging Face and local caching, renders progress component directly.
Frontend Component: Onboarding
src/pages/Onboarding.tsx
Extensive refactor: removes multiple lucide icons, updates typography/styling to new "accent/cream" tokens and border/surface layouts; refactors StepAudio (meter UI), StepHardware (device labels, new HardwareDetailsPanel), StepModel (grid layout, lowercase labels), StepTheme ("Launch at login" wording), StepFinal (styling); changes default theme to "dark", updates progress buttons with accessibility attributes.
Frontend Component: Popup
src/pages/Popup.tsx
Converts static JSX to Framer Motion transitions (AnimatePresence with idle/recording/processing states); adds animated dot/bars for recording, spinner/wave-dots for processing, periodic model sync (10s poll), conditional compact model label display, reduced-motion support.
Frontend Component: Dashboard
src/pages/Dashboard.tsx
Adds HotkeyStatusBanner above scrollable Routes; converts mobile header to <header> with ARIA attributes (aria-expanded, aria-controls), updates branding/icon presentation, assigns stable sheet ID.
Testing
src-pyloid/tests/test_audio.py, src-pyloid/tests/test_model_manager.py
Audio test validates TARGET_SAMPLE_RATE (16000); extensive new model manager integration/unit tests added for progress reporting with byte-level assertions, cancellation verification, and tqdm byte-counter validation (gated by environment variables).

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Poem

🐰 From buttons clicked to hotkeys sung,
We bundle CUDA, dark themes flung—
The UI springs with Framer's dance,
While Linux gains its loving glance. ✨
A rabbit's work: the code now bright,
Version 1.5, polished just right! 🎤

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/issue-19-various-issues

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
Review rate limit: 0/1 reviews remaining, refill in 60 minutes.

Comment @coderabbitai help to get the list of available commands and usage tips.

@infiniV
infiniV deleted the fix/issue-19-various-issues branch April 29, 2026 07:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant