added quickshell and updated zsh
66
flake.lock
generated
@@ -77,6 +77,27 @@
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"herdr": {
|
||||
"inputs": {
|
||||
"nixpkgs": [
|
||||
"nixpkgs"
|
||||
],
|
||||
"rust-overlay": "rust-overlay"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1787017031,
|
||||
"narHash": "sha256-FeF4eZhxRl4jBbAkMaEzDCmMcaitV6GZapjaeeNZy7A=",
|
||||
"owner": "herdrdev",
|
||||
"repo": "herdr",
|
||||
"rev": "2d24950ad9a02096921bc764e2e3dcd8900c3366",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "herdrdev",
|
||||
"repo": "herdr",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"home-manager": {
|
||||
"inputs": {
|
||||
"nixpkgs": [
|
||||
@@ -193,15 +214,58 @@
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"quickshell": {
|
||||
"inputs": {
|
||||
"nixpkgs": [
|
||||
"nixpkgs"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1786864924,
|
||||
"narHash": "sha256-9nqIJWuSlAwy4Vs98N9ffoVeU2dmsWcAJ7/DyIDihqM=",
|
||||
"ref": "refs/heads/master",
|
||||
"rev": "532a0606cafb05851a203e65be22c9a057447ee2",
|
||||
"revCount": 844,
|
||||
"type": "git",
|
||||
"url": "https://git.outfoxxed.me/outfoxxed/quickshell"
|
||||
},
|
||||
"original": {
|
||||
"type": "git",
|
||||
"url": "https://git.outfoxxed.me/outfoxxed/quickshell"
|
||||
}
|
||||
},
|
||||
"root": {
|
||||
"inputs": {
|
||||
"antigravity": "antigravity",
|
||||
"comfy-ui": "comfy-ui",
|
||||
"disko": "disko",
|
||||
"herdr": "herdr",
|
||||
"home-manager": "home-manager",
|
||||
"nixos-hardware": "nixos-hardware",
|
||||
"nixpkgs": "nixpkgs_3",
|
||||
"nixpkgs-unstable": "nixpkgs-unstable"
|
||||
"nixpkgs-unstable": "nixpkgs-unstable",
|
||||
"quickshell": "quickshell"
|
||||
}
|
||||
},
|
||||
"rust-overlay": {
|
||||
"inputs": {
|
||||
"nixpkgs": [
|
||||
"herdr",
|
||||
"nixpkgs"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1783577481,
|
||||
"narHash": "sha256-EWMxOWrJ031f8f/lrcEmhTRs4npw1/5N3Hcjin846c8=",
|
||||
"owner": "oxalica",
|
||||
"repo": "rust-overlay",
|
||||
"rev": "4cdea398dc491b082dccdce0fad1ed0b75fa3394",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "oxalica",
|
||||
"repo": "rust-overlay",
|
||||
"type": "github"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
10
flake.nix
@@ -24,6 +24,16 @@
|
||||
comfy-ui = {
|
||||
url = "github:utensils/comfyui-nix";
|
||||
};
|
||||
|
||||
quickshell = {
|
||||
url = "git+https://git.outfoxxed.me/outfoxxed/quickshell";
|
||||
inputs.nixpkgs.follows = "nixpkgs";
|
||||
};
|
||||
|
||||
herdr = {
|
||||
url = "github:herdrdev/herdr";
|
||||
inputs.nixpkgs.follows = "nixpkgs";
|
||||
};
|
||||
};
|
||||
|
||||
outputs =
|
||||
|
||||
@@ -1,11 +1,73 @@
|
||||
{ pkgs, ... }:
|
||||
{ pkgs, inputs, ... }:
|
||||
|
||||
let
|
||||
herdrPkg = if (inputs ? herdr && inputs.herdr ? packages && inputs.herdr.packages ? ${pkgs.stdenv.hostPlatform.system})
|
||||
then inputs.herdr.packages.${pkgs.stdenv.hostPlatform.system}.default
|
||||
else (pkgs.herdr or null);
|
||||
|
||||
herdrPicker = pkgs.writeShellScriptBin "herdr-picker" (builtins.readFile ./scripts/herdr-picker.sh);
|
||||
in
|
||||
{
|
||||
home.packages = (if herdrPkg != null then [ herdrPkg ] else []) ++ [
|
||||
herdrPicker
|
||||
];
|
||||
|
||||
programs.foot = {
|
||||
enable = true;
|
||||
settings = {
|
||||
main = {
|
||||
font = "JetBrainsMono Nerd Font:size=16";
|
||||
font = "JetBrainsMono Nerd Font:size=15";
|
||||
font-bold = "JetBrainsMono Nerd Font:size=15:weight=bold";
|
||||
font-italic = "JetBrainsMono Nerd Font:size=15:slant=italic";
|
||||
pad = "16x16 center";
|
||||
shell = "${herdrPicker}/bin/herdr-picker";
|
||||
selection-target = "clipboard";
|
||||
};
|
||||
|
||||
scrollback = {
|
||||
lines = 10000;
|
||||
multiplier = 3.0;
|
||||
};
|
||||
|
||||
cursor = {
|
||||
style = "beam";
|
||||
blink = "yes";
|
||||
beam-thickness = 1.5;
|
||||
};
|
||||
|
||||
mouse = {
|
||||
hide-when-typing = "yes";
|
||||
};
|
||||
|
||||
colors = {
|
||||
alpha = 0.90;
|
||||
background = "18181a";
|
||||
foreground = "f2f2f7";
|
||||
|
||||
# macOS Catalina Apple System Dark Palette
|
||||
regular0 = "1c1c1e"; # Black / Slate
|
||||
regular1 = "ff453a"; # Apple Red
|
||||
regular2 = "30d158"; # Apple Green
|
||||
regular3 = "ffd60a"; # Apple Yellow
|
||||
regular4 = "0a84ff"; # Apple Blue
|
||||
regular5 = "bf5af2"; # Apple Purple
|
||||
regular6 = "64d2ff"; # Apple Cyan
|
||||
regular7 = "f2f2f7"; # Apple Light Gray
|
||||
|
||||
# Bright Colors
|
||||
bright0 = "636366"; # Gray
|
||||
bright1 = "ff6961"; # Bright Red
|
||||
bright2 = "34c759"; # Bright Green
|
||||
bright3 = "ffe066"; # Bright Yellow
|
||||
bright4 = "409cff"; # Bright Blue
|
||||
bright5 = "c774f4"; # Bright Purple
|
||||
bright6 = "70d7ff"; # Bright Cyan
|
||||
bright7 = "ffffff"; # Pure White
|
||||
|
||||
# Selection & URLs
|
||||
selection-foreground = "ffffff";
|
||||
selection-background = "0058d0"; # Apple selection blue
|
||||
urls = "0a84ff";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
./hyprland.nix
|
||||
./hyprlock.nix
|
||||
./hypridle.nix
|
||||
./waybar.nix
|
||||
./quickshell.nix
|
||||
];
|
||||
|
||||
home.packages = with pkgs; [
|
||||
@@ -17,11 +17,8 @@
|
||||
brightnessctl
|
||||
|
||||
# Desktop Environment / Hyprland ecosystem
|
||||
waybar
|
||||
hyprpaper
|
||||
swaynotificationcenter
|
||||
rofi
|
||||
wlogout
|
||||
hyprpolkitagent
|
||||
];
|
||||
}
|
||||
|
||||
@@ -178,9 +178,8 @@ in
|
||||
_args = [
|
||||
"hyprland.start"
|
||||
(mkLua ''function()
|
||||
hl.exec_cmd("waybar")
|
||||
hl.exec_cmd("quickshell")
|
||||
hl.exec_cmd("hyprpaper")
|
||||
hl.exec_cmd("swaync")
|
||||
hl.exec_cmd("${pkgs.hyprpolkitagent}/libexec/hyprpolkitagent")
|
||||
end'')
|
||||
];
|
||||
@@ -194,9 +193,10 @@ in
|
||||
# Launchers & Window Controls
|
||||
{ _args = [ (mkLua "mainMod .. \" + Q\"") (mkLua "hl.dsp.exec_cmd(terminal)") ]; }
|
||||
{ _args = [ (mkLua "mainMod .. \" + C\"") (mkLua "hl.dsp.window.close()") ]; }
|
||||
{ _args = [ (mkLua "mainMod .. \" + N\"") (mkLua "hl.dsp.exec_cmd(\"swaync-client -t -sw\")") ]; }
|
||||
{ _args = [ (mkLua "mainMod .. \" + SPACE\"") (mkLua "hl.dsp.exec_cmd(\"quickshell ipc call spotlight toggle || rofi -show combi -show-icons\")") ]; }
|
||||
{ _args = [ (mkLua "mainMod .. \" + N\"") (mkLua "hl.dsp.exec_cmd(\"quickshell ipc call controlCenter toggle || swaync-client -t -sw\")") ]; }
|
||||
{ _args = [ (mkLua "mainMod .. \" + V\"") (mkLua "hl.dsp.window.float({ action = \"toggle\" })") ]; }
|
||||
{ _args = [ (mkLua "mainMod .. \" + R\"") (mkLua "hl.dsp.exec_cmd(\"rofi -show combi -show-icons\")") ]; }
|
||||
{ _args = [ (mkLua "mainMod .. \" + R\"") (mkLua "hl.dsp.exec_cmd(\"quickshell ipc call spotlight toggle || rofi -show combi -show-icons\")") ]; }
|
||||
{ _args = [ (mkLua "mainMod .. \" + G\"") (mkLua "hl.dsp.group.toggle()") ]; }
|
||||
{ _args = [ (mkLua "mainMod .. \" + F\"") (mkLua "hl.dsp.window.fullscreen({ fullscreen = 0 })") ]; }
|
||||
|
||||
@@ -221,7 +221,7 @@ in
|
||||
{ _args = [ (mkLua "mainMod .. \" + mouse_up\"") (mkLua "hl.dsp.focus({ workspace = \"e-1\" })") ]; }
|
||||
|
||||
# Lock shortcut
|
||||
{ _args = [ (mkLua "mainMod .. \" + S\"") (mkLua "hl.dsp.exec_cmd(\"wlogout\")") ]; }
|
||||
{ _args = [ (mkLua "mainMod .. \" + S\"") (mkLua "hl.dsp.exec_cmd(\"hyprlock\")") ]; }
|
||||
|
||||
# Quick access applications
|
||||
{ _args = [ (mkLua "mainMod .. \" + b\"") (mkLua "hl.dsp.exec_cmd(browser)") ]; }
|
||||
|
||||
25
home/hypr/quickshell.nix
Normal file
@@ -0,0 +1,25 @@
|
||||
{ pkgs, inputs, ... }:
|
||||
|
||||
let
|
||||
quickshellPkg = if (inputs ? quickshell && inputs.quickshell ? packages && inputs.quickshell.packages ? ${pkgs.stdenv.hostPlatform.system})
|
||||
then inputs.quickshell.packages.${pkgs.stdenv.hostPlatform.system}.default
|
||||
else (pkgs.quickshell or pkgs.qt6.qtbase);
|
||||
in
|
||||
{
|
||||
home.packages = with pkgs; [
|
||||
quickshellPkg
|
||||
brightnessctl
|
||||
pamixer
|
||||
playerctl
|
||||
libnotify
|
||||
grim
|
||||
slurp
|
||||
wl-clipboard
|
||||
];
|
||||
|
||||
# Link Quickshell configuration into ~/.config/quickshell
|
||||
xdg.configFile."quickshell" = {
|
||||
source = ./quickshell;
|
||||
recursive = true;
|
||||
};
|
||||
}
|
||||
280
home/hypr/quickshell/GlobalState.qml
Normal file
@@ -0,0 +1,280 @@
|
||||
pragma Singleton
|
||||
import QtQuick
|
||||
import Quickshell
|
||||
import Quickshell.Hyprland
|
||||
import Quickshell.Io
|
||||
|
||||
QtObject {
|
||||
id: root
|
||||
|
||||
// Modal / Overlay states
|
||||
property bool spotlightVisible: false
|
||||
property bool controlCenterVisible: false
|
||||
property bool aboutMacVisible: false
|
||||
property bool appleMenuVisible: false
|
||||
property string activeDropdownMenu: ""
|
||||
property bool volumePopupVisible: false
|
||||
property bool wifiPopupVisible: false
|
||||
property bool batteryPopupVisible: false
|
||||
property bool calendarPopupVisible: false
|
||||
|
||||
// Active Window & Compositor info
|
||||
property string activeWindowTitle: "Desktop"
|
||||
property string activeWindowClass: "Finder"
|
||||
property var runningApps: []
|
||||
|
||||
// Date & Time
|
||||
property string timeString: "9:41 AM"
|
||||
property string dateString: "Mon Oct 21"
|
||||
property string fullDateString: "Monday, October 21, 2026"
|
||||
|
||||
// System Monitoring States
|
||||
property int cpuPercent: 0
|
||||
property int memPercent: 0
|
||||
property string memUsedGb: "0.0"
|
||||
property string memTotalGb: "0.0"
|
||||
|
||||
property int volume: 50
|
||||
property bool volumeMuted: false
|
||||
property int brightness: 100
|
||||
|
||||
property bool wifiConnected: true
|
||||
property string wifiSsid: "Wi-Fi"
|
||||
property string ipAddress: ""
|
||||
|
||||
property bool batteryPresent: false
|
||||
property int batteryPercent: 100
|
||||
property bool batteryCharging: false
|
||||
property string batteryStatus: "Full"
|
||||
|
||||
// Media Player State (MPRIS)
|
||||
property string mediaTitle: ""
|
||||
property string mediaArtist: ""
|
||||
property bool mediaPlaying: false
|
||||
|
||||
// Close all popups and dropdowns
|
||||
function closeAllPopups() {
|
||||
appleMenuVisible = false;
|
||||
activeDropdownMenu = "";
|
||||
volumePopupVisible = false;
|
||||
wifiPopupVisible = false;
|
||||
batteryPopupVisible = false;
|
||||
calendarPopupVisible = false;
|
||||
}
|
||||
|
||||
function toggleSpotlight() {
|
||||
closeAllPopups();
|
||||
spotlightVisible = !spotlightVisible;
|
||||
}
|
||||
|
||||
function toggleControlCenter() {
|
||||
closeAllPopups();
|
||||
controlCenterVisible = !controlCenterVisible;
|
||||
}
|
||||
|
||||
function toggleAboutMac() {
|
||||
closeAllPopups();
|
||||
aboutMacVisible = !aboutMacVisible;
|
||||
}
|
||||
|
||||
// Hyprland raw event processing
|
||||
readonly property Connections hyprConn: Connections {
|
||||
target: Hyprland
|
||||
function onRawEvent(event) {
|
||||
if (!event) return;
|
||||
if (event.startsWith("activewindow>>")) {
|
||||
const data = event.substring(14);
|
||||
const parts = data.split(",");
|
||||
const title = parts[1] ? parts[1].trim() : (parts[0] ? parts[0].trim() : "");
|
||||
const cls = parts[0] ? parts[0].trim() : "Finder";
|
||||
|
||||
root.activeWindowClass = cls && cls.length > 0 ? cls : "Finder";
|
||||
root.activeWindowTitle = title && title.length > 0 ? title : root.activeWindowClass;
|
||||
} else if (event.startsWith("activewindowv2>>")) {
|
||||
// Update running clients
|
||||
clientsProc.running = true;
|
||||
} else if (event.startsWith("openwindow>>") || event.startsWith("closewindow>>")) {
|
||||
clientsProc.running = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Time Updater Timer
|
||||
readonly property Timer clockTimer: Timer {
|
||||
interval: 1000
|
||||
running: true
|
||||
repeat: true
|
||||
triggeredOnStart: true
|
||||
onTriggered: {
|
||||
const now = new Date();
|
||||
root.timeString = Qt.formatDateTime(now, "h:mm AP");
|
||||
root.dateString = Qt.formatDateTime(now, "ddd MMM d");
|
||||
root.fullDateString = Qt.formatDateTime(now, "dddd, MMMM d, yyyy");
|
||||
}
|
||||
}
|
||||
|
||||
// System Stats Poll Timer
|
||||
readonly property Timer statsTimer: Timer {
|
||||
interval: 3000
|
||||
running: true
|
||||
repeat: true
|
||||
triggeredOnStart: true
|
||||
onTriggered: {
|
||||
cpuProc.running = true;
|
||||
memProc.running = true;
|
||||
batProc.running = true;
|
||||
wifiProc.running = true;
|
||||
audioProc.running = true;
|
||||
brightProc.running = true;
|
||||
mprisProc.running = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Background Process Listeners
|
||||
readonly property Process cpuProc: Process {
|
||||
command: ["bash", "-c", "read -r c u n s i io ir st g gn < /proc/stat; t1=$((u+n+s+i+io+ir+st+g+gn)); i1=$((i+io)); sleep 0.15; read -r c u n s i io ir st g gn < /proc/stat; t2=$((u+n+s+i+io+ir+st+g+gn)); i2=$((i+io)); td=$((t2-t1)); id=$((i2-i1)); [ $td -gt 0 ] && echo $(( (td-id)*100/td )) || echo 0"]
|
||||
stdout: StdioCollector {
|
||||
onDataChanged: {
|
||||
const val = parseInt(data.trim());
|
||||
if (!isNaN(val)) root.cpuPercent = Math.max(0, Math.min(100, val));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
readonly property Process memProc: Process {
|
||||
command: ["bash", "-c", "t=$(grep MemTotal /proc/meminfo | awk '{print $2}'); a=$(grep MemAvailable /proc/meminfo | awk '{print $2}'); if [ -n \"$t\" ] && [ -n \"$a\" ] && [ \"$t\" -gt 0 ]; then u=$((t-a)); p=$((u*100/t)); ug=$(awk \"BEGIN {printf \\\"%.1f\\\", $u/1048576}\"); tg=$(awk \"BEGIN {printf \\\"%.1f\\\", $t/1048576}\"); echo \"$p $ug $tg\"; fi"]
|
||||
stdout: StdioCollector {
|
||||
onDataChanged: {
|
||||
const parts = data.trim().split(" ");
|
||||
if (parts.length >= 3) {
|
||||
root.memPercent = parseInt(parts[0]) || 0;
|
||||
root.memUsedGb = parts[1];
|
||||
root.memTotalGb = parts[2];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
readonly property Process batProc: Process {
|
||||
command: ["bash", "-c", "b=$(find /sys/class/power_supply/ -name 'BAT*' 2>/dev/null | head -n 1); if [ -n \"$b\" ]; then cap=$(cat $b/capacity 2>/dev/null || echo 100); st=$(cat $b/status 2>/dev/null || echo Full); echo \"1 $cap $st\"; else echo \"0 100 AC\"; fi"]
|
||||
stdout: StdioCollector {
|
||||
onDataChanged: {
|
||||
const parts = data.trim().split(" ");
|
||||
if (parts.length >= 3) {
|
||||
root.batteryPresent = parts[0] === "1";
|
||||
root.batteryPercent = parseInt(parts[1]) || 100;
|
||||
root.batteryStatus = parts[2];
|
||||
root.batteryCharging = parts[2] === "Charging" || parts[2] === "Full";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
readonly property Process wifiProc: Process {
|
||||
command: ["bash", "-c", "ssid=\"\"; if command -v nmcli &>/dev/null; then ssid=$(nmcli -t -f active,ssid dev wifi 2>/dev/null | grep '^yes:' | cut -d':' -f2); fi; [ -z \"$ssid\" ] && [ -f /proc/net/wireless ] && ssid=$(iwgetid -r 2>/dev/null); ip=$(ip -4 route get 1.1.1.1 2>/dev/null | awk '{print $7; exit}'); echo \"${ssid:-Ethernet} ${ip:-Offline}\""]
|
||||
stdout: StdioCollector {
|
||||
onDataChanged: {
|
||||
const str = data.trim();
|
||||
const parts = str.split(" ");
|
||||
if (parts.length >= 2) {
|
||||
root.wifiSsid = parts[0];
|
||||
root.ipAddress = parts[1];
|
||||
root.wifiConnected = parts[1] !== "Offline";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
readonly property Process audioProc: Process {
|
||||
command: ["bash", "-c", "if command -v pactl &>/dev/null; then v=$(pactl get-sink-volume @DEFAULT_SINK@ 2>/dev/null | grep -Po '[0-9]+(?=%)' | head -n 1); m=$(pactl get-sink-mute @DEFAULT_SINK@ 2>/dev/null | grep -q yes && echo 1 || echo 0); echo \"${v:-50} $m\"; fi"]
|
||||
stdout: StdioCollector {
|
||||
onDataChanged: {
|
||||
const parts = data.trim().split(" ");
|
||||
if (parts.length >= 2) {
|
||||
root.volume = parseInt(parts[0]) || 50;
|
||||
root.volumeMuted = parts[1] === "1";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
readonly property Process brightProc: Process {
|
||||
command: ["bash", "-c", "if command -v brightnessctl &>/dev/null; then b=$(brightnessctl -m 2>/dev/null | cut -d, -f4 | tr -d '%'); echo \"${b:-100}\"; else echo 100; fi"]
|
||||
stdout: StdioCollector {
|
||||
onDataChanged: {
|
||||
const val = parseInt(data.trim());
|
||||
if (!isNaN(val)) root.brightness = val;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
readonly property Process mprisProc: Process {
|
||||
command: ["bash", "-c", "if command -v playerctl &>/dev/null; then t=$(playerctl metadata title 2>/dev/null); a=$(playerctl metadata artist 2>/dev/null); s=$(playerctl status 2>/dev/null); echo \"$s|$t|$a\"; fi"]
|
||||
stdout: StdioCollector {
|
||||
onDataChanged: {
|
||||
const parts = data.trim().split("|");
|
||||
if (parts.length >= 3) {
|
||||
root.mediaPlaying = parts[0] === "Playing";
|
||||
root.mediaTitle = parts[1] || "";
|
||||
root.mediaArtist = parts[2] || "";
|
||||
} else {
|
||||
root.mediaPlaying = false;
|
||||
root.mediaTitle = "";
|
||||
root.mediaArtist = "";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
readonly property Process clientsProc: Process {
|
||||
command: ["bash", "-c", "if command -v hyprctl &>/dev/null; then hyprctl -j clients 2>/dev/null; else echo '[]'; fi"]
|
||||
stdout: StdioCollector {
|
||||
onDataChanged: {
|
||||
try {
|
||||
const parsed = JSON.parse(data.trim());
|
||||
if (Array.isArray(parsed)) {
|
||||
root.runningApps = parsed;
|
||||
}
|
||||
} catch(e) {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Helper functions to control system
|
||||
function setVolume(val) {
|
||||
root.volume = Math.max(0, Math.min(100, val));
|
||||
Process.exec(["pactl", "set-sink-volume", "@DEFAULT_SINK@", `${root.volume}%`]);
|
||||
}
|
||||
|
||||
function toggleVolumeMute() {
|
||||
Process.exec(["pactl", "set-sink-mute", "@DEFAULT_SINK@", "toggle"]);
|
||||
root.volumeMuted = !root.volumeMuted;
|
||||
}
|
||||
|
||||
function setBrightness(val) {
|
||||
root.brightness = Math.max(1, Math.min(100, val));
|
||||
Process.exec(["brightnessctl", "set", `${root.brightness}%`]);
|
||||
}
|
||||
|
||||
function mediaPlayPause() {
|
||||
Process.exec(["playerctl", "play-pause"]);
|
||||
mprisProc.running = true;
|
||||
}
|
||||
|
||||
function mediaNext() {
|
||||
Process.exec(["playerctl", "next"]);
|
||||
mprisProc.running = true;
|
||||
}
|
||||
|
||||
function mediaPrevious() {
|
||||
Process.exec(["playerctl", "previous"]);
|
||||
mprisProc.running = true;
|
||||
}
|
||||
|
||||
function launchApp(execCmd) {
|
||||
if (!execCmd) return;
|
||||
Process.exec(["bash", "-c", `${execCmd} &`]);
|
||||
closeAllPopups();
|
||||
spotlightVisible = false;
|
||||
}
|
||||
}
|
||||
3
home/hypr/quickshell/assets/apple.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 170 170" width="100%" height="100%">
|
||||
<path fill="#ffffff" d="M150.37 130.25c-2.45 5.66-5.35 10.87-8.71 15.66-4.58 6.53-8.33 11.05-11.22 13.56-4.48 4.12-9.28 6.23-14.42 6.35-3.69 0-8.14-1.05-13.32-3.18-5.19-2.12-9.97-3.17-14.34-3.17-4.58 0-9.49 1.05-14.75 3.17-5.26 2.13-9.5 3.24-12.74 3.35-4.35.13-9.16-1.9-14.42-6.08-3.7-3.04-7.69-7.75-11.96-14.12-6.53-9.78-11.75-21.2-15.66-34.25-3.9-13.06-5.86-25.2-5.86-36.42 0-14.35 3.69-26.4 11.08-36.17 7.39-9.78 16.74-14.77 28.05-14.99 4.35 0 9.49 1.25 15.42 3.75 5.94 2.5 9.78 3.75 11.53 3.75 1.52 0 5.58-1.29 12.18-3.87 6.6-2.58 11.89-3.75 15.88-3.52 11.52.44 20.98 4.7 28.38 12.79-10.43 6.31-15.54 15.11-15.33 26.41.22 8.91 3.69 16.52 10.42 22.82 6.74 6.31 14.78 9.9 24.13 10.77-2.17 6.74-4.89 13.7-8.15 20.87zM119.22 31.02c0-7.18 2.61-13.8 7.83-19.89 5.22-6.09 11.63-9.9 19.24-11.43.22 1.3.33 2.5.33 3.59 0 7.18-2.72 13.92-8.15 20.22-5.43 6.31-12.06 10.01-19.25 11.1-.33-1.09-.5-2.29-.5-3.59z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1003 B |
20
home/hypr/quickshell/assets/appstore.svg
Normal file
@@ -0,0 +1,20 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 128 128" width="100%" height="100%">
|
||||
<defs>
|
||||
<linearGradient id="appStoreBg" x1="0%" y1="0%" x2="0%" y2="100%">
|
||||
<stop offset="0%" stop-color="#2DA9FF"/>
|
||||
<stop offset="100%" stop-color="#0062E3"/>
|
||||
</linearGradient>
|
||||
<filter id="shadowAppStore" x="-10%" y="-10%" width="120%" height="120%">
|
||||
<feDropShadow dx="0" dy="4" stdDeviation="4" flood-color="#000000" flood-opacity="0.35"/>
|
||||
</filter>
|
||||
</defs>
|
||||
<rect x="10" y="10" width="108" height="108" rx="24" fill="url(#appStoreBg)" filter="url(#shadowAppStore)" stroke="#4BB4FF" stroke-width="1.5"/>
|
||||
|
||||
<!-- Apple App Store stylized 'A' using bars -->
|
||||
<!-- Left Leg (Pencil/Ruler) -->
|
||||
<line x1="38" y1="94" x2="64" y2="34" stroke="#FFFFFF" stroke-width="9" stroke-linecap="round"/>
|
||||
<!-- Right Leg (Brush) -->
|
||||
<line x1="90" y1="94" x2="64" y2="34" stroke="#FFFFFF" stroke-width="9" stroke-linecap="round"/>
|
||||
<!-- Crossbar -->
|
||||
<line x1="30" y1="74" x2="98" y2="74" stroke="#FFFFFF" stroke-width="9" stroke-linecap="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.0 KiB |
8
home/hypr/quickshell/assets/battery.svg
Normal file
@@ -0,0 +1,8 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 28 16" width="100%" height="100%">
|
||||
<!-- Outer shell -->
|
||||
<rect x="1" y="1" width="22" height="14" rx="3.5" fill="none" stroke="#FFFFFF" stroke-width="1.8"/>
|
||||
<!-- Anode terminal -->
|
||||
<path d="M25 5.5 C25.8 5.5 26.5 6.2 26.5 7 V9 C26.5 9.8 25.8 10.5 25 10.5 Z" fill="#FFFFFF"/>
|
||||
<!-- Fill bar inside -->
|
||||
<rect x="3.5" y="3.5" width="15" height="9" rx="2" fill="#FFFFFF"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 439 B |
3
home/hypr/quickshell/assets/bluetooth.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="100%" height="100%">
|
||||
<path fill="none" stroke="#FFFFFF" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" d="M7 7l10 10-5 5V2l5 5L7 17"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 228 B |
4
home/hypr/quickshell/assets/brightness.svg
Normal file
@@ -0,0 +1,4 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="100%" height="100%">
|
||||
<circle cx="12" cy="12" r="5" fill="#FFFFFF"/>
|
||||
<path stroke="#FFFFFF" stroke-width="2" stroke-linecap="round" d="M12 1v2 M12 21v2 M4.22 4.22l1.42 1.42 M18.36 18.36l1.42 1.42 M1 12h2 M21 12h2 M4.22 19.78l1.42-1.42 M18.36 5.64l1.42-1.42"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 337 B |
26
home/hypr/quickshell/assets/browser.svg
Normal file
@@ -0,0 +1,26 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 128 128" width="100%" height="100%">
|
||||
<defs>
|
||||
<linearGradient id="safBg" x1="0%" y1="0%" x2="0%" y2="100%">
|
||||
<stop offset="0%" stop-color="#EBF4FB"/>
|
||||
<stop offset="100%" stop-color="#C2D8EB"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="safCompass" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<stop offset="0%" stop-color="#00C7FF"/>
|
||||
<stop offset="100%" stop-color="#0062E3"/>
|
||||
</linearGradient>
|
||||
<filter id="shadowSaf" x="-10%" y="-10%" width="120%" height="120%">
|
||||
<feDropShadow dx="0" dy="4" stdDeviation="4" flood-color="#000000" flood-opacity="0.35"/>
|
||||
</filter>
|
||||
</defs>
|
||||
<rect x="10" y="10" width="108" height="108" rx="24" fill="url(#safBg)" filter="url(#shadowSaf)" stroke="#B0C4DE" stroke-width="1.5"/>
|
||||
<circle cx="64" cy="64" r="44" fill="url(#safCompass)" stroke="#FFFFFF" stroke-width="2"/>
|
||||
|
||||
<!-- Dial ticks -->
|
||||
<circle cx="64" cy="64" r="38" fill="none" stroke="#FFFFFF" stroke-width="1" stroke-dasharray="2 6" opacity="0.6"/>
|
||||
|
||||
<!-- Compass Needle Red/North -->
|
||||
<polygon points="64,28 72,64 64,61 56,64" fill="#FF3B30"/>
|
||||
<!-- Compass Needle White/South -->
|
||||
<polygon points="64,100 72,64 64,67 56,64" fill="#FFFFFF"/>
|
||||
<circle cx="64" cy="64" r="4" fill="#FFFFFF" stroke="#D0D0D0" stroke-width="1"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.3 KiB |
40
home/hypr/quickshell/assets/catalina-badge.svg
Normal file
@@ -0,0 +1,40 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 160 160" width="100%" height="100%">
|
||||
<defs>
|
||||
<clipPath id="circleClip">
|
||||
<circle cx="80" cy="80" r="72"/>
|
||||
</clipPath>
|
||||
<linearGradient id="skyGrad" x1="0%" y1="0%" x2="0%" y2="100%">
|
||||
<stop offset="0%" stop-color="#1A385C"/>
|
||||
<stop offset="50%" stop-color="#C27A59"/>
|
||||
<stop offset="100%" stop-color="#FFD6A5"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="seaGrad" x1="0%" y1="0%" x2="0%" y2="100%">
|
||||
<stop offset="0%" stop-color="#1E4566"/>
|
||||
<stop offset="100%" stop-color="#0E2338"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="islandGrad" x1="0%" y1="0%" x2="0%" y2="100%">
|
||||
<stop offset="0%" stop-color="#544943"/>
|
||||
<stop offset="100%" stop-color="#241E1C"/>
|
||||
</linearGradient>
|
||||
<filter id="badgeShadow">
|
||||
<feDropShadow dx="0" dy="6" stdDeviation="6" flood-color="#000000" flood-opacity="0.4"/>
|
||||
</filter>
|
||||
</defs>
|
||||
|
||||
<circle cx="80" cy="80" r="74" fill="none" stroke="#FFFFFF" stroke-width="3" filter="url(#badgeShadow)"/>
|
||||
|
||||
<g clip-path="url(#circleClip)">
|
||||
<!-- Sky -->
|
||||
<rect x="0" y="0" width="160" height="160" fill="url(#skyGrad)"/>
|
||||
<!-- Sun glow -->
|
||||
<circle cx="95" cy="80" r="28" fill="#FFF2DE" opacity="0.6"/>
|
||||
<!-- Distant Mountains -->
|
||||
<polygon points="10,105 50,75 90,105" fill="#755E57" opacity="0.7"/>
|
||||
<polygon points="70,105 115,70 155,105" fill="#5E4843" opacity="0.8"/>
|
||||
<!-- Santa Catalina Island Cliffs -->
|
||||
<path d="M0 110 Q40 85 85 102 Q125 75 160 115 L160 160 L0 160 Z" fill="url(#islandGrad)"/>
|
||||
<!-- Ocean / Waves -->
|
||||
<path d="M0 120 C30 115 60 125 100 118 C130 112 150 122 160 120 L160 160 L0 160 Z" fill="url(#seaGrad)"/>
|
||||
<path d="M0 135 C40 130 80 140 120 132 C140 128 155 134 160 135 L160 160 L0 160 Z" fill="#132B45" opacity="0.6"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.8 KiB |
23
home/hypr/quickshell/assets/code.svg
Normal file
@@ -0,0 +1,23 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 128 128" width="100%" height="100%">
|
||||
<defs>
|
||||
<linearGradient id="codeBg" x1="0%" y1="0%" x2="0%" y2="100%">
|
||||
<stop offset="0%" stop-color="#1E293B"/>
|
||||
<stop offset="100%" stop-color="#0F172A"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="codeBrackets" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<stop offset="0%" stop-color="#38BDF8"/>
|
||||
<stop offset="100%" stop-color="#818CF8"/>
|
||||
</linearGradient>
|
||||
<filter id="shadowCode" x="-10%" y="-10%" width="120%" height="120%">
|
||||
<feDropShadow dx="0" dy="4" stdDeviation="4" flood-color="#000000" flood-opacity="0.35"/>
|
||||
</filter>
|
||||
</defs>
|
||||
<rect x="10" y="10" width="108" height="108" rx="24" fill="url(#codeBg)" filter="url(#shadowCode)" stroke="#334155" stroke-width="1.5"/>
|
||||
|
||||
<!-- Left Bracket < -->
|
||||
<path d="M46 42 L26 64 L46 86" fill="none" stroke="url(#codeBrackets)" stroke-width="8" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<!-- Right Bracket > -->
|
||||
<path d="M82 42 L102 64 L82 86" fill="none" stroke="url(#codeBrackets)" stroke-width="8" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<!-- Slash / -->
|
||||
<line x1="70" y1="36" x2="58" y2="92" stroke="#F43F5E" stroke-width="7" stroke-linecap="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.2 KiB |
8
home/hypr/quickshell/assets/controlcenter.svg
Normal file
@@ -0,0 +1,8 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="100%" height="100%">
|
||||
<!-- Top switch -->
|
||||
<rect x="3" y="5" width="18" height="6" rx="3" fill="none" stroke="#FFFFFF" stroke-width="1.8"/>
|
||||
<circle cx="16" cy="8" r="2.2" fill="#FFFFFF"/>
|
||||
<!-- Bottom switch -->
|
||||
<rect x="3" y="13" width="18" height="6" rx="3" fill="none" stroke="#FFFFFF" stroke-width="1.8"/>
|
||||
<circle cx="8" cy="16" r="2.2" fill="#FFFFFF"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 441 B |
37
home/hypr/quickshell/assets/finder.svg
Normal file
@@ -0,0 +1,37 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 128 128" width="100%" height="100%">
|
||||
<defs>
|
||||
<linearGradient id="finderBg" x1="0%" y1="0%" x2="0%" y2="100%">
|
||||
<stop offset="0%" stop-color="#E8E8E8"/>
|
||||
<stop offset="100%" stop-color="#B0B0B0"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="faceBlue" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<stop offset="0%" stop-color="#47A3F3"/>
|
||||
<stop offset="100%" stop-color="#1D72DB"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="faceLightBlue" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<stop offset="0%" stop-color="#9AD7F8"/>
|
||||
<stop offset="100%" stop-color="#56AEF7"/>
|
||||
</linearGradient>
|
||||
<filter id="shadow" x="-10%" y="-10%" width="120%" height="120%">
|
||||
<feDropShadow dx="0" dy="4" stdDeviation="4" flood-color="#000000" flood-opacity="0.35"/>
|
||||
</filter>
|
||||
</defs>
|
||||
|
||||
<rect x="10" y="10" width="108" height="108" rx="24" fill="url(#finderBg)" filter="url(#shadow)" stroke="#A0A0A0" stroke-width="1.5"/>
|
||||
|
||||
<!-- Right half of face (Darker Blue) -->
|
||||
<path d="M64 12 H94 C107.25 12 116 20.75 116 34 V94 C116 107.25 107.25 116 94 116 H64 Z" fill="url(#faceBlue)"/>
|
||||
|
||||
<!-- Left half of face (Lighter Blue) -->
|
||||
<path d="M34 12 H64 V116 H34 C20.75 116 12 107.25 12 94 V34 C12 20.75 20.75 12 34 12 Z" fill="url(#faceLightBlue)"/>
|
||||
|
||||
<!-- Eyes -->
|
||||
<ellipse cx="40" cy="52" rx="5" ry="9" fill="#1C2D42"/>
|
||||
<ellipse cx="88" cy="52" rx="5" ry="9" fill="#1C2D42"/>
|
||||
|
||||
<!-- Nose (macOS Finder split curve) -->
|
||||
<path d="M64 34 C64 34 65 62 61 68 C58 72 52 74 48 75" fill="none" stroke="#1C2D42" stroke-width="4.5" stroke-linecap="round"/>
|
||||
|
||||
<!-- Smile -->
|
||||
<path d="M30 80 C36 102 92 102 98 80" fill="none" stroke="#1C2D42" stroke-width="5" stroke-linecap="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.7 KiB |
25
home/hypr/quickshell/assets/folder-downloads.svg
Normal file
@@ -0,0 +1,25 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 128 128" width="100%" height="100%">
|
||||
<defs>
|
||||
<linearGradient id="folderBack" x1="0%" y1="0%" x2="0%" y2="100%">
|
||||
<stop offset="0%" stop-color="#4CA1F5"/>
|
||||
<stop offset="100%" stop-color="#1B6DC7"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="folderFront" x1="0%" y1="0%" x2="0%" y2="100%">
|
||||
<stop offset="0%" stop-color="#80C8FC"/>
|
||||
<stop offset="100%" stop-color="#3894EE"/>
|
||||
</linearGradient>
|
||||
<filter id="shadowFolder" x="-10%" y="-10%" width="120%" height="120%">
|
||||
<feDropShadow dx="0" dy="4" stdDeviation="4" flood-color="#000000" flood-opacity="0.35"/>
|
||||
</filter>
|
||||
</defs>
|
||||
|
||||
<!-- Folder Back Tab -->
|
||||
<path d="M16 28 C16 24 20 20 24 20 H50 L60 30 H104 C108 30 112 34 112 38 V96 C112 100 108 104 104 104 H24 C20 104 16 100 16 96 Z" fill="url(#folderBack)" filter="url(#shadowFolder)"/>
|
||||
|
||||
<!-- Folder Front Pocket -->
|
||||
<path d="M12 44 C12 40 16 36 20 36 H108 C112 36 116 40 116 44 V98 C116 104 110 108 104 108 H24 C18 108 12 104 12 98 Z" fill="url(#folderFront)"/>
|
||||
|
||||
<!-- Download Downward Arrow Badge -->
|
||||
<circle cx="64" cy="74" r="18" fill="#FFFFFF" opacity="0.9"/>
|
||||
<path d="M64 62 V82 M54 74 L64 84 L74 74" fill="none" stroke="#1B6DC7" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.3 KiB |
35
home/hypr/quickshell/assets/launchpad.svg
Normal file
@@ -0,0 +1,35 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 128 128" width="100%" height="100%">
|
||||
<defs>
|
||||
<linearGradient id="lpBg" x1="0%" y1="0%" x2="0%" y2="100%">
|
||||
<stop offset="0%" stop-color="#485563"/>
|
||||
<stop offset="100%" stop-color="#29323c"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="rocketGrad" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<stop offset="0%" stop-color="#FFFFFF"/>
|
||||
<stop offset="100%" stop-color="#D0D4DC"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="fireGrad" x1="0%" y1="0%" x2="0%" y2="100%">
|
||||
<stop offset="0%" stop-color="#FFCC00"/>
|
||||
<stop offset="100%" stop-color="#FF3B30"/>
|
||||
</linearGradient>
|
||||
<filter id="shadowLp" x="-10%" y="-10%" width="120%" height="120%">
|
||||
<feDropShadow dx="0" dy="4" stdDeviation="4" flood-color="#000000" flood-opacity="0.35"/>
|
||||
</filter>
|
||||
</defs>
|
||||
<rect x="10" y="10" width="108" height="108" rx="24" fill="url(#lpBg)" filter="url(#shadowLp)" stroke="#5A6675" stroke-width="1.5"/>
|
||||
|
||||
<!-- Rocket Body -->
|
||||
<path d="M88 28 C74 34 52 50 44 68 C40 76 40 84 42 88 C46 90 54 90 62 86 C80 78 96 56 102 42 C104 36 102 30 96 28 C93 27 90 27 88 28 Z" fill="url(#rocketGrad)"/>
|
||||
|
||||
<!-- Rocket Window -->
|
||||
<circle cx="74" cy="54" r="7" fill="#0A84FF" stroke="#FFFFFF" stroke-width="2"/>
|
||||
|
||||
<!-- Rocket Wing Left -->
|
||||
<path d="M48 76 C40 76 30 84 26 92 C34 94 44 90 48 84 Z" fill="#E02438"/>
|
||||
|
||||
<!-- Rocket Wing Right -->
|
||||
<path d="M76 48 C76 40 84 30 92 26 C94 34 90 44 84 48 Z" fill="#E02438"/>
|
||||
|
||||
<!-- Fire Exhaust -->
|
||||
<path d="M42 88 C36 94 32 104 34 108 C38 106 48 102 54 96 Z" fill="url(#fireGrad)"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.6 KiB |
21
home/hypr/quickshell/assets/music.svg
Normal file
@@ -0,0 +1,21 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 128 128" width="100%" height="100%">
|
||||
<defs>
|
||||
<linearGradient id="musicBg" x1="0%" y1="0%" x2="0%" y2="100%">
|
||||
<stop offset="0%" stop-color="#FF385C"/>
|
||||
<stop offset="100%" stop-color="#E00034"/>
|
||||
</linearGradient>
|
||||
<filter id="shadowMusic" x="-10%" y="-10%" width="120%" height="120%">
|
||||
<feDropShadow dx="0" dy="4" stdDeviation="4" flood-color="#000000" flood-opacity="0.35"/>
|
||||
</filter>
|
||||
</defs>
|
||||
<rect x="10" y="10" width="108" height="108" rx="24" fill="url(#musicBg)" filter="url(#shadowMusic)" stroke="#FF6B8B" stroke-width="1.5"/>
|
||||
|
||||
<!-- Musical Eighth Notes -->
|
||||
<!-- Stems and Beam -->
|
||||
<path d="M48 76 V38 C48 34 50 32 54 32 H82 C86 32 88 34 88 38 V70" fill="none" stroke="#FFFFFF" stroke-width="6" stroke-linecap="round"/>
|
||||
<!-- Beams -->
|
||||
<polygon points="48,32 88,32 88,44 48,44" fill="#FFFFFF"/>
|
||||
<!-- Note Heads -->
|
||||
<ellipse cx="40" cy="80" rx="12" ry="8" transform="rotate(-20 40 80)" fill="#FFFFFF"/>
|
||||
<ellipse cx="80" cy="74" rx="12" ry="8" transform="rotate(-20 80 74)" fill="#FFFFFF"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
44
home/hypr/quickshell/assets/preferences.svg
Normal file
@@ -0,0 +1,44 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 128 128" width="100%" height="100%">
|
||||
<defs>
|
||||
<linearGradient id="prefBg" x1="0%" y1="0%" x2="0%" y2="100%">
|
||||
<stop offset="0%" stop-color="#D8DBE2"/>
|
||||
<stop offset="100%" stop-color="#8F96A3"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="gearBig" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<stop offset="0%" stop-color="#7C8494"/>
|
||||
<stop offset="100%" stop-color="#4A5260"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="gearSmall" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<stop offset="0%" stop-color="#9CA3AF"/>
|
||||
<stop offset="100%" stop-color="#5B6270"/>
|
||||
</linearGradient>
|
||||
<filter id="shadowPref" x="-10%" y="-10%" width="120%" height="120%">
|
||||
<feDropShadow dx="0" dy="4" stdDeviation="4" flood-color="#000000" flood-opacity="0.35"/>
|
||||
</filter>
|
||||
</defs>
|
||||
<rect x="10" y="10" width="108" height="108" rx="24" fill="url(#prefBg)" filter="url(#shadowPref)" stroke="#B0B6C2" stroke-width="1.5"/>
|
||||
|
||||
<!-- Big Gear -->
|
||||
<g transform="translate(50, 50)">
|
||||
<circle cx="0" cy="0" r="28" fill="url(#gearBig)"/>
|
||||
<!-- Teeth -->
|
||||
<path d="M-6 -34 H6 V-26 H-6 Z M-6 26 H6 V34 H-6 Z M-34 -6 V6 H-26 V-6 Z M26 -6 V6 H34 V-6 Z" fill="url(#gearBig)"/>
|
||||
<path d="M-6 -34 H6 V-26 H-6 Z" transform="rotate(45)" fill="url(#gearBig)"/>
|
||||
<path d="M-6 -34 H6 V-26 H-6 Z" transform="rotate(135)" fill="url(#gearBig)"/>
|
||||
<path d="M-6 -34 H6 V-26 H-6 Z" transform="rotate(225)" fill="url(#gearBig)"/>
|
||||
<path d="M-6 -34 H6 V-26 H-6 Z" transform="rotate(315)" fill="url(#gearBig)"/>
|
||||
<circle cx="0" cy="0" r="12" fill="url(#prefBg)" stroke="#3A404D" stroke-width="1.5"/>
|
||||
</g>
|
||||
|
||||
<!-- Small Gear -->
|
||||
<g transform="translate(86, 86)">
|
||||
<circle cx="0" cy="0" r="18" fill="url(#gearSmall)"/>
|
||||
<!-- Teeth -->
|
||||
<path d="M-4 -22 H4 V-16 H-4 Z M-4 16 H4 V22 H-4 Z M-22 -4 V4 H-16 V-4 Z M16 -4 V4 H22 V-4 Z" fill="url(#gearSmall)"/>
|
||||
<path d="M-4 -22 H4 V-16 H-4 Z" transform="rotate(45)" fill="url(#gearSmall)"/>
|
||||
<path d="M-4 -22 H4 V-16 H-4 Z" transform="rotate(135)" fill="url(#gearSmall)"/>
|
||||
<path d="M-4 -22 H4 V-16 H-4 Z" transform="rotate(225)" fill="url(#gearSmall)"/>
|
||||
<path d="M-4 -22 H4 V-16 H-4 Z" transform="rotate(315)" fill="url(#gearSmall)"/>
|
||||
<circle cx="0" cy="0" r="8" fill="url(#prefBg)" stroke="#3A404D" stroke-width="1.5"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.3 KiB |
3
home/hypr/quickshell/assets/spotlight.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="100%" height="100%">
|
||||
<path fill="none" stroke="#FFFFFF" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" d="M10.5 18a7.5 7.5 0 1 1 0-15 7.5 7.5 0 0 1 0 15z M16 16l5 5"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 263 B |
21
home/hypr/quickshell/assets/terminal.svg
Normal file
@@ -0,0 +1,21 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 128 128" width="100%" height="100%">
|
||||
<defs>
|
||||
<linearGradient id="termBg" x1="0%" y1="0%" x2="0%" y2="100%">
|
||||
<stop offset="0%" stop-color="#2D2D30"/>
|
||||
<stop offset="100%" stop-color="#141416"/>
|
||||
</linearGradient>
|
||||
<filter id="shadowTerm" x="-10%" y="-10%" width="120%" height="120%">
|
||||
<feDropShadow dx="0" dy="4" stdDeviation="4" flood-color="#000000" flood-opacity="0.35"/>
|
||||
</filter>
|
||||
</defs>
|
||||
<rect x="10" y="10" width="108" height="108" rx="24" fill="url(#termBg)" filter="url(#shadowTerm)" stroke="#4A4A50" stroke-width="1.5"/>
|
||||
<rect x="18" y="24" width="92" height="80" rx="10" fill="#0C0C0E" stroke="#2A2A2E" stroke-width="1"/>
|
||||
<!-- macOS Terminal header dots -->
|
||||
<circle cx="28" cy="32" r="3" fill="#FF5F56"/>
|
||||
<circle cx="36" cy="32" r="3" fill="#FFBD2E"/>
|
||||
<circle cx="44" cy="32" r="3" fill="#27C93F"/>
|
||||
|
||||
<!-- Terminal Prompt >_ -->
|
||||
<path d="M28 54 L44 64 L28 74" fill="none" stroke="#30D158" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<line x1="50" y1="74" x2="68" y2="74" stroke="#FFFFFF" stroke-width="4" stroke-linecap="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
23
home/hypr/quickshell/assets/trash-empty.svg
Normal file
@@ -0,0 +1,23 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 128 128" width="100%" height="100%">
|
||||
<defs>
|
||||
<linearGradient id="trashCan" x1="0%" y1="0%" x2="0%" y2="100%">
|
||||
<stop offset="0%" stop-color="#E2E8F0"/>
|
||||
<stop offset="100%" stop-color="#94A3B8"/>
|
||||
</linearGradient>
|
||||
<filter id="shadowTrash" x="-10%" y="-10%" width="120%" height="120%">
|
||||
<feDropShadow dx="0" dy="4" stdDeviation="4" flood-color="#000000" flood-opacity="0.35"/>
|
||||
</filter>
|
||||
</defs>
|
||||
|
||||
<!-- Lid -->
|
||||
<ellipse cx="64" cy="30" rx="36" ry="8" fill="#CBD5E1" stroke="#94A3B8" stroke-width="2"/>
|
||||
<rect x="56" y="18" width="16" height="8" rx="4" fill="#94A3B8"/>
|
||||
|
||||
<!-- Can Body -->
|
||||
<path d="M30 30 L40 106 C41 112 48 116 64 116 C80 116 87 112 88 106 L98 30 Z" fill="url(#trashCan)" stroke="#64748B" stroke-width="2"/>
|
||||
|
||||
<!-- Ribs / Vertical Mesh Lines -->
|
||||
<line x1="48" y1="36" x2="52" y2="108" stroke="#FFFFFF" stroke-width="2" stroke-linecap="round" opacity="0.6"/>
|
||||
<line x1="64" y1="36" x2="64" y2="110" stroke="#FFFFFF" stroke-width="2" stroke-linecap="round" opacity="0.6"/>
|
||||
<line x1="80" y1="36" x2="76" y2="108" stroke="#FFFFFF" stroke-width="2" stroke-linecap="round" opacity="0.6"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.2 KiB |
24
home/hypr/quickshell/assets/trash-full.svg
Normal file
@@ -0,0 +1,24 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 128 128" width="100%" height="100%">
|
||||
<defs>
|
||||
<linearGradient id="trashCanFull" x1="0%" y1="0%" x2="0%" y2="100%">
|
||||
<stop offset="0%" stop-color="#E2E8F0"/>
|
||||
<stop offset="100%" stop-color="#94A3B8"/>
|
||||
</linearGradient>
|
||||
<filter id="shadowTrashFull" x="-10%" y="-10%" width="120%" height="120%">
|
||||
<feDropShadow dx="0" dy="4" stdDeviation="4" flood-color="#000000" flood-opacity="0.35"/>
|
||||
</filter>
|
||||
</defs>
|
||||
|
||||
<!-- Crumpled paper overflowing -->
|
||||
<path d="M42 28 C38 18 52 14 60 20 C68 12 82 16 80 26 C88 24 92 34 84 38 C74 42 54 42 42 28 Z" fill="#F8FAFC" stroke="#CBD5E1" stroke-width="1.5"/>
|
||||
<path d="M48 24 L56 16 L64 24 Z" fill="#E2E8F0"/>
|
||||
<path d="M68 20 L76 14 L82 22 Z" fill="#E2E8F0"/>
|
||||
|
||||
<!-- Can Body -->
|
||||
<path d="M30 36 L40 106 C41 112 48 116 64 116 C80 116 87 112 88 106 L98 36 Z" fill="url(#trashCanFull)" stroke="#64748B" stroke-width="2"/>
|
||||
|
||||
<!-- Ribs / Vertical Mesh Lines -->
|
||||
<line x1="48" y1="42" x2="52" y2="108" stroke="#FFFFFF" stroke-width="2" stroke-linecap="round" opacity="0.6"/>
|
||||
<line x1="64" y1="42" x2="64" y2="110" stroke="#FFFFFF" stroke-width="2" stroke-linecap="round" opacity="0.6"/>
|
||||
<line x1="80" y1="42" x2="76" y2="108" stroke="#FFFFFF" stroke-width="2" stroke-linecap="round" opacity="0.6"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.3 KiB |
3
home/hypr/quickshell/assets/volume.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="100%" height="100%">
|
||||
<path fill="#FFFFFF" d="M3 9v6h4l5 5V4L7 9H3zm13.5 3c0-1.77-1.02-3.29-2.5-4.03v8.05c1.48-.73 2.5-2.25 2.5-4.02zM14 3.23v2.06c2.89.86 5 3.54 5 6.71s-2.11 5.85-5 6.71v2.06c4.01-.91 7-4.49 7-8.77s-2.99-7.86-7-8.77z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 312 B |
3
home/hypr/quickshell/assets/wifi.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="100%" height="100%">
|
||||
<path fill="#FFFFFF" d="M12 18.5a1.5 1.5 0 1 0 0 3 1.5 1.5 0 0 0 0-3z M4.93 12.93a10 10 0 0 1 14.14 0 1 1 0 0 0 1.42-1.42 12 12 0 0 0-16.98 0 1 1 0 0 0 1.42 1.42z M8.46 16.46a5 5 0 0 1 7.08 0 1 1 0 0 0 1.41-1.41 7 7 0 0 0-9.9 0 1 1 0 0 0 1.41 1.41z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 349 B |
260
home/hypr/quickshell/components/AboutMac.qml
Normal file
@@ -0,0 +1,260 @@
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import QtQuick.Controls
|
||||
import Quickshell
|
||||
import Quickshell.Wayland
|
||||
import "../theme"
|
||||
import ".."
|
||||
|
||||
PanelWindow {
|
||||
id: aboutWindow
|
||||
visible: GlobalState.aboutMacVisible
|
||||
|
||||
anchors {
|
||||
top: true
|
||||
left: true
|
||||
right: true
|
||||
bottom: true
|
||||
}
|
||||
|
||||
color: Qt.rgba(0, 0, 0, 0.35)
|
||||
WlrLayershell.layer: WlrLayer.Overlay
|
||||
WlrLayershell.namespace: "quickshell-aboutmac"
|
||||
WlrLayershell.keyboardFocus: visible ? WlrKeyboardFocus.Exclusive : WlrKeyboardFocus.None
|
||||
|
||||
// Dismiss when clicking outside modal
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
onClicked: GlobalState.aboutMacVisible = false
|
||||
}
|
||||
|
||||
// Centered Dialog
|
||||
Rectangle {
|
||||
id: dialogBox
|
||||
anchors.centerIn: parent
|
||||
width: 540
|
||||
height: 330
|
||||
radius: Theme.radiusModal
|
||||
color: Theme.windowBg
|
||||
border.color: Theme.windowBorder
|
||||
border.width: 1
|
||||
|
||||
// Prevent clicking inside from dismissing
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
onClicked: {}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
anchors.fill: parent
|
||||
anchors.margins: 16
|
||||
spacing: 12
|
||||
|
||||
// Top Traffic Lights Bar
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: 8
|
||||
|
||||
// Red Close Dot
|
||||
Rectangle {
|
||||
width: 12
|
||||
height: 12
|
||||
radius: 6
|
||||
color: closeMouse.containsMouse ? "#E0443E" : "#FF5F56"
|
||||
border.color: "#E0443E"
|
||||
border.width: 0.5
|
||||
|
||||
Text {
|
||||
anchors.centerIn: parent
|
||||
visible: closeMouse.containsMouse
|
||||
text: "×"
|
||||
font.pixelSize: 10
|
||||
color: "#4A0002"
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: closeMouse
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: GlobalState.aboutMacVisible = false
|
||||
}
|
||||
}
|
||||
|
||||
// Yellow Minimize Dot
|
||||
Rectangle {
|
||||
width: 12
|
||||
height: 12
|
||||
radius: 6
|
||||
color: "#FFBD2E"
|
||||
border.color: "#DEA123"
|
||||
border.width: 0.5
|
||||
}
|
||||
|
||||
// Green Zoom Dot
|
||||
Rectangle {
|
||||
width: 12
|
||||
height: 12
|
||||
radius: 6
|
||||
color: "#27C93F"
|
||||
border.color: "#1AAB29"
|
||||
border.width: 0.5
|
||||
}
|
||||
|
||||
Item { Layout.fillWidth: true }
|
||||
|
||||
Text {
|
||||
text: "Overview"
|
||||
font.family: Theme.fontFamily
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
font.weight: Font.DemiBold
|
||||
color: Theme.textSecondary
|
||||
}
|
||||
|
||||
Item { Layout.fillWidth: true }
|
||||
}
|
||||
|
||||
// Main Specs Content
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
spacing: 24
|
||||
|
||||
// Left: Catalina Island Badge
|
||||
Image {
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
width: 130
|
||||
height: 130
|
||||
source: "../assets/catalina-badge.svg"
|
||||
sourceSize: Qt.size(130, 130)
|
||||
}
|
||||
|
||||
// Right: System Details
|
||||
ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
spacing: 6
|
||||
|
||||
Text {
|
||||
text: "macOS Catalina"
|
||||
font.family: Theme.fontFamily
|
||||
font.pixelSize: 22
|
||||
font.weight: Font.Bold
|
||||
color: "#FFFFFF"
|
||||
}
|
||||
|
||||
Text {
|
||||
text: "Version 10.15.7 (NixOS Edition)"
|
||||
font.family: Theme.fontFamily
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
color: Theme.textSecondary
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: 1
|
||||
color: Qt.rgba(1.0, 1.0, 1.0, 0.12)
|
||||
}
|
||||
|
||||
// Specs Rows
|
||||
SpecRow { label: "Host"; value: "Hades (AMD + NVIDIA RTX)" }
|
||||
SpecRow { label: "Processor"; value: "AMD High-Performance Multi-Core" }
|
||||
SpecRow { label: "Memory"; value: `${GlobalState.memTotalGb} GB RAM (${GlobalState.memUsedGb} GB in use)` }
|
||||
SpecRow { label: "Graphics"; value: "NVIDIA GeForce RTX (Proprietary Driver)" }
|
||||
SpecRow { label: "Compositor"; value: "Hyprland 0.55+ (Wayland)" }
|
||||
SpecRow { label: "Operating System"; value: "NixOS 26.05 (x86_64-linux)" }
|
||||
}
|
||||
}
|
||||
|
||||
// Bottom Action Buttons
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
Layout.alignment: Qt.AlignRight
|
||||
spacing: 12
|
||||
|
||||
Rectangle {
|
||||
implicitWidth: sysRepText.implicitWidth + 20
|
||||
height: 24
|
||||
radius: Theme.radiusSmall
|
||||
color: repMouse.containsMouse ? Qt.rgba(1,1,1,0.2) : Qt.rgba(1,1,1,0.1)
|
||||
border.color: Qt.rgba(1,1,1,0.15)
|
||||
border.width: 1
|
||||
|
||||
Text {
|
||||
id: sysRepText
|
||||
anchors.centerIn: parent
|
||||
text: "System Report..."
|
||||
font.family: Theme.fontFamily
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
color: Theme.textPrimary
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: repMouse
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: {
|
||||
GlobalState.aboutMacVisible = false;
|
||||
GlobalState.launchApp("foot -e btop");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
implicitWidth: updText.implicitWidth + 20
|
||||
height: 24
|
||||
radius: Theme.radiusSmall
|
||||
color: updMouse.containsMouse ? Qt.rgba(1,1,1,0.2) : Qt.rgba(1,1,1,0.1)
|
||||
border.color: Qt.rgba(1,1,1,0.15)
|
||||
border.width: 1
|
||||
|
||||
Text {
|
||||
id: updText
|
||||
anchors.centerIn: parent
|
||||
text: "Software Update..."
|
||||
font.family: Theme.fontFamily
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
color: Theme.textPrimary
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: updMouse
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: {
|
||||
GlobalState.aboutMacVisible = false;
|
||||
GlobalState.launchApp("foot -e zsh -c 'echo \"Checking NixOS system configuration...\"; sudo nixos-rebuild dry-activate --flake /home/anish/NixOS-Configs#hades; exec zsh'");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
component SpecRow: RowLayout {
|
||||
property string label: ""
|
||||
property string value: ""
|
||||
Layout.fillWidth: true
|
||||
spacing: 10
|
||||
|
||||
Text {
|
||||
text: label
|
||||
font.family: Theme.fontFamily
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
font.weight: Font.DemiBold
|
||||
color: Theme.textSecondary
|
||||
Layout.preferredWidth: 105
|
||||
}
|
||||
|
||||
Text {
|
||||
text: value
|
||||
font.family: Theme.fontFamily
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
color: Theme.textPrimary
|
||||
Layout.fillWidth: true
|
||||
elide: Text.ElideRight
|
||||
}
|
||||
}
|
||||
}
|
||||
147
home/hypr/quickshell/components/AppMenuDropdown.qml
Normal file
@@ -0,0 +1,147 @@
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import QtQuick.Controls
|
||||
import "../theme"
|
||||
import ".."
|
||||
|
||||
Rectangle {
|
||||
id: root
|
||||
property string menuName: ""
|
||||
width: 200
|
||||
implicitHeight: menuCol.implicitHeight + 12
|
||||
radius: Theme.radiusMedium
|
||||
color: Theme.menuDropdownBg
|
||||
border.color: Theme.menuDropdownBorder
|
||||
border.width: 1
|
||||
|
||||
ColumnLayout {
|
||||
id: menuCol
|
||||
anchors.fill: parent
|
||||
anchors.margins: 6
|
||||
spacing: 2
|
||||
|
||||
// FILE MENU
|
||||
Repeater {
|
||||
model: root.menuName === "File" ? [
|
||||
{ text: "New Window", shortcut: "⌘N", cmd: "foot &" },
|
||||
{ text: "New Incognito Window", shortcut: "⇧⌘N", cmd: "brave --incognito &" },
|
||||
{ text: "Close Window", shortcut: "⌘W", cmd: "hyprctl dispatch killactive" },
|
||||
{ text: "---", shortcut: "", cmd: "" },
|
||||
{ text: "Take Screenshot", shortcut: "⇧⌘5", cmd: "grim -g \"$(slurp)\" ~/screenshot.png" }
|
||||
] : []
|
||||
delegate: MenuEntryDelegate {}
|
||||
}
|
||||
|
||||
// EDIT MENU
|
||||
Repeater {
|
||||
model: root.menuName === "Edit" ? [
|
||||
{ text: "Undo", shortcut: "⌘Z", cmd: "" },
|
||||
{ text: "Redo", shortcut: "⇧⌘Z", cmd: "" },
|
||||
{ text: "---", shortcut: "", cmd: "" },
|
||||
{ text: "Cut", shortcut: "⌘X", cmd: "wl-copy" },
|
||||
{ text: "Copy", shortcut: "⌘C", cmd: "wl-copy" },
|
||||
{ text: "Paste", shortcut: "⌘V", cmd: "wl-paste" },
|
||||
{ text: "Select All", shortcut: "⌘A", cmd: "" }
|
||||
] : []
|
||||
delegate: MenuEntryDelegate {}
|
||||
}
|
||||
|
||||
// VIEW MENU
|
||||
Repeater {
|
||||
model: root.menuName === "View" ? [
|
||||
{ text: "Toggle Fullscreen", shortcut: "⌃⌘F", cmd: "hyprctl dispatch fullscreen 0" },
|
||||
{ text: "Toggle Floating", shortcut: "⌘V", cmd: "hyprctl dispatch togglefloating" },
|
||||
{ text: "Toggle Group", shortcut: "⌘G", cmd: "hyprctl dispatch togglegroup" },
|
||||
{ text: "---", shortcut: "", cmd: "" },
|
||||
{ text: "Zoom In", shortcut: "⌘+", cmd: "" },
|
||||
{ text: "Zoom Out", shortcut: "⌘-", cmd: "" }
|
||||
] : []
|
||||
delegate: MenuEntryDelegate {}
|
||||
}
|
||||
|
||||
// WINDOW MENU
|
||||
Repeater {
|
||||
model: root.menuName === "Window" ? [
|
||||
{ text: "Minimize Window", shortcut: "⌘M", cmd: "hyprctl dispatch movetoworkspacesilent special:scratch" },
|
||||
{ text: "Tile Left", shortcut: "⌃⌥←", cmd: "hyprctl dispatch movewindow l" },
|
||||
{ text: "Tile Right", shortcut: "⌃⌥→", cmd: "hyprctl dispatch movewindow r" },
|
||||
{ text: "---", shortcut: "", cmd: "" },
|
||||
{ text: "Show Scratchpad", shortcut: "⌘↩", cmd: "hyprctl dispatch togglespecialworkspace scratch" },
|
||||
{ text: "Show Messaging", shortcut: "⌘M", cmd: "hyprctl dispatch togglespecialworkspace messaging" }
|
||||
] : []
|
||||
delegate: MenuEntryDelegate {}
|
||||
}
|
||||
|
||||
// HELP MENU
|
||||
Repeater {
|
||||
model: root.menuName === "Help" ? [
|
||||
{ text: "Hyprland Documentation", shortcut: "", cmd: "brave https://wiki.hyprland.org &" },
|
||||
{ text: "NixOS Manual", shortcut: "", cmd: "brave https://nixos.org/manual/nixos/stable/ &" },
|
||||
{ text: "---", shortcut: "", cmd: "" },
|
||||
{ text: "About Antigravity", shortcut: "", cmd: "" }
|
||||
] : []
|
||||
delegate: MenuEntryDelegate {}
|
||||
}
|
||||
}
|
||||
|
||||
component MenuEntryDelegate: Item {
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: modelData.text === "---" ? 7 : 22
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
visible: modelData.text === "---"
|
||||
color: "transparent"
|
||||
Rectangle {
|
||||
anchors.centerIn: parent
|
||||
width: parent.width
|
||||
height: 1
|
||||
color: Qt.rgba(1.0, 1.0, 1.0, 0.12)
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
visible: modelData.text !== "---"
|
||||
radius: Theme.radiusSmall
|
||||
color: itemMouse.containsMouse ? Theme.menuHoverBlue : "transparent"
|
||||
|
||||
RowLayout {
|
||||
anchors.fill: parent
|
||||
anchors.leftMargin: 10
|
||||
anchors.rightMargin: 10
|
||||
spacing: 8
|
||||
|
||||
Text {
|
||||
text: modelData.text
|
||||
font.family: Theme.fontFamily
|
||||
font.pixelSize: Theme.fontSizeNormal
|
||||
color: itemMouse.containsMouse ? Theme.menuTextHover : Theme.textPrimary
|
||||
Layout.fillWidth: true
|
||||
elide: Text.ElideRight
|
||||
}
|
||||
|
||||
Text {
|
||||
visible: modelData.shortcut !== ""
|
||||
text: modelData.shortcut
|
||||
font.family: Theme.fontFamily
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
color: itemMouse.containsMouse ? Theme.menuTextHover : Theme.textTertiary
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: itemMouse
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: {
|
||||
GlobalState.activeDropdownMenu = "";
|
||||
if (modelData.cmd) {
|
||||
GlobalState.launchApp(modelData.cmd);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
158
home/hypr/quickshell/components/AppleMenu.qml
Normal file
@@ -0,0 +1,158 @@
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import QtQuick.Controls
|
||||
import "../theme"
|
||||
import ".."
|
||||
|
||||
Rectangle {
|
||||
id: root
|
||||
width: 220
|
||||
implicitHeight: menuColumn.implicitHeight + 12
|
||||
radius: Theme.radiusMedium
|
||||
color: Theme.menuDropdownBg
|
||||
border.color: Theme.menuDropdownBorder
|
||||
border.width: 1
|
||||
|
||||
// Drop shadow
|
||||
layer.enabled: true
|
||||
|
||||
ColumnLayout {
|
||||
id: menuColumn
|
||||
anchors.fill: parent
|
||||
anchors.margins: 6
|
||||
spacing: 2
|
||||
|
||||
// About This Mac
|
||||
MenuItem {
|
||||
text: "About This Mac"
|
||||
onTriggered: {
|
||||
GlobalState.appleMenuVisible = false;
|
||||
GlobalState.toggleAboutMac();
|
||||
}
|
||||
}
|
||||
|
||||
MenuSeparator {}
|
||||
|
||||
MenuItem {
|
||||
text: "System Preferences..."
|
||||
onTriggered: {
|
||||
GlobalState.launchApp("pavucontrol");
|
||||
}
|
||||
}
|
||||
|
||||
MenuItem {
|
||||
text: "App Store..."
|
||||
onTriggered: {
|
||||
GlobalState.launchApp("foot -e zsh -c 'echo NixOS Packages; nix-env -qaP | head -n 50; exec zsh'");
|
||||
}
|
||||
}
|
||||
|
||||
MenuSeparator {}
|
||||
|
||||
MenuItem {
|
||||
text: "Activity Monitor"
|
||||
shortcut: "⌥⌘Esc"
|
||||
onTriggered: {
|
||||
GlobalState.launchApp("foot -e btop");
|
||||
}
|
||||
}
|
||||
|
||||
MenuSeparator {}
|
||||
|
||||
MenuItem {
|
||||
text: "Sleep"
|
||||
onTriggered: {
|
||||
GlobalState.appleMenuVisible = false;
|
||||
GlobalState.launchApp("systemctl suspend");
|
||||
}
|
||||
}
|
||||
|
||||
MenuItem {
|
||||
text: "Restart..."
|
||||
onTriggered: {
|
||||
GlobalState.appleMenuVisible = false;
|
||||
GlobalState.launchApp("systemctl reboot");
|
||||
}
|
||||
}
|
||||
|
||||
MenuItem {
|
||||
text: "Shut Down..."
|
||||
onTriggered: {
|
||||
GlobalState.appleMenuVisible = false;
|
||||
GlobalState.launchApp("systemctl poweroff");
|
||||
}
|
||||
}
|
||||
|
||||
MenuSeparator {}
|
||||
|
||||
MenuItem {
|
||||
text: "Lock Screen"
|
||||
shortcut: "⌃⌘Q"
|
||||
onTriggered: {
|
||||
GlobalState.appleMenuVisible = false;
|
||||
GlobalState.launchApp("hyprlock");
|
||||
}
|
||||
}
|
||||
|
||||
MenuItem {
|
||||
text: "Log Out anish..."
|
||||
shortcut: "⇧⌘Q"
|
||||
onTriggered: {
|
||||
GlobalState.appleMenuVisible = false;
|
||||
GlobalState.launchApp("hyprctl dispatch exit");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
component MenuItem: Rectangle {
|
||||
id: itemRoot
|
||||
property string text: ""
|
||||
property string shortcut: ""
|
||||
signal triggered()
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: 22
|
||||
radius: Theme.radiusSmall
|
||||
color: mouseArea.containsMouse ? Theme.menuHoverBlue : "transparent"
|
||||
|
||||
RowLayout {
|
||||
anchors.fill: parent
|
||||
anchors.leftMargin: 10
|
||||
anchors.rightMargin: 10
|
||||
spacing: 8
|
||||
|
||||
Text {
|
||||
text: itemRoot.text
|
||||
font.family: Theme.fontFamily
|
||||
font.pixelSize: Theme.fontSizeNormal
|
||||
color: mouseArea.containsMouse ? Theme.menuTextHover : Theme.textPrimary
|
||||
Layout.fillWidth: true
|
||||
elide: Text.ElideRight
|
||||
}
|
||||
|
||||
Text {
|
||||
visible: itemRoot.shortcut !== ""
|
||||
text: itemRoot.shortcut
|
||||
font.family: Theme.fontFamily
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
color: mouseArea.containsMouse ? Theme.menuTextHover : Theme.textTertiary
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: mouseArea
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: itemRoot.triggered()
|
||||
}
|
||||
}
|
||||
|
||||
component MenuSeparator: Rectangle {
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: 1
|
||||
Layout.topMargin: 3
|
||||
Layout.bottomMargin: 3
|
||||
color: Qt.rgba(1.0, 1.0, 1.0, 0.12)
|
||||
}
|
||||
}
|
||||
102
home/hypr/quickshell/components/BatteryPopup.qml
Normal file
@@ -0,0 +1,102 @@
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import QtQuick.Controls
|
||||
import "../theme"
|
||||
import ".."
|
||||
|
||||
Rectangle {
|
||||
id: root
|
||||
width: 240
|
||||
implicitHeight: batCol.implicitHeight + 24
|
||||
radius: Theme.radiusMedium
|
||||
color: Theme.menuDropdownBg
|
||||
border.color: Theme.menuDropdownBorder
|
||||
border.width: 1
|
||||
|
||||
ColumnLayout {
|
||||
id: batCol
|
||||
anchors.fill: parent
|
||||
anchors.margins: 12
|
||||
spacing: 8
|
||||
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
Text {
|
||||
text: "Battery"
|
||||
font.family: Theme.fontFamily
|
||||
font.pixelSize: Theme.fontSizeNormal
|
||||
font.weight: Font.DemiBold
|
||||
color: Theme.textPrimary
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
Text {
|
||||
text: `${GlobalState.batteryPercent}%`
|
||||
font.family: Theme.fontFamily
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
font.weight: Font.DemiBold
|
||||
color: GlobalState.batteryPercent <= 20 ? Theme.accentRed : Theme.accentGreen
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: 1
|
||||
color: Qt.rgba(1.0, 1.0, 1.0, 0.12)
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: 3
|
||||
|
||||
Text {
|
||||
text: GlobalState.batteryPresent
|
||||
? (GlobalState.batteryCharging ? "Power Source: Power Adapter" : "Power Source: Battery")
|
||||
: "Power Source: AC Connected"
|
||||
font.family: Theme.fontFamily
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
color: Theme.textPrimary
|
||||
}
|
||||
|
||||
Text {
|
||||
text: GlobalState.batteryPresent
|
||||
? `Status: ${GlobalState.batteryStatus}`
|
||||
: "Desktop Workstation Mode"
|
||||
font.family: Theme.fontFamily
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
color: Theme.textSecondary
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: 1
|
||||
color: Qt.rgba(1.0, 1.0, 1.0, 0.12)
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: 24
|
||||
radius: Theme.radiusSmall
|
||||
color: batPrefMouse.containsMouse ? Qt.rgba(1.0, 1.0, 1.0, 0.15) : Qt.rgba(1.0, 1.0, 1.0, 0.08)
|
||||
|
||||
Text {
|
||||
anchors.centerIn: parent
|
||||
text: "Energy Saver Preferences..."
|
||||
font.family: Theme.fontFamily
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
color: Theme.textPrimary
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: batPrefMouse
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: {
|
||||
GlobalState.batteryPopupVisible = false;
|
||||
GlobalState.launchApp("foot -e btop");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
94
home/hypr/quickshell/components/CalendarPopup.qml
Normal file
@@ -0,0 +1,94 @@
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import QtQuick.Controls
|
||||
import "../theme"
|
||||
import ".."
|
||||
|
||||
Rectangle {
|
||||
id: root
|
||||
width: 250
|
||||
implicitHeight: calCol.implicitHeight + 24
|
||||
radius: Theme.radiusMedium
|
||||
color: Theme.menuDropdownBg
|
||||
border.color: Theme.menuDropdownBorder
|
||||
border.width: 1
|
||||
|
||||
ColumnLayout {
|
||||
id: calCol
|
||||
anchors.fill: parent
|
||||
anchors.margins: 12
|
||||
spacing: 10
|
||||
|
||||
Text {
|
||||
text: GlobalState.fullDateString
|
||||
font.family: Theme.fontFamily
|
||||
font.pixelSize: Theme.fontSizeNormal
|
||||
font.weight: Font.DemiBold
|
||||
color: Theme.textPrimary
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
}
|
||||
|
||||
Text {
|
||||
text: GlobalState.timeString
|
||||
font.family: Theme.fontFamily
|
||||
font.pixelSize: 22
|
||||
font.weight: Font.Light
|
||||
color: Theme.accentBlue
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: 1
|
||||
color: Qt.rgba(1.0, 1.0, 1.0, 0.12)
|
||||
}
|
||||
|
||||
// Days grid header
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
Repeater {
|
||||
model: ["S", "M", "T", "W", "T", "F", "S"]
|
||||
delegate: Text {
|
||||
text: modelData
|
||||
font.family: Theme.fontFamily
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
font.weight: Font.DemiBold
|
||||
color: Theme.textTertiary
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Quick mini-calendar representation
|
||||
GridLayout {
|
||||
id: gridDays
|
||||
columns: 7
|
||||
Layout.fillWidth: true
|
||||
rowSpacing: 4
|
||||
columnSpacing: 4
|
||||
|
||||
readonly property var todayDate: new Date()
|
||||
readonly property int currentDay: todayDate.getDate()
|
||||
|
||||
Repeater {
|
||||
model: 31
|
||||
delegate: Rectangle {
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: 22
|
||||
radius: 11
|
||||
color: (index + 1) === gridDays.currentDay ? Theme.accentBlue : "transparent"
|
||||
|
||||
Text {
|
||||
anchors.centerIn: parent
|
||||
text: `${index + 1}`
|
||||
font.family: Theme.fontFamily
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
font.weight: (index + 1) === gridDays.currentDay ? Font.Bold : Font.Normal
|
||||
color: (index + 1) === gridDays.currentDay ? "#FFFFFF" : Theme.textSecondary
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
638
home/hypr/quickshell/components/ControlCenter.qml
Normal file
@@ -0,0 +1,638 @@
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import QtQuick.Controls
|
||||
import Quickshell
|
||||
import Quickshell.Wayland
|
||||
import "../theme"
|
||||
import ".."
|
||||
|
||||
PanelWindow {
|
||||
id: ccWindow
|
||||
visible: GlobalState.controlCenterVisible
|
||||
|
||||
anchors {
|
||||
top: true
|
||||
right: true
|
||||
bottom: true
|
||||
}
|
||||
|
||||
margins {
|
||||
top: Theme.menuBarHeight
|
||||
}
|
||||
|
||||
implicitWidth: 360
|
||||
color: "transparent"
|
||||
WlrLayershell.layer: WlrLayer.Overlay
|
||||
WlrLayershell.namespace: "quickshell-controlcenter"
|
||||
WlrLayershell.keyboardFocus: WlrKeyboardFocus.None
|
||||
|
||||
property int activeTab: 0 // 0 = Today, 1 = Notifications
|
||||
|
||||
// Sidebar Container
|
||||
Rectangle {
|
||||
id: sidebarPanel
|
||||
anchors.fill: parent
|
||||
color: Theme.windowBg
|
||||
border.color: Theme.windowBorder
|
||||
border.width: 1
|
||||
|
||||
ColumnLayout {
|
||||
anchors.fill: parent
|
||||
anchors.margins: 14
|
||||
spacing: 12
|
||||
|
||||
// =================================================================
|
||||
// TAB SWITCHER (Today | Notifications)
|
||||
// =================================================================
|
||||
Rectangle {
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: 30
|
||||
radius: Theme.radiusMedium
|
||||
color: Qt.rgba(0.0, 0.0, 0.0, 0.3)
|
||||
|
||||
RowLayout {
|
||||
anchors.fill: parent
|
||||
spacing: 0
|
||||
|
||||
// Today Tab Button
|
||||
Rectangle {
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
radius: Theme.radiusMedium
|
||||
color: ccWindow.activeTab === 0 ? Qt.rgba(1.0, 1.0, 1.0, 0.18) : "transparent"
|
||||
|
||||
Text {
|
||||
anchors.centerIn: parent
|
||||
text: "Today"
|
||||
font.family: Theme.fontFamily
|
||||
font.pixelSize: Theme.fontSizeNormal
|
||||
font.weight: ccWindow.activeTab === 0 ? Font.DemiBold : Font.Normal
|
||||
color: ccWindow.activeTab === 0 ? "#FFFFFF" : Theme.textTertiary
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: ccWindow.activeTab = 0
|
||||
}
|
||||
}
|
||||
|
||||
// Notifications Tab Button
|
||||
Rectangle {
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
radius: Theme.radiusMedium
|
||||
color: ccWindow.activeTab === 1 ? Qt.rgba(1.0, 1.0, 1.0, 0.18) : "transparent"
|
||||
|
||||
Text {
|
||||
anchors.centerIn: parent
|
||||
text: "Notifications"
|
||||
font.family: Theme.fontFamily
|
||||
font.pixelSize: Theme.fontSizeNormal
|
||||
font.weight: ccWindow.activeTab === 1 ? Font.DemiBold : Font.Normal
|
||||
color: ccWindow.activeTab === 1 ? "#FFFFFF" : Theme.textTertiary
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: ccWindow.activeTab = 1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// =================================================================
|
||||
// TAB 1: TODAY VIEW
|
||||
// =================================================================
|
||||
ScrollView {
|
||||
visible: ccWindow.activeTab === 0
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
clip: true
|
||||
|
||||
ColumnLayout {
|
||||
width: parent.width
|
||||
spacing: 12
|
||||
|
||||
// 1. Large Date & Time Widget
|
||||
Rectangle {
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: 70
|
||||
radius: Theme.radiusLarge
|
||||
color: Theme.cardBg
|
||||
border.color: Theme.cardBorder
|
||||
border.width: 1
|
||||
|
||||
ColumnLayout {
|
||||
anchors.fill: parent
|
||||
anchors.margins: 12
|
||||
spacing: 2
|
||||
|
||||
Text {
|
||||
text: GlobalState.fullDateString
|
||||
font.family: Theme.fontFamily
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
font.weight: Font.DemiBold
|
||||
color: Theme.accentBlue
|
||||
}
|
||||
|
||||
Text {
|
||||
text: GlobalState.timeString
|
||||
font.family: Theme.fontFamily
|
||||
font.pixelSize: 26
|
||||
font.weight: Font.Light
|
||||
color: "#FFFFFF"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 2. Control Toggles (Wi-Fi, Bluetooth, DND, Night Shift)
|
||||
Rectangle {
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: 120
|
||||
radius: Theme.radiusLarge
|
||||
color: Theme.cardBg
|
||||
border.color: Theme.cardBorder
|
||||
border.width: 1
|
||||
|
||||
GridLayout {
|
||||
anchors.fill: parent
|
||||
anchors.margins: 10
|
||||
columns: 2
|
||||
rowSpacing: 8
|
||||
columnSpacing: 8
|
||||
|
||||
// Wi-Fi Toggle
|
||||
ToggleCard {
|
||||
iconSource: "../assets/wifi.svg"
|
||||
title: "Wi-Fi"
|
||||
subtitle: GlobalState.wifiSsid
|
||||
isActive: GlobalState.wifiConnected
|
||||
onClicked: GlobalState.launchApp("nm-connection-editor || foot -e nmtui")
|
||||
}
|
||||
|
||||
// Bluetooth Toggle
|
||||
ToggleCard {
|
||||
iconSource: "../assets/bluetooth.svg"
|
||||
title: "Bluetooth"
|
||||
subtitle: "Active"
|
||||
isActive: true
|
||||
onClicked: GlobalState.launchApp("foot -e bluetoothctl")
|
||||
}
|
||||
|
||||
// Do Not Disturb Toggle
|
||||
ToggleCard {
|
||||
iconSource: "../assets/preferences.svg"
|
||||
title: "Do Not Disturb"
|
||||
subtitle: "Off"
|
||||
isActive: false
|
||||
onClicked: {}
|
||||
}
|
||||
|
||||
// Sound Mute Toggle
|
||||
ToggleCard {
|
||||
iconSource: "../assets/volume.svg"
|
||||
title: "Sound"
|
||||
subtitle: GlobalState.volumeMuted ? "Muted" : `${GlobalState.volume}%`
|
||||
isActive: !GlobalState.volumeMuted
|
||||
onClicked: GlobalState.toggleVolumeMute()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 3. Brightness & Volume Sliders
|
||||
Rectangle {
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: 110
|
||||
radius: Theme.radiusLarge
|
||||
color: Theme.cardBg
|
||||
border.color: Theme.cardBorder
|
||||
border.width: 1
|
||||
|
||||
ColumnLayout {
|
||||
anchors.fill: parent
|
||||
anchors.margins: 12
|
||||
spacing: 10
|
||||
|
||||
// Display Brightness Slider
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: 8
|
||||
|
||||
Image {
|
||||
width: 16
|
||||
height: 16
|
||||
source: "../assets/brightness.svg"
|
||||
sourceSize: Qt.size(16, 16)
|
||||
}
|
||||
|
||||
Item {
|
||||
Layout.fillWidth: true
|
||||
height: 18
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
radius: 9
|
||||
color: Qt.rgba(1,1,1,0.18)
|
||||
|
||||
Rectangle {
|
||||
anchors.left: parent.left
|
||||
anchors.top: parent.top
|
||||
anchors.bottom: parent.bottom
|
||||
width: parent.width * (GlobalState.brightness / 100.0)
|
||||
radius: 9
|
||||
color: "#FFFFFF"
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
function update(m) {
|
||||
const frac = Math.max(0.01, Math.min(1.0, m.x / width));
|
||||
GlobalState.setBrightness(Math.round(frac * 100));
|
||||
}
|
||||
onPressed: (m) => update(m)
|
||||
onPositionChanged: (m) => { if (pressed) update(m); }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Sound Volume Slider
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: 8
|
||||
|
||||
Image {
|
||||
width: 16
|
||||
height: 16
|
||||
source: "../assets/volume.svg"
|
||||
sourceSize: Qt.size(16, 16)
|
||||
}
|
||||
|
||||
Item {
|
||||
Layout.fillWidth: true
|
||||
height: 18
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
radius: 9
|
||||
color: Qt.rgba(1,1,1,0.18)
|
||||
|
||||
Rectangle {
|
||||
anchors.left: parent.left
|
||||
anchors.top: parent.top
|
||||
anchors.bottom: parent.bottom
|
||||
width: parent.width * (GlobalState.volume / 100.0)
|
||||
radius: 9
|
||||
color: Theme.accentBlue
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
function update(m) {
|
||||
const frac = Math.max(0.0, Math.min(1.0, m.x / width));
|
||||
GlobalState.setVolume(Math.round(frac * 100));
|
||||
}
|
||||
onPressed: (m) => update(m)
|
||||
onPositionChanged: (m) => { if (pressed) update(m); }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 4. System Resources Monitor Widget
|
||||
Rectangle {
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: 90
|
||||
radius: Theme.radiusLarge
|
||||
color: Theme.cardBg
|
||||
border.color: Theme.cardBorder
|
||||
border.width: 1
|
||||
|
||||
ColumnLayout {
|
||||
anchors.fill: parent
|
||||
anchors.margins: 12
|
||||
spacing: 6
|
||||
|
||||
Text {
|
||||
text: "System Performance"
|
||||
font.family: Theme.fontFamily
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
font.weight: Font.DemiBold
|
||||
color: Theme.textSecondary
|
||||
}
|
||||
|
||||
// CPU Bar
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: 8
|
||||
Text {
|
||||
text: "CPU"
|
||||
font.family: Theme.fontFamily
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
color: Theme.textPrimary
|
||||
Layout.preferredWidth: 32
|
||||
}
|
||||
Rectangle {
|
||||
Layout.fillWidth: true
|
||||
height: 6
|
||||
radius: 3
|
||||
color: Qt.rgba(1,1,1,0.15)
|
||||
Rectangle {
|
||||
anchors.left: parent.left
|
||||
anchors.top: parent.top
|
||||
anchors.bottom: parent.bottom
|
||||
width: parent.width * (GlobalState.cpuPercent / 100.0)
|
||||
radius: 3
|
||||
color: GlobalState.cpuPercent > 80 ? Theme.accentRed : Theme.accentGreen
|
||||
}
|
||||
}
|
||||
Text {
|
||||
text: `${GlobalState.cpuPercent}%`
|
||||
font.family: Theme.fontFamily
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
color: Theme.textSecondary
|
||||
Layout.preferredWidth: 36
|
||||
horizontalAlignment: Text.AlignRight
|
||||
}
|
||||
}
|
||||
|
||||
// Memory Bar
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: 8
|
||||
Text {
|
||||
text: "RAM"
|
||||
font.family: Theme.fontFamily
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
color: Theme.textPrimary
|
||||
Layout.preferredWidth: 32
|
||||
}
|
||||
Rectangle {
|
||||
Layout.fillWidth: true
|
||||
height: 6
|
||||
radius: 3
|
||||
color: Qt.rgba(1,1,1,0.15)
|
||||
Rectangle {
|
||||
anchors.left: parent.left
|
||||
anchors.top: parent.top
|
||||
anchors.bottom: parent.bottom
|
||||
width: parent.width * (GlobalState.memPercent / 100.0)
|
||||
radius: 3
|
||||
color: Theme.accentBlue
|
||||
}
|
||||
}
|
||||
Text {
|
||||
text: `${GlobalState.memUsedGb}G`
|
||||
font.family: Theme.fontFamily
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
color: Theme.textSecondary
|
||||
Layout.preferredWidth: 36
|
||||
horizontalAlignment: Text.AlignRight
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 5. Now Playing Media Widget
|
||||
Rectangle {
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: 80
|
||||
radius: Theme.radiusLarge
|
||||
color: Theme.cardBg
|
||||
border.color: Theme.cardBorder
|
||||
border.width: 1
|
||||
|
||||
RowLayout {
|
||||
anchors.fill: parent
|
||||
anchors.margins: 12
|
||||
spacing: 10
|
||||
|
||||
Image {
|
||||
width: 36
|
||||
height: 36
|
||||
source: "../assets/music.svg"
|
||||
sourceSize: Qt.size(36, 36)
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: 2
|
||||
|
||||
Text {
|
||||
text: GlobalState.mediaTitle || "No Media Playing"
|
||||
font.family: Theme.fontFamily
|
||||
font.pixelSize: Theme.fontSizeNormal
|
||||
font.weight: Font.Medium
|
||||
color: "#FFFFFF"
|
||||
elide: Text.ElideRight
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
Text {
|
||||
text: GlobalState.mediaArtist || "Apple Music"
|
||||
font.family: Theme.fontFamily
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
color: Theme.textTertiary
|
||||
elide: Text.ElideRight
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
}
|
||||
|
||||
// Playback controls
|
||||
RowLayout {
|
||||
spacing: 4
|
||||
|
||||
Rectangle {
|
||||
width: 24
|
||||
height: 24
|
||||
radius: 12
|
||||
color: Qt.rgba(1,1,1,0.1)
|
||||
Text { anchors.centerIn: parent; text: "⏮"; color: "#FFFFFF"; font.pixelSize: 10 }
|
||||
MouseArea { anchors.fill: parent; cursorShape: Qt.PointingHandCursor; onClicked: GlobalState.mediaPrevious() }
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
width: 28
|
||||
height: 28
|
||||
radius: 14
|
||||
color: Theme.accentBlue
|
||||
Text { anchors.centerIn: parent; text: GlobalState.mediaPlaying ? "⏸" : "▶"; color: "#FFFFFF"; font.pixelSize: 11 }
|
||||
MouseArea { anchors.fill: parent; cursorShape: Qt.PointingHandCursor; onClicked: GlobalState.mediaPlayPause() }
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
width: 24
|
||||
height: 24
|
||||
radius: 12
|
||||
color: Qt.rgba(1,1,1,0.1)
|
||||
Text { anchors.centerIn: parent; text: "⏭"; color: "#FFFFFF"; font.pixelSize: 10 }
|
||||
MouseArea { anchors.fill: parent; cursorShape: Qt.PointingHandCursor; onClicked: GlobalState.mediaNext() }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// =================================================================
|
||||
// TAB 2: NOTIFICATIONS VIEW
|
||||
// =================================================================
|
||||
ColumnLayout {
|
||||
visible: ccWindow.activeTab === 1
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
spacing: 12
|
||||
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
Text {
|
||||
text: "Notifications"
|
||||
font.family: Theme.fontFamily
|
||||
font.pixelSize: Theme.fontSizeLarge
|
||||
font.weight: Font.DemiBold
|
||||
color: "#FFFFFF"
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
Rectangle {
|
||||
implicitWidth: clearText.implicitWidth + 12
|
||||
height: 22
|
||||
radius: Theme.radiusSmall
|
||||
color: Qt.rgba(1,1,1,0.12)
|
||||
Text {
|
||||
id: clearText
|
||||
anchors.centerIn: parent
|
||||
text: "Clear All"
|
||||
font.family: Theme.fontFamily
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
color: Theme.textSecondary
|
||||
}
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Placeholder / Active notifications card
|
||||
Rectangle {
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: 70
|
||||
radius: Theme.radiusLarge
|
||||
color: Theme.cardBg
|
||||
border.color: Theme.cardBorder
|
||||
border.width: 1
|
||||
|
||||
RowLayout {
|
||||
anchors.fill: parent
|
||||
anchors.margins: 12
|
||||
spacing: 10
|
||||
|
||||
Image {
|
||||
width: 28
|
||||
height: 28
|
||||
source: "../assets/apple.svg"
|
||||
sourceSize: Qt.size(28, 28)
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: 2
|
||||
|
||||
Text {
|
||||
text: "macOS Catalina on Hyprland"
|
||||
font.family: Theme.fontFamily
|
||||
font.pixelSize: Theme.fontSizeNormal
|
||||
font.weight: Font.Medium
|
||||
color: "#FFFFFF"
|
||||
}
|
||||
|
||||
Text {
|
||||
text: "Quickshell desktop loaded successfully."
|
||||
font.family: Theme.fontFamily
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
color: Theme.textSecondary
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
Layout.fillHeight: true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Toggle Card Component
|
||||
component ToggleCard: Rectangle {
|
||||
id: cardRoot
|
||||
property string iconSource: ""
|
||||
property string title: ""
|
||||
property string subtitle: ""
|
||||
property bool isActive: false
|
||||
signal clicked()
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: 46
|
||||
radius: Theme.radiusMedium
|
||||
color: cardMouse.containsMouse ? Qt.rgba(1.0, 1.0, 1.0, 0.22) : Qt.rgba(1.0, 1.0, 1.0, 0.12)
|
||||
|
||||
RowLayout {
|
||||
anchors.fill: parent
|
||||
anchors.margins: 8
|
||||
spacing: 8
|
||||
|
||||
Rectangle {
|
||||
width: 28
|
||||
height: 28
|
||||
radius: 14
|
||||
color: cardRoot.isActive ? Theme.accentBlue : Qt.rgba(1,1,1,0.2)
|
||||
|
||||
Image {
|
||||
anchors.centerIn: parent
|
||||
width: 14
|
||||
height: 14
|
||||
source: cardRoot.iconSource
|
||||
sourceSize: Qt.size(14, 14)
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: 1
|
||||
|
||||
Text {
|
||||
text: cardRoot.title
|
||||
font.family: Theme.fontFamily
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
font.weight: Font.Medium
|
||||
color: "#FFFFFF"
|
||||
elide: Text.ElideRight
|
||||
}
|
||||
|
||||
Text {
|
||||
text: cardRoot.subtitle
|
||||
font.family: Theme.fontFamily
|
||||
font.pixelSize: 10
|
||||
color: Theme.textTertiary
|
||||
elide: Text.ElideRight
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: cardMouse
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: cardRoot.clicked()
|
||||
}
|
||||
}
|
||||
}
|
||||
338
home/hypr/quickshell/components/Dock.qml
Normal file
@@ -0,0 +1,338 @@
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import QtQuick.Controls
|
||||
import Quickshell
|
||||
import Quickshell.Wayland
|
||||
import Quickshell.Hyprland
|
||||
import "../theme"
|
||||
import ".."
|
||||
|
||||
PanelWindow {
|
||||
id: dockWindow
|
||||
property var modelData
|
||||
screen: modelData
|
||||
|
||||
anchors {
|
||||
bottom: true
|
||||
}
|
||||
|
||||
margins {
|
||||
bottom: 6
|
||||
}
|
||||
|
||||
// Adjust width to fit content
|
||||
implicitWidth: dockTray.implicitWidth + 32
|
||||
implicitHeight: 90
|
||||
|
||||
color: "transparent"
|
||||
WlrLayershell.layer: WlrLayer.Top
|
||||
WlrLayershell.namespace: "quickshell-dock"
|
||||
WlrLayershell.keyboardFocus: WlrKeyboardFocus.None
|
||||
|
||||
// Reserve 70px exclusive zone so windows don't overlap the dock
|
||||
exclusiveZone: 70
|
||||
|
||||
// Tooltip overlay container
|
||||
Item {
|
||||
id: tooltipContainer
|
||||
anchors.fill: parent
|
||||
z: 100
|
||||
}
|
||||
|
||||
// 2.5D Frosted Glass Dock Tray
|
||||
Rectangle {
|
||||
id: dockTray
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
implicitWidth: dockLayout.implicitWidth + 24
|
||||
height: 68
|
||||
radius: Theme.radiusDock
|
||||
color: Theme.dockBg
|
||||
border.color: Theme.dockBorder
|
||||
border.width: 1
|
||||
|
||||
// Top glossy reflection edge line (Catalina shelf characteristic)
|
||||
Rectangle {
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.top: parent.top
|
||||
anchors.leftMargin: 12
|
||||
anchors.rightMargin: 12
|
||||
height: 1
|
||||
color: Theme.dockHighlight
|
||||
opacity: 0.8
|
||||
}
|
||||
|
||||
// Inner items layout
|
||||
RowLayout {
|
||||
id: dockLayout
|
||||
anchors.centerIn: parent
|
||||
spacing: 6
|
||||
|
||||
// =================================================================
|
||||
// 1. PINNED APPS
|
||||
// =================================================================
|
||||
DockItem {
|
||||
id: finderItem
|
||||
name: "Finder"
|
||||
iconSource: "../assets/finder.svg"
|
||||
execCmd: "foot -e sh -c 'cd ~; lf || yazi || ls -la; exec zsh'"
|
||||
appClass: "foot"
|
||||
isPinned: true
|
||||
}
|
||||
|
||||
DockItem {
|
||||
name: "Launchpad"
|
||||
iconSource: "../assets/launchpad.svg"
|
||||
execCmd: ""
|
||||
isPinned: true
|
||||
onCustomClicked: GlobalState.toggleSpotlight()
|
||||
}
|
||||
|
||||
DockItem {
|
||||
name: "Brave Browser"
|
||||
iconSource: "../assets/browser.svg"
|
||||
execCmd: "brave"
|
||||
appClass: "brave-browser"
|
||||
isPinned: true
|
||||
}
|
||||
|
||||
DockItem {
|
||||
name: "Terminal"
|
||||
iconSource: "../assets/terminal.svg"
|
||||
execCmd: "foot"
|
||||
appClass: "foot"
|
||||
isPinned: true
|
||||
}
|
||||
|
||||
DockItem {
|
||||
name: "Neovim"
|
||||
iconSource: "../assets/code.svg"
|
||||
execCmd: "foot -e nvim"
|
||||
appClass: "nvim"
|
||||
isPinned: true
|
||||
}
|
||||
|
||||
DockItem {
|
||||
name: "System Preferences"
|
||||
iconSource: "../assets/preferences.svg"
|
||||
execCmd: "pavucontrol"
|
||||
appClass: "pavucontrol"
|
||||
isPinned: true
|
||||
}
|
||||
|
||||
DockItem {
|
||||
name: "App Store"
|
||||
iconSource: "../assets/appstore.svg"
|
||||
execCmd: "foot -e zsh -c 'echo NixOS Packages; nix-env -qaP | head -n 50; exec zsh'"
|
||||
appClass: ""
|
||||
isPinned: true
|
||||
}
|
||||
|
||||
DockItem {
|
||||
name: "Music"
|
||||
iconSource: "../assets/music.svg"
|
||||
execCmd: ""
|
||||
appClass: ""
|
||||
isPinned: true
|
||||
onCustomClicked: GlobalState.mediaPlayPause()
|
||||
}
|
||||
|
||||
// =================================================================
|
||||
// 2. DYNAMIC RUNNING APPS (Not pinned)
|
||||
// =================================================================
|
||||
Repeater {
|
||||
model: {
|
||||
const pinnedClasses = ["foot", "brave-browser", "pavucontrol", "nvim"];
|
||||
const list = [];
|
||||
const seen = {};
|
||||
if (Array.isArray(GlobalState.runningApps)) {
|
||||
for (let i = 0; i < GlobalState.runningApps.length; i++) {
|
||||
const client = GlobalState.runningApps[i];
|
||||
const cls = (client.class || "").toLowerCase();
|
||||
if (cls && !pinnedClasses.includes(cls) && !seen[cls]) {
|
||||
seen[cls] = true;
|
||||
list.push({
|
||||
name: client.title || client.class || "App",
|
||||
cls: client.class,
|
||||
address: client.address
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
return list;
|
||||
}
|
||||
delegate: DockItem {
|
||||
required property var modelData
|
||||
name: modelData.name
|
||||
iconSource: "../assets/code.svg"
|
||||
appClass: modelData.cls
|
||||
isPinned: false
|
||||
onCustomClicked: {
|
||||
if (modelData.address) {
|
||||
Hyprland.dispatch(`focuswindow address:${modelData.address}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// =================================================================
|
||||
// 3. DOCK SEPARATOR DIVIDER
|
||||
// =================================================================
|
||||
Rectangle {
|
||||
Layout.preferredWidth: 1
|
||||
Layout.preferredHeight: 44
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
Layout.leftMargin: 4
|
||||
Layout.rightMargin: 4
|
||||
color: Qt.rgba(1.0, 1.0, 1.0, 0.22)
|
||||
}
|
||||
|
||||
// =================================================================
|
||||
// 4. DOWNLOADS FOLDER & TRASH
|
||||
// =================================================================
|
||||
DockItem {
|
||||
name: "Downloads"
|
||||
iconSource: "../assets/folder-downloads.svg"
|
||||
execCmd: "foot -e sh -c 'cd ~/Downloads; ls -la; exec zsh'"
|
||||
isPinned: true
|
||||
}
|
||||
|
||||
DockItem {
|
||||
name: "Trash"
|
||||
iconSource: "../assets/trash-empty.svg"
|
||||
execCmd: "foot -e sh -c 'cd ~/.local/share/Trash/files 2>/dev/null || cd ~; ls -la; exec zsh'"
|
||||
isPinned: true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Reusable Dock Item Component
|
||||
component DockItem: Item {
|
||||
id: itemRoot
|
||||
property string name: ""
|
||||
property string iconSource: ""
|
||||
property string execCmd: ""
|
||||
property string appClass: ""
|
||||
property bool isPinned: true
|
||||
signal customClicked()
|
||||
|
||||
// Check if app is running
|
||||
readonly property bool isRunning: {
|
||||
if (!itemRoot.appClass) return false;
|
||||
const target = itemRoot.appClass.toLowerCase();
|
||||
if (GlobalState.activeWindowClass.toLowerCase().includes(target)) return true;
|
||||
if (Array.isArray(GlobalState.runningApps)) {
|
||||
for (let i = 0; i < GlobalState.runningApps.length; i++) {
|
||||
const c = GlobalState.runningApps[i];
|
||||
if ((c.class || "").toLowerCase().includes(target)) return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
implicitWidth: 50
|
||||
implicitHeight: 64
|
||||
Layout.alignment: Qt.AlignBottom
|
||||
|
||||
// Container with scale & bounce transform
|
||||
Item {
|
||||
id: iconWrapper
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.bottomMargin: 8
|
||||
width: 48
|
||||
height: 48
|
||||
|
||||
scale: itemArea.containsMouse ? 1.28 : 1.0
|
||||
y: itemArea.containsMouse ? -8 : 0
|
||||
|
||||
Behavior on scale {
|
||||
NumberAnimation { duration: 180; easing.type: Easing.OutCubic }
|
||||
}
|
||||
Behavior on y {
|
||||
NumberAnimation { duration: 180; easing.type: Easing.OutCubic }
|
||||
}
|
||||
|
||||
// Icon Image
|
||||
Image {
|
||||
anchors.centerIn: parent
|
||||
width: 46
|
||||
height: 46
|
||||
source: itemRoot.iconSource
|
||||
sourceSize: Qt.size(92, 92)
|
||||
smooth: true
|
||||
mipmap: true
|
||||
}
|
||||
|
||||
// Click Bounce Sequential Animation
|
||||
SequentialAnimation {
|
||||
id: bounceAnim
|
||||
NumberAnimation { target: iconWrapper; property: "y"; to: -24; duration: 150; easing.type: Easing.OutQuad }
|
||||
NumberAnimation { target: iconWrapper; property: "y"; to: -4; duration: 130; easing.type: Easing.InQuad }
|
||||
NumberAnimation { target: iconWrapper; property: "y"; to: -14; duration: 100; easing.type: Easing.OutQuad }
|
||||
NumberAnimation { target: iconWrapper; property: "y"; to: 0; duration: 100; easing.type: Easing.InQuad }
|
||||
}
|
||||
}
|
||||
|
||||
// Active App Running Indicator Dot (White glowing pill)
|
||||
Rectangle {
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.bottomMargin: 2
|
||||
width: 4
|
||||
height: 4
|
||||
radius: 2
|
||||
color: Theme.dockDot
|
||||
visible: itemRoot.isRunning
|
||||
}
|
||||
|
||||
// Hover Tooltip Badge (macOS Catalina Style Black Pill)
|
||||
Rectangle {
|
||||
id: tooltipPill
|
||||
visible: itemArea.containsMouse
|
||||
opacity: itemArea.containsMouse ? 1.0 : 0.0
|
||||
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.bottom: parent.top
|
||||
anchors.bottomMargin: 6
|
||||
implicitWidth: tooltipText.implicitWidth + 16
|
||||
height: 24
|
||||
radius: 6
|
||||
color: Qt.rgba(0.08, 0.08, 0.10, 0.92)
|
||||
border.color: Qt.rgba(1.0, 1.0, 1.0, 0.18)
|
||||
border.width: 1
|
||||
|
||||
Behavior on opacity {
|
||||
NumberAnimation { duration: 120 }
|
||||
}
|
||||
|
||||
Text {
|
||||
id: tooltipText
|
||||
anchors.centerIn: parent
|
||||
text: itemRoot.name
|
||||
font.family: Theme.fontFamily
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
font.weight: Font.Medium
|
||||
color: "#FFFFFF"
|
||||
}
|
||||
}
|
||||
|
||||
// Mouse interaction
|
||||
MouseArea {
|
||||
id: itemArea
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
|
||||
onClicked: {
|
||||
bounceAnim.start();
|
||||
if (itemRoot.execCmd !== "") {
|
||||
GlobalState.launchApp(itemRoot.execCmd);
|
||||
} else {
|
||||
itemRoot.customClicked();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
507
home/hypr/quickshell/components/MenuBar.qml
Normal file
@@ -0,0 +1,507 @@
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import QtQuick.Controls
|
||||
import Quickshell
|
||||
import Quickshell.Wayland
|
||||
import Quickshell.Hyprland
|
||||
import Quickshell.Services.SystemTray
|
||||
import "../theme"
|
||||
import ".."
|
||||
|
||||
PanelWindow {
|
||||
id: menuBarWindow
|
||||
property var modelData
|
||||
screen: modelData
|
||||
|
||||
anchors {
|
||||
top: true
|
||||
left: true
|
||||
right: true
|
||||
}
|
||||
|
||||
// Set implicitHeight to accommodate bar + open popups without clipping
|
||||
implicitHeight: (GlobalState.appleMenuVisible || GlobalState.activeDropdownMenu !== "" ||
|
||||
GlobalState.volumePopupVisible || GlobalState.wifiPopupVisible ||
|
||||
GlobalState.batteryPopupVisible || GlobalState.calendarPopupVisible) ? 420 : Theme.menuBarHeight
|
||||
|
||||
color: "transparent"
|
||||
WlrLayershell.layer: WlrLayer.Top
|
||||
WlrLayershell.namespace: "quickshell-menubar"
|
||||
WlrLayershell.keyboardFocus: WlrKeyboardFocus.None
|
||||
|
||||
// Set exclusive zone only for the 26px bar
|
||||
exclusiveZone: Theme.menuBarHeight
|
||||
|
||||
// Background Bar (26px)
|
||||
Rectangle {
|
||||
id: barRect
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
height: Theme.menuBarHeight
|
||||
color: Theme.menuBarBg
|
||||
border.color: Theme.menuBarBorder
|
||||
border.width: 0
|
||||
|
||||
// Bottom hairline border
|
||||
Rectangle {
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.bottom: parent.bottom
|
||||
height: 1
|
||||
color: Theme.menuBarBorder
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
anchors.fill: parent
|
||||
anchors.leftMargin: 12
|
||||
anchors.rightMargin: 12
|
||||
spacing: 0
|
||||
|
||||
// =================================================================
|
||||
// LEFT SIDE: Apple Menu + App Title + Standard Menus
|
||||
// =================================================================
|
||||
RowLayout {
|
||||
Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter
|
||||
spacing: 2
|
||||
|
||||
// Apple Logo Button
|
||||
Rectangle {
|
||||
id: appleBtn
|
||||
width: 28
|
||||
height: 20
|
||||
radius: Theme.radiusSmall
|
||||
color: (GlobalState.appleMenuVisible || appleMouse.containsMouse)
|
||||
? Qt.rgba(1.0, 1.0, 1.0, 0.18) : "transparent"
|
||||
|
||||
Image {
|
||||
anchors.centerIn: parent
|
||||
width: 14
|
||||
height: 14
|
||||
source: "../assets/apple.svg"
|
||||
sourceSize: Qt.size(14, 14)
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: appleMouse
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: {
|
||||
const wasOpen = GlobalState.appleMenuVisible;
|
||||
GlobalState.closeAllPopups();
|
||||
GlobalState.appleMenuVisible = !wasOpen;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Active App Title (Bold)
|
||||
Rectangle {
|
||||
id: appTitleBtn
|
||||
implicitWidth: appTitleText.implicitWidth + 16
|
||||
height: 20
|
||||
radius: Theme.radiusSmall
|
||||
color: titleMouse.containsMouse ? Qt.rgba(1.0, 1.0, 1.0, 0.15) : "transparent"
|
||||
|
||||
Text {
|
||||
id: appTitleText
|
||||
anchors.centerIn: parent
|
||||
text: GlobalState.activeWindowClass || "Finder"
|
||||
font.family: Theme.fontFamily
|
||||
font.pixelSize: Theme.fontSizeMenuBar
|
||||
font.weight: Font.Bold
|
||||
color: Theme.textPrimary
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: titleMouse
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: {
|
||||
const wasOpen = (GlobalState.activeDropdownMenu === "File");
|
||||
GlobalState.closeAllPopups();
|
||||
GlobalState.activeDropdownMenu = wasOpen ? "" : "File";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Standard Menus: File, Edit, View, Window, Help
|
||||
Repeater {
|
||||
model: ["File", "Edit", "View", "Window", "Help"]
|
||||
delegate: Rectangle {
|
||||
id: menuBtn
|
||||
implicitWidth: menuText.implicitWidth + 14
|
||||
height: 20
|
||||
radius: Theme.radiusSmall
|
||||
color: (GlobalState.activeDropdownMenu === modelData || menuMouse.containsMouse)
|
||||
? Qt.rgba(1.0, 1.0, 1.0, 0.15) : "transparent"
|
||||
|
||||
Text {
|
||||
id: menuText
|
||||
anchors.centerIn: parent
|
||||
text: modelData
|
||||
font.family: Theme.fontFamily
|
||||
font.pixelSize: Theme.fontSizeMenuBar
|
||||
font.weight: Font.Normal
|
||||
color: Theme.textPrimary
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: menuMouse
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onEntered: {
|
||||
// If a menu is already open, hovering switches to this one
|
||||
if (GlobalState.activeDropdownMenu !== "") {
|
||||
GlobalState.activeDropdownMenu = modelData;
|
||||
}
|
||||
}
|
||||
onClicked: {
|
||||
const wasOpen = (GlobalState.activeDropdownMenu === modelData);
|
||||
GlobalState.closeAllPopups();
|
||||
GlobalState.activeDropdownMenu = wasOpen ? "" : modelData;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Spacer
|
||||
Item {
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
// =================================================================
|
||||
// RIGHT SIDE: Tray + Media + Volume + Wifi + Battery + Spotlight + Control Center + Clock
|
||||
// =================================================================
|
||||
RowLayout {
|
||||
Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
|
||||
spacing: 4
|
||||
|
||||
// System Tray Icons
|
||||
Repeater {
|
||||
model: SystemTray.items
|
||||
delegate: Rectangle {
|
||||
required property SystemTrayItem modelData
|
||||
width: 22
|
||||
height: 20
|
||||
radius: Theme.radiusSmall
|
||||
color: trayMouse.containsMouse ? Qt.rgba(1.0, 1.0, 1.0, 0.15) : "transparent"
|
||||
|
||||
Image {
|
||||
anchors.centerIn: parent
|
||||
width: 15
|
||||
height: 15
|
||||
source: modelData.icon || ""
|
||||
sourceSize: Qt.size(15, 15)
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: trayMouse
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: modelData.activate()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Media Indicator (visible if playing)
|
||||
Rectangle {
|
||||
visible: GlobalState.mediaPlaying
|
||||
implicitWidth: mediaRow.implicitWidth + 12
|
||||
height: 20
|
||||
radius: Theme.radiusSmall
|
||||
color: mediaMouse.containsMouse ? Qt.rgba(1.0, 1.0, 1.0, 0.15) : "transparent"
|
||||
|
||||
RowLayout {
|
||||
id: mediaRow
|
||||
anchors.centerIn: parent
|
||||
spacing: 4
|
||||
Image {
|
||||
width: 13
|
||||
height: 13
|
||||
source: "../assets/music.svg"
|
||||
sourceSize: Qt.size(13, 13)
|
||||
}
|
||||
Text {
|
||||
text: GlobalState.mediaTitle ? `${GlobalState.mediaTitle.substring(0, 18)}...` : "Playing"
|
||||
font.family: Theme.fontFamily
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
color: Theme.textSecondary
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: mediaMouse
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: GlobalState.mediaPlayPause()
|
||||
}
|
||||
}
|
||||
|
||||
// Volume Item
|
||||
Rectangle {
|
||||
id: volBtn
|
||||
width: 26
|
||||
height: 20
|
||||
radius: Theme.radiusSmall
|
||||
color: (GlobalState.volumePopupVisible || volMouse.containsMouse)
|
||||
? Qt.rgba(1.0, 1.0, 1.0, 0.15) : "transparent"
|
||||
|
||||
Image {
|
||||
anchors.centerIn: parent
|
||||
width: 14
|
||||
height: 14
|
||||
source: "../assets/volume.svg"
|
||||
sourceSize: Qt.size(14, 14)
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: volMouse
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: {
|
||||
const wasOpen = GlobalState.volumePopupVisible;
|
||||
GlobalState.closeAllPopups();
|
||||
GlobalState.volumePopupVisible = !wasOpen;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Wi-Fi Item
|
||||
Rectangle {
|
||||
id: wifiBtn
|
||||
width: 26
|
||||
height: 20
|
||||
radius: Theme.radiusSmall
|
||||
color: (GlobalState.wifiPopupVisible || wifiMouse.containsMouse)
|
||||
? Qt.rgba(1.0, 1.0, 1.0, 0.15) : "transparent"
|
||||
|
||||
Image {
|
||||
anchors.centerIn: parent
|
||||
width: 14
|
||||
height: 14
|
||||
source: "../assets/wifi.svg"
|
||||
sourceSize: Qt.size(14, 14)
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: wifiMouse
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: {
|
||||
const wasOpen = GlobalState.wifiPopupVisible;
|
||||
GlobalState.closeAllPopups();
|
||||
GlobalState.wifiPopupVisible = !wasOpen;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Battery Item
|
||||
Rectangle {
|
||||
id: batBtn
|
||||
implicitWidth: batRow.implicitWidth + 8
|
||||
height: 20
|
||||
radius: Theme.radiusSmall
|
||||
color: (GlobalState.batteryPopupVisible || batMouse.containsMouse)
|
||||
? Qt.rgba(1.0, 1.0, 1.0, 0.15) : "transparent"
|
||||
|
||||
RowLayout {
|
||||
id: batRow
|
||||
anchors.centerIn: parent
|
||||
spacing: 4
|
||||
|
||||
Text {
|
||||
visible: GlobalState.batteryPresent
|
||||
text: `${GlobalState.batteryPercent}%`
|
||||
font.family: Theme.fontFamily
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
color: Theme.textPrimary
|
||||
}
|
||||
|
||||
Image {
|
||||
width: 18
|
||||
height: 12
|
||||
source: "../assets/battery.svg"
|
||||
sourceSize: Qt.size(18, 12)
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: batMouse
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: {
|
||||
const wasOpen = GlobalState.batteryPopupVisible;
|
||||
GlobalState.closeAllPopups();
|
||||
GlobalState.batteryPopupVisible = !wasOpen;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Spotlight Search Button
|
||||
Rectangle {
|
||||
id: spotBtn
|
||||
width: 26
|
||||
height: 20
|
||||
radius: Theme.radiusSmall
|
||||
color: (GlobalState.spotlightVisible || spotMouse.containsMouse)
|
||||
? Qt.rgba(1.0, 1.0, 1.0, 0.15) : "transparent"
|
||||
|
||||
Image {
|
||||
anchors.centerIn: parent
|
||||
width: 14
|
||||
height: 14
|
||||
source: "../assets/spotlight.svg"
|
||||
sourceSize: Qt.size(14, 14)
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: spotMouse
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: GlobalState.toggleSpotlight()
|
||||
}
|
||||
}
|
||||
|
||||
// Control Center / Notification Center Toggle Button
|
||||
Rectangle {
|
||||
id: ccBtn
|
||||
width: 26
|
||||
height: 20
|
||||
radius: Theme.radiusSmall
|
||||
color: (GlobalState.controlCenterVisible || ccMouse.containsMouse)
|
||||
? Qt.rgba(1.0, 1.0, 1.0, 0.15) : "transparent"
|
||||
|
||||
Image {
|
||||
anchors.centerIn: parent
|
||||
width: 14
|
||||
height: 14
|
||||
source: "../assets/controlcenter.svg"
|
||||
sourceSize: Qt.size(14, 14)
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: ccMouse
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: GlobalState.toggleControlCenter()
|
||||
}
|
||||
}
|
||||
|
||||
// Date & Time (Clock)
|
||||
Rectangle {
|
||||
id: clockBtn
|
||||
implicitWidth: clockRow.implicitWidth + 10
|
||||
height: 20
|
||||
radius: Theme.radiusSmall
|
||||
color: (GlobalState.calendarPopupVisible || clockMouse.containsMouse)
|
||||
? Qt.rgba(1.0, 1.0, 1.0, 0.15) : "transparent"
|
||||
|
||||
RowLayout {
|
||||
id: clockRow
|
||||
anchors.centerIn: parent
|
||||
spacing: 6
|
||||
|
||||
Text {
|
||||
text: GlobalState.dateString
|
||||
font.family: Theme.fontFamily
|
||||
font.pixelSize: Theme.fontSizeMenuBar
|
||||
color: Theme.textSecondary
|
||||
}
|
||||
|
||||
Text {
|
||||
text: GlobalState.timeString
|
||||
font.family: Theme.fontFamily
|
||||
font.pixelSize: Theme.fontSizeMenuBar
|
||||
font.weight: Font.Medium
|
||||
color: Theme.textPrimary
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: clockMouse
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: {
|
||||
const wasOpen = GlobalState.calendarPopupVisible;
|
||||
GlobalState.closeAllPopups();
|
||||
GlobalState.calendarPopupVisible = !wasOpen;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// DROPDOWNS & POPUPS
|
||||
// =========================================================================
|
||||
|
||||
// 1. Apple Menu Dropdown
|
||||
AppleMenu {
|
||||
id: appleDropdown
|
||||
visible: GlobalState.appleMenuVisible
|
||||
x: 8
|
||||
y: Theme.menuBarHeight + 2
|
||||
}
|
||||
|
||||
// 2. Application Menus Dropdown (File, Edit, View, Window, Help)
|
||||
AppMenuDropdown {
|
||||
id: appMenuDropdown
|
||||
visible: GlobalState.activeDropdownMenu !== ""
|
||||
menuName: GlobalState.activeDropdownMenu
|
||||
x: 60
|
||||
y: Theme.menuBarHeight + 2
|
||||
}
|
||||
|
||||
// 3. Volume Popup
|
||||
VolumePopup {
|
||||
id: volumeDropdown
|
||||
visible: GlobalState.volumePopupVisible
|
||||
anchors.top: barRect.bottom
|
||||
anchors.topMargin: 2
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: 190
|
||||
}
|
||||
|
||||
// 4. Wi-Fi Popup
|
||||
WifiPopup {
|
||||
id: wifiDropdown
|
||||
visible: GlobalState.wifiPopupVisible
|
||||
anchors.top: barRect.bottom
|
||||
anchors.topMargin: 2
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: 160
|
||||
}
|
||||
|
||||
// 5. Battery Popup
|
||||
BatteryPopup {
|
||||
id: batteryDropdown
|
||||
visible: GlobalState.batteryPopupVisible
|
||||
anchors.top: barRect.bottom
|
||||
anchors.topMargin: 2
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: 110
|
||||
}
|
||||
|
||||
// 6. Calendar Popup
|
||||
CalendarPopup {
|
||||
id: calDropdown
|
||||
visible: GlobalState.calendarPopupVisible
|
||||
anchors.top: barRect.bottom
|
||||
anchors.topMargin: 2
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: 10
|
||||
}
|
||||
}
|
||||
340
home/hypr/quickshell/components/Spotlight.qml
Normal file
@@ -0,0 +1,340 @@
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import QtQuick.Controls
|
||||
import Quickshell
|
||||
import Quickshell.Wayland
|
||||
import Quickshell.Io
|
||||
import "../theme"
|
||||
import ".."
|
||||
|
||||
PanelWindow {
|
||||
id: spotWindow
|
||||
visible: GlobalState.spotlightVisible
|
||||
|
||||
anchors {
|
||||
top: true
|
||||
left: true
|
||||
right: true
|
||||
bottom: true
|
||||
}
|
||||
|
||||
color: Qt.rgba(0, 0, 0, 0.35)
|
||||
WlrLayershell.layer: WlrLayer.Overlay
|
||||
WlrLayershell.namespace: "quickshell-spotlight"
|
||||
WlrLayershell.keyboardFocus: visible ? WlrKeyboardFocus.Exclusive : WlrKeyboardFocus.None
|
||||
|
||||
// Dismiss when clicking outside modal
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
onClicked: GlobalState.spotlightVisible = false
|
||||
}
|
||||
|
||||
// App Database Loader Process
|
||||
readonly property Process appScanProc: Process {
|
||||
command: ["bash", "-c", "dirs=(\"/run/current-system/sw/share/applications\" \"$HOME/.nix-profile/share/applications\" \"$HOME/.local/share/applications\"); echo '['; first=1; for d in \"${dirs[@]}\"; do [ -d \"$d\" ] && for f in \"$d\"/*.desktop; do [ -e \"$f\" ] || continue; grep -q \"^NoDisplay=true\" \"$f\" 2>/dev/null && continue; n=$(grep -m 1 \"^Name=\" \"$f\" 2>/dev/null | cut -d= -f2-); e=$(grep -m 1 \"^Exec=\" \"$f\" 2>/dev/null | cut -d= -f2- | sed -E 's/%[fFuUdDnNickvm]//g'); if [ -n \"$n\" ] && [ -n \"$e\" ]; then [ $first -eq 0 ] && echo ','; first=0; echo \"{\\\"name\\\":\\\"$n\\\",\\\"exec\\\":\\\"$e\\\"}\"; fi; done; done; echo ']'"]
|
||||
stdout: StdioCollector {
|
||||
onDataChanged: {
|
||||
try {
|
||||
const parsed = JSON.parse(data.trim());
|
||||
if (Array.isArray(parsed)) {
|
||||
spotWindow.allApps = parsed;
|
||||
}
|
||||
} catch(e) {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
property var allApps: [
|
||||
{ name: "Brave Browser", exec: "brave", comment: "Web Browser", icon: "browser" },
|
||||
{ name: "Foot Terminal", exec: "foot", comment: "Terminal Emulator", icon: "terminal" },
|
||||
{ name: "Neovim", exec: "foot -e nvim", comment: "Code Editor", icon: "code" },
|
||||
{ name: "Activity Monitor (Btop)", exec: "foot -e btop", comment: "System Resources", icon: "preferences" },
|
||||
{ name: "Volume & Sound (Pavucontrol)", exec: "pavucontrol", comment: "Audio Settings", icon: "preferences" },
|
||||
{ name: "Lock Screen", exec: "hyprlock", comment: "Lock Session", icon: "preferences" },
|
||||
{ name: "System Reboot", exec: "systemctl reboot", comment: "Reboot NixOS", icon: "preferences" },
|
||||
{ name: "Shut Down", exec: "systemctl poweroff", comment: "Power Off", icon: "preferences" }
|
||||
]
|
||||
|
||||
property int selectedIndex: 0
|
||||
|
||||
onVisibleChanged: {
|
||||
if (visible) {
|
||||
appScanProc.running = true;
|
||||
searchInput.text = "";
|
||||
selectedIndex = 0;
|
||||
searchInput.forceActiveFocus();
|
||||
}
|
||||
}
|
||||
|
||||
// Centered Spotlight Search Modal
|
||||
Rectangle {
|
||||
id: modalBox
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
y: 140
|
||||
width: 640
|
||||
implicitHeight: searchBarRow.height + (resultsListView.count > 0 || mathResultText !== "" ? 340 : 0)
|
||||
radius: Theme.radiusModal
|
||||
color: Theme.windowBg
|
||||
border.color: Theme.windowBorder
|
||||
border.width: 1
|
||||
|
||||
Behavior on implicitHeight {
|
||||
NumberAnimation { duration: 180; easing.type: Easing.OutQuad }
|
||||
}
|
||||
|
||||
// Prevent clicking inside from closing modal
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
onClicked: {}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
anchors.fill: parent
|
||||
anchors.margins: 14
|
||||
spacing: 12
|
||||
|
||||
// Top Search Bar
|
||||
RowLayout {
|
||||
id: searchBarRow
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: 44
|
||||
spacing: 12
|
||||
|
||||
Image {
|
||||
width: 24
|
||||
height: 24
|
||||
source: "../assets/spotlight.svg"
|
||||
sourceSize: Qt.size(24, 24)
|
||||
}
|
||||
|
||||
TextInput {
|
||||
id: searchInput
|
||||
Layout.fillWidth: true
|
||||
font.family: Theme.fontFamily
|
||||
font.pixelSize: Theme.fontSizeSpotlight
|
||||
font.weight: Font.Light
|
||||
color: Theme.textPrimary
|
||||
focus: true
|
||||
clip: true
|
||||
|
||||
Text {
|
||||
anchors.fill: parent
|
||||
visible: searchInput.text === ""
|
||||
text: "Spotlight Search"
|
||||
font.family: Theme.fontFamily
|
||||
font.pixelSize: Theme.fontSizeSpotlight
|
||||
font.weight: Font.Light
|
||||
color: Theme.textTertiary
|
||||
}
|
||||
|
||||
Keys.onEscapePressed: GlobalState.spotlightVisible = false
|
||||
Keys.onDownPressed: {
|
||||
if (resultsListView.count > 0) {
|
||||
spotWindow.selectedIndex = Math.min(resultsListView.count - 1, spotWindow.selectedIndex + 1);
|
||||
}
|
||||
}
|
||||
Keys.onUpPressed: {
|
||||
if (resultsListView.count > 0) {
|
||||
spotWindow.selectedIndex = Math.max(0, spotWindow.selectedIndex - 1);
|
||||
}
|
||||
}
|
||||
Keys.onReturnPressed: {
|
||||
if (filteredModel.count > 0 && spotWindow.selectedIndex < filteredModel.count) {
|
||||
const item = filteredModel.get(spotWindow.selectedIndex);
|
||||
GlobalState.launchApp(item.exec);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Clear button
|
||||
Rectangle {
|
||||
visible: searchInput.text !== ""
|
||||
width: 20
|
||||
height: 20
|
||||
radius: 10
|
||||
color: Qt.rgba(1.0, 1.0, 1.0, 0.2)
|
||||
|
||||
Text {
|
||||
anchors.centerIn: parent
|
||||
text: "✕"
|
||||
font.pixelSize: 11
|
||||
color: Theme.textPrimary
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: searchInput.text = ""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Math Calculation Result Banner (if valid formula)
|
||||
Rectangle {
|
||||
visible: mathResultText !== ""
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: 36
|
||||
radius: Theme.radiusSmall
|
||||
color: Qt.rgba(0.04, 0.52, 1.0, 0.2)
|
||||
border.color: Theme.accentBlue
|
||||
border.width: 1
|
||||
|
||||
RowLayout {
|
||||
anchors.fill: parent
|
||||
anchors.margins: 8
|
||||
spacing: 8
|
||||
|
||||
Text {
|
||||
text: "="
|
||||
font.family: Theme.fontFamily
|
||||
font.pixelSize: 16
|
||||
font.weight: Font.Bold
|
||||
color: Theme.accentBlue
|
||||
}
|
||||
Text {
|
||||
text: mathResultText
|
||||
font.family: Theme.fontFamily
|
||||
font.pixelSize: 16
|
||||
font.weight: Font.DemiBold
|
||||
color: "#FFFFFF"
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Divider Line
|
||||
Rectangle {
|
||||
visible: resultsListView.count > 0
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: 1
|
||||
color: Qt.rgba(1.0, 1.0, 1.0, 0.12)
|
||||
}
|
||||
|
||||
// Search Results List View
|
||||
ListView {
|
||||
id: resultsListView
|
||||
visible: count > 0
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: Math.min(270, count * 42)
|
||||
clip: true
|
||||
spacing: 2
|
||||
model: filteredModel
|
||||
|
||||
delegate: Rectangle {
|
||||
id: itemDelegate
|
||||
required property int index
|
||||
required property string name
|
||||
required property string exec
|
||||
required property string comment
|
||||
|
||||
width: resultsListView.width
|
||||
height: 40
|
||||
radius: Theme.radiusSmall
|
||||
color: spotWindow.selectedIndex === index ? Theme.menuHoverBlue : "transparent"
|
||||
|
||||
RowLayout {
|
||||
anchors.fill: parent
|
||||
anchors.leftMargin: 10
|
||||
anchors.rightMargin: 10
|
||||
spacing: 10
|
||||
|
||||
Image {
|
||||
width: 22
|
||||
height: 22
|
||||
source: "../assets/terminal.svg"
|
||||
sourceSize: Qt.size(22, 22)
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: 1
|
||||
|
||||
Text {
|
||||
text: itemDelegate.name
|
||||
font.family: Theme.fontFamily
|
||||
font.pixelSize: Theme.fontSizeNormal
|
||||
font.weight: Font.Medium
|
||||
color: "#FFFFFF"
|
||||
elide: Text.ElideRight
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
Text {
|
||||
text: itemDelegate.comment || itemDelegate.exec
|
||||
font.family: Theme.fontFamily
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
color: spotWindow.selectedIndex === itemDelegate.index
|
||||
? Qt.rgba(1,1,1,0.85) : Theme.textTertiary
|
||||
elide: Text.ElideRight
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
}
|
||||
|
||||
Text {
|
||||
text: "↩"
|
||||
font.pixelSize: 14
|
||||
color: spotWindow.selectedIndex === itemDelegate.index
|
||||
? "#FFFFFF" : "transparent"
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onEntered: spotWindow.selectedIndex = itemDelegate.index
|
||||
onClicked: GlobalState.launchApp(itemDelegate.exec)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Math calculation evaluator property
|
||||
readonly property string mathResultText: {
|
||||
const query = searchInput.text.trim();
|
||||
if (/^[0-9+\-*/^().\s%]+$/.test(query) && /[0-9]/.test(query) && /[+\-*/^%]/.test(query)) {
|
||||
try {
|
||||
// Safe evaluation of arithmetic
|
||||
const res = Function(`"use strict"; return (${query.replace(/\^/g, "**")})`)();
|
||||
if (typeof res === "number" && !isNaN(res) && isFinite(res)) {
|
||||
return `${res}`;
|
||||
}
|
||||
} catch(e) {}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
// Filtered ListModel updated on text change
|
||||
ListModel {
|
||||
id: filteredModel
|
||||
}
|
||||
|
||||
function updateFilter() {
|
||||
filteredModel.clear();
|
||||
const query = searchInput.text.toLowerCase().trim();
|
||||
let count = 0;
|
||||
for (let i = 0; i < allApps.length && count < 12; i++) {
|
||||
const app = allApps[i];
|
||||
if (!app.name) continue;
|
||||
if (query === "" || app.name.toLowerCase().includes(query) || (app.exec && app.exec.toLowerCase().includes(query))) {
|
||||
filteredModel.append({
|
||||
name: app.name,
|
||||
exec: app.exec || "",
|
||||
comment: app.comment || ""
|
||||
});
|
||||
count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: searchInput
|
||||
function onTextChanged() {
|
||||
spotWindow.selectedIndex = 0;
|
||||
updateFilter();
|
||||
}
|
||||
}
|
||||
|
||||
Component.onCompleted: updateFilter()
|
||||
}
|
||||
148
home/hypr/quickshell/components/VolumePopup.qml
Normal file
@@ -0,0 +1,148 @@
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import QtQuick.Controls
|
||||
import "../theme"
|
||||
import ".."
|
||||
|
||||
Rectangle {
|
||||
id: root
|
||||
width: 240
|
||||
height: 110
|
||||
radius: Theme.radiusMedium
|
||||
color: Theme.menuDropdownBg
|
||||
border.color: Theme.menuDropdownBorder
|
||||
border.width: 1
|
||||
|
||||
ColumnLayout {
|
||||
anchors.fill: parent
|
||||
anchors.margins: 12
|
||||
spacing: 8
|
||||
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
Text {
|
||||
text: "Sound"
|
||||
font.family: Theme.fontFamily
|
||||
font.pixelSize: Theme.fontSizeNormal
|
||||
font.weight: Font.DemiBold
|
||||
color: Theme.textPrimary
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
Text {
|
||||
text: `${GlobalState.volume}%`
|
||||
font.family: Theme.fontFamily
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
color: Theme.textSecondary
|
||||
}
|
||||
}
|
||||
|
||||
// Custom Catalina Volume Slider
|
||||
Item {
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: 24
|
||||
|
||||
Rectangle {
|
||||
id: track
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
height: 6
|
||||
radius: 3
|
||||
color: Qt.rgba(1.0, 1.0, 1.0, 0.2)
|
||||
|
||||
Rectangle {
|
||||
anchors.left: parent.left
|
||||
anchors.top: parent.top
|
||||
anchors.bottom: parent.bottom
|
||||
width: track.width * (GlobalState.volume / 100.0)
|
||||
radius: 3
|
||||
color: Theme.accentBlue
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: thumb
|
||||
x: Math.max(0, Math.min(track.width - width, (track.width * (GlobalState.volume / 100.0)) - width / 2))
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
width: 16
|
||||
height: 16
|
||||
radius: 8
|
||||
color: "#FFFFFF"
|
||||
border.color: Qt.rgba(0, 0, 0, 0.2)
|
||||
border.width: 1
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
preventStealing: true
|
||||
|
||||
function updateFromMouse(mouse) {
|
||||
const frac = Math.max(0.0, Math.min(1.0, mouse.x / width));
|
||||
GlobalState.setVolume(Math.round(frac * 100));
|
||||
}
|
||||
|
||||
onPressed: (mouse) => updateFromMouse(mouse)
|
||||
onPositionChanged: (mouse) => {
|
||||
if (pressed) updateFromMouse(mouse);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Bottom Controls
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: 8
|
||||
|
||||
Rectangle {
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: 24
|
||||
radius: Theme.radiusSmall
|
||||
color: muteMouse.containsMouse ? Qt.rgba(1.0, 1.0, 1.0, 0.15) : Qt.rgba(1.0, 1.0, 1.0, 0.08)
|
||||
|
||||
Text {
|
||||
anchors.centerIn: parent
|
||||
text: GlobalState.volumeMuted ? "Unmute" : "Mute"
|
||||
font.family: Theme.fontFamily
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
color: Theme.textPrimary
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: muteMouse
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: GlobalState.toggleVolumeMute()
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: 24
|
||||
radius: Theme.radiusSmall
|
||||
color: prefMouse.containsMouse ? Qt.rgba(1.0, 1.0, 1.0, 0.15) : Qt.rgba(1.0, 1.0, 1.0, 0.08)
|
||||
|
||||
Text {
|
||||
anchors.centerIn: parent
|
||||
text: "Sound Preferences..."
|
||||
font.family: Theme.fontFamily
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
color: Theme.textPrimary
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: prefMouse
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: {
|
||||
GlobalState.volumePopupVisible = false;
|
||||
GlobalState.launchApp("pavucontrol");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
102
home/hypr/quickshell/components/WifiPopup.qml
Normal file
@@ -0,0 +1,102 @@
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import QtQuick.Controls
|
||||
import "../theme"
|
||||
import ".."
|
||||
|
||||
Rectangle {
|
||||
id: root
|
||||
width: 240
|
||||
implicitHeight: wifiCol.implicitHeight + 24
|
||||
radius: Theme.radiusMedium
|
||||
color: Theme.menuDropdownBg
|
||||
border.color: Theme.menuDropdownBorder
|
||||
border.width: 1
|
||||
|
||||
ColumnLayout {
|
||||
id: wifiCol
|
||||
anchors.fill: parent
|
||||
anchors.margins: 12
|
||||
spacing: 8
|
||||
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
Text {
|
||||
text: "Wi-Fi"
|
||||
font.family: Theme.fontFamily
|
||||
font.pixelSize: Theme.fontSizeNormal
|
||||
font.weight: Font.DemiBold
|
||||
color: Theme.textPrimary
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
Text {
|
||||
text: GlobalState.wifiConnected ? "On" : "Off"
|
||||
font.family: Theme.fontFamily
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
color: GlobalState.wifiConnected ? Theme.accentGreen : Theme.textTertiary
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: 1
|
||||
color: Qt.rgba(1.0, 1.0, 1.0, 0.12)
|
||||
}
|
||||
|
||||
// Active network info
|
||||
ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: 3
|
||||
|
||||
Text {
|
||||
text: GlobalState.wifiConnected ? `Connected to: ${GlobalState.wifiSsid}` : "No Network Connected"
|
||||
font.family: Theme.fontFamily
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
font.weight: Font.Medium
|
||||
color: Theme.textPrimary
|
||||
elide: Text.ElideRight
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
Text {
|
||||
visible: GlobalState.ipAddress !== "" && GlobalState.ipAddress !== "Offline"
|
||||
text: `IP: ${GlobalState.ipAddress}`
|
||||
font.family: Theme.fontFamily
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
color: Theme.textSecondary
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: 1
|
||||
color: Qt.rgba(1.0, 1.0, 1.0, 0.12)
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: 24
|
||||
radius: Theme.radiusSmall
|
||||
color: netMouse.containsMouse ? Qt.rgba(1.0, 1.0, 1.0, 0.15) : Qt.rgba(1.0, 1.0, 1.0, 0.08)
|
||||
|
||||
Text {
|
||||
anchors.centerIn: parent
|
||||
text: "Network Preferences..."
|
||||
font.family: Theme.fontFamily
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
color: Theme.textPrimary
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: netMouse
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: {
|
||||
GlobalState.wifiPopupVisible = false;
|
||||
GlobalState.launchApp("nm-connection-editor || foot -e nmtui");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
1
home/hypr/quickshell/qmldir
Normal file
@@ -0,0 +1 @@
|
||||
singleton GlobalState 1.0 GlobalState.qml
|
||||
66
home/hypr/quickshell/scripts/get_apps.sh
Executable file
@@ -0,0 +1,66 @@
|
||||
#!/usr/bin/env bash
|
||||
# Scans .desktop files and outputs JSON array of apps
|
||||
dirs=(
|
||||
"/run/current-system/sw/share/applications"
|
||||
"$HOME/.nix-profile/share/applications"
|
||||
"$HOME/.local/share/applications"
|
||||
"/usr/share/applications"
|
||||
)
|
||||
|
||||
declare -A seen
|
||||
apps="["
|
||||
first=true
|
||||
|
||||
# Add default curated Mac/system apps first
|
||||
curated=(
|
||||
'{"name":"Finder","exec":"foot -e sh -c \"cd ~; lf || yazi || ls -la; exec zsh\"","icon":"finder","comment":"File Manager"}'
|
||||
'{"name":"Terminal","exec":"foot","icon":"terminal","comment":"Command Line Interface"}'
|
||||
'{"name":"Brave Browser","exec":"brave","icon":"browser","comment":"Web Browser"}'
|
||||
'{"name":"Neovim","exec":"foot -e nvim","icon":"code","comment":"Text & Code Editor"}'
|
||||
'{"name":"System Preferences","exec":"pavucontrol","icon":"preferences","comment":"Audio & System Settings"}'
|
||||
'{"name":"Activity Monitor","exec":"foot -e btop","icon":"preferences","comment":"Resource Monitor"}'
|
||||
'{"name":"Lock Screen","exec":"hyprlock","icon":"preferences","comment":"Lock current session"}'
|
||||
'{"name":"Restart System","exec":"systemctl reboot","icon":"preferences","comment":"Reboot NixOS"}'
|
||||
'{"name":"Shut Down","exec":"systemctl poweroff","icon":"preferences","comment":"Power off computer"}'
|
||||
)
|
||||
|
||||
for app in "${curated[@]}"; do
|
||||
if [ "$first" = true ]; then
|
||||
apps="$apps$app"
|
||||
first=false
|
||||
else
|
||||
apps="$apps,$app"
|
||||
fi
|
||||
done
|
||||
|
||||
for dir in "${dirs[@]}"; do
|
||||
if [ -d "$dir" ]; then
|
||||
for f in "$dir"/*.desktop; do
|
||||
[ -e "$f" ] || continue
|
||||
# Check NoDisplay
|
||||
if grep -q "^NoDisplay=true" "$f" 2>/dev/null; then
|
||||
continue
|
||||
fi
|
||||
|
||||
name=$(grep -m 1 "^Name=" "$f" 2>/dev/null | cut -d= -f2- | tr -d '\r\n')
|
||||
exec_cmd=$(grep -m 1 "^Exec=" "$f" 2>/dev/null | cut -d= -f2- | sed -E 's/%[fFuUdDnNickvm]//g' | tr -d '\r\n')
|
||||
icon=$(grep -m 1 "^Icon=" "$f" 2>/dev/null | cut -d= -f2- | tr -d '\r\n')
|
||||
comment=$(grep -m 1 "^Comment=" "$f" 2>/dev/null | cut -d= -f2- | tr -d '\r\n')
|
||||
|
||||
if [ -n "$name" ] && [ -n "$exec_cmd" ] && [ -z "${seen[$name]}" ]; then
|
||||
seen[$name]=1
|
||||
# Escape quotes
|
||||
name_esc=$(echo "$name" | sed 's/"/\\"/g')
|
||||
exec_esc=$(echo "$exec_cmd" | sed 's/"/\\"/g')
|
||||
icon_esc=$(echo "$icon" | sed 's/"/\\"/g')
|
||||
comment_esc=$(echo "$comment" | sed 's/"/\\"/g')
|
||||
|
||||
entry="{\"name\":\"$name_esc\",\"exec\":\"$exec_esc\",\"icon\":\"$icon_esc\",\"comment\":\"$comment_esc\"}"
|
||||
apps="$apps,$entry"
|
||||
fi
|
||||
done
|
||||
fi
|
||||
done
|
||||
|
||||
apps="$apps]"
|
||||
echo "$apps"
|
||||
20
home/hypr/quickshell/scripts/get_audio.sh
Executable file
@@ -0,0 +1,20 @@
|
||||
#!/usr/bin/env bash
|
||||
# Read audio volume & mute state
|
||||
volume=50
|
||||
muted=false
|
||||
|
||||
if command -v pactl &>/dev/null; then
|
||||
vol_str=$(pactl get-sink-volume @DEFAULT_SINK@ 2>/dev/null | grep -Po '[0-9]+(?=%)' | head -n 1)
|
||||
if [ -n "$vol_str" ]; then
|
||||
volume=$vol_str
|
||||
fi
|
||||
mute_str=$(pactl get-sink-mute @DEFAULT_SINK@ 2>/dev/null)
|
||||
if echo "$mute_str" | grep -q "yes"; then
|
||||
muted=true
|
||||
fi
|
||||
elif command -v pamixer &>/dev/null; then
|
||||
volume=$(pamixer --get-volume 2>/dev/null || echo 50)
|
||||
muted=$(pamixer --get-mute 2>/dev/null || echo false)
|
||||
fi
|
||||
|
||||
echo "{\"volume\": $volume, \"muted\": $muted}"
|
||||
16
home/hypr/quickshell/scripts/get_battery.sh
Executable file
@@ -0,0 +1,16 @@
|
||||
#!/usr/bin/env bash
|
||||
# Read battery state
|
||||
bat_path=$(find /sys/class/power_supply/ -name "BAT*" 2>/dev/null | head -n 1)
|
||||
|
||||
if [ -n "$bat_path" ] && [ -d "$bat_path" ]; then
|
||||
capacity=$(cat "$bat_path/capacity" 2>/dev/null || echo 100)
|
||||
status=$(cat "$bat_path/status" 2>/dev/null || echo "Full")
|
||||
is_charging=false
|
||||
if [ "$status" = "Charging" ] || [ "$status" = "Full" ]; then
|
||||
is_charging=true
|
||||
fi
|
||||
echo "{\"present\": true, \"percent\": $capacity, \"status\": \"$status\", \"charging\": $is_charging}"
|
||||
else
|
||||
# Desktop without battery
|
||||
echo "{\"present\": false, \"percent\": 100, \"status\": \"AC\", \"charging\": true}"
|
||||
fi
|
||||
12
home/hypr/quickshell/scripts/get_brightness.sh
Executable file
@@ -0,0 +1,12 @@
|
||||
#!/usr/bin/env bash
|
||||
# Read brightness
|
||||
percent=100
|
||||
|
||||
if command -v brightnessctl &>/dev/null; then
|
||||
percent=$(brightnessctl -m 2>/dev/null | cut -d, -f4 | tr -d '%' | head -n 1)
|
||||
if [ -z "$percent" ]; then
|
||||
percent=100
|
||||
fi
|
||||
fi
|
||||
|
||||
echo "{\"brightness\": ${percent:-100}}"
|
||||
20
home/hypr/quickshell/scripts/get_cpu.sh
Executable file
@@ -0,0 +1,20 @@
|
||||
#!/usr/bin/env bash
|
||||
# Read /proc/stat CPU utilization
|
||||
read -r cpu u n s i io ir st g gn < /proc/stat
|
||||
total1=$((u + n + s + i + io + ir + st + g + gn))
|
||||
idle1=$((i + io))
|
||||
sleep 0.2
|
||||
read -r cpu u n s i io ir st g gn < /proc/stat
|
||||
total2=$((u + n + s + i + io + ir + st + g + gn))
|
||||
idle2=$((i + io))
|
||||
|
||||
total_diff=$((total2 - total1))
|
||||
idle_diff=$((idle2 - idle1))
|
||||
|
||||
if [ "$total_diff" -gt 0 ]; then
|
||||
cpu_usage=$(( (total_diff - idle_diff) * 100 / total_diff ))
|
||||
else
|
||||
cpu_usage=0
|
||||
fi
|
||||
|
||||
echo "$cpu_usage"
|
||||
14
home/hypr/quickshell/scripts/get_mem.sh
Executable file
@@ -0,0 +1,14 @@
|
||||
#!/usr/bin/env bash
|
||||
# Read memory usage from /proc/meminfo
|
||||
mem_total=$(grep MemTotal /proc/meminfo | awk '{print $2}')
|
||||
mem_avail=$(grep MemAvailable /proc/meminfo | awk '{print $2}')
|
||||
|
||||
if [ -n "$mem_total" ] && [ -n "$mem_avail" ] && [ "$mem_total" -gt 0 ]; then
|
||||
mem_used=$((mem_total - mem_avail))
|
||||
mem_percent=$((mem_used * 100 / mem_total))
|
||||
used_gb=$(awk "BEGIN {printf \"%.1f\", $mem_used/1048576}")
|
||||
total_gb=$(awk "BEGIN {printf \"%.1f\", $mem_total/1048576}")
|
||||
echo "{\"percent\": $mem_percent, \"used_gb\": \"$used_gb\", \"total_gb\": \"$total_gb\"}"
|
||||
else
|
||||
echo "{\"percent\": 0, \"used_gb\": \"0\", \"total_gb\": \"0\"}"
|
||||
fi
|
||||
30
home/hypr/quickshell/scripts/get_wifi.sh
Executable file
@@ -0,0 +1,30 @@
|
||||
#!/usr/bin/env bash
|
||||
# Read WiFi status
|
||||
ssid=""
|
||||
ip=""
|
||||
|
||||
if command -v nmcli &>/dev/null; then
|
||||
active_line=$(nmcli -t -f active,ssid,signal dev wifi 2>/dev/null | grep '^yes' | head -n 1)
|
||||
if [ -n "$active_line" ]; then
|
||||
ssid=$(echo "$active_line" | cut -d':' -f2)
|
||||
signal=$(echo "$active_line" | cut -d':' -f3)
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ -z "$ssid" ]; then
|
||||
# Fallback to iwgetid or /proc/net/wireless
|
||||
if command -v iwgetid &>/dev/null; then
|
||||
ssid=$(iwgetid -r 2>/dev/null)
|
||||
fi
|
||||
fi
|
||||
|
||||
# Get default IPv4
|
||||
ip=$(ip -4 route get 1.1.1.1 2>/dev/null | awk '{print $7; exit}')
|
||||
|
||||
if [ -n "$ssid" ]; then
|
||||
echo "{\"connected\": true, \"ssid\": \"$ssid\", \"ip\": \"${ip:-N/A}\"}"
|
||||
elif [ -n "$ip" ]; then
|
||||
echo "{\"connected\": true, \"ssid\": \"Ethernet\", \"ip\": \"$ip\"}"
|
||||
else
|
||||
echo "{\"connected\": false, \"ssid\": \"Not Connected\", \"ip\": \"None\"}"
|
||||
fi
|
||||
79
home/hypr/quickshell/shell.qml
Normal file
@@ -0,0 +1,79 @@
|
||||
import QtQuick
|
||||
import Quickshell
|
||||
import Quickshell.Io
|
||||
import "theme"
|
||||
import "components"
|
||||
import "."
|
||||
|
||||
Scope {
|
||||
id: root
|
||||
|
||||
// =========================================================================
|
||||
// IPC HANDLERS (Allows controlling shell via `qs ipc call <target> <method>`)
|
||||
// =========================================================================
|
||||
IpcHandler {
|
||||
target: "spotlight"
|
||||
function toggle() {
|
||||
GlobalState.toggleSpotlight();
|
||||
}
|
||||
function open() {
|
||||
GlobalState.spotlightVisible = true;
|
||||
}
|
||||
function close() {
|
||||
GlobalState.spotlightVisible = false;
|
||||
}
|
||||
}
|
||||
|
||||
IpcHandler {
|
||||
target: "controlCenter"
|
||||
function toggle() {
|
||||
GlobalState.toggleControlCenter();
|
||||
}
|
||||
function open() {
|
||||
GlobalState.controlCenterVisible = true;
|
||||
}
|
||||
function close() {
|
||||
GlobalState.controlCenterVisible = false;
|
||||
}
|
||||
}
|
||||
|
||||
IpcHandler {
|
||||
target: "aboutMac"
|
||||
function toggle() {
|
||||
GlobalState.toggleAboutMac();
|
||||
}
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// MULTI-MONITOR BARS & DOCKS
|
||||
// =========================================================================
|
||||
// 1. macOS Catalina Top Menu Bar (Spawned per monitor)
|
||||
Variants {
|
||||
model: Quickshell.screens
|
||||
MenuBar {}
|
||||
}
|
||||
|
||||
// 2. macOS Catalina Floating Dock (Spawned per monitor)
|
||||
Variants {
|
||||
model: Quickshell.screens
|
||||
Dock {}
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// GLOBAL MODAL OVERLAYS
|
||||
// =========================================================================
|
||||
// 3. Spotlight Search Overlay
|
||||
Spotlight {
|
||||
id: spotlightOverlay
|
||||
}
|
||||
|
||||
// 4. Control Center & Notification Center Sidebar
|
||||
ControlCenter {
|
||||
id: controlCenterSidebar
|
||||
}
|
||||
|
||||
// 5. About This Mac Dialog
|
||||
AboutMac {
|
||||
id: aboutMacDialog
|
||||
}
|
||||
}
|
||||
57
home/hypr/quickshell/theme/Theme.qml
Normal file
@@ -0,0 +1,57 @@
|
||||
pragma Singleton
|
||||
import QtQuick
|
||||
|
||||
QtObject {
|
||||
// macOS Catalina Color Palette
|
||||
readonly property color menuBarBg: Qt.rgba(0.12, 0.12, 0.14, 0.88)
|
||||
readonly property color menuBarBorder: Qt.rgba(1.0, 1.0, 1.0, 0.12)
|
||||
|
||||
readonly property color dockBg: Qt.rgba(0.15, 0.16, 0.18, 0.72)
|
||||
readonly property color dockBorder: Qt.rgba(1.0, 1.0, 1.0, 0.22)
|
||||
readonly property color dockHighlight: Qt.rgba(1.0, 1.0, 1.0, 0.35)
|
||||
readonly property color dockShadow: Qt.rgba(0.0, 0.0, 0.0, 0.45)
|
||||
readonly property color dockDot: Qt.rgba(1.0, 1.0, 1.0, 0.95)
|
||||
|
||||
readonly property color windowBg: Qt.rgba(0.14, 0.14, 0.16, 0.94)
|
||||
readonly property color windowBorder: Qt.rgba(1.0, 1.0, 1.0, 0.15)
|
||||
readonly property color cardBg: Qt.rgba(0.22, 0.22, 0.26, 0.65)
|
||||
readonly property color cardBorder: Qt.rgba(1.0, 1.0, 1.0, 0.10)
|
||||
|
||||
readonly property color menuDropdownBg: Qt.rgba(0.16, 0.16, 0.18, 0.96)
|
||||
readonly property color menuDropdownBorder: Qt.rgba(1.0, 1.0, 1.0, 0.18)
|
||||
readonly property color menuHoverBlue: "#0062E3"
|
||||
readonly property color menuTextHover: "#FFFFFF"
|
||||
|
||||
readonly property color accentBlue: "#0A84FF"
|
||||
readonly property color accentGreen: "#30D158"
|
||||
readonly property color accentRed: "#FF453A"
|
||||
readonly property color accentYellow: "#FFD60A"
|
||||
readonly property color accentOrange: "#FF9F0A"
|
||||
|
||||
readonly property color textPrimary: "#FFFFFF"
|
||||
readonly property color textSecondary: Qt.rgba(1.0, 1.0, 1.0, 0.72)
|
||||
readonly property color textTertiary: Qt.rgba(1.0, 1.0, 1.0, 0.45)
|
||||
readonly property color textDisabled: Qt.rgba(1.0, 1.0, 1.0, 0.25)
|
||||
|
||||
// Typography
|
||||
readonly property string fontFamily: "SF Pro Display, SF Pro Text, Helvetica Neue, Cantarell, Liberation Sans, sans-serif"
|
||||
readonly property int fontSizeMenuBar: 13
|
||||
readonly property int fontSizeSmall: 11
|
||||
readonly property int fontSizeNormal: 13
|
||||
readonly property int fontSizeLarge: 15
|
||||
readonly property int fontSizeTitle: 19
|
||||
readonly property int fontSizeSpotlight: 20
|
||||
|
||||
// Geometry & Radii
|
||||
readonly property int menuBarHeight: 26
|
||||
readonly property int radiusSmall: 4
|
||||
readonly property int radiusMedium: 8
|
||||
readonly property int radiusLarge: 14
|
||||
readonly property int radiusDock: 18
|
||||
readonly property int radiusModal: 16
|
||||
|
||||
// Animation Durations
|
||||
readonly property int animFast: 120
|
||||
readonly property int animNormal: 220
|
||||
readonly property int animSmooth: 320
|
||||
}
|
||||
1
home/hypr/quickshell/theme/qmldir
Normal file
@@ -0,0 +1 @@
|
||||
singleton Theme 1.0 Theme.qml
|
||||
@@ -1,230 +0,0 @@
|
||||
{ pkgs, ... }:
|
||||
|
||||
{
|
||||
programs.waybar = {
|
||||
enable = true;
|
||||
settings = {
|
||||
mainBar = {
|
||||
layer = "top";
|
||||
modules-left = [ "custom/symbol" "cpu" "memory" "keyboard-state" "tray" ];
|
||||
modules-center = [ "hyprland/workspaces" ];
|
||||
modules-right = [ "idle_inhibitor" "backlight" "pulseaudio" "group/networks" "battery" "clock" ];
|
||||
|
||||
pulseaudio = {
|
||||
tooltip = false;
|
||||
scroll-step = 5;
|
||||
format = "{icon} {volume}%";
|
||||
format-muted = " {volume}%";
|
||||
on-click-right = "pactl set-sink-mute @DEFAULT_SINK@ toggle";
|
||||
on-click = "pavucontrol";
|
||||
format-icons = {
|
||||
default = [
|
||||
""
|
||||
""
|
||||
""
|
||||
];
|
||||
};
|
||||
};
|
||||
|
||||
idle_inhibitor = {
|
||||
format = "{icon}";
|
||||
format-icons = {
|
||||
activated = "";
|
||||
deactivated = "";
|
||||
};
|
||||
};
|
||||
|
||||
"custom/network-symbol" = {
|
||||
format = " ";
|
||||
tooltip = false;
|
||||
on-click = "nm-connection-editor";
|
||||
};
|
||||
|
||||
"group/networks" = {
|
||||
orientation = "inherit";
|
||||
drawer = {
|
||||
children-class = "drawer-network";
|
||||
};
|
||||
modules = [
|
||||
"custom/network-symbol"
|
||||
"network"
|
||||
];
|
||||
};
|
||||
|
||||
network = {
|
||||
tooltip = false;
|
||||
format-wifi = "{ipaddr} ({essid})";
|
||||
format-ethernet = "{ipaddr}";
|
||||
};
|
||||
|
||||
keyboard-state = {
|
||||
numlock = true;
|
||||
capslock = true;
|
||||
format = {
|
||||
numlock = "N {icon}";
|
||||
capslock = "C {icon}";
|
||||
};
|
||||
format-icons = {
|
||||
locked = " ";
|
||||
unlocked = " ";
|
||||
};
|
||||
};
|
||||
|
||||
backlight = {
|
||||
tooltip = false;
|
||||
format = " {}%";
|
||||
interval = 1;
|
||||
on-scroll-up = "brightnessctl set +5%";
|
||||
on-scroll-down = "brightnessctl set 5%-";
|
||||
};
|
||||
|
||||
battery = {
|
||||
states = {
|
||||
good = 95;
|
||||
warning = 30;
|
||||
critical = 20;
|
||||
};
|
||||
format = "{icon} {capacity}%";
|
||||
format-charging = " {capacity}%";
|
||||
format-plugged = " {capacity}%";
|
||||
format-alt = "{time} {icon}";
|
||||
format-icons = [
|
||||
""
|
||||
""
|
||||
""
|
||||
""
|
||||
""
|
||||
];
|
||||
};
|
||||
|
||||
tray = {
|
||||
icon-size = 18;
|
||||
spacing = 10;
|
||||
};
|
||||
|
||||
clock = {
|
||||
format = "{:%I:%M %p | %m/%d/%Y}";
|
||||
};
|
||||
|
||||
cpu = {
|
||||
interval = 15;
|
||||
format = " {}%";
|
||||
max-length = 10;
|
||||
};
|
||||
|
||||
memory = {
|
||||
interval = 30;
|
||||
format = " {}%";
|
||||
max-length = 10;
|
||||
};
|
||||
|
||||
"custom/symbol" = {
|
||||
format = " ";
|
||||
tooltip = false;
|
||||
};
|
||||
|
||||
"group/workspaces" = {
|
||||
orientation = "inherit";
|
||||
drawer = {
|
||||
children-class = "drawer-workspace";
|
||||
};
|
||||
modules = [
|
||||
"custom/symbol"
|
||||
"hyprland/workspaces"
|
||||
];
|
||||
};
|
||||
|
||||
"hyprland/workspaces" = {
|
||||
disable-scroll = true;
|
||||
active-only = true;
|
||||
format = "{name} [ {windows} ]";
|
||||
window-rewrite-default = "";
|
||||
format-window-separator = " ";
|
||||
window-rewrite = {
|
||||
"class<alacritty>" = "";
|
||||
"class<foot>" = "";
|
||||
"class<zathura>" = "";
|
||||
"class<steam>" = "";
|
||||
"class<Brave-browser>" = "";
|
||||
"class<WebCord>" = "";
|
||||
"class<Signal>" = "";
|
||||
"class<zoom>" = "";
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
style = ''
|
||||
* {
|
||||
font-family: "SF Pro Display", "Font Awesome 6 Free", "JetBrainsMono Nerd Font";
|
||||
font-size: 16px;
|
||||
min-height: 10px;
|
||||
color: #c0caf5;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
window#waybar {
|
||||
background: rgba(255, 255, 255, 0.2);
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
#keyboard-state,
|
||||
#pulseaudio,
|
||||
#backlight,
|
||||
#clock,
|
||||
#memory,
|
||||
#cpu,
|
||||
#battery,
|
||||
#custom-symbol,
|
||||
#custom-network-symbol,
|
||||
#privacy,
|
||||
#tray,
|
||||
#idle_inhibitor,
|
||||
#workspaces button,
|
||||
#workspaces button.active {
|
||||
margin: 5px;
|
||||
border-radius: 3px;
|
||||
transition: none;
|
||||
border-bottom: 3px solid #c0caf5;
|
||||
}
|
||||
|
||||
#keyboard-state:hover,
|
||||
#pulseaudio:hover,
|
||||
#backlight:hover,
|
||||
#clock:hover,
|
||||
#memory:hover,
|
||||
#cpu:hover,
|
||||
#custom-symbol:hover,
|
||||
#custom-network-symbol:hover,
|
||||
#privacy:hover,
|
||||
#tray:hover,
|
||||
#idle_inhibitor:hover,
|
||||
#workspaces button:hover {
|
||||
border-bottom: 3px solid #0dd2e7;
|
||||
}
|
||||
|
||||
#battery.charging,
|
||||
#battery.plugged {
|
||||
color: #c3e88d;
|
||||
border-bottom: 3px solid #c3e88d;
|
||||
}
|
||||
|
||||
#battery.critical:not(.charging) {
|
||||
color: #ff757f;
|
||||
border-bottom: 3px solid #ff757f;
|
||||
}
|
||||
|
||||
#workspaces button.active,
|
||||
#workspaces button.focus {
|
||||
outline: none;
|
||||
color: #0dd2e7;
|
||||
text-shadow: none;
|
||||
}
|
||||
'';
|
||||
};
|
||||
|
||||
home.packages = with pkgs; [
|
||||
pavucontrol
|
||||
networkmanagerapplet
|
||||
];
|
||||
}
|
||||
88
home/scripts/herdr-picker.sh
Executable file
@@ -0,0 +1,88 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Prevent nested execution if already inside Herdr, tmux, zellij, or running non-interactively
|
||||
if [ -n "$HERDR" ] || [ -n "$HERDR_SESSION" ] || [ -n "$TMUX" ] || [ -n "$ZELLIJ" ] || [ ! -t 0 ]; then
|
||||
exec zsh "$@"
|
||||
fi
|
||||
|
||||
# Function to start raw shell
|
||||
start_plain_shell() {
|
||||
exec zsh "$@"
|
||||
}
|
||||
|
||||
# If herdr binary is not found, launch zsh directly without producing noisy stdout
|
||||
if ! command -v herdr &>/dev/null; then
|
||||
start_plain_shell "$@"
|
||||
fi
|
||||
|
||||
# Fetch active herdr sessions
|
||||
active_sessions=()
|
||||
raw_output=$(herdr session list 2>/dev/null)
|
||||
if [ $? -eq 0 ] && [ -n "$raw_output" ]; then
|
||||
while IFS= read -r line; do
|
||||
# Extract first word as session name
|
||||
sname=$(echo "$line" | awk '{print $1}' | tr -d '*:')
|
||||
if [ -n "$sname" ] && [ "$sname" != "NAME" ] && [ "$sname" != "Sessions:" ] && [ "$sname" != "No" ]; then
|
||||
active_sessions+=("$sname")
|
||||
fi
|
||||
done <<< "$raw_output"
|
||||
fi
|
||||
|
||||
# Prepare menu options
|
||||
menu_items=()
|
||||
menu_items+=("✨ [Create New Session]")
|
||||
menu_items+=("⚡ [Default Session]")
|
||||
|
||||
for s in "${active_sessions[@]}"; do
|
||||
if [ "$s" != "default" ]; then
|
||||
menu_items+=("📦 $s")
|
||||
fi
|
||||
done
|
||||
|
||||
menu_items+=("🐚 [Skip - Plain Shell]")
|
||||
|
||||
# Interactive session selector using fzf
|
||||
if command -v fzf &>/dev/null; then
|
||||
selected=$(printf '%s\n' "${menu_items[@]}" | fzf \
|
||||
--height=45% \
|
||||
--layout=reverse \
|
||||
--border=rounded \
|
||||
--prompt=" Herdr Session > " \
|
||||
--header="Select Herdr session to attach / create | Esc for plain shell" \
|
||||
--header-first)
|
||||
else
|
||||
echo "=========================================="
|
||||
echo " HERDR SESSION PICKER "
|
||||
echo "=========================================="
|
||||
PS3="Choose an option: "
|
||||
select selected in "${menu_items[@]}"; do
|
||||
[ -n "$selected" ] && break
|
||||
done
|
||||
fi
|
||||
|
||||
case "$selected" in
|
||||
"✨ [Create New Session]")
|
||||
echo ""
|
||||
read -r -p "Enter session name (press Enter for auto-generated name): " s_name
|
||||
if [ -z "$s_name" ]; then
|
||||
s_name="workspace-$(date +%m%d-%H%M)"
|
||||
fi
|
||||
export HERDR_SESSION="$s_name"
|
||||
exec herdr session attach "$s_name" 2>/dev/null || exec herdr session new "$s_name" 2>/dev/null || exec herdr "$s_name"
|
||||
;;
|
||||
"⚡ [Default Session]")
|
||||
export HERDR_SESSION="default"
|
||||
exec herdr
|
||||
;;
|
||||
"🐚 [Skip - Plain Shell]"|"")
|
||||
start_plain_shell "$@"
|
||||
;;
|
||||
📦\ *)
|
||||
s_name="${selected#📦 }"
|
||||
export HERDR_SESSION="$s_name"
|
||||
exec herdr session attach "$s_name" 2>/dev/null || exec herdr "$s_name"
|
||||
;;
|
||||
*)
|
||||
start_plain_shell "$@"
|
||||
;;
|
||||
esac
|
||||
54
home/starship/starship.toml
Normal file
@@ -0,0 +1,54 @@
|
||||
# macOS Catalina Dark Aesthetic Starship Theme (Single-Line)
|
||||
add_newline = false
|
||||
scan_timeout = 10
|
||||
|
||||
format = "$os$directory$git_branch$git_status$nix_shell$rust$c$python$cmd_duration$character"
|
||||
|
||||
[os]
|
||||
disabled = false
|
||||
style = "bold #f2f2f7"
|
||||
symbols = { NixOS = " " }
|
||||
|
||||
[directory]
|
||||
truncation_length = 3
|
||||
truncate_to_repo = true
|
||||
style = "bold #0a84ff"
|
||||
format = "[$path]($style) "
|
||||
|
||||
[git_branch]
|
||||
symbol = " "
|
||||
style = "bold #bf5af2"
|
||||
format = "on [$symbol$branch]($style) "
|
||||
|
||||
[git_status]
|
||||
style = "bold #ff453a"
|
||||
format = "([$all_status$ahead_behind]($style) )"
|
||||
|
||||
[nix_shell]
|
||||
symbol = " "
|
||||
style = "bold #64d2ff"
|
||||
format = "via [$symbol$state]($style) "
|
||||
|
||||
[rust]
|
||||
symbol = " "
|
||||
style = "bold #ff9f0a"
|
||||
format = "[$symbol($version )]($style)"
|
||||
|
||||
[c]
|
||||
symbol = " "
|
||||
style = "bold #5e5ce6"
|
||||
format = "[$symbol($version )]($style)"
|
||||
|
||||
[python]
|
||||
symbol = " "
|
||||
style = "bold #ffd60a"
|
||||
format = "[$symbol($version )]($style)"
|
||||
|
||||
[cmd_duration]
|
||||
min_time = 2000
|
||||
style = "bold #ffd60a"
|
||||
format = "took [$duration]($style) "
|
||||
|
||||
[character]
|
||||
success_symbol = "[➜](bold #30d158) "
|
||||
error_symbol = "[➜](bold #ff453a) "
|
||||
152
home/zsh.nix
@@ -37,6 +37,12 @@
|
||||
];
|
||||
};
|
||||
|
||||
programs.starship = {
|
||||
enable = true;
|
||||
enableZshIntegration = true;
|
||||
settings = builtins.fromTOML (builtins.readFile ./starship/starship.toml);
|
||||
};
|
||||
|
||||
programs.zsh = {
|
||||
enable = true;
|
||||
enableCompletion = true;
|
||||
@@ -65,11 +71,6 @@
|
||||
};
|
||||
|
||||
plugins = [
|
||||
{
|
||||
name = "powerlevel10k";
|
||||
src = pkgs.zsh-powerlevel10k;
|
||||
file = "share/zsh-powerlevel10k/powerlevel10k.zsh-theme";
|
||||
}
|
||||
{
|
||||
name = "fzf-tab";
|
||||
src = pkgs.zsh-fzf-tab;
|
||||
@@ -113,141 +114,16 @@
|
||||
"rebuild-test" = "rebuild test";
|
||||
"rebuild-boot" = "rebuild boot";
|
||||
"rebuild-build" = "rebuild build";
|
||||
|
||||
# Herdr multiplexer aliases
|
||||
hs = "herdr-picker";
|
||||
ha = "herdr session attach";
|
||||
hl = "herdr session list";
|
||||
hn = "herdr session new";
|
||||
hk = "herdr session stop";
|
||||
};
|
||||
|
||||
initContent = lib.mkMerge [
|
||||
(lib.mkBefore ''
|
||||
# Enable Powerlevel10k instant prompt. Should stay close to the top of ~/.zshrc.
|
||||
if [[ -r "''${XDG_CACHE_HOME:-$HOME/.cache}/p10k-instant-prompt-''${(%):-%n}.zsh" ]]; then
|
||||
source "''${XDG_CACHE_HOME:-$HOME/.cache}/p10k-instant-prompt-''${(%):-%n}.zsh"
|
||||
fi
|
||||
'')
|
||||
(lib.mkAfter ''
|
||||
# To customize prompt, run `p10k configure` or edit ~/.p10k.zsh.
|
||||
[[ ! -f ~/.p10k.zsh ]] || source ~/.p10k.zsh
|
||||
|
||||
# Path configuration
|
||||
export PATH="$HOME/.local/bin:$HOME/bin:$HOME/.cargo/bin:$PNPM_HOME:$PATH"
|
||||
|
||||
if [ -d "$HOME/Tools/xilinx/Vivado/2023.1/bin" ]; then
|
||||
export PATH="$HOME/Tools/xilinx/Vivado/2023.1/bin:$PATH"
|
||||
fi
|
||||
|
||||
# Miniconda / conda profile hook if installed
|
||||
if [ -f /opt/miniconda3/etc/profile.d/conda.sh ]; then
|
||||
source /opt/miniconda3/etc/profile.d/conda.sh
|
||||
fi
|
||||
|
||||
# Environment modules support if available
|
||||
if (( ''${+functions[module]} )) || [ -f /etc/modules/init/zsh ]; then
|
||||
[ -f /etc/modules/init/zsh ] && source /etc/modules/init/zsh
|
||||
module use --append "$HOME/.config/modulefiles" 2>/dev/null || true
|
||||
fi
|
||||
|
||||
# Auto-tmux configuration
|
||||
use_auto_tmux () {
|
||||
! ([ -z "$DISPLAY" ] && [ -z "$WAYLAND_DISPLAY" ] || $(pgrep -x "i3" > /dev/null || pgrep -x "sway" > /dev/null || pgrep -x "Hyprland" > /dev/null))
|
||||
}
|
||||
|
||||
if use_auto_tmux; then
|
||||
export ZSH_TMUX_AUTOSTART=true
|
||||
export ZSH_TMUX_AUTOCONNECT=false
|
||||
export ZSH_TMUX_AUTOQUIT=true
|
||||
else
|
||||
export ZSH_TMUX_AUTOSTART=false
|
||||
export ZSH_TMUX_AUTOCONNECT=false
|
||||
export ZSH_TMUX_AUTOQUIT=false
|
||||
fi
|
||||
|
||||
if [[ -n "$TMUX" ]]; then
|
||||
tmux-window-name() {
|
||||
if [ -n "$TMUX_PLUGIN_MANAGER_PATH" ] && [ -f "$TMUX_PLUGIN_MANAGER_PATH/tmux-window-name/scripts/rename_session_windows.py" ]; then
|
||||
("$TMUX_PLUGIN_MANAGER_PATH/tmux-window-name/scripts/rename_session_windows.py" &)
|
||||
fi
|
||||
}
|
||||
autoload -Uz add-zsh-hook
|
||||
add-zsh-hook chpwd tmux-window-name
|
||||
fi
|
||||
|
||||
# Custom helper functions
|
||||
rebuild () {
|
||||
local action="''${1:-switch}"
|
||||
shift 2>/dev/null || true
|
||||
|
||||
local config_dir="''${FLAKE_CONFIG:-$HOME/NixOS-Configs}"
|
||||
local host="hades"
|
||||
local target="$config_dir#$host"
|
||||
|
||||
case "$action" in
|
||||
switch|test|boot)
|
||||
echo "==> Running: sudo nixos-rebuild $action --flake $target $*"
|
||||
sudo nixos-rebuild "$action" --flake "$target" "$@"
|
||||
;;
|
||||
build|dry-build|dry-run|dry-activate)
|
||||
local act="$action"
|
||||
[[ "$action" == "dry-run" ]] && act="dry-build"
|
||||
echo "==> Running: nixos-rebuild $act --flake $target $*"
|
||||
nixos-rebuild "$act" --flake "$target" "$@"
|
||||
;;
|
||||
help|-h|--help)
|
||||
echo "Usage: rebuild [switch|test|boot|build|dry-build] [extra nixos-rebuild options]"
|
||||
echo ""
|
||||
echo "Commands:"
|
||||
echo " switch Build, activate, and make default boot entry (default)"
|
||||
echo " test Build and activate without creating a boot entry"
|
||||
echo " boot Build and make default boot entry without activating"
|
||||
echo " build Build system to ./result without activating or sudo"
|
||||
echo " dry-build Show what would be built/fetched"
|
||||
;;
|
||||
*)
|
||||
echo "==> Running: sudo nixos-rebuild $action --flake $target $*"
|
||||
sudo nixos-rebuild "$action" --flake "$target" "$@"
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
perlmutter_pass () {
|
||||
echo -n 'bw master password: '
|
||||
read -s MASTER_PASS
|
||||
echo
|
||||
echo -n 'Copying passkey...'
|
||||
echo "$(echo "$MASTER_PASS" | bw get password iris.nersc.gov 2>/dev/null)$(echo "$MASTER_PASS" | bw get totp iris.nersc.gov 2>/dev/null)" | wl-copy
|
||||
echo 'Done.'
|
||||
}
|
||||
|
||||
compiler_explorer () {
|
||||
docker run -d -p 10240:10240 --name compiler-explorer madduci/docker-compiler-explorer
|
||||
}
|
||||
|
||||
airplay_server () {
|
||||
uxplay -p 10000 -nh -n "$(hostname)" -restrict -allow 88:66:5A:76:32:F5 -allow C4:AC:AA:76:B7:80
|
||||
}
|
||||
|
||||
zathura_md () {
|
||||
cat "$1" | pandoc -f markdown -t pdf | zathura -
|
||||
}
|
||||
|
||||
fooocus () {
|
||||
if [ -f "$HOME/Tools/Fooocus/entry_with_update.py" ]; then
|
||||
"$HOME/.conda/envs/fooocus/bin/python" "$HOME/Tools/Fooocus/entry_with_update.py"
|
||||
else
|
||||
echo "Fooocus not found at $HOME/Tools/Fooocus"
|
||||
fi
|
||||
}
|
||||
|
||||
# Per-host / system local overrides fallback
|
||||
if [ -f "$HOME/.config/zsh/$(hostname)/env.sh" ]; then
|
||||
source "$HOME/.config/zsh/$(hostname)/env.sh"
|
||||
elif [ -f "$HOME/.zshrc.local" ]; then
|
||||
source "$HOME/.zshrc.local"
|
||||
fi
|
||||
|
||||
# Fetch / Banner on interactive terminal start
|
||||
if [[ -o interactive ]] && [ -z "$TMUX" ] && command -v pfetch >/dev/null 2>&1; then
|
||||
pfetch
|
||||
fi
|
||||
'')
|
||||
];
|
||||
initContent = builtins.readFile ./zsh/init.zsh;
|
||||
};
|
||||
|
||||
home.packages = with pkgs; [
|
||||
|
||||
120
home/zsh/init.zsh
Normal file
@@ -0,0 +1,120 @@
|
||||
# Path configuration
|
||||
export PATH="$HOME/.local/bin:$HOME/bin:$HOME/.cargo/bin:$PNPM_HOME:$PATH"
|
||||
|
||||
if [ -d "$HOME/Tools/xilinx/Vivado/2023.1/bin" ]; then
|
||||
export PATH="$HOME/Tools/xilinx/Vivado/2023.1/bin:$PATH"
|
||||
fi
|
||||
|
||||
# Miniconda / conda profile hook if installed
|
||||
if [ -f /opt/miniconda3/etc/profile.d/conda.sh ]; then
|
||||
source /opt/miniconda3/etc/profile.d/conda.sh
|
||||
fi
|
||||
|
||||
# Environment modules support if available
|
||||
if (( ${+functions[module]} )) || [ -f /etc/modules/init/zsh ]; then
|
||||
[ -f /etc/modules/init/zsh ] && source /etc/modules/init/zsh
|
||||
module use --append "$HOME/.config/modulefiles" 2>/dev/null || true
|
||||
fi
|
||||
|
||||
# Auto-tmux configuration
|
||||
use_auto_tmux () {
|
||||
! ([ -z "$DISPLAY" ] && [ -z "$WAYLAND_DISPLAY" ] || $(pgrep -x "i3" > /dev/null || pgrep -x "sway" > /dev/null || pgrep -x "Hyprland" > /dev/null))
|
||||
}
|
||||
|
||||
if use_auto_tmux; then
|
||||
export ZSH_TMUX_AUTOSTART=true
|
||||
export ZSH_TMUX_AUTOCONNECT=false
|
||||
export ZSH_TMUX_AUTOQUIT=true
|
||||
else
|
||||
export ZSH_TMUX_AUTOSTART=false
|
||||
export ZSH_TMUX_AUTOCONNECT=false
|
||||
export ZSH_TMUX_AUTOQUIT=false
|
||||
fi
|
||||
|
||||
if [[ -n "$TMUX" ]]; then
|
||||
tmux-window-name() {
|
||||
if [ -n "$TMUX_PLUGIN_MANAGER_PATH" ] && [ -f "$TMUX_PLUGIN_MANAGER_PATH/tmux-window-name/scripts/rename_session_windows.py" ]; then
|
||||
("$TMUX_PLUGIN_MANAGER_PATH/tmux-window-name/scripts/rename_session_windows.py" &)
|
||||
fi
|
||||
}
|
||||
autoload -Uz add-zsh-hook
|
||||
add-zsh-hook chpwd tmux-window-name
|
||||
fi
|
||||
|
||||
# Custom helper functions
|
||||
rebuild () {
|
||||
local action="${1:-switch}"
|
||||
shift 2>/dev/null || true
|
||||
|
||||
local config_dir="${FLAKE_CONFIG:-$HOME/NixOS-Configs}"
|
||||
local host="hades"
|
||||
local target="$config_dir#$host"
|
||||
|
||||
case "$action" in
|
||||
switch|test|boot)
|
||||
echo "==> Running: sudo nixos-rebuild $action --flake $target $*"
|
||||
sudo nixos-rebuild "$action" --flake "$target" "$@"
|
||||
;;
|
||||
build|dry-build|dry-run|dry-activate)
|
||||
local act="$action"
|
||||
[[ "$action" == "dry-run" ]] && act="dry-build"
|
||||
echo "==> Running: nixos-rebuild $act --flake $target $*"
|
||||
nixos-rebuild "$act" --flake "$target" "$@"
|
||||
;;
|
||||
help|-h|--help)
|
||||
echo "Usage: rebuild [switch|test|boot|build|dry-build] [extra nixos-rebuild options]"
|
||||
echo ""
|
||||
echo "Commands:"
|
||||
echo " switch Build, activate, and make default boot entry (default)"
|
||||
echo " test Build and activate without creating a boot entry"
|
||||
echo " boot Build and make default boot entry without activating"
|
||||
echo " build Build system to ./result without activating or sudo"
|
||||
echo " dry-build Show what would be built/fetched"
|
||||
;;
|
||||
*)
|
||||
echo "==> Running: sudo nixos-rebuild $action --flake $target $*"
|
||||
sudo nixos-rebuild "$action" --flake "$target" "$@"
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
perlmutter_pass () {
|
||||
echo -n 'bw master password: '
|
||||
read -s MASTER_PASS
|
||||
echo
|
||||
echo -n 'Copying passkey...'
|
||||
echo "$(echo "$MASTER_PASS" | bw get password iris.nersc.gov 2>/dev/null)$(echo "$MASTER_PASS" | bw get totp iris.nersc.gov 2>/dev/null)" | wl-copy
|
||||
echo 'Done.'
|
||||
}
|
||||
|
||||
compiler_explorer () {
|
||||
docker run -d -p 10240:10240 --name compiler-explorer madduci/docker-compiler-explorer
|
||||
}
|
||||
|
||||
airplay_server () {
|
||||
uxplay -p 10000 -nh -n "$(hostname)" -restrict -allow 88:66:5A:76:32:F5 -allow C4:AC:AA:76:B7:80
|
||||
}
|
||||
|
||||
zathura_md () {
|
||||
cat "$1" | pandoc -f markdown -t pdf | zathura -
|
||||
}
|
||||
|
||||
fooocus () {
|
||||
if [ -f "$HOME/Tools/Fooocus/entry_with_update.py" ]; then
|
||||
"$HOME/.conda/envs/fooocus/bin/python" "$HOME/Tools/Fooocus/entry_with_update.py"
|
||||
else
|
||||
echo "Fooocus not found at $HOME/Tools/Fooocus"
|
||||
fi
|
||||
}
|
||||
|
||||
# Per-host / system local overrides fallback
|
||||
if [ -f "$HOME/.config/zsh/$(hostname)/env.sh" ]; then
|
||||
source "$HOME/.config/zsh/$(hostname)/env.sh"
|
||||
elif [ -f "$HOME/.zshrc.local" ]; then
|
||||
source "$HOME/.zshrc.local"
|
||||
fi
|
||||
|
||||
# Fetch / Banner on interactive terminal start
|
||||
if [[ -o interactive ]] && [ -z "$TMUX" ] && command -v pfetch >/dev/null 2>&1; then
|
||||
pfetch
|
||||
fi
|
||||
@@ -27,7 +27,7 @@
|
||||
lsof
|
||||
valgrind
|
||||
heaptrack
|
||||
linuxPackages.perf
|
||||
perf
|
||||
flamegraph
|
||||
|
||||
# Decompilers, Disassemblers & Binary Analysis
|
||||
|
||||