- Go 76.7%
- Nix 23.3%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
|
All checks were successful
Check / check (push) Successful in 8m33s
The workflow runs `nix flake check` and nothing else, so what CI enforces is the same thing a developer runs locally, and adding a check means editing flake.nix rather than the workflow. The checks are the package build — buildGoModule runs `go test` in its check phase, so building is testing — plus a formatting check covering both the Go and the Nix sources, and the Nix linters, mirroring what the nix config repo already does. Models moved from a nested `packages.models` attrset to flattened `packages.model-<name>` entries: flake check requires every packages.* output to be a derivation. Also switches the licence metadata to GPL-3.0 to match LICENSE, and adds an alejandra.toml so the Nix sources use tabs like the other repos. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
| .forgejo/workflows | ||
| internal | ||
| nix | ||
| .gitignore | ||
| alejandra.toml | ||
| flake.lock | ||
| flake.nix | ||
| go.mod | ||
| go.sum | ||
| LICENSE | ||
| main.go | ||
| README.md | ||
dictate
Push-to-talk dictation for Wayland. Press a key, talk, press it again, and what you said is typed into whatever window has focus. Transcription is whisper.cpp running locally — no account, no network, no telemetry.
While it records, a small pill floats above your windows showing a live waveform of what the microphone is picking up and how long you have been talking.
How it works
press ──▶ pw-record streams 16kHz mono PCM ──▶ overlay draws the levels
│
press ──▶ whisper.cpp transcribes ──────────┴──▶ wtype types the result
Three subprocesses and no linked audio stack. dictate itself is the state
machine and the indicator.
Wayland compositors hand their key bindings a press but never a release, so
hold-to-talk is not something a binding can express. dictate is a toggle
instead: the first press opens a recording, the second closes it. The two
presses are separate processes — the second one finds the first through a lock
file and signals it.
The overlay is a wlr-layer-shell surface rather than an ordinary window,
because an ordinary window would take keyboard focus the moment it appeared,
and the transcript would then be typed into the indicator instead of into the
document you were writing.
Requirements
- A Wayland compositor with
wlr-layer-shellandzwp_virtual_keyboard. niri, sway, Hyprland and river all qualify; GNOME does not. pw-record,whisper-cli,wtypeonPATH(the Nix package wires these up).- PipeWire. To record through something else, override
commands.recordwith anything that writes 16kHz mono s16le PCM to stdout —ffmpeg -f pulse -i default -ar 16000 -ac 1 -f s16le -, say.
Nothing needs root, a system service, /dev/uinput, or membership in the
input group.
Install with Nix
Add the flake and enable the module:
{
inputs.dictate.url = "git+https://git.cumsek.com/Marti/dictate";
# ... in your NixOS configuration:
imports = [inputs.dictate.nixosModules.default];
programs.dictate = {
enable = true;
model = "large-v3-turbo";
language = "auto";
# GPU support comes entirely from this package.
whisperPackage = pkgs.whisper-cpp-vulkan;
};
}
Then bind it. For niri:
binds {
Mod+S { spawn "dictate"; }
}
The model is fetched and pinned by Nix, so there is nothing to download by hand on a new machine.
Options
programs.dictate covers model, modelFile, language, threads, gpu,
prompt, source, maxSeconds, whisperPackage, and the overlay.* group
(enable, anchor, margin, bars, barWidth, barGap, height, gain
and colors.*). Anything without an option of its own can go in
programs.dictate.settings, which is merged over the rest.
Run dictate without Nix by putting the same keys in
$XDG_CONFIG_HOME/dictate/config.json; that file wins over the packaged
defaults, key by key.
Usage
dictate # toggle: start, or stop and transcribe
dictate start # start, failing if one is already running
dictate stop # stop and transcribe
dictate cancel # stop and throw the recording away
dictate status # exit 0 if recording
Choosing a model
| Model | Size | Notes |
|---|---|---|
tiny |
78 MB | Fast, and wrong often enough to notice. |
base |
148 MB | Fine for short English. |
small |
488 MB | The sensible floor for multilingual use. |
medium |
1.5 GB | Better, and noticeably slower. |
large-v3 |
3.1 GB | Most accurate. |
large-v3-turbo |
1.6 GB | Nearly large-v3, at roughly small's speed. |
language = "auto" detects per recording, which is what you want if you switch
languages mid-day. Naming the language outright is more accurate on very short
clips.
Development
nix develop
go build ./...
go run . start & # in one shell
go run . stop # in another
nix flake check runs exactly what CI runs: the package build (which runs
go test in its check phase), a formatting check over both the Go and the Nix
sources, and the Nix linters.
Licence
GPL-3.0-only. See LICENSE.