import QtQuick import QtQuick.Layouts import Quickshell import Caelestia.Config import Caelestia.Services import qs.components import qs.components.controls import qs.services import qs.modules.nexus.common PageBase { id: root // Lyrics backends, ordered to match LyricsBackend::Backend (Auto, Local, LRCLIB, NetEase) readonly property list lyricsItems: [ MenuItem { text: qsTr("Auto") }, MenuItem { text: "Local" }, MenuItem { text: "LRCLIB" }, MenuItem { text: "NetEase" } ] // GPU options + the config string each maps to (see Gpu::parseType) readonly property list gpuItems: [ MenuItem { text: qsTr("Auto") }, MenuItem { text: "NVIDIA" }, MenuItem { text: qsTr("Generic") }, MenuItem { text: qsTr("None") } ] readonly property list gpuValues: ["", "NVIDIA", "GENERIC", "None"] function gpuKeyToIndex(key: string): int { const u = (key ?? "").trim().toUpperCase(); if (u === "") return 0; // Auto if (u === "NVIDIA") return 1; if (u === "GENERIC") return 2; return 3; // None } title: qsTr("Services") ColumnLayout { anchors.horizontalCenter: parent.horizontalCenter anchors.top: parent.top width: root.cappedWidth spacing: Tokens.spacing.extraSmall / 2 // Detected running players, used as default-player options Variants { id: playerVariants model: [...new Set(Players.list.map(p => Players.getIdentity(p)).filter(id => id))] MenuItem { required property string modelData text: modelData icon: modelData === GlobalConfig.services.defaultPlayer ? "check" : "" activeIcon: "music_note" } } // Polling SectionHeader { first: true text: qsTr("Polling") } StepperRow { Layout.fillWidth: true first: true label: qsTr("Media refresh") subtext: qsTr("How often the media position updates (ms)") value: GlobalConfig.dashboard.mediaUpdateInterval from: 100 to: 2000 stepSize: 50 onMoved: v => GlobalConfig.dashboard.mediaUpdateInterval = v } StepperRow { Layout.fillWidth: true label: qsTr("System stats refresh") subtext: qsTr("CPU, memory and GPU update interval (seconds)") value: GlobalConfig.dashboard.resourceUpdateInterval / 1000 from: 0.5 to: 10 stepSize: 0.5 onMoved: v => GlobalConfig.dashboard.resourceUpdateInterval = Math.round(v * 1000) } StepperRow { Layout.fillWidth: true last: true label: qsTr("Wi-Fi rescan") subtext: qsTr("How often available networks are rescanned (seconds)") value: GlobalConfig.nexus.networkRescanInterval / 1000 from: 5 to: 120 stepSize: 5 onMoved: v => GlobalConfig.nexus.networkRescanInterval = Math.round(v * 1000) } // Media & lyrics SectionHeader { text: qsTr("Media & lyrics") } SelectRow { Layout.fillWidth: true first: true label: qsTr("Lyrics backend") subtext: qsTr("Source used to fetch synced lyrics") menuItems: root.lyricsItems active: root.lyricsItems[Lyrics.preferredBackend] ?? root.lyricsItems[0] onSelected: item => Lyrics.preferredBackend = root.lyricsItems.indexOf(item) } SelectRow { Layout.fillWidth: true last: true label: qsTr("Default player") subtext: qsTr("Preferred media player when several are open") menuItems: playerVariants.instances active: menuItems.find(i => i.text === GlobalConfig.services.defaultPlayer) ?? null fallbackIcon: "music_note" fallbackText: GlobalConfig.services.defaultPlayer || qsTr("Auto") onSelected: item => GlobalConfig.services.defaultPlayer = item.text } // Input increments SectionHeader { text: qsTr("Input increments") } StepperRow { Layout.fillWidth: true first: true label: qsTr("Volume step") subtext: qsTr("Amount the volume changes per scroll (%)") value: Math.round(GlobalConfig.services.audioIncrement * 100) from: 1 to: 50 stepSize: 1 onMoved: v => GlobalConfig.services.audioIncrement = v / 100 } StepperRow { Layout.fillWidth: true label: qsTr("Brightness step") subtext: qsTr("Amount the brightness changes per scroll (%)") value: Math.round(GlobalConfig.services.brightnessIncrement * 100) from: 1 to: 50 stepSize: 1 onMoved: v => GlobalConfig.services.brightnessIncrement = v / 100 } StepperRow { Layout.fillWidth: true last: true label: qsTr("Max volume") subtext: qsTr("Upper limit for output volume (%)") value: Math.round(GlobalConfig.services.maxVolume * 100) from: 50 to: 200 stepSize: 5 onMoved: v => GlobalConfig.services.maxVolume = v / 100 } // Service tuning SectionHeader { text: qsTr("Service tuning") } StepperRow { Layout.fillWidth: true first: true label: qsTr("Visualiser bars") subtext: qsTr("Number of bars in the audio visualisers") value: GlobalConfig.services.visualiserBars from: 10 to: 120 stepSize: 2 onMoved: v => GlobalConfig.services.visualiserBars = v } ToggleRow { Layout.fillWidth: true text: qsTr("Smart colour scheme") subtext: qsTr("Derive theme mode and variant from the wallpaper") checked: GlobalConfig.services.smartScheme onToggled: GlobalConfig.services.smartScheme = checked } // NoSignal: time-boxed passwordless sudo (15 min) toggle SudoToggleRow { Layout.fillWidth: true } SelectRow { Layout.fillWidth: true last: true label: qsTr("GPU") subtext: Gpu.name ? qsTr("Monitoring: %1").arg(Gpu.name) : qsTr("Override for GPU type") menuOnTop: true menuItems: root.gpuItems active: root.gpuItems[root.gpuKeyToIndex(GlobalConfig.services.gpuType)] onSelected: item => GlobalConfig.services.gpuType = root.gpuValues[root.gpuItems.indexOf(item)] } } }