Regenerated

This commit is contained in:
2026-05-01 11:54:51 +02:00
parent 0d616c5068
commit ea485ac930
4 changed files with 2308 additions and 400 deletions
+635
View File
@@ -2964,6 +2964,7 @@ bind = , XF86Sleep, exec, systemctl suspend
bind = , XF86PowerOff, exec, systemctl poweroff
bind = , XF86WakeUp, exec, systemctl suspend
bind = , XF86ScreenSaver, exec, loginctl lock-session
bind = $mainMod, P, exec, qs -c powermgmt
#########################
# Start apps
@@ -3657,6 +3658,640 @@ ShellRoot {
}
#+END_SRC
** =generated/.config/quickshell/powermgmt/shell.qml=
Provides powermanagement settings
#+BEGIN_SRC qml :tangle generated/.config/quickshell/powermgmt/shell.qml :noweb yes :mkdirp yes :eval never
// --- Power Management Panel for NixOS + Hyprland ---
// Launch with: bind=$MAINMOD,p,exec,qs powermgmt
import Quickshell
import Quickshell.Io
import QtQuick
import QtQuick.Layouts
ShellRoot {
// ── Catppuccin Mocha — full palette from colors.css ───────────────────
QtObject {
id: colors
// Base layers
readonly property color crust: "#11111b"
readonly property color mantle: "#181825"
readonly property color base: "#1e1e2e"
// Surfaces
readonly property color surface0: "#313244"
readonly property color surface1: "#45475a"
readonly property color surface2: "#585b70"
// Overlays
readonly property color overlay0: "#6c7086"
readonly property color overlay1: "#7f849c"
readonly property color overlay2: "#9399b2"
// Text
readonly property color subtext0: "#a6adc8"
readonly property color subtext1: "#bac2de"
readonly property color text: "#cdd6f4"
// Accent colours
readonly property color border: "#96cdd2"
readonly property color rosewater: "#f5e0dc"
readonly property color flamingo: "#f2cdcd"
readonly property color pink: "#f5c2e7"
readonly property color mauve: "#cba6f7"
readonly property color red: "#f38ba8"
readonly property color maroon: "#eba0ac"
readonly property color peach: "#fab387"
readonly property color yellow: "#f9e2af"
readonly property color green: "#a6e3a1"
readonly property color teal: "#94e2d5"
readonly property color sapphire: "#74c7ec"
readonly property color blue: "#89b4fa"
readonly property color lavender: "#b4befe"
// Convenience: semi-transparent base
readonly property color baseAlpha: Qt.rgba(0.118, 0.118, 0.180, 0.8)
}
// ── State ──────────────────────────────────────────────────────────────
property string activeProfile: "balanced" // performance | balanced | powersave
property bool lidClose: true
property bool wifiPower: true
property bool bluetoothOff: false
property string statusMsg: ""
property bool statusOk: true
// Hypridle timeouts — initialised from current hypridle.conf values:
// lock @ 600s = 10 min
// dpms @ 900s = 15 min
// suspend @ 1200s = 20 min
property int lockTimeout: 10 // minutes (0 = disabled)
property int dpmsTimeout: 15 // minutes (0 = disabled)
property int suspendTimeout: 20 // minutes (0 = disabled)
property int brightnessLevel: 80 // percent
// ── hypridle config writer ─────────────────────────────────────────────
// Mirrors the structure of the original hypridle.conf exactly,
// preserving: after_sleep_cmd, ignore_dbus_inhibit, on-resume for dpms.
function buildHypridleConf() {
var s =
"# --- managed by quickshell powermgmt panel ---\n" +
"general {\n" +
" lock_cmd = hyprlock\n" +
" before_sleep_cmd = hyprlock\n" +
" after_sleep_cmd = hyprctl dispatch dpms on\n" +
" ignore_dbus_inhibit = false\n" +
"}\n"
if (lockTimeout > 0) {
s +=
"\nlistener {\n" +
" timeout = " + (lockTimeout * 60) + "\n" +
" on-timeout = hyprlock\n" +
"}\n"
}
if (dpmsTimeout > 0) {
s +=
"\nlistener {\n" +
" timeout = " + (dpmsTimeout * 60) + "\n" +
" on-timeout = hyprctl dispatch dpms off\n" +
" on-resume = hyprctl dispatch dpms on\n" +
"}\n"
}
if (suspendTimeout > 0) {
s +=
"\nlistener {\n" +
" timeout = " + (suspendTimeout * 60) + "\n" +
" on-timeout = systemctl suspend\n" +
"}\n"
}
return s
}
function applyHypridle() {
var conf = buildHypridleConf()
// Use printf to avoid shell interpretation of special chars in the conf
writeProc.command = ["bash", "-c",
"printf '%s' " + JSON.stringify(conf) +
" > ~/.config/hypr/hypridle.conf && pkill hypridle; hypridle &"]
writeProc.running = true
}
// ── Helpers ────────────────────────────────────────────────────────────
function run(cmd) {
runnerProc.command = cmd
runnerProc.running = true
}
function showStatus(msg, ok) {
statusMsg = msg
statusOk = ok
statusTimer.restart()
}
function applyProfile(p) {
activeProfile = p
if (p === "performance") run(["powerprofilesctl", "set", "performance"])
else if (p === "balanced") run(["powerprofilesctl", "set", "balanced"])
else run(["powerprofilesctl", "set", "power-saver"])
showStatus("Profile → " + p, true)
}
function applyBrightness(v) {
brightnessLevel = v
run(["brightnessctl", "set", v + "%"])
}
function setLockTimeout(mins) {
lockTimeout = mins
applyHypridle()
showStatus(mins === 0 ? "Auto-lock disabled" : "Lock after " + mins + " min", true)
}
function setDpmsTimeout(mins) {
dpmsTimeout = mins
applyHypridle()
showStatus(mins === 0 ? "Screen timeout disabled" : "Screen off after " + mins + " min", true)
}
function setSuspendTimeout(mins) {
suspendTimeout = mins
applyHypridle()
showStatus(mins === 0 ? "Auto-suspend disabled" : "Suspend after " + mins + " min", true)
}
function toggleWifiPower(v) {
wifiPower = v
// Replace wlan0 with your actual interface if different (check: ip link)
run(v ? ["iw", "dev", "wlan0", "set", "power_save", "on"]
: ["iw", "dev", "wlan0", "set", "power_save", "off"])
showStatus("WiFi power save " + (v ? "on" : "off"), true)
}
function toggleBluetooth(v) {
bluetoothOff = v
run(v ? ["rfkill", "block", "bluetooth"]
: ["rfkill", "unblock", "bluetooth"])
showStatus("Bluetooth " + (v ? "disabled" : "enabled"), true)
}
function toggleLid(v) {
lidClose = v
run(["bash", "-c",
"loginctl set-handle-lid-switch " + (v ? "suspend" : "ignore")])
showStatus("Lid close → " + (v ? "suspend" : "ignore"), true)
}
// ── Processes ──────────────────────────────────────────────────────────
Process {
id: runnerProc
onExited: (code) => {
if (code !== 0)
showStatus("Command failed (exit " + code + ")", false)
}
}
Process {
id: writeProc
onExited: (code) => {
if (code !== 0)
showStatus("hypridle reload failed (exit " + code + ")", false)
}
}
Timer {
id: statusTimer
interval: 3000
onTriggered: statusMsg = ""
}
// ── Window ─────────────────────────────────────────────────────────────
FloatingWindow {
title: "quickshell-powermgmt"
visible: true
width: 540
height: mainCol.implicitHeight + 40
color: "transparent"
Shortcut { sequence: "Escape"; onActivated: Qt.quit() }
// Gradient border — uses border colour from colors.css as midpoint
Rectangle {
anchors.fill: parent
anchors.margins: -2
radius: 20
z: -1
gradient: Gradient {
orientation: Gradient.Horizontal
GradientStop { position: 0.0; color: colors.mauve }
GradientStop { position: 0.5; color: colors.border }
GradientStop { position: 1.0; color: colors.blue }
}
}
Rectangle {
anchors.fill: parent
radius: 18
color: colors.base
ColumnLayout {
id: mainCol
anchors {
top: parent.top
left: parent.left
right: parent.right
margins: 20
}
spacing: 14
// ── Header ─────────────────────────────────────────────
RowLayout {
Layout.topMargin: 4
spacing: 10
Text { text: "⚡"; font.pixelSize: 18 }
Text {
text: "Power Management"
color: colors.text
font.pixelSize: 16
font.bold: true
}
Item { Layout.fillWidth: true }
Text {
text: "✕"
color: colors.surface2
font.pixelSize: 14
MouseArea {
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
onClicked: Qt.quit()
}
}
}
// ── Power Profile ───────────────────────────────────────
SectionLabel { label: "Power Profile" }
RowLayout {
Layout.fillWidth: true
spacing: 8
ProfileButton {
label: "Performance"
icon: "🚀"
accent: colors.peach
active: activeProfile === "performance"
onClicked: applyProfile("performance")
}
ProfileButton {
label: "Balanced"
icon: "⚖"
accent: colors.blue
active: activeProfile === "balanced"
onClicked: applyProfile("balanced")
}
ProfileButton {
label: "Power Save"
icon: "🌿"
accent: colors.green
active: activeProfile === "powersave"
onClicked: applyProfile("powersave")
}
}
// ── Brightness ──────────────────────────────────────────
SectionLabel { label: "Screen Brightness" }
RowLayout {
Layout.fillWidth: true
spacing: 10
Text { text: "🔅"; font.pixelSize: 13 }
Rectangle {
Layout.fillWidth: true
height: 6
radius: 3
color: colors.surface0
Rectangle {
width: parent.width * (brightnessLevel / 100)
height: parent.height
radius: parent.radius
gradient: Gradient {
orientation: Gradient.Horizontal
GradientStop { position: 0.0; color: colors.yellow }
GradientStop { position: 1.0; color: colors.peach }
}
}
MouseArea {
anchors.fill: parent
onClicked: (mouse) => {
var v = Math.round((mouse.x / width) * 100)
applyBrightness(Math.max(5, Math.min(100, v)))
}
onPositionChanged: (mouse) => {
if (pressed) {
var v = Math.round((mouse.x / width) * 100)
applyBrightness(Math.max(5, Math.min(100, v)))
}
}
}
}
Text { text: "🔆"; font.pixelSize: 13 }
Text {
text: brightnessLevel + "%"
color: colors.subtext0
font.pixelSize: 12
width: 32
}
}
// ── Hypridle Timeouts ───────────────────────────────────
// These three rows map 1-to-1 to the three listeners in
// hypridle.conf and rewrite + reload the daemon on change.
SectionLabel { label: "Idle Timeouts (hypridle)" }
TimeoutRow {
label: "Auto-lock"
icon: "🔒"
accent: colors.lavender
value: lockTimeout
options: [5, 10, 15, 30, 0]
onPicked: (v) => setLockTimeout(v)
}
TimeoutRow {
label: "Screen off"
icon: "🖥"
accent: colors.sapphire
value: dpmsTimeout
options: [5, 10, 15, 30, 0]
onPicked: (v) => setDpmsTimeout(v)
}
TimeoutRow {
label: "Suspend"
icon: "💤"
accent: colors.mauve
value: suspendTimeout
options: [15, 20, 30, 60, 0]
onPicked: (v) => setSuspendTimeout(v)
}
// ── Toggles ─────────────────────────────────────────────
SectionLabel { label: "Settings" }
ToggleRow {
label: "Suspend on lid close"
icon: "💻"
checked: lidClose
onToggled: (v) => toggleLid(v)
}
ToggleRow {
label: "WiFi power saving"
icon: "📶"
checked: wifiPower
onToggled: (v) => toggleWifiPower(v)
}
ToggleRow {
label: "Bluetooth off"
icon: "🔵"
checked: bluetoothOff
onToggled: (v) => toggleBluetooth(v)
}
// ── Status bar ──────────────────────────────────────────
Rectangle {
Layout.fillWidth: true
height: visible ? 32 : 0
visible: statusMsg !== ""
radius: 8
color: statusOk
? Qt.rgba(colors.green.r, colors.green.g, colors.green.b, 0.12)
: Qt.rgba(colors.red.r, colors.red.g, colors.red.b, 0.12)
Text {
anchors.centerIn: parent
text: statusMsg
color: statusOk ? colors.green : colors.red
font.pixelSize: 12
}
}
// ── Actions ─────────────────────────────────────────────
SectionLabel { label: "Actions" }
RowLayout {
Layout.fillWidth: true
Layout.bottomMargin: 8
spacing: 8
ActionButton { label: "Lock"; icon: "🔒"; accent: colors.blue; onClicked: run(["hyprlock"]) }
ActionButton { label: "Suspend"; icon: "💤"; accent: colors.mauve; onClicked: run(["systemctl", "suspend"]) }
ActionButton { label: "Hibernate"; icon: "🌙"; accent: colors.sapphire; onClicked: run(["systemctl", "hibernate"]) }
ActionButton { label: "Reboot"; icon: "🔄"; accent: colors.peach; onClicked: run(["systemctl", "reboot"]) }
ActionButton { label: "Shutdown"; icon: "⏻"; accent: colors.red; onClicked: run(["systemctl", "poweroff"]) }
}
}
}
}
// ── Inline components ──────────────────────────────────────────────────
component SectionLabel: Text {
required property string label
text: label.toUpperCase()
color: colors.overlay0
font.pixelSize: 10
font.bold: true
letterSpacing: 1.2
}
component ProfileButton: Rectangle {
required property string label
required property string icon
required property color accent
required property bool active
signal clicked
Layout.fillWidth: true
height: 56
radius: 10
color: active ? Qt.rgba(accent.r, accent.g, accent.b, 0.15) : colors.surface0
border.color: active ? accent : "transparent"
border.width: 1
Behavior on color { ColorAnimation { duration: 120 } }
ColumnLayout {
anchors.centerIn: parent
spacing: 2
Text {
Layout.alignment: Qt.AlignHCenter
text: icon
font.pixelSize: 18
}
Text {
Layout.alignment: Qt.AlignHCenter
text: label
color: active ? accent : colors.subtext0
font.pixelSize: 11
font.bold: active
}
}
MouseArea {
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
onClicked: parent.clicked()
}
}
component TimeoutRow: RowLayout {
required property string label
required property string icon
required property color accent
required property int value
required property var options
signal picked(int v)
Layout.fillWidth: true
spacing: 8
Text { text: icon; font.pixelSize: 14 }
Text {
text: label
color: colors.subtext1
font.pixelSize: 12
Layout.preferredWidth: 100
}
Item { Layout.fillWidth: true }
Repeater {
model: options
delegate: Rectangle {
readonly property int mins: modelData
readonly property bool sel: value === mins
width: mins === 0 ? 40 : 36
height: 24
radius: 6
color: sel ? Qt.rgba(accent.r, accent.g, accent.b, 0.2) : colors.surface0
border.color: sel ? accent : "transparent"
border.width: 1
Text {
anchors.centerIn: parent
text: mins === 0 ? "off" : mins + "m"
color: sel ? accent : colors.subtext0
font.pixelSize: 10
font.bold: sel
}
MouseArea {
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
onClicked: picked(mins)
}
}
}
}
component ToggleRow: RowLayout {
required property string label
required property string icon
required property bool checked
signal toggled(bool v)
Layout.fillWidth: true
spacing: 8
Text { text: icon; font.pixelSize: 14 }
Text {
text: label
color: colors.subtext1
font.pixelSize: 12
}
Item { Layout.fillWidth: true }
Rectangle {
width: 42
height: 22
radius: 11
color: checked ? Qt.rgba(colors.green.r, colors.green.g, colors.green.b, 0.3) : colors.surface1
border.color: checked ? colors.green : colors.surface2
border.width: 1
Behavior on color { ColorAnimation { duration: 150 } }
Rectangle {
x: checked ? parent.width - width - 3 : 3
y: 3
width: 16; height: 16; radius: 8
color: checked ? colors.green : colors.surface2
Behavior on x { NumberAnimation { duration: 150; easing.type: Easing.OutCubic } }
Behavior on color { ColorAnimation { duration: 150 } }
}
MouseArea {
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
onClicked: toggled(!checked)
}
}
}
component ActionButton: Rectangle {
required property string label
required property string icon
required property color accent
signal clicked
Layout.fillWidth: true
height: 52
radius: 10
color: colors.surface0
ColumnLayout {
anchors.centerIn: parent
spacing: 2
Text {
Layout.alignment: Qt.AlignHCenter
text: icon
font.pixelSize: 16
}
Text {
Layout.alignment: Qt.AlignHCenter
text: label
color: colors.subtext0
font.pixelSize: 10
}
}
Rectangle {
id: hoverBorder
anchors.fill: parent
radius: parent.radius
color: "transparent"
border.color: accent
border.width: 0
}
MouseArea {
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
hoverEnabled: true
onEntered: hoverBorder.border.width = 1
onExited: hoverBorder.border.width = 0
onClicked: parent.clicked()
}
}
}
#+END_SRC
** =generated/.config/quickshell/powermenu/shell.qml=
Provides a powermenu
#+BEGIN_SRC qml :tangle generated/.config/quickshell/powermenu/shell.qml :noweb yes :mkdirp yes :eval never