Complete Panama settings controls

This commit is contained in:
Gabriel Brown
2026-08-18 01:36:07 -04:00
parent 768801dbe4
commit 1ca571458c
11 changed files with 725 additions and 555 deletions
+10 -10
View File
@@ -28,13 +28,13 @@ Singleton {
// label deliberately general rather than exposing precise coordinates in
// the UI or guessing at a city from them.
readonly property string weatherLocation: "Local weather"
readonly property string temperatureUnit: "fahrenheit"
readonly property int weatherRefreshMinutes: 20
readonly property string temperatureUnit: DesktopPreferences.get("temperatureUnit")
readonly property int weatherRefreshMinutes: DesktopPreferences.get("weatherRefreshMinutes")
// ── Vitals ──────────────────────────────────────────────────────────────
// The GNOME Vitals extension showed processor usage, memory usage and GPU
// usage, in that order. Same here.
readonly property int vitalsIntervalMs: 2000
readonly property int vitalsIntervalMs: DesktopPreferences.get("vitalsIntervalMs")
readonly property bool showCpu: DesktopPreferences.get("showCpu")
readonly property bool showMemory: DesktopPreferences.get("showMemory")
readonly property bool showGpu: DesktopPreferences.get("showGpu")
@@ -51,10 +51,10 @@ Singleton {
readonly property bool nightLightEnabledByDefault: DesktopPreferences.get("nightLightEnabled")
// ── Notifications ───────────────────────────────────────────────────────
readonly property int notificationTimeoutMs: 5000
readonly property int notificationTimeoutCriticalMs: 0 // 0 = never auto-expire
readonly property int notificationHistoryLimit: 100
readonly property int maxVisibleToasts: 4
readonly property int notificationTimeoutMs: DesktopPreferences.get("notificationTimeoutMs")
readonly property int notificationTimeoutCriticalMs: DesktopPreferences.get("notificationTimeoutCriticalMs") // 0 = never auto-expire
readonly property int notificationHistoryLimit: DesktopPreferences.get("notificationHistoryLimit")
readonly property int maxVisibleToasts: DesktopPreferences.get("maxVisibleToasts")
// ── Focus ──────────────────────────────────────────────────────────────
// One deliberate default rather than a preset picker: quick settings and
@@ -79,9 +79,9 @@ Singleton {
readonly property int dockHideDelayMs: DesktopPreferences.get("dockHideDelayMs")
// ── Capture ─────────────────────────────────────────────────────────────
readonly property string screenshotDir: "Pictures/Screenshots"
readonly property string recordingDir: "Videos/Recordings"
readonly property string screenshotDir: DesktopPreferences.get("screenshotDir")
readonly property string recordingDir: DesktopPreferences.get("recordingDir")
// Passed to wf-recorder. Uses VAAPI on the AMD card so recording does not
// cost CPU while gaming.
readonly property string recorderArgs: "-c h264_vaapi -d /dev/dri/renderD128"
readonly property string recorderArgs: DesktopPreferences.get("recorderArgs")
}
@@ -3,39 +3,49 @@ import Quickshell
import qs.config
import qs.services
Item {
Flickable {
anchors.fill: parent
clip: true
contentWidth: width
contentHeight: content.implicitHeight + 64
boundsBehavior: Flickable.StopAtBounds
SettingsPage {
title: "About Panama"
lede: "A curated Hyprland desktop built around focus, speed, and good taste."
Column {
id: content
width: parent.width - 68
x: 34
y: 30
spacing: 16
SettingsCard {
title: "Panama Desktop"
subtitle: "Tokyo Night Moon · Prism glass · native tiling"
Text { text: "About Panama"; color: Theme.fg; font.family: Theme.fontFamily; font.pixelSize: 27; font.weight: Font.DemiBold }
Text { text: "A curated Hyprland desktop built around focus, speed, and good taste."; color: Theme.fgDim; font.family: Theme.fontFamily; font.pixelSize: Theme.fontSize; bottomPadding: 6 }
TextRow {
label: "Hyprland"
value: SystemSettings.hyprlandVersion || "Detecting…"
}
TextRow {
label: "Quickshell"
value: SystemSettings.quickshellVersion
}
TextRow {
label: "Display"
value: SystemSettings.monitorName || "Detecting…"
}
TextRow {
label: "Configuration"
detail: Quickshell.shellDir
value: "Local"
divider: false
}
}
SettingsCard {
title: "Panama Desktop"
subtitle: "Tokyo Night Moon · Prism glass · native tiling"
SettingRow { label: "Hyprland"; value: SystemSettings.hyprlandVersion || "Detecting…" }
SettingRow { label: "Quickshell"; value: SystemSettings.quickshellVersion }
SettingRow { label: "Display"; value: SystemSettings.monitorName || "Detecting…" }
SettingRow { label: "Configuration"; detail: Quickshell.shellDir; value: "Local"; divider: false }
}
SettingsCard {
title: "Design principles"
SettingsCard {
title: "Design principles"
SettingRow { label: "Curated by default"; detail: "Strong choices instead of an incoherent matrix of switches" }
SettingRow { label: "Quiet while idle"; detail: "No continuous decorative repaint loops" }
SettingRow { label: "Real system boundaries"; detail: "Every control either works or clearly hands off to its owner"; divider: false }
}
TextRow {
label: "Curated by default"
detail: "Strong choices instead of an incoherent matrix of switches"
}
TextRow {
label: "Quiet while idle"
detail: "No continuous decorative repaint loops"
}
TextRow {
label: "Real system boundaries"
detail: "Every control either works or clearly hands off to its owner"
divider: false
}
}
}
@@ -5,9 +5,12 @@ import qs.config
import qs.services
import qs.modules.quicksettings
Item {
SettingsPage {
id: root
title: "Network & Devices"
lede: "Connect graphically—no terminal workflow required."
readonly property var wifiDevice: {
for (const device of Networking.devices.values) {
if (device.type === DeviceType.Wifi)
@@ -17,72 +20,73 @@ Item {
}
readonly property var bluetoothAdapter: Bluetooth.defaultAdapter
Flickable {
anchors.fill: parent
clip: true
contentWidth: width
contentHeight: content.implicitHeight + 64
boundsBehavior: Flickable.StopAtBounds
SettingsCard {
title: "WiFi"
subtitle: Networking.wifiEnabled ? "Available networks" : "Wireless networking is off"
Column {
id: content
width: parent.width - 68
x: 34
y: 30
spacing: 16
SettingRow {
label: "WiFi"
detail: root.wifiDevice ? "Managed by NetworkManager" : "No wireless adapter found"
controlWidth: 48
Text { text: "Network & Devices"; color: Theme.fg; font.family: Theme.fontFamily; font.pixelSize: 27; font.weight: Font.DemiBold }
Text { text: "Connect graphically—no terminal workflow required."; color: Theme.fgDim; font.family: Theme.fontFamily; font.pixelSize: Theme.fontSize; bottomPadding: 6 }
SettingsCard {
title: "WiFi"
subtitle: Networking.wifiEnabled ? "Available networks" : "Wireless networking is off"
SettingRow {
label: "WiFi"
detail: root.wifiDevice ? "Managed by NetworkManager" : "No wireless adapter found"
controlWidth: 48
SettingsToggle {
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
checked: Networking.wifiEnabled
enabled: Networking.wifiHardwareEnabled
onToggled: value => Networking.wifiEnabled = value
}
}
WifiList { width: parent.width; device: root.wifiDevice; active: true; maxHeight: 240 }
SettingRow {
label: "Advanced network settings"
detail: "VPN, wired profiles, DNS, and connection details"
divider: false
controlWidth: 104
SettingsButton { anchors.right: parent.right; anchors.verticalCenter: parent.verticalCenter; text: "Open panel"; onClicked: SystemSettings.openGnomePanel("network") }
}
SettingsToggle {
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
checked: Networking.wifiEnabled
enabled: Networking.wifiHardwareEnabled
onToggled: value => Networking.wifiEnabled = value
}
}
SettingsCard {
title: "Bluetooth"
subtitle: root.bluetoothAdapter?.enabled ? "Nearby and paired devices" : "Bluetooth is off"
SettingRow {
label: "Bluetooth"
detail: root.bluetoothAdapter ? "Pair and reconnect without leaving Settings" : "No Bluetooth adapter found"
controlWidth: 48
SettingsToggle {
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
checked: root.bluetoothAdapter?.enabled ?? false
enabled: root.bluetoothAdapter !== null
onToggled: value => { if (root.bluetoothAdapter) root.bluetoothAdapter.enabled = value; }
}
}
BluetoothList { width: parent.width; active: true; maxHeight: 220 }
SettingRow {
label: "Advanced Bluetooth settings"
detail: "Device details and system-level options"
divider: false
controlWidth: 104
SettingsButton { anchors.right: parent.right; anchors.verticalCenter: parent.verticalCenter; text: "Open panel"; onClicked: SystemSettings.openGnomePanel("bluetooth") }
WifiList {
width: parent.width
device: root.wifiDevice
active: true
maxHeight: 240
}
ActionRow {
label: "Advanced network settings"
detail: "VPN, wired profiles, DNS, and connection details"
divider: false
action: "Open panel"
onTriggered: SystemSettings.openGnomePanel("network")
}
}
SettingsCard {
title: "Bluetooth"
subtitle: root.bluetoothAdapter?.enabled ? "Nearby and paired devices" : "Bluetooth is off"
SettingRow {
label: "Bluetooth"
detail: root.bluetoothAdapter ? "Pair and reconnect without leaving Settings" : "No Bluetooth adapter found"
controlWidth: 48
SettingsToggle {
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
checked: root.bluetoothAdapter?.enabled ?? false
enabled: root.bluetoothAdapter !== null
onToggled: value => {
if (root.bluetoothAdapter)
root.bluetoothAdapter.enabled = value;
}
}
}
BluetoothList {
width: parent.width
active: true
maxHeight: 220
}
ActionRow {
label: "Advanced Bluetooth settings"
detail: "Device details and system-level options"
divider: false
action: "Open panel"
onTriggered: SystemSettings.openGnomePanel("bluetooth")
}
}
}
@@ -3,64 +3,50 @@ import qs.config
import qs.services
import qs.widgets
Item {
Flickable {
anchors.fill: parent
clip: true
contentWidth: width
contentHeight: content.implicitHeight + 64
boundsBehavior: Flickable.StopAtBounds
SettingsPage {
title: "Displays"
lede: SystemSettings.monitorDescription || "Reading the active display…"
Column {
id: content
width: parent.width - 68
x: 34
y: 30
spacing: 16
SettingsCard {
title: SystemSettings.monitorName || "Active display"
subtitle: `${SystemSettings.monitorWidth} × ${SystemSettings.monitorHeight} at ${SystemSettings.monitorRefreshRate.toFixed(0)} Hz · ${SystemSettings.monitorScale.toFixed(1)}× scale · ${SystemSettings.monitorFormat}`
TextRow { label: "Color mode"; detail: "Wide-gamut SDR desktop at 10-bit"; value: SystemSettings.colorPreset || "wide" }
TextRow { label: "Variable refresh"; detail: SystemSettings.monitorVrrActive ? "Active for current fullscreen content" : "Ready when game or video content requests it"; value: SystemSettings.monitorVrrActive ? "Active" : "Standby"; divider: false }
}
Text { text: "Displays"; color: Theme.fg; font.family: Theme.fontFamily; font.pixelSize: 27; font.weight: Font.DemiBold }
Text { text: SystemSettings.monitorDescription || "Reading the active display…"; color: Theme.fgDim; font.family: Theme.fontFamily; font.pixelSize: Theme.fontSize; bottomPadding: 6 }
SettingsCard {
title: SystemSettings.monitorName || "Active display"
subtitle: `${SystemSettings.monitorWidth} × ${SystemSettings.monitorHeight} at ${SystemSettings.monitorRefreshRate.toFixed(0)} Hz · ${SystemSettings.monitorScale.toFixed(1)}× scale · ${SystemSettings.monitorFormat}`
SettingRow { label: "Color mode"; detail: "Wide-gamut SDR desktop at 10-bit"; value: SystemSettings.colorPreset || "wide" }
SettingRow { label: "Variable refresh"; detail: SystemSettings.monitorVrrActive ? "Active for current fullscreen content" : "Ready when game or video content requests it"; value: SystemSettings.monitorVrrActive ? "Active" : "Standby"; divider: false }
}
SettingsCard {
title: "Gaming display policy"
subtitle: "These values apply immediately and are restored when Panama starts."
SettingRow {
SettingsCard {
title: "Gaming display policy"
subtitle: "These values apply immediately and are restored when Panama starts."
SettingRow {
label: "Game-aware HDR"
detail: "Enter HDR only for fullscreen content that requests it"
controlWidth: 48
SettingsToggle { anchors.right: parent.right; anchors.verticalCenter: parent.verticalCenter; checked: SystemSettings.autoHdr; onToggled: value => SystemSettings.setAutoHdr(value) }
}
SettingRow {
}
SettingRow {
label: "Content-aware VRR"
detail: "Enable variable refresh only for game and video content"
controlWidth: 48
SettingsToggle { anchors.right: parent.right; anchors.verticalCenter: parent.verticalCenter; checked: SystemSettings.vrrPolicy === 3; onToggled: value => SystemSettings.setVrrPolicy(value ? 3 : 0) }
}
SettingRow {
}
SettingRow {
label: "Direct scanout for games"
detail: "Bypass compositing only for windows classified as games"
divider: false
controlWidth: 48
SettingsToggle { anchors.right: parent.right; anchors.verticalCenter: parent.verticalCenter; checked: SystemSettings.directScanoutPolicy === 2; onToggled: value => SystemSettings.setDirectScanoutPolicy(value ? 2 : 0) }
}
}
}
}
SettingsCard {
title: "Night Light"
SettingRow {
SettingsCard {
title: "Night Light"
SettingRow {
label: "Warm display colors"
detail: NightLight.automatic ? "Following the evening schedule" : "Manual control"
controlWidth: 48
SettingsToggle { anchors.right: parent.right; anchors.verticalCenter: parent.verticalCenter; checked: NightLight.active; onToggled: NightLight.toggle() }
}
SettingRow {
}
SettingRow {
label: "Color temperature"
detail: `${NightLight.temperature} K`
divider: false
@@ -71,27 +57,25 @@ Item {
icon: "weather-clear-night-symbolic"
onMoved: value => NightLight.temperature = Math.round((6500 - value * 4000) / 50) * 50
}
}
}
}
}
Rectangle {
width: parent.width
height: warningText.implicitHeight + 30
radius: Theme.cardRadius
color: Theme.alpha(Theme.warn, 0.085)
border.width: 1
border.color: Theme.alpha(Theme.warn, 0.22)
Text {
id: warningText
anchors.fill: parent
anchors.margins: 15
text: "Full-time desktop HDR stays unavailable here because the current compositor path can break screenshots, OBS, Sunshine, and lock-screen capture. Game-aware HDR keeps the desktop dependable without giving up HDR games."
color: Theme.mix(Theme.fg, Theme.warn, 0.25)
font.family: Theme.fontFamily
font.pixelSize: Theme.fontSizeSmall
wrapMode: Text.WordWrap
}
}
Rectangle {
width: parent.width
height: warningText.implicitHeight + 30
radius: Theme.cardRadius
color: Theme.alpha(Theme.warn, 0.085)
border.width: 1
border.color: Theme.alpha(Theme.warn, 0.22)
Text {
id: warningText
anchors.fill: parent
anchors.margins: 15
text: "Full-time desktop HDR stays unavailable here because the current compositor path can break screenshots, OBS, Sunshine, and lock-screen capture. Game-aware HDR keeps the desktop dependable without giving up HDR games."
color: Theme.mix(Theme.fg, Theme.warn, 0.25)
font.family: Theme.fontFamily
font.pixelSize: Theme.fontSizeSmall
wrapMode: Text.WordWrap
}
}
}
@@ -2,160 +2,159 @@ import QtQuick
import qs.config
import qs.services
Item {
SettingsPage {
id: root
readonly property int openedHour: new Date().getHours()
readonly property string greeting: openedHour < 12 ? "Good morning" : (openedHour < 18 ? "Good afternoon" : "Good evening")
Flickable {
anchors.fill: parent
clip: true
contentWidth: width
contentHeight: content.implicitHeight + 64
boundsBehavior: Flickable.StopAtBounds
title: `${root.greeting}, Gabriel`
lede: "Your Panama desktop is configured and ready."
Column {
id: content
width: parent.width - 68
x: 34
y: 30
spacing: 16
SettingsCard {
title: SystemSettings.monitorDescription || "Active display"
subtitle: SystemSettings.monitorName || "Detecting your display…"
Text {
text: `${root.greeting}, Gabriel`
color: Theme.fg
font.family: Theme.fontFamily
font.pixelSize: 27
font.weight: Font.DemiBold
}
Grid {
id: monitorLayout
Text {
text: "Your Panama desktop is configured and ready."
color: Theme.fgDim
font.family: Theme.fontFamily
font.pixelSize: Theme.fontSize
bottomPadding: 6
}
width: parent.width
columns: width >= 620 ? 2 : 1
columnSpacing: 28
rowSpacing: 16
SettingsCard {
title: SystemSettings.monitorDescription || "Active display"
subtitle: SystemSettings.monitorName || "Detecting your display…"
Item {
width: monitorLayout.columns === 2
? (monitorLayout.width - monitorLayout.columnSpacing) * 0.47
: monitorLayout.width
height: 164
Row {
width: parent.width
height: 164
spacing: 28
Rectangle {
width: Math.min(parent.width - 24, 260)
height: width * 0.64
anchors.horizontalCenter: parent.horizontalCenter
anchors.top: parent.top
anchors.topMargin: 5
radius: 12
color: Theme.alpha(Theme.bgDark, 0.94)
border.width: 1
border.color: Theme.alpha(Theme.accent, 0.34)
Item {
width: parent.width * 0.47
height: parent.height
Rectangle {
width: Math.min(parent.width - 24, 260)
height: width * 0.64
anchors.horizontalCenter: parent.horizontalCenter
anchors.top: parent.top
anchors.topMargin: 5
radius: 12
color: Theme.alpha(Theme.bgDark, 0.94)
border.width: 1
border.color: Theme.alpha(Theme.accent, 0.34)
Rectangle {
anchors.fill: parent
anchors.margins: 10
radius: 7
gradient: Gradient {
orientation: Gradient.Horizontal
GradientStop { position: 0; color: Theme.mix(Theme.bg, Theme.accent, 0.08) }
GradientStop { position: 1; color: Theme.mix(Theme.bg, Theme.accentSecondary, 0.08) }
}
Text {
anchors.centerIn: parent
text: SystemSettings.monitorName || "DISPLAY"
color: Theme.fgMuted
font.family: Theme.fontFamily
font.pixelSize: Theme.fontSizeSmall
font.letterSpacing: 1.5
}
}
Rectangle {
anchors.fill: parent
anchors.margins: 10
radius: 7
gradient: Gradient {
orientation: Gradient.Horizontal
GradientStop { position: 0; color: Theme.mix(Theme.bg, Theme.accent, 0.08) }
GradientStop { position: 1; color: Theme.mix(Theme.bg, Theme.accentSecondary, 0.08) }
}
}
Column {
width: parent.width * 0.47
anchors.verticalCenter: parent.verticalCenter
spacing: 13
Text {
text: `${SystemSettings.monitorWidth} × ${SystemSettings.monitorHeight}`
color: Theme.fg
anchors.centerIn: parent
text: SystemSettings.monitorName || "DISPLAY"
color: Theme.fgMuted
font.family: Theme.fontFamily
font.features: Theme.tabularFigures
font.pixelSize: Theme.fontSizeLarge
font.weight: Font.DemiBold
}
Text {
text: `${SystemSettings.monitorRefreshRate.toFixed(0)} Hz · ${SystemSettings.monitorScale.toFixed(1)}× scale`
color: Theme.fgDim
font.family: Theme.fontFamily
font.features: Theme.tabularFigures
font.pixelSize: Theme.fontSize
}
Text {
text: `${SystemSettings.monitorFormat || "Detecting format"} · ${SystemSettings.colorPreset || "standard color"}`
color: Theme.fgDim
font.family: Theme.fontFamily
font.pixelSize: Theme.fontSize
}
Text {
text: SystemSettings.autoHdr ? "Game-aware HDR is ready" : "Game-aware HDR is off"
color: SystemSettings.autoHdr ? Theme.ok : Theme.warn
font.family: Theme.fontFamily
font.pixelSize: Theme.fontSize
font.pixelSize: Theme.fontSizeSmall
font.letterSpacing: 1.5
}
}
}
}
Row {
width: parent.width
spacing: 16
Column {
width: monitorLayout.columns === 2
? (monitorLayout.width - monitorLayout.columnSpacing) * 0.47
: monitorLayout.width
height: monitorLayout.columns === 2 ? 164 : implicitHeight
spacing: 13
SettingsCard {
width: (parent.width - parent.spacing) / 2
title: "Quiet focus"
subtitle: "Notifications and focused work"
SettingRow {
label: "Do Not Disturb"
detail: Notifs.doNotDisturb ? "Banners are currently quiet" : "Notification banners are visible"
divider: false
controlWidth: 48
SettingsToggle {
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
checked: Notifs.doNotDisturb
onToggled: value => Notifs.doNotDisturb = value
}
}
Text {
text: `${SystemSettings.monitorWidth} × ${SystemSettings.monitorHeight}`
color: Theme.fg
font.family: Theme.fontFamily
font.features: Theme.tabularFigures
font.pixelSize: Theme.fontSizeLarge
font.weight: Font.DemiBold
}
SettingsCard {
width: (parent.width - parent.spacing) / 2
title: "Desktop services"
subtitle: "The essentials are running"
SettingRow {
label: "Sync & remote access"
detail: `${SystemSettings.nextcloudActive ? "Nextcloud ready" : "Nextcloud stopped"} · ${SystemSettings.rustdeskActive ? "RustDesk ready" : "RustDesk stopped"}`
divider: false
value: SystemSettings.nextcloudActive && SystemSettings.rustdeskActive ? "Healthy" : "Review"
}
Text {
text: `${SystemSettings.monitorRefreshRate.toFixed(0)} Hz · ${SystemSettings.monitorScale.toFixed(1)}× scale`
color: Theme.fgDim
font.family: Theme.fontFamily
font.features: Theme.tabularFigures
font.pixelSize: Theme.fontSize
}
Text {
text: `${SystemSettings.monitorFormat || "Detecting format"} · ${SystemSettings.colorPreset || "standard color"}`
color: Theme.fgDim
font.family: Theme.fontFamily
font.pixelSize: Theme.fontSize
}
Text {
text: SystemSettings.autoHdr ? "Game-aware HDR is ready" : "Game-aware HDR is off"
color: SystemSettings.autoHdr ? Theme.ok : Theme.warn
font.family: Theme.fontFamily
font.pixelSize: Theme.fontSize
}
}
}
}
SettingsCard {
title: "Weather"
subtitle: "Local conditions in the date menu"
ChoiceRow { setting: "temperatureUnit" }
SliderRow { setting: "weatherRefreshMinutes"; divider: false }
}
SettingsCard {
title: "System vitals"
subtitle: "Processor, memory, and graphics activity in the bar"
SliderRow { setting: "vitalsIntervalMs"; divider: false }
}
Grid {
id: summaryCards
width: parent.width
columns: width >= 720 ? 2 : 1
columnSpacing: 16
rowSpacing: 16
SettingsCard {
width: summaryCards.columns === 2
? (summaryCards.width - summaryCards.columnSpacing) / 2
: summaryCards.width
title: "Quiet focus"
subtitle: "Notifications and focused work"
SettingRow {
label: "Do Not Disturb"
detail: Notifs.doNotDisturb ? "Banners are currently quiet" : "Notification banners are visible"
divider: false
controlWidth: 48
SettingsToggle {
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
checked: Notifs.doNotDisturb
onToggled: value => Notifs.doNotDisturb = value
}
}
}
SettingsCard {
width: summaryCards.columns === 2
? (summaryCards.width - summaryCards.columnSpacing) / 2
: summaryCards.width
title: "Desktop services"
subtitle: "The essentials are running"
TextRow {
label: "Sync & remote access"
detail: `${SystemSettings.nextcloudActive ? "Nextcloud ready" : "Nextcloud stopped"} · ${SystemSettings.rustdeskActive ? "RustDesk ready" : "RustDesk stopped"}`
divider: false
value: SystemSettings.nextcloudActive && SystemSettings.rustdeskActive ? "Healthy" : "Review"
}
}
}
}
@@ -2,78 +2,97 @@ import QtQuick
import qs.config
import qs.services
Item {
Flickable {
anchors.fill: parent
clip: true
contentWidth: width
contentHeight: content.implicitHeight + 64
boundsBehavior: Flickable.StopAtBounds
SettingsPage {
title: "Notifications & Focus"
lede: "Control interruptions without losing useful history."
Column {
id: content
width: parent.width - 68
x: 34
y: 30
spacing: 16
SettingsCard {
title: "Notifications"
Text { text: "Notifications & Focus"; color: Theme.fg; font.family: Theme.fontFamily; font.pixelSize: 27; font.weight: Font.DemiBold }
Text { text: "Control interruptions without losing useful history."; color: Theme.fgDim; font.family: Theme.fontFamily; font.pixelSize: Theme.fontSize; bottomPadding: 6 }
SettingRow {
label: "Do Not Disturb"
detail: "Keep notifications in the center but suppress banners"
controlWidth: 48
SettingsCard {
title: "Notifications"
SettingRow {
label: "Do Not Disturb"
detail: "Keep notifications in the center but suppress banners"
controlWidth: 48
SettingsToggle { anchors.right: parent.right; anchors.verticalCenter: parent.verticalCenter; checked: Notifs.doNotDisturb; onToggled: value => Notifs.doNotDisturb = value }
}
SettingRow { label: "Notification history"; detail: "Live notifications retained by Panama"; value: `${Notifs.history.length} items` }
SettingRow {
label: "Clear notification history"
detail: "Dismiss every item currently in the notification center"
divider: false
controlWidth: 94
SettingsButton { anchors.right: parent.right; anchors.verticalCenter: parent.verticalCenter; text: "Clear all"; enabled: Notifs.history.length > 0; onClicked: Notifs.dismissAll() }
}
SettingsToggle {
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
checked: Notifs.doNotDisturb
onToggled: value => Notifs.doNotDisturb = value
}
}
TextRow {
label: "Notification history"
detail: "Live notifications retained by Panama"
value: `${Notifs.history.length} items`
}
ActionRow {
label: "Clear notification history"
detail: "Dismiss every item currently in the notification center"
divider: false
action: "Clear all"
enabled: Notifs.history.length > 0
onTriggered: Notifs.dismissAll()
}
}
SettingsCard {
title: "Banner behavior"
SliderRow { setting: "notificationTimeoutMs" }
SliderRow {
setting: "notificationTimeoutCriticalMs"
zeroLabel: "Never"
}
SliderRow { setting: "notificationHistoryLimit" }
SliderRow { setting: "maxVisibleToasts"; divider: false }
}
SettingsCard {
title: "Focus sessions"
subtitle: "A focus session binds quiet mode and Caffeine to the current workspace."
SettingRow {
id: durationRow
label: "Default duration"
detail: "Used by Super+Shift+F and Quick Settings"
controlWidth: 264
Row {
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
spacing: 6
Repeater {
model: [25, 45, 60, 90]
SettingsCard {
title: "Focus sessions"
subtitle: "A focus session binds quiet mode and Caffeine to the current workspace."
SettingRow {
label: "Default duration"
detail: "Used by Super+Shift+F and Quick Settings"
controlWidth: 264
Row {
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
spacing: 6
Repeater {
model: [25, 45, 60, 90]
SettingsButton {
required property int modelData
text: `${modelData}m`
tone: DesktopPreferences.get("focusDurationMinutes") === modelData ? "accent" : "normal"
onClicked: DesktopPreferences.set("focusDurationMinutes", modelData)
}
}
}
}
SettingRow {
label: FocusSession.active ? `Active on ${FocusSession.workspaceLabel}` : "No active focus session"
detail: FocusSession.active ? `${FocusSession.remainingText} remaining` : "Start one without leaving Settings"
divider: false
controlWidth: 120
SettingsButton {
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
text: FocusSession.active ? "Show controls" : "Start focus"
tone: FocusSession.active ? "normal" : "accent"
onClicked: FocusSession.reveal()
required property int modelData
text: `${modelData}m`
tone: DesktopPreferences.get("focusDurationMinutes") === modelData ? "accent" : "normal"
onClicked: SystemSettings.commitPreference("focusDurationMinutes", modelData)
}
}
}
}
SettingRow {
label: FocusSession.active ? `Active on ${FocusSession.workspaceLabel}` : "No active focus session"
detail: FocusSession.active ? `${FocusSession.remainingText} remaining` : "Start one without leaving Settings"
divider: false
controlWidth: 120
SettingsButton {
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
text: FocusSession.active ? "Show controls" : "Start focus"
tone: FocusSession.active ? "normal" : "accent"
onClicked: FocusSession.reveal()
}
}
}
}
@@ -2,9 +2,12 @@ import QtQuick
import qs.config
import qs.services
Item {
SettingsPage {
id: root
title: "Screen Intelligence"
lede: "Turn text and codes on screen into content you can use."
Component.onCompleted: ScreenIntelligence.refresh()
Timer {
@@ -13,98 +16,78 @@ Item {
onTriggered: Capture.openIntelligence()
}
Flickable {
anchors.fill: parent
clip: true
contentWidth: width
contentHeight: content.implicitHeight + 64
boundsBehavior: Flickable.StopAtBounds
SettingsCard {
title: "Read anything on screen"
subtitle: "Select a region, window, or display. Panama recognizes it locally and gives you clean follow-up actions."
Column {
id: content
width: parent.width - 68
x: 34
y: 30
spacing: 16
SettingRow {
icon: "󰗊"
label: "Read screen text"
detail: "Copy, search, translate, or open detected links"
controlWidth: 160
divider: false
Text {
text: "Screen Intelligence"
color: Theme.fg
font.family: Theme.fontFamily
font.pixelSize: 27
font.weight: Font.DemiBold
}
Text {
text: "Turn text and codes on screen into content you can use."
color: Theme.fgDim
font.family: Theme.fontFamily
font.pixelSize: Theme.fontSize
bottomPadding: 6
}
SettingsCard {
title: "Read anything on screen"
subtitle: "Select a region, window, or display. Panama recognizes it locally and gives you clean follow-up actions."
SettingRow {
icon: "󰗊"
label: "Read screen text"
detail: "Copy, search, translate, or open detected links"
controlWidth: 160
divider: false
SettingsButton {
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
text: "Start reading"
tone: "accent"
enabled: ScreenIntelligence.ocrReady && ScreenIntelligence.englishReady
onClicked: {
ShellState.closeSettings();
launchDelay.restart();
}
}
}
}
SettingsCard {
title: "Local recognition"
subtitle: "The screen image stays in Panama's cache and is deleted when you dismiss the result."
SettingRow {
label: "Text recognition"
detail: "Tesseract with the English language model"
value: ScreenIntelligence.ocrReady && ScreenIntelligence.englishReady ? "Ready" : "Needs install"
}
SettingRow {
label: "QR & barcodes"
detail: "ZBar recognizes codes alongside ordinary text"
value: ScreenIntelligence.codeReady ? "Ready" : "Needs install"
}
SettingRow {
label: "Privacy"
detail: "Only Search, Translate, and Open send the selected result to another application or service"
value: "Local first"
divider: false
}
}
SettingsCard {
title: "Shortcut"
SettingRow {
label: "Read a screen selection"
detail: "Also available as Read in the Print-screen picker"
value: "Super + Shift + S"
controlWidth: 190
divider: ScreenIntelligence.ocrReady && ScreenIntelligence.codeReady && ScreenIntelligence.englishReady
}
SettingRow {
visible: !ScreenIntelligence.ocrReady || !ScreenIntelligence.codeReady || !ScreenIntelligence.englishReady
label: "Install recognition engines"
detail: "sudo dnf install -y tesseract zbar"
value: "Required once"
divider: false
SettingsButton {
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
text: "Start reading"
tone: "accent"
enabled: ScreenIntelligence.ocrReady && ScreenIntelligence.englishReady
onClicked: {
ShellState.closeSettings();
launchDelay.restart();
}
}
}
}
SettingsCard {
title: "Capture preferences"
subtitle: "Choose where captures go and how recordings are encoded."
ChoiceRow { setting: "screenshotDir" }
ChoiceRow { setting: "recordingDir" }
ChoiceRow { setting: "recorderArgs"; divider: false }
}
SettingsCard {
title: "Local recognition"
subtitle: "The screen image stays in Panama's cache and is deleted when you dismiss the result."
TextRow {
label: "Text recognition"
detail: "Tesseract with the English language model"
value: ScreenIntelligence.ocrReady && ScreenIntelligence.englishReady ? "Ready" : "Needs install"
}
TextRow {
label: "QR & barcodes"
detail: "ZBar recognizes codes alongside ordinary text"
value: ScreenIntelligence.codeReady ? "Ready" : "Needs install"
}
TextRow {
label: "Privacy"
detail: "Only Search, Translate, and Open send the selected result to another application or service"
value: "Local first"
divider: false
}
}
SettingsCard {
title: "Shortcut"
TextRow {
label: "Read a screen selection"
detail: "Also available as Read in the Print-screen picker"
value: "Super + Shift + S"
controlWidth: 190
divider: ScreenIntelligence.ocrReady && ScreenIntelligence.codeReady && ScreenIntelligence.englishReady
}
TextRow {
visible: !ScreenIntelligence.ocrReady || !ScreenIntelligence.codeReady || !ScreenIntelligence.englishReady
label: "Install recognition engines"
detail: "sudo dnf install -y tesseract zbar"
value: "Required once"
divider: false
}
}
}
@@ -2,89 +2,132 @@ import QtQuick
import qs.config
import qs.services
Item {
function status(active: bool): string { return active ? "Running" : "Stopped"; }
SettingsPage {
title: "Startup & Services"
lede: "A clear view of the background tools that make the desktop feel complete."
Flickable {
anchors.fill: parent
clip: true
contentWidth: width
contentHeight: content.implicitHeight + 64
boundsBehavior: Flickable.StopAtBounds
function status(active: bool): string {
return active ? "Running" : "Stopped";
}
Column {
id: content
width: parent.width - 68
x: 34
y: 30
spacing: 16
Item {
width: parent.width
implicitHeight: refresh.implicitHeight
SettingsButton {
id: refresh
anchors.right: parent.right
text: SystemSettings.busy ? "Refreshing…" : "Refresh"
enabled: !SystemSettings.busy
onClicked: SystemSettings.refresh()
}
}
SettingsCard {
title: "Your services"
SettingRow {
label: "Nextcloud"
detail: "File synchronization and tray status"
controlWidth: 190
Row {
width: parent.width
Text { width: parent.width - refresh.width; text: "Startup & Services"; color: Theme.fg; font.family: Theme.fontFamily; font.pixelSize: 27; font.weight: Font.DemiBold }
SettingsButton { id: refresh; text: SystemSettings.busy ? "Refreshing…" : "Refresh"; enabled: !SystemSettings.busy; onClicked: SystemSettings.refresh() }
}
Text { text: "A clear view of the background tools that make the desktop feel complete."; color: Theme.fgDim; font.family: Theme.fontFamily; font.pixelSize: Theme.fontSize; bottomPadding: 6 }
SettingsCard {
title: "Your services"
SettingRow {
label: "Nextcloud"
detail: "File synchronization and tray status"
controlWidth: 190
Row {
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
spacing: 10
Text { anchors.verticalCenter: parent.verticalCenter; text: status(SystemSettings.nextcloudActive); color: Theme.fgDim; font.family: Theme.fontFamily; font.pixelSize: Theme.fontSize }
SettingsButton { text: "Open"; onClicked: SystemSettings.openApplication("nextcloud") }
}
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
spacing: 10
Text {
anchors.verticalCenter: parent.verticalCenter
text: status(SystemSettings.nextcloudActive)
color: Theme.fgDim
font.family: Theme.fontFamily
font.pixelSize: Theme.fontSize
}
SettingRow {
label: "RustDesk"
detail: "Remote access through the enabled system service"
controlWidth: 190
Row {
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
spacing: 10
Text { anchors.verticalCenter: parent.verticalCenter; text: status(SystemSettings.rustdeskActive); color: Theme.fgDim; font.family: Theme.fontFamily; font.pixelSize: Theme.fontSize }
SettingsButton { text: "Open"; onClicked: SystemSettings.openApplication("rustdesk") }
}
}
SettingRow {
label: "KDE Connect"
detail: "Phone pairing, clipboard, files, and remote controls"
divider: false
controlWidth: 190
Row {
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
spacing: 10
Text { anchors.verticalCenter: parent.verticalCenter; text: status(SystemSettings.kdeconnectActive); color: Theme.fgDim; font.family: Theme.fontFamily; font.pixelSize: Theme.fontSize }
SettingsButton { text: "Open"; onClicked: SystemSettings.openApplication("kdeconnect") }
}
SettingsButton {
text: "Open"
onClicked: SystemSettings.openApplication("nextcloud")
}
}
}
SettingsCard {
title: "Desktop foundation"
SettingRow { label: "Hyprpaper"; detail: "Wallpaper service"; value: status(SystemSettings.hyprpaperActive) }
SettingRow { label: "Hypridle"; detail: "Idle and lock policy"; value: status(SystemSettings.hypridleActive) }
SettingRow { label: "Vicinae"; detail: "Spotlight-style launcher daemon"; value: status(SystemSettings.vicinaeActive); divider: false }
SettingRow {
label: "RustDesk"
detail: "Remote access through the enabled system service"
controlWidth: 190
Row {
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
spacing: 10
Text {
anchors.verticalCenter: parent.verticalCenter
text: status(SystemSettings.rustdeskActive)
color: Theme.fgDim
font.family: Theme.fontFamily
font.pixelSize: Theme.fontSize
}
SettingsButton {
text: "Open"
onClicked: SystemSettings.openApplication("rustdesk")
}
}
}
SettingsCard {
title: "Fedora system settings"
subtitle: "These remain owned by trusted system services and GNOME's mature panels."
SettingRow {
label: "Network, Bluetooth, printers, users, and accounts"
detail: "GNOME Settings remains searchable from the launcher too"
divider: false
controlWidth: 122
SettingsButton { anchors.right: parent.right; anchors.verticalCenter: parent.verticalCenter; text: "Open network"; onClicked: SystemSettings.openGnomePanel("network") }
SettingRow {
label: "KDE Connect"
detail: "Phone pairing, clipboard, files, and remote controls"
divider: false
controlWidth: 190
Row {
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
spacing: 10
Text {
anchors.verticalCenter: parent.verticalCenter
text: status(SystemSettings.kdeconnectActive)
color: Theme.fgDim
font.family: Theme.fontFamily
font.pixelSize: Theme.fontSize
}
SettingsButton {
text: "Open"
onClicked: SystemSettings.openApplication("kdeconnect")
}
}
}
}
SettingsCard {
title: "Desktop foundation"
TextRow {
label: "Hyprpaper"
detail: "Wallpaper service"
value: status(SystemSettings.hyprpaperActive)
}
TextRow {
label: "Hypridle"
detail: "Idle and lock policy"
value: status(SystemSettings.hypridleActive)
}
TextRow {
label: "Vicinae"
detail: "Spotlight-style launcher daemon"
value: status(SystemSettings.vicinaeActive)
divider: false
}
}
SettingsCard {
title: "Fedora system settings"
subtitle: "These remain owned by trusted system services and GNOME's mature panels."
ActionRow {
label: "Network, Bluetooth, printers, users, and accounts"
detail: "GNOME Settings remains searchable from the launcher too"
divider: false
action: "Open network"
onTriggered: SystemSettings.openGnomePanel("network")
}
}
}
@@ -4,50 +4,61 @@ import qs.config
import qs.services
import qs.modules.quicksettings
Item {
Flickable {
anchors.fill: parent
clip: true
contentWidth: width
contentHeight: content.implicitHeight + 64
boundsBehavior: Flickable.StopAtBounds
SettingsPage {
title: "Sound"
lede: "Live PipeWire output, input, and device selection."
Column {
id: content
width: parent.width - 68
x: 34
y: 30
spacing: 16
SettingsCard {
title: "Output"
subtitle: Pipewire.defaultAudioSink?.description ?? "No output device"
Text { text: "Sound"; color: Theme.fg; font.family: Theme.fontFamily; font.pixelSize: 27; font.weight: Font.DemiBold }
Text { text: "Live PipeWire output, input, and device selection."; color: Theme.fgDim; font.family: Theme.fontFamily; font.pixelSize: Theme.fontSize; bottomPadding: 6 }
AudioSlider {
width: parent.width
node: Pipewire.defaultAudioSink
output: true
}
Rectangle {
width: parent.width
height: 1
color: Theme.alpha(Theme.fg, 0.06)
}
AudioDeviceList {
width: parent.width
output: true
maxHeight: 190
}
}
SettingsCard {
title: "Output"
subtitle: Pipewire.defaultAudioSink?.description ?? "No output device"
AudioSlider { width: parent.width; node: Pipewire.defaultAudioSink; output: true }
Rectangle { width: parent.width; height: 1; color: Theme.alpha(Theme.fg, 0.06) }
AudioDeviceList { width: parent.width; output: true; maxHeight: 190 }
}
SettingsCard {
title: "Input"
subtitle: Pipewire.defaultAudioSource?.description ?? "No input device"
SettingsCard {
title: "Input"
subtitle: Pipewire.defaultAudioSource?.description ?? "No input device"
AudioSlider { width: parent.width; node: Pipewire.defaultAudioSource; output: false }
Rectangle { width: parent.width; height: 1; color: Theme.alpha(Theme.fg, 0.06) }
AudioDeviceList { width: parent.width; output: false; maxHeight: 160 }
}
AudioSlider {
width: parent.width
node: Pipewire.defaultAudioSource
output: false
}
Rectangle {
width: parent.width
height: 1
color: Theme.alpha(Theme.fg, 0.06)
}
AudioDeviceList {
width: parent.width
output: false
maxHeight: 160
}
}
SettingsCard {
title: "Advanced sound"
SettingRow {
label: "Application volumes and profiles"
detail: "Open Fedora's complete sound panel"
divider: false
controlWidth: 104
SettingsButton { anchors.right: parent.right; anchors.verticalCenter: parent.verticalCenter; text: "Open panel"; onClicked: SystemSettings.openGnomePanel("sound") }
}
}
SettingsCard {
title: "Advanced sound"
ActionRow {
label: "Application volumes and profiles"
detail: "Open Fedora's complete sound panel"
divider: false
action: "Open panel"
onTriggered: SystemSettings.openGnomePanel("sound")
}
}
}