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
+28 -1
View File
@@ -1,5 +1,10 @@
// Read-only contract harness for the Sound page. It instantiates every device
// row against the real PipeWire graph but exposes no mutating IPC methods.
//
// The two services that read the machine rather than the graph -- SoundDefaults
// and SoundCards -- are fed through their own fixture seams by the contract, so
// the ghost row and the profile card can be measured against canned state
// instead of against whatever hardware is plugged in today.
import Quickshell
import Quickshell.Io
@@ -34,6 +39,20 @@ ShellRoot {
height: 900
}
// A device list of its own, so the ghost row can be read without reaching
// into the page's internals.
SoundDeviceList {
id: outputList
width: 620
visible: false
output: true
}
SoundCaptureRow {
id: captureProbe
width: 620
}
ApplicationMixer {
id: populatedMixer
width: 620
@@ -78,7 +97,15 @@ ShellRoot {
emptyRows: emptyMixer.rowCount,
emptyStatus: emptyMixer.statusText,
unavailableRows: unavailableMixer.rowCount,
unavailableStatus: unavailableMixer.statusText
unavailableStatus: unavailableMixer.statusText,
captureApplications: AudioDevices.captureApplications.length,
captureTypesValid: AudioDevices.captureApplications.every(application =>
application.nodes.every(node =>
(node.type & PwNodeType.AudioInStream)
=== PwNodeType.AudioInStream)),
captureRowVisible: captureProbe.visible,
ghostVisible: !!outputList.absent,
ghostLabel: outputList.absent ? String(outputList.absent.label) : ""
});
}
}