From ea485ac930643abca53c7929638ff9ee7c724a0d Mon Sep 17 00:00:00 2001 From: "info@data-pro.nu" Date: Fri, 1 May 2026 11:54:51 +0200 Subject: [PATCH] Regenerated --- Droidnix/README.html | 1442 ++++++++++++----- Droidnix/README.org | 635 ++++++++ Droidnix/generated/.config/hypr/bindings.conf | 1 + .../.config/quickshell/powermgmt/shell.qml | 630 +++++++ 4 files changed, 2308 insertions(+), 400 deletions(-) create mode 100644 Droidnix/generated/.config/quickshell/powermgmt/shell.qml diff --git a/Droidnix/README.html b/Droidnix/README.html index db39ce67a..3e53cb26c 100644 --- a/Droidnix/README.html +++ b/Droidnix/README.html @@ -3,7 +3,7 @@ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> - + Droidnix: A Dendritic NixOS + Home Manager Configuration NixOS Configuration Structure @@ -204,145 +204,146 @@

Table of Contents

-
-

Shortcuts

-
+
+

Shortcuts

+

Introduction The Assets Folder @@ -358,25 +359,25 @@

Introduction   intro

-
-

What is Droidnix

-
+
+

What is Droidnix

+

Droidnix is a modular, declarative NixOS + Home Manager configuration system. with configurations managed via Emacs Org and Nix Flakes. The project is designed for reproducibility, maintainability, and cross-machine compatibility.

-
-

Installed components:

-
+
+

Installed components:

+
    -
  • Core
  • -
  • Hyprland
  • +
  • Core
  • +
  • Hyprland
-
-

Goals, project Structure, import hierarchy

-
+
+

Goals, project Structure, import hierarchy

+

This project uses a modular NixOS configuration with Hyprland support, designed for literate programming and cross-device reusability. The Droidnix repository is organized into two main parts: @@ -389,9 +390,9 @@ The Droidnix repository is organized into two main parts:

-
-

Root Level

-
+
+

Root Level

+
  • = is the entry point and imports:
      @@ -402,16 +403,16 @@ The Droidnix repository is organized into two main parts:
-
-

Generated Structure

-
+
+

Generated Structure

+

The generated/ directory contains all generated configurations, divided into three main groups: system, hyprland, and mangowc.

-
-

First Setup

-
+
+

First Setup

+
  1. Clone this repository.
  2. Run the setup script: ./setup_droid.
  3. @@ -432,9 +433,9 @@ The generated/ directory contains all generated configurations, div The .assets/ folder contains all static files, such as configs, scripts, and themes. These files are not generated and can be edited directly.

-
-

generated/assets/2_b_installed.conf

-
+
+

generated/assets/2_b_installed.conf

+

This is a list of additional apps to install

@@ -513,9 +514,9 @@ com.logseq.Logseq
-
-

generated/assets/aliases.conf

-
+
+

generated/assets/aliases.conf

+

This is a list of additional apps to install

@@ -557,9 +558,9 @@ keys=hyprctl binds This section contains the Org blocks for tangling Nix code into the generated folders.

-
-

generated/flake.nix

-
+
+

generated/flake.nix

+

The Nix flake definition for Droidnix.

@@ -650,9 +651,9 @@ The Nix flake definition for Droidnix.
-
-

generated/modules/traveldroid/system/colors.nix

-
+
+

generated/modules/traveldroid/system/colors.nix

+

Setting the colors for Droidnix.

@@ -729,9 +730,9 @@ in
-
-

generated/hosts/traveldroid/boot.nix

-
+
+

generated/hosts/traveldroid/boot.nix

+
{ config, pkgs, lib, flakeRoot, ... }:
 {
@@ -802,9 +803,9 @@ in
 
-
-

generated/hosts/traveldroid/hardware-configuration.nix

-
+
+

generated/hosts/traveldroid/hardware-configuration.nix

+
  1. Boot into NixOS Live ISO or your installed system.
  2. Open a terminal.
  3. @@ -862,9 +863,9 @@ in
-
-

generated/hosts/traveldroid/host.nix

-
+
+

generated/hosts/traveldroid/host.nix

+
{ lib, config, pkgs, flakeRoot, import-tree, home-manager, ... }:
 
@@ -924,13 +925,13 @@ in
 
-
-

generated/traveldroid/modules/apps

-
+
+

generated/traveldroid/modules/apps

+
-
-

generated/modules/traveldroid/apps/2_b_installed.nix

-
+
+

generated/modules/traveldroid/apps/2_b_installed.nix

+

This installs a list of apps

@@ -1038,9 +1039,9 @@ in {
-
-

generated/modules/traveldroid/apps/emacs/emacs.nix

-
+
+

generated/modules/traveldroid/apps/emacs/emacs.nix

+

This installs emacs

@@ -1135,9 +1136,9 @@ in
-
-

generated/modules/traveldroid/apps/kdeconnect.nix

-
+
+

generated/modules/traveldroid/apps/kdeconnect.nix

+

This is top file of this level which contains just an import statement for all relevant files and/or the subfolder in this folder

@@ -1165,9 +1166,9 @@ in
-
-

generated/modules/traveldroid/apps/kitty.nix

-
+
+

generated/modules/traveldroid/apps/kitty.nix

+

This file sets up Kitty terminal

@@ -1219,9 +1220,9 @@ in
-
-

generated/modules/traveldroid/apps/stackstorage.nix

-
+
+

generated/modules/traveldroid/apps/stackstorage.nix

+

Installs the TransIP Stackstorage Appimage

@@ -1245,9 +1246,9 @@ in {
-
-

generated/modules/traveldroid/apps/starship.nix

-
+
+

generated/modules/traveldroid/apps/starship.nix

+

This file sets up starship prompt

@@ -1286,9 +1287,9 @@ in
-
-

generated/modules/traveldroid/apps/thunar.nix

-
+
+

generated/modules/traveldroid/apps/thunar.nix

+

This is top file of this level which contains just an import statement for all relevant files and/or the subfolder in this folder

@@ -1334,9 +1335,9 @@ in
-
-

generated/modules/traveldroid/apps/wofi.nix

-
+
+

generated/modules/traveldroid/apps/wofi.nix

+

This is the install for Wofi, the launcher

@@ -1371,9 +1372,9 @@ in
-
-

generated/modules/traveldroid/apps/zenbrowser.nix

-
+
+

generated/modules/traveldroid/apps/zenbrowser.nix

+

This installs zen browser

@@ -1393,9 +1394,9 @@ in
-
-

generated/modules/traveldroid/apps/zeditor.nix

-
+
+

generated/modules/traveldroid/apps/zeditor.nix

+

This sets Zed Editor

@@ -1450,9 +1451,9 @@ in
-
-

generated/modules/traveldroid/apps/zsh.nix

-
+
+

generated/modules/traveldroid/apps/zsh.nix

+

This sets up the zsh in the terminal

@@ -1566,13 +1567,13 @@ in
-
-

generated/modules/traveldroid/desktop

-
+
+

generated/modules/traveldroid/desktop

+
-
-

generated/modules/traveldroid/desktop/fonts.nix

-
+
+

generated/modules/traveldroid/desktop/fonts.nix

+

This file installs and configures fonts

@@ -1590,9 +1591,9 @@ This file installs and configures fonts
-
-

generated/modules/traveldroid/desktop/gtk.nix

-
+
+

generated/modules/traveldroid/desktop/gtk.nix

+

Setting up GTK

@@ -1618,9 +1619,9 @@ in
-
-

generated/modules/traveldroid/desktop/hyprland.nix

-
+
+

generated/modules/traveldroid/desktop/hyprland.nix

+

Setting up Hyprland

@@ -1665,9 +1666,9 @@ in
-
-

generated/modules/traveldroid/desktop/stylix.nix

-
+
+

generated/modules/traveldroid/desktop/stylix.nix

+
{ lib, config, pkgs, flakeRoot, stylix, ... }:
 
@@ -1754,9 +1755,9 @@ in
 
-
-

generated/modules/traveldroid/desktop/wallpaper.nix

-
+
+

generated/modules/traveldroid/desktop/wallpaper.nix

+

Setting up wallpaper engine + wallpaper gui

@@ -1848,9 +1849,9 @@ in
-
-

generated/.config/awww/scripts/randomizeWallpapers.sh

-
+
+

generated/.config/awww/scripts/randomizeWallpapers.sh

+

Numbers all pictures in ~/Wallpapers/pictures in random order

@@ -1912,9 +1913,9 @@ randomize_and_rename_wallpapers
-
-

generated/.config/awww/scripts/ws-daemon.sh

-
+
+

generated/.config/awww/scripts/ws-daemon.sh

+

Little daemon that sets a workspace when user switches to a different workspace

@@ -1935,9 +1936,9 @@ done
-
-

generated/.config/awww/scripts/ws-wallpaper.sh

-
+
+

generated/.config/awww/scripts/ws-wallpaper.sh

+

The script that actually sets wallpaper per workspace

@@ -1980,9 +1981,9 @@ esac
-
-

generated/modules/traveldroid/desktop/waybar.nix

-
+
+

generated/modules/traveldroid/desktop/waybar.nix

+

This file installs and configures waybar

@@ -2028,9 +2029,9 @@ in
-
-

generated/modules/traveldroid/desktop/wayland.nix

-
+
+

generated/modules/traveldroid/desktop/wayland.nix

+
{ lib, config, pkgs, ... }:
 
@@ -2060,9 +2061,9 @@ in
 
-
-

generated/modules/traveldroid/desktop/xdg.nix

-
+
+

generated/modules/traveldroid/desktop/xdg.nix

+

This sets the XDG implementation

@@ -2133,13 +2134,13 @@ in
-
-

generated/modules/traveldroid/system

-
+
+

generated/modules/traveldroid/system

+
-
-

generated/modules/traveldroid/system/audio.nix

-
+
+

generated/modules/traveldroid/system/audio.nix

+
{ lib, config, pkgs, ... }:
 
@@ -2160,9 +2161,9 @@ in
 
-
-

generated/modules/traveldroid/system/avahi.nix

-
+
+

generated/modules/traveldroid/system/avahi.nix

+

Avahi helps discovering services

@@ -2182,9 +2183,9 @@ Avahi helps discovering services
-
-

generated/modules/traveldroid/system/bluetooth.nix

-
+
+

generated/modules/traveldroid/system/bluetooth.nix

+

Installing Bluetooth services and supporting aps

@@ -2211,9 +2212,9 @@ in
-
-

generated/modules/traveldroid/system/copy_scripts.nix

-
+
+

generated/modules/traveldroid/system/copy_scripts.nix

+

This copies any scripts from generated.config/shared//scripts to ~/.config/shared/scripts and makes any .sh files executable.

@@ -2254,9 +2255,9 @@ in
-
-

generated/modules/traveldroid/system/dbus.nix

-
+
+

generated/modules/traveldroid/system/dbus.nix

+

This sets the dbus implementation

@@ -2283,9 +2284,9 @@ This sets the dbus implementation
-
-

generated/modules/traveldroid/system/firewall.nix

-
+
+

generated/modules/traveldroid/system/firewall.nix

+

This sets the firewall.

@@ -2366,9 +2367,9 @@ This sets the firewall.
-
-

generated/modules/traveldroid/system/hypridle.nix

-
+
+

generated/modules/traveldroid/system/hypridle.nix

+

This installs hypridle

@@ -2424,9 +2425,9 @@ in
-
-

generated/modules/traveldroid/system/hyprlock.nix

-
+
+

generated/modules/traveldroid/system/hyprlock.nix

+

This installs hyprlock

@@ -2483,9 +2484,9 @@ in
-
-

generated/modules/traveldroid/system/gnome-keyring.nix

-
+
+

generated/modules/traveldroid/system/gnome-keyring.nix

+

This sets the dbus implementation

@@ -2533,9 +2534,9 @@ This sets the dbus implementation
-
-

generated/modules/traveldroid/system/login-tuigreet.nix

-
+
+

generated/modules/traveldroid/system/login-tuigreet.nix

+

This sets up tuigreeter which is not fancy but imho fits the aesthetic I am aiming for

@@ -2583,9 +2584,9 @@ in
-
-

generated/modules/traveldroid/system/networking.nix

-
+
+

generated/modules/traveldroid/system/networking.nix

+

This sets the networking.

@@ -2634,9 +2635,9 @@ This sets the networking.
-
-

generated/modules/traveldroid/system/numlock-check.nix

-
+
+

generated/modules/traveldroid/system/numlock-check.nix

+
{ lib, config, pkgs, flakeRoot, ... }:
 let
@@ -2679,9 +2680,9 @@ in
 
-
-

generated/modules/traveldroid/system/nix.nix

-
+
+

generated/modules/traveldroid/system/nix.nix

+
{ lib, config, ... }:
 {
@@ -2704,9 +2705,9 @@ in
 
-
-

generated/modules/traveldroid/system/printing.nix

-
+
+

generated/modules/traveldroid/system/printing.nix

+

This sets the dbus implementation

@@ -2730,9 +2731,9 @@ This sets the dbus implementation
-
-

generated/modules/traveldroid/system/quickshell.nix

-
+
+

generated/modules/traveldroid/system/quickshell.nix

+

This sets the quickshell implementation

@@ -2778,9 +2779,9 @@ in
-
-

generated/modules/traveldroid/system/swaync.nix

-
+
+

generated/modules/traveldroid/system/swaync.nix

+

This sets the dbus implementation

@@ -2829,13 +2830,13 @@ in
-
-

generated/users

-
+
+

generated/users

+
-
-

generated/users/

-
+
+

generated/users/

+

This is the default user, just search and replace henrov another name if you want to change

@@ -2897,9 +2898,9 @@ in

These are all the prepared config files

-
-

generated/.config/emacs/early-init.el

-
+
+

generated/.config/emacs/early-init.el

+

This contaions emacs

@@ -3000,9 +3001,9 @@ package-archive-priorities '(("gnu" . 99)
-
-

generated/.config/emacs/init.el

-
+
+

generated/.config/emacs/init.el

+

This contaions emacs

@@ -3412,9 +3413,9 @@ the top of the file."
-
-

generated/.config/hypr/animations.conf

-
+
+

generated/.config/hypr/animations.conf

+

These settings decide animations

@@ -3457,9 +3458,9 @@ These settings decide animations
-
-

generated/.config/hypr/behaviour.conf

-
+
+

generated/.config/hypr/behaviour.conf

+

These are config files for .config/hypr

@@ -3480,9 +3481,9 @@ input {
-
-

generated/.config/hypr/bindings.conf

-
+
+

generated/.config/hypr/bindings.conf

+

These are config files for .config/hypr

@@ -3626,6 +3627,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 @@ -3648,9 +3650,9 @@ bind = $mainMod, U, exec, qs -c updater
-
-

generated/.config/hypr/exec-once.conf

-
+
+

generated/.config/hypr/exec-once.conf

+

What is executed after startup, when hyprland is running?

@@ -3672,9 +3674,9 @@ exec-once = waybar
-
-

generated/.config/hypr/hypridle.conf

-
+
+

generated/.config/hypr/hypridle.conf

+

These are config files for hypridle

@@ -3705,9 +3707,9 @@ listener {
-
-

generated/.config/hypr/hyprlock.conf

-
+
+

generated/.config/hypr/hyprlock.conf

+

These are config files for .config/hypr

@@ -3737,9 +3739,9 @@ input-field {
-
-

generated/.config/hypr/hyprland.conf

-
+
+

generated/.config/hypr/hyprland.conf

+

These are config files for .config/hypr

@@ -3758,9 +3760,9 @@ source = ./workspace-rules.conf
-
-

generated/.config/hypr/layer-rules.conf

-
+
+

generated/.config/hypr/layer-rules.conf

+

These are config files for .config/hypr

@@ -3773,9 +3775,9 @@ layerrule = xray 1, match:namespace swaync-control-center
-
-

generated/.config/hypr/layout.conf

-
+
+

generated/.config/hypr/layout.conf

+

These settings control Hyprland config

@@ -3790,9 +3792,9 @@ scrolling {
-
-

generated/.config/hypr/monitor-rules.conf

-
+
+

generated/.config/hypr/monitor-rules.conf

+

These are config files for .config/hypr

@@ -3803,9 +3805,9 @@ monitor = eDP-1, preferred, auto-left, 1.5
-
-

generated/.config/hypr/theming.conf

-
+
+

generated/.config/hypr/theming.conf

+

These are config files for .config/hypr

@@ -3871,9 +3873,9 @@ misc {
-
-

generated/.config/hypr/window-rules.conf

-
+
+

generated/.config/hypr/window-rules.conf

+

These are config files for .config/hypr

@@ -3974,9 +3976,9 @@ windowrule {
-
-

generated/.config/hypr/workspace-rules.conf

-
+
+

generated/.config/hypr/workspace-rules.conf

+

These setttings configure rules for workspaces

@@ -3991,9 +3993,9 @@ workspace = 6, monitor:DP-1 #, layout:dwindle
-
-

generated/.config/quickshell/media/shell.qml

-
+
+

generated/.config/quickshell/media/shell.qml

+

offers a audio widget

@@ -4387,9 +4389,649 @@ ShellRoot {
-
-

generated/.config/quickshell/powermenu/shell.qml

-
+
+

generated/.config/quickshell/powermgmt/shell.qml

+
+

+Provides powermanagement settings +

+
+
// --- 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()
+        }
+    }
+}
+
+
+
+
+
+

generated/.config/quickshell/powermenu/shell.qml

+

Provides a powermenu

@@ -4537,9 +5179,9 @@ ShellRoot {
-
-

generated/.config/quickshell/updater/shell.qml

-
+
+

generated/.config/quickshell/updater/shell.qml

+

Updates the system

@@ -4846,9 +5488,9 @@ ShellRoot {
-
-

generated/.config/quickshell/layoutswitcher/shell.qml

-
+
+

generated/.config/quickshell/layoutswitcher/shell.qml

+

Provides a layout menu

@@ -4995,9 +5637,9 @@ ShellRoot {
-
-

generated/.config/waybar/scripts/batterywarn.sh

-
+
+

generated/.config/waybar/scripts/batterywarn.sh

+

Providing an media

@@ -5014,9 +5656,9 @@ fi
-
-

generated/.config/waybar/scripts/media.sh

-
+
+

generated/.config/waybar/scripts/media.sh

+

Providing an media

@@ -5115,9 +5757,9 @@ jq -c -n \
-
-

generated/.config/shared/scripts/numlock-check.sh

-
+
+

generated/.config/shared/scripts/numlock-check.sh

+

Count keyboards and enable numlock if more then 1, else disable

@@ -5138,9 +5780,9 @@ fi
-
-

generated/.config/wofi/scripts/wofi-launcher.sh

-
+
+

generated/.config/wofi/scripts/wofi-launcher.sh

+

Providing an media

@@ -5155,9 +5797,9 @@ Providing an media
-
-

generated/.config/shared/css/colors.css

-
+
+

generated/.config/shared/css/colors.css

+

A file containing color variables

@@ -5198,9 +5840,9 @@ A file containing color variables
-
-

generated/.config/starship.toml

-
+
+

generated/.config/starship.toml

+

These are config files for Starship

@@ -5488,9 +6130,9 @@ crust = "#181926"
-
-

generated/.config/stylix/stylix.conf

-
+
+

generated/.config/stylix/stylix.conf

+

These are config files for .config/stylix

@@ -5526,9 +6168,9 @@ icons = {
-
-

generated/.config/stylix/palette.json

-
+
+

generated/.config/stylix/palette.json

+

These are config files for .config/stylix

@@ -5558,9 +6200,9 @@ These are config files for .config/stylix
-
-

generated/.config/stylix/palette.html

-
+
+

generated/.config/stylix/palette.html

+

These are config files for .config/stylix

@@ -5637,9 +6279,9 @@ These are config files for .config/stylix
-
-

generated/.config/swaync/config.json

-
+
+

generated/.config/swaync/config.json

+

These are config files for waybar

@@ -5700,9 +6342,9 @@ These are config files for waybar
-
-

generated/.config/swaync/style.css

-
+
+

generated/.config/swaync/style.css

+

These are config files for waybar

@@ -5895,9 +6537,9 @@ These are config files for waybar
-
-

generated/.config/waybar/config.jsonc

-
+
+

generated/.config/waybar/config.jsonc

+

These are config files for waybar

@@ -6245,9 +6887,9 @@ These are config files for waybar
-
-

generated/.config/waybar/style-dark.css

-
+
+

generated/.config/waybar/style-dark.css

+

This file contains all css for waybar

@@ -6507,9 +7149,9 @@ label#custom-windows:not(.active) {
-
-

generated/.config/waybar/scripts/bluetooth-status.sh

-
+
+

generated/.config/waybar/scripts/bluetooth-status.sh

+

These are config files for waybar

@@ -6534,9 +7176,9 @@ printf '{"text": "%s", "tooltip": "%s"}\n' "$icon" "$tooltip"
-
-

generated/.config/waybar/scripts/hypr-workspaces.sh

-
+
+

generated/.config/waybar/scripts/hypr-workspaces.sh

+

These are config files for waybar

@@ -6578,9 +7220,9 @@ jq -c -n \
-
-

generated/.config/waybar/scripts/hypr-workspacesmenu.sh

-
+
+

generated/.config/waybar/scripts/hypr-workspacesmenu.sh

+

These are config files for waybar

@@ -6602,9 +7244,9 @@ hyprctl dispatch focuswindow address:"$addr"
-
-

generated/.config/waybar/scripts/kdeconnect-status.sh

-
+
+

generated/.config/waybar/scripts/kdeconnect-status.sh

+

These are config files for waybar

@@ -6628,9 +7270,9 @@ fi
-
-

generated/.config/waypaper/config.ini

-
+
+

generated/.config/waypaper/config.ini

+

These are config files for waypaper

@@ -6667,9 +7309,9 @@ keybindings = ~/.config/waypaper/keybindings.ini
-
-

generated/.config/waypaper/config.ini

-
+
+

generated/.config/waypaper/config.ini

+

These are config files for .config/waypaper

@@ -6705,9 +7347,9 @@ keybindings = ~/.config/waypaper/keybindings.ini
-
-

generated/.config/wofi/config

-
+
+

generated/.config/wofi/config

+

These are config files for .config/wofi

@@ -6734,9 +7376,9 @@ prompt = > ...
-
-

generated/.config/wofi/style.css

-
+
+

generated/.config/wofi/style.css

+

This is the default layout for wofi

@@ -6832,9 +7474,9 @@ This is the default layout for wofi
-
-

generated/.config/zed/settings.json

-
+
+

generated/.config/zed/settings.json

+

These are config files for Zed editor

@@ -6872,9 +7514,9 @@ These are config files for Zed editor
-
-

generated/.config/zsh/.zshrc

-
+
+

generated/.config/zsh/.zshrc

+

This sets up the zsh in the terminal

@@ -6911,7 +7553,7 @@ cd() {

Author: Henro Veijer

-

Created: 2026-05-01 vr 10:36

+

Created: 2026-05-01 vr 11:54

Validate

diff --git a/Droidnix/README.org b/Droidnix/README.org index eb38bbeb9..fc2f25802 100644 --- a/Droidnix/README.org +++ b/Droidnix/README.org @@ -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 diff --git a/Droidnix/generated/.config/hypr/bindings.conf b/Droidnix/generated/.config/hypr/bindings.conf index 539a8094f..3823250ac 100644 --- a/Droidnix/generated/.config/hypr/bindings.conf +++ b/Droidnix/generated/.config/hypr/bindings.conf @@ -138,6 +138,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 diff --git a/Droidnix/generated/.config/quickshell/powermgmt/shell.qml b/Droidnix/generated/.config/quickshell/powermgmt/shell.qml new file mode 100644 index 000000000..2155d344a --- /dev/null +++ b/Droidnix/generated/.config/quickshell/powermgmt/shell.qml @@ -0,0 +1,630 @@ +// --- This file has been auto-generated. For permanent changes alter the appropriate block in the README.org. --- +// --- 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() + } + } +}