Files
Panama/config/dot/quickshell/modules/settings/SettingsShell.qml
T
Gabriel Brown ac5e6e2130 Add a Containers page, grouped by project and led by what is exposed
Every container on this machine is created by rootless podman-compose and
labelled with the project it belongs to, so the grouping is read from the
labels rather than invented. State then decides prominence within that
grouping -- running containers get rows, stopped ones collapse to a line --
which is why neither axis had to be chosen over the other.

Acting on a stack uses plain podman over the labelled set, never
`podman-compose down`, which would remove containers this shell did not
create. The compose file is the source of truth for what exists and belongs
to the repository. Nothing here needs privilege.

The findings on top are the crossing the Firewall page reports, seen from the
side that can close it: the firewall knows only that something is listening,
while this page knows which container, which compose file, and which token is
missing from it. So `bind-local` prepends a loopback address and leaves the
line byte-for-byte -- variables, quoting and style intact -- then re-parses and
rolls back unless exactly those ports moved. It refuses anything ambiguous
rather than guessing. Rewriting the mapping to the port podman reports today
would have deleted the ${POSTGRES_PORT} indirection that makes it
configurable at all.

Unused volumes are read from podman's own dangling filter. The first version
used MountCount, which is a runtime lock counter and not a usage signal: it
reads zero for a volume a running container has mounted this second, so
"remove unused volumes" offered to delete the live Command Center database.
The cross-check against `podman system df` is what exposed it. The contract
reintroduces that bug deliberately and fails if the guard does not catch it,
because a guard nobody has seen fail proves nothing.

Every mutation in the contract runs against a stubbed podman. Nothing in the
suite starts, stops or removes a real container, image or volume.

Claude-Session: https://claude.ai/code/session_01BRvzt4H8XXLPVH5MyYdk9L
2026-08-19 20:49:04 -04:00

203 lines
7.5 KiB
QML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import QtQuick
import qs.config
import qs.services
Rectangle {
id: root
property var hostWindow: null
readonly property var homePhoneDiagnostics: pageLoader.status === Loader.Ready
&& pageLoader.item
&& pageLoader.item.objectName === "home-phone-page"
? pageLoader.item.pageDiagnostics
: ({})
readonly property var healthDiagnostics: pageLoader.status === Loader.Ready
&& pageLoader.item
&& pageLoader.item.objectName === "system-health-page"
? pageLoader.item.uiDiagnostics()
: ({})
function requestHealthAction(id: string): bool {
if (pageLoader.status !== Loader.Ready
|| !pageLoader.item
|| pageLoader.item.objectName !== "system-health-page")
return false;
return pageLoader.item.activateRenderedAction(id);
}
color: Theme.bg
radius: 18
border.width: 1
border.color: Theme.alpha(Theme.fg, 0.08)
clip: true
SettingsSidebar {
id: sidebar
anchors.left: parent.left
anchors.top: titlebar.bottom
anchors.bottom: parent.bottom
selectedPage: ShellState.settingsPage
onPageRequested: page => ShellState.openSettings(page)
}
Rectangle {
id: titlebar
anchors.left: parent.left
anchors.right: parent.right
anchors.top: parent.top
height: 48
color: Theme.alpha(Theme.bgDark, 0.97)
border.width: 0
Text {
anchors.left: parent.left
anchors.leftMargin: 18
anchors.verticalCenter: parent.verticalCenter
text: "Settings"
color: Theme.fg
font.family: Theme.fontFamily
font.pixelSize: Theme.fontSize
font.weight: Font.DemiBold
}
Row {
anchors.right: parent.right
anchors.rightMargin: 12
anchors.verticalCenter: parent.verticalCenter
spacing: 7
Rectangle {
width: 28; height: 28; radius: 9
color: minMouse.containsMouse ? Theme.alpha(Theme.fg, 0.12) : Theme.alpha(Theme.fg, 0)
Text { anchors.centerIn: parent; text: "—"; color: Theme.fgDim; font.family: Theme.fontFamily; font.pixelSize: Theme.fontSize }
MouseArea { id: minMouse; anchors.fill: parent; hoverEnabled: true; cursorShape: Qt.PointingHandCursor; onClicked: if (root.hostWindow) root.hostWindow.minimized = true }
}
Rectangle {
width: 28; height: 28; radius: 9
color: closeMouse.containsMouse ? Theme.alpha(Theme.danger, 0.17) : Theme.alpha(Theme.fg, 0)
Text { anchors.centerIn: parent; text: "×"; color: closeMouse.containsMouse ? Theme.danger : Theme.fgDim; font.family: Theme.fontFamily; font.pixelSize: 18 }
MouseArea { id: closeMouse; anchors.fill: parent; hoverEnabled: true; cursorShape: Qt.PointingHandCursor; onClicked: ShellState.closeSettings() }
}
}
DragHandler {
target: null
onActiveChanged: if (active && root.hostWindow) root.hostWindow.startSystemMove()
}
}
Rectangle {
anchors.left: sidebar.right
anchors.top: titlebar.bottom
anchors.bottom: parent.bottom
width: 1
color: Theme.alpha(Theme.fg, 0.06)
}
Loader {
id: pageLoader
anchors.left: sidebar.right
anchors.right: parent.right
anchors.top: titlebar.bottom
anchors.bottom: parent.bottom
sourceComponent: {
switch (ShellState.settingsPage) {
case "appearance": return appearancePage;
case "displays": return displaysPage;
case "connectivity": return connectivityPage;
case "home-phone": return homePhonePage;
case "desktop": return desktopPage;
case "sound": return soundPage;
case "gaming": return gamingPage;
case "notifications": return notificationsPage;
case "screen-intelligence": return screenIntelligencePage;
case "shortcuts": return shortcutsPage;
case "mouse": return mousePage;
case "privacy": return privacyPage;
case "region": return regionPage;
case "accounts": return onlineAccountsPage;
case "accessibility": return accessibilityPage;
case "power": return powerPage;
case "datetime": return dateTimePage;
case "applications": return applicationsPage;
case "storage": return storagePage;
case "snapshots": return snapshotsPage;
case "updates": return updatesPage;
case "users": return usersPage;
case "sharing": return sharingPage;
case "firewall": return firewallPage;
case "printers": return printersPage;
case "containers": return containersPage;
case "services": return healthPage;
case "about": return aboutPage;
default: return homePage;
}
}
}
Rectangle {
anchors.right: parent.right
anchors.rightMargin: 22
anchors.bottom: parent.bottom
anchors.bottomMargin: 20
width: Math.min(460, errorCopy.implicitWidth + 34)
height: 42
radius: 12
visible: SystemSettings.lastError !== ""
color: Theme.mix(Theme.bgPopover, Theme.danger, 0.1)
border.width: 1
border.color: Theme.alpha(Theme.danger, 0.28)
Text {
id: errorCopy
anchors.centerIn: parent
text: SystemSettings.lastError
color: Theme.fg
font.family: Theme.fontFamily
font.pixelSize: Theme.fontSizeSmall
}
MouseArea {
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
onClicked: SystemSettings.lastError = ""
}
}
Component { id: homePage; HomePage {} }
Component { id: applicationsPage; ApplicationsPage {} }
Component { id: storagePage; StoragePage {} }
Component { id: snapshotsPage; SnapshotsPage {} }
Component { id: updatesPage; UpdatesPage {} }
Component { id: usersPage; UsersPage {} }
Component { id: sharingPage; SharingPage {} }
Component { id: firewallPage; FirewallPage {} }
Component { id: containersPage; ContainersPage {} }
Component { id: printersPage; PrintersPage {} }
Component { id: accessibilityPage; AccessibilityPage {} }
Component { id: powerPage; PowerPage {} }
Component { id: dateTimePage; DateTimePage {} }
Component { id: appearancePage; AppearancePage {} }
Component { id: displaysPage; DisplaysPage {} }
Component { id: connectivityPage; ConnectivityPage {} }
Component { id: homePhonePage; HomePhonePage {} }
Component { id: desktopPage; DesktopPage {} }
Component { id: soundPage; SoundPage {} }
Component { id: gamingPage; GamingPage {} }
Component { id: notificationsPage; NotificationsPage {} }
Component { id: screenIntelligencePage; ScreenIntelligencePage {} }
Component { id: shortcutsPage; ShortcutsPage {} }
Component { id: mousePage; MousePage {} }
Component { id: privacyPage; PrivacyPage {} }
Component { id: regionPage; RegionPage {} }
Component { id: onlineAccountsPage; OnlineAccountsPage {} }
Component { id: healthPage; HealthPage {} }
Component { id: aboutPage; AboutPage {} }
Shortcut {
sequence: "Escape"
onActivated: ShellState.closeSettings()
}
}