Files
Panama/config/dot/quickshell/modules/settings/SettingsShell.qml
T
Gabriel Brown 1f40f8e136 Add a snapshot manager, and start covering home
The machine already had snapper running hourly on btrfs, so the tool was
never missing. What was missing is that snapper's only configuration
covered / -- and /home is a separate subvolume with no configuration at
all. Six hundred and forty-three snapshots existed and not one of them
contained a document. Anyone reaching for file history would have found
their system and none of their files.

/home now has a configuration on the same hourly timeline, with
deliberately conservative retention: Steam's 1.2 TB lives on that
subvolume and churns on every game update, so keeping five hourly and
seven daily bounds what those updates can pin.

Per volume, because on this machine "one is covered and the important
one is not" was the news, and a timeline opening on system snapshots
would have buried it. Inside a volume the timeline is the familiar view:
points in time, newest first, each openable as a folder tree to take a
file out of.

Restoring sets the current version aside as .before-restore-N rather
than overwriting it. A restore that destroys the thing you were about to
compare against is how someone loses the work they were trying to save.

Rollback is deliberately absent. snapper's rollback changes the btrfs
default subvolume, and this system's fstab pins subvol= explicitly,
which overrides it -- so a rollback would report success and change
nothing after a reboot. A recovery feature that silently does nothing is
worse than not having one, and making it work means editing fstab and
the bootloader, whose failure cannot be repaired from inside the
desktop.

Per-snapshot size is reported as not measured, because measuring it
needs btrfs quotas that cost performance on every write. Free space is
shown instead, which is the number that decides anything.

Claude-Session: https://claude.ai/code/session_01BRvzt4H8XXLPVH5MyYdk9L
2026-08-19 16:11:50 -04:00

195 lines
7.1 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 "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 "users": return usersPage;
case "sharing": return sharingPage;
case "printers": return printersPage;
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: usersPage; UsersPage {} }
Component { id: sharingPage; SharingPage {} }
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: 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()
}
}