feat: manage Handy preferences and startup declaratively

This commit is contained in:
kiri committed 2026-09-09 20:39:08 +02:00
1 parent 345ef7c56c
commit 0992f3b863
6 files changed
+74 -46

No files matched your search

+23 -34
View File
@@ -119,40 +119,29 @@ reports are not a fresh comparison with this baseline.
## Open choices and next discussion
Dictation: plain local transcription is selected; rambling cleanup is deferred.
The shared Niri module installs Handy and starts it hidden with the desktop.
Super+D toggles recording; Super+Shift+D cancels. Commands were verified against
the pinned Handy 0.9.1 source. No microphone recording or model download was run.
After user activation, launch Handy from Walker, download/select Parakeet V3,
keep automatic language detection and disable push-to-talk for toggle operation.
In Advanced, choose paste method None and clipboard handling Copy to Clipboard.
Keep post-processing disabled. Settings/models remain writable app-owned state.
Start Handy manually for the current session; desktop autostart applies on login.
Try an English and a Dutch recording, paste into Codex with Ctrl+Shift+V, then
submit manually. User feedback needed: recognition accuracy, delay and shortcut
delivery. Automatic paste can follow if wanted after checking Niri compatibility.
Verification: formatting, flake evaluation, Zenith and Polaris no-link system
builds, and whitespace checks passed. No system activation or interactive
dictation was performed; recognition quality and latency remain unmeasured.
First-use diagnosis: Handy was set to Nemotron, Direct Input, Don't Modify
Clipboard and push-to-talk. One recording transcribed but output used X11 input
simulation; other recordings returned zero usable samples. Default digital mic
is unmuted. Set None + Copy to Clipboard and retry a full sentence; microphone
selection/voice detection needs further checking if History stays empty.
Niri Super+D invokes a forced toggle, independent of Handy's push-to-talk setting;
Handy's Ctrl+Space global binding is not the Niri binding. Keep the user's minimal
overlay. Noctalia's native microphone filter suppresses all microphone activity
popups/indicators, while camera and screen-sharing indications remain enabled.
The Noctalia change passed formatting, flake evaluation and both workstation
builds; user activation and the clipboard retry remain pending.
Automatic paste: Niri now includes wtype, which Handy natively uses to deliver
Wayland paste shortcuts. Set Paste Method to Ctrl+Shift+V for Kitty, keep automatic
submission off, and restart Handy after activation. Interactive delivery remains
unverified. Handy 0.9.1 supports Overlay Style Live with streaming models; the
selected Nemotron Streaming 3.5 is a suitable candidate. Minimal hides live text.
This supersedes the initial Parakeet V3/clipboard-only recommendation when live
feedback and automatic paste are wanted; no custom input script is needed.
The wtype addition passed formatting, flake evaluation, both workstation builds
and whitespace checking. Activation and the interactive paste trial are pending.
Handy preferences are Nix-owned in `modules/capabilities/handy.nix`: Nemotron
Streaming 3.5, automatic language detection, default microphone, Live overlay at
the bottom, Ctrl+Shift+V paste through wtype, preserved clipboard, manual submission
and no cloud processing. Other preferences use pinned app defaults. A user service
copies the baseline into Handy's writable store on each start, allowing migrations
while resetting GUI edits. Models/recordings stay outside Nix; the selected model
is already downloaded on Zenith. Other hosts still need model provisioning.
Super+Shift+D toggles; Super+Ctrl+D cancels. Super+D push-to-talk awaits the user's
decision about a keyboard listener: Niri 26.04 has no key-release bindings.
Noctalia suppresses microphone activity popups/indicators; camera and screen-sharing
indications remain. Automatic paste and live capture need interactive checks.
Hang investigation: stopping live Vulkan transcription never completed; cancel
returned the UI to idle but subsequent requests reported pipeline busy. Handy was
no longer running when inspected; no crash cause is established. The same saved
1.56-second recording completed in CPU batch mode (135 ms inference, 729 ms load)
and Vulkan batch mode (156 ms inference, 420 ms load). This checks model inference,
not live capture/stop/cancel. No new audio was recorded. Switching to CPU is not a
confirmed fix. After activation, retry live recording and stopping, and inspect
the service logs if it hangs.
Verification: formatting, flake evaluation, both workstation builds and whitespace
checks passed. Handy loaded the generated settings in an isolated data directory
without settings parse errors. System activation remains user-owned and pending.
The focus is the broader environment, not further API/database polish. Candidate
areas remain places/ownership, navigation/interaction, human/agent collaboration,
+2 -2
View File
@@ -29,8 +29,8 @@ use a flake to declare its tools without Nix owning the surrounding workspace.
interaction with useful connections to the rest of the environment.
- Use Handy for local plain dictation, mainly English with occasional Dutch.
Use native Wayland automatic paste and optional live text with a streaming model.
Niri owns recording shortcuts;
model downloads and microphone preferences remain application-owned. Cleanup
Nix owns Handy preferences and desktop integration; the user service restores
settings on each start. Model downloads and recordings remain application data. Cleanup
through Codex, a cloud API or a local language model is deferred.
- Outlook is acceptable as an application, but its interaction and integration
do not fit as naturally. Accessible mail for tools and agents matters separately
+46
View File
@@ -0,0 +1,46 @@
{
flake.modules.homeManager.handy =
{ config, pkgs, ... }:
let
settings = (pkgs.formats.json { }).generate "handy-settings.json" {
settings = {
settings_schema_version = 1;
selected_model = "handy-computer/nemotron-3.5-asr-streaming-0.6b-gguf/nemotron-3.5-asr-streaming-0.6b-Q8_0.gguf";
selected_language = "auto";
translate_to_english = false;
selected_microphone = null;
always_on_microphone = false;
push_to_talk = true;
paste_method = "ctrl_shift_v";
typing_tool = "wtype";
clipboard_handling = "dont_modify";
auto_submit = false;
overlay_style = "live";
overlay_position = "bottom";
post_process_enabled = false;
start_hidden = true;
autostart_enabled = false;
update_checks_enabled = false;
show_whats_new_on_update = false;
onboarding_completed = true;
};
};
in
{
home.packages = [ pkgs.handy ];
systemd.user.services.handy = {
Unit = {
Description = "Handy dictation";
PartOf = [ "graphical-session.target" ];
After = [ "graphical-session.target" ];
};
Install.WantedBy = [ "graphical-session.target" ];
Service = {
# Handy writes migrations and runtime preferences: restore the Nix baseline on start.
ExecStartPre = "${pkgs.coreutils}/bin/install -Dm600 ${settings} ${config.xdg.dataHome}/com.pais.handy/settings_store.json";
ExecStart = "${pkgs.handy}/bin/handy --start-hidden";
};
};
};
}
+2 -2
View File
@@ -36,7 +36,7 @@ in
};
# Dictation
"Mod+D" = {
"Mod+Shift+D" = {
spawn = [
handy
"--toggle-transcription"
@@ -44,7 +44,7 @@ in
_props.repeat = false;
_props.hotkey-overlay-title = "Toggle Dictation";
};
"Mod+Shift+D" = {
"Mod+Ctrl+D" = {
spawn = [
handy
"--cancel"
-8
View File
@@ -41,19 +41,11 @@ in
playerctl
brightnessctl
wireplumber
handy
wtype
]
++ [ swapMonitorWorkspaces ];
};
desktop.autostart = [
[
(lib.getExe pkgs.handy)
"--start-hidden"
]
];
dconf.settings."org/gnome/desktop/interface".color-scheme = "prefer-dark";
wayland.windowManager.niri = {
+1
View File
@@ -43,6 +43,7 @@ in
imports = [
homeModules.passwords
homeModules.clipboard
homeModules.handy
homeModules.desktop-commands
homeModules.dev-tools
homeModules.xdg