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
365 lines
14 KiB
QML
365 lines
14 KiB
QML
import QtQuick
|
|
import qs.config
|
|
import qs.services
|
|
|
|
Rectangle {
|
|
id: root
|
|
|
|
property string selectedPage: "home"
|
|
property string query: searchInput.text.trim().toLowerCase()
|
|
signal pageRequested(string page)
|
|
|
|
readonly property var results: SettingsSearch.search(root.query)
|
|
|
|
onQueryChanged: {
|
|
if (sidebarScroll)
|
|
sidebarScroll.contentY = 0;
|
|
}
|
|
|
|
function pageLabel(page: string): string {
|
|
const found = root.destinations.find(item => item.page === page);
|
|
return found ? found.label : "Settings";
|
|
}
|
|
|
|
readonly property var destinations: [
|
|
{ page: "home", label: "Home", icon: "\u{F02DC}" },
|
|
{ page: "appearance", label: "Appearance", icon: "\u{F0E0D}" },
|
|
{ page: "displays", label: "Displays", icon: "\u{F0379}" },
|
|
{ page: "connectivity", label: "Network & Devices", icon: "\u{F08D4}" },
|
|
{ page: "sharing", label: "Sharing", icon: "\u{F04E6}" },
|
|
{ page: "firewall", label: "Firewall", icon: "\u{F0483}" },
|
|
{ page: "printers", label: "Printers", icon: "\u{F042A}" },
|
|
{ page: "containers", label: "Containers", icon: "\u{F0868}" },
|
|
{ page: "home-phone", label: "Home & Phone", icon: "\u{F02DC}" },
|
|
{ page: "desktop", label: "Desktop & Dock", icon: "\u{F04A4}" },
|
|
{ page: "sound", label: "Sound", icon: "\u{F057E}" },
|
|
{ page: "gaming", label: "Gaming", icon: "\u{F0297}" },
|
|
{ page: "notifications", label: "Notifications & Focus", icon: "\u{F009A}" },
|
|
{ page: "screen-intelligence", label: "Screen Intelligence", icon: "\u{F05A8}" },
|
|
{ page: "shortcuts", label: "Keyboard", icon: "\u{F030C}" },
|
|
{ page: "mouse", label: "Mouse & Touchpad", icon: "\u{F037D}" },
|
|
{ page: "privacy", label: "Privacy & Security", icon: "\u{F0483}" },
|
|
{ page: "region", label: "Region & Language", icon: "\u{F0AC2}" },
|
|
{ page: "accounts", label: "Online Accounts", icon: "\u{F0004}" },
|
|
{ page: "accessibility", label: "Accessibility", icon: "\u{F0208}" },
|
|
{ page: "power", label: "Power & Lock", icon: "\u{F0425}" },
|
|
{ page: "datetime", label: "Date & Time", icon: "\u{F0954}" },
|
|
{ page: "applications", label: "Applications", icon: "\u{F003B}" },
|
|
{ page: "updates", label: "Software Update", icon: "\u{F06B0}" },
|
|
{ page: "storage", label: "Storage", icon: "\u{F02CA}" },
|
|
{ page: "snapshots", label: "Snapshots", icon: "\u{F0954}" },
|
|
{ page: "users", label: "Users", icon: "\u{F0004}" },
|
|
{ page: "services", label: "System Health", icon: "\u{F0493}" },
|
|
{ page: "about", label: "About", icon: "\u{F02FD}" }
|
|
]
|
|
|
|
width: 272
|
|
color: Theme.alpha(Theme.bgDark, 0.96)
|
|
border.width: 0
|
|
|
|
Column {
|
|
id: sidebarHeader
|
|
|
|
anchors.left: parent.left
|
|
anchors.right: parent.right
|
|
anchors.top: parent.top
|
|
anchors.leftMargin: 18
|
|
anchors.rightMargin: 18
|
|
anchors.topMargin: 18
|
|
height: implicitHeight
|
|
spacing: 12
|
|
|
|
Rectangle {
|
|
width: parent.width
|
|
height: 36
|
|
radius: 10
|
|
color: Theme.alpha(Theme.fg, searchArea.containsMouse || searchInput.activeFocus ? 0.105 : 0.065)
|
|
border.width: 1
|
|
border.color: searchInput.activeFocus ? Theme.alpha(Theme.accent, 0.5) : Theme.alpha(Theme.fg, 0.055)
|
|
|
|
Text {
|
|
anchors.left: parent.left
|
|
anchors.leftMargin: 11
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
text: "\u{F0349}"
|
|
color: Theme.fgDim
|
|
font.family: Theme.fontMono
|
|
font.pixelSize: 14
|
|
}
|
|
|
|
TextInput {
|
|
id: searchInput
|
|
anchors.left: parent.left
|
|
anchors.leftMargin: 35
|
|
anchors.right: parent.right
|
|
anchors.rightMargin: 10
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
color: Theme.fg
|
|
selectionColor: Theme.accent
|
|
selectedTextColor: Theme.bgDark
|
|
font.family: Theme.fontFamily
|
|
font.pixelSize: Theme.fontSize
|
|
clip: true
|
|
|
|
Text {
|
|
anchors.fill: parent
|
|
visible: !searchInput.text && !searchInput.activeFocus
|
|
text: "Search settings"
|
|
color: Theme.fgMuted
|
|
font: searchInput.font
|
|
verticalAlignment: Text.AlignVCenter
|
|
}
|
|
}
|
|
|
|
MouseArea {
|
|
id: searchArea
|
|
anchors.fill: parent
|
|
hoverEnabled: true
|
|
cursorShape: Qt.IBeamCursor
|
|
onClicked: searchInput.forceActiveFocus()
|
|
}
|
|
}
|
|
}
|
|
|
|
Flickable {
|
|
id: sidebarScroll
|
|
|
|
anchors.left: parent.left
|
|
anchors.right: parent.right
|
|
anchors.top: sidebarHeader.bottom
|
|
anchors.bottom: healthFooter.top
|
|
anchors.leftMargin: 18
|
|
anchors.rightMargin: 18
|
|
anchors.topMargin: 12
|
|
anchors.bottomMargin: 12
|
|
contentWidth: width
|
|
contentHeight: scrollContent.implicitHeight
|
|
flickableDirection: Flickable.VerticalFlick
|
|
boundsBehavior: Flickable.StopAtBounds
|
|
clip: true
|
|
|
|
Column {
|
|
id: scrollContent
|
|
|
|
width: sidebarScroll.width
|
|
|
|
// ── Search results ──────────────────────────────────────────────
|
|
// Typing searches the settings themselves, not page names.
|
|
Column {
|
|
id: searchResults
|
|
|
|
width: parent.width
|
|
spacing: 3
|
|
visible: root.query !== ""
|
|
|
|
Text {
|
|
width: parent.width
|
|
leftPadding: 4
|
|
bottomPadding: 4
|
|
text: root.results.length === 0
|
|
? "Nothing matches"
|
|
: root.results.length + (root.results.length === 1 ? " result" : " results")
|
|
color: Theme.fgMuted
|
|
font.family: Theme.fontFamily
|
|
font.pixelSize: Theme.fontSizeSmall
|
|
}
|
|
|
|
Repeater {
|
|
model: root.results
|
|
|
|
Rectangle {
|
|
id: hit
|
|
|
|
required property var modelData
|
|
|
|
width: parent.width
|
|
height: 44
|
|
radius: 10
|
|
color: hitMouse.containsMouse ? Theme.alpha(Theme.fg, 0.08) : "transparent"
|
|
border.width: 0
|
|
|
|
Column {
|
|
anchors.left: parent.left
|
|
anchors.right: parent.right
|
|
anchors.leftMargin: 12
|
|
anchors.rightMargin: 10
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
spacing: 1
|
|
|
|
Text {
|
|
width: parent.width
|
|
text: hit.modelData.label
|
|
color: Theme.fg
|
|
font.family: Theme.fontFamily
|
|
font.pixelSize: Theme.fontSize
|
|
elide: Text.ElideRight
|
|
}
|
|
|
|
Text {
|
|
width: parent.width
|
|
text: hit.modelData.kind === "shortcut"
|
|
? hit.modelData.detail
|
|
: root.pageLabel(hit.modelData.page)
|
|
color: hit.modelData.kind === "shortcut" ? Theme.accent : Theme.fgMuted
|
|
font.family: Theme.fontFamily
|
|
font.pixelSize: Theme.fontSizeSmall
|
|
elide: Text.ElideRight
|
|
}
|
|
}
|
|
|
|
MouseArea {
|
|
id: hitMouse
|
|
anchors.fill: parent
|
|
hoverEnabled: true
|
|
cursorShape: Qt.PointingHandCursor
|
|
onClicked: {
|
|
root.pageRequested(hit.modelData.page);
|
|
searchInput.text = "";
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
Column {
|
|
id: navigationList
|
|
|
|
width: parent.width
|
|
spacing: 4
|
|
visible: root.query === ""
|
|
|
|
Repeater {
|
|
model: root.destinations
|
|
|
|
Rectangle {
|
|
id: navItem
|
|
required property var modelData
|
|
width: parent.width
|
|
height: 40
|
|
radius: 10
|
|
color: modelData.page === root.selectedPage
|
|
? Theme.alpha(Theme.accent, 0.17)
|
|
: (navMouse.containsMouse ? Theme.alpha(Theme.fg, Theme.hoverAlpha * 0.55) : Theme.alpha(Theme.fg, 0))
|
|
border.width: modelData.page === root.selectedPage ? 1 : 0
|
|
border.color: Theme.alpha(Theme.accent, 0.26)
|
|
|
|
Rectangle {
|
|
width: 2
|
|
height: 18
|
|
radius: 1
|
|
anchors.left: parent.left
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
visible: navItem.modelData.page === root.selectedPage
|
|
gradient: Gradient {
|
|
GradientStop { position: 0; color: Theme.accent }
|
|
GradientStop { position: 1; color: Theme.accentSecondary }
|
|
}
|
|
}
|
|
|
|
Text {
|
|
anchors.left: parent.left
|
|
anchors.leftMargin: 13
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
width: 25
|
|
text: navItem.modelData.icon
|
|
color: navItem.modelData.page === root.selectedPage ? Theme.accent : Theme.fgDim
|
|
font.family: Theme.fontMono
|
|
font.pixelSize: 15
|
|
}
|
|
|
|
Text {
|
|
anchors.left: parent.left
|
|
anchors.leftMargin: 47
|
|
anchors.right: parent.right
|
|
anchors.rightMargin: 9
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
text: navItem.modelData.label
|
|
color: navItem.modelData.page === root.selectedPage ? Theme.fg : Theme.fgDim
|
|
font.family: Theme.fontFamily
|
|
font.pixelSize: Theme.fontSize
|
|
font.weight: navItem.modelData.page === root.selectedPage ? Font.Medium : Font.Normal
|
|
elide: Text.ElideRight
|
|
}
|
|
|
|
MouseArea {
|
|
id: navMouse
|
|
anchors.fill: parent
|
|
hoverEnabled: true
|
|
cursorShape: Qt.PointingHandCursor
|
|
onClicked: root.pageRequested(navItem.modelData.page)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
Rectangle {
|
|
id: healthFooter
|
|
|
|
anchors.left: parent.left
|
|
anchors.right: parent.right
|
|
anchors.bottom: parent.bottom
|
|
height: 54
|
|
activeFocusOnTab: true
|
|
color: footerTap.hovered || activeFocus
|
|
? Theme.alpha(Theme.fg, 0.07)
|
|
: Theme.alpha(Theme.bg, 0.35)
|
|
border.width: activeFocus ? 2 : 0
|
|
border.color: Theme.accent
|
|
|
|
function footerText(): string {
|
|
if (Health.checks.length === 0)
|
|
return Health.diagnosticUnavailable ? "Health check unavailable" : "Checking the desktop";
|
|
if (Health.status === "error")
|
|
return "Desktop needs attention";
|
|
if (Health.status === "warning") {
|
|
const count = Health.summary.warnings + Health.summary.errors;
|
|
return count + (count === 1 ? " health observation" : " health observations");
|
|
}
|
|
return "Desktop is healthy";
|
|
}
|
|
|
|
function footerColor(): color {
|
|
if (Health.checks.length === 0)
|
|
return Health.diagnosticUnavailable ? Theme.danger : Theme.fgMuted;
|
|
if (Health.status === "error")
|
|
return Theme.danger;
|
|
if (Health.status === "warning")
|
|
return Theme.warn;
|
|
return Theme.ok;
|
|
}
|
|
|
|
Rectangle {
|
|
width: 7
|
|
height: 7
|
|
radius: 4
|
|
anchors.left: parent.left
|
|
anchors.leftMargin: 19
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
color: healthFooter.footerColor()
|
|
}
|
|
|
|
Text {
|
|
anchors.left: parent.left
|
|
anchors.leftMargin: 36
|
|
anchors.right: parent.right
|
|
anchors.rightMargin: 12
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
text: healthFooter.footerText()
|
|
color: Theme.fgDim
|
|
font.family: Theme.fontFamily
|
|
font.pixelSize: Theme.fontSizeSmall
|
|
elide: Text.ElideRight
|
|
}
|
|
|
|
TapHandler {
|
|
id: footerTap
|
|
onTapped: root.pageRequested("services")
|
|
}
|
|
|
|
Keys.onReturnPressed: root.pageRequested("services")
|
|
Keys.onSpacePressed: root.pageRequested("services")
|
|
}
|
|
}
|