Make the dictation setup button call a command that exists

The Download button on Sound → Dictation ran `panama-dictate download`. There
is no such command: the helper answered {"ok": false, "error":
"unknown-command"} and the button did nothing, every time, for anyone who
tried it. The real command is `setup`, which pulls the speech server image
and downloads the ~490 MB model together -- one trip, by design. And the card
told you the server needed `panama app whisper-vulkan`, an app that exists in
no package or setup/apps entry; the server is a prebuilt image the helper
pulls with podman.

So the card described three things and got all three wrong. It now shows the
real state of both pieces and offers one action -- "Set up dictation" -- wired
to the command that actually fetches them, with progress that names which half
is running (server image, then model). Verified end to end: `panama-dictate
setup` emits the pulling/downloading JSON the service parses.

Claude-Session: https://claude.ai/code/session_01Epx9ZC1gwm81K3jm9x9CKh
This commit is contained in:
Gabriel Brown
2026-08-23 13:59:42 -04:00
parent e723fabed3
commit 14fc4fc8a3
2 changed files with 50 additions and 29 deletions
+17 -3
View File
@@ -33,6 +33,9 @@ Singleton {
property int modelBytes: 0
property bool downloading: false
// "pulling" while the server image comes down, "downloading" while the
// model does. Empty when idle. Lets the button say which half is running.
property string phase: ""
property int downloadedBytes: 0
property int downloadTotalBytes: 0
property string lastError: ""
@@ -76,7 +79,12 @@ Singleton {
Process {
id: downloadRun
command: [root.helper, "download"]
// "setup", not "download": the helper has no "download" command -- it
// has one "setup" that pulls the server image AND downloads the model,
// because being told to fetch half a gigabyte only to hear an image is
// also needed would be two trips. Calling the wrong name is why this
// button did nothing for so long.
command: [root.helper, "setup"]
stdout: SplitParser {
// One JSON object per line, roughly once a second, so the progress
// bar moves without the helper flooding a pipe nobody drains.
@@ -87,6 +95,8 @@ Singleton {
root.lastError = "The model could not be downloaded.";
return;
}
if (update.state !== undefined)
root.phase = String(update.state);
if (update.bytes !== undefined)
root.downloadedBytes = update.bytes;
if (update.total !== undefined)
@@ -99,8 +109,9 @@ Singleton {
}
onExited: (exitCode, exitStatus) => {
root.downloading = false;
root.phase = "";
if (exitCode !== 0 && root.lastError === "")
root.lastError = "The model could not be downloaded.";
root.lastError = "Dictation setup did not finish. Check that podman is installed and the network is up.";
root.refresh();
}
}
@@ -110,10 +121,13 @@ Singleton {
statusRun.running = true;
}
function download(): void {
// Pulls the speech server image and downloads the model, with progress.
// One action because the helper's one "setup" command does both.
function setup(): void {
if (downloadRun.running)
return;
root.lastError = "";
root.phase = "";
root.downloadedBytes = 0;
root.downloadTotalBytes = 0;
root.downloading = true;