diff --git a/Droidnix/README.html b/Droidnix/README.html index 850f9ca2b..4f79c63c0 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,128 +204,128 @@

Table of Contents

-
-

Shortcuts

-
+
+

Shortcuts

+

Introduction The Assets Folder @@ -341,25 +341,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: @@ -372,9 +372,9 @@ The Droidnix repository is organized into two main parts:

-
-

Root Level

-
+
+

Root Level

+
  • flake.nix is the entry point and imports:
      @@ -385,16 +385,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. @@ -415,9 +415,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

@@ -495,9 +495,9 @@ com.todoist.Todoist 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.

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

generated/modules/traveldroid/system/Colors.nix

-
+
+

generated/modules/traveldroid/system/Colors.nix

+

Setting the colors for Droidnix.

@@ -657,9 +657,9 @@ in
-
-

generated/hosts/traveldroid/boot.nix

-
+
+

generated/hosts/traveldroid/boot.nix

+
{ config, pkgs, lib, flakeRoot, ... }:
 
@@ -705,9 +705,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. @@ -765,9 +765,9 @@ in
-
-

generated/hosts/traveldroid/host.nix

-
+
+

generated/hosts/traveldroid/host.nix

+
{ lib, config, pkgs, flakeRoot, import-tree, home-manager, ... }:
 
@@ -826,13 +826,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

@@ -940,9 +940,9 @@ in {
-
-

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

-
+
+

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

+

This installs emacs

@@ -1038,9 +1038,9 @@ in
-
-

generated/modules/traveldroid/apps/flameshot.nix

-
+
+

generated/modules/traveldroid/apps/flameshot.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

@@ -1079,9 +1079,9 @@ in
-
-

generated/modules/traveldroid/apps/kitty.nix

-
+
+

generated/modules/traveldroid/apps/kitty.nix

+

This file sets up Kitty terminal

@@ -1139,9 +1139,9 @@ in
-
-

generated/modules/traveldroid/apps/starship.nix

-
+
+

generated/modules/traveldroid/apps/starship.nix

+

This file sets up starship prompt

@@ -1180,9 +1180,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

@@ -1223,9 +1223,9 @@ in
-
-

generated/modules/traveldroid/apps/wofi.nix

-
+
+

generated/modules/traveldroid/apps/wofi.nix

+

This is the install for Wofi, the launcher

@@ -1258,9 +1258,9 @@ in
-
-

generated/modules/traveldroid/apps/zenbrowser.nix

-
+
+

generated/modules/traveldroid/apps/zenbrowser.nix

+

This installs zen browser

@@ -1280,9 +1280,9 @@ in
-
-

generated/modules/traveldroid/apps/zsh.nix

-
+
+

generated/modules/traveldroid/apps/zsh.nix

+

This sets up the zsh in the terminal

@@ -1345,13 +1345,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

@@ -1369,9 +1369,9 @@ This file installs and configures fonts
-
-

generated/modules/traveldroid/desktop/gtk.nix

-
+
+

generated/modules/traveldroid/desktop/gtk.nix

+

Setting up GTK

@@ -1397,9 +1397,9 @@ in
-
-

generated/modules/traveldroid/desktop/hyprland.nix

-
+
+

generated/modules/traveldroid/desktop/hyprland.nix

+

Setting up Hyprland

@@ -1445,9 +1445,9 @@ in
-
-

generated/modules/traveldroid/desktop/stylix.nix

-
+
+

generated/modules/traveldroid/desktop/stylix.nix

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

generated/modules/traveldroid/desktop/wallpaper.nix

-
+
+

generated/modules/traveldroid/desktop/wallpaper.nix

+

Setting up wallpaper engine + wallpaper gui

@@ -1635,9 +1635,9 @@ in
-
-

generated/modules/traveldroid/desktop/waybar.nix

-
+
+

generated/modules/traveldroid/desktop/waybar.nix

+

This file installs and configures waybar

@@ -1704,9 +1704,9 @@ in
-
-

generated/modules/traveldroid/desktop/wayland.nix

-
+
+

generated/modules/traveldroid/desktop/wayland.nix

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

generated/modules/traveldroid/desktop/xdg.nix

-
+
+

generated/modules/traveldroid/desktop/xdg.nix

+

This sets the XDG implementation

@@ -1792,13 +1792,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, ... }:
 
@@ -1817,9 +1817,9 @@ in
 
-
-

generated/modules/traveldroid/system/bluetooth.nix

-
+
+

generated/modules/traveldroid/system/bluetooth.nix

+
{ lib, config, pkgs, home-manager, ... }:
 
@@ -1840,9 +1840,9 @@ in
 
-
-

generated/modules/traveldroid/system/copy_scripts.nix

-
+
+

generated/modules/traveldroid/system/copy_scripts.nix

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

generated/modules/traveldroid/system/dbus.nix

-
+
+

generated/modules/traveldroid/system/dbus.nix

+

This sets the dbus implementation

@@ -1909,9 +1909,9 @@ This sets the dbus implementation
-
-

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

-
+
+

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

+

This sets the dbus implementation

@@ -1959,9 +1959,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 imo fits the aesthetic I am aiming for

@@ -2015,9 +2015,9 @@ in
-
-

generated/modules/traveldroid/system/networking.nix

-
+
+

generated/modules/traveldroid/system/networking.nix

+

This sets the networking.

@@ -2082,9 +2082,9 @@ This sets the networking.
-
-

generated/modules/traveldroid/system/nix.nix

-
+
+

generated/modules/traveldroid/system/nix.nix

+
{ lib, config, ... }:
 
@@ -2100,9 +2100,9 @@ This sets the networking.
 
-
-

generated/modules/traveldroid/system/printing.nix

-
+
+

generated/modules/traveldroid/system/printing.nix

+

This sets the dbus implementation

@@ -2126,9 +2126,9 @@ This sets the dbus implementation
-
-

generated/modules/traveldroid/system/quickshell.nix

-
+
+

generated/modules/traveldroid/system/quickshell.nix

+

This sets the dbus implementation

@@ -2181,13 +2181,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

@@ -2249,9 +2249,9 @@ in

These are all the prepared config files

-
-

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

-
+
+

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

+

This contaions emacs

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

generated/.config/emacs/init.el

-
+
+

generated/.config/emacs/init.el

+

This contaions emacs

@@ -2764,9 +2764,9 @@ the top of the file."
-
-

generated/.config/hypr/animations.conf

-
+
+

generated/.config/hypr/animations.conf

+

These are config files for .config/hypr

@@ -2787,9 +2787,9 @@ animations {
-
-

generated/.config/hypr/behaviour.conf

-
+
+

generated/.config/hypr/behaviour.conf

+

These are config files for .config/hypr

@@ -2803,9 +2803,9 @@ These are config files for .config/hypr
-
-

generated/.config/hypr/bindings.conf

-
+
+

generated/.config/hypr/bindings.conf

+

These are config files for .config/hypr

@@ -2969,9 +2969,9 @@ bind = $mainMod, U, exec, kitty -e bash -lc "$HOME/.config/scripts/update.sh"
-
-

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

-
+
+

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

+

These are config files for .config/hypr

@@ -2991,9 +2991,9 @@ exec-once = ~/.config/scripts/hypr-autocolwidth.sh
-
-

generated/.config/hypr/hypridle.conf

-
+
+

generated/.config/hypr/hypridle.conf

+

These are config files for .config/hypr

@@ -3018,9 +3018,9 @@ listener {
-
-

generated/.config/hypr/hyprland.conf

-
+
+

generated/.config/hypr/hyprland.conf

+

These are config files for .config/hypr

@@ -3066,9 +3066,9 @@ misc {
-
-

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

-
+
+

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

+

These are config files for .config/hypr

@@ -3083,9 +3083,9 @@ layerrule = blur on, ignore_alpha 1, match:namespace swaync-notification-window
-
-

generated/.config/hypr/layout.conf

-
+
+

generated/.config/hypr/layout.conf

+

These are config files for .config/hypr

@@ -3100,9 +3100,9 @@ scrolling {
-
-

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

-
+
+

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

+

These are config files for .config/hypr

@@ -3113,9 +3113,9 @@ monitor=DP-1,3840x1080@144,1920x0,1
-
-

generated/.config/hypr/theming.conf

-
+
+

generated/.config/hypr/theming.conf

+

These are config files for .config/hypr

@@ -3165,9 +3165,9 @@ decoration {
-
-

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

-
+
+

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

+

These are config files for .config/hypr

@@ -3224,9 +3224,9 @@ windowrule {
-
-

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

-
+
+

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

+

These are config files for .config/hypr

@@ -3251,9 +3251,9 @@ workspace = 10
-
-

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

-
+
+

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

+

offers a adio widget

@@ -3286,15 +3286,19 @@ ShellRoot { // State QtObject { id: media - property string artist: "" - property string title: "" - property string album: "" - property string artUrl: "" - property string status: "" - property string device: "" - property real progress: 0.0 - property real duration: 0.0 - property real position: 0.0 + property string artist: "" + property string title: "" + property string album: "" + property string artUrl: "" + property string status: "" + property string device: "" + property string player: "" + property real progress: 0.0 + property real duration: 0.0 + property real position: 0.0 + // 0 = no shuffle, 1 = shuffle, 2 = smart shuffle + property int shuffleMode: 0 + readonly property bool isSpotify: player.indexOf("spotify") !== -1 } // Poll playerctl every second @@ -3303,13 +3307,34 @@ ShellRoot { running: true repeat: true onTriggered: { - artistProc.running = true - titleProc.running = true - albumProc.running = true - artProc.running = true - statusProc.running = true + playerProc.running = true + artistProc.running = true + titleProc.running = true + albumProc.running = true + artProc.running = true + statusProc.running = true positionProc.running = true - lengthProc.running = true + lengthProc.running = true + if (media.isSpotify) + shuffleProc.running = true + } + } + + // Detect active player — prefers spotify + Process { + id: playerProc + command: ["playerctl", "-l"] + stdout: StdioCollector { + onStreamFinished: { + var lines = text.trim().split("\n") + for (var i = 0; i < lines.length; i++) { + if (lines[i].indexOf("spotify") !== -1) { + media.player = lines[i].trim() + return + } + } + media.player = lines[0] ? lines[0].trim() : "" + } } } @@ -3359,10 +3384,48 @@ ShellRoot { } } + // Read shuffle state from playerctl + Process { + id: shuffleProc + command: ["playerctl", "--player=" + media.player, "shuffle"] + stdout: StdioCollector { + onStreamFinished: { + if (media.shuffleMode !== 2) + media.shuffleMode = (text.trim() === "On") ? 1 : 0 + } + } + } + + Process { id: shuffleOnProc; command: ["playerctl", "--player=" + media.player, "shuffle", "on"] } + Process { id: shuffleOffProc; command: ["playerctl", "--player=" + media.player, "shuffle", "off"] } + + function cycleShuffleMode() { + var next = (media.shuffleMode + 1) % 3 + media.shuffleMode = next + if (next === 0) + shuffleOffProc.running = true + else + shuffleOnProc.running = true + } + + // Focus / open the active player app + Process { + id: focusProc + property string cmd: "" + command: ["bash", "-c", cmd] + } + + function focusPlayer() { + var cls = media.isSpotify ? "Spotify" : media.player.split(".")[0] + var bin = cls.toLowerCase() + focusProc.cmd = "hyprctl dispatch focuswindow class:^(" + cls + ")$ 2>/dev/null || " + bin + " &" + focusProc.running = true + } + // Playback controls - Process { id: prevProc; command: ["playerctl", "previous"] } - Process { id: playProc; command: ["playerctl", "play-pause"] } - Process { id: nextProc; command: ["playerctl", "next"] } + Process { id: prevProc; command: ["playerctl", "previous"] } + Process { id: playProc; command: ["playerctl", "play-pause"] } + Process { id: nextProc; command: ["playerctl", "next"] } FloatingWindow { id: root @@ -3377,7 +3440,7 @@ ShellRoot { onActivated: Qt.quit() } - // Gradient border + // Gradient border — hidden when window has focus Rectangle { anchors.fill: parent anchors.margins: -2 @@ -3385,13 +3448,13 @@ ShellRoot { z: -1 opacity: root.active ? 0 : 1 Behavior on opacity { - NumberAnimation { duration: 150 } - } + NumberAnimation { duration: 150 } + } gradient: Gradient { orientation: Gradient.Horizontal GradientStop { position: 0.0; color: colors.blue } GradientStop { position: 1.0; color: colors.green } - } + } } Rectangle { @@ -3406,7 +3469,7 @@ ShellRoot { } spacing: 12 - // Album art + // Album art — click to focus player Rectangle { Layout.fillWidth: true Layout.preferredHeight: 200 @@ -3421,7 +3484,6 @@ ShellRoot { visible: media.artUrl !== "" } - // Placeholder when no art Text { anchors.centerIn: parent text: "󰝚" @@ -3429,6 +3491,12 @@ ShellRoot { color: colors.surface2 visible: media.artUrl === "" } + + MouseArea { + anchors.fill: parent + cursorShape: Qt.PointingHandCursor + onClicked: focusPlayer() + } } // Artist @@ -3515,11 +3583,46 @@ ShellRoot { } } - // Playback controls + // Playback controls + shuffle RowLayout { Layout.fillWidth: true Layout.alignment: Qt.AlignHCenter - spacing: 24 + spacing: 20 + + // Shuffle button (Spotify only) + Item { + visible: media.isSpotify + width: 28 + height: 28 + + Text { + anchors.centerIn: parent + text: "󰒟" + font.pixelSize: 18 + color: media.shuffleMode === 0 ? colors.surface2 + : media.shuffleMode === 1 ? colors.blue + : colors.mauve + + // "S" badge for smart shuffle + Text { + anchors.bottom: parent.bottom + anchors.right: parent.right + anchors.bottomMargin: -2 + anchors.rightMargin: -4 + text: "S" + font.pixelSize: 7 + font.bold: true + color: colors.mauve + visible: media.shuffleMode === 2 + } + } + + MouseArea { + anchors.fill: parent + cursorShape: Qt.PointingHandCursor + onClicked: cycleShuffleMode() + } + } Text { text: "󰒮" @@ -3529,6 +3632,7 @@ ShellRoot { id: prevHover anchors.fill: parent hoverEnabled: true + cursorShape: Qt.PointingHandCursor onClicked: prevProc.running = true } } @@ -3541,6 +3645,7 @@ ShellRoot { id: playHover anchors.fill: parent hoverEnabled: true + cursorShape: Qt.PointingHandCursor onClicked: playProc.running = true } } @@ -3553,10 +3658,22 @@ ShellRoot { id: nextHover anchors.fill: parent hoverEnabled: true + cursorShape: Qt.PointingHandCursor onClicked: nextProc.running = true } } } + + // Shuffle mode label + Text { + Layout.alignment: Qt.AlignHCenter + text: media.shuffleMode === 1 ? "Shuffle" + : media.shuffleMode === 2 ? "Smart Shuffle" + : "" + color: media.shuffleMode === 2 ? colors.mauve : colors.blue + font.pixelSize: 10 + visible: media.isSpotify && media.shuffleMode !== 0 + } } } } @@ -3565,9 +3682,9 @@ ShellRoot {
-
-

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

-
+
+

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

+

Provides a powermenu

@@ -3711,14 +3828,16 @@ ShellRoot {
-
-

generated/.config/scripts/media.sh

-
+
+

generated/.config/scripts/media.sh

+

Providing an media

-
# Player selection — prefer actively playing player
+
#!/usr/bin/env bash
+
+# Player selection — prefer actively playing player
 player=$(playerctl -l 2>/dev/null | while read -r p; do
     st=$(playerctl --player="$p" status 2>/dev/null)
     [ "$st" = "Playing" ] && echo "$p" && break
@@ -3809,9 +3928,9 @@ jq -c -n \
 
-
-

generated/.config/scripts/layout-selector.sh

-
+
+

generated/.config/scripts/layout-selector.sh

+

Choose your layout

@@ -3853,9 +3972,9 @@ hyprctl dispatch oSD "Layout: $LAYOUT_NAME" 2000
-
-

generated/.config/scripts/hypr-autocolwidth.sh

-
+
+

generated/.config/scripts/hypr-autocolwidth.sh

+

These are config files for .config/scripts

@@ -3909,9 +4028,9 @@ done
-
-

generated/.config/scripts/power.sh

-
+
+

generated/.config/scripts/power.sh

+

A file containing color variables

@@ -3967,9 +4086,9 @@ main
-
-

generated/.config/scripts/update.sh

-
+
+

generated/.config/scripts/update.sh

+

A file containing color variables

@@ -3988,9 +4107,9 @@ flatpak update -y
-
-

generated/.config/shared/Colors.css

-
+
+

generated/.config/shared/Colors.css

+

A file containing color variables

@@ -4030,9 +4149,9 @@ A file containing color variables
-
-

generated/.config/starship.toml

-
+
+

generated/.config/starship.toml

+

These are config files for Starship

@@ -4320,9 +4439,9 @@ crust = "#181926"
-
-

generated/.config/stylix/stylix.conf

-
+
+

generated/.config/stylix/stylix.conf

+

These are config files for .config/stylix

@@ -4359,9 +4478,9 @@ icons = {
-
-

generated/.config/stylix/palette.json

-
+
+

generated/.config/stylix/palette.json

+

These are config files for .config/stylix

@@ -4391,9 +4510,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

@@ -4471,9 +4590,9 @@ These are config files for .config/stylix
-
-

generated/.config/waybar/config

-
+
+

generated/.config/waybar/config

+

These are config files for waybar

@@ -4738,9 +4857,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

@@ -4965,9 +5084,9 @@ label#custom-media.module {
-
-

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

-
+
+

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

+

These are config files for waybar

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

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

-
+
+

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

+

These are config files for waybar

@@ -5036,9 +5155,9 @@ jq -c -n \
-
-

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

-
+
+

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

+

These are config files for waybar

@@ -5054,9 +5173,9 @@ hyprctl dispatch focuswindow address:$addr
-
-

generated/.config/waypaper/config.ini

-
+
+

generated/.config/waypaper/config.ini

+

These are config files for waypaper

@@ -5093,9 +5212,9 @@ keybindings = ~/.config/waypaper/keybindings.ini
-
-

generated/.config/waypaper/config.ini

-
+
+

generated/.config/waypaper/config.ini

+

These are config files for .config/waypaper

@@ -5131,9 +5250,9 @@ keybindings = ~/.config/waypaper/keybindings.ini
-
-

generated/.config/wofi/config

-
+
+

generated/.config/wofi/config

+

These are config files for .config/wofi

@@ -5182,9 +5301,9 @@ prompt = > ...
-
-

generated/.config/wofi/style.css

-
+
+

generated/.config/wofi/style.css

+

These are config files for .config/wofi

@@ -5279,9 +5398,9 @@ These are config files for .config/wofi
-
-

generated/.config/zed/settings.json

-
+
+

generated/.config/zed/settings.json

+

These are config files for Zed editor

@@ -5317,9 +5436,9 @@ These are config files for Zed editor
-
-

generated/.config/zsh/.zshrc

-
+
+

generated/.config/zsh/.zshrc

+

This sets up the zsh in the terminal

@@ -5344,7 +5463,7 @@ fi

Author: Henro Veijer

-

Created: 2026-04-13 ma 10:55

+

Created: 2026-04-13 ma 11:02

Validate

diff --git a/Droidnix/README.org b/Droidnix/README.org index 8eed7ab10..3f0caf33a 100644 --- a/Droidnix/README.org +++ b/Droidnix/README.org @@ -2656,15 +2656,19 @@ ShellRoot { // State QtObject { id: media - property string artist: "" - property string title: "" - property string album: "" - property string artUrl: "" - property string status: "" - property string device: "" - property real progress: 0.0 - property real duration: 0.0 - property real position: 0.0 + property string artist: "" + property string title: "" + property string album: "" + property string artUrl: "" + property string status: "" + property string device: "" + property string player: "" + property real progress: 0.0 + property real duration: 0.0 + property real position: 0.0 + // 0 = no shuffle, 1 = shuffle, 2 = smart shuffle + property int shuffleMode: 0 + readonly property bool isSpotify: player.indexOf("spotify") !== -1 } // Poll playerctl every second @@ -2673,13 +2677,34 @@ ShellRoot { running: true repeat: true onTriggered: { - artistProc.running = true - titleProc.running = true - albumProc.running = true - artProc.running = true - statusProc.running = true + playerProc.running = true + artistProc.running = true + titleProc.running = true + albumProc.running = true + artProc.running = true + statusProc.running = true positionProc.running = true - lengthProc.running = true + lengthProc.running = true + if (media.isSpotify) + shuffleProc.running = true + } + } + + // Detect active player — prefers spotify + Process { + id: playerProc + command: ["playerctl", "-l"] + stdout: StdioCollector { + onStreamFinished: { + var lines = text.trim().split("\n") + for (var i = 0; i < lines.length; i++) { + if (lines[i].indexOf("spotify") !== -1) { + media.player = lines[i].trim() + return + } + } + media.player = lines[0] ? lines[0].trim() : "" + } } } @@ -2729,10 +2754,48 @@ ShellRoot { } } + // Read shuffle state from playerctl + Process { + id: shuffleProc + command: ["playerctl", "--player=" + media.player, "shuffle"] + stdout: StdioCollector { + onStreamFinished: { + if (media.shuffleMode !== 2) + media.shuffleMode = (text.trim() === "On") ? 1 : 0 + } + } + } + + Process { id: shuffleOnProc; command: ["playerctl", "--player=" + media.player, "shuffle", "on"] } + Process { id: shuffleOffProc; command: ["playerctl", "--player=" + media.player, "shuffle", "off"] } + + function cycleShuffleMode() { + var next = (media.shuffleMode + 1) % 3 + media.shuffleMode = next + if (next === 0) + shuffleOffProc.running = true + else + shuffleOnProc.running = true + } + + // Focus / open the active player app + Process { + id: focusProc + property string cmd: "" + command: ["bash", "-c", cmd] + } + + function focusPlayer() { + var cls = media.isSpotify ? "Spotify" : media.player.split(".")[0] + var bin = cls.toLowerCase() + focusProc.cmd = "hyprctl dispatch focuswindow class:^(" + cls + ")$ 2>/dev/null || " + bin + " &" + focusProc.running = true + } + // Playback controls - Process { id: prevProc; command: ["playerctl", "previous"] } - Process { id: playProc; command: ["playerctl", "play-pause"] } - Process { id: nextProc; command: ["playerctl", "next"] } + Process { id: prevProc; command: ["playerctl", "previous"] } + Process { id: playProc; command: ["playerctl", "play-pause"] } + Process { id: nextProc; command: ["playerctl", "next"] } FloatingWindow { id: root @@ -2747,7 +2810,7 @@ ShellRoot { onActivated: Qt.quit() } - // Gradient border + // Gradient border — hidden when window has focus Rectangle { anchors.fill: parent anchors.margins: -2 @@ -2755,13 +2818,13 @@ ShellRoot { z: -1 opacity: root.active ? 0 : 1 Behavior on opacity { - NumberAnimation { duration: 150 } - } + NumberAnimation { duration: 150 } + } gradient: Gradient { orientation: Gradient.Horizontal GradientStop { position: 0.0; color: colors.blue } GradientStop { position: 1.0; color: colors.green } - } + } } Rectangle { @@ -2776,7 +2839,7 @@ ShellRoot { } spacing: 12 - // Album art + // Album art — click to focus player Rectangle { Layout.fillWidth: true Layout.preferredHeight: 200 @@ -2791,7 +2854,6 @@ ShellRoot { visible: media.artUrl !== "" } - // Placeholder when no art Text { anchors.centerIn: parent text: "󰝚" @@ -2799,6 +2861,12 @@ ShellRoot { color: colors.surface2 visible: media.artUrl === "" } + + MouseArea { + anchors.fill: parent + cursorShape: Qt.PointingHandCursor + onClicked: focusPlayer() + } } // Artist @@ -2885,11 +2953,46 @@ ShellRoot { } } - // Playback controls + // Playback controls + shuffle RowLayout { Layout.fillWidth: true Layout.alignment: Qt.AlignHCenter - spacing: 24 + spacing: 20 + + // Shuffle button (Spotify only) + Item { + visible: media.isSpotify + width: 28 + height: 28 + + Text { + anchors.centerIn: parent + text: "󰒟" + font.pixelSize: 18 + color: media.shuffleMode === 0 ? colors.surface2 + : media.shuffleMode === 1 ? colors.blue + : colors.mauve + + // "S" badge for smart shuffle + Text { + anchors.bottom: parent.bottom + anchors.right: parent.right + anchors.bottomMargin: -2 + anchors.rightMargin: -4 + text: "S" + font.pixelSize: 7 + font.bold: true + color: colors.mauve + visible: media.shuffleMode === 2 + } + } + + MouseArea { + anchors.fill: parent + cursorShape: Qt.PointingHandCursor + onClicked: cycleShuffleMode() + } + } Text { text: "󰒮" @@ -2899,6 +3002,7 @@ ShellRoot { id: prevHover anchors.fill: parent hoverEnabled: true + cursorShape: Qt.PointingHandCursor onClicked: prevProc.running = true } } @@ -2911,6 +3015,7 @@ ShellRoot { id: playHover anchors.fill: parent hoverEnabled: true + cursorShape: Qt.PointingHandCursor onClicked: playProc.running = true } } @@ -2923,10 +3028,22 @@ ShellRoot { id: nextHover anchors.fill: parent hoverEnabled: true + cursorShape: Qt.PointingHandCursor onClicked: nextProc.running = true } } } + + // Shuffle mode label + Text { + Layout.alignment: Qt.AlignHCenter + text: media.shuffleMode === 1 ? "Shuffle" + : media.shuffleMode === 2 ? "Smart Shuffle" + : "" + color: media.shuffleMode === 2 ? colors.mauve : colors.blue + font.pixelSize: 10 + visible: media.isSpotify && media.shuffleMode !== 0 + } } } } @@ -3076,6 +3193,8 @@ ShellRoot { ** =generated/.config/scripts/media.sh= Providing an media #+BEGIN_SRC sh :tangle generated/.config/scripts/media.sh :shebang "#!/usr/bin/env bash" :noweb yes :mkdirp yes :eval never +#!/usr/bin/env bash + # Player selection — prefer actively playing player player=$(playerctl -l 2>/dev/null | while read -r p; do st=$(playerctl --player="$p" status 2>/dev/null) diff --git a/Droidnix/generated/.config/quickshell/media/shell.qml b/Droidnix/generated/.config/quickshell/media/shell.qml index d7c9626a2..a4066222d 100644 --- a/Droidnix/generated/.config/quickshell/media/shell.qml +++ b/Droidnix/generated/.config/quickshell/media/shell.qml @@ -27,15 +27,19 @@ ShellRoot { // State QtObject { id: media - property string artist: "" - property string title: "" - property string album: "" - property string artUrl: "" - property string status: "" - property string device: "" - property real progress: 0.0 - property real duration: 0.0 - property real position: 0.0 + property string artist: "" + property string title: "" + property string album: "" + property string artUrl: "" + property string status: "" + property string device: "" + property string player: "" + property real progress: 0.0 + property real duration: 0.0 + property real position: 0.0 + // 0 = no shuffle, 1 = shuffle, 2 = smart shuffle + property int shuffleMode: 0 + readonly property bool isSpotify: player.indexOf("spotify") !== -1 } // Poll playerctl every second @@ -44,13 +48,34 @@ ShellRoot { running: true repeat: true onTriggered: { - artistProc.running = true - titleProc.running = true - albumProc.running = true - artProc.running = true - statusProc.running = true + playerProc.running = true + artistProc.running = true + titleProc.running = true + albumProc.running = true + artProc.running = true + statusProc.running = true positionProc.running = true - lengthProc.running = true + lengthProc.running = true + if (media.isSpotify) + shuffleProc.running = true + } + } + + // Detect active player — prefers spotify + Process { + id: playerProc + command: ["playerctl", "-l"] + stdout: StdioCollector { + onStreamFinished: { + var lines = text.trim().split("\n") + for (var i = 0; i < lines.length; i++) { + if (lines[i].indexOf("spotify") !== -1) { + media.player = lines[i].trim() + return + } + } + media.player = lines[0] ? lines[0].trim() : "" + } } } @@ -100,10 +125,48 @@ ShellRoot { } } + // Read shuffle state from playerctl + Process { + id: shuffleProc + command: ["playerctl", "--player=" + media.player, "shuffle"] + stdout: StdioCollector { + onStreamFinished: { + if (media.shuffleMode !== 2) + media.shuffleMode = (text.trim() === "On") ? 1 : 0 + } + } + } + + Process { id: shuffleOnProc; command: ["playerctl", "--player=" + media.player, "shuffle", "on"] } + Process { id: shuffleOffProc; command: ["playerctl", "--player=" + media.player, "shuffle", "off"] } + + function cycleShuffleMode() { + var next = (media.shuffleMode + 1) % 3 + media.shuffleMode = next + if (next === 0) + shuffleOffProc.running = true + else + shuffleOnProc.running = true + } + + // Focus / open the active player app + Process { + id: focusProc + property string cmd: "" + command: ["bash", "-c", cmd] + } + + function focusPlayer() { + var cls = media.isSpotify ? "Spotify" : media.player.split(".")[0] + var bin = cls.toLowerCase() + focusProc.cmd = "hyprctl dispatch focuswindow class:^(" + cls + ")$ 2>/dev/null || " + bin + " &" + focusProc.running = true + } + // Playback controls - Process { id: prevProc; command: ["playerctl", "previous"] } - Process { id: playProc; command: ["playerctl", "play-pause"] } - Process { id: nextProc; command: ["playerctl", "next"] } + Process { id: prevProc; command: ["playerctl", "previous"] } + Process { id: playProc; command: ["playerctl", "play-pause"] } + Process { id: nextProc; command: ["playerctl", "next"] } FloatingWindow { id: root @@ -118,7 +181,7 @@ ShellRoot { onActivated: Qt.quit() } - // Gradient border + // Gradient border — hidden when window has focus Rectangle { anchors.fill: parent anchors.margins: -2 @@ -126,13 +189,13 @@ ShellRoot { z: -1 opacity: root.active ? 0 : 1 Behavior on opacity { - NumberAnimation { duration: 150 } - } + NumberAnimation { duration: 150 } + } gradient: Gradient { orientation: Gradient.Horizontal GradientStop { position: 0.0; color: colors.blue } GradientStop { position: 1.0; color: colors.green } - } + } } Rectangle { @@ -147,7 +210,7 @@ ShellRoot { } spacing: 12 - // Album art + // Album art — click to focus player Rectangle { Layout.fillWidth: true Layout.preferredHeight: 200 @@ -162,7 +225,6 @@ ShellRoot { visible: media.artUrl !== "" } - // Placeholder when no art Text { anchors.centerIn: parent text: "󰝚" @@ -170,6 +232,12 @@ ShellRoot { color: colors.surface2 visible: media.artUrl === "" } + + MouseArea { + anchors.fill: parent + cursorShape: Qt.PointingHandCursor + onClicked: focusPlayer() + } } // Artist @@ -256,11 +324,46 @@ ShellRoot { } } - // Playback controls + // Playback controls + shuffle RowLayout { Layout.fillWidth: true Layout.alignment: Qt.AlignHCenter - spacing: 24 + spacing: 20 + + // Shuffle button (Spotify only) + Item { + visible: media.isSpotify + width: 28 + height: 28 + + Text { + anchors.centerIn: parent + text: "󰒟" + font.pixelSize: 18 + color: media.shuffleMode === 0 ? colors.surface2 + : media.shuffleMode === 1 ? colors.blue + : colors.mauve + + // "S" badge for smart shuffle + Text { + anchors.bottom: parent.bottom + anchors.right: parent.right + anchors.bottomMargin: -2 + anchors.rightMargin: -4 + text: "S" + font.pixelSize: 7 + font.bold: true + color: colors.mauve + visible: media.shuffleMode === 2 + } + } + + MouseArea { + anchors.fill: parent + cursorShape: Qt.PointingHandCursor + onClicked: cycleShuffleMode() + } + } Text { text: "󰒮" @@ -270,6 +373,7 @@ ShellRoot { id: prevHover anchors.fill: parent hoverEnabled: true + cursorShape: Qt.PointingHandCursor onClicked: prevProc.running = true } } @@ -282,6 +386,7 @@ ShellRoot { id: playHover anchors.fill: parent hoverEnabled: true + cursorShape: Qt.PointingHandCursor onClicked: playProc.running = true } } @@ -294,10 +399,22 @@ ShellRoot { id: nextHover anchors.fill: parent hoverEnabled: true + cursorShape: Qt.PointingHandCursor onClicked: nextProc.running = true } } } + + // Shuffle mode label + Text { + Layout.alignment: Qt.AlignHCenter + text: media.shuffleMode === 1 ? "Shuffle" + : media.shuffleMode === 2 ? "Smart Shuffle" + : "" + color: media.shuffleMode === 2 ? colors.mauve : colors.blue + font.pixelSize: 10 + visible: media.isSpotify && media.shuffleMode !== 0 + } } } } diff --git a/Droidnix/generated/.config/scripts/media.sh b/Droidnix/generated/.config/scripts/media.sh index fff9f3edc..b117e5db0 100755 --- a/Droidnix/generated/.config/scripts/media.sh +++ b/Droidnix/generated/.config/scripts/media.sh @@ -1,5 +1,7 @@ #!/usr/bin/env bash # --- This file has been auto-generated. For permanent changes alter the appropriate block in the README.org. --- +#!/usr/bin/env bash + # Player selection — prefer actively playing player player=$(playerctl -l 2>/dev/null | while read -r p; do st=$(playerctl --player="$p" status 2>/dev/null)