diff --git a/config/dot/quickshell/modules/settings/ConnectivityPage.qml b/config/dot/quickshell/modules/settings/ConnectivityPage.qml index f387e07..a8f379a 100644 --- a/config/dot/quickshell/modules/settings/ConnectivityPage.qml +++ b/config/dot/quickshell/modules/settings/ConnectivityPage.qml @@ -169,24 +169,15 @@ SettingsPage { SettingsCard { title: "Owned by Fedora" - subtitle: "VPNs, per-connection routing, printers, and online accounts are configured by GNOME's panels, which are installed and searchable." + subtitle: "VPNs and per-connection routing are still configured by GNOME's panel, which is installed and searchable. Printers and online accounts have their own pages here." ActionRow { label: "Network connections" detail: "VPN, proxies, and per-connection settings" action: "Open" + divider: false onTriggered: SystemSettings.openGnomePanel("network") } - ActionRow { - label: "Printers" - action: "Open" - onTriggered: SystemSettings.openGnomePanel("printers") - } - ActionRow { - label: "Online accounts" - action: "Open" - divider: false - onTriggered: SystemSettings.openGnomePanel("online-accounts") - } + } } diff --git a/config/dot/quickshell/modules/settings/HealthPage.qml b/config/dot/quickshell/modules/settings/HealthPage.qml index f3094a0..f2d0c56 100644 --- a/config/dot/quickshell/modules/settings/HealthPage.qml +++ b/config/dot/quickshell/modules/settings/HealthPage.qml @@ -422,22 +422,6 @@ SettingsPage { } } - ActionRow { - objectName: "health-fedora-handoff:users" - label: "Users" - detail: "Accounts, passwords, and automatic login" - action: "Open users" - onTriggered: SystemSettings.openGnomePanel("system", "users") - } - - ActionRow { - objectName: "health-fedora-handoff:sharing" - label: "Sharing" - detail: "Remote desktop, media sharing, and remote login" - action: "Open sharing" - onTriggered: SystemSettings.openGnomePanel("sharing") - } - ActionRow { objectName: "health-fedora-handoff:color" label: "Color profiles" diff --git a/config/dot/quickshell/modules/settings/HomePhonePage.qml b/config/dot/quickshell/modules/settings/HomePhonePage.qml index c97641c..14d34ef 100644 --- a/config/dot/quickshell/modules/settings/HomePhonePage.qml +++ b/config/dot/quickshell/modules/settings/HomePhonePage.qml @@ -43,10 +43,14 @@ SettingsPage { return "Home Assistant is unavailable"; } + // The entity list is a one-time migration seed for the Control Center + // selection, not the light catalog -- the helper discovers that live from + // Home Assistant. It is passed back unchanged so that saving a URL or a + // token cannot disturb the seed. function saveHomeAssistantConfig(): void { HomeAssistantConfig.save( homeUrlInput.text, - homeEntitiesInput.text, + HomeAssistantConfig.entities.join(", "), homeTokenInput.text ); } @@ -57,7 +61,6 @@ SettingsPage { function onConfigurationSaved(): void { homeTokenInput.clear(); homeUrlInput.text = HomeAssistantConfig.url; - homeEntitiesInput.text = HomeAssistantConfig.entities.join(", "); } } @@ -130,65 +133,6 @@ SettingsPage { } } - Column { - width: parent.width - spacing: 7 - topPadding: 10 - bottomPadding: 12 - - Text { - width: parent.width - text: "Light entities" - color: Theme.fg - font.family: Theme.fontFamily - font.pixelSize: Theme.fontSize - font.weight: Font.Medium - } - - Text { - width: parent.width - text: "Comma-separated entity IDs. These define the discoverable light catalog." - color: Theme.fgDim - font.family: Theme.fontFamily - font.pixelSize: Theme.fontSizeSmall - wrapMode: Text.WordWrap - } - - Rectangle { - width: parent.width - height: 72 - radius: 10 - color: Theme.alpha(Theme.fg, 0.055) - border.width: homeEntitiesInput.activeFocus ? 2 : 1 - border.color: homeEntitiesInput.activeFocus - ? Theme.alpha(Theme.accent, 0.55) : Theme.alpha(Theme.fg, 0.06) - - TextEdit { - id: homeEntitiesInput - anchors.fill: parent - anchors.margins: 10 - activeFocusOnTab: true - text: HomeAssistantConfig.entities.join(", ") - color: Theme.fg - selectionColor: Theme.alpha(Theme.accent, 0.5) - selectedTextColor: Theme.fg - font.family: Theme.fontMono - font.pixelSize: Theme.fontSizeSmall - wrapMode: TextEdit.Wrap - clip: true - - Text { - anchors.fill: parent - visible: homeEntitiesInput.text === "" - text: "light.living_room, light.kitchen" - color: Theme.fgMuted - font: homeEntitiesInput.font - wrapMode: Text.WordWrap - } - } - } - } - Text { width: parent.width visible: HomeAssistantConfig.lastError !== "" diff --git a/config/dot/quickshell/modules/settings/NotificationsPage.qml b/config/dot/quickshell/modules/settings/NotificationsPage.qml index 2c3873e..1ae4ee1 100644 --- a/config/dot/quickshell/modules/settings/NotificationsPage.qml +++ b/config/dot/quickshell/modules/settings/NotificationsPage.qml @@ -25,7 +25,7 @@ SettingsPage { TextRow { label: "Notification history" detail: "Live notifications retained by the shell" - value: `${Notifs.history.length} items` + value: Notifs.history.length === 1 ? "1 item" : `${Notifs.history.length} items` } ActionRow { diff --git a/config/dot/quickshell/modules/settings/SharingPage.qml b/config/dot/quickshell/modules/settings/SharingPage.qml index 6e2d17c..95c59b1 100644 --- a/config/dot/quickshell/modules/settings/SharingPage.qml +++ b/config/dot/quickshell/modules/settings/SharingPage.qml @@ -107,11 +107,14 @@ SettingsPage { onToggled: value => Sharing.setRemoteDesktop(value) } - TextRow { + TextFieldRow { visible: Sharing.remoteDesktop?.available === true label: "Port" detail: "The RDP port other machines connect to" - value: String(Sharing.remoteDesktop?.port ?? "") + text: String(Sharing.remoteDesktop?.port ?? "") + placeholder: "3389" + enabled: !Sharing.busy + onAccepted: value => Sharing.setRdpPort(value) } SwitchRow { @@ -123,16 +126,6 @@ SettingsPage { onToggled: value => Sharing.setRdpViewOnly(value) } - TextFieldRow { - visible: Sharing.remoteDesktop?.available === true - label: "Port" - detail: "The port other machines connect to" - text: String(Sharing.remoteDesktop?.port ?? "") - placeholder: "3389" - enabled: !Sharing.busy - onAccepted: value => Sharing.setRdpPort(value) - } - // The password is typed into gnome-remote-desktop's own tool in a // terminal, never into this page. grdctl prompts for it on a terminal // and crashes without one, and passing it as an argument would publish diff --git a/config/dot/quickshell/scripts/panama-about b/config/dot/quickshell/scripts/panama-about index ca9918f..aeeed0b 100755 --- a/config/dot/quickshell/scripts/panama-about +++ b/config/dot/quickshell/scripts/panama-about @@ -121,7 +121,19 @@ emit "Memory" "$(awk '/^MemTotal:/ { printf "%.1f GiB", $2 / 1048576 }' /proc/me swap="$(awk '/^SwapTotal:/ { if ($2 > 0) printf "%.1f GiB", $2 / 1048576 }' /proc/meminfo 2>/dev/null)" emit "Swap" "$swap" -read -r size used avail <<<"$(df -h --output=size,used,avail / 2>/dev/null | tail -1)" -[[ -n "${size:-}" ]] && emit "Disk" "$avail free of $size" +# Decimal units with explicit GB/TB labels, matching the Storage page and the +# way drives are actually sold. `df -h` is binary but prints a bare "G", so the +# same drive read 488G here and 523 GB there. +read -r size avail <<<"$(df -B1 --output=size,avail / 2>/dev/null | tail -1)" +if [[ -n "${size:-}" ]]; then + emit "Disk" "$(awk -v a="$avail" -v s="$size" ' + function human(v, units, i) { + split("B KB MB GB TB PB", units, " ") + i = 1 + while (v >= 1000 && i < 6) { v /= 1000; i++ } + return sprintf("%.*f %s", (v < 10 && i > 2) ? 1 : 0, v, units[i]) + } + BEGIN { printf "%s free of %s", human(a), human(s) }')" +fi printf '[%s]\n' "$(IFS=,; printf '%s' "${facts[*]}")" diff --git a/tests/quickshell/gnome-handoff-contract.sh b/tests/quickshell/gnome-handoff-contract.sh new file mode 100755 index 0000000..819fbaf --- /dev/null +++ b/tests/quickshell/gnome-handoff-contract.sh @@ -0,0 +1,95 @@ +#!/usr/bin/env bash + +# No settings page may hand off to a GNOME panel that Panama itself owns. +# +# Panama absorbed Users, Sharing, Printers, Online Accounts and others one page +# at a time. Each time, the rows pointing at GNOME's equivalent stayed where +# they were -- so a settings app whose stated purpose is to make GNOME Settings +# unnecessary shipped four separate doors back to it, two of them inside a card +# headed "these areas remain owned by Fedora". +# +# Nothing failed. Every row worked exactly as written. The rows were simply no +# longer true, and no test could notice because none of them knew what Panama +# had come to own in the meantime. +# +# This reads the sidebar for the pages that exist and the pages for the panels +# they hand off, and fails on any overlap. It is deliberately derived from both +# sides rather than from a hand-kept list, so absorbing the next page cannot +# leave a stale door behind. +# +# Read-only. + +set -uo pipefail + +repo_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" +settings_dir="$repo_dir/config/dot/quickshell/modules/settings" +sidebar="$settings_dir/SettingsSidebar.qml" + +fail() { + printf 'gnome handoff contract: %s\n' "$1" >&2 + exit 1 +} + +[[ -r "$sidebar" ]] || fail "missing $sidebar" + +# GNOME panel names that correspond to a Panama page. Only entries whose panel +# genuinely duplicates a Panama page belong here: "network" stays off it because +# Panama has no VPN or per-connection routing, and "online-accounts" is listed +# because Panama has an Online Accounts page -- but adding an account still has +# to go through GOA's own dialog, so that one exception is named explicitly. +declare -A OWNED=( + [printers]=printers + [online-accounts]=accounts + [sharing]=sharing + [users]=users + [system\ users]=users +) + +# Handoffs that are correct despite naming an owned panel, with the reason. +# Anything here must be justified, not merely tolerated. +declare -A ALLOWED=( + ["OnlineAccountsPage.qml:online-accounts"]="adding an account requires GOA's own dialog" +) + +pages="$(grep -oE '\{ *page: "[a-z-]+"' "$sidebar" | sed 's/.*"\(.*\)"/\1/' | sort -u)" +[[ -n "$pages" ]] || fail 'no pages could be read from the sidebar, so this proves nothing' + +has_page() { + grep -qx "$1" <<<"$pages" +} + +violations=0 +checked=0 + +while IFS= read -r hit; do + file="${hit%%:*}" + base="$(basename "$file")" + # openGnomePanel("a") or openGnomePanel("a", "b") -> "a" / "a b" + panel="$(sed -E 's/.*openGnomePanel\("([^"]+)"(, *"([^"]+)")?\).*/\1 \3/' <<<"$hit" \ + | sed 's/ *$//')" + [[ -n "$panel" ]] || continue + checked=$((checked + 1)) + + owner="${OWNED[$panel]:-}" + [[ -n "$owner" ]] || continue + has_page "$owner" || continue + + key="$base:${panel// /-}" + if [[ -n "${ALLOWED[$key]:-}" ]]; then + continue + fi + + printf 'gnome handoff contract: %s hands "%s" to GNOME, but Panama owns the "%s" page\n' \ + "$base" "$panel" "$owner" >&2 + violations=$((violations + 1)) +done < <(grep -rno --include='*.qml' -E 'openGnomePanel\("[^"]*"(, *"[^"]*")?\)' "$settings_dir") + +(( checked > 0 )) || fail 'no handoffs were examined, so this proves nothing' + +if (( violations > 0 )); then + printf 'Each of these sends someone to GNOME for a page this app already has.\n' >&2 + exit 1 +fi + +printf 'gnome handoff contract: ok (%d handoffs checked against %d pages)\n' \ + "$checked" "$(wc -l <<<"$pages")" diff --git a/tests/quickshell/health-ui-contract.sh b/tests/quickshell/health-ui-contract.sh index 8abbb38..a27875c 100755 --- a/tests/quickshell/health-ui-contract.sh +++ b/tests/quickshell/health-ui-contract.sh @@ -60,10 +60,13 @@ rg -Fq 'Health.refresh()' "$settings_dir/HealthPage.qml" \ || fail 'opening System Health does not request a fresh scan' rg -Fq 'SystemSettings.openGnomePanel("network")' "$settings_dir/HealthPage.qml" \ || fail 'Fedora ownership boundary does not open GNOME Settings' +# Users and Sharing are Panama pages now. A handoff here would send someone to +# GNOME for a panel this app owns, which is the opposite of the point -- so the +# assertion is inverted rather than deleted. rg -Fq 'SystemSettings.openGnomePanel("system", "users")' "$settings_dir/HealthPage.qml" \ - || fail 'Fedora ownership boundary lost the Users handoff' + && fail 'System Health still hands Users to GNOME, but Panama owns that page' rg -Fq 'SystemSettings.openGnomePanel("sharing")' "$settings_dir/HealthPage.qml" \ - || fail 'Fedora ownership boundary lost the Sharing handoff' + && fail 'System Health still hands Sharing to GNOME, but Panama owns that page' rg -Fq 'SystemSettings.openGnomePanel("color")' "$settings_dir/HealthPage.qml" \ || fail 'Fedora ownership boundary lost the Color profiles handoff' rg -Fq 'SystemSettings.openGnomePanel("wellbeing")' "$settings_dir/HealthPage.qml" \ @@ -305,8 +308,6 @@ jq -e ' and (.renderedRows | map(.id) | unique | length) == 6 and .emptyQuietGroups == ["desktop-foundation"] and .fedoraHandoffs == [ - {id:"users", label:"Users", action:"Open users"}, - {id:"sharing", label:"Sharing", action:"Open sharing"}, {id:"color", label:"Color profiles", action:"Open color"}, {id:"wellbeing", label:"Digital wellbeing", action:"Open wellbeing"} ]