Files
Panama/config/dot/quickshell/modules/settings/SettingsShell.qml
T
Gabriel Brown 7578348db1 Merge Home & Phone into a three-tab Home that knows your house
Home is now Overview | My Home | Phone. Overview leads with quick-action
tiles (focus, Do Not Disturb, health, snapshots, storage), keeps the
findings card — updates fold in, the reclaim-space prompt is gone on
purpose — and adds glance cards, the next calendar event, and weather.

My Home groups every light by Home Assistant area: the helper gained an
`areas` command (one REST template render, no websocket), and the rooms
degrade to a flat list on setups without areas. The favorites editor and
connection card moved intact. Phone gains a vitals strip — battery and
cell signal read from KDE Connect's plugin D-Bus objects, where absence
is data, not an error — beside ring, clipboard, send-a-file, and the
BlueBubbles handoff.

The retired home-phone id resolves to my-home forever via a new alias
map in SettingsRoutes (with a hasOwnProperty guard so prototype names
cannot leak into settingsPage). Storage no longer claims 0 B free — the
old page read a field the disks helper never emitted.

Contracts updated alongside; per the new workflow, the full suite runs
once at the end of the redesign (see the test backlog note).

Claude-Session: https://claude.ai/code/session_01Ms2FbjQy31TVf3CEvQhGM8
2026-08-23 22:06:18 -04:00

239 lines
8.8 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 myHomeDiagnostics: pageLoader.status === Loader.Ready
&& pageLoader.item
&& pageLoader.item.objectName === "my-home-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)
}
// The tab strip for the current category, rendered above the page so the
// leaf pages themselves stay untouched by the grouping. A category with a
// single subject earns no strip.
readonly property var categoryTabs: SettingsRoutes.tabsFor(ShellState.settingsPage)
Item {
id: categoryStrip
readonly property bool shown: root.categoryTabs.length > 1
anchors.left: sidebar.right
anchors.right: parent.right
anchors.top: titlebar.bottom
anchors.leftMargin: 34
anchors.rightMargin: 34
height: shown ? 48 : 0
visible: shown
SettingsTabs {
anchors.left: parent.left
anchors.right: parent.right
anchors.bottom: parent.bottom
tabs: root.categoryTabs.map(tab => ({ value: tab.page, label: tab.label }))
current: ShellState.settingsPage
onSelected: value => ShellState.openSettings(value)
}
}
Loader {
id: pageLoader
anchors.left: sidebar.right
anchors.right: parent.right
anchors.top: categoryStrip.bottom
anchors.bottom: parent.bottom
sourceComponent: {
switch (ShellState.settingsPage) {
case "appearance": return appearancePage;
case "displays": return displaysPage;
case "connectivity": return connectivityPage;
case "my-home": return myHomePage;
case "phone": return phonePage;
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 "dictation": return dictationPage;
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 "ssh-keys": return sshKeysPage;
case "services": return healthPage;
case "manual": return manualPage;
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: sshKeysPage; SshKeysPage {} }
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: myHomePage; MyHomePage {} }
Component { id: phonePage; PhonePage {} }
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: dictationPage; DictationPage {} }
Component { id: privacyPage; PrivacyPage {} }
Component { id: regionPage; RegionPage {} }
Component { id: onlineAccountsPage; OnlineAccountsPage {} }
Component { id: healthPage; HealthPage {} }
Component { id: manualPage; ManualPage {} }
Component { id: aboutPage; AboutPage {} }
Shortcut {
sequence: "Escape"
onActivated: ShellState.closeSettings()
}
}