Commit graph

107 commits

Author SHA1 Message Date
Marcelo Terreiro Prado
f6e71862ff
fix: show friendly error when no audio input device is available (#1115) 2026-03-23 07:53:55 +08:00
Fero
17277cf6ce
Save recordings before transcription (#1024)
* save recordings before transcription

* impart cj preferences

* cleanup

* re-transcribe anything

* format

* fix tests

* format

* ui cleanup

* format

* cleanup

* bunch of fixes, mainly for vm

* put comments back

* update transcription failure text

* alert -> toast

* spawn_blocking

---------

Co-authored-by: CJ Pais <cj@cjpais.com>
2026-03-22 15:43:02 +08:00
Viren Mohindra
58cda3f3b3
fix: sha256 verification to prevent corrupt partial download loop (#1095)
* fix: add SHA256 verification to prevent corrupt partial download loop

Fixes issue #858 — corrupt .partial files caused an infinite loop where
a failed/cancelled download would resume from EOF, pass extraction, and
loop forever with no way to recover without manual file deletion.

Three-part fix:
- Add sha2 crate and compute_sha256() helper; verify hash post-download
  before extraction/rename for all 15 built-in models. Hash mismatch
  deletes the .partial so the next attempt starts fresh.
- Delete .partial on extraction failure (independent of checksums) so
  corrupt archives can't re-enter the loop via the same path.
- Emit model-download-failed event from the command handler so all
  callers (ModelsSettings, Onboarding) get consistent error feedback
  via a central store listener rather than per-caller return-value checks.

SHA256 hashes hardcoded alongside each model's URL in ModelInfo.
Custom models (sha256: None) skip verification silently.

* chore: remove doc comment from sha256 field

Doc comments on exported specta types propagate into bindings.ts as
inline JSDoc — not useful in generated TS. Removing at the source.

* test: add SHA256 verification unit tests

Extracts inline verify logic into ModelManager::verify_sha256() and
covers all four branches:
- None expected hash → skipped (custom models)
- Matching hash → ok, file kept
- Mismatched hash → error returned, partial file deleted
- Unreadable file → error returned

Ensures corrupt partial downloads are always cleaned up so the next
attempt starts fresh.

* chore: cargo fmt

* fix: clear cancel flag on sha256 failure, spawn_blocking for hash, fallback cleanup

- remove cancel_flags entry on sha256 failure to prevent a stale flag
  from immediately cancelling the next retry attempt (HIGH)
- add fallback state cleanup in modelStore when result.status != ok,
  so spinner clears even if model-download-failed event is missed (HIGH)
- run compute_sha256 in spawn_blocking to avoid stalling the async
  executor while hashing large model files (up to 1.6 GB) (MEDIUM)

* emit some more events for the ui to pick up

* add translations + format

---------

Co-authored-by: CJ Pais <cj@cjpais.com>
2026-03-21 14:19:26 +08:00
CJ Pais
e35f0a714e
improve history performance (#1107)
* paginate history

* fixes

* fix

* format

* remove unecessary key
2026-03-21 12:06:29 +08:00
CJ Pais
a3015026a0
whisper-rs 0.16.0 and ort.rc12 (#1041)
* test build for 0.16.0

* macos 10.15?

* format

* shorter dir for build on windows?

* Update build.yml

* move

* Update build.yml

* long path support

* clang

* try

* try

* move to ort rc12

* use ms prebuilt?

* use lower version for 1.23.1

* download my prebuilt

* Update build.yml

* Update build.yml

* fix?

* fix(nix): use dynamic linking for ort-sys 2.0.0-rc.12

ort-sys rc.12 removed pkg-config support; without ORT_PREFER_DYNAMIC_LINK
it defaults to static linking against ORT_LIB_LOCATION, which fails
because nixpkgs only provides shared libraries (.so).

* fix(nix): override onnxruntime to 1.24.2 for ort-sys rc.12 compatibility

ort 2.0.0-rc.12 enables API v24 by default, but nixpkgs only ships
onnxruntime 1.23.2 (API v23), causing a runtime panic on model load.

Use Microsoft's prebuilt binaries for onnxruntime 1.24.2 via overlay,
patched with autoPatchelfHook for NixOS compatibility.

This overlay should be removed once nixpkgs merges onnxruntime ≥ 1.24:
https://github.com/NixOS/nixpkgs/pull/499389

* updated deps for whisper

* fix(nix): sync devShell with packages for ort-sys rc.12 compatibility

- Extract shared definitions (commonNativeDeps, gstPlugins, commonEnv,
  onnxruntimeOverlay) to avoid duplication between packages and devShells
- Add onnxruntime 1.24.2 overlay to devShell (was only in packages)
- Add BINDGEN_EXTRA_CLANG_ARGS to devShell so bindgen can find stdio.h
  and generate correct vulkan bindings for whisper-rs-sys
- Add ORT_LIB_LOCATION and ORT_PREFER_DYNAMIC_LINK to devShell
- Replace libappindicator with libayatana-appindicator in devShell
- Add onnxruntime and vulkan-loader to devShell LD_LIBRARY_PATH

* fix missing lib in the handy blob dl

* scope lib verification

* dont run bun2nix on windows

---------

Co-authored-by: Evgeny <evgeny.khudoba@yandex.ru>
2026-03-19 15:00:51 +08:00
Viren Mohindra
cb32d35b9e
feat(audio): lazy stream close for bluetooth mic latency (#747)
* feat(audio): opt-in lazy stream close for bluetooth mic latency

keep the microphone stream open for 30s after recording stops to
reduce latency on back-to-back transcriptions. gated behind an
experimental setting (off by default) since it keeps the mic actively
capturing while idle — degrading bluetooth audio quality on macOS.

adds lazy_stream_close setting, tauri command, toggle in experimental
settings section, and i18n strings.

* fix: gate lazy close on setting in cancel_recording path

cancel_recording was unconditionally calling schedule_lazy_close,
keeping the mic open for 30s even when the setting was disabled.
also bump close_generation on AlwaysOn→OnDemand switch to cancel
any stale lazy close timers.

* feat(audio): opt-in lazy stream close for bluetooth mic latency

keep the microphone stream open for 30s after recording stops to
reduce latency on back-to-back transcriptions. gated behind an
experimental setting (off by default) since it keeps the mic actively
capturing while idle — degrading bluetooth audio quality on macOS.

adds lazy_stream_close setting, tauri command, toggle in experimental
settings section, and i18n strings.

* fix: gate lazy close on setting in cancel_recording path

cancel_recording was unconditionally calling schedule_lazy_close,
keeping the mic open for 30s even when the setting was disabled.
also bump close_generation on AlwaysOn→OnDemand switch to cancel
any stale lazy close timers.

* chore: fix cargo fmt on model.rs line from main merge

* fix race

---------

Co-authored-by: CJ Pais <cj@cjpais.com>
2026-03-19 14:12:43 +08:00
Viren Mohindra
095f4ac455
fix: prevent idle watcher from unloading model during recording (#1085)
* fix: prevent idle watcher from unloading model during recording

the idle watcher treats ModelUnloadTimeout::Immediately as a 0s
timeout, causing idle_ms > 0 to always be true. this unloads the
model on the next 10s tick — even while the user is still recording.

the Immediately variant is already handled correctly by
maybe_unload_immediately() which fires after each transcription
completes. the idle watcher should skip it entirely.

fixes regression introduced in d1da935.

* fix race condition

* cleanup

* format

---------

Co-authored-by: CJ Pais <cj@cjpais.com>
2026-03-19 13:45:09 +08:00
CJ Pais
2eeb21296d
upgrade path from old giga-am to new (#1088) 2026-03-18 23:20:27 +08:00
CJ Pais
5a3e6e33d1
add extra recording buffer (#1089)
* add extra recording buffer

* translations
2026-03-18 21:00:24 +08:00
Viren Mohindra
d1da935479
fix: auto-unload model after idle timeout to reduce memory (#1051)
- Default model_unload_timeout from Never to Min5
- Fix Drop impl: use take() on watcher handle so clones from
  initiate_model_load() don't kill the watcher thread
- Reset last_activity on model load to prevent immediate unload
- Upgrade watcher logging from debug to info level
- Remove duplicate "unloaded" event (unload_model already emits it)

Co-authored-by: CJ Pais <cj@cjpais.com>
2026-03-17 12:51:15 +08:00
CJ Pais
cafc2b7208
experimental: pick between cpu/gpu acceleration + enable directml on windows (#1058)
* first pass at gpu acceleration

* unload the model when changing

* mock accelerator

* translations and build fix

* format

* clean up helptext

* type cleanup

* format

* lint
2026-03-16 20:41:30 +08:00
Ivan Zhuravlev
f8bbcd791c
Migrate to transcribe-rs-0.3.1 and add Canary support (#1023)
* refactor: migrate transcribe-rs from v0.2.8 to v0.3.0

Breaking API migration:
- engines::* → onnx::* module paths, whisper_cpp::* for Whisper
- TranscriptionEngine trait → SpeechModel trait
- Two-step new()+load_model() → one-step Model::load()
- transcribe_samples(Vec<f32>) → transcribe(&[f32])
- Explicit unload_model() → RAII drop
- Canary engine now from transcribe-rs instead of local crate

Removes canary-engine local crate dependency.
Uses path dependency to local transcribe-rs clone (temporary).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* feat: upgrade transcribe-rs to v0.3.1, add Canary models

Update transcribe-rs dependency from v0.2.8 to v0.3.1 with
restructured features (whisper + onnx). Add Canary 180M Flash
(4 languages, 146MB) and Canary 1B v2 (25 EU languages, 692MB)
with translation support. Add i18n translations for all 17 locales.

Languages verified against HuggingFace model cards:
- Canary 180M Flash: en, de, es, fr
- Canary 1B v2: bg, hr, cs, da, nl, en, et, fi, fr, de, el, hu,
  it, lv, lt, mt, pl, pt, ro, sk, sl, es, sv, ru, uk

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* make sure when changing models the language changes with it

* format

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: CJ Pais <cj@cjpais.com>
2026-03-16 16:31:44 +08:00
CJ Pais
30a940629a
be able to change models from the tray (#1002)
* be able to change models from the tray

* persist at the right times

* fixes

* fix tests
2026-03-14 12:19:06 +08:00
CJ Pais
46fd170d69
attempt to clean up warnings on build (#1037)
* attempt to clean up warnings on build

* format

* fix linux warning
2026-03-14 11:42:42 +08:00
Javier Campanini
85a8ed77b5
feat: pass custom words as Whisper initial_prompt instead of post-correction (#1035)
Whisper supports an `initial_prompt` parameter that biases the model's
vocabulary during transcription. For Whisper models, we now join the
user's custom words list into a comma-separated string and pass it as
the initial_prompt. This guides the model to prefer those spellings
during decoding rather than relying solely on fuzzy post-correction.

For non-Whisper engines (Parakeet, Moonshine, SenseVoice, GigaAM),
the existing apply_custom_words post-correction continues to apply
since those engines don't support prompt-based vocabulary biasing.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-14 09:37:46 +08:00
Fero
785c3317e4
Handle microphone init failure without aborting (#945)
* Handle microphone start failures without aborting

Return recorder init errors instead of panicking/aborting in the worker thread and reset UI state when recording start fails.

Refs #436

* prop errors to ui

* format

* Add missing recording error translation keys

---------

Co-authored-by: CJ Pais <cj@cjpais.com>
2026-03-10 13:07:21 +08:00
CJ Pais
615b3c93bb
feat: language-aware filler word removal (#971)
* Remove filler words that conflict with European languages

Removed three filler words from the transcription filter that are actual
words in European languages:

- 'um' - Portuguese/Spanish indefinite article meaning 'a/an' (masculine)
  Example: Portuguese 'Isto é um teste' (This is a test)
  Removing this was breaking Portuguese transcriptions

- 'ha' - Spanish/Italian/Norwegian/Swedish auxiliary verb meaning 'has/have'
  Example: Spanish 'Él ha comido' (He has eaten)
  This is a very common verb form in Romance and Scandinavian languages

- 'eh' - Italian interjection and Canadian English discourse marker
  While less critical, this can appear in legitimate Italian speech

The remaining filler words are primarily English
vocalized hesitations that don't conflict with common words in other
European languages.

Updated tests to use 'uhm' instead of 'um' where needed.

Fixes #941

* customize list

---------

Co-authored-by: Alexander Yastrebov <yastrebov.alex@gmail.com>
2026-03-06 20:24:29 +08:00
Shehab Tarek
10beb60d63
feat: add portable mode to NSIS installer (#807)
* ci: add temporary test workflow for portable ZIP validation

* ci: fix mock setup in test workflow

* ci: remove temporary test workflow

* feat: add portable mode option to NSIS installer

Custom NSIS template based on tauri-v2.9.1 upstream with 6 targeted
modifications for portable mode support:

- New PortableMode variable and /PORTABLE CLI flag for silent installs
- Install type selection page with Normal/Portable radio buttons
- Conditional default directory (Desktop for portable, LocalAppData for normal)
- Skip registry writes, file associations, deep links, and uninstaller for portable
- Skip Start Menu and desktop shortcuts for portable installs
- Create portable marker file and Data/ directory on portable install

The existing portable.rs runtime detection is unchanged — it looks for the
same marker file this installer creates.

Usage:
  GUI: select "Portable Installation" on the install type page
  Silent: Handy_setup.exe /S /PORTABLE /D=C:\path\to\Handy

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* feat: implement true portable mode via source code path override

Detect a `portable` marker file next to the executable at startup.
When present, redirect all data paths (settings, models, recordings,
database, logs) to a local `Data/` directory instead of %APPDATA%.

This approach works because it intercepts path resolution at the Rust
level, bypassing Tauri/Windows Shell API limitations that made the
previous environment-variable and junction approaches unreliable.

Changes:
- Add src-tauri/src/portable.rs with OnceLock-based detection
- Replace all app.path().app_data_dir() calls with portable helper
- Configure tauri-plugin-log to use Folder target when portable
- Configure tauri-plugin-store with absolute path when portable
- Simplify build.yml portable ZIP (exe + resources + marker file)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: add missing Manager trait import in portable.rs

The .path() method on AppHandle requires `tauri::Manager` in scope.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: redirect WebView2 cache to portable Data dir

Move main window creation from tauri.conf.json to setup closure so
we can set data_directory when in portable mode. This prevents
WebView2 from creating %LOCALAPPDATA%\com.pais.handy.

Also set data_directory on the recording overlay window.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: auto-detect portable mode during updates

When the Tauri updater runs the installer with /UPDATE, the install type
page is skipped and /PORTABLE is not passed. Detect the existing portable
marker file in .onInit so updates preserve portable mode automatically.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* style: fix prettier formatting in portable.rs and lib.rs

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: CJ Pais <cj@cjpais.com>
2026-03-06 12:07:11 +08:00
pantafive
ff8612297b
feat: add GigaAM v3 for Russian speech recognition (#913)
* feat: add GigaAM v3 model for Russian speech recognition

Add GigaAM v3 e2e_ctc as a new transcription engine using
transcribe-rs 0.2.7 gigaam feature. Russian speech recognition
with punctuation, Latin characters and digit support.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: cargo fmt formatting

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Keep the file name of the model download the same as the file on the
blob website.

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: CJ Pais <cj@cjpais.com>
2026-03-01 20:53:48 +07:00
CJ Pais
83e6f5c492
support moonshine v2 (#837)
* support moonshine v2

* format

* add translations for models
2026-02-17 11:44:10 +08:00
CJ Pais
4c9c200709
Add support for SenseVoice (#745)
* Add support for sense voice.

* format
2026-02-09 12:15:42 +08:00
Hoss
a4999164ae
feat(models): add opt-in custom Whisper model discovery (#622)
* feat(models): auto-discover custom Whisper models in models directory

Enable automatic discovery of custom GGML-format Whisper models (.bin files)
placed in the models directory, so users don't need to modify source code
to use their own fine-tuned models.

Backend changes:
- Add discover_custom_whisper_models() to scan for .bin files
- Generate display names from filenames (e.g., "my-model" → "My Model")
- Skip predefined model filenames to avoid duplicates
- Add 3 unit tests for discovery logic

Frontend changes:
- Split model dropdown into 3 sections: Custom, Downloaded, Downloadable
- Add collapsible section for downloadable models to reduce clutter
- Add max-height with scroll for long model lists
- Add "Custom" badge for user-provided models

* feat(models): make custom Whisper model discovery opt-in

[why] Address maintainer concern about support burden from community
models. Custom models should be a power-user feature, not enabled by
default.

[how] Add custom_models_enabled setting (default: false) in Debug
settings. Discovery only runs when enabled. Models show "Not officially
supported" messaging. Documentation updated with enable steps.

* feat(models): add is_custom field and integrate custom models with new UI

[why]
PR review requested an explicit is_custom field instead of inferring
custom status from url === null. Custom models also need proper
integration with the new models settings page from PR #478.

[how]
- Add is_custom: bool to ModelInfo struct, set true on discovered models
- Change discover_custom_whisper_models signature from &PathBuf to &Path
- Use 0.0 sentinel scores so UI hides score bars for custom models
- Add "Custom Models" section to ModelsSettings with 3-way model split
- Show "Custom" badge in ModelCard and ModelDropdown
- Hide language/translation tags when supported_languages is empty
- Remove custom models from available_models on delete instead of
  just marking as not downloaded (they have no re-download URL)
- Update tests with new fields and assertions

* chore(i18n): add custom model translation keys to all locales

[why]
Custom model feature introduces 5 new translation keys that need
to be present in all 15 non-English locales for CI to pass.

[how]
Add English placeholder values for: customModelDescription,
modelSelector.custom, settings.models.customModels,
settings.debug.customModels.label, settings.debug.customModels.description

* fix(models): keep language filter visible when no models match

[why]
The language filter was inside a conditionally rendered section
that disappeared when no downloaded models matched the selected
language, leaving the user stuck with no way to change the filter.

[how]
Always render the "Your Models" header row with the language filter,
only conditionally render the model cards below it.

* docs: fix custom models section reference in README

Model selector → Models settings page.

* fix(models): apply custom models toggle immediately without restart

[why]
Two bugs reported in PR review: (1) app breaks on restart when a
custom model was selected and the toggle is disabled — selected_model
still points to the missing model, causing "Model not found" error.
(2) Custom models remain visible in the UI after toggle-off because
the in-memory model list is never updated and no event is emitted.

[how]
- Add remove_custom_models() and add_custom_models() to ModelManager
  for runtime mutation of the available_models mutex
- On disable: reset selected_model to empty if it's a custom model,
  then remove custom models from the in-memory list
- On enable: run discover_custom_whisper_models against the mutex
- Emit model-state-changed event so the frontend refreshes immediately

* chore(i18n): remove "restart required" from custom model descriptions

Toggle now takes effect immediately, so the restart sentence
is inaccurate. Updated all 16 locales and README instructions.

* fix(models): clear stale model selection on startup

[why]
If a custom model file is deleted from disk while it's the selected
model, the app gets stuck on "Loading..." forever on next launch
because the model ID is not in available_models but
auto_select_model_if_needed only checked for empty string.

[how]
Validate that selected_model exists in available_models before
accepting it. If not found, clear the selection so auto-select
picks a valid downloaded model.

* remove toggle and clean up

* format

---------

Co-authored-by: CJ Pais <cj@cjpais.com>
2026-02-09 10:15:45 +08:00
CJ Pais
0bb6073108
Add Breeze ASR (#733)
* add breeze asr

* Update model.rs
2026-02-08 16:09:55 +08:00
Viren Mohindra
ef24cfefda
feat: add models settings page with filtering and management (#478)
* feat: add models settings page with filtering and management

- add dedicated models settings page in sidebar
- reuse ModelCard component from onboarding for consistent UI
- add filter buttons for all/multi-language/translation models
- add model deletion with native confirmation dialog (tauri-plugin-dialog)
- consolidate model event listeners into useModels hook (Zustand store)
- remove duplicate event listeners from ModelSelector, LanguageSelector, TranslateToEnglish
- gate AccessibilityPermissions to macOS only
- add is_recommended field to model registry for featured models
- remove unused get_recommended_first_model command
- add translations for all 9 supported languages

* feat: add language filter to models settings page

adds a searchable language dropdown filter to the models page that lets users filter models by language support. when a non-english language is selected, models that don't support multiple languages (like parakeet) are hidden.

- add searchable language dropdown (right-aligned in filter row)
- filter models using supports_language_selection capability
- add allLanguages translation key to all 10 locales

* feat: add translation consistency checker script

- add scripts/check-translations.cjs to validate all language files
- script dynamically discovers languages from directory structure
- checks that all languages have same keys as english reference
- detects missing and extra keys in each language
- add check:translations npm script
- integrate into github actions lint workflow
- validates translations on every pull request

* fix: update imports and add missing translations after rebase

- replace useModels hook with useModelStore in components
- add permission error keys to all languages
- add model settings keys to cs and tr (new languages from main)

* fix: translate english placeholders in cs and tr locale files

* refactor: simplify model dropdown and migrate store to immer

- model dropdown now only shows downloaded models (no download/delete)
- convert store to immer with record types for immutability
- remove unused translation keys (welcome, downloadPrompt, etc.)
- add missing moonshine-base model fields
- sync translations after rebase

* fix: add download cancellation support and ui improvements

- add full download cancellation with Arc<AtomicBool> flags in rust backend
- add progress event throttling (100ms) to prevent ui freeze
- add cancel button to model card in settings page
- add model-deleted event listener to refresh dropdown after deletion
- remove pink background from recommended models in settings (keep badge only)
- add cancel/cancelDownload translation keys to all 14 languages

* fix: remove duplicate language/translate settings from general and advanced

settings are now only in ModelSettingsCard, not duplicated in their old locations

* fix: prevent model dropdown from being clipped by window edge

* add languages explicitly, clean up some ui

* fix: clear bottom progress bar when download is cancelled

The ModelSelector component maintains its own local state for download
progress. When a download was cancelled, the Rust backend would update
its state but never emitted an event to notify the frontend. This caused
the bottom progress bar to remain stuck showing "Downloading X%".

Added model-download-cancelled event emission in Rust and corresponding
listener in ModelSelector to clear progress state on cancellation.

* fix: align model card content to top to prevent floating elements

Changed ModelCard flex alignment from items-center to items-start so
the accuracy/speed bars stay at the top when the card expands (e.g.,
during download with progress bar visible).

* fix: prevent model deletion from interrupting active extractions

Added extracting_models HashSet to track models currently being
extracted. The update_download_status() function now skips cleanup
of .extracting directories for models that are actively extracting,
preventing a race condition where deleting one model would interrupt
another model's extraction process.

* refactor: migrate ModelCard buttons to Button component

Added two new Button variants for common patterns:
- primary-soft: soft/tinted primary buttons (used for download)
- danger-ghost: subtle destructive actions (used for delete/cancel)

Migrated all hardcoded buttons in ModelCard to use the shared Button
component for consistency and maintainability.

* feat: separate downloaded and available models into sections

Split the models list into "Your Models" and "Available to Download"
sections for clearer visual distinction between downloaded and
downloadable models.

Also adds missing translation keys to all locales:
- modelSelector.capabilities.singleLanguage
- modelSelector.capabilities.languageOnly
- settings.models.yourModels
- settings.models.availableModels

* fix: add missing translations after rebase onto main

add post-processing hotkey translations to all 15 locales and
backfill 29 missing keys for korean locale added in main.

* fix: add label to model delete button for clearer destructive state

* check translations as typescript

* format

* better text for dropdown

* wip ui tweaks

* rounded + ui tweaks

* fix download not 0% immediately, ui tweaks

* tweak name

* block for model downloading in onboarding

* small fixes

---------

Co-authored-by: CJ Pais <cj@cjpais.com>
2026-02-08 12:18:22 +08:00
Josh Ribakoff
a033a67b13
fix: run Rust tests in CI (#671)
* fix: run Rust tests in CI

Fixes #670

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix: run Rust tests in CI

Adds a separate test.yml workflow to run existing Rust unit tests.

Fixes #670

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* refactor: use mock TranscriptionManager in CI tests

Swap to a mock adapter during CI to avoid compiling whisper/Vulkan.
The mock has the same interface but no heavy dependencies.
Existing tests don't exercise transcription code, so this is safe.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-01 11:18:50 +08:00
Prasad Chalasani
c84e863423
feat: add automatic filler word removal from transcriptions (#589)
* feat: add automatic filler word removal from transcriptions

Add filter_transcription_output() that removes filler words (uh, um, hmm,
etc.) and hallucination patterns ([AUDIO], (pause), <tag>...</tag>) from
transcriptions. Inspired by VoiceInk's approach.

- Add regex-based filter in audio_toolkit/text.rs
- Integrate into transcription pipeline after custom word correction
- Add comprehensive tests for filler word removal
- Add regex crate dependency

* feat: collapse repeated 1-2 letter stutters in transcriptions

Add collapse_stutters() to reduce model hallucination artifacts like
"wh wh wh wh wh why" -> "wh why" or "I I I I think" -> "I think".

Collapses any 1-2 letter word that repeats 3+ times consecutively
to a single instance (case-insensitive matching).

* remove bracket/xml stuff

* format

---------

Co-authored-by: CJ Pais <cj@cjpais.com>
2026-01-18 10:52:39 +08:00
ja
6522944886
feat(tray): add copy last transcript action (#598) 2026-01-18 10:33:50 +08:00
Vlad Gerasimov
1b580d54e8
feat: add Moonshine Base speech recognition model (#556)
* feat: add Moonshine Base speech recognition model

Add support for the Moonshine Base ASR model, which offers:
- 5x faster transcription than Whisper
- Better accent recognition
- ~240MB model size (encoder + decoder + tokenizer)

Changes:
- Update transcribe-rs to v0.2 with moonshine feature
- Add EngineType::Moonshine variant
- Add Moonshine Base model definition (400MB, 80% accuracy, 90% speed)
- Add model loading and transcription logic
- Add i18n translation for Moonshine Base

Note: Model files must be hosted at blob.handy.computer/moonshine-base.tar.gz
Expected structure: encoder_model.onnx, decoder_model_merged.onnx, tokenizer.json

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Update moonshine parameters.

* add translations

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Co-authored-by: CJ Pais <cj@cjpais.com>
2026-01-10 11:22:49 +08:00
Viren Mohindra
e0b72f5f77
fix: unload model on cancel when immediate unload enabled (#498)
* fix: unload model on cancel when immediate unload enabled

adds model unload check to cancel_current_operation() to match
the behavior in transcribe(). previously, cancelling with escape
would leave the model loaded even with "unload immediately" enabled.

fixes #479

* also fix model not unloading if there is empty transcription

* refactor to util function

---------

Co-authored-by: CJ Pais <cj@cjpais.com>
2025-12-29 10:59:54 +07:00
CJ Pais
d2a11ed53b
Refactor History SQLite (#412)
* yolo rewrite history with claude - needs review

* Update history.rs

* format
2025-12-09 21:17:34 +07:00
CJ Pais
4734d56021
fix? (#388) 2025-11-28 09:23:26 +07:00
Jackson
381b8a8bfb
Add a cancel hotkey and configuration (#224)
* From scratch

* Cleanup

* Small fixes

* simplify

---------

Co-authored-by: CJ Pais <cj@cjpais.com>
2025-11-28 09:00:47 +07:00
CJ Pais
9521ab9332
WIP: Add tauri-specta for generating TS bindings (#322)
* initial specta commands

* refactor to use the bindings in the ui

* merge fixes

* remove unused commands

* add clamshell to lib

* Remove Invoke from everything.

* fix settings not being loaded properly

* fix settings being overwritten due to deserialization failure + remove
provider kind

* fix model loading text bug
2025-11-27 17:07:22 +07:00
CJ Pais
71d0eeed73 run format 2025-11-20 10:20:22 +07:00
figwood
ec3b06d5b2
feat: add conversion between Simplified and Traditional Chinese. (#356)
* feat: add conversion between Simplified and Traditional Chinese. If the selected language is Simplified or Traditional Chinese, it will convert the text to the selected variant(Simplified or Traditional) after been recognized, depends on opencc, if it is not installed, the conversion will skip.

* Adopting ferrous-opencc makes cross-platform packaging easier.
2025-11-19 20:31:44 +07:00
CJ Pais
18536aa81e
Add file based debug logging (#347)
* add basic logging to handy

* 100kb of logs

* add open button to the ui for logs

* use log everywhere

* some minor cleanup

* finish debug logging
2025-11-17 12:55:24 +07:00
CJ Pais
315c32eab4 format 2025-11-17 10:41:31 +07:00
Jorge
3b3ed5abc5
feat: add more options for deleting recordings automatically and added button to open the folder that holds the recordings (#330)
* feat: add more options for deleting recordings automatically and added button to open the folder that olds the recordings

- deleting automatically options: after certain time, never or preserving certain number of recordings
- new button "Open Recordings Folder" located on History page

* refactor: moved "Delete Recordings" selector to Debug settings

* refactor: simplify opening recordings folder logic by using tauri's opener plugin

* clean up ui and code a bit

---------

Co-authored-by: CJ Pais <cj@cjpais.com>
2025-11-17 10:41:05 +07:00
Jorge
45eec57226
feat: add fallback microphone for clamshell/desktop mode (#329)
* feat: add fallback microphone for clamshell/desktop mode

* refactor: moved "Closed Laptop Microphone" setting to Debug

* cleanup

* run format

* add check back

---------

Co-authored-by: CJ Pais <cj@cjpais.com>
2025-11-17 10:38:41 +07:00
Maicon Moreira
25f4a564fd
Fix unstable mute implementation on Windows/Linux (#341)
* Fix unstable mute implementation on Windows/Linux

* add some delay before the mute so the audio feedback plays

---------

Co-authored-by: CJ Pais <cj@cjpais.com>
2025-11-17 09:40:47 +07:00
Luis Eduardo
f7380fa69a
feat: LLM based post-processing (#222)
* Add LLM post-processing feature with settings management and UI integration

* Refactor LLM post-processing settings and prompt selection logic

* Rename CLAUDE.md to more standard AGENTS.md

* Replace Open Router with OpenAI Compatible endpoints for post-processing

* Fix post processing settings api styles

* refactor or something

* simplify

* cleanup a bit?

* remove useless code

* minor improvements

* fix laggy textarea

* Store post-processing results in db

* fix text color

* new default prompt

---------

Co-authored-by: CJ Pais <cj@cjpais.com>
2025-11-05 18:29:03 -08:00
Maicon Moreira
7c04ee85a7
feat: add “mute while recording” setting (#257)
* feat: mute while recording

* refactor: move MuteWhileRecording from AdvancedSettings to DebugSettings
2025-10-28 17:49:33 -07:00
CJ Pais
65c1e2dbcb
parakeet v2 support (#116)
* parakeet v2 support

* Update model.rs

* fix parakeet size

* new onboarding UI for parakeet v2 introduction

* tweak height
2025-10-28 16:32:26 -07:00
CJ Pais
a9a3e7bfd8
display download size + refactor (#221) 2025-10-17 16:57:51 -07:00
Jackson
93215dc097
A few optimizations on startup (#182)
* Use an optimal release profile... load managers & hotkeys first, add a
splash, simplify app handler

* removed splashscreen, hopefully app boots fast enough anyhow

* No need to load transcription model on start now that its loaded on
demand & cleanup

* Make an unloaded model status

* tiny modification to unloading

* update comment

---------

Co-authored-by: CJ Pais <cj@cjpais.com>
2025-10-17 16:05:43 -07:00
Jackson
83970b0f44
Load model in parallel (#181) 2025-10-08 16:44:55 -07:00
CJ Pais
18f32c7c8d simplify history slightly 2025-10-08 16:28:01 -07:00
becker
d0e6a17c17
Add history limit setting (#150)
* Add history limit setting

Dearest Reviewer,

I have added a history limit setting. The limit change is applied instantly.
If the limit is 0 then no recordings are saved.

Becker

* remove extra save

save happens on exit

* move to debug menu

* use the text color

---------

Co-authored-by: CJ Pais <cj@cjpais.com>
2025-10-08 13:01:09 -07:00
segudev
03289f84d7
fix: restore translate-to-english functionality (#173)
* fix: restore translate-to-english functionality

The translate option broke when migrating from whisper-rs to transcribe-rs.
This fix patches transcribe-rs to add translate parameter support and passes
the translate_to_english setting to the Whisper engine.

Additionally fixed AccessibilityPermissions API calls to match updated library.

Translation only works with Whisper multilingual models (not .en models).
Parakeet models don't support translation as they are transcription-only.

Fixes #152

* bump transcribe rs

---------

Co-authored-by: CJ Pais <cj@cjpais.com>
2025-10-08 11:57:47 -07:00
Han Pham
9185b3db08
feat: delete a single audio entry (#119)
* feat: delete a single audio entry

* fix(HistorySetting): remove unnecessary loading state
2025-09-19 12:30:50 -07:00