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
153 lines
5.2 KiB
QML
153 lines
5.2 KiB
QML
import QtQuick
|
|
import qs.config
|
|
import qs.services
|
|
|
|
SettingsPage {
|
|
title: "Sound"
|
|
lede: "Live PipeWire output, input, and device selection."
|
|
|
|
SettingsCard {
|
|
title: "Output"
|
|
subtitle: AudioDevices.current(true)?.description ?? "No output device"
|
|
|
|
SoundDeviceList {
|
|
width: parent.width
|
|
output: true
|
|
}
|
|
|
|
AudioBalance {
|
|
width: parent.width
|
|
node: AudioDevices.current(true)
|
|
}
|
|
}
|
|
|
|
SettingsCard {
|
|
title: "Input"
|
|
subtitle: AudioDevices.current(false)?.description ?? "No input device"
|
|
|
|
SoundDeviceList {
|
|
width: parent.width
|
|
output: false
|
|
}
|
|
}
|
|
|
|
// Beside Input on purpose: dictation listens through whichever device that
|
|
// card selects, and putting the two together is what makes that obvious.
|
|
SettingsCard {
|
|
title: "Dictation"
|
|
subtitle: Dictation.ready
|
|
? "Hold Super+D, speak, and release. The words are typed where the cursor is."
|
|
: "Speech to text, on the GPU. Hold Super+D once it is set up. The one-time setup below fetches a speech server and a ~490 MB model — neither ships with Panama, because both are large and want the network."
|
|
|
|
// Status of the two pieces, read from the machine rather than guessed.
|
|
TextRow {
|
|
label: "Speech server"
|
|
detail: Dictation.serverReady
|
|
? "Running"
|
|
: (Dictation.imageBuilt ? "Ready — starts on the first dictation" : "Not set up yet")
|
|
value: Dictation.imageBuilt ? "Ready" : "Missing"
|
|
}
|
|
|
|
TextRow {
|
|
label: "Speech model"
|
|
detail: Dictation.modelInstalled
|
|
? "Kept across rebuilds of the server"
|
|
: "About 490 MB, downloaded once"
|
|
value: Dictation.modelInstalled ? Math.round(Dictation.modelBytes / 1048576) + " MB" : "Missing"
|
|
}
|
|
|
|
// ONE action that actually works: panama-dictate setup pulls the
|
|
// server image and downloads the model together. This card used to
|
|
// offer a Download button wired to a command the helper does not have,
|
|
// and told you to build a "panama app whisper-vulkan" that does not
|
|
// exist -- so nothing here did anything. It does now.
|
|
ActionRow {
|
|
visible: !Dictation.ready || Dictation.downloading
|
|
label: "Set up dictation"
|
|
detail: {
|
|
if (!Dictation.downloading)
|
|
return "Fetches the speech server and the model. Runs once, keeps both.";
|
|
if (Dictation.phase === "pulling")
|
|
return "Fetching the speech server…";
|
|
if (Dictation.downloadTotalBytes > 0)
|
|
return "Downloading model — " + Math.round(Dictation.downloadFraction * 100)
|
|
+ "% of " + Math.round(Dictation.downloadTotalBytes / 1048576) + " MB";
|
|
return "Setting up…";
|
|
}
|
|
action: Dictation.downloading ? "Working…" : "Set up"
|
|
enabled: !Dictation.downloading
|
|
onTriggered: Dictation.setup()
|
|
divider: !Dictation.typingAvailable || Dictation.lastError !== ""
|
|
}
|
|
|
|
TextRow {
|
|
visible: !Dictation.typingAvailable
|
|
label: "Typing"
|
|
detail: "wtype is missing, so dictated text would go to the clipboard instead of being typed."
|
|
value: "Missing"
|
|
divider: Dictation.lastError !== ""
|
|
}
|
|
|
|
TextRow {
|
|
visible: Dictation.lastError !== ""
|
|
label: "Problem"
|
|
detail: Dictation.lastError
|
|
value: ""
|
|
divider: false
|
|
}
|
|
}
|
|
|
|
SettingsCard {
|
|
title: "Applications"
|
|
subtitle: "Control each application currently playing through PipeWire."
|
|
|
|
ApplicationMixer {
|
|
width: parent.width
|
|
}
|
|
}
|
|
|
|
SettingsCard {
|
|
title: "Sound feedback"
|
|
subtitle: "Use the same event preferences as GTK and GNOME applications."
|
|
|
|
SettingRow {
|
|
label: "Event sounds"
|
|
detail: "Play alerts and interface event sounds"
|
|
controlWidth: 42
|
|
|
|
SettingsToggle {
|
|
anchors.fill: parent
|
|
checked: SoundFeedback.eventSounds
|
|
enabled: !SoundFeedback.busy
|
|
onToggled: checked => SoundFeedback.setEventSounds(checked)
|
|
}
|
|
}
|
|
|
|
SettingRow {
|
|
label: "Input feedback"
|
|
detail: "Play sounds for supported typing and input events"
|
|
controlWidth: 42
|
|
divider: false
|
|
|
|
SettingsToggle {
|
|
anchors.fill: parent
|
|
checked: SoundFeedback.inputFeedback
|
|
enabled: !SoundFeedback.busy
|
|
onToggled: checked => SoundFeedback.setInputFeedback(checked)
|
|
}
|
|
}
|
|
}
|
|
|
|
SettingsCard {
|
|
title: "Advanced sound"
|
|
|
|
ActionRow {
|
|
label: "Device profiles"
|
|
detail: "Open Fedora's complete device profile panel"
|
|
divider: false
|
|
action: "Open panel"
|
|
onTriggered: SystemSettings.openGnomePanel("sound")
|
|
}
|
|
}
|
|
}
|