diff --git a/flake.lock b/flake.lock index 0a8b5df..4cb5976 100644 --- a/flake.lock +++ b/flake.lock @@ -30,11 +30,11 @@ ] }, "locked": { - "lastModified": 1769774308, - "narHash": "sha256-8Ve6VdUpcYbl8bS5oyDwVnnNobyPxdPmUHGgSDyOazQ=", + "lastModified": 1773507676, + "narHash": "sha256-UabDWdQTsGopWsC+RW0GDBRvAXF5KbkHwEcW4UQpd/A=", "owner": "aylur", "repo": "astal", - "rev": "eb235f8813bdea2a4a38ac228f2efc4e2a8a90af", + "rev": "d3fa2117d581b50e63eb3aefbf92d4883fa23f1b", "type": "github" }, "original": { @@ -132,11 +132,11 @@ ] }, "locked": { - "lastModified": 1773093840, - "narHash": "sha256-u/96NoAyN8BSRuM3ZimGf7vyYgXa3pLx4MYWjokuoH4=", + "lastModified": 1773810247, + "narHash": "sha256-6Vz1Thy/1s7z+Rq5OfkWOBAdV4eD+OrvDs10yH6xJzQ=", "owner": "nix-community", "repo": "home-manager", - "rev": "bb014746edb2a98d975abde4dd40fa240de4cf86", + "rev": "d47357a4c806d18a3e853ad2699eaec3c01622e7", "type": "github" }, "original": { @@ -184,11 +184,11 @@ }, "nixpkgs_2": { "locked": { - "lastModified": 1772963539, - "narHash": "sha256-9jVDGZnvCckTGdYT53d/EfznygLskyLQXYwJLKMPsZs=", + "lastModified": 1773734432, + "narHash": "sha256-IF5ppUWh6gHGHYDbtVUyhwy/i7D261P7fWD1bPefOsw=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "9dcb002ca1690658be4a04645215baea8b95f31d", + "rev": "cda48547b432e8d3b18b4180ba07473762ec8558", "type": "github" }, "original": { @@ -200,11 +200,11 @@ }, "nixpkgs_3": { "locked": { - "lastModified": 1772963539, - "narHash": "sha256-9jVDGZnvCckTGdYT53d/EfznygLskyLQXYwJLKMPsZs=", + "lastModified": 1773734432, + "narHash": "sha256-IF5ppUWh6gHGHYDbtVUyhwy/i7D261P7fWD1bPefOsw=", "owner": "nixos", "repo": "nixpkgs", - "rev": "9dcb002ca1690658be4a04645215baea8b95f31d", + "rev": "cda48547b432e8d3b18b4180ba07473762ec8558", "type": "github" }, "original": { @@ -252,11 +252,11 @@ "nixpkgs": "nixpkgs_3" }, "locked": { - "lastModified": 1773125003, - "narHash": "sha256-EF89LT3zxm8dM2pgoAohUcdQoTVVv1v02Jq+z6j8u+0=", + "lastModified": 1773823796, + "narHash": "sha256-AYFcL50MSz2kdfEl1NgL5r5ukJ06MUos7l3ak2Rtn/M=", "owner": "nix-community", "repo": "NUR", - "rev": "eb59c1e77102b9f836cdea9f12568f191a9aff57", + "rev": "098c5b211d0e445773f57d8ecefb497af07e8767", "type": "github" }, "original": { @@ -299,11 +299,11 @@ "systems": "systems" }, "locked": { - "lastModified": 1773099334, - "narHash": "sha256-ciiNQFZ2m2JOEDDg9ZZxOuxykl4Aw/2C1EQsOcq+F+E=", + "lastModified": 1773692852, + "narHash": "sha256-rVzZeR2gsPCgft+SHbxlxldejpOBI6bwj6gC24XpEIk=", "owner": "vicinaehq", "repo": "vicinae", - "rev": "2a427670116461a4ce0207db2c089aee9f40428e", + "rev": "f3dc9cb4696dda1218f00f2756cf25882fa5df9b", "type": "github" }, "original": { diff --git a/home/Aloria.nix b/home/Aloria.nix index 7846f96..791ab4d 100644 --- a/home/Aloria.nix +++ b/home/Aloria.nix @@ -47,11 +47,6 @@ in { "DP-3,1920x1080,3840x-540,1" "DP-1,1920x1080,3840x-540,1" ]; - bind = [ - ", XF86PowerOff, exec, ${pkgs.rofi}/bin/rofi -show power-menu -modi power-menu:${ - ./hyprland/files/rofi-power-menu.sh - }" - ]; general.gaps_out = 1; }; diff --git a/home/ags/files/Bar.tsx b/home/ags/files/Bar.tsx index 708dc09..2176d98 100644 --- a/home/ags/files/Bar.tsx +++ b/home/ags/files/Bar.tsx @@ -2,9 +2,8 @@ import { Astal, Gdk, Gtk } from "ags/gtk4"; import app from "ags/gtk4/app"; import { createBinding, createState, For, With, Accessor } from "ags"; import { createPoll } from "ags/time"; +import { subprocess, execAsync } from "ags/process"; import Tray from "gi://AstalTray"; -import { execAsync } from "ags/process"; -import Hyprland from "gi://AstalHyprland"; import { getIconName } from "./utils"; import Wp from "gi://AstalWp"; import Battery from "gi://AstalBattery"; @@ -16,6 +15,36 @@ const sensorsAvailable = await execAsync(["sensors"]) .catch(() => false); const wirePlumber = Wp.get_default(); +// --- NIRI IPC INTEGRATION --- +const [workspaces, setWorkspaces] = createState([]); +const [windows, setWindows] = createState([]); + +async function updateNiriState() { + try { + const wsOut = await execAsync(["niri", "msg", "-j", "workspaces"]); + setWorkspaces(JSON.parse(wsOut) || []); + + const winOut = await execAsync(["niri", "msg", "-j", "windows"]); + setWindows(JSON.parse(winOut) || []); + } catch (e) { + console.error("Failed to fetch Niri state:", e); + } +} + +// Initialize state immediately +updateNiriState(); + +// Subscribe to Niri's native event stream +subprocess( + ["niri", "msg", "-j", "event-stream"], + (line) => { + // A state change happened in Niri. Re-fetching guarantees perfect sync + updateNiriState(); + }, + (err) => console.error("Niri event stream error:", err) +); +// ---------------------------- + function SysTray(): JSX.Element { const tray = Tray.get_default(); let items = createBinding(tray, "items"); @@ -52,18 +81,18 @@ function SysTray(): JSX.Element { ); } -function Left(): JSX.Element { +function Left({ connector }: { connector: string }): JSX.Element { return ( - + ); } -function Center(): JSX.Element { +function Center({ connector }: { connector: string }): JSX.Element { return ( - + ); } @@ -244,47 +273,22 @@ function Volume(): JSX.Element { ); } -function Workspaces(): JSX.Element { - const hyprland = Hyprland.get_default(); - let workspaces = createBinding(hyprland, "workspaces"); +function Workspaces({ connector }: { connector: string }): JSX.Element { return ( - - {(wss: Array) => ( - - - {(fm: Hyprland.Monitor) => { - let filtered_wss = new Accessor(() => - wss - .sort((a, b) => a.id - b.id) - .filter( - (ws) => - ws && - ws.get_monitor() && - ws.get_monitor().get_id() === fm.get_id(), - ), - ); - return ( - - - {(ws: Hyprland.Workspace, _index) => ( - - )} - - - ); - }} - - + {/* Generate a derived binding by passing a transformation function into the `workspaces` state */} + + wss.filter((ws) => ws.output === connector).sort((a, b) => a.idx - b.idx) + )}> + {(ws: any) => ( + )} - + ); } @@ -293,59 +297,40 @@ function shorten(title: string) { return title.length > 40 ? title.slice(0, 20) + "..." : title; } -function Clients(): JSX.Element { - const hyprland = Hyprland.get_default(); - let clients = createBinding(hyprland, "clients"); +function Clients({ connector }: { connector: string }): JSX.Element { return ( - - - {(fw: Hyprland.Workspace) => ( - - - {(cls: Array) => { - let filtered_clients = new Accessor(() => - cls - .sort((a, b) => a.pid - b.pid) - .filter((cl) => !cl.title.includes("rofi")) - .filter( - (cl) => fw && cl.get_workspace().get_id() === fw.get_id(), - ), - ); + + {/* Use With to react to workspace changes, so we know which WS is active */} + + {(wss: any[]) => { + let active_ws_for_monitor = wss.find((ws: any) => ws.is_active && ws.output === connector)?.id; - return ( - - - {(cl: Hyprland.Client, _index) => ( - - a && a.address === cl.address - ? "focused" - : "unfocused", - )} - > - - - )} - + return ( + + {/* Generate a derived binding for windows, depending on the active workspace */} + + wins.filter((w: any) => !w.title?.includes("rofi")) + .filter((w: any) => w.workspace_id === active_ws_for_monitor) + )}> + {(win: any) => ( + + + - ); - }} - - - )} + )} + + + ); + }} ); } export default function Bar(gdkmonitor: Gdk.Monitor, scaleFactor: number = 1) { - console.log("Creating Bar on monitor:", gdkmonitor); + console.log("Creating Bar on monitor:", gdkmonitor.get_connector()); + const connector = gdkmonitor.get_connector() || ""; + return ( - -
+ +
diff --git a/home/ags/files/app.ts b/home/ags/files/app.ts index 0fe4139..6fd0330 100644 --- a/home/ags/files/app.ts +++ b/home/ags/files/app.ts @@ -1,60 +1,19 @@ import app from "ags/gtk4/app"; +import { Gdk } from "ags/gtk4"; import style from "./style.scss"; import Bar from "./Bar"; -import Hyprland from "gi://AstalHyprland"; import NotificationPopups from "./notifications/NotificationPopups"; -const hyprland = Hyprland.get_default(); - -function find_main_monitor(): Hyprland.Monitor { - let monitors = hyprland.get_monitors(); - for (let j = 0; j < monitors.length; j++) { - for (const monitor of ["eDP", "DP", "HDMI-A"]) { - for (let i = 0; i < monitors.length; i++) { - console.log( - "Checking monitor:", - monitors[i].get_name(), - "against", - monitor + "-" + j, - ); - if (monitors[i].get_name() == monitor + "-" + j) { - return monitors[i]; - } - } - } - } - return monitors[0]; -} - -function register_windows(monitor: Hyprland.Monitor) { - let gtkMonitor = app.get_monitors()[0]; - let scale = monitor.get_width() >= 3000 ? 1.2 : 1; - Bar(gtkMonitor, scale); +function register_windows(monitor: Gdk.Monitor) { + let scale = (monitor.get_geometry().width >= 3000) ? 1.2 : 1; + Bar(monitor, scale); NotificationPopups(); } -function switch_to_best_monitor() { - let mainMonitor = find_main_monitor(); - for (var wd of app.get_windows()) { - wd.destroy(); - } - register_windows(mainMonitor); -} - -hyprland.connect("monitor-added", (_, _monitor: Hyprland.Monitor) => { - switch_to_best_monitor(); -}); - -hyprland.connect("monitor-removed", () => { - switch_to_best_monitor(); -}); - -console.log("Trying to find the best monitor"); - app.start({ css: style, iconTheme: "Papirus", main() { - switch_to_best_monitor(); + register_windows(app.get_monitors()[0]); }, }); diff --git a/home/ags/files/utils.ts b/home/ags/files/utils.ts index a49a3c3..b23afce 100644 --- a/home/ags/files/utils.ts +++ b/home/ags/files/utils.ts @@ -1,5 +1,4 @@ import Apps from "gi://AstalApps"; -import AstalHyprland from "gi://AstalHyprland"; const app_icons = new Apps.Apps().list.reduce( (acc, app) => { @@ -16,57 +15,39 @@ const app_icons = new Apps.Apps().list.reduce( { classOrNames: {} as Record, executables: {} as Record }, ); -export function getIconName(client: AstalHyprland.Client | null | undefined) { - if (!client) return ""; +export function getIconName(app_id: string | null | undefined, title: string | null | undefined) { + if (!app_id && !title) return ""; - // try a bunch of fields (snake_case and camelCase variants) + // try fields matching Niri outputs const possibleKeys = [ - // common client properties (snake_case) - (client as any).wm_class, - (client as any).initial_class, - (client as any).executable, - // camelCase variants (some bindings expose these) - (client as any).class, - (client as any).initialClass, - (client as any).initialTitle, - (client as any).title, - (client as any).name, + app_id, + title ].filter(Boolean) as string[]; // 1) direct exact match for (const k of possibleKeys) { const icon = app_icons.classOrNames[k] ?? app_icons.executables[k]; if (icon) { - // cache the mapping for this client's canonical class (prefer wm_class or class) - const cacheKey = (client as any).wm_class ?? (client as any).class ?? (client as any).name ?? k; + // cache the mapping + const cacheKey = app_id ?? title ?? k; app_icons.classOrNames[cacheKey] = icon; return icon; } } // 2) fuzzy match: see if any stored key includes any of the client strings (title, name, etc.) - const searchStrings = [ - (client as any).title, - (client as any).initialTitle, - (client as any).initial_class, - (client as any).initialClass, - (client as any).wm_class, - (client as any).class, - (client as any).name, - ].filter(Boolean) as string[]; - - for (const s of searchStrings) { + for (const s of possibleKeys) { const matchKey = Object.keys(app_icons.classOrNames).find((key) => key.includes(s)); if (matchKey) { const icon = app_icons.classOrNames[matchKey]; - const cacheKey = (client as any).wm_class ?? (client as any).class ?? (client as any).name ?? s; + const cacheKey = app_id ?? title ?? s; app_icons.classOrNames[cacheKey] = icon; return icon; } } // nothing found — cache empty string so we don't repeat work - const cacheKey = (client as any).wm_class ?? (client as any).class ?? (client as any).name ?? ""; + const cacheKey = app_id ?? title ?? ""; if (cacheKey) app_icons.classOrNames[cacheKey] = ""; return ""; } diff --git a/home/ags/flake.nix b/home/ags/flake.nix index 277e7ab..3f955cb 100644 --- a/home/ags/flake.nix +++ b/home/ags/flake.nix @@ -21,7 +21,7 @@ pkgs.accountsservice battery - hyprland + # hyprland tray powerprofiles wireplumber diff --git a/home/firefox/files/Chrome/Cached Theme.pak b/home/firefox/files/Chrome/Cached Theme.pak index 836c956..31432e4 100644 Binary files a/home/firefox/files/Chrome/Cached Theme.pak and b/home/firefox/files/Chrome/Cached Theme.pak differ diff --git a/home/hyprland/default.nix b/home/hyprland/default.nix index ed62c80..900cf48 100644 --- a/home/hyprland/default.nix +++ b/home/hyprland/default.nix @@ -147,7 +147,6 @@ in { "rounding 0, match:float 0, match:workspace f[1]" ]; - # UPDATED: New Layer Rule Syntax (0.53.0) # Syntax: effect value, match:namespace regex layerrule = [ "no_anim on, match:namespace ags_bar_0" diff --git a/home/niri/default.nix b/home/niri/default.nix new file mode 100644 index 0000000..0cbee30 --- /dev/null +++ b/home/niri/default.nix @@ -0,0 +1,142 @@ +{pkgs, ...}: let + terminal = "${pkgs.foot}/bin/footclient"; + nautilus = "${pkgs.nautilus}/bin/nautilus"; +in { + # Since native Home Manager doesn't have a programs.niri module yet, + # we generate the KDL configuration file directly. + services.hyprpaper = { + enable = true; + settings = { + ipc = "off"; + splash = false; + wallpaper = { + path = "${../hyprland/files/wallpaper.png}"; + monitor = ""; + }; + }; + }; + + programs.hyprlock = { + enable = true; + settings = { + input-field = [ + { + size = "200, 50"; + position = "0, -80"; + monitor = ""; + dots_center = true; + fade_on_empty = true; + font_color = "rgb(202, 211, 245)"; + inner_color = "rgb(91, 96, 120)"; + outer_color = "rgb(24, 25, 38)"; + outline_thickness = 3; + placeholder_text = "Password..."; + shadow_passes = 2; + } + ]; + general = { + no_fade_in = false; + }; + background = [ + { + path = "screenshot"; + blur_passes = 3; + blur_size = 8; + } + ]; + }; + }; + + xdg.configFile."niri/config.kdl".text = '' + spawn-at-startup "${pkgs.hyprpaper}/bin/hyprpaper" + spawn-at-startup "foot" "--server" + spawn-at-startup "ags" "run" + spawn-at-startup "${pkgs.mate-polkit}/bin/polkit-mate" + prefer-no-csd + + layout { + gaps 5 + border { + off + } + focus-ring { + width 4 + active-color "rgba(0, 47, 95, 238)" + inactive-color "rgba(255, 255, 255, 0)" + } + } + + input { + touchpad { + tap + } + focus-follows-mouse max-scroll-amount="10%" + warp-mouse-to-focus + } + output "eDP-1" { + scale 1.2 + } + window-rule { + match app-id="vicinae" + open-floating true + } + + binds { + // General Binds + Mod+Return { spawn "${terminal}"; } + Mod+Shift+Return { spawn "${terminal}"; } + Mod+Q { close-window; } + Mod+A { spawn "${nautilus}"; } + Mod+B { spawn "Helium"; } + Mod+M { maximize-column; } + Mod+F { fullscreen-window; } + + // Vicinae Binds + Mod+C { spawn "vicinae" "vicinae://extensions/vicinae/clipboard/history"; } + Mod+N { spawn "vicinae" "vicinae://extensions/vicinae/wm/switch-windows"; } + Mod+Escape { spawn "vicinae" "open" "-q" "Power Management "; } + + // Main Vicinae Toggle + Mod+Space { spawn "vicinae" "toggle"; } + + Mod+Comma { consume-or-expel-window-left; } + Mod+Period { consume-or-expel-window-right; } + + Mod+Shift+Minus { set-window-height "-10%"; } + Mod+Shift+Equal { set-window-height "+10%"; } + + // Movement + Mod+H { focus-column-left; } + Mod+J { focus-window-down; } + Mod+K { focus-window-up; } + Mod+L { focus-column-right; } + + Mod+Shift+H { move-column-left; } + Mod+Shift+L { move-column-right; } + Mod+Shift+J { move-window-down; } + Mod+Shift+K { move-window-up; } + + // Workspaces + Mod+1 { focus-workspace 1; } + Mod+2 { focus-workspace 2; } + Mod+3 { focus-workspace 3; } + Mod+4 { focus-workspace 4; } + Mod+5 { focus-workspace 5; } + Mod+6 { focus-workspace 6; } + Mod+7 { focus-workspace 7; } + Mod+8 { focus-workspace 8; } + Mod+9 { focus-workspace 9; } + + // Move to workspace + Mod+Shift+1 { move-column-to-workspace 1; } + Mod+Shift+2 { move-column-to-workspace 2; } + Mod+Shift+3 { move-column-to-workspace 3; } + Mod+Shift+4 { move-column-to-workspace 4; } + Mod+Shift+5 { move-column-to-workspace 5; } + Mod+Shift+6 { move-column-to-workspace 6; } + Mod+Shift+7 { move-column-to-workspace 7; } + Mod+Shift+8 { move-column-to-workspace 8; } + Mod+Shift+9 { move-column-to-workspace 9; } + } + ''; +} diff --git a/home/utils/desktop.nix b/home/utils/desktop.nix index b8a7d34..9d7ddf9 100644 --- a/home/utils/desktop.nix +++ b/home/utils/desktop.nix @@ -7,7 +7,8 @@ imports = [ (import ./common.nix {inherit inputs config pkgs;}) (import ./python.nix {inherit inputs config pkgs;}) - (import ../hyprland {inherit inputs pkgs;}) + # (import ../hyprland {inherit inputs pkgs;}) + (import ../niri {inherit inputs config pkgs;}) (import ../ags {inherit inputs pkgs;}) ../rofi ../vicinae diff --git a/hosts/Common/desktop.nix b/hosts/Common/desktop.nix index 12e6d2c..ba7ab5d 100644 --- a/hosts/Common/desktop.nix +++ b/hosts/Common/desktop.nix @@ -15,15 +15,17 @@ vulkan-extension-layer ]; }; - programs.hyprland = { - enable = true; - withUWSM = true; - }; + # programs.hyprland = { + # enable = true; + # withUWSM = true; + # }; + programs.niri.enable = true; services.greetd = { enable = pkgs.lib.mkDefault true; settings = rec { initial_session = { - command = "uwsm start hyprland-uwsm.desktop"; + # command = "uwsm start hyprland-uwsm.desktop"; + command = "niri-session"; user = "user"; }; default_session = initial_session;