- niri: index/config-tree rework, displays.md desk-profile rewrite, tips/action-reference/default-bindings refresh (26.04), generated keybindings.md (119 bindings) - noctalia-shell.md: settings.toml lives in ~/.local/state, taskbar widget, v5 keybind table (Mod+D/Mod+V/Alt+Tab) - handy.md: NEW; live nested store shape, fleet endpoint, known thinking-model empty-content breaker + verified cure - log.md: DE redesign entry + pam_faillock sudo-lockout incident
234 lines
6.9 KiB
Markdown
234 lines
6.9 KiB
Markdown
---
|
|
type: reference
|
|
title: Handy
|
|
description: Offline speech-to-text transcription with WebKitGTK backend and Tauri v2 UI.
|
|
resource: https://github.com/cjpais/Handy
|
|
tags:
|
|
- handy
|
|
- stt
|
|
- speech-to-text
|
|
- transcription
|
|
- wayland
|
|
- tauri
|
|
timestamp: 2026-10-09
|
|
verified: 2026-10-09
|
|
---
|
|
|
|
# Handy
|
|
|
|
Handy is an offline speech-to-text (STT) transcription tool built with Tauri v2 and WebKitGTK. It runs entirely offline, supports multiple GPU backends, and provides real-time transcription with optional post-processing.
|
|
|
|
**Installed version:** 0.9.6-1 (`local/handy-bin`, `/usr/bin/handy`) — **0.9.8 upgrade pending**: `yay -Syu handy-bin` (root work is operator-owned; agents never sudo).
|
|
**Upstream latest:** 0.9.8 (as of 2026-10-03)
|
|
**Binary:** `/usr/bin/handy`
|
|
**Config:** `~/.config/com.pais.handy/` (upstream docs) / `~/.local/share/com.pais.handy/settings_store.json` (live observed)
|
|
|
|
---
|
|
|
|
## Architecture
|
|
|
|
Handy uses Tauri v2 for its UI (WebKitGTK for rendering) and a native Rust backend for STT processing. On Wayland, it requires compositor-level global shortcuts.
|
|
|
|
**Live settings snapshot (blankpad):**
|
|
|
|
| Setting | Value |
|
|
|---------|-------|
|
|
| `paste_method` | `none` |
|
|
| `copy_to_clipboard` | `true` |
|
|
| `push_to_talk` | `true` |
|
|
| `bindings` | transcribe=`ctrl+space`, transcribe_with_post_process=`ctrl+shift+space`, cancel=`escape` |
|
|
| `keyboard_implementation` | `handy_keys` |
|
|
| `overlay` | `bottom` (live), `true` (config) |
|
|
| `model` | `handy-computer/canary-180m-flash-gguf` (Q8_0) |
|
|
| `transcribe_gpu` | `vulkan` (PCI 07:00.0) |
|
|
| `autostart_enabled` | `false` (niri-managed) |
|
|
| `experimental` | `true` |
|
|
| `filler_word_removal` | `true` |
|
|
| `post_process_enabled` | `true` (fleet-wired) |
|
|
| `clipboard_handling` | `copy_to_clipboard` |
|
|
| `provider` | `custom` (base_url=`http://192.168.0.143:7771/v1`, model=`frank-secondary`) |
|
|
| `prompt` | `default_improve_transcriptions` |
|
|
|
|
---
|
|
|
|
## Configuration
|
|
|
|
### Config Location
|
|
|
|
**Upstream docs:** `~/.config/com.pais.handy/`
|
|
**Live observed:** `~/.local/share/com.pais.handy/settings_store.json`
|
|
|
|
Both locations may contain settings; the live data directory is the authoritative runtime store.
|
|
|
|
### Live Settings (blankpad)
|
|
|
|
The store is nested: everything lives under a top-level `settings` object.
|
|
Post-processing wiring (applied 2026-10-09) touches five keys plus the
|
|
`custom` entry inside `post_process_providers`:
|
|
|
|
```json
|
|
{ "settings": {
|
|
"post_process_enabled": true,
|
|
"post_process_provider_id": "custom",
|
|
"post_process_models": { "custom": "frank-secondary", "openai": "", "...": "" },
|
|
"post_process_selected_prompt_id": "default_improve_transcriptions",
|
|
"post_process_providers": [
|
|
{ "id": "custom", "base_url": "http://192.168.0.143:7771/v1",
|
|
"allow_base_url_edit": true, "models_endpoint": "/models", "...": "" }
|
|
],
|
|
"post_process_api_keys": { "custom": "", "...": "" },
|
|
"autostart_enabled": false,
|
|
"push_to_talk": true,
|
|
"paste_method": "none",
|
|
"clipboard_handling": "copy_to_clipboard",
|
|
"keyboard_implementation": "handy_keys",
|
|
"overlay_position": "bottom",
|
|
"overlay_style": "live"
|
|
} }
|
|
```
|
|
|
|
Repo-side merge patch + apply procedure:
|
|
`dotfiles/zz-blankpad/.local/share/com.pais.handy/` (README.md).
|
|
|
|
---
|
|
|
|
## Command-Line Interface
|
|
|
|
### Startup Flags
|
|
|
|
```bash
|
|
handy --start-hidden # Start without showing the UI
|
|
handy --no-tray # Hide the system tray icon
|
|
handy --debug # Enable debug mode (Ctrl+Shift+D)
|
|
```
|
|
|
|
### Remote Control
|
|
|
|
Send commands to a running instance:
|
|
|
|
```bash
|
|
handy --toggle-transcription # Toggle transcription on/off
|
|
handy --toggle-post-process # Toggle post-processing on/off
|
|
handy --cancel # Cancel the current transcription
|
|
```
|
|
|
|
---
|
|
|
|
## Wayland Integration
|
|
|
|
### Global Shortcuts
|
|
|
|
On Wayland, global shortcuts **must** be defined in the compositor (niri), not in Handy itself.
|
|
|
|
**File:** `~/.config/niri/cfg/keybinds.kdl`
|
|
|
|
```kdl
|
|
// Live (cfg/keybinds.kdl):
|
|
Mod+Shift+Space hotkey-overlay-title="Handy: toggle post-process recording" allow-when-locked=true { spawn-sh "handy --toggle-post-process"; }
|
|
// Transcription itself stays on Handy's own ctrl+space (keyboard_implementation="handy_keys").
|
|
```
|
|
|
|
### Text Input
|
|
|
|
**Requirement:** `wtype` or `dotool` for text input (NOT installed on blankpad).
|
|
**Current config:** `paste_method=none` + manual `Ctrl+V` paste.
|
|
|
|
### Overlay
|
|
|
|
**Default:** Disabled on Linux (can steal focus and break paste).
|
|
**Live config:** `mode=bottom`, `live=true`.
|
|
|
|
---
|
|
|
|
## Troubleshooting
|
|
|
|
### WebKit Renderer Issue
|
|
|
|
**Symptom:** Blank or corrupted rendering.
|
|
|
|
**Fix:**
|
|
|
|
```bash
|
|
export WEBKIT_DISABLE_DMABUF_RENDERER=1
|
|
handy
|
|
```
|
|
|
|
### SIGUSR1 Crash
|
|
|
|
**Trap:** The upstream SIGUSR1 handler was **REMOVED** (WebKitGTK now owns it).
|
|
|
|
```bash
|
|
# WRONG (may crash the app):
|
|
pkill -USR1 handy
|
|
|
|
# CORRECT (toggle transcription):
|
|
pkill -USR2 -n handy
|
|
# or use CLI:
|
|
handy --toggle-transcription
|
|
```
|
|
|
|
### Debug Mode
|
|
|
|
```bash
|
|
handy --debug
|
|
# or press Ctrl+Shift+D in a running instance
|
|
```
|
|
|
|
---
|
|
|
|
## Dependencies
|
|
|
|
| Package | Purpose |
|
|
|---------|---------|
|
|
| `handy` | Main application |
|
|
| `tauri-apps` | Tauri v2 runtime |
|
|
| `webkit2gtk` | WebKitGTK rendering |
|
|
| `gtk-layer-shell` | Wayland layer-shell support |
|
|
| `vulkan` | GPU-accelerated STT |
|
|
| `ffmpeg` | Audio processing |
|
|
|
|
---
|
|
|
|
## Fleet LLM Endpoint
|
|
|
|
**Post-processing pipeline:** `handy` → `http://192.168.0.143:7771/v1`, model id
|
|
`frank-secondary` (the served id; harness callsign for the same unit is
|
|
`frank-zealot` — same llama.cpp server, also the fleet compaction endpoint).
|
|
|
|
**Endpoint configuration:**
|
|
- **Provider:** `custom` (the one preset with `allow_base_url_edit: true`)
|
|
- **Base URL:** `http://192.168.0.143:7771/v1`
|
|
- **Model:** `frank-secondary`
|
|
- **Prompt:** `default_improve_transcriptions`
|
|
|
|
### Known issues
|
|
|
|
**Thinking-model empty content (2026-10-09, open).** `frank-secondary` is a
|
|
thinking model. Default chat completions return `content: ""` with the answer
|
|
trapped in `reasoning_content` — Handy reads `content`, so post-processed
|
|
transcripts arrive empty. Verified per-request cure:
|
|
`"chat_template_kwargs": {"enable_thinking": false}` (probe below returns
|
|
cleaned text in `content`). Handy has no field to send extra body keys, and
|
|
the cure CANNOT be set server-global on :7771 without degrading fleet
|
|
compaction (same server). Proposed fix (not yet implemented): a dedicated
|
|
no-think unit/port for Handy traffic, e.g. llama-server on `:7772` with
|
|
thinking disabled at the template level, then repoint `custom.base_url`.
|
|
|
|
```bash
|
|
curl -s http://192.168.0.143:7771/v1/chat/completions -d '{
|
|
"model": "frank-secondary",
|
|
"messages": [{"role":"user","content":"Clean this transcript: twenty five items cost ten dollars um ok."}],
|
|
"max_tokens": 200,
|
|
"chat_template_kwargs": {"enable_thinking": false}}'
|
|
```
|
|
|
|
See [[noctalia-shell]] for IPC integration patterns.
|
|
|
|
---
|
|
|
|
## See Also
|
|
|
|
- [[noctalia-shell|noctalia-shell.md]] — Desktop environment integration
|
|
- [[niri|niri/index.md]] — Compositor configuration
|
|
- [`handy --help`](https://github.com/cjpais/Handy) — CLI reference
|