diff --git a/settings/hyprland.nix b/settings/hyprland.nix
index 197016a..3b56e85 100644
--- a/settings/hyprland.nix
+++ b/settings/hyprland.nix
@@ -60,7 +60,6 @@ in
networkmanagerapplet
pavucontrol
polkit_gnome
- ] ++ lib.optionals isGaming [
quickshell
qt6.qt5compat
];
@@ -423,145 +422,11 @@ in
};
};
- # Scope all HM Wayland services (hyprpaper, waybar, …) to the
+ # Scope all HM Wayland services (hyprpaper, etc.) to the
# Hyprland session so they don't crash-loop in a GNOME session.
wayland.systemd.target = "hyprland-session.target";
- programs.waybar = {
- enable = true;
- systemd.enable = true;
-
- settings.mainBar = {
- layer = "top";
- position = "top";
- height = 30;
- spacing = 6;
-
- modules-left = [ "hyprland/workspaces" ];
- modules-center = [ "clock" ];
- modules-right = lib.optionals isMacbook [ "battery" ] ++ [ "group/tray-drawer" ];
-
- "hyprland/workspaces" = {
- format = "{name}";
- on-click = "activate";
- sort-by-number = true;
- };
-
- clock = {
- format = "{:%H:%M}";
- tooltip-format = "{:%A, %d %B %Y}\n{calendar}";
- };
-
- "group/tray-drawer" = {
- orientation = "horizontal";
- drawer = {
- transition-duration = 500;
- transition-left-to-right = false;
- };
- modules = [ "custom/tray-handle" "pulseaudio" "tray" ];
- };
-
- "custom/tray-handle" = {
- format = builtins.fromJSON ''"\ue0b2"''; # U+E0B2 Nerd Font powerline filled left-arrow
- tooltip = false;
- };
-
- # Pulseaudio module, now conditionally visible
- pulseaudio = {
- format = "{icon} {volume}%";
- format-muted = " muted";
- format-icons = {
- default = [ "" "" "" ];
- headphone = "";
- headset = "";
- };
- on-click = "pavucontrol";
- scroll-step = 5;
- };
-
- # Tray module, now conditionally visible
- tray = {
- icon-size = 16;
- spacing = 8;
- };
- } // lib.optionalAttrs isMacbook {
- battery = {
- format = "{capacity}% {icon}";
- format-charging = "{capacity}% ";
- format-icons = [ "" "" "" "" "" ];
- states = { warning = 30; critical = 15; };
- };
- };
-
- style = ''
- * {
- font-family: "FiraMono Nerd Font", monospace;
- font-size: 13px;
- min-height: 0;
- border: none;
- border-radius: 0;
- }
-
- window#waybar {
- background: alpha(@base00, 0.82);
- color: @base05;
- }
-
- #workspaces {
- margin-left: 6px;
- }
-
- #workspaces button {
- padding: 0 8px;
- color: @base03;
- background: transparent;
- }
-
- #workspaces button.active {
- color: @base05;
- }
-
- #workspaces button:hover {
- background: alpha(@base05, 0.08);
- color: @base05;
- box-shadow: none;
- text-shadow: none;
- }
-
- #clock {
- color: @base05;
- font-weight: 500;
- }
-
- #pulseaudio,
- #tray {
- padding: 0 10px;
- color: @base05;
- }
-
- #pulseaudio.muted {
- color: @base03;
- }
-
- #tray {
- margin-right: 6px;
- }
-
- #custom-tray-handle {
- padding: 0 0px;
- color: @base05;
- }
-
- #battery {
- padding: 0 10px;
- color: @base05;
- }
- #battery.warning { color: @base0A; }
- #battery.critical { color: @base08; }
- '';
- };
-
- xdg.configFile."quickshell/shell.qml" = lib.mkIf isGaming {
+ xdg.configFile."quickshell/shell.qml" = {
text = ''
//@ pragma UseQApplication
import Quickshell
@@ -637,6 +502,32 @@ in
// Spacer
Item { Layout.fillWidth: true }
+ // Clock
+ Text {
+ id: clockText
+ property date now: new Date()
+ text: now.toLocaleTimeString(Qt.locale(), "HH:mm")
+ color: "#${c.base05}"
+ font.family: "FiraMono Nerd Font"
+ font.pixelSize: 13
+ font.weight: Font.Medium
+
+ Timer {
+ interval: 1000
+ running: true
+ repeat: true
+ onTriggered: clockText.now = new Date()
+ }
+
+ MouseArea {
+ anchors.fill: parent
+ onClicked: calPopup.visible = !calPopup.visible
+ }
+ }
+
+ // Spacer
+ Item { Layout.fillWidth: true }
+
// Network status
Item {
id: netWidget
@@ -703,6 +594,78 @@ in
}
}
+ ${lib.optionalString isMacbook ''
+ // Battery
+ Item {
+ id: batteryWidget
+ Layout.preferredHeight: 30
+ Layout.preferredWidth: batteryRow.width
+ Layout.rightMargin: 10
+
+ property int batteryLevel: 0
+ property bool charging: false
+ property string batteryIcon: "\u{f008e}"
+
+ function updateIcon() {
+ if (charging) { batteryIcon = "\u{f0084}"; return; }
+ if (batteryLevel >= 90) batteryIcon = "\u{f0079}";
+ else if (batteryLevel >= 70) batteryIcon = "\u{f0082}";
+ else if (batteryLevel >= 50) batteryIcon = "\u{f007f}";
+ else if (batteryLevel >= 30) batteryIcon = "\u{f007c}";
+ else if (batteryLevel >= 15) batteryIcon = "\u{f007a}";
+ else batteryIcon = "\u{f008e}";
+ }
+
+ Timer {
+ interval: 10000
+ running: true
+ repeat: true
+ triggeredOnStart: true
+ onTriggered: batteryProc.running = true
+ }
+
+ Process {
+ id: batteryProc
+ command: ["sh", "-c", "cat /sys/class/power_supply/BAT0/capacity; cat /sys/class/power_supply/BAT0/status"]
+ stdout: SplitParser {
+ onRead: data => {
+ let trimmed = data.trim();
+ if (/^\\d+$/.test(trimmed)) {
+ batteryWidget.batteryLevel = parseInt(trimmed);
+ } else {
+ batteryWidget.charging = (trimmed === "Charging");
+ }
+ batteryWidget.updateIcon();
+ }
+ }
+ }
+
+ RowLayout {
+ id: batteryRow
+ anchors.verticalCenter: parent.verticalCenter
+ spacing: 4
+
+ Text {
+ text: batteryWidget.batteryLevel + "%"
+ color: batteryWidget.batteryLevel <= 15 ? "#${c.base08}"
+ : batteryWidget.batteryLevel <= 30 ? "#${c.base0A}"
+ : "#${c.base05}"
+ font.family: "FiraMono Nerd Font"
+ font.pixelSize: 13
+ }
+
+ Text {
+ text: batteryWidget.batteryIcon
+ color: batteryWidget.batteryLevel <= 15 ? "#${c.base08}"
+ : batteryWidget.batteryLevel <= 30 ? "#${c.base0A}"
+ : "#${c.base05}"
+ font.family: "FiraMono Nerd Font"
+ font.pixelSize: 14
+ }
+ }
+ }
+ ''}
+
// Tray icons inline
RowLayout {
id: trayArea
@@ -851,6 +814,111 @@ in
}
}
}
+
+ // Calendar popup
+ PopupWindow {
+ id: calPopup
+ anchor.item: clockText
+ anchor.edges: Edges.Bottom
+ anchor.gravity: Edges.Bottom
+ anchor.adjustment: PopupAdjustment.Slide
+ grabFocus: true
+ visible: false
+ color: "transparent"
+ implicitWidth: calContent.width + 2
+ implicitHeight: calContent.height + 2
+
+ Rectangle {
+ id: calContent
+ width: calCol.width + 32
+ height: calCol.height + 24
+ color: "#${c.base00}"
+ border.color: "#${c.base03}"
+ border.width: 1
+ radius: 8
+
+ Column {
+ id: calCol
+ anchors.centerIn: parent
+ spacing: 8
+
+ // Date header
+ Text {
+ anchors.horizontalCenter: parent.horizontalCenter
+ text: clockText.now.toLocaleDateString(Qt.locale(), "dddd, d MMMM yyyy")
+ color: "#${c.base05}"
+ font.family: "FiraMono Nerd Font"
+ font.pixelSize: 14
+ font.weight: Font.Medium
+ }
+
+ // Day headers
+ Row {
+ spacing: 0
+ Repeater {
+ model: ["Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"]
+ Text {
+ required property var modelData
+ width: 28
+ horizontalAlignment: Text.AlignHCenter
+ text: modelData
+ color: "#${c.base03}"
+ font.family: "FiraMono Nerd Font"
+ font.pixelSize: 11
+ }
+ }
+ }
+
+ // Calendar grid
+ Grid {
+ columns: 7
+ spacing: 0
+
+ Repeater {
+ id: calRepeater
+ model: 42
+
+ Rectangle {
+ required property int index
+ width: 28
+ height: 24
+ radius: 4
+ color: {
+ let d = clockText.now;
+ let first = new Date(d.getFullYear(), d.getMonth(), 1);
+ let startDay = (first.getDay() + 6) % 7;
+ let dayNum = index - startDay + 1;
+ let daysInMonth = new Date(d.getFullYear(), d.getMonth() + 1, 0).getDate();
+ return (dayNum === d.getDate() && dayNum >= 1 && dayNum <= daysInMonth)
+ ? "#${c.base02}" : "transparent";
+ }
+
+ Text {
+ anchors.centerIn: parent
+ text: {
+ let d = clockText.now;
+ let first = new Date(d.getFullYear(), d.getMonth(), 1);
+ let startDay = (first.getDay() + 6) % 7;
+ let dayNum = parent.index - startDay + 1;
+ let daysInMonth = new Date(d.getFullYear(), d.getMonth() + 1, 0).getDate();
+ return (dayNum >= 1 && dayNum <= daysInMonth) ? dayNum.toString() : "";
+ }
+ color: {
+ let d = clockText.now;
+ let first = new Date(d.getFullYear(), d.getMonth(), 1);
+ let startDay = (first.getDay() + 6) % 7;
+ let dayNum = parent.index - startDay + 1;
+ return (dayNum === d.getDate()) ? "#${c.base05}" : "#${c.base04}";
+ }
+ font.family: "FiraMono Nerd Font"
+ font.pixelSize: 11
+ }
+ }
+ }
+ }
+ }
+ }
+ }
}
}
}