Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
2cb0e78
Change zh-Hant OpenCC conversion from S2twp to S2tw (#1171)
Roger-Wu Mar 28, 2026
b710ff2
docs: add feature freeze notice to CONTRIBUTING.md and fix typo in PR…
collinstevens Mar 28, 2026
075fb5f
[bugfix] remove long repeating words (#976)
AlexanderYastrebov Mar 28, 2026
4239cd3
fix: don't log cloud provider keys (#1145)
shaankhosla Mar 28, 2026
b76febd
docs: add release signature verification steps (#1178)
AICatKing Mar 28, 2026
320dfb2
fix(tray): set tray tooltip text on Windows (#1185)
tomerar Mar 29, 2026
be2c584
Update bindings.ts
cjpais Mar 29, 2026
5bf7646
Update bindings.ts
cjpais Mar 29, 2026
7ed8a55
Load the VAD when model loads in parallel (#1177)
jacksongoode Mar 29, 2026
d1d3393
upgrade transcribe rs to 0.3.5 (#1188)
cjpais Mar 29, 2026
552ebe5
preserve legacy portable marker during updates (#1167)
ferologics Mar 30, 2026
892658e
fix(nix): remove onnxruntime overlay, use nixpkgs native package (#1149)
xilec Mar 30, 2026
4609db7
add cohere (#1200)
cjpais Apr 1, 2026
743d8a5
Update Italian translation (#1202)
albanobattistella Apr 1, 2026
b123c1e
fix crash on old cpus (#1176)
cjpais Apr 2, 2026
39e855d
release v0.8.2
cjpais Apr 2, 2026
c1697b2
nix: use symlinkJoin for ALSA_PLUGIN_DIR (#1226)
yuxqiu Apr 5, 2026
84d88f9
perf: add reasoning_effort passthrough to avoid thinking-mode latency…
luoxi Apr 7, 2026
30b57c4
fix(issue 522): surface paste errors as UI toast notification (#1198)
odedindi Apr 7, 2026
3c0ba16
Add chunked transcription with safe fallback and settings toggle
meetuljain Apr 8, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/PULL_REQUEST_TEMPLATE.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
## Before Submitting This PR

<!--
HANDY IS UNDERGOING A FEATURE FREEZE. IF YOU ARE SUBMITTING A PR WHICH IS A NEW FEATURE THAT THE COMMUNITY HAS NOT ASKED FOR: PREPARE TO BE REJECTED. IF THE COMMUNITY HAS ASKED FOR IT, OR YOU HAVE EXPLICITLY GATEHRED SUPPORT IT MAY STILL BE CONSIDERED.
HANDY IS UNDERGOING A FEATURE FREEZE. IF YOU ARE SUBMITTING A PR WHICH IS A NEW FEATURE THAT THE COMMUNITY HAS NOT ASKED FOR: PREPARE TO BE REJECTED. IF THE COMMUNITY HAS ASKED FOR IT, OR YOU HAVE EXPLICITLY GATHERED SUPPORT IT MAY STILL BE CONSIDERED.

BUG FIXES ARE THE TOP PRIORITY. THERE ARE 60+ ISSUES TO FIX.
-->
Expand Down
11 changes: 9 additions & 2 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -353,8 +353,15 @@ jobs:
AZURE_TENANT_ID: ${{ inputs.sign-binaries && secrets.AZURE_TENANT_ID || '' }}
TAURI_SIGNING_PRIVATE_KEY: ${{ inputs.sign-binaries && secrets.TAURI_SIGNING_PRIVATE_KEY || '' }}
TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ inputs.sign-binaries && secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD || '' }}
WHISPER_NO_AVX: ${{ contains(inputs.platform, 'ubuntu') && !contains(inputs.platform, 'arm') && 'ON' || '' }}
WHISPER_NO_AVX2: ${{ contains(inputs.platform, 'ubuntu') && !contains(inputs.platform, 'arm') && 'ON' || '' }}
# Disable -march=native and all SIMD instruction sets above SSE4.2 for
# x86_64 builds. Without this, CPUs lacking AVX/FMA (e.g. Celeron N2920,
# FX-8350) crash with SIGILL. The Vulkan backend handles heavy compute
# so disabling these has negligible performance impact.
GGML_NATIVE: ${{ !contains(inputs.platform, 'macos') && !contains(inputs.platform, 'arm') && !contains(inputs.target, 'aarch64') && 'OFF' || '' }}
GGML_AVX: ${{ !contains(inputs.platform, 'macos') && !contains(inputs.platform, 'arm') && !contains(inputs.target, 'aarch64') && 'OFF' || '' }}
GGML_AVX2: ${{ !contains(inputs.platform, 'macos') && !contains(inputs.platform, 'arm') && !contains(inputs.target, 'aarch64') && 'OFF' || '' }}
GGML_FMA: ${{ !contains(inputs.platform, 'macos') && !contains(inputs.platform, 'arm') && !contains(inputs.target, 'aarch64') && 'OFF' || '' }}
GGML_F16C: ${{ !contains(inputs.platform, 'macos') && !contains(inputs.platform, 'arm') && !contains(inputs.target, 'aarch64') && 'OFF' || '' }}
with:
tagName: ${{ inputs.release-id && format('v{0}', steps.get-version.outputs.version) || '' }}
releaseName: ${{ inputs.release-id && format('v{0}', steps.get-version.outputs.version) || '' }}
Expand Down
7 changes: 5 additions & 2 deletions .github/workflows/nix-check.yml
Original file line number Diff line number Diff line change
Expand Up @@ -108,9 +108,12 @@ jobs:
- name: Check if nix files changed
if: github.event_name == 'pull_request'
id: nix-files
env:
GH_TOKEN: ${{ github.token }}
run: |
git fetch origin ${{ github.base_ref }} --depth=1
if git diff --name-only origin/${{ github.base_ref }}...HEAD | grep -qE '^(flake\.(nix|lock)|\.nix/|bun\.lock|src-tauri/(Cargo\.(toml|lock)|tauri\.conf\.json|build\.rs))'; then
if gh api repos/${{ github.repository }}/pulls/${{ github.event.pull_request.number }}/files \
--paginate --jq '.[].filename' | \
grep -qE '^(flake\.(nix|lock)|\.nix/|bun\.lock|src-tauri/(Cargo\.(toml|lock)|tauri\.conf\.json|build\.rs))'; then
echo "changed=true" >> "$GITHUB_OUTPUT"
fi

Expand Down
6 changes: 6 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,12 @@

Thank you for your interest in contributing to Handy! This guide will help you get started with contributing to this open source speech-to-text application.

## ⚠️ Feature Freeze

**Handy is currently undergoing a feature freeze.** If you are submitting a PR which is a new feature that the community has not asked for, it will be rejected. If the community has asked for it, or you have explicitly gathered support, it may still be considered.

**Bug fixes are the top priority.** There are 60+ issues to fix. Please focus your contributions on fixing bugs and improving stability.

## 📖 Philosophy

Handy aims to be the most forkable speech-to-text app. The goal is to create both a useful tool and a foundation for others to build upon—a well-patterned, simple codebase that serves the community. We prioritize:
Expand Down
35 changes: 35 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,41 @@ We're actively working on several features and improvements. Contributions and f
- Abstract and organize Tauri command patterns
- Investigate tauri-specta for improved type safety and organization

## Verify Release Signatures

Handy release artifacts are signed with Tauri's updater signature format. The public key is stored in [`src-tauri/tauri.conf.json`](src-tauri/tauri.conf.json) under `plugins.updater.pubkey`.

To verify a release manually, set `ARTIFACT` to the filename you downloaded, save the `pubkey` value from `src-tauri/tauri.conf.json` to `handy.pub.b64`, then decode the public key and matching `.sig` file from base64 and verify the artifact with `minisign`:

```bash
# Replace with the file you downloaded
ARTIFACT="Handy_0.8.1_amd64.AppImage"

python3 - "$ARTIFACT" <<'PY'
import base64, pathlib, sys

artifact = sys.argv[1]

pub = pathlib.Path("handy.pub.b64").read_text().strip()
pathlib.Path("handy.pub").write_bytes(base64.b64decode(pub))

sig = pathlib.Path(f"{artifact}.sig").read_text().strip()
pathlib.Path(f"{artifact}.minisig").write_bytes(base64.b64decode(sig))
PY

minisign -Vm "$ARTIFACT" \
-p handy.pub \
-x "$ARTIFACT.minisig"
```

On success, `minisign` prints:

```text
Signature and comment signature verified
```

Do not use `gpg` for these `.sig` files.

## Troubleshooting

### Manual Model Installation (For Proxy Users or Network Restrictions)
Expand Down
6 changes: 3 additions & 3 deletions flake.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

44 changes: 8 additions & 36 deletions flake.nix
Original file line number Diff line number Diff line change
Expand Up @@ -67,39 +67,6 @@
GST_PLUGIN_SYSTEM_PATH_1_0 = "${lib.makeSearchPathOutput "lib" "lib/gstreamer-1.0" (gstPlugins pkgs)}";
};

# TODO: Remove this overlay once nixpkgs ships onnxruntime ≥ 1.24.
# Tracking PR: https://github.com/NixOS/nixpkgs/pull/499389
# ort-sys 2.0.0-rc.12 requires ONNX Runtime 1.24 (API v24);
# nixpkgs only ships 1.23.2, so use MS prebuilt binaries.
onnxruntimeOverlay = (final: prev: {
onnxruntime = let
onnxVersion = "1.24.2";
platform = {
x86_64-linux = { name = "linux-x64"; hash = "sha256-Q3JUdLpWY2QuF2hHF5Rmk4UOIAXvvXJKxy2ieP6tJeY="; };
aarch64-linux = { name = "linux-aarch64"; hash = "sha256-spla8PQ3xOAi/YAcV/tcJf0f5mDNM9JutHGUSQpbRsQ="; };
}.${final.system};
in prev.stdenv.mkDerivation {
pname = "onnxruntime";
version = onnxVersion;
src = prev.fetchurl {
url = "https://github.com/microsoft/onnxruntime/releases/download/v${onnxVersion}/onnxruntime-${platform.name}-${onnxVersion}.tgz";
hash = platform.hash;
};
sourceRoot = "onnxruntime-${platform.name}-${onnxVersion}";
nativeBuildInputs = [ prev.autoPatchelfHook ];
buildInputs = [ prev.stdenv.cc.cc.lib ];
installPhase = ''
runHook preInstall
mkdir -p $out/lib $out/include
cp -r lib/* $out/lib/
cp -r include/* $out/include/
runHook postInstall
'';
meta = prev.onnxruntime.meta // {
description = "ONNX Runtime ${onnxVersion} (prebuilt by Microsoft)";
};
};
});
in
{
packages = forAllSystems (
Expand All @@ -109,10 +76,16 @@
inherit system;
overlays = [
bun2nix.overlays.default
onnxruntimeOverlay
];
};
lib = pkgs.lib;
combinedAlsaPlugins = pkgs.symlinkJoin {
name = "combined-alsa-plugins";
paths = [
"${pkgs.pipewire}/lib/alsa-lib"
"${pkgs.alsa-plugins}/lib/alsa-lib"
];
};
in
{
handy = pkgs.rustPlatform.buildRustPackage {
Expand Down Expand Up @@ -212,7 +185,7 @@
preFixup = ''
gappsWrapperArgs+=(
--set WEBKIT_DISABLE_DMABUF_RENDERER 1
--set ALSA_PLUGIN_DIR "${pkgs.pipewire}/lib/alsa-lib:${pkgs.alsa-plugins}/lib/alsa-lib"
--set ALSA_PLUGIN_DIR "${combinedAlsaPlugins}"
--prefix LD_LIBRARY_PATH : "${
lib.makeLibraryPath [
pkgs.vulkan-loader
Expand Down Expand Up @@ -257,7 +230,6 @@
let
pkgs = import nixpkgs {
inherit system;
overlays = [ onnxruntimeOverlay ];
};
in
{
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "handy-app",
"private": true,
"version": "0.8.1",
"version": "0.8.2",
"type": "module",
"scripts": {
"dev": "vite",
Expand Down
6 changes: 3 additions & 3 deletions src-tauri/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions src-tauri/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "handy"
version = "0.8.1"
version = "0.8.2"
description = "Handy"
authors = ["cjpais"]
edition = "2021"
Expand Down Expand Up @@ -69,7 +69,7 @@ rusqlite = { version = "0.37", features = ["bundled"] }
tar = "0.4.44"
flate2 = "1.0"
sha2 = "0.10"
transcribe-rs = { version = "0.3.3", features = ["whisper-cpp", "onnx"] }
transcribe-rs = { version = "0.3.8", features = ["whisper-cpp", "onnx"] }
handy-keys = "0.2.4"
ferrous-opencc = "0.2.3"
clap = { version = "4", features = ["derive"] }
Expand Down
12 changes: 8 additions & 4 deletions src-tauri/nsis/installer.nsi
Original file line number Diff line number Diff line change
Expand Up @@ -587,17 +587,21 @@ Function .onInit


; --- PORTABLE MODE --- Auto-detect portable mode during updates.
; If the target directory already has a valid portable marker file, preserve
; portable mode so the Tauri updater works without needing /PORTABLE.
; We validate the magic string to avoid false-positives from stale empty files
; left by scoop's NSIS extraction (dl.7z side-effect).
; Preserve portable installs that use either the current magic-string marker
; or the legacy empty marker created by older Handy releases. Require Data/
; for the legacy empty-marker case so stale scoop side-effect files do not
; accidentally opt an updater run into portable mode.
${If} $PortableMode <> 1
${AndIf} $UpdateMode = 1
${AndIf} ${FileExists} "$INSTDIR\portable"
FileOpen $1 "$INSTDIR\portable" r
FileRead $1 $2
FileClose $1
${If} $2 == "Handy Portable Mode"
StrCpy $PortableMode 1
${OrIf} $2 == ""
${AndIf} ${FileExists} "$INSTDIR\Data"
StrCpy $PortableMode 1
${EndIf}
${EndIf}

Expand Down
Loading