Give Sound the whole story, and keep the buttons inside the card

Claude-Session: https://claude.ai/code/session_01Ms2FbjQy31TVf3CEvQhGM8
This commit is contained in:
Gabriel Brown
2026-08-24 11:39:16 -04:00
parent 9bc68ba358
commit b58371bb35
38 changed files with 3884 additions and 213 deletions
@@ -1,3 +1,12 @@
// The devices of one direction, as a list you choose from.
//
// The list tells the whole story, which means it also has to account for the
// device that is not here: a session whose default is a pair of AirPods in
// their case used to show no trace of them, so the output that audio will
// return to the moment they connect looked like something Panama had
// forgotten. SoundDefaults knows the configured name, and a configured name
// with no node behind it renders last, dimmed and inert -- a ghost row.
import QtQuick
import Quickshell.Services.Pipewire
import qs.config
@@ -10,8 +19,14 @@ Column {
readonly property var nodes: AudioDevices.nodes(root.output)
readonly property var current: AudioDevices.current(root.output)
// { name, label } for the configured default that is not present, or null
// when everything the session chose is here. The service owns both the
// comparison and the naming: an absent device has no description to borrow,
// and its stored name is all there is to read it from.
readonly property var absent: SoundDefaults.absent(root.output)
width: parent ? parent.width : 620
spacing: 8
spacing: 0
Repeater {
model: root.nodes
@@ -26,9 +41,17 @@ Column {
}
}
SoundDeviceRow {
width: root.width
visible: !!root.absent
output: root.output
ghost: true
ghostLabel: root.absent ? String(root.absent.label) : ""
}
Text {
width: parent.width
visible: root.nodes.length === 0
visible: root.nodes.length === 0 && !root.absent
text: Pipewire.ready ? "No audio devices found" : "Discovering audio devices…"
color: Theme.fgDim
font.family: Theme.fontFamily