# settings/hyprland.nix { config, pkgs, lib, inputs, ... }: let hyprland-pkgs = inputs.hyprland.packages.${pkgs.stdenv.hostPlatform.system}; anyrun-pkgs = inputs.anyrun.packages.${pkgs.stdenv.hostPlatform.system}; isMacbook = config.networking.hostName == "FredOS-Macbook"; isGaming = !isMacbook; in { config = lib.mkIf (lib.elem config.networking.hostName [ "FredOS-Gaming" "FredOS-Macbook" ]) { programs.hyprland = { enable = true; xwayland.enable = true; package = hyprland-pkgs.hyprland; portalPackage = hyprland-pkgs.xdg-desktop-portal-hyprland; }; xdg.portal = { enable = true; # xdg-desktop-portal-hyprland is registered automatically by # programs.hyprland.portalPackage; listing it here too produced a # duplicate user-unit symlink during nixos-rebuild. extraPortals = with pkgs; [ xdg-desktop-portal-gtk ]; config.hyprland.default = [ "hyprland" "gtk" ]; }; security.polkit.enable = true; # Polkit GUI agent for GUI sudo prompts under Hyprland systemd.user.services.polkit-gnome-authentication-agent-1 = { description = "polkit-gnome-authentication-agent-1"; wantedBy = [ "graphical-session.target" ]; partOf = [ "graphical-session.target" ]; after = [ "graphical-session.target" ]; serviceConfig = { Type = "simple"; ExecStart = "${pkgs.polkit_gnome}/libexec/polkit-gnome-authentication-agent-1"; Restart = "on-failure"; }; }; environment.systemPackages = with pkgs; [ ghostty mako grim slurp wl-clipboard cliphist brightnessctl swayosd playerctl hyprpaper hyprlock hypridle hyprshot networkmanagerapplet pavucontrol polkit_gnome quickshell qt6.qt5compat ]; # Use upstream anyrun flake's HM module instead of the built-in one # for working daemon mode. home-manager.sharedModules = [ ({ modulesPath, ... }: { disabledModules = [ "${modulesPath}/programs/anyrun.nix" ]; }) inputs.anyrun.homeManagerModules.default ]; home-manager.users.fred = { config, lib, pkgs, inputs, ... }: let c = config.lib.stylix.colors; rgb = hex: "rgb(${hex})"; rgba = hex: a: "rgba(${hex}${a})"; in { # Stylix's Hyprland target injects settings.{general,decoration,group,misc} # as top-level keys, which render as hl.general()/hl.decoration()/… in Lua # mode — functions that don't exist. Disable it and absorb the colours # into settings.config below. stylix.targets.hyprland.enable = false; # The disabled Hyprland target would normally enable this; do it # manually. Stylix's hyprpaper target (auto-enabled) still handles # preload/wallpaper settings. services.hyprpaper.enable = true; wayland.windowManager.hyprland = { enable = true; configType = "lua"; systemd.variables = [ "--all" ]; package = hyprland-pkgs.hyprland; settings = { # hl.config({...}) — all static named-section configuration. # monitor is set per-host in hosts/FredOS-{Gaming,Macbook}.nix. config = { general = { gaps_in = 6; gaps_out = 12; border_size = 2; layout = "dwindle"; resize_on_border = true; "col.active_border" = rgb c.base0D; "col.inactive_border" = rgb c.base03; }; decoration = { rounding = 8; blur = { enabled = true; }; shadow.color = rgba c.base00 "99"; }; group = { "col.border_active" = rgb c.base0D; "col.border_inactive" = rgb c.base03; "col.border_locked_active" = rgb c.base0C; groupbar = { text_color = rgb c.base05; "col.active" = rgb c.base0D; "col.inactive" = rgb c.base03; }; }; render = { direct_scanout = false; }; animations = { enabled = true; }; input = { kb_layout = "gb,no"; kb_options = "grp:alt_shift_toggle"; follow_mouse = 1; accel_profile = "flat"; sensitivity = 0; } // lib.optionalAttrs isMacbook { touchpad = { tap_to_click = true; tap_button_map = "lrm"; natural_scroll = true; }; }; cursor = { no_warps = true; }; dwindle = { preserve_split = true; }; misc = { disable_hyprland_logo = true; disable_splash_rendering = true; # Apps demanding attention don't get to yank focus — they'll # show as urgent in the bar instead. focus_on_activate = false; vrr = 2; background_color = rgb c.base00; }; # vfr moved from misc: to debug: in 0.55.0 debug = { vfr = false; # keep compositor ticking, don't idle between frames disable_logs = false; }; }; }; extraConfig = let powerMenu = pkgs.writeShellScript "power-menu" '' # Stop the daemon so standalone stdin mode can run cleanly. # systemd restarts it automatically afterwards (Restart=on-failure). systemctl --user stop anyrun.service 2>/dev/null || true choice=$(printf '%s\n' \ $'\uf023 Lock' \ $'\uf08b Logout' \ $'\uf01e Reboot' \ $'\uf011 Shutdown' \ | ${anyrun-pkgs.anyrun}/bin/anyrun \ --plugins "${anyrun-pkgs.stdin}/lib/libstdin.so" \ --show-results-immediately true \ --hide-plugin-info true \ --close-on-click true) # Restart the daemon service (reset-failed clears the start-rate limiter). systemctl --user reset-failed anyrun.service 2>/dev/null systemctl --user start anyrun.service 2>/dev/null case "$choice" in *Lock) ${pkgs.hyprlock}/bin/hyprlock ;; *Logout) hyprctl dispatch exit ;; *Reboot) systemctl reboot ;; *Shutdown) systemctl poweroff ;; esac ''; kbdBrightUp = pkgs.writeShellScript "kbd-bright-up" '' ${pkgs.brightnessctl}/bin/brightnessctl -d smc::kbd_backlight set +10% brightness=$(${pkgs.brightnessctl}/bin/brightnessctl -d smc::kbd_backlight get) max=$(${pkgs.brightnessctl}/bin/brightnessctl -d smc::kbd_backlight max) echo $(( brightness * 100 / max )) > "$XDG_RUNTIME_DIR/wob.fifo" ''; kbdBrightDown = pkgs.writeShellScript "kbd-bright-down" '' ${pkgs.brightnessctl}/bin/brightnessctl -d smc::kbd_backlight set 10%- brightness=$(${pkgs.brightnessctl}/bin/brightnessctl -d smc::kbd_backlight get) max=$(${pkgs.brightnessctl}/bin/brightnessctl -d smc::kbd_backlight max) echo $(( brightness * 100 / max )) > "$XDG_RUNTIME_DIR/wob.fifo" ''; in '' -- Environment hl.env("XCURSOR_THEME", "Bibata-Modern-Ice") hl.env("XCURSOR_SIZE", "24") hl.env("HYPRCURSOR_THEME", "Bibata-Modern-Ice") hl.env("HYPRCURSOR_SIZE", "24") hl.env("ELECTRON_OZONE_PLATFORM_HINT", "wayland") hl.env("MOZ_ENABLE_WAYLAND", "1") hl.env("QT_QPA_PLATFORM", "wayland;xcb") hl.env("SDL_VIDEODRIVER", "wayland") hl.env("_JAVA_AWT_WM_NONREPARENTING", "1") ${lib.optionalString isGaming '' -- GPU pinning — Navi 22 is card1 on the dual-GPU gaming box. hl.env("AQ_DRM_DEVICES", "/dev/dri/card1") hl.env("DRI_PRIME", "pci-0000_03_00_0") ''} -- Startup hl.on("hyprland.start", function() -- Ensure hyprland-session.target starts even if HM's -- dbus-update-activation-environment chain fails upstream. hl.exec_cmd("systemctl --user start hyprland-session.target") hl.exec_cmd("qs") hl.exec_cmd("mako") ${lib.optionalString isMacbook ''hl.exec_cmd("nm-applet --indicator")''} hl.exec_cmd("wl-paste --type text --watch cliphist store") hl.exec_cmd("wl-paste --type image --watch cliphist store") hl.exec_cmd("hyprctl setcursor Bibata-Modern-Ice 24") hl.exec_cmd("swayosd-server") ${lib.optionalString isMacbook ''hl.exec_cmd("hypridle")''} end) -- Animation curve and definitions hl.curve("snap", { type = "bezier", points = { {0.05, 0.9}, {0.1, 1.0} } }) hl.animation({ leaf = "windows", enabled = true, speed = 1, bezier = "snap" }) hl.animation({ leaf = "windowsOut", enabled = true, speed = 1, bezier = "snap", style = "popin 80%" }) hl.animation({ leaf = "layers", enabled = true, speed = 1, bezier = "snap" }) hl.animation({ leaf = "border", enabled = true, speed = 2, bezier = "default" }) hl.animation({ leaf = "fade", enabled = true, speed = 1, bezier = "default" }) hl.animation({ leaf = "workspaces", enabled = true, speed = 1, bezier = "snap" }) -- Window rules -- Battle.net tray icon leaks as a tiny floating XWayland window. hl.window_rule({ match = { class = "steam_app_0", title = "^$", float = true }, workspace = "special silent", }) -- Binds local mod = "SUPER" -- Apps hl.bind(mod .. " + T", hl.dsp.exec_cmd("ghostty")) hl.bind(mod .. " + E", hl.dsp.exec_cmd("nemo")) hl.bind(mod .. " + R", hl.dsp.exec_cmd("anyrun close || anyrun")) hl.bind(mod .. " + Q", hl.dsp.window.close()) hl.bind(mod .. " + SHIFT + E", hl.dsp.exit()) -- Floating / layout hl.bind(mod .. " + V", hl.dsp.window.float({ action = "toggle" })) hl.bind(mod .. " + F", hl.dsp.window.fullscreen()) hl.bind(mod .. " + P", hl.dsp.window.pseudo()) hl.bind(mod .. " + S", hl.dsp.layout("togglesplit")) -- Focus hl.bind(mod .. " + left", hl.dsp.focus({ direction = "left" })) hl.bind(mod .. " + right", hl.dsp.focus({ direction = "right" })) hl.bind(mod .. " + up", hl.dsp.focus({ direction = "up" })) hl.bind(mod .. " + down", hl.dsp.focus({ direction = "down" })) hl.bind(mod .. " + H", hl.dsp.focus({ direction = "left" })) hl.bind(mod .. " + K", hl.dsp.focus({ direction = "up" })) hl.bind(mod .. " + J", hl.dsp.focus({ direction = "down" })) -- Power menu — dismiss launcher if open, then show menu hl.bind(mod .. " + L", hl.dsp.exec_cmd("anyrun close 2>/dev/null; ${powerMenu}")) -- Move windows hl.bind(mod .. " + SHIFT + left", hl.dsp.window.move({ direction = "left" })) hl.bind(mod .. " + SHIFT + right", hl.dsp.window.move({ direction = "right" })) hl.bind(mod .. " + SHIFT + up", hl.dsp.window.move({ direction = "up" })) hl.bind(mod .. " + SHIFT + down", hl.dsp.window.move({ direction = "down" })) -- Workspaces for i = 0, 9 do local workspace_id = tostring((i == 0) and 10 or i) hl.bind(mod .. " + " .. i, hl.dsp.focus({ workspace = workspace_id })) hl.bind(mod .. " + SHIFT + " .. i, hl.dsp.window.move({ workspace = workspace_id, follow = false })) end -- Screenshots — Shift+Super+S matches GNOME binding hl.bind(mod .. " + SHIFT + S", hl.dsp.exec_cmd("hyprshot -m region --clipboard-only")) hl.bind("Print", hl.dsp.exec_cmd("hyprshot -m output --clipboard-only")) -- Settings shortcut — Super+I matches GNOME binding hl.bind(mod .. " + I", hl.dsp.exec_cmd("pavucontrol")) -- Custom shortcuts hl.bind(mod .. " + Z", hl.dsp.exec_cmd("zen-beta")) -- Mouse window manipulation hl.bind(mod .. " + mouse:272", hl.dsp.window.drag(), { mouse = true }) hl.bind(mod .. " + mouse:273", hl.dsp.window.resize(), { mouse = true }) -- Volume / brightness (repeating) hl.bind("XF86AudioRaiseVolume", hl.dsp.exec_cmd("swayosd-client --output-volume raise"), { repeating = true }) hl.bind("XF86AudioLowerVolume", hl.dsp.exec_cmd("swayosd-client --output-volume lower"), { repeating = true }) hl.bind("XF86AudioMute", hl.dsp.exec_cmd("swayosd-client --output-volume mute-toggle"), { repeating = true }) hl.bind("XF86MonBrightnessUp", hl.dsp.exec_cmd("swayosd-client --brightness raise"), { repeating = true }) hl.bind("XF86MonBrightnessDown", hl.dsp.exec_cmd("swayosd-client --brightness lower"), { repeating = true }) ${lib.optionalString isMacbook '' hl.bind("XF86KbdBrightnessUp", hl.dsp.exec_cmd("${kbdBrightUp}"), { repeating = true }) hl.bind("XF86KbdBrightnessDown", hl.dsp.exec_cmd("${kbdBrightDown}"), { repeating = true }) ''} -- Media keys (locked — work through lockscreen) hl.bind("XF86AudioPlay", hl.dsp.exec_cmd("playerctl play-pause"), { locked = true }) hl.bind("XF86AudioNext", hl.dsp.exec_cmd("playerctl next"), { locked = true }) hl.bind("XF86AudioPrev", hl.dsp.exec_cmd("playerctl previous"), { locked = true }) ''; }; programs.anyrun = { enable = true; config = { plugins = [ anyrun-pkgs.applications ]; x.fraction = 0.5; y.fraction = 0.25; width.absolute = 350; height.absolute = 0; hideIcons = false; ignoreExclusiveZones = false; layer = "overlay"; hidePluginInfo = true; closeOnClick = true; maxEntries = 8; }; extraCss = let c = config.lib.stylix.colors; in '' * { all: unset; font-family: "FiraMono Nerd Font", monospace; font-size: 13px; } window { background: transparent; } box.main { background: #${c.base00}; border: 1px solid #${c.base03}; border-radius: 10px; padding: 8px; margin: 16px; } text { background: #${c.base01}; color: #${c.base05}; caret-color: #${c.base0D}; padding: 8px 16px; border-radius: 6px; min-height: 0; } list.plugin { background: transparent; } .matches { background: transparent; } .match { padding: 4px 16px; border-radius: 6px; color: #${c.base05}; background: transparent; } .match:selected { background: #${c.base02}; border: none; } label.match.description { color: #${c.base04}; font-size: 11px; } ''; extraConfigFiles."applications.ron".text = '' Config( desktop_actions: false, max_entries: 8, terminal: Some("ghostty"), ) ''; }; programs.hyprlock.enable = true; services.hypridle = lib.mkIf isMacbook { enable = true; settings = { general = { lock_cmd = "pidof hyprlock || hyprlock"; before_sleep_cmd = "loginctl lock-session"; after_sleep_cmd = "hyprctl dispatch dpms on"; }; listener = [ { timeout = 300; # 5 min — lock on-timeout = "loginctl lock-session"; } { timeout = 420; # 7 min — display off on-timeout = "hyprctl dispatch dpms off"; on-resume = "hyprctl dispatch dpms on"; } { timeout = 600; # 10 min — suspend on-timeout = "systemctl suspend"; } ]; }; }; # 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"; xdg.configFile."quickshell/shell.qml" = { text = '' //@ pragma UseQApplication import Quickshell import Quickshell.Hyprland import Quickshell.Services.SystemTray import Quickshell.Widgets import Quickshell.Io import QtQuick import QtQuick.Layouts import Qt5Compat.GraphicalEffects ShellRoot { Variants { model: Quickshell.screens PanelWindow { id: bar required property var modelData screen: modelData anchors { top: true left: true right: true } implicitWidth: screen.width implicitHeight: 30 color: "#D1${c.base00}" RowLayout { width: bar.screen.width height: 30 spacing: 0 // Workspaces RowLayout { spacing: 0 Layout.leftMargin: 6 Repeater { model: Hyprland.workspaces Item { required property var modelData Layout.preferredWidth: 28 Layout.preferredHeight: 30 Text { anchors.centerIn: parent text: modelData.name color: modelData.focused ? "#${c.base05}" : "#${c.base03}" font.family: "FiraMono Nerd Font" font.pixelSize: 13 } // Underline indicator Rectangle { anchors.bottom: parent.bottom anchors.horizontalCenter: parent.horizontalCenter width: parent.width - 8 height: 2 color: "#${c.base05}" visible: modelData.focused } MouseArea { anchors.fill: parent onClicked: modelData.activate() } } } } // 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 Layout.preferredHeight: 30 Layout.preferredWidth: netRow.width Layout.rightMargin: 10 property string netState: "" property string netConn: "" property string netIcon: "\u{f0b1}" Timer { interval: 5000 running: true repeat: true triggeredOnStart: true onTriggered: netProc.running = true } Process { id: netProc command: ["${pkgs.networkmanager}/bin/nmcli", "-t", "-f", "DEVICE,TYPE,STATE,CONNECTION", "device"] stdout: SplitParser { onRead: data => { let fields = data.split(":"); if (fields.length >= 4 && fields[1] === "ethernet") { let state = fields[2]; let conn = fields[3]; if (state === "connected") { netWidget.netState = "connected"; netWidget.netConn = conn; netWidget.netIcon = "\u{f0200}"; } else { netWidget.netState = "disconnected"; netWidget.netConn = ""; netWidget.netIcon = "\u{f0201}"; } } } } } RowLayout { id: netRow anchors.verticalCenter: parent.verticalCenter spacing: 6 Text { text: netWidget.netIcon color: "#${c.base05}" font.family: "FiraMono Nerd Font" font.pixelSize: 14 } } Process { id: nmEditorProc command: ["${pkgs.networkmanagerapplet}/bin/nm-connection-editor"] } MouseArea { anchors.fill: parent onClicked: nmEditorProc.running = true } } ${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 spacing: 8 Layout.rightMargin: 8 Repeater { model: SystemTray.items Item { required property var modelData Layout.preferredWidth: 16 Layout.preferredHeight: 16 Image { id: trayIcon anchors.fill: parent source: modelData.icon sourceSize.width: 16 sourceSize.height: 16 smooth: true mipmap: true visible: false } ColorOverlay { anchors.fill: trayIcon source: trayIcon color: "#${c.base05}" } MouseArea { anchors.fill: parent acceptedButtons: Qt.LeftButton | Qt.RightButton onClicked: (event) => { if (event.button === Qt.RightButton && modelData.hasMenu) { contextMenu.trayItem = modelData; menuOpener.menu = modelData.menu; contextMenu.visible = true; } else { modelData.activate(); } } } } } } } // Custom-rendered context menu PopupWindow { id: contextMenu property var trayItem: null anchor.item: trayArea anchor.edges: Edges.Bottom | Edges.Right anchor.gravity: Edges.Bottom | Edges.Left anchor.adjustment: PopupAdjustment.Slide grabFocus: true visible: false color: "transparent" implicitWidth: menuColumn.width + 2 implicitHeight: menuColumn.height + 2 QsMenuOpener { id: menuOpener } Rectangle { id: menuColumn width: menuItems.width + 16 height: menuItems.height + 12 color: "#${c.base00}" border.color: "#${c.base03}" border.width: 1 radius: 8 Column { id: menuItems anchors.centerIn: parent width: 200 Repeater { model: menuOpener.children Rectangle { required property var modelData width: 200 height: modelData.isSeparator ? 9 : 28 color: !modelData.isSeparator && itemMouse.containsMouse && modelData.enabled ? "#${c.base02}" : "transparent" radius: modelData.isSeparator ? 0 : 4 // Separator line Rectangle { visible: modelData.isSeparator anchors.centerIn: parent width: parent.width - 20 height: 1 color: "#${c.base03}" } // Menu item content RowLayout { visible: !modelData.isSeparator anchors.fill: parent anchors.leftMargin: 10 anchors.rightMargin: 10 spacing: 8 Text { Layout.fillWidth: true text: modelData.text ?? "" color: modelData.enabled ? "#${c.base05}" : "#${c.base03}" font.family: "FiraMono Nerd Font" font.pixelSize: 12 elide: Text.ElideRight } Text { visible: modelData.buttonType !== QsMenuButtonType.None text: modelData.checkState === Qt.Checked ? "\u2713" : "" color: "#${c.base0D}" font.family: "FiraMono Nerd Font" font.pixelSize: 12 } } MouseArea { id: itemMouse anchors.fill: parent hoverEnabled: true enabled: !modelData.isSeparator && modelData.enabled onClicked: { modelData.triggered(); contextMenu.visible = false; } } } } } } onVisibleChanged: { if (!visible) { menuOpener.menu = null; } } } // 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 } } } } } } } } } } ''; }; }; }; }