feat: replace noctalia with an eww panel (#3)
This commit was merged in pull request #3.
This commit is contained in:
@@ -0,0 +1,292 @@
|
||||
{
|
||||
config,
|
||||
lib,
|
||||
pkgs,
|
||||
inputs,
|
||||
...
|
||||
}:
|
||||
let
|
||||
cfg = config.custom.hyprland;
|
||||
|
||||
# Every length in the stylesheet goes through this, so one option resizes the
|
||||
# whole dashboard for 4K hosts.
|
||||
px = n: "${toString (builtins.ceil (n * cfg.panelScale))}px";
|
||||
|
||||
mkScript =
|
||||
name: runtimeInputs:
|
||||
pkgs.writeShellApplication {
|
||||
inherit name runtimeInputs;
|
||||
text = builtins.readFile (./scripts + "/${name}.sh");
|
||||
};
|
||||
|
||||
battery = mkScript "battery" [
|
||||
pkgs.coreutils
|
||||
pkgs.findutils
|
||||
pkgs.jq
|
||||
];
|
||||
network = mkScript "network" [
|
||||
pkgs.networkmanager
|
||||
pkgs.gawk
|
||||
pkgs.gnused
|
||||
pkgs.jq
|
||||
];
|
||||
bluetooth = mkScript "bluetooth" [
|
||||
pkgs.bluez
|
||||
pkgs.coreutils
|
||||
pkgs.findutils
|
||||
pkgs.gawk
|
||||
pkgs.jq
|
||||
];
|
||||
audio = mkScript "audio" [
|
||||
pkgs.gawk
|
||||
pkgs.jq
|
||||
pkgs.wireplumber
|
||||
];
|
||||
panelToggle = mkScript "eww-panel" [
|
||||
config.programs.eww.package
|
||||
pkgs.jq
|
||||
inputs.hyprland.packages.${pkgs.stdenv.hostPlatform.system}.hyprland
|
||||
];
|
||||
|
||||
eww = lib.getExe config.programs.eww.package;
|
||||
bluetoothctl = "${pkgs.bluez}/bin/bluetoothctl";
|
||||
wpctl = "${pkgs.wireplumber}/bin/wpctl";
|
||||
wpaperctl = "${pkgs.wpaperd}/bin/wpaperctl";
|
||||
|
||||
# setsid, and before the close rather than after it: eww kills the onclick
|
||||
# handler's process group when the window it came from goes away, so a bare
|
||||
# `close panel; cmd` never reaches cmd.
|
||||
act = cmd: "${pkgs.util-linux}/bin/setsid --fork ${cmd}; ${eww} close panel";
|
||||
in
|
||||
{
|
||||
home.packages = [
|
||||
panelToggle
|
||||
pkgs.waypaper
|
||||
];
|
||||
|
||||
programs.eww = {
|
||||
enable = true;
|
||||
|
||||
systemd.enable = true;
|
||||
|
||||
yuckConfig = ''
|
||||
;; Each script prints one JSON object; widgets read .icon / .label / .sub
|
||||
;; off it.
|
||||
(defpoll clock-time :interval "10s" :initial "--:--" `date +%H:%M`)
|
||||
(defpoll clock-date :interval "1h" :initial "" `date "+%A, %d %B"`)
|
||||
|
||||
(defpoll battery
|
||||
:interval "30s"
|
||||
:initial '{"present":false,"icon":"","label":"","sub":""}'
|
||||
`${lib.getExe battery}`)
|
||||
|
||||
(defpoll network
|
||||
:interval "10s"
|
||||
:initial '{"icon":"","label":"...","sub":""}'
|
||||
`${lib.getExe network}`)
|
||||
|
||||
(defpoll bluetooth
|
||||
:interval "10s"
|
||||
:initial '{"powered":false,"icon":"","label":"...","sub":""}'
|
||||
`${lib.getExe bluetooth}`)
|
||||
|
||||
(defpoll speaker
|
||||
:interval "2s"
|
||||
:initial '{"present":false,"volume":0,"muted":true,"icon":""}'
|
||||
`${lib.getExe audio} sink`)
|
||||
|
||||
(defpoll mic
|
||||
:interval "2s"
|
||||
:initial '{"present":false,"volume":0,"muted":true,"icon":""}'
|
||||
`${lib.getExe audio} source`)
|
||||
|
||||
(defwidget card [title]
|
||||
(box :class "card" :orientation "v" :space-evenly false
|
||||
(label :class "card-title" :halign "start" :text title :visible {title != ""})
|
||||
(box :orientation "v" :space-evenly false
|
||||
(children))))
|
||||
|
||||
(defwidget row [icon label sub onclick]
|
||||
(eventbox :class "row" :cursor "pointer" :onclick onclick
|
||||
(box :space-evenly false
|
||||
(label :class "row-icon" :text icon)
|
||||
(box :orientation "v" :space-evenly false :hexpand true
|
||||
(label :class "row-label" :halign "start" :text label)
|
||||
(label :class "row-sub" :halign "start" :text sub :visible {sub != ""})))))
|
||||
|
||||
;; Mute button, slider, readout. `state` is the speaker/mic json object.
|
||||
(defwidget volume [state node]
|
||||
(box :space-evenly false :visible {state.present}
|
||||
(button :class {state.muted ? "mute muted" : "mute"}
|
||||
:onclick "${wpctl} set-mute ''${node} toggle"
|
||||
{state.icon})
|
||||
(scale :class "slider" :hexpand true
|
||||
:value {state.volume} :min 0 :max 101
|
||||
:onchange "${wpctl} set-volume ''${node} {}%")
|
||||
(label :class "row-sub" :text {"''${state.volume}%"})))
|
||||
|
||||
(defwidget action [icon tooltip onclick]
|
||||
(button :class "action" :tooltip tooltip :onclick onclick icon))
|
||||
|
||||
(defwidget dashboard []
|
||||
(box :class "dash" :orientation "v" :space-evenly false
|
||||
|
||||
(box :space-evenly false
|
||||
|
||||
(card :title ""
|
||||
(label :class "clock-time" :halign "start" :text clock-time)
|
||||
(label :class "clock-date" :halign "start" :text clock-date))
|
||||
|
||||
(card :title "System"
|
||||
(box :visible {battery.present} :space-evenly false
|
||||
(row :icon {battery.icon} :label {battery.label}
|
||||
:sub {battery.sub} :onclick ""))
|
||||
(row :icon {network.icon} :label {network.label} :sub {network.sub}
|
||||
:onclick "${act "${pkgs.networkmanagerapplet}/bin/nm-connection-editor"}")
|
||||
(row :icon {bluetooth.icon} :label {bluetooth.label} :sub {bluetooth.sub}
|
||||
:onclick "${act "${pkgs.blueman}/bin/blueman-manager"}")))
|
||||
|
||||
(box :space-evenly false
|
||||
|
||||
(card :title "Audio"
|
||||
(volume :state speaker :node "@DEFAULT_AUDIO_SINK@")
|
||||
(volume :state mic :node "@DEFAULT_AUDIO_SOURCE@"))
|
||||
|
||||
(card :title "Actions"
|
||||
(box :space-evenly false :halign "start"
|
||||
(action :icon "" :tooltip "Next wallpaper"
|
||||
:onclick "${wpaperctl} next")
|
||||
(action :icon "" :tooltip "Pick wallpaper"
|
||||
:onclick "${act "${lib.getExe pkgs.waypaper}"}")
|
||||
(action :icon "" :tooltip "Toggle bluetooth"
|
||||
:onclick {bluetooth.powered
|
||||
? "${bluetoothctl} power off"
|
||||
: "${bluetoothctl} power on"})
|
||||
(action :icon "" :tooltip "Lock"
|
||||
:onclick "${act "${pkgs.systemd}/bin/loginctl lock-session"}"))))))
|
||||
|
||||
;; Sized to its content. A full-screen backdrop would give click-outside-
|
||||
;; to-close, but its handler also fires for clicks on the sliders.
|
||||
(defwindow panel
|
||||
:namespace "eww-panel"
|
||||
:geometry (geometry :anchor "center")
|
||||
:stacking "overlay"
|
||||
(dashboard))
|
||||
'';
|
||||
|
||||
scssConfig = ''
|
||||
$bg: rgba(46, 52, 64, 0.96);
|
||||
$card: rgba(59, 66, 82, 0.72);
|
||||
$border: #4c566a;
|
||||
$fg: #eceff4;
|
||||
$muted: #81a1c1;
|
||||
$accent: #88c0d0;
|
||||
$red: #bf616a;
|
||||
|
||||
* {
|
||||
all: unset;
|
||||
font-family: "FiraCode Nerd Font", monospace;
|
||||
}
|
||||
|
||||
window {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
.dash {
|
||||
background-color: $bg;
|
||||
border: ${px 1} solid $border;
|
||||
border-radius: ${px 20};
|
||||
padding: ${px 14};
|
||||
color: $fg;
|
||||
}
|
||||
|
||||
.card {
|
||||
background-color: $card;
|
||||
border-radius: ${px 14};
|
||||
padding: ${px 16};
|
||||
margin: ${px 6};
|
||||
min-width: ${px 300};
|
||||
}
|
||||
|
||||
.card-title {
|
||||
font-size: ${px 11};
|
||||
color: $muted;
|
||||
margin-bottom: ${px 8};
|
||||
}
|
||||
|
||||
.clock-time {
|
||||
font-size: ${px 56};
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.clock-date {
|
||||
font-size: ${px 14};
|
||||
color: $muted;
|
||||
}
|
||||
|
||||
.row {
|
||||
border-radius: ${px 10};
|
||||
padding: ${px 10} ${px 12};
|
||||
|
||||
&:hover {
|
||||
background-color: rgba(136, 192, 208, 0.14);
|
||||
}
|
||||
}
|
||||
|
||||
.row-icon {
|
||||
font-size: ${px 22};
|
||||
color: $accent;
|
||||
min-width: ${px 34};
|
||||
}
|
||||
|
||||
.row-label {
|
||||
font-size: ${px 14};
|
||||
}
|
||||
|
||||
.row-sub {
|
||||
font-size: ${px 11};
|
||||
color: $muted;
|
||||
}
|
||||
|
||||
.mute {
|
||||
font-size: ${px 20};
|
||||
color: $accent;
|
||||
min-width: ${px 34};
|
||||
|
||||
&.muted {
|
||||
color: $red;
|
||||
}
|
||||
}
|
||||
|
||||
.slider {
|
||||
margin: 0 ${px 12};
|
||||
min-width: ${px 180};
|
||||
|
||||
trough {
|
||||
background-color: rgba(76, 86, 106, 0.6);
|
||||
border-radius: ${px 8};
|
||||
min-height: ${px 8};
|
||||
|
||||
highlight {
|
||||
background-color: $accent;
|
||||
border-radius: ${px 8};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.action {
|
||||
background-color: rgba(76, 86, 106, 0.45);
|
||||
border-radius: ${px 12};
|
||||
padding: ${px 12} ${px 18};
|
||||
margin-right: ${px 8};
|
||||
font-size: ${px 20};
|
||||
color: $fg;
|
||||
|
||||
&:hover {
|
||||
background-color: $accent;
|
||||
color: #2e3440;
|
||||
}
|
||||
}
|
||||
'';
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user