From 44b257ce8554ec55485b389cb5b0b7a482931ffb Mon Sep 17 00:00:00 2001 From: 28allday Date: Thu, 18 Jun 2026 21:06:20 +0100 Subject: [PATCH] feat(nosignal): CachyOS repo toggle + wire Quick Settings placebos - CachyRepoToggleRow.qml (Settings -> Services): sudoless CachyOS optimized repos + linux-cachyos kernel switch, auto-tier (v4>v3); polls nosignal-cachy-repo status, runs enable/disable in a floating kitty. Inserted after SudoToggleRow in ServicesPage.qml. - NsQuickSettings.qml: Night Light now drives hyprsunset (tile bound to the real pgrep'd process, not a flag); Airplane drives nmcli + bluetoothctl and binds to real radio state; VPN placebo tile removed (no backend). Both built + live-tested on the reference box (06-17 PM). Helper script, sudoers and hyprsunset dep ship in the nosignal builder. Co-Authored-By: Claude Opus 4.8 (1M context) --- modules/nexus/common/CachyRepoToggleRow.qml | 73 +++++++++++++++++++++ modules/nexus/pages/ServicesPage.qml | 5 ++ modules/nsbar/panels/NsQuickSettings.qml | 64 ++++++++++++++---- 3 files changed, 131 insertions(+), 11 deletions(-) create mode 100644 modules/nexus/common/CachyRepoToggleRow.qml diff --git a/modules/nexus/common/CachyRepoToggleRow.qml b/modules/nexus/common/CachyRepoToggleRow.qml new file mode 100644 index 00000000..51375de8 --- /dev/null +++ b/modules/nexus/common/CachyRepoToggleRow.qml @@ -0,0 +1,73 @@ +// CachyRepoToggleRow.qml (NoSignal) — Settings -> Services toggle to enable / +// disable the CachyOS pacman repositories. Auto-detects the best x86-64 microarch +// tier the CPU supports (v4 > v3). Live state polls `nosignal-cachy-repo status` +// (no root). enable/disable run in a floating terminal so the pacman/download +// output stays visible; the grant is passwordless via sudo -n +// (/etc/sudoers.d/02-nosignal-cachy). enable adds repos, converts userspace to +// the optimized builds (pacman -Suu, pacman pinned stock), and installs the +// linux-cachyos kernel; disable reverts to stock and removes the cachy kernel +// (keeping stock `linux` bootable). A reboot switches the running kernel. +// +// Untracked file under modules/nexus/common — auto-discovered as the type +// `CachyRepoToggleRow` via `import qs.modules.nexus.common`. Survives caelestia +// upgrades; pairs with the one-line insert in ServicesPage.qml. +import QtQuick +import Quickshell.Io +import qs.modules.nexus.common + +ToggleRow { + id: root + + property bool repoOn: false // NB: not `enabled` (reserved Item property) + + text: qsTr("CachyOS repositories") + subtext: repoOn + ? qsTr("On — CachyOS optimized builds + linux-cachyos kernel (reboot to run it)") + : qsTr("Switch to CachyOS optimized builds + linux-cachyos kernel (reboot after)") + + onToggled: { + if (checked) + enableProc.running = true; + else + disableProc.running = true; + reconcile.restart(); + } + + // --- live state ----------------------------------------------------------- + Process { + id: statusProc + command: ["/usr/local/bin/nosignal-cachy-repo", "status"] + stdout: StdioCollector { + onStreamFinished: { + root.repoOn = text.trim() === "enabled"; + root.checked = root.repoOn; // drive switch from real state + } + } + } + + // --- actions (passwordless; shown in a terminal for pacman output) --------- + Process { + id: enableProc + command: ["kitty", "--class", "TUI.float", "-e", "sh", "-c", "sudo -n /usr/local/bin/nosignal-cachy-repo enable; printf '\\nPress Enter to close...'; read _"] + onExited: reconcile.restart() + } + Process { + id: disableProc + command: ["kitty", "--class", "TUI.float", "-e", "sh", "-c", "sudo -n /usr/local/bin/nosignal-cachy-repo disable; printf '\\nPress Enter to close...'; read _"] + onExited: reconcile.restart() + } + + // --- polling -------------------------------------------------------------- + Timer { + interval: 5000 + repeat: true + running: true + triggeredOnStart: true + onTriggered: statusProc.running = true + } + Timer { + id: reconcile + interval: 2000 + onTriggered: statusProc.running = true + } +} diff --git a/modules/nexus/pages/ServicesPage.qml b/modules/nexus/pages/ServicesPage.qml index 15cb9529..aa635b21 100644 --- a/modules/nexus/pages/ServicesPage.qml +++ b/modules/nexus/pages/ServicesPage.qml @@ -216,6 +216,11 @@ PageBase { Layout.fillWidth: true } + // NoSignal: CachyOS repos enable/disable (sudoless, auto-tier) + CachyRepoToggleRow { + Layout.fillWidth: true + } + SelectRow { Layout.fillWidth: true last: true diff --git a/modules/nsbar/panels/NsQuickSettings.qml b/modules/nsbar/panels/NsQuickSettings.qml index a06add81..9577cd3e 100644 --- a/modules/nsbar/panels/NsQuickSettings.qml +++ b/modules/nsbar/panels/NsQuickSettings.qml @@ -3,10 +3,13 @@ pragma ComponentBehavior: Bound // NoSignal Quick Settings popout (off the media / battery pill). Avatar + user + // uptime + power button; 2-col toggle grid; brightness + volume sliders; media // card. Wired to the real services (Network, Bluetooth, Notifs, Audio, -// Brightness, Players); Night Light / Airplane / VPN are stateful toggles for now. +// Brightness, Players). Night Light drives hyprsunset; Airplane drives the +// NetworkManager + Bluetooth radios. (VPN tile removed pending a real backend.) import QtQuick import QtQuick.Layouts +import Quickshell +import Quickshell.Io import Quickshell.Bluetooth import qs.services import qs.utils @@ -20,6 +23,35 @@ NsPanel { readonly property var brightMon: Brightness.monitors[0] ?? null + // Night Light state tracks the real hyprsunset process rather than a local + // flag, so the tile stays correct across shell reloads or changes made + // elsewhere. Seeded on load (probe runs immediately), reconciled on a timer, + // and re-checked right after a toggle. + property bool nightLightOn: false + + Process { + id: nightProbe + command: ["pgrep", "-x", "hyprsunset"] + running: true + stdout: StdioCollector { + onStreamFinished: root.nightLightOn = text.trim().length > 0 + } + } + + Timer { + interval: 3000 + running: true + repeat: true + onTriggered: nightProbe.running = true + } + + Timer { + id: nightRefresh + interval: 500 + repeat: false + onTriggered: nightProbe.running = true + } + // ── Header ─────────────────────────────────────────────────────────── RowLayout { Layout.fillWidth: true @@ -128,21 +160,31 @@ NsPanel { icon: "nightlight" label: "Night Light" sub: on ? "Warm 3500K" : "Off" - onToggled: on = !on + on: root.nightLightOn + onToggled: { + if (root.nightLightOn) + Quickshell.execDetached(["pkill", "-x", "hyprsunset"]); + else + Quickshell.execDetached(["sh", "-c", "pkill -x hyprsunset; hyprsunset -t 3500"]); + nightRefresh.restart(); + } } QSToggle { id: airTog icon: "airplanemode_active" label: "Airplane" - sub: on ? "On" : "Off" - onToggled: on = !on - } - QSToggle { - id: vpnTog - icon: "vpn_key" - label: "VPN" - sub: on ? "On" : "Off" - onToggled: on = !on + // Reflects the real radio state: airplane is "On" when both Wi-Fi and + // Bluetooth are down. Network.wifiEnabled tracks nmcli; the adapter + // tracks BlueZ, so the tile self-corrects if radios change elsewhere. + readonly property bool radiosOff: !Network.wifiEnabled && !(Bluetooth.defaultAdapter?.enabled ?? false) + sub: radiosOff ? "On" : "Off" + on: radiosOff + onToggled: { + if (radiosOff) + Quickshell.execDetached(["sh", "-c", "nmcli radio all on; bluetoothctl power on"]); + else + Quickshell.execDetached(["sh", "-c", "nmcli radio all off; bluetoothctl power off"]); + } } }